The Service Layer is the orchestration backbone of the backtest-kit framework, sitting between the Public API Layer and the Business Logic Layer. It manages dependency injection, configuration routing, instance caching, and execution context propagation. The layer is organized into four distinct service categories: Connection Services (instance routing and memoization), Schema Services (configuration registries), Global Services (context injection), and Logic Services (execution orchestration).
For details on the Public API that consumes these services, see Public API Reference. For the Business Logic Layer that these services coordinate, see Core Business Logic. The Dependency Injection System that powers this layer is documented in Dependency Injection System.
The Service Layer consists of 19 distinct services organized into four functional categories. Each category has a specific architectural responsibility in the framework's orchestration pipeline.
Service Category Responsibilities
| Category | Services | Responsibility |
|---|---|---|
| Base | LoggerService |
Cross-cutting logging with automatic context enrichment |
| Context | ExecutionContextService, MethodContextService |
Implicit context propagation using di-scoped |
| Schema | StrategySchemaService, ExchangeSchemaService, FrameSchemaService |
Configuration registry and schema validation |
| Connection | StrategyConnectionService, ExchangeConnectionService, FrameConnectionService |
Instance routing with memoization, client lifecycle management |
| Global | StrategyGlobalService, ExchangeGlobalService, FrameGlobalService, LiveGlobalService, BacktestGlobalService |
Context injection wrapper around connection services |
| Logic | BacktestLogicPublicService, BacktestLogicPrivateService, LiveLogicPublicService, LiveLogicPrivateService |
Execution orchestration, async generator management |
| Markdown | BacktestMarkdownService, LiveMarkdownService |
Report generation and event accumulation |
All services are registered using the dependency injection system powered by di-kit and di-scoped. The TYPES constant defines Symbol identifiers for each service, and the provide.ts file registers factory functions that instantiate services.
Service Type Symbols
The TYPES constant in src/lib/core/types.ts:1-56 defines Symbol identifiers organized by category:
| Category | Symbol Examples | Purpose |
|---|---|---|
baseServices |
TYPES.loggerService |
Cross-cutting concerns |
contextServices |
TYPES.executionContextService, TYPES.methodContextService |
Context propagation |
connectionServices |
TYPES.strategyConnectionService, TYPES.exchangeConnectionService, TYPES.frameConnectionService |
Client routing |
schemaServices |
TYPES.strategySchemaService, TYPES.exchangeSchemaService, TYPES.frameSchemaService |
Configuration storage |
globalServices |
TYPES.strategyGlobalService, TYPES.exchangeGlobalService, TYPES.frameGlobalService, TYPES.liveGlobalService, TYPES.backtestGlobalService |
Context injection |
logicPrivateServices |
TYPES.backtestLogicPrivateService, TYPES.liveLogicPrivateService |
Private orchestration |
logicPublicServices |
TYPES.backtestLogicPublicService, TYPES.liveLogicPublicService |
Public API facades |
markdownServices |
TYPES.backtestMarkdownService, TYPES.liveMarkdownService |
Report generation |
Connection Services implement a routing pattern that directs method calls to the appropriate client instance based on MethodContextService.context. Each connection service uses memoize from functools-kit to cache client instances, ensuring that the same configuration always returns the same instance.
StrategyConnectionService Architecture
Memoization Cache Key Strategy
The getStrategy method in src/lib/services/connection/StrategyConnectionService.ts:67-83 demonstrates the memoization pattern:
private getStrategy = memoize(
([strategyName]) => `${strategyName}`, // Cache key function
(strategyName: StrategyName) => { // Factory function
const { getSignal, interval, callbacks } =
this.strategySchemaService.get(strategyName);
return new ClientStrategy({
interval,
execution: this.executionContextService,
method: this.methodContextService,
logger: this.loggerService,
exchange: this.exchangeConnectionService,
strategyName,
getSignal,
callbacks,
});
}
);
The cache key is the strategyName string. On first access, the factory function executes, creating a ClientStrategy instance. Subsequent calls with the same strategyName return the cached instance. This ensures:
ClientStrategy instanceConnection Services implement the IStrategy, IExchange, and IFrame interfaces, delegating all method calls to the appropriate client instance. The routing is automatic based on MethodContextService.context, which is set by Global Services.
Key Routing Components
strategyName, exchangeName, frameName) in di-scoped contextgetStrategy() / getExchange() / getFrame() memoized factorylistenSignal APIThe backtest object exported from src/lib/index.ts:101-117 aggregates all services into a single importable object. This service locator pattern simplifies imports and provides a single access point for the entire framework.
Service Aggregation Structure
export const backtest = {
...baseServices, // loggerService
...contextServices, // executionContextService, methodContextService
...connectionServices, // exchangeConnectionService, strategyConnectionService, frameConnectionService
...schemaServices, // exchangeSchemaService, strategySchemaService, frameSchemaService
...globalServices, // exchangeGlobalService, strategyGlobalService, frameGlobalService, liveGlobalService, backtestGlobalService
...logicPrivateServices, // backtestLogicPrivateService, liveLogicPrivateService
...logicPublicServices, // backtestLogicPublicService, liveLogicPublicService
...markdownServices, // backtestMarkdownService, liveMarkdownService
};
Service Import Pattern
Users and internal framework code can import services in two ways:
Individual service import (rare, typically only for context services)
import { ExecutionContextService } from 'backtest-kit';
Service locator import (common, used throughout framework)
import backtest from 'backtest-kit';
const result = await backtest.backtestLogicPublicService.run(...);
The service aggregator is initialized via init() at src/lib/index.ts:112, which triggers all service registrations from provide.ts.
Services follow a lazy initialization pattern. The DI container is initialized at module load time, but individual service instances are created only when first injected. This allows for:
Initialization Order
Service Instance Lifecycle
provide.ts registers factory functions with DI containerinit() prepares container for service resolutionaddStrategy(), addExchange(), addFrame() to populate schema servicesBacktest.run() or Live.run()The following table summarizes each service's primary responsibility and its place in the execution flow:
| Service | File Location | Primary Responsibility | Called By | Calls |
|---|---|---|---|---|
LoggerService |
src/lib/services/base/ |
Cross-cutting logging with context enrichment | All services | - |
ExecutionContextService |
src/lib/services/context/ |
Stores execution context (date, backtest mode) | Logic services | - |
MethodContextService |
src/lib/services/context/ |
Stores method context (strategy/exchange/frame names) | Global services | - |
StrategySchemaService |
src/lib/services/schema/ |
Registry of strategy configurations | Connection services | - |
ExchangeSchemaService |
src/lib/services/schema/ |
Registry of exchange configurations | Connection services | - |
FrameSchemaService |
src/lib/services/schema/ |
Registry of frame configurations | Connection services | - |
StrategyConnectionService |
src/lib/services/connection/ |
Routes to cached ClientStrategy instances | Global services | ClientStrategy |
ExchangeConnectionService |
src/lib/services/connection/ |
Routes to cached ClientExchange instances | Global services | ClientExchange |
FrameConnectionService |
src/lib/services/connection/ |
Routes to cached ClientFrame instances | Global services | ClientFrame |
StrategyGlobalService |
src/lib/services/global/ |
Injects method context for strategies | Logic services | Connection services |
ExchangeGlobalService |
src/lib/services/global/ |
Injects method context for exchanges | Logic services | Connection services |
FrameGlobalService |
src/lib/services/global/ |
Injects method context for frames | Logic services | Connection services |
LiveGlobalService |
src/lib/services/global/ |
Wraps live logic with error handling | Public API | Private logic services |
BacktestGlobalService |
src/lib/services/global/ |
Wraps backtest logic with error handling | Public API | Private logic services |
BacktestLogicPrivateService |
src/lib/services/logic/private/ |
Orchestrates backtest execution loop | Public logic services | Global services |
LiveLogicPrivateService |
src/lib/services/logic/private/ |
Orchestrates live trading infinite loop | Public logic services | Global services |
BacktestLogicPublicService |
src/lib/services/logic/public/ |
Public API facade for backtest | Public API functions | Private logic services |
LiveLogicPublicService |
src/lib/services/logic/public/ |
Public API facade for live trading | Public API functions | Private logic services |
BacktestMarkdownService |
src/lib/services/markdown/ |
Accumulates backtest results for reports | Logic services | - |
LiveMarkdownService |
src/lib/services/markdown/ |
Accumulates live trading results for reports | Logic services | - |
The following pages provide detailed documentation for each service category:
StrategyConnectionService, ExchangeConnectionService, FrameConnectionService routing patterns with memoizationStrategySchemaService, ExchangeSchemaService, FrameSchemaService configuration registriesBacktestLogicPrivateService and LiveLogicPrivateService orchestration of async generator execution