Discussing the article: "Package-based approach with KnitPkg for MQL5 development"

 

Check out the new article: Package-based approach with KnitPkg for MQL5 development.

For maximum reliability and productivity in MetaTrader products built with MQL, this article advocates a development approach based on reusable “packages” managed by KnitPkg, a project manager for MQL5/MQL4. A package can be used as a building block for other packages or as the foundation for final artifacts that run directly on the MetaTrader platform, such as EAs, indicators, and more.

A trader‑developer often needs to maintain several MQL5 projects (EAs, indicators, utilities) that share the same math and utility code. Today, this reuse is usually done the old way: copying folders between projects and manually editing #include statements. With every update, version drift appears—somewhere there is a new fix, somewhere an old file—and a “pure MetaTrader” build depends on what has already been manually patched in that terminal. What is really needed is a way to implement a component once in Git, version it with SemVer, and include it in different projects so that installation and builds are repeatable on any machine: dependencies are fixed, artifacts are generated automatically (include tree or flat header), and moving code between projects no longer requires editing paths or copy‑pasting files.

This article presents such a workflow for MQL4/MQL5 based on KnitPkg packages: each piece of functionality is defined as a reusable, versioned component and then consumed across projects through a manifest rather than via copy‑paste. Throughout the article, we will see how this approach makes assembly, reuse between EAs/indicators, and publication more robust by turning scattered snippets into structured, dependency‑managed building blocks.

Author: Douglas Nascimento Rechia

 

Looks interesting, but I have some doubts.

If someone clone a git repository with an MQL5 project/program/package supposed to be managed by KnitPkg, he/she will most likely get noncompilable sources because autogenerated files (such as _flat)  are missing in the repository. Wouldn't it be feasible to commit all necessary autogenerated source to git to get the release compilable no matter how it's obtained?

I'm not sure how convenient will it be to make some changes in dependencies, since we actually compile and debug autogenerated code, and can't easily "jump" to corresponding origin - am I missing something? For example, you need to change the bar package while working on the indicator (if not the expert adviser). How would you do it?

At last, I'm suspicious about some things in implementation of examples (probably, because they are just examples), such as new/delete on every tick/bar. Also, for example, you do:

int OnCalculate(const int32_t rates_total,
                const int32_t prev_calculated,
                const int32_t begin,
                const double &price[])
{
   ...
   douglasrechia::TimeSeriesArray<double> priceArray(price, prev_calculated - InpSMAPeriod - 1, rates_total-1, false);
   ...
   for (int shift=shiftStart; shift >= 0 && !IsStopped(); shift--)
   {
      SMABuffer[shift] = douglasrechia::SMA(priceArray, InpSMAPeriod, shift);
   }
   ...
}

What's the purpose to pass the price array into priceArray constructor (where a copy of the part of the array is created), and then pass priceArray into SMA - why not pass the original price array by reference into SMA without copying?

 
Stanislav Korotky #:

Looks interesting, but I have some doubts.

If someone clone a git repository with an MQL5 project/program/package supposed to be managed by KnitPkg, he/she will most likely get noncompilable sources because autogenerated files (such as _flat)  are missing in the repository. Wouldn't it be feasible to commit all necessary autogenerated source to git to get the release compilable no matter how it's obtained?

I'm not sure how convenient will it be to make some changes in dependencies, since we actually compile and debug autogenerated code, and can't easily "jump" to corresponding origin - am I missing something? For example, you need to change the bar package while working on the indicator (if not the expert adviser). How would you do it?

At last, I'm suspicious about some things in implementation of examples (probably, because they are just examples), such as new/delete on every tick/bar. Also, for example, you do:

What's the purpose to pass the price array into priceArray constructor (where a copy of the part of the array is created), and then pass priceArray into SMA - why not pass the original price array by reference into SMA without copying?


Hi Stanislav,

Thanks for taking the time to read the article and for raising these points.

On committing generated code: the idea with KnitPkg is that autogenerated artifacts (flat headers, resolved includes, etc.) should not live in the Git repository, because then you effectively duplicate code across repos and risk it going out of sync. If some functionality is implemented in a package/repository, that is the single source of truth; consumers declare a dependency (using SemVer ranges) and let kp install / kp autocomplete regenerate the derived files locally. This way, when a dependency is updated within the allowed SemVer range, your project can pick up the new version cleanly, instead of being “stuck” with stale generated code committed into its own repo. There is a concrete example of updating dependency versions in the documentation here.

On your concern about “how convenient it will be to make some changes in dependencies”: this was something I thought about while designing the tool. KnitPkg supports using local dependencies, so you can point a project directly to a package living in a local directory, modify that package, and test changes in a real consumer project during development. The workflow is described in more detail in the “Local Dependencies” section of the KnitPkg documentation: https://docs.knitpkg.dev/user-guide/local-dependencies/

Regarding price -> TimeSeriesArray -> SMA: the intention there is not to show the most efficient micro‑optimization, but to show a standardized interface. SMA receives an ITimeSeries (not TimeSeriesArray directly), and it only cares that “most recent is at index 0, next at 1, etc.”. TimeSeriesArray is just one implementation used in the example, which happens to copy a slice of price. Someone else could implement a different ITimeSeries that uses a more cache‑friendly structure. The point is to decouple the calculation from the concrete storage layout; how smart or efficient the ITimeSeries implementation is becomes a matter of design and creativity.

I’m always happy to discuss these design choices, and feedback on KnitPkg—whether on the workflow or the example code—is very welcome, so feel free to challenge or extend any of these ideas.

Local Dependencies¶
  • docs.knitpkg.dev
Local Dependencies¶ During development, it is often useful to test a package in a real project before publishing it to the registry. In this section, we will demonstrate how to use local dependencies by integrating the package into the project. We will implement a simple SMA-based trading strategy with the following logic: Entry condition...