This page documents the public API functions for registering and retrieving component schemas in the framework. These functions enable users to define strategies, exchanges, frames, risk profiles, sizing methods, and walkers that the framework uses during execution.
For conceptual information about component registration, see Component Registration. For detailed schema interfaces and their properties, see Component Types.
The framework provides six registration functions (add*) and corresponding listing functions (list*) that manage component schemas:
| Registration Function | Listing Function | Schema Type | Purpose |
|---|---|---|---|
addStrategy() |
listStrategies() |
IStrategySchema |
Register signal generation logic |
addExchange() |
listExchanges() |
IExchangeSchema |
Register market data source |
addFrame() |
listFrames() |
IFrameSchema |
Register backtest timeframe |
addRisk() |
listRisks() |
IRiskSchema |
Register risk management rules |
addSizing() |
listSizings() |
ISizingSchema |
Register position sizing method |
addWalker() |
listWalkers() |
IWalkerSchema |
Register multi-strategy comparison |
All registration functions perform two operations:
*ValidationService for runtime validation checks*SchemaService for retrieval by nameRegistration Flow:
addStrategy(), addExchange(), etc. with schema objectbacktest.loggerService*ValidationService for runtime validation*SchemaService for retrieval by name*ConnectionService retrieves schema from *SchemaServiceClient* class instantiated with retrieved schemaRegisters a trading strategy schema defining signal generation logic, interval throttling, and lifecycle callbacks.
Signature:
function addStrategy(strategySchema: IStrategySchema): void
Parameters:
strategySchema.strategyName (string): Unique strategy identifierstrategySchema.interval (SignalInterval): Throttling interval ("1m" | "3m" | "5m" | "15m" | "30m" | "1h")strategySchema.getSignal (function): Async function returning ISignalDto | nullstrategySchema.riskName (string, optional): Risk profile to usestrategySchema.callbacks (object, optional): Lifecycle callbacks (onOpen, onClose, onTick, etc.)strategySchema.note (string, optional): Developer documentationFor detailed IStrategySchema documentation, see Strategy Schemas.
Example:
addStrategy({
strategyName: "momentum-long",
interval: "5m",
riskName: "conservative",
getSignal: async (symbol) => {
const candles = await getCandles(symbol, "5m", 20);
// Signal generation logic...
return {
position: "long",
priceTakeProfit: 51000,
priceStopLoss: 49000,
minuteEstimatedTime: 60,
};
},
callbacks: {
onOpen: (symbol, signal, price, backtest) => {
console.log(`Signal opened: ${signal.id}`);
},
},
});
Internal Flow:
backtest.loggerService.info()StrategyValidationService via addStrategy(name, schema)StrategySchemaService via register(name, schema)Returns array of all registered strategy schemas.
Signature:
async function listStrategies(): Promise<IStrategySchema[]>
Returns: Array of strategy schemas with all properties (strategyName, interval, getSignal, callbacks, etc.)
Example:
const strategies = await listStrategies();
console.log(strategies.map(s => s.strategyName));
// ["momentum-long", "mean-reversion", "breakout-v2"]
Internal Flow: Delegates to backtest.strategyValidationService.list() which returns all registered schemas.
Registers an exchange data source providing candle data, price formatting, and quantity formatting.
Signature:
function addExchange(exchangeSchema: IExchangeSchema): void
Parameters:
exchangeSchema.exchangeName (string): Unique exchange identifierexchangeSchema.getCandles (function): Fetch historical candle dataexchangeSchema.formatPrice (function): Format price for exchange precisionexchangeSchema.formatQuantity (function): Format quantity for exchange precisionexchangeSchema.callbacks (object, optional): Lifecycle callbacks (onCandleData)exchangeSchema.note (string, optional): Developer documentationFor detailed IExchangeSchema documentation, see Exchange Schemas.
Example:
addExchange({
exchangeName: "binance-spot",
getCandles: async (symbol, interval, since, limit) => {
// Fetch from Binance API or database
return [{
timestamp: Date.now(),
open: 50000,
high: 51000,
low: 49000,
close: 50500,
volume: 100.5,
}];
},
formatPrice: async (symbol, price) => {
return price.toFixed(2);
},
formatQuantity: async (symbol, quantity) => {
return quantity.toFixed(8);
},
});
Internal Flow:
backtest.loggerService.info()ExchangeValidationService via addExchange(name, schema)ExchangeSchemaService via register(name, schema)Returns array of all registered exchange schemas.
Signature:
async function listExchanges(): Promise<IExchangeSchema[]>
Returns: Array of exchange schemas with all properties (exchangeName, getCandles, formatPrice, formatQuantity, callbacks)
Example:
const exchanges = await listExchanges();
exchanges.forEach(ex => {
console.log(`Exchange: ${ex.exchangeName}`);
if (ex.note) console.log(` Note: ${ex.note}`);
});
Registers a timeframe generator for backtesting, defining start/end dates and interval for timestamp generation.
Signature:
function addFrame(frameSchema: IFrameSchema): void
Parameters:
frameSchema.frameName (string): Unique frame identifierframeSchema.interval (FrameInterval): Timeframe interval ("1m" | "3m" | "5m" | "15m" | "30m" | "1h" | "2h" | "4h" | "6h" | "8h" | "12h" | "1d" | "3d")frameSchema.startDate (Date): Backtest period start (inclusive)frameSchema.endDate (Date): Backtest period end (inclusive)frameSchema.callbacks (object, optional): Lifecycle callbacks (onTimeframe)frameSchema.note (string, optional): Developer documentationFor detailed IFrameSchema documentation, see Frame Schemas.
Example:
addFrame({
frameName: "jan-2024",
interval: "1m",
startDate: new Date("2024-01-01T00:00:00Z"),
endDate: new Date("2024-01-31T23:59:59Z"),
callbacks: {
onTimeframe: (timeframe, start, end, interval) => {
console.log(`Generated ${timeframe.length} timestamps`);
},
},
});
Internal Flow:
backtest.loggerService.info()FrameValidationService via addFrame(name, schema)FrameSchemaService via register(name, schema)Returns array of all registered frame schemas.
Signature:
async function listFrames(): Promise<IFrameSchema[]>
Returns: Array of frame schemas with all properties (frameName, interval, startDate, endDate, callbacks)
Example:
const frames = await listFrames();
frames.forEach(frame => {
console.log(`Frame: ${frame.frameName}`);
console.log(` Period: ${frame.startDate} to ${frame.endDate}`);
console.log(` Interval: ${frame.interval}`);
});
Registers a risk management configuration with custom validation functions and portfolio-level position limits.
Signature:
function addRisk(riskSchema: IRiskSchema): void
Parameters:
riskSchema.riskName (string): Unique risk profile identifierriskSchema.validations (array): Custom validation functions with access to active positionsriskSchema.callbacks (object, optional): Lifecycle callbacks (onRejected, onAllowed)riskSchema.note (string, optional): Developer documentationFor detailed IRiskSchema documentation and validation patterns, see Risk Schemas.
Example:
addRisk({
riskName: "advanced",
validations: [
{
validate: async ({ activePositionCount, activePositions }) => {
// Check portfolio-level constraints
if (activePositionCount >= 10) {
throw new Error("Max 10 concurrent positions");
}
// Check correlation between active positions
const symbols = activePositions.map(p => p.signal.symbol);
if (hasHighCorrelation(symbols)) {
throw new Error("High correlation between positions");
}
},
note: "Limit concurrent positions and correlation",
},
async ({ currentPrice, symbol }) => {
// Quick validation without note
if (currentPrice < 1000) {
throw new Error("Price too low for this risk profile");
}
},
],
callbacks: {
onRejected: (symbol, params) => {
console.log(`Signal rejected for ${symbol}`);
},
},
});
Internal Flow:
backtest.loggerService.info()RiskValidationService via addRisk(name, schema)RiskSchemaService via register(name, schema)Cross-Strategy Risk Sharing: Multiple strategies can reference the same riskName. The framework creates a single ClientRisk instance per riskName, enabling portfolio-level risk management across strategies.
Returns array of all registered risk schemas.
Signature:
async function listRisks(): Promise<IRiskSchema[]>
Returns: Array of risk schemas with all properties (riskName, validations, callbacks)
Example:
const risks = await listRisks();
risks.forEach(risk => {
console.log(`Risk profile: ${risk.riskName}`);
console.log(` Validations: ${risk.validations.length}`);
});
Registers a position sizing configuration using fixed-percentage, Kelly Criterion, or ATR-based methods.
Signature:
function addSizing(sizingSchema: ISizingSchema): void
Parameters (discriminated union by method):
sizingSchema.sizingName (string): Unique sizing identifiersizingSchema.method (string): "fixed-percentage" | "kelly-criterion" | "atr-based"sizingSchema.riskPercentage (number, for fixed/ATR): Risk % per tradesizingSchema.kellyMultiplier (number, for Kelly): Fraction of Kelly to use (default: 0.25)sizingSchema.atrMultiplier (number, for ATR): ATR multiplier for stop distance (default: 2)sizingSchema.maxPositionPercentage (number, optional): Max position as % of accountsizingSchema.minPositionSize (number, optional): Minimum position sizesizingSchema.maxPositionSize (number, optional): Maximum position sizesizingSchema.callbacks (object, optional): Lifecycle callbacks (onCalculate)sizingSchema.note (string, optional): Developer documentationFor detailed ISizingSchema documentation, see Sizing Schemas.
Example:
// Fixed percentage sizing
addSizing({
sizingName: "conservative-fixed",
method: "fixed-percentage",
riskPercentage: 1,
maxPositionPercentage: 10,
note: "Risk 1% per trade, max 10% per position",
});
// Kelly Criterion sizing
addSizing({
sizingName: "kelly-quarter",
method: "kelly-criterion",
kellyMultiplier: 0.25,
maxPositionPercentage: 20,
note: "Quarter-Kelly with 20% position limit",
});
// ATR-based dynamic sizing
addSizing({
sizingName: "atr-dynamic",
method: "atr-based",
riskPercentage: 2,
atrMultiplier: 2,
callbacks: {
onCalculate: (quantity, params) => {
console.log(`Calculated ${quantity} for ${params.symbol}`);
},
},
});
Internal Flow:
backtest.loggerService.info()SizingValidationService via addSizing(name, schema)SizingSchemaService via register(name, schema)Returns array of all registered sizing schemas.
Signature:
async function listSizings(): Promise<ISizingSchema[]>
Returns: Array of sizing schemas with all properties (method-specific parameters)
Example:
const sizings = await listSizings();
sizings.forEach(sizing => {
console.log(`Sizing: ${sizing.sizingName} (${sizing.method})`);
if (sizing.method === "fixed-percentage") {
console.log(` Risk: ${sizing.riskPercentage}%`);
}
});
Registers a walker for multi-strategy comparison, executing backtests on multiple strategies and selecting the best by a specified metric.
Signature:
function addWalker(walkerSchema: IWalkerSchema): void
Parameters:
walkerSchema.walkerName (string): Unique walker identifierwalkerSchema.exchangeName (string): Exchange to use for all strategieswalkerSchema.frameName (string): Frame to use for all strategieswalkerSchema.strategies (array): Array of strategy names to comparewalkerSchema.metric (WalkerMetric): Optimization metric ("sharpeRatio" | "annualizedSharpeRatio" | "winRate" | "totalPnl" | "certaintyRatio" | "avgPnl" | "expectedYearlyReturns")walkerSchema.callbacks (object, optional): Lifecycle callbacks (onStrategyComplete, onComplete)walkerSchema.note (string, optional): Developer documentationFor detailed IWalkerSchema documentation, see Walker Schemas.
Example:
addWalker({
walkerName: "llm-prompt-optimizer",
exchangeName: "binance-spot",
frameName: "jan-2024",
strategies: [
"momentum-long-v1",
"momentum-long-v2",
"momentum-long-v3",
],
metric: "sharpeRatio",
callbacks: {
onStrategyComplete: (strategyName, symbol, stats, metric) => {
console.log(`${strategyName}: Sharpe=${metric}`);
},
onComplete: (results) => {
console.log(`Best: ${results.bestStrategy}`);
},
},
});
Internal Flow:
backtest.loggerService.info()WalkerValidationService via addWalker(name, schema)WalkerSchemaService via register(name, schema)Returns array of all registered walker schemas.
Signature:
async function listWalkers(): Promise<IWalkerSchema[]>
Returns: Array of walker schemas with all properties (walkerName, strategies, metric, exchangeName, frameName)
Example:
const walkers = await listWalkers();
walkers.forEach(walker => {
console.log(`Walker: ${walker.walkerName}`);
console.log(` Strategies: ${walker.strategies.join(", ")}`);
console.log(` Metric: ${walker.metric}`);
});
All *ValidationService classes follow the same pattern for schema validation and memoization:
ValidationService Responsibilities:
Map<name, schema> for validationlist() methodKey Methods:
add*(name: string, schema: I*Schema): void - Store schema for validationlist(): Promise<I*Schema[]> - Return all registered schemasvalidate*(name: string): boolean - Runtime check if schema exists (memoized)All *SchemaService classes follow the ToolRegistry pattern for schema storage and retrieval:
SchemaService Responsibilities:
ToolRegistry (Map-based) by nameget(name)has(name)Key Methods:
register(name: string, schema: I*Schema): void - Store schemaget(name: string): I*Schema - Retrieve schema (throws if not found)has(name: string): boolean - Check if schema existsToolRegistry Pattern: Each SchemaService extends a base ToolRegistry class that provides generic Map<name, schema> storage with get/has/register methods.
The framework performs validation at two distinct phases:
| Validation Phase | When | Where | What |
|---|---|---|---|
| Registration | add*() call |
*ValidationService.add*() |
Stores schema for future validation |
| Runtime | Execution start | *ValidationService.validate*() |
Checks schema exists (memoized) |
Registration Phase (src/function/add.ts:54-57):
// Store schema in ValidationService
backtest.strategyValidationService.addStrategy(
strategySchema.strategyName,
strategySchema
);
Runtime Phase (used by *GlobalService classes):
// Check if strategy was registered (memoized)
const isValid = await strategyValidationService.validateStrategy(strategyName);
if (!isValid) {
throw new Error(`Strategy ${strategyName} not registered`);
}
Memoization: Validation results are cached using memoize from functools-kit to avoid repeated schema lookups during execution.
Registration functions do not throw errors during the add*() call itself. Instead:
Example Error Flow:
// This succeeds (no validation yet)
addStrategy({
strategyName: "my-strategy",
interval: "5m",
riskName: "non-existent-risk", // <-- Error not caught here
getSignal: async (symbol) => ({ /* ... */ }),
});
// Error thrown here when ClientStrategy tries to get ClientRisk
await Backtest.run("BTCUSDT", {
strategyName: "my-strategy",
exchangeName: "binance",
frameName: "jan-2024",
});
// Error: Risk profile "non-existent-risk" not registered
This design allows: