Building & deploying
Overview & discovery
What a plugin DLL is, how the TradeStrike host finds it on launch, which catalog each type lands in, and the build → deploy → restart loop you repeat as you develop.
What a plugin is
A plugin is a single .NET assembly — one .dll — that references the
TradeStrike.Sdk contract package and contains one or more public types implementing a TradeStrike
extension contract. There is no manifest, no registration call, and no entry-point method
to write. You compile against the contracts, drop the DLL in the host's Plugins folder, and
the host finds your types on its next launch.
A plugin is not limited to one kind of extension. A single assembly can carry any mix of indicators, strategies, bar-type builders, data-provider factories and Market Analyzer columns at once, so you can ship a whole feature — an indicator plus the custom bar type it depends on, say — as one file. There is no upper limit and no requirement that the types be related.
The build, deploy, restart loop
Plugin discovery happens once, at startup. The host scans the Plugins
folder, loads each DLL, and registers what it finds into its catalogs — then never re-scans. Your
inner development loop is therefore three steps:
-
Build your plugin project (
dotnet build, or build in your IDE). -
Deploy the freshly built DLL into the host's
Pluginsfolder — either by the SDK's opt-in auto-deploy target or a manual copy (see Where to deploy). - Restart TradeStrike. The new build is picked up on the next launch.
What gets discovered
The loader entry point is PluginLoader.ScanDirectory. For each DLL it produces a
PluginCatalog that buckets the types it found by the contract each one implements. You never
write a line of wiring code — discovery is purely by the interfaces a type
implements. Implement the right contract and the type appears; implement two and it appears in
two catalogs. The table below maps "contract I implemented" to "catalog I land in", with a link to the
chapter that explains how to build each kind.
| Implement this… | …and you land in | Chapter |
|---|---|---|
IIndicator (usually via IndicatorBase) |
Indicators |
Indicators |
IStrategy (or the pipeline Strategy base, see note) |
Strategies |
Strategies |
ITickBarBuilder |
TickBarBuilders |
Bar types |
IDerivedBarBuilder |
DerivedBarBuilders |
Bar types |
BarSpecification (subclass) |
BarSpecifications |
Bar types |
IDataProviderFactory |
DataProviderFactories |
Connections |
IColumnDefinition |
MarketAnalyzerColumns |
Market Analyzer |
DrawingToolBase + [DrawingToolMetadata]
|
drawing-tool catalog (scanned from loaded assemblies) | Drawing tools |
Strategy
base class is a plain class, not an IIndicator, so the per-DLL PluginCatalog
lists strategies separately and the host's StrategyCatalog.BuildDefault() — which
scans every loaded assembly — surfaces them. Because the installer loads your plugin
assembly first, your strategies are found automatically. Drawing tools are likewise picked up by the
DrawingToolCatalog scanning loaded assemblies for [DrawingToolMetadata]. In
every case the rule is the same: drop the DLL, restart, and they appear.
Discovery rules — make your type loadable
Before a type is registered, the loader checks it against a short list of requirements. Every rule must pass; a type that fails even one is skipped silently rather than crashing the load. The overwhelming majority of "my plugin doesn't show up" reports come down to one of these, so it is worth knowing them.
-
Public — not
internal, not nested-private. (A public nested type is fine.) -
Concrete — not
abstract, not an interface, not an open generic type definition. -
Zero-arg constructable — the host constructs instances generically before applying
parameter values, so the type must have a public constructor callable with no arguments: either a real
parameterless
()ctor or one whose every parameter has a default value. This is the single most common reason a type "doesn't show up". - Implements a recognised contract from the table above.
MyIndicator(IIndicator? parent = null, ISeries<double>? input = null) is callable
with no arguments, so it passes — this is exactly the convention the built-in indicators use. A
constructor with even one required parameter (MyIndicator(int period)) fails the
check and the type is dropped.
The example below puts the rules side by side. The first type satisfies all of them and is discovered; the next two each break exactly one rule and are silently ignored.
C# — good vs badusing TradeStrike.Pipeline.Indicators;
// Discovered: public, sealed (concrete), zero-arg ctor, implements IIndicator via IndicatorBase.
public sealed class MyIndicator : IndicatorBase
{
public MyIndicator() { }
}
// NOT discovered: the only ctor needs a required argument, so it is not zero-arg constructable.
public sealed class Broken : IndicatorBase
{
public Broken(int period) { }
}
// NOT discovered: internal type.
internal sealed class Hidden : IndicatorBase { }
[IndicatorParameter] (indicators) or declared with
IntParameter() / DoubleParameter() (strategies); the host sets them after
construction. See the Indicators and
Strategies parameter chapters.