This page provides a comprehensive overview of the six component types that can be registered in backtest-kit. Each component type serves a distinct role in the trading system architecture: Strategy (signal generation logic), Exchange (market data access), Frame (backtest timeframe generation), Risk (portfolio-level risk management), Sizing (position size calculation), and Walker (multi-strategy comparison).
For detailed implementation guidance on individual component types, see their respective subsections: Strategy Schemas, Exchange Schemas, Frame Schemas, Risk Schemas, Sizing Schemas, and Walker Schemas. For information about registering components via the public API, see Component Registration Functions. For information about the service layer that manages these components, see Schema Services.
backtest-kit uses a schema-based registration system where users define component configurations as TypeScript objects and register them via add* functions. Each component type has:
addStrategy, addExchange, etc.) for schema registrationToolRegistry pattern for schema retrieval by nameThis architecture enables separation of concerns: schemas define "what" components do, while the framework handles "how" they integrate with execution modes (Backtest, Live, Walker).
All component registration follows the same pattern implemented in src/function/add.ts:1-342:
// 1. User calls public registration function
addStrategy(schema); // or addExchange, addFrame, addRisk, addSizing, addWalker
// 2. Framework validates schema
backtest.strategyValidationService.addStrategy(schema.strategyName, schema);
// 3. Framework stores schema in registry
backtest.strategySchemaService.register(schema.strategyName, schema);
The validation step performs:
| Component Type | Schema Interface | Registration Function | Primary Responsibility | Used In Modes |
|---|---|---|---|---|
| Strategy | IStrategySchema |
addStrategy() |
Signal generation logic with getSignal() function |
Backtest, Live, Walker |
| Exchange | IExchangeSchema |
addExchange() |
Market data access via getCandles(), price/quantity formatting |
Backtest, Live, Walker |
| Frame | IFrameSchema |
addFrame() |
Timeframe generation for historical backtesting periods | Backtest, Walker |
| Risk | IRiskSchema |
addRisk() |
Portfolio-level risk validation and position tracking | Backtest, Live, Walker |
| Sizing | ISizingSchema |
addSizing() |
Position size calculation (fixed, Kelly, ATR-based methods) | Backtest, Live, Walker |
| Walker | IWalkerSchema |
addWalker() |
Multi-strategy comparison orchestration | Walker only |
Each component type has a corresponding *SchemaService class that implements the ToolRegistry pattern for name-based storage and retrieval:
Each component type has a *ValidationService class responsible for:
add* functions before schema storage| Service Class | Purpose | Registered Via | Retrieved Via |
|---|---|---|---|
StrategyValidationService |
Validates IStrategySchema fields (strategyName, interval, getSignal) |
addStrategy() |
listStrategies() |
ExchangeValidationService |
Validates IExchangeSchema fields (exchangeName, getCandles, formatPrice, formatQuantity) |
addExchange() |
listExchanges() |
FrameValidationService |
Validates IFrameSchema fields (frameName, interval, startDate, endDate) |
addFrame() |
listFrames() |
RiskValidationService |
Validates IRiskSchema fields (riskName, validations array) |
addRisk() |
listRisks() |
SizingValidationService |
Validates ISizingSchema discriminated union (method-specific fields) |
addSizing() |
listSizings() |
WalkerValidationService |
Validates IWalkerSchema fields (walkerName, strategies array, metric) |
addWalker() |
listWalkers() |
*ConnectionService classes act as memoized factories that create and cache Client* instances. Each component name gets exactly one client instance, created lazily on first use:
The framework uses Symbol-based tokens for dependency injection, defined in src/lib/core/types.ts:1-81:
const schemaServices = {
exchangeSchemaService: Symbol('exchangeSchemaService'),
strategySchemaService: Symbol('strategySchemaService'),
frameSchemaService: Symbol('frameSchemaService'),
walkerSchemaService: Symbol('walkerSchemaService'),
sizingSchemaService: Symbol('sizingSchemaService'),
riskSchemaService: Symbol('riskSchemaService'),
}
const validationServices = {
exchangeValidationService: Symbol('exchangeValidationService'),
strategyValidationService: Symbol('strategyValidationService'),
frameValidationService: Symbol('frameValidationService'),
walkerValidationService: Symbol('walkerValidationService'),
sizingValidationService: Symbol('sizingValidationService'),
riskValidationService: Symbol('riskValidationService'),
}
const connectionServices = {
exchangeConnectionService: Symbol('exchangeConnectionService'),
strategyConnectionService: Symbol('strategyConnectionService'),
frameConnectionService: Symbol('frameConnectionService'),
sizingConnectionService: Symbol('sizingConnectionService'),
riskConnectionService: Symbol('riskConnectionService'),
}
Each component type has a corresponding string-based name type for type-safe registration and retrieval:
| Component | Name Type | Example Value |
|---|---|---|
| Strategy | StrategyName (alias of string) |
"my-strategy" |
| Exchange | ExchangeName (alias of string) |
"binance" |
| Frame | FrameName (alias of string) |
"1d-backtest" |
| Risk | RiskName (alias of string) |
"conservative" |
| Sizing | SizingName (alias of string) |
"fixed-1-percent" |
| Walker | WalkerName (alias of string) |
"llm-optimizer" |
These type aliases provide semantic clarity while maintaining string compatibility for runtime lookup.
Some component types reference other components by name, creating a dependency graph:
The framework provides list* functions defined in src/function/list.ts:1-218 for retrieving all registered component schemas:
| Function | Returns | Purpose |
|---|---|---|
listStrategies() |
Promise<IStrategySchema[]> |
All registered strategies |
listExchanges() |
Promise<IExchangeSchema[]> |
All registered exchanges |
listFrames() |
Promise<IFrameSchema[]> |
All registered frames |
listRisks() |
Promise<IRiskSchema[]> |
All registered risk profiles |
listSizings() |
Promise<ISizingSchema[]> |
All registered sizing configurations |
listWalkers() |
Promise<IWalkerSchema[]> |
All registered walkers |
These functions are useful for:
All component schemas share these common optional fields:
| Field | Type | Purpose |
|---|---|---|
note |
string | undefined |
Optional developer documentation for the component |
callbacks |
Partial<I*Callbacks> | undefined |
Optional lifecycle event handlers for debugging/logging |
The callbacks field provides hooks into component lifecycle events without requiring custom code. Each component type has its own callback interface (e.g., IStrategyCallbacks, IExchangeCallbacks) with event-specific methods.
For each component type, the framework maintains three service classes:
This consistent three-layer pattern (Validation → Schema → Connection → Client) applies to all component types, providing predictable behavior and code organization.