Create a manifest-backed trading algorithm
Build against the public SDK, handle lifecycle and warmup correctly and validate exact instrument routes.
Reviewed 2026-10-24 · Windows & macOS
On this page
Start with the scaffold#
Create an external plugin through AMM's New Plugin scaffold with kind algorithm or both. Choose an active watched root and a stable plugin ID. Keep user algorithms outside the AMM application's source tree. The project consumes the published binary AMM.PluginSdk and AMM.Core contract; indicator helpers additionally require the compatible indicator-library dependency supplied by your development package/workspace.
The generated project targets .NET 10 and publishes a manifest when built. Follow its README to output the manifest/DLL into a watched distribution directory. A source-only project is not available in the algorithm selector. Query /api/plugins and /api/algorithms after build to confirm the exact loaded plugin and metadata before running research.
Begin with a no-order lifecycle probe#
This minimal algorithm observes completed primary bars without placing trades. It is suitable for checking callback identity and Output before introducing execution logic:
using AMM.Algorithms;
using AMM.Core.Models;
public sealed class ObservationProbeAlgorithm : AlgorithmBase
{
private int _bars;
public override int? RecommendedIndicatorHistoryDepth => 2;
protected override void OnInit()
{
_bars = 0;
}
protected override void OnData(Bar bar)
{
if (bar.Symbol != PrimaryInstrument)
return;
if (++_bars == 1)
Output($"First completed primary bar at {CurrentTime:O}");
}
}
Use the scaffold's class/namespace organization and replace its sample deliberately. Build, choose this discovered algorithm, select one exact instrument and a short stored range, then inspect Output. The probe can report a warmup bar because it deliberately submits no orders. This sample establishes callback wiring only; it makes no trading or profitability claim.
Lifecycle and data modes#
OnInit resolves configured instruments and registers indicators/timers. OnStart performs run-start work. OnData(Bar) handles completed bars. OnTick runs only when tick callbacks are enabled and the selected source supplies ticks. OnData(DataSlice) is a Standard-mode multi-timeframe callback; do not depend on it in cTraderParity mode. OnOrder and OnTrade handle execution events; OnEnd and error handling finish/diagnose the run.
Warmup still feeds bars/indicators but rejects entries. Guard strategy state where needed and reset day/session tracking in OnWarmupComplete so pre-start observations do not become live decisions accidentally. Time-based flattening should use the supported timer mechanism when it must fire across data gaps; do not assume a bar arrives exactly at a session close.
Filter both symbol and timeframe in multi-instrument/multi-timeframe logic. The event being delivered is not automatically the intended decision bar. Hold completed higher-timeframe values causally; never read a forming future close from a later aggregation boundary.
Indicators, readiness and retained history#
SDK helpers include SMA, EMA, first-close EMA, RSI, MACD, CCI, TSI, RVI, ATR, Bollinger bands, SAR and candlestick patterns. Not every concrete indicator has a helper; Stochastic, WMA, StandardDeviation, DonchianChannel, PivotPoints and the manual MACD variant must be constructed/updated explicitly when used that way.
HasValue means a meaningful current value exists, sometimes a partial warmup value. IsReady means full warmup is complete. HasLast(n) establishes a real retained prior sample; Last(n) can otherwise return a zero placeholder. MACD's base HasValue behavior needs particular care: use its stability/readiness contract rather than assuming bar one is warmed.
Declare RecommendedIndicatorHistoryDepth as the deepest indicator history index read plus one, including parameter-driven loops. This is retained indicator history, not warmup length or your own bar buffers. The run's actual setting remains authoritative. Use raw double accessors for parity-sensitive crossover comparisons where the library supplies them.
Exact instruments and account graphs#
Resolve configured instruments through exact IDs. DeclareInstruments(parameters) runs before validation and must be deterministic, pure and free of I/O or wall-clock choices. The host binds parameter defaults, resolves exact contracts and checks the graph. Preview declared instruments in the research form before Run.
Runtime AddInstrument prepares the new route at the current event time. History(symbol,count) returns completed bars ending no later than that callback; it does not advance time. Use returned history to seed your own indicators deliberately. Repeating the identical declaration is safe; changing a declaration under an existing ID fails.
For live graphs, additional broker accounts and data feeds must be configured and reviewed before launch. Runtime additions cannot introduce an unapproved account. Account/reporting/profit currency conversion needs explicit routes; future quotes cannot value earlier fills. A provider alias changes lookup spelling, not price basis or economic equivalence.
Orders and fixed sizing#
Use explicit quantity units and exact account grids. Fixed-lot helpers suit FX/CFDs, shares suit equities, contracts suit futures and fractional base units suit spot crypto. Percentage holdings compound exposure and are not equivalent to fixed lots. Keep lots, units, contracts, risk percentage, leverage and allocation out of ordinary strategy parameter optimization; portfolio sizing optimization is a separate explicitly chosen task.
Respect venue order/protection capabilities. A common SDK method does not make an unsupported IB bracket or crypto short valid. Inspect order/trade events and broker readback instead of treating submission as a fill. Handle rejections and uncertain outcomes without blindly duplicating orders.
Validate and update#
Start with a short no-order probe, then a deterministic research case with frozen parameters, exact data basis, costs and sizing. Test warmup, missing data, sessions/DST, partial exits, recovery and contract rolls where relevant. cTrader comparison requires matched logic, data family and fixed-size full-range runs; matching trade counts alone does not prove full economic parity.
Rebuild the payload, verify its loaded hash and start a new run after code changes. Existing instances keep old code. For debugging, attach to the API/worker process actually executing the plugin and verify its shadow-copied DLL/PDB. See Plugins and Indicator authoring.