Sizing Schemas

Sizing schemas define position sizing methods for calculating trade quantities based on account balance, risk parameters, and market conditions. The framework provides three built-in sizing methods via a discriminated union: fixed-percentage, Kelly Criterion, and ATR-based. Strategies reference sizing schemas by name via the sizingName field in IStrategySchema.

For information about how strategies reference sizing, see Strategy Schemas. For risk management that controls whether positions are allowed, see Risk Schemas.


The ISizingSchema type is a discriminated union using the method field as the discriminator. Each method has specific required and optional parameters suited to its calculation algorithm.

Mermaid Diagram


Sizing schemas are registered via addSizing(), which stores them in SizingSchemaService and validates them through SizingValidationService. Strategies reference sizing configurations by name, triggering lazy instantiation of ClientSizing via SizingConnectionService.

Mermaid Diagram


Fixed percentage sizing risks a constant percentage of account balance per trade. Position size is calculated as:

Position Size = (Account Balance × Risk Percentage) / Distance to Stop Loss
Field Type Required Description
sizingName string Yes Unique identifier for this sizing configuration
method "fixed-percentage" Yes Discriminator for fixed percentage method
riskPercentage number Yes Percentage of account balance to risk per trade
maxPositionPercentage number No Maximum position size as percentage of account (default: no limit)
minPositionSize number No Minimum position size (default: no limit)
maxPositionSize number No Maximum position size (default: no limit)
callbacks Partial<ISizingCallbacks> No Optional lifecycle callbacks
note string No Optional developer documentation
addSizing({
sizingName: "conservative",
method: "fixed-percentage",
riskPercentage: 1, // Risk 1% per trade
maxPositionPercentage: 10, // Max 10% of account per position
minPositionSize: 0.001, // Minimum 0.001 BTC
callbacks: {
onCalculate: (quantity, params) => {
console.log(`Position size: ${quantity} for ${params.symbol}`);
},
},
});

Kelly Criterion sizing calculates optimal position size based on win probability and win/loss ratio using the Kelly formula:

Kelly % = (Win Rate × Avg Win) / Avg Loss - (1 - Win Rate)
Position Size = Account Balance × Kelly % × Kelly Multiplier

The kellyMultiplier (default: 0.25) reduces the Kelly percentage to manage risk, as full Kelly can be aggressive.

Field Type Required Description
sizingName string Yes Unique identifier for this sizing configuration
method "kelly-criterion" Yes Discriminator for Kelly Criterion method
kellyMultiplier number No Multiplier to reduce Kelly percentage (default: 0.25)
maxPositionPercentage number No Maximum position size as percentage of account (default: no limit)
minPositionSize number No Minimum position size (default: no limit)
maxPositionSize number No Maximum position size (default: no limit)
callbacks Partial<ISizingCallbacks> No Optional lifecycle callbacks
note string No Optional developer documentation
addSizing({
sizingName: "kelly-quarter",
method: "kelly-criterion",
kellyMultiplier: 0.25, // Use quarter Kelly (conservative)
maxPositionPercentage: 20, // Max 20% of account
callbacks: {
onCalculate: (quantity, params) => {
console.log(`Kelly size: ${quantity} (win rate: ${params.winRate})`);
},
},
});

ATR-based sizing uses Average True Range to dynamically adjust position size based on market volatility. Position size is calculated as:

Position Size = (Account Balance × Risk Percentage) / (ATR × ATR Multiplier)

Higher ATR (more volatile markets) results in smaller position sizes to maintain consistent risk.

Field Type Required Description
sizingName string Yes Unique identifier for this sizing configuration
method "atr-based" Yes Discriminator for ATR-based method
riskPercentage number Yes Percentage of account balance to risk per trade
atrMultiplier number No Multiplier for ATR value (default: 2)
maxPositionPercentage number No Maximum position size as percentage of account (default: no limit)
minPositionSize number No Minimum position size (default: no limit)
maxPositionSize number No Maximum position size (default: no limit)
callbacks Partial<ISizingCallbacks> No Optional lifecycle callbacks
note string No Optional developer documentation
addSizing({
sizingName: "atr-dynamic",
method: "atr-based",
riskPercentage: 2, // Risk 2% per trade
atrMultiplier: 2, // Stop loss at 2× ATR
maxPositionPercentage: 15,
callbacks: {
onCalculate: (quantity, params) => {
console.log(`ATR size: ${quantity} (ATR: ${params.atr})`);
},
},
});

