Logic Services orchestrate the execution of backtest and live trading operations using async generators. They coordinate between Strategy, Exchange, and Frame services while managing execution context propagation. Logic Services form the top layer of the Service Orchestration hierarchy and are the primary entry point for execution flows initiated by the Public API.
For configuration and registration of strategies, exchanges, and frames, see Configuration Functions. For the business logic implementations that Logic Services orchestrate, see Core Business Logic.
Logic Services follow a two-tier pattern with distinct responsibilities:
| Layer | Classes | Responsibility |
|---|---|---|
| Public | BacktestLogicPublicService, LiveLogicPublicService |
Context propagation, method routing, public API exposure |
| Private | BacktestLogicPrivateService, LiveLogicPrivateService |
Core orchestration logic, async generator implementation, service coordination |
Logic Services use a two-tier pattern to separate context management from execution logic:
Public Services:
MethodContextService.runAsyncIterator()IMethodContext containing strategyName, exchangeName, frameNamePrivate Services:
Core orchestration service for backtest execution using memory-efficient async generators.
| Property | Type | Purpose |
|---|---|---|
loggerService |
LoggerService |
Logs orchestration events (tick results, signal state changes) |
strategyGlobalService |
StrategyGlobalService |
Executes tick() and backtest() operations |
exchangeGlobalService |
ExchangeGlobalService |
Fetches future candles for fast-forward simulation |
frameGlobalService |
FrameGlobalService |
Retrieves timeframe array for iteration |
methodContextService |
TMethodContextService |
Accesses current execution context for event emission |
run(symbol: string): AsyncGenerator<IStrategyTickResultClosed>
Iterates through historical timeframes, executing strategy ticks and fast-forwarding through opened signals. Handles both immediate and scheduled signals.
performance.now() src/lib/services/logic/private/BacktestLogicPrivateService.ts:64FrameGlobalService src/lib/services/logic/private/BacktestLogicPrivateService.ts:66-69tick() for each timestamp to check for signal generation src/lib/services/logic/private/BacktestLogicPrivateService.ts:91Scheduled Signal Processing:
When action === "scheduled", the service handles signals waiting for price activation:
CC_SCHEDULE_AWAIT_MINUTES + minuteEstimatedTime + 1 candles to cover both the activation wait period and signal lifetime src/lib/services/logic/private/BacktestLogicPrivateService.ts:109-119backtest() method handles detecting if/when priceOpen is reached src/lib/services/logic/private/BacktestLogicPrivateService.ts:138-143priceOpen within CC_SCHEDULE_AWAIT_MINUTES, signal is cancelled src/lib/services/logic/private/BacktestLogicPrivateService.ts:112IStrategyTickResultClosed or IStrategyTickResultCancelled src/lib/services/logic/private/BacktestLogicPrivateService.ts:182Opened Signal Processing:
When action === "opened", the service handles immediately active signals:
getNextCandles(symbol, "1m", minuteEstimatedTime) to retrieve future candles src/lib/services/logic/private/BacktestLogicPrivateService.ts:197-203backtest(candles) to simulate signal lifecycle without iterating each timestamp src/lib/services/logic/private/BacktestLogicPrivateService.ts:216-221IStrategyTickResultClosed with PnL calculation src/lib/services/logic/private/BacktestLogicPrivateService.ts:252The service emits granular performance metrics through performanceEmitter:
| Metric Type | Emitted When | Data Tracked |
|---|---|---|
backtest_timeframe |
After each timeframe iteration | Duration per timeframe tick |
backtest_signal |
After each signal closes | Duration from signal open to close |
backtest_total |
At generator completion | Total backtest execution time |
Each metric includes timestamp, previousTimestamp, strategyName, exchangeName, symbol, and backtest: true flag src/lib/services/logic/private/BacktestLogicPrivateService.ts:161-172,232-242,256-268,286-298.
Core orchestration service for live trading execution with infinite async generation.
| Property | Type | Purpose |
|---|---|---|
loggerService |
LoggerService |
Logs tick results and signal state changes |
strategyGlobalService |
StrategyGlobalService |
Executes tick() to monitor signals in real-time |
methodContextService |
TMethodContextService |
Accesses current execution context for performance metrics |
| Constant | Value | Purpose |
|---|---|---|
TICK_TTL |
1 * 60 * 1_000 + 1 |
1 minute + 1ms sleep duration between ticks |
run(symbol: string): AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed | IStrategyTickResultCancelled>
Infinite generator that monitors signal state every minute and yields opened/closed/cancelled events.
previousEventTimestamp for performance tracking src/lib/services/logic/private/LiveLogicPrivateService.ts:65while(true) loop for continuous monitoring src/lib/services/logic/private/LiveLogicPrivateService.ts:67performance.now() src/lib/services/logic/private/LiveLogicPrivateService.ts:68new Date() on each iteration src/lib/services/logic/private/LiveLogicPrivateService.ts:69tick(symbol, when, false) to check signal status src/lib/services/logic/private/LiveLogicPrivateService.ts:71live_tick performance metric after each tick src/lib/services/logic/private/LiveLogicPrivateService.ts:79-91idle, active, and scheduled states (does not yield) src/lib/services/logic/private/LiveLogicPrivateService.ts:93-106opened, closed, and cancelled results src/lib/services/logic/private/LiveLogicPrivateService.ts:109TICK_TTL (1 minute + 1ms) between iterations src/lib/services/logic/private/LiveLogicPrivateService.ts:94,99,104,111Live trading filters intermediate states to reduce noise:
| State | Yielded | Reason |
|---|---|---|
idle |
No | No active signal - nothing to report src/lib/services/logic/private/LiveLogicPrivateService.ts:98-101 |
scheduled |
No | Signal awaiting price activation - monitored internally src/lib/services/logic/private/LiveLogicPrivateService.ts:103-106 |
opened |
Yes | New signal created - requires notification src/lib/services/logic/private/LiveLogicPrivateService.ts:109 |
active |
No | Signal monitoring in progress - no state change src/lib/services/logic/private/LiveLogicPrivateService.ts:93-96 |
closed |
Yes | Signal completed - PnL finalized src/lib/services/logic/private/LiveLogicPrivateService.ts:109 |
cancelled |
Yes | Scheduled signal timeout/SL hit - requires notification src/lib/services/logic/private/LiveLogicPrivateService.ts:109 |
The service emits live_tick performance metrics through performanceEmitter after each tick cycle, tracking the duration of signal status checks. Each metric includes timestamp, previousTimestamp, strategyName, exchangeName, symbol, and backtest: false flag src/lib/services/logic/private/LiveLogicPrivateService.ts:79-91.
Public-facing wrapper for BacktestLogicPrivateService that manages context propagation.
| Property | Type | Purpose |
|---|---|---|
loggerService |
LoggerService |
Logs public service invocations |
backtestLogicPrivateService |
BacktestLogicPrivateService |
Core orchestration logic |
run(symbol: string, context: {strategyName, exchangeName, frameName}): AsyncGenerator
Wraps private service generator with method context injection.
// Method signature from source
public run = (
symbol: string,
context: {
strategyName: string;
exchangeName: string;
frameName: string;
}
)
Implementation Pattern:
MethodContextService.runAsyncIterator() with generator and context src/lib/services/logic/public/BacktestLogicPublicService.ts:58-65| Field | Type | Description |
|---|---|---|
strategyName |
string |
Identifies which strategy schema to use |
exchangeName |
string |
Identifies which exchange schema to use |
frameName |
string |
Identifies which frame schema to use for timeframe generation |
Public-facing wrapper for LiveLogicPrivateService that manages context propagation.
| Property | Type | Purpose |
|---|---|---|
loggerService |
LoggerService |
Logs public service invocations |
liveLogicPrivateService |
LiveLogicPrivateService |
Core orchestration logic |
run(symbol: string, context: {strategyName, exchangeName}): AsyncGenerator
Wraps private service generator with method context injection.
// Method signature from source
public run = (
symbol: string,
context: {
strategyName: string;
exchangeName: string;
}
)
Implementation Pattern:
MethodContextService.runAsyncIterator() with generator and context src/lib/services/logic/public/LiveLogicPublicService.ts:66-73| Field | Type | Description |
|---|---|---|
strategyName |
string |
Identifies which strategy schema to use |
exchangeName |
string |
Identifies which exchange schema to use |
frameName |
string |
Empty string for live mode (no frame needed) src/lib/services/logic/public/LiveLogicPublicService.ts:71 |
Logic Services use async generators (async function*) for memory-efficient streaming execution.
| Aspect | Backtest | Live |
|---|---|---|
| Completion | Finite (completes when timeframe exhausted) | Infinite (never completes) |
| Iteration | Array of historical timestamps | Real-time new Date() every minute |
| Yielded Types | IStrategyTickResultClosed | IStrategyTickResultCancelled |
IStrategyTickResultOpened | IStrategyTickResultClosed | IStrategyTickResultCancelled |
| Memory Usage | O(1) - streams results without accumulation | O(1) - no result accumulation |
| Early Termination | Supported via break in consumer |
Supported via break in consumer |
| Progress Tracking | progressEmitter with percentage completion |
performanceEmitter with tick duration |
| Performance Metrics | backtest_total, backtest_timeframe, backtest_signal |
live_tick |
Backtest with Early Termination:
// Example from documentation
for await (const result of backtestLogic.run("BTCUSDT")) {
console.log(result.closeReason, result.pnl.pnlPercentage);
if (result.pnl.pnlPercentage < -10) break; // Early termination
}
Live with Infinite Loop:
// Example from documentation
for await (const result of liveLogic.run("BTCUSDT")) {
if (result.action === "opened") {
console.log("New signal:", result.signal.id);
}
if (result.action === "closed") {
console.log("PNL:", result.pnl.pnlPercentage);
}
// Infinite loop - will never complete
}
Logic Services coordinate multiple service types through dependency injection.
BacktestLogicPrivateService:
LoggerService - Event logging src/lib/services/logic/private/BacktestLogicPrivateService.ts:23StrategyGlobalService - Signal tick and backtest execution src/lib/services/logic/private/BacktestLogicPrivateService.ts:24-26ExchangeGlobalService - Future candle fetching src/lib/services/logic/private/BacktestLogicPrivateService.ts:27-29FrameGlobalService - Timeframe array retrieval src/lib/services/logic/private/BacktestLogicPrivateService.ts:30-32LiveLogicPrivateService:
LoggerService - Event logging src/lib/services/logic/private/LiveLogicPrivateService.ts:30StrategyGlobalService - Signal tick execution src/lib/services/logic/private/LiveLogicPrivateService.ts:31-33TMethodContextService - Context access for performance metrics src/lib/services/logic/private/LiveLogicPrivateService.ts:34-36BacktestLogicPublicService:
LoggerService - Invocation logging src/lib/services/logic/public/BacktestLogicPublicService.ts:32BacktestLogicPrivateService - Orchestration delegation src/lib/services/logic/public/BacktestLogicPublicService.ts:33-34LiveLogicPublicService:
LoggerService - Invocation logging src/lib/services/logic/public/LiveLogicPublicService.ts:39LiveLogicPrivateService - Orchestration delegation src/lib/services/logic/public/LiveLogicPublicService.ts:40-42Logic Services use MethodContextService.runAsyncIterator() to inject context into async generators.
MethodContextService.runAsyncIterator() wraps generator with scoped context src/lib/services/logic/public/BacktestLogicPublicService.ts:58-65| Field | Type | Purpose |
|---|---|---|
strategyName |
StrategyName |
Routes to correct strategy schema |
exchangeName |
ExchangeName |
Routes to correct exchange schema |
frameName |
FrameName |
Routes to correct frame schema (empty for live) |
Logic Services delegate business logic execution to Global Services that inject ExecutionContextService.
BacktestLogicPrivateService calls:
| Global Service | Method | Purpose | Line Reference |
|---|---|---|---|
FrameGlobalService |
getTimeframe(symbol) |
Retrieve historical timestamp array | src/lib/services/logic/private/BacktestLogicPrivateService.ts:53 |
StrategyGlobalService |
tick(symbol, when, true) |
Check for signal generation at timestamp | src/lib/services/logic/private/BacktestLogicPrivateService.ts:60 |
ExchangeGlobalService |
getNextCandles(symbol, "1m", limit) |
Fetch future candles for simulation | src/lib/services/logic/private/BacktestLogicPrivateService.ts:73-79 |
StrategyGlobalService |
backtest(symbol, candles, when, true) |
Fast-forward simulate signal lifecycle | src/lib/services/logic/private/BacktestLogicPrivateService.ts:92-97 |
LiveLogicPrivateService calls:
| Global Service | Method | Purpose | Line Reference |
|---|---|---|---|
StrategyGlobalService |
tick(symbol, when, false) |
Monitor signal status in real-time | src/lib/services/logic/private/LiveLogicPrivateService.ts:61 |
Core orchestration service for multi-strategy comparison execution.
WalkerLogicPrivateService orchestrates the sequential execution of multiple backtest runs to compare strategy performance. It iterates through a list of strategies, executes a full backtest for each, tracks the best-performing strategy, and emits progress updates.
| Property | Type | Purpose |
|---|---|---|
loggerService |
LoggerService |
Logs walker orchestration events |
backtestLogicPublicService |
BacktestLogicPublicService |
Delegates individual backtest execution |
backtestMarkdownService |
BacktestMarkdownService |
Generates reports for each strategy |
walkerSchemaService |
WalkerSchemaService |
Retrieves walker configuration |
run(symbol: string, strategies: string[], metric: string): AsyncGenerator
Executes backtests for all strategies sequentially and identifies the best performer based on the specified metric.
strategies arrayMethodContextService.runAsyncIterator() for context injectionWalkerLogicPrivateService delegates to BacktestLogicPublicService for individual strategy execution:
The walker uses a configurable metric for strategy comparison:
| Common Metrics | Description | Optimization Goal |
|---|---|---|
sharpeRatio |
Risk-adjusted return | Maximize |
totalPnl |
Total profit/loss | Maximize |
winRate |
Percentage of winning trades | Maximize |
maxDrawdown |
Largest peak-to-trough decline | Minimize (inverted) |
averagePnl |
Mean PnL per trade | Maximize |
The metric name must correspond to a field in IBacktestStatistics returned by BacktestMarkdownService.getData().
Public-facing wrapper for WalkerLogicPrivateService that manages context propagation.
| Property | Type | Purpose |
|---|---|---|
loggerService |
LoggerService |
Logs public walker invocations |
walkerLogicPrivateService |
WalkerLogicPrivateService |
Core walker orchestration logic |
walkerSchemaService |
WalkerSchemaService |
Retrieves walker schema configuration |
run(symbol: string, context: {walkerName, exchangeName, frameName}): AsyncGenerator
Wraps private walker generator with method context injection and retrieves strategy list from walker schema.
// Conceptual signature
public run = (
symbol: string,
context: {
walkerName: string;
exchangeName: string;
frameName: string;
}
)
Implementation Pattern:
MethodContextService.runAsyncIterator() with walker contextwalkerLogicPrivateService.run(symbol, strategies, metric)| Field | Type | Description |
|---|---|---|
walkerName |
string |
Identifies which walker schema to use |
exchangeName |
string |
Exchange for all strategy backtests |
frameName |
string |
Timeframe for all strategy backtests |
Logic Services are accessed through the Backtest and Live classes in the Public API layer.
Backtest Entry Point:
// From Backtest class
async *run(
symbol: string,
options: {
strategyName: string;
exchangeName: string;
frameName: string;
}
): AsyncGenerator<IStrategyTickResultClosed>
Live Entry Point:
// From Live class
async *run(
symbol: string,
options: {
strategyName: string;
exchangeName: string;
}
): AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed>