Strategy Contract
Quantago talks to every strategy through the same contract. The goal is to make the execution runtime replaceable without changing how the backtest engine thinks.
Why This Contract Exists
- The platform owns orchestration, order simulation, storage, and charts.
- The strategy owns a single decision for a single candle.
- The payload shape stays stable whether the strategy is native, remote, or WASM.
- Versioning stays explicit so a backtest can be reproduced later.
Core Contract
The platform invokes a strategy with one candle at a time plus the current portfolio snapshot.
Input:
{
"candle": {
"time": "2026-03-10T00:00:00.000Z",
"open": 102.1,
"high": 104.8,
"low": 101.7,
"close": 104.2,
"volume": 183920
},
"history": [
{
"time": "2026-03-09T00:00:00.000Z",
"open": 100.3,
"high": 103.1,
"low": 99.8,
"close": 102.1,
"volume": 164002
}
],
"portfolio": {
"cash": 10000,
"equity": 10000,
"openPosition": null,
"lastTradeAt": null
},
"parameters": {
"timeframe": "1d",
"riskPerTrade": 2,
"maxTradeTime": 8
},
"context": {
"index": 1,
"totalCandles": 252,
"timeframe": "1d",
"strategy": {
"name": "Momentum Strategy",
"version": "1.0.0",
"runtime": "native",
"language": "typescript"
}
}
}
Output:
{
"action": "BUY",
"size": 0.25,
"reason": "Fast EMA crossed above slow EMA",
"metadata": {
"fastEma": 103.4,
"slowEma": 102.8
}
}
Rules:
actionmust beBUY,SELL, orHOLD.sizeis optional. If omitted, the platform uses its own position-sizing rules.metadatais optional and is persisted alongside strategy signals for analysis.- Strategies are expected to behave like pure functions of the supplied input.
Runtime Modes
Tier 1: Native TypeScript
Built-in TypeScript strategies execute inside the backend runtime with no transport overhead.
- Runtime type:
native - Language:
typescript - Best for the fastest local iteration inside the existing codebase
Remote HTTP
Remote strategies run anywhere and expose a single HTTP endpoint that accepts the protocol payload.
- Runtime type:
remote - Language: any
- Current first-class path for Python, local development servers, serverless functions, and managed containers
Request:
POST /signal
Content-Type: application/json
The request body is the full protocol payload shown above.
WASM
WASM is now supported through a simple JSON-over-memory ABI. This tier is intended for portable high-performance strategies compiled from Rust, Go, C, C++, Zig, AssemblyScript, and similar languages.
- Runtime type:
wasm - Artifact source: persisted R2 object key or external
moduleUrl - Planned use: Rust, Go, C++, AssemblyScript, and other languages that compile to WASM
Required exports by default:
memoryalloc(size: i32) -> i32signal(ptr: i32, len: i32) -> i32signal_len() -> i32
Optional export:
dealloc(ptr: i32, len: i32)
Execution model:
- The platform serializes the strategy input as UTF-8 JSON.
- The JSON bytes are written into WASM memory after calling
alloc. signal(ptr, len)returns the pointer to a UTF-8 JSON response.signal_len()returns the output length.- The JSON response must contain the same
action,size,reason, and optionalmetadatashape used by remote HTTP strategies.
Strategy Definition
Each strategy definition contains:
name: user-facing strategy namedescription: short explanation of intentversion: explicit strategy version for reproducibilityruntime: one ofnative,remote,wasmminCandles: minimum data required before a backtest is allowed to runlookbackCandles: how much history the platform should send to the strategydefaultConfig: platform-level default execution settingsparameterSchema: JSON-schema-like form metadata used by the UI and validation layer
Registry Model
Persisted strategies are stored in Postgres and versioned independently of backtests:
strategy_definitions: identity, runtime family, parameter schema, defaults, and ownershipstrategy_versions: immutable versions with runtime config and optional artifact key
Binary artifacts such as .wasm modules are stored in R2 under the STRATEGY_ARTIFACTS binding.
Registry endpoints:
GET /api/strategies
POST /api/strategies
GET /api/strategies/:slug/versions
POST /api/strategies/:slug/versions
The list endpoint returns built-in strategies plus persisted strategies visible to the current user.
Backtest API Usage
Built-in native strategies continue to use the existing request shape:
{
"backtest": {
"symbol": "AAPL",
"exchange": "nasdaq",
"strategy": "Momentum Strategy",
"start_date": "2025-01-01",
"end_date": "2025-12-31",
"initial_balance": 10000,
"parameters": {
"timeframe": "1d"
}
}
}
Remote strategies can be queued without shipping code into the platform runtime by supplying runtime metadata in parameters:
{
"backtest": {
"symbol": "BTC-USD",
"exchange": "crypto",
"strategy": "My Python Momentum",
"start_date": "2025-01-01",
"end_date": "2025-03-01",
"initial_balance": 10000,
"parameters": {
"timeframe": "1h",
"strategyVersion": "0.1.0",
"strategyDescription": "Remote pandas-based strategy",
"strategyMinCandles": 30,
"strategyLookbackCandles": 60,
"strategyRuntime": {
"type": "remote",
"language": "python",
"endpoint": "http://127.0.0.1:8000/signal",
"timeoutMs": 5000,
"lookbackCandles": 60
},
"strategyParameterSchema": {
"type": "object",
"properties": {
"fastWindow": {
"type": "integer",
"default": 12
},
"slowWindow": {
"type": "integer",
"default": 26
}
}
},
"fastWindow": 12,
"slowWindow": 26
}
}
}
Catalog Endpoint
Built-in strategies are exposed from:
GET /api/strategies
The response includes runtime metadata, default config, and parameter schema so the frontend and admin tools can render forms from the backend source of truth instead of duplicating strategy metadata.
For persisted strategies it also includes the registry slug and currently active version.
Validation
Before a full backtest runs, the platform executes a small preflight window against the strategy contract.
- Native strategies must return valid actions.
- Remote strategies must return a valid JSON payload.
- Invalid
actionorsizevalues fail the backtest early.
This is the first enforcement step. Synthetic fixture validation and managed packaging can build on top of the same protocol without changing the contract again.
Python SDK
The repository includes a zero-dependency Python SDK and CLI in sdks/python.
Install locally:
cd sdks/python
python3 -m pip install -e .
Serve a strategy:
quantago serve examples.moving_average_strategy:MovingAverageStrategy --host 127.0.0.1 --port 8000
This exposes:
POST /signalGET /health
The Python SDK maps the JSON payload into typed dataclasses and expects strategies to return Signal.buy(...), Signal.sell(...), or Signal.hold(...).