All sizing methods support optional position size constraints to enforce portfolio-level limits:

Mermaid Diagram

Parameter Type Purpose
maxPositionPercentage number Maximum position as % of account balance (prevents over-concentration)
minPositionSize number Minimum position size in base asset (prevents dust trades)
maxPositionSize number Maximum position size in base asset (absolute limit)

When calculating position size, ClientSizing receives method-specific parameters through the discriminated union ISizingCalculateParams:

Mermaid Diagram


The ISizingCallbacks interface provides lifecycle hooks for monitoring sizing calculations:

interface ISizingCallbacks {
onCalculate: (
quantity: number,
params: ISizingCalculateParams
) => void;
}

Mermaid Diagram


Strategies reference sizing schemas via the optional sizingName field. If omitted, no position sizing is applied (strategy must manage quantities manually).

addStrategy({
strategyName: "my-strategy",
interval: "5m",
sizingName: "conservative", // References sizing schema
getSignal: async (symbol) => ({
position: "long",
priceTakeProfit: 51000,
priceStopLoss: 49000,
minuteEstimatedTime: 60,
// No quantity specified - calculated by sizing
}),
});

Mermaid Diagram


The sizing system follows the standard service layer pattern with Schema, Validation, Connection, and Client services:

Service File Pattern Responsibility
SizingSchemaService services/schema/SizingSchemaService Stores sizing schemas in ToolRegistry by sizingName
SizingValidationService services/validation/SizingValidationService Validates sizing schemas during registration
SizingConnectionService services/connection/SizingConnectionService Memoizes ClientSizing instances (one per sizingName)
ClientSizing classes/ClientSizing Executes sizing algorithms and applies constraints

SizingConnectionService memoizes ClientSizing instances to ensure singleton behavior per sizing name:

Mermaid Diagram


The listSizings() function retrieves all registered sizing schemas for inspection or debugging:

import { listSizings } from "backtest-kit";

const sizings = await listSizings();

sizings.forEach(schema => {
console.log(`${schema.sizingName}: ${schema.method}`);

if (schema.method === "fixed-percentage") {
console.log(` Risk: ${schema.riskPercentage}%`);
} else if (schema.method === "kelly-criterion") {
console.log(` Multiplier: ${schema.kellyMultiplier || 0.25}`);
} else if (schema.method === "atr-based") {
console.log(` Risk: ${schema.riskPercentage}%, ATR: ${schema.atrMultiplier || 2}×`);
}
});

addSizing({
sizingName: "conservative-fixed",
note: "Low risk fixed percentage for stable strategies",
method: "fixed-percentage",
riskPercentage: 1,
maxPositionPercentage: 10,
minPositionSize: 0.001,
maxPositionSize: 1,
});
addSizing({
sizingName: "aggressive-kelly",
note: "Half Kelly for high-conviction trades",
method: "kelly-criterion",
kellyMultiplier: 0.5, // Half Kelly (more aggressive)
maxPositionPercentage: 25,
callbacks: {
onCalculate: (quantity, params) => {
if (params.method === "kelly-criterion") {
console.log(`Win rate: ${params.winRate}, Size: ${quantity}`);
}
},
},
});
addSizing({
sizingName: "volatility-adjusted",
note: "ATR-based sizing for volatile markets",
method: "atr-based",
riskPercentage: 2,
atrMultiplier: 1.5, // Tighter stop loss
maxPositionPercentage: 15,
minPositionSize: 0.001,
callbacks: {
onCalculate: (quantity, params) => {
if (params.method === "atr-based") {
console.log(`ATR: ${params.atr}, Size: ${quantity}`);
}
},
},
});