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 barhelper package into the expertdemo project.
We will implement a simple SMA-based trading strategy with the following logic:
- Entry condition: Enter long when
sma1crosses abovesma2. - Entry filter: Only enter if
close > sma1 > sma2 > sma3. This filter can be toggled on or off. - Exit condition: Exit only when
sma1crosses belowsma2. - Parameter validation: Ensure that
sma1.period < sma2.period < sma3.period.
We will use the CrossUp function from barhelper before it is published to the registry, and we will retrieve SMA values using the KnitPkgSMA indicator. As a result, expertdemo will no longer depend on the calc package.
Why Use Local Dependencies¶
Even with a complete suite of unit tests (and we know you always write them), you may want to validate your package in a real project before publishing. In other cases, you might want to integrate a package into a consumer project while still developing it. KnitPkg supports this workflow through local dependencies.
Adding a Local Dependency¶
To add a local dependency, manually edit the knitpkg.yaml manifest and specify the path to the local package directory. You can use:
- A relative path (must start with
./or../and use/as separator) - A file URI (must start with
file://, supports both/and\, and can be absolute or relative)
Here’s how to add barhelper as a local dependency to expertdemo:
Should You Declare an Indirect Dependency?¶
Since barhelper depends on bar, the bar package is available to expertdemo as an indirect dependency. But should we declare it explicitly?
Let’s consider:
barhelperexposes a function (CrossUp) whose signature usesITimeSeries, a type declared inbar. This is a dependency in the API, not just the implementation.- If
barhelperusedbaronly internally, and its API did not expose any symbols frombar, thenexpertdemowould not need to depend onbar. expertdemoalso uses symbols frombardirectly (e.g.,BarWatcher,BarMqlRates), unrelated tobarhelper.
From this, we conclude that an indirect dependency should be declared explicitly if:
- The API of a direct dependency uses symbols from the indirect dependency
- The consumer project uses symbols from the indirect dependency directly
Therefore, expertdemo should declare bar as a direct dependency. However, this also means that expertdemo becomes responsible for defining the version of bar to be used — either with a version range or an exact match.
In practice, this setup would work with or without declaring bar, but being explicit has its benefits.
Forcing a Specific Version of an Indirect Dependency¶
If you decide not to declare an indirect dependency explicitly in the dependencies section, but still want to control which version is used, you can use the overrides field in your manifest.
This allows you to force a specific version of a dependency that is required transitively by another package.
Here’s an example:
In this case, even though @douglasrechia/bar is not listed as a direct dependency, the override ensures that version 1.0.0 will be used if calc (or any other dependency) requires bar.
This is useful when:
- You want to avoid declaring a dependency explicitly
- You need to resolve version conflicts
- You want to ensure reproducibility across builds
For more details, see Version Conflicts and Overrides.
Adjusting Dependencies to Use the sma Indicator¶
Let’s add bar as a direct dependency:
We will use CrossUp to detect SMA crossovers. This function expects ITimeSeries inputs, which we will construct using the KnitPkgSMA indicator. This eliminates the need for the calc package.
Final dependency list in expertdemo:
Order of Dependency Declarations Matters
In the example above, the dependency on @douglasrechia/bar was declared before @douglasrechia/barhelper. This is intentional.
KnitPkg resolves dependencies in the order they appear in the manifest. If barhelper is declared first, KnitPkg will resolve its transitive dependency on bar before seeing the version constraint declared by expertdemo. As a result, the version of bar used will be the one required by barhelper, not the one specified by expertdemo.
Declaring @douglasrechia/bar before @douglasrechia/barhelper ensures that the version of bar is locked according to the version range specified by expertdemo.
For more details, see Version Conflicts and Overrides.
Updating the Entrypoint¶
The expertdemo project uses Flat mode, and its entrypoint is KnitPkgExpertDemo.mqh.
Update the includes:
Installing Dependencies¶
Run:
You should see output like this:

@douglasrechia/barhelper appears in the dependency tree. Let’s confirm in MetaEditor:

The IntelliSense confirms that CrossUp is available via the generated flat file.
Updating expertdemo¶
We’ll now implement the SMA strategy using CrossUp. Here’s the updated OnNewBar() function:
You can find the full source here:
Here’s the equity curve for EURUSD H4 (2025-01-01 to 2025-12-31, with default parameters but filter off):

Additional Improvements¶
Let’s improve the code by abstracting the SMA buffer-to-series logic into a helper function in barhelper.
We’ll create a new function NewTimeSeriesFromIndicator() in barhelper:
TimeSeriesArray<double>* NewTimeSeriesFromIndicator(int indicatorHandler, int indicatorBufferNum, int startPos, int count)
{
double array[];
ArraySetAsSeries(array, true);
if(CopyBuffer(indicatorHandler, indicatorBufferNum, startPos, count, array) == -1)
return NULL;
return new TimeSeriesArray<double>(array);
}
This function should be placed in
knitpkg/include/douglasrechia/barhelper/IndicatorSeries.mqh, which is available here.
After updating barhelper, we can simplify OnNewBar() like this:
This encapsulates the buffer-copy logic into a reusable abstraction. The final expert code is available here:
Note
Applicable to packages only: always run kp checkinstall to verify that the directives are correct.
Applicable to all project types except packages: kp install is required every time you update any @knitpkg:include directive in the entrypoint header.
Tip
Execute kp build --no-locked to run install + compile at one shot; --no-locked is required because the build command installs in --locked mode by default and we have local dependencies. See kp build.
Congratulations! You’ve learned how to use local dependencies, manage indirect dependencies, and improve code abstraction using KnitPkg.