removed old services

This commit is contained in:
Boki 2025-06-18 11:43:39 -04:00
parent f69c7b034b
commit bc14acaeba
32 changed files with 0 additions and 2816 deletions

View file

@ -1,37 +0,0 @@
{
"name": "@stock-bot/execution-service",
"version": "1.0.0",
"description": "Execution service for stock trading bot - handles order execution and broker integration",
"main": "dist/index.js",
"type": "module",
"scripts": {
"build": "tsc",
"devvvvv": "bun --watch src/index.ts",
"start": "bun src/index.ts",
"test": "bun test",
"lint": "eslint src --ext .ts",
"type-check": "tsc --noEmit"
},
"dependencies": {
"@hono/node-server": "^1.12.0",
"hono": "^4.6.1",
"@stock-bot/config": "*",
"@stock-bot/logger": "*",
"@stock-bot/types": "*",
"@stock-bot/event-bus": "*",
"@stock-bot/utils": "*"
},
"devDependencies": {
"@types/node": "^22.5.0",
"typescript": "^5.5.4"
},
"keywords": [
"trading",
"execution",
"broker",
"orders",
"stock-bot"
],
"author": "Stock Bot Team",
"license": "MIT"
}

View file

@ -1,94 +0,0 @@
import { Order, OrderResult, OrderStatus } from '@stock-bot/types';
export interface BrokerInterface {
/**
* Execute an order with the broker
*/
executeOrder(order: Order): Promise<OrderResult>;
/**
* Get order status from broker
*/
getOrderStatus(orderId: string): Promise<OrderStatus>;
/**
* Cancel an order
*/
cancelOrder(orderId: string): Promise<boolean>;
/**
* Get current positions
*/
getPositions(): Promise<Position[]>;
/**
* Get account balance
*/
getAccountBalance(): Promise<AccountBalance>;
}
export interface Position {
symbol: string;
quantity: number;
averagePrice: number;
currentPrice: number;
unrealizedPnL: number;
side: 'long' | 'short';
}
export interface AccountBalance {
totalValue: number;
availableCash: number;
buyingPower: number;
marginUsed: number;
}
export class MockBroker implements BrokerInterface {
private orders: Map<string, OrderResult> = new Map();
private positions: Position[] = [];
async executeOrder(order: Order): Promise<OrderResult> {
const orderId = `mock_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const result: OrderResult = {
orderId,
symbol: order.symbol,
quantity: order.quantity,
side: order.side,
status: 'filled',
executedPrice: order.price || 100, // Mock price
executedAt: new Date(),
commission: 1.0,
};
this.orders.set(orderId, result);
return result;
}
async getOrderStatus(orderId: string): Promise<OrderStatus> {
const order = this.orders.get(orderId);
return order?.status || 'unknown';
}
async cancelOrder(orderId: string): Promise<boolean> {
const order = this.orders.get(orderId);
if (order && order.status === 'pending') {
order.status = 'cancelled';
return true;
}
return false;
}
async getPositions(): Promise<Position[]> {
return this.positions;
}
async getAccountBalance(): Promise<AccountBalance> {
return {
totalValue: 100000,
availableCash: 50000,
buyingPower: 200000,
marginUsed: 0,
};
}
}

View file

@ -1,58 +0,0 @@
import { logger } from '@stock-bot/logger';
import { Order, OrderResult } from '@stock-bot/types';
import { BrokerInterface } from '../broker/interface.ts';
export class OrderManager {
private broker: BrokerInterface;
private pendingOrders: Map<string, Order> = new Map();
constructor(broker: BrokerInterface) {
this.broker = broker;
}
async executeOrder(order: Order): Promise<OrderResult> {
try {
logger.info(
`Executing order: ${order.symbol} ${order.side} ${order.quantity} @ ${order.price}`
);
// Add to pending orders
const orderId = `order_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
this.pendingOrders.set(orderId, order);
// Execute with broker
const result = await this.broker.executeOrder(order);
// Remove from pending
this.pendingOrders.delete(orderId);
logger.info(`Order executed successfully: ${result.orderId}`);
return result;
} catch (error) {
logger.error('Order execution failed', error);
throw error;
}
}
async cancelOrder(orderId: string): Promise<boolean> {
try {
const success = await this.broker.cancelOrder(orderId);
if (success) {
this.pendingOrders.delete(orderId);
logger.info(`Order cancelled: ${orderId}`);
}
return success;
} catch (error) {
logger.error('Order cancellation failed', error);
throw error;
}
}
async getOrderStatus(orderId: string) {
return await this.broker.getOrderStatus(orderId);
}
getPendingOrders(): Order[] {
return Array.from(this.pendingOrders.values());
}
}

View file

@ -1,113 +0,0 @@
import { getLogger } from '@stock-bot/logger';
import { Order } from '@stock-bot/types';
export interface RiskRule {
name: string;
validate(order: Order, context: RiskContext): Promise<RiskValidationResult>;
}
export interface RiskContext {
currentPositions: Map<string, number>;
accountBalance: number;
totalExposure: number;
maxPositionSize: number;
maxDailyLoss: number;
}
export interface RiskValidationResult {
isValid: boolean;
reason?: string;
severity: 'info' | 'warning' | 'error';
}
export class RiskManager {
private logger = getLogger('risk-manager');
private rules: RiskRule[] = [];
constructor() {
this.initializeDefaultRules();
}
addRule(rule: RiskRule): void {
this.rules.push(rule);
}
async validateOrder(order: Order, context: RiskContext): Promise<RiskValidationResult> {
for (const rule of this.rules) {
const result = await rule.validate(order, context);
if (!result.isValid) {
logger.warn(`Risk rule violation: ${rule.name}`, {
order,
reason: result.reason,
});
return result;
}
}
return { isValid: true, severity: 'info' };
}
private initializeDefaultRules(): void {
// Position size rule
this.addRule({
name: 'MaxPositionSize',
async validate(order: Order, context: RiskContext): Promise<RiskValidationResult> {
const orderValue = order.quantity * (order.price || 0);
if (orderValue > context.maxPositionSize) {
return {
isValid: false,
reason: `Order size ${orderValue} exceeds maximum position size ${context.maxPositionSize}`,
severity: 'error',
};
}
return { isValid: true, severity: 'info' };
},
});
// Balance check rule
this.addRule({
name: 'SufficientBalance',
async validate(order: Order, context: RiskContext): Promise<RiskValidationResult> {
const orderValue = order.quantity * (order.price || 0);
if (order.side === 'buy' && orderValue > context.accountBalance) {
return {
isValid: false,
reason: `Insufficient balance: need ${orderValue}, have ${context.accountBalance}`,
severity: 'error',
};
}
return { isValid: true, severity: 'info' };
},
});
// Concentration risk rule
this.addRule({
name: 'ConcentrationLimit',
async validate(order: Order, context: RiskContext): Promise<RiskValidationResult> {
const currentPosition = context.currentPositions.get(order.symbol) || 0;
const newPosition =
order.side === 'buy'
? currentPosition + order.quantity
: currentPosition - order.quantity;
const positionValue = Math.abs(newPosition) * (order.price || 0);
const concentrationRatio = positionValue / context.accountBalance;
if (concentrationRatio > 0.25) {
// 25% max concentration
return {
isValid: false,
reason: `Position concentration ${(concentrationRatio * 100).toFixed(2)}% exceeds 25% limit`,
severity: 'warning',
};
}
return { isValid: true, severity: 'info' };
},
});
}
}

View file

@ -1,101 +0,0 @@
import { serve } from '@hono/node-server';
import { Hono } from 'hono';
import { config } from '@stock-bot/config';
import { getLogger } from '@stock-bot/logger';
// import { BrokerInterface } from './broker/interface.ts';
// import { OrderManager } from './execution/order-manager.ts';
// import { RiskManager } from './execution/risk-manager.ts';
const app = new Hono();
const logger = getLogger('execution-service');
// Health check endpoint
app.get('/health', c => {
return c.json({
status: 'healthy',
service: 'execution-service',
timestamp: new Date().toISOString(),
});
});
// Order execution endpoints
app.post('/orders/execute', async c => {
try {
const orderRequest = await c.req.json();
logger.info('Received order execution request', orderRequest);
// TODO: Validate order and execute
return c.json({
orderId: `order_${Date.now()}`,
status: 'pending',
message: 'Order submitted for execution',
});
} catch (error) {
logger.error('Order execution failed', error);
return c.json({ error: 'Order execution failed' }, 500);
}
});
app.get('/orders/:orderId/status', async c => {
const orderId = c.req.param('orderId');
try {
// TODO: Get order status from broker
return c.json({
orderId,
status: 'filled',
executedAt: new Date().toISOString(),
});
} catch (error) {
logger.error('Failed to get order status', error);
return c.json({ error: 'Failed to get order status' }, 500);
}
});
app.post('/orders/:orderId/cancel', async c => {
const orderId = c.req.param('orderId');
try {
// TODO: Cancel order with broker
return c.json({
orderId,
status: 'cancelled',
cancelledAt: new Date().toISOString(),
});
} catch (error) {
logger.error('Failed to cancel order', error);
return c.json({ error: 'Failed to cancel order' }, 500);
}
});
// Risk management endpoints
app.get('/risk/position/:symbol', async c => {
const symbol = c.req.param('symbol');
try {
// TODO: Get position risk metrics
return c.json({
symbol,
position: 100,
exposure: 10000,
risk: 'low',
});
} catch (error) {
logger.error('Failed to get position risk', error);
return c.json({ error: 'Failed to get position risk' }, 500);
}
});
const port = config.EXECUTION_SERVICE_PORT || 3004;
logger.info(`Starting execution service on port ${port}`);
serve(
{
fetch: app.fetch,
port,
},
info => {
logger.info(`Execution service is running on port ${info.port}`);
}
);

View file

@ -1,25 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": [
"node_modules",
"dist",
"**/*.test.ts",
"**/*.spec.ts",
"**/test/**",
"**/tests/**",
"**/__tests__/**"
],
"references": [
{ "path": "../../libs/types" },
{ "path": "../../libs/config" },
{ "path": "../../libs/logger" },
{ "path": "../../libs/utils" },
{ "path": "../../libs/event-bus" },
{ "path": "../../libs/shutdown" }
]
}

View file

@ -1,26 +0,0 @@
{
"extends": ["//"],
"tasks": {
"build": {
"dependsOn": [
"@stock-bot/types#build",
"@stock-bot/config#build",
"@stock-bot/logger#build",
"@stock-bot/utils#build",
"@stock-bot/event-bus#build",
"@stock-bot/shutdown#build"
],
"outputs": ["dist/**"],
"inputs": [
"src/**",
"package.json",
"tsconfig.json",
"!**/*.test.ts",
"!**/*.spec.ts",
"!**/test/**",
"!**/tests/**",
"!**/__tests__/**"
]
}
}
}

View file

@ -1,38 +0,0 @@
{
"name": "@stock-bot/portfolio-service",
"version": "1.0.0",
"description": "Portfolio service for stock trading bot - handles portfolio tracking and performance analytics",
"main": "dist/index.js",
"type": "module",
"scripts": {
"build": "tsc",
"devvvvv": "bun --watch src/index.ts",
"start": "bun src/index.ts",
"test": "bun test",
"lint": "eslint src --ext .ts",
"type-check": "tsc --noEmit"
},
"dependencies": {
"@hono/node-server": "^1.12.0",
"hono": "^4.6.1",
"@stock-bot/config": "*",
"@stock-bot/logger": "*",
"@stock-bot/types": "*",
"@stock-bot/questdb-client": "*",
"@stock-bot/utils": "*",
"@stock-bot/data-frame": "*"
},
"devDependencies": {
"@types/node": "^22.5.0",
"typescript": "^5.5.4"
},
"keywords": [
"trading",
"portfolio",
"performance",
"analytics",
"stock-bot"
],
"author": "Stock Bot Team",
"license": "MIT"
}

View file

@ -1,240 +0,0 @@
import { PortfolioSnapshot } from '../portfolio/portfolio-manager';
export interface PerformanceMetrics {
totalReturn: number;
annualizedReturn: number;
sharpeRatio: number;
maxDrawdown: number;
volatility: number;
beta: number;
alpha: number;
calmarRatio: number;
sortinoRatio: number;
}
export interface RiskMetrics {
var95: number; // Value at Risk (95% confidence)
cvar95: number; // Conditional Value at Risk
maxDrawdown: number;
downsideDeviation: number;
correlationMatrix: Record<string, Record<string, number>>;
}
export class PerformanceAnalyzer {
private snapshots: PortfolioSnapshot[] = [];
private benchmarkReturns: number[] = []; // S&P 500 or other benchmark
addSnapshot(snapshot: PortfolioSnapshot): void {
this.snapshots.push(snapshot);
// Keep only last 252 trading days (1 year)
if (this.snapshots.length > 252) {
this.snapshots = this.snapshots.slice(-252);
}
}
calculatePerformanceMetrics(
period: 'daily' | 'weekly' | 'monthly' = 'daily'
): PerformanceMetrics {
if (this.snapshots.length < 2) {
throw new Error('Need at least 2 snapshots to calculate performance');
}
const returns = this.calculateReturns(period);
const riskFreeRate = 0.02; // 2% annual risk-free rate
return {
totalReturn: this.calculateTotalReturn(),
annualizedReturn: this.calculateAnnualizedReturn(returns),
sharpeRatio: this.calculateSharpeRatio(returns, riskFreeRate),
maxDrawdown: this.calculateMaxDrawdown(),
volatility: this.calculateVolatility(returns),
beta: this.calculateBeta(returns),
alpha: this.calculateAlpha(returns, riskFreeRate),
calmarRatio: this.calculateCalmarRatio(returns),
sortinoRatio: this.calculateSortinoRatio(returns, riskFreeRate),
};
}
calculateRiskMetrics(): RiskMetrics {
const returns = this.calculateReturns('daily');
return {
var95: this.calculateVaR(returns, 0.95),
cvar95: this.calculateCVaR(returns, 0.95),
maxDrawdown: this.calculateMaxDrawdown(),
downsideDeviation: this.calculateDownsideDeviation(returns),
correlationMatrix: {}, // TODO: Implement correlation matrix
};
}
private calculateReturns(_period: 'daily' | 'weekly' | 'monthly'): number[] {
if (this.snapshots.length < 2) {
return [];
}
const returns: number[] = [];
for (let i = 1; i < this.snapshots.length; i++) {
const currentValue = this.snapshots[i].totalValue;
const previousValue = this.snapshots[i - 1].totalValue;
const return_ = (currentValue - previousValue) / previousValue;
returns.push(return_);
}
return returns;
}
private calculateTotalReturn(): number {
if (this.snapshots.length < 2) {
return 0;
}
const firstValue = this.snapshots[0].totalValue;
const lastValue = this.snapshots[this.snapshots.length - 1].totalValue;
return (lastValue - firstValue) / firstValue;
}
private calculateAnnualizedReturn(returns: number[]): number {
if (returns.length === 0) {
return 0;
}
const avgReturn = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
return Math.pow(1 + avgReturn, 252) - 1; // 252 trading days per year
}
private calculateVolatility(returns: number[]): number {
if (returns.length === 0) {
return 0;
}
const avgReturn = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance =
returns.reduce((sum, ret) => sum + Math.pow(ret - avgReturn, 2), 0) / returns.length;
return Math.sqrt(variance * 252); // Annualized volatility
}
private calculateSharpeRatio(returns: number[], riskFreeRate: number): number {
if (returns.length === 0) {
return 0;
}
const avgReturn = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const annualizedReturn = Math.pow(1 + avgReturn, 252) - 1;
const volatility = this.calculateVolatility(returns);
if (volatility === 0) {
return 0;
}
return (annualizedReturn - riskFreeRate) / volatility;
}
private calculateMaxDrawdown(): number {
if (this.snapshots.length === 0) {
return 0;
}
let maxDrawdown = 0;
let peak = this.snapshots[0].totalValue;
for (const snapshot of this.snapshots) {
if (snapshot.totalValue > peak) {
peak = snapshot.totalValue;
}
const drawdown = (peak - snapshot.totalValue) / peak;
maxDrawdown = Math.max(maxDrawdown, drawdown);
}
return maxDrawdown;
}
private calculateBeta(returns: number[]): number {
if (returns.length === 0 || this.benchmarkReturns.length === 0) {
return 1.0;
}
// Simple beta calculation - would need actual benchmark data
return 1.0; // Placeholder
}
private calculateAlpha(returns: number[], riskFreeRate: number): number {
const beta = this.calculateBeta(returns);
const portfolioReturn = this.calculateAnnualizedReturn(returns);
const benchmarkReturn = 0.1; // 10% benchmark return (placeholder)
return portfolioReturn - (riskFreeRate + beta * (benchmarkReturn - riskFreeRate));
}
private calculateCalmarRatio(returns: number[]): number {
const annualizedReturn = this.calculateAnnualizedReturn(returns);
const maxDrawdown = this.calculateMaxDrawdown();
if (maxDrawdown === 0) {
return 0;
}
return annualizedReturn / maxDrawdown;
}
private calculateSortinoRatio(returns: number[], riskFreeRate: number): number {
const annualizedReturn = this.calculateAnnualizedReturn(returns);
const downsideDeviation = this.calculateDownsideDeviation(returns);
if (downsideDeviation === 0) {
return 0;
}
return (annualizedReturn - riskFreeRate) / downsideDeviation;
}
private calculateDownsideDeviation(returns: number[]): number {
if (returns.length === 0) {
return 0;
}
const negativeReturns = returns.filter(ret => ret < 0);
if (negativeReturns.length === 0) {
return 0;
}
const avgNegativeReturn =
negativeReturns.reduce((sum, ret) => sum + ret, 0) / negativeReturns.length;
const variance =
negativeReturns.reduce((sum, ret) => sum + Math.pow(ret - avgNegativeReturn, 2), 0) /
negativeReturns.length;
return Math.sqrt(variance * 252); // Annualized
}
private calculateVaR(returns: number[], confidence: number): number {
if (returns.length === 0) {
return 0;
}
const sortedReturns = returns.slice().sort((a, b) => a - b);
const index = Math.floor((1 - confidence) * sortedReturns.length);
return -sortedReturns[index]; // Return as positive value
}
private calculateCVaR(returns: number[], confidence: number): number {
if (returns.length === 0) {
return 0;
}
const sortedReturns = returns.slice().sort((a, b) => a - b);
const cutoffIndex = Math.floor((1 - confidence) * sortedReturns.length);
const tailReturns = sortedReturns.slice(0, cutoffIndex + 1);
if (tailReturns.length === 0) {
return 0;
}
const avgTailReturn = tailReturns.reduce((sum, ret) => sum + ret, 0) / tailReturns.length;
return -avgTailReturn; // Return as positive value
}
}

View file

@ -1,134 +0,0 @@
import { serve } from '@hono/node-server';
import { Hono } from 'hono';
import { config } from '@stock-bot/config';
import { getLogger } from '@stock-bot/logger';
const app = new Hono();
const logger = getLogger('portfolio-service');
// Health check endpoint
app.get('/health', c => {
return c.json({
status: 'healthy',
service: 'portfolio-service',
timestamp: new Date().toISOString(),
});
});
// Portfolio endpoints
app.get('/portfolio/overview', async c => {
try {
// TODO: Get portfolio overview
return c.json({
totalValue: 125000,
totalReturn: 25000,
totalReturnPercent: 25.0,
dayChange: 1250,
dayChangePercent: 1.0,
positions: [],
});
} catch (error) {
logger.error('Failed to get portfolio overview', error);
return c.json({ error: 'Failed to get portfolio overview' }, 500);
}
});
app.get('/portfolio/positions', async c => {
try {
// TODO: Get current positions
return c.json([
{
symbol: 'AAPL',
quantity: 100,
averagePrice: 150.0,
currentPrice: 155.0,
marketValue: 15500,
unrealizedPnL: 500,
unrealizedPnLPercent: 3.33,
},
]);
} catch (error) {
logger.error('Failed to get positions', error);
return c.json({ error: 'Failed to get positions' }, 500);
}
});
app.get('/portfolio/history', async c => {
const days = c.req.query('days') || '30';
try {
// TODO: Get portfolio history
return c.json({
period: `${days} days`,
data: [],
});
} catch (error) {
logger.error('Failed to get portfolio history', error);
return c.json({ error: 'Failed to get portfolio history' }, 500);
}
});
// Performance analytics endpoints
app.get('/analytics/performance', async c => {
const period = c.req.query('period') || '1M';
try {
// TODO: Calculate performance metrics
return c.json({
period,
totalReturn: 0.25,
annualizedReturn: 0.3,
sharpeRatio: 1.5,
maxDrawdown: 0.05,
volatility: 0.15,
beta: 1.1,
alpha: 0.02,
});
} catch (error) {
logger.error('Failed to get performance analytics', error);
return c.json({ error: 'Failed to get performance analytics' }, 500);
}
});
app.get('/analytics/risk', async c => {
try {
// TODO: Calculate risk metrics
return c.json({
var95: 0.02,
cvar95: 0.03,
maxDrawdown: 0.05,
downside_deviation: 0.08,
correlation_matrix: {},
});
} catch (error) {
logger.error('Failed to get risk analytics', error);
return c.json({ error: 'Failed to get risk analytics' }, 500);
}
});
app.get('/analytics/attribution', async c => {
try {
// TODO: Calculate performance attribution
return c.json({
sector_allocation: {},
security_selection: {},
interaction_effect: {},
});
} catch (error) {
logger.error('Failed to get attribution analytics', error);
return c.json({ error: 'Failed to get attribution analytics' }, 500);
}
});
const port = config.PORTFOLIO_SERVICE_PORT || 3005;
logger.info(`Starting portfolio service on port ${port}`);
serve(
{
fetch: app.fetch,
port,
},
info => {
logger.info(`Portfolio service is running on port ${info.port}`);
}
);

View file

@ -1,159 +0,0 @@
import { getLogger } from '@stock-bot/logger';
export interface Position {
symbol: string;
quantity: number;
averagePrice: number;
currentPrice: number;
marketValue: number;
unrealizedPnL: number;
unrealizedPnLPercent: number;
costBasis: number;
lastUpdated: Date;
}
export interface PortfolioSnapshot {
timestamp: Date;
totalValue: number;
cashBalance: number;
positions: Position[];
totalReturn: number;
totalReturnPercent: number;
dayChange: number;
dayChangePercent: number;
}
export interface Trade {
id: string;
symbol: string;
quantity: number;
price: number;
side: 'buy' | 'sell';
timestamp: Date;
commission: number;
}
export class PortfolioManager {
private logger = getLogger('PortfolioManager');
private positions: Map<string, Position> = new Map();
private trades: Trade[] = [];
private cashBalance: number = 100000; // Starting cash
constructor(initialCash: number = 100000) {
this.cashBalance = initialCash;
}
addTrade(trade: Trade): void {
this.trades.push(trade);
this.updatePosition(trade);
logger.info(`Trade added: ${trade.symbol} ${trade.side} ${trade.quantity} @ ${trade.price}`);
}
private updatePosition(trade: Trade): void {
const existing = this.positions.get(trade.symbol);
if (!existing) {
// New position
if (trade.side === 'buy') {
this.positions.set(trade.symbol, {
symbol: trade.symbol,
quantity: trade.quantity,
averagePrice: trade.price,
currentPrice: trade.price,
marketValue: trade.quantity * trade.price,
unrealizedPnL: 0,
unrealizedPnLPercent: 0,
costBasis: trade.quantity * trade.price + trade.commission,
lastUpdated: trade.timestamp,
});
this.cashBalance -= trade.quantity * trade.price + trade.commission;
}
return;
}
// Update existing position
if (trade.side === 'buy') {
const newQuantity = existing.quantity + trade.quantity;
const newCostBasis = existing.costBasis + trade.quantity * trade.price + trade.commission;
existing.quantity = newQuantity;
existing.averagePrice = (newCostBasis - this.getTotalCommissions(trade.symbol)) / newQuantity;
existing.costBasis = newCostBasis;
existing.lastUpdated = trade.timestamp;
this.cashBalance -= trade.quantity * trade.price + trade.commission;
} else if (trade.side === 'sell') {
existing.quantity -= trade.quantity;
existing.lastUpdated = trade.timestamp;
const proceeds = trade.quantity * trade.price - trade.commission;
this.cashBalance += proceeds;
// Remove position if quantity is zero
if (existing.quantity <= 0) {
this.positions.delete(trade.symbol);
}
}
}
updatePrice(symbol: string, price: number): void {
const position = this.positions.get(symbol);
if (position) {
position.currentPrice = price;
position.marketValue = position.quantity * price;
position.unrealizedPnL = position.marketValue - position.quantity * position.averagePrice;
position.unrealizedPnLPercent =
(position.unrealizedPnL / (position.quantity * position.averagePrice)) * 100;
position.lastUpdated = new Date();
}
}
getPosition(symbol: string): Position | undefined {
return this.positions.get(symbol);
}
getAllPositions(): Position[] {
return Array.from(this.positions.values());
}
getPortfolioSnapshot(): PortfolioSnapshot {
const positions = this.getAllPositions();
const totalMarketValue = positions.reduce((sum, pos) => sum + pos.marketValue, 0);
const totalValue = totalMarketValue + this.cashBalance;
const totalUnrealizedPnL = positions.reduce((sum, pos) => sum + pos.unrealizedPnL, 0);
return {
timestamp: new Date(),
totalValue,
cashBalance: this.cashBalance,
positions,
totalReturn: totalUnrealizedPnL, // Simplified - should include realized gains
totalReturnPercent: (totalUnrealizedPnL / (totalValue - totalUnrealizedPnL)) * 100,
dayChange: 0, // TODO: Calculate from previous day
dayChangePercent: 0,
};
}
getTrades(symbol?: string): Trade[] {
if (symbol) {
return this.trades.filter(trade => trade.symbol === symbol);
}
return this.trades;
}
private getTotalCommissions(symbol: string): number {
return this.trades
.filter(trade => trade.symbol === symbol)
.reduce((sum, trade) => sum + trade.commission, 0);
}
getCashBalance(): number {
return this.cashBalance;
}
getNetLiquidationValue(): number {
const positions = this.getAllPositions();
const positionValue = positions.reduce((sum, pos) => sum + pos.marketValue, 0);
return positionValue + this.cashBalance;
}
}

View file

@ -1,26 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": [
"node_modules",
"dist",
"**/*.test.ts",
"**/*.spec.ts",
"**/test/**",
"**/tests/**",
"**/__tests__/**"
],
"references": [
{ "path": "../../libs/types" },
{ "path": "../../libs/config" },
{ "path": "../../libs/logger" },
{ "path": "../../libs/utils" },
{ "path": "../../libs/postgres-client" },
{ "path": "../../libs/event-bus" },
{ "path": "../../libs/shutdown" }
]
}

View file

@ -1,27 +0,0 @@
{
"extends": ["//"],
"tasks": {
"build": {
"dependsOn": [
"@stock-bot/types#build",
"@stock-bot/config#build",
"@stock-bot/logger#build",
"@stock-bot/utils#build",
"@stock-bot/postgres-client#build",
"@stock-bot/event-bus#build",
"@stock-bot/shutdown#build"
],
"outputs": ["dist/**"],
"inputs": [
"src/**",
"package.json",
"tsconfig.json",
"!**/*.test.ts",
"!**/*.spec.ts",
"!**/test/**",
"!**/tests/**",
"!**/__tests__/**"
]
}
}
}

View file

@ -1,27 +0,0 @@
{
"name": "@stock-bot/processing-service",
"version": "1.0.0",
"description": "Combined data processing and technical indicators service",
"main": "dist/index.js",
"type": "module",
"scripts": {
"dev": "bun --watch src/index.ts",
"build": "bun build src/index.ts --outdir dist --target node",
"start": "bun dist/index.js",
"test": "bun test",
"clean": "rm -rf dist"
},
"dependencies": {
"@stock-bot/config": "*",
"@stock-bot/logger": "*",
"@stock-bot/shutdown": "*",
"@stock-bot/types": "*",
"@stock-bot/utils": "*",
"@stock-bot/event-bus": "*",
"@stock-bot/vector-engine": "*",
"hono": "^4.0.0"
},
"devDependencies": {
"typescript": "^5.0.0"
}
}

View file

@ -1,122 +0,0 @@
/**
* Processing Service - Data processing and technical indicators service
*/
import { Hono } from 'hono';
import { loadEnvVariables } from '@stock-bot/config';
import { getLogger, shutdownLoggers } from '@stock-bot/logger';
import { Shutdown } from '@stock-bot/shutdown';
import { healthRoutes, processingRoutes } from './routes';
import { processingServiceManager } from './services';
// Load environment variables
loadEnvVariables();
const app = new Hono();
const logger = getLogger('processing-service');
const PORT = parseInt(process.env.PROCESSING_SERVICE_PORT || '3003');
let server: ReturnType<typeof Bun.serve> | null = null;
// Initialize shutdown manager with 15 second timeout
const shutdown = Shutdown.getInstance({ timeout: 15000 });
// Register all routes
app.route('', healthRoutes);
app.route('/api/processing', processingRoutes);
// Initialize services
async function initializeServices() {
logger.info('Initializing processing service...');
try {
// Initialize processing service manager
logger.info('Starting processing service manager initialization...');
await processingServiceManager.initialize();
logger.info('Processing service manager initialized');
// TODO: Add other service initializations here as needed
// - MongoDB client for reading/writing processed data
// - Event bus for listening to data events
// - Technical indicators engines
// - Vector engines for similarity calculations
logger.info('All services initialized successfully');
} catch (error) {
logger.error('Failed to initialize services', { error });
throw error;
}
}
// Start server
async function startServer() {
await initializeServices();
// Start the HTTP server using Bun's native serve
server = Bun.serve({
port: PORT,
fetch: app.fetch,
development: process.env.NODE_ENV === 'development',
});
logger.info(`Processing Service started on port ${PORT}`);
}
// Register shutdown handlers
shutdown.onShutdown(async () => {
if (server) {
logger.info('Stopping HTTP server...');
try {
server.stop();
logger.info('HTTP server stopped successfully');
} catch (error) {
logger.error('Error stopping HTTP server', { error });
}
}
});
shutdown.onShutdown(async () => {
logger.info('Shutting down processing service manager...');
try {
await processingServiceManager.shutdown();
logger.info('Processing service manager shut down successfully');
} catch (error) {
logger.error('Error shutting down processing service manager', { error });
}
});
shutdown.onShutdown(async () => {
logger.info('Shutting down loggers...');
try {
await shutdownLoggers();
logger.info('Loggers shut down successfully');
} catch (error) {
logger.error('Error shutting down loggers', { error });
}
});
// Handle uncaught exceptions and unhandled rejections
process.on('uncaughtException', error => {
logger.error('Uncaught exception', { error });
shutdown.shutdownAndExit('uncaughtException', 1);
});
process.on('unhandledRejection', (reason, promise) => {
logger.error('Unhandled rejection', { reason, promise });
shutdown.shutdownAndExit('unhandledRejection', 1);
});
// Handle shutdown signals
process.on('SIGINT', () => {
logger.info('Received SIGINT signal');
shutdown.shutdownAndExit('SIGINT', 0);
});
process.on('SIGTERM', () => {
logger.info('Received SIGTERM signal');
shutdown.shutdownAndExit('SIGTERM', 0);
});
// Start the service
startServer().catch(error => {
logger.error('Failed to start processing service', { error });
process.exit(1);
});

View file

@ -1,44 +0,0 @@
/**
* Health and status routes for processing service
*/
import { Hono } from 'hono';
const healthRoutes = new Hono();
// Health check endpoint
healthRoutes.get('/health', c => {
return c.json({
status: 'healthy',
service: 'processing-service',
timestamp: new Date().toISOString(),
version: '1.0.0',
});
});
// Detailed status endpoint
healthRoutes.get('/status', c => {
return c.json({
service: 'processing-service',
status: 'running',
uptime: process.uptime(),
memory: process.memoryUsage(),
timestamp: new Date().toISOString(),
environment: process.env.NODE_ENV || 'development',
});
});
// Ready check endpoint
healthRoutes.get('/ready', c => {
// TODO: Add checks for service dependencies
// - Database connections
// - Event bus connections
// - Required resources
return c.json({
status: 'ready',
service: 'processing-service',
timestamp: new Date().toISOString(),
});
});
export { healthRoutes };

View file

@ -1,5 +0,0 @@
/**
* Route exports for processing service
*/
export { healthRoutes } from './health.routes';
export { processingRoutes } from './processing.routes';

View file

@ -1,57 +0,0 @@
/**
* Processing routes for data processing operations
*/
import { Hono } from 'hono';
import { getLogger } from '@stock-bot/logger';
import { processingServiceManager } from '../services';
const processingRoutes = new Hono();
const logger = getLogger('processing-routes');
// Process data endpoint
processingRoutes.post('/process', async c => {
try {
const body = await c.req.json();
logger.info('Processing request received', {
dataType: body.type,
recordCount: body.data?.length || 0,
});
// Use processing service manager to handle the request
const result = await processingServiceManager.processData(
body.type || 'unknown',
body.data || []
);
return c.json({
status: 'success',
message: 'Data processing completed',
result,
timestamp: new Date().toISOString(),
});
} catch (error) {
logger.error('Processing error', { error });
return c.json(
{
status: 'error',
message: 'Processing failed',
error: error instanceof Error ? error.message : 'Unknown error',
},
500
);
}
});
// Get processing status
processingRoutes.get('/status', c => {
const status = processingServiceManager.getStatus();
return c.json({
...status,
activeJobs: 0, // TODO: Implement job tracking
queueLength: 0, // TODO: Implement queue monitoring
lastProcessed: null, // TODO: Track last processing time
});
});
export { processingRoutes };

View file

@ -1,4 +0,0 @@
/**
* Services exports for processing service
*/
export { ProcessingServiceManager, processingServiceManager } from './processing.service';

View file

@ -1,115 +0,0 @@
/**
* Processing Service Manager
*
* Manages the core processing operations for the processing service
*/
import { getLogger } from '@stock-bot/logger';
const logger = getLogger('processing-service-manager');
export class ProcessingServiceManager {
private isInitialized = false;
async initialize(): Promise<void> {
if (this.isInitialized) {
logger.warn('Processing service manager already initialized');
return;
}
logger.info('Initializing processing service manager...');
try {
// TODO: Initialize processing components
// - Technical indicators engine
// - Data transformation pipeline
// - Event listeners for data events
// - Job queues for processing tasks
this.isInitialized = true;
logger.info('Processing service manager initialized successfully');
} catch (error) {
logger.error('Failed to initialize processing service manager', { error });
throw error;
}
}
async shutdown(): Promise<void> {
if (!this.isInitialized) {
logger.warn('Processing service manager not initialized, nothing to shutdown');
return;
}
logger.info('Shutting down processing service manager...');
try {
// TODO: Cleanup processing components
// - Stop job processing
// - Close database connections
// - Cleanup event listeners
this.isInitialized = false;
logger.info('Processing service manager shut down successfully');
} catch (error) {
logger.error('Error shutting down processing service manager', { error });
throw error;
}
}
/**
* Process data with technical indicators
*/
async processData(
dataType: string,
data: unknown[]
): Promise<{
status: string;
dataType: string;
inputCount: number;
outputCount: number;
processedAt: Date;
processingTime: number;
}> {
if (!this.isInitialized) {
throw new Error('Processing service manager not initialized');
}
logger.info(`Processing ${data.length} records of type: ${dataType}`);
try {
// TODO: Implement actual processing logic
// - Apply technical indicators
// - Calculate signals
// - Transform data format
// - Save processed results
const result = {
status: 'success',
dataType,
inputCount: data.length,
outputCount: data.length, // Placeholder
processedAt: new Date(),
processingTime: 0, // Placeholder
};
logger.info('Data processing completed', result);
return result;
} catch (error) {
logger.error('Data processing failed', { error, dataType, inputCount: data.length });
throw error;
}
}
/**
* Get processing service status
*/
getStatus() {
return {
initialized: this.isInitialized,
status: this.isInitialized ? 'ready' : 'not_initialized',
timestamp: new Date().toISOString(),
};
}
}
// Export singleton instance
export const processingServiceManager = new ProcessingServiceManager();

View file

@ -1,28 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": [
"node_modules",
"dist",
"**/*.test.ts",
"**/*.spec.ts",
"**/test/**",
"**/tests/**",
"**/__tests__/**"
],
"references": [
{ "path": "../../libs/types" },
{ "path": "../../libs/config" },
{ "path": "../../libs/logger" },
{ "path": "../../libs/utils" },
{ "path": "../../libs/data-frame" },
{ "path": "../../libs/vector-engine" },
{ "path": "../../libs/mongodb-client" },
{ "path": "../../libs/event-bus" },
{ "path": "../../libs/shutdown" }
]
}

View file

@ -1,29 +0,0 @@
{
"extends": ["//"],
"tasks": {
"build": {
"dependsOn": [
"@stock-bot/types#build",
"@stock-bot/config#build",
"@stock-bot/logger#build",
"@stock-bot/utils#build",
"@stock-bot/data-frame#build",
"@stock-bot/vector-engine#build",
"@stock-bot/mongodb-client#build",
"@stock-bot/event-bus#build",
"@stock-bot/shutdown#build"
],
"outputs": ["dist/**"],
"inputs": [
"src/**",
"package.json",
"tsconfig.json",
"!**/*.test.ts",
"!**/*.spec.ts",
"!**/test/**",
"!**/tests/**",
"!**/__tests__/**"
]
}
}
}

View file

@ -1,34 +0,0 @@
{
"name": "@stock-bot/strategy-service",
"version": "1.0.0",
"description": "Combined strategy execution and multi-mode backtesting service",
"main": "dist/index.js",
"type": "module",
"scripts": {
"devvvvv": "bun --watch src/index.ts",
"build": "bun build src/index.ts --outdir dist --target node",
"start": "bun dist/index.js",
"test": "bun test",
"clean": "rm -rf dist",
"backtest": "bun src/cli/index.ts",
"optimize": "bun src/cli/index.ts optimize",
"cli": "bun src/cli/index.ts"
},
"dependencies": {
"@stock-bot/config": "*",
"@stock-bot/logger": "*",
"@stock-bot/types": "*",
"@stock-bot/utils": "*",
"@stock-bot/event-bus": "*",
"@stock-bot/strategy-engine": "*",
"@stock-bot/vector-engine": "*",
"@stock-bot/data-frame": "*",
"@stock-bot/questdb-client": "*",
"hono": "^4.0.0",
"commander": "^11.0.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"typescript": "^5.0.0"
}
}

View file

@ -1,75 +0,0 @@
/**
* Event-Driven Backtesting Mode
* Processes data point by point with realistic order execution
*/
import { ExecutionMode, MarketData, Order, OrderResult } from '../../framework/execution-mode';
export interface BacktestConfig {
startDate: Date;
endDate: Date;
initialCapital: number;
slippageModel?: string;
commissionModel?: string;
}
export class EventMode extends ExecutionMode {
name = 'event-driven';
private simulationTime: Date;
private historicalData: Map<string, MarketData[]> = new Map();
constructor(private config: BacktestConfig) {
super();
this.simulationTime = config.startDate;
}
async executeOrder(order: Order): Promise<OrderResult> {
this.logger.debug('Simulating order execution', {
orderId: order.id,
simulationTime: this.simulationTime,
});
// TODO: Implement realistic order simulation
// Include slippage, commission, market impact
const simulatedResult: OrderResult = {
orderId: order.id,
symbol: order.symbol,
executedQuantity: order.quantity,
executedPrice: 100, // TODO: Get realistic price
commission: 1.0, // TODO: Calculate based on commission model
slippage: 0.01, // TODO: Calculate based on slippage model
timestamp: this.simulationTime,
executionTime: 50, // ms
};
return simulatedResult;
}
getCurrentTime(): Date {
return this.simulationTime;
}
async getMarketData(symbol: string): Promise<MarketData> {
const data = this.historicalData.get(symbol) || [];
const currentData = data.find(d => d.timestamp <= this.simulationTime);
if (!currentData) {
throw new Error(`No market data available for ${symbol} at ${this.simulationTime}`);
}
return currentData;
}
async publishEvent(event: string, data: any): Promise<void> {
// In-memory event bus for simulation
this.logger.debug('Publishing simulation event', { event, data });
}
// Simulation control methods
advanceTime(newTime: Date): void {
this.simulationTime = newTime;
}
loadHistoricalData(symbol: string, data: MarketData[]): void {
this.historicalData.set(symbol, data);
}
}

View file

@ -1,424 +0,0 @@
import { DataFrame } from '@stock-bot/data-frame';
import { EventBus } from '@stock-bot/event-bus';
import { getLogger } from '@stock-bot/logger';
import { VectorEngine, VectorizedBacktestResult } from '@stock-bot/vector-engine';
import { BacktestContext, BacktestResult, ExecutionMode } from '../framework/execution-mode';
import { EventMode } from './event-mode';
import VectorizedMode from './vectorized-mode';
export interface HybridModeConfig {
vectorizedThreshold: number; // Switch to vectorized if data points > threshold
warmupPeriod: number; // Number of periods for initial vectorized calculation
eventDrivenRealtime: boolean; // Use event-driven for real-time portions
optimizeIndicators: boolean; // Pre-calculate indicators vectorized
batchSize: number; // Size of batches for hybrid processing
}
export class HybridMode extends ExecutionMode {
private vectorEngine: VectorEngine;
private eventMode: EventMode;
private vectorizedMode: VectorizedMode;
private config: HybridModeConfig;
private precomputedIndicators: Map<string, number[]> = new Map();
private currentIndex: number = 0;
constructor(context: BacktestContext, eventBus: EventBus, config: HybridModeConfig = {}) {
super(context, eventBus);
this.config = {
vectorizedThreshold: 50000,
warmupPeriod: 1000,
eventDrivenRealtime: true,
optimizeIndicators: true,
batchSize: 10000,
...config,
};
this.vectorEngine = new VectorEngine();
this.eventMode = new EventMode(context, eventBus);
this.vectorizedMode = new VectorizedMode(context, eventBus);
this.logger = getLogger('hybrid-mode');
}
async initialize(): Promise<void> {
await super.initialize();
// Initialize both modes
await this.eventMode.initialize();
await this.vectorizedMode.initialize();
this.logger.info('Hybrid mode initialized', {
backtestId: this.context.backtestId,
config: this.config,
});
}
async execute(): Promise<BacktestResult> {
const startTime = Date.now();
this.logger.info('Starting hybrid backtest execution');
try {
// Determine execution strategy based on data size
const dataSize = await this.estimateDataSize();
if (dataSize <= this.config.vectorizedThreshold) {
// Small dataset: use pure vectorized approach
this.logger.info('Using pure vectorized approach for small dataset', { dataSize });
return await this.vectorizedMode.execute();
}
// Large dataset: use hybrid approach
this.logger.info('Using hybrid approach for large dataset', { dataSize });
return await this.executeHybrid(startTime);
} catch (error) {
this.logger.error('Hybrid backtest failed', {
error,
backtestId: this.context.backtestId,
});
await this.eventBus.publishBacktestUpdate(this.context.backtestId, 0, {
status: 'failed',
error: error.message,
});
throw error;
}
}
private async executeHybrid(startTime: number): Promise<BacktestResult> {
// Phase 1: Vectorized warmup and indicator pre-computation
const warmupResult = await this.executeWarmupPhase();
// Phase 2: Event-driven processing with pre-computed indicators
const eventResult = await this.executeEventPhase(warmupResult);
// Phase 3: Combine results
const combinedResult = this.combineResults(warmupResult, eventResult, startTime);
await this.eventBus.publishBacktestUpdate(this.context.backtestId, 100, {
status: 'completed',
result: combinedResult,
});
this.logger.info('Hybrid backtest completed', {
backtestId: this.context.backtestId,
duration: Date.now() - startTime,
totalTrades: combinedResult.trades.length,
warmupTrades: warmupResult.trades.length,
eventTrades: eventResult.trades.length,
});
return combinedResult;
}
private async executeWarmupPhase(): Promise<BacktestResult> {
this.logger.info('Executing vectorized warmup phase', {
warmupPeriod: this.config.warmupPeriod,
});
// Load warmup data
const warmupData = await this.loadWarmupData();
const dataFrame = this.createDataFrame(warmupData);
// Pre-compute indicators for entire dataset if optimization is enabled
if (this.config.optimizeIndicators) {
await this.precomputeIndicators(dataFrame);
}
// Run vectorized backtest on warmup period
const strategyCode = this.generateStrategyCode();
const vectorResult = await this.vectorEngine.executeVectorizedStrategy(
dataFrame.head(this.config.warmupPeriod),
strategyCode
);
// Convert to standard format
return this.convertVectorizedResult(vectorResult, Date.now());
}
private async executeEventPhase(warmupResult: BacktestResult): Promise<BacktestResult> {
this.logger.info('Executing event-driven phase');
// Set up event mode with warmup context
this.currentIndex = this.config.warmupPeriod;
// Create modified context for event phase
const eventContext: BacktestContext = {
...this.context,
initialPortfolio: this.extractFinalPortfolio(warmupResult),
};
// Execute event-driven backtest for remaining data
const eventMode = new EventMode(eventContext, this.eventBus);
await eventMode.initialize();
// Override indicator calculations to use pre-computed values
if (this.config.optimizeIndicators) {
this.overrideIndicatorCalculations(eventMode);
}
return await eventMode.execute();
}
private async precomputeIndicators(dataFrame: DataFrame): Promise<void> {
this.logger.info('Pre-computing indicators vectorized');
const close = dataFrame.getColumn('close');
const high = dataFrame.getColumn('high');
const low = dataFrame.getColumn('low');
// Import technical indicators from vector engine
const { TechnicalIndicators } = await import('@stock-bot/vector-engine');
// Pre-compute common indicators
this.precomputedIndicators.set('sma_20', TechnicalIndicators.sma(close, 20));
this.precomputedIndicators.set('sma_50', TechnicalIndicators.sma(close, 50));
this.precomputedIndicators.set('ema_12', TechnicalIndicators.ema(close, 12));
this.precomputedIndicators.set('ema_26', TechnicalIndicators.ema(close, 26));
this.precomputedIndicators.set('rsi', TechnicalIndicators.rsi(close));
this.precomputedIndicators.set('atr', TechnicalIndicators.atr(high, low, close));
const macd = TechnicalIndicators.macd(close);
this.precomputedIndicators.set('macd', macd.macd);
this.precomputedIndicators.set('macd_signal', macd.signal);
this.precomputedIndicators.set('macd_histogram', macd.histogram);
const bb = TechnicalIndicators.bollingerBands(close);
this.precomputedIndicators.set('bb_upper', bb.upper);
this.precomputedIndicators.set('bb_middle', bb.middle);
this.precomputedIndicators.set('bb_lower', bb.lower);
this.logger.info('Indicators pre-computed', {
indicators: Array.from(this.precomputedIndicators.keys()),
});
}
private overrideIndicatorCalculations(eventMode: EventMode): void {
// Override the event mode's indicator calculations to use pre-computed values
// This is a simplified approach - in production you'd want a more sophisticated interface
const _originalCalculateIndicators = (eventMode as any).calculateIndicators;
(eventMode as any).calculateIndicators = (symbol: string, index: number) => {
const indicators: Record<string, number> = {};
for (const [name, values] of this.precomputedIndicators.entries()) {
if (index < values.length) {
indicators[name] = values[index];
}
}
return indicators;
};
}
private async estimateDataSize(): Promise<number> {
// Estimate the number of data points for the backtest period
const startTime = new Date(this.context.startDate).getTime();
const endTime = new Date(this.context.endDate).getTime();
const timeRange = endTime - startTime;
// Assume 1-minute intervals (60000ms)
const estimatedPoints = Math.floor(timeRange / 60000);
this.logger.debug('Estimated data size', {
timeRange,
estimatedPoints,
threshold: this.config.vectorizedThreshold,
});
return estimatedPoints;
}
private async loadWarmupData(): Promise<any[]> {
// Load historical data for warmup phase
// This should load more data than just the warmup period for indicator calculations
const data = [];
const startTime = new Date(this.context.startDate).getTime();
const warmupEndTime = startTime + this.config.warmupPeriod * 60000;
// Add extra lookback for indicator calculations
const lookbackTime = startTime - 200 * 60000; // 200 periods lookback
for (let timestamp = lookbackTime; timestamp <= warmupEndTime; timestamp += 60000) {
const basePrice = 100 + Math.sin(timestamp / 1000000) * 10;
const volatility = 0.02;
const open = basePrice + (Math.random() - 0.5) * volatility * basePrice;
const close = open + (Math.random() - 0.5) * volatility * basePrice;
const high = Math.max(open, close) + Math.random() * volatility * basePrice;
const low = Math.min(open, close) - Math.random() * volatility * basePrice;
const volume = Math.floor(Math.random() * 10000) + 1000;
data.push({
timestamp,
symbol: this.context.symbol,
open,
high,
low,
close,
volume,
});
}
return data;
}
private createDataFrame(data: any[]): DataFrame {
return new DataFrame(data, {
columns: ['timestamp', 'symbol', 'open', 'high', 'low', 'close', 'volume'],
dtypes: {
timestamp: 'number',
symbol: 'string',
open: 'number',
high: 'number',
low: 'number',
close: 'number',
volume: 'number',
},
});
}
private generateStrategyCode(): string {
// Generate strategy code based on context
const strategy = this.context.strategy;
if (strategy.type === 'sma_crossover') {
return 'sma_crossover';
}
return strategy.code || 'sma_crossover';
}
private convertVectorizedResult(
vectorResult: VectorizedBacktestResult,
startTime: number
): BacktestResult {
return {
backtestId: this.context.backtestId,
strategy: this.context.strategy,
symbol: this.context.symbol,
startDate: this.context.startDate,
endDate: this.context.endDate,
mode: 'hybrid-vectorized',
duration: Date.now() - startTime,
trades: vectorResult.trades.map(trade => ({
id: `trade_${trade.entryIndex}_${trade.exitIndex}`,
symbol: this.context.symbol,
side: trade.side,
entryTime: vectorResult.timestamps[trade.entryIndex],
exitTime: vectorResult.timestamps[trade.exitIndex],
entryPrice: trade.entryPrice,
exitPrice: trade.exitPrice,
quantity: trade.quantity,
pnl: trade.pnl,
commission: 0,
slippage: 0,
})),
performance: {
totalReturn: vectorResult.metrics.totalReturns,
sharpeRatio: vectorResult.metrics.sharpeRatio,
maxDrawdown: vectorResult.metrics.maxDrawdown,
winRate: vectorResult.metrics.winRate,
profitFactor: vectorResult.metrics.profitFactor,
totalTrades: vectorResult.metrics.totalTrades,
winningTrades: vectorResult.trades.filter(t => t.pnl > 0).length,
losingTrades: vectorResult.trades.filter(t => t.pnl <= 0).length,
avgTrade: vectorResult.metrics.avgTrade,
avgWin:
vectorResult.trades.filter(t => t.pnl > 0).reduce((sum, t) => sum + t.pnl, 0) /
vectorResult.trades.filter(t => t.pnl > 0).length || 0,
avgLoss:
vectorResult.trades.filter(t => t.pnl <= 0).reduce((sum, t) => sum + t.pnl, 0) /
vectorResult.trades.filter(t => t.pnl <= 0).length || 0,
largestWin: Math.max(...vectorResult.trades.map(t => t.pnl), 0),
largestLoss: Math.min(...vectorResult.trades.map(t => t.pnl), 0),
},
equity: vectorResult.equity,
drawdown: vectorResult.metrics.drawdown,
metadata: {
mode: 'hybrid-vectorized',
dataPoints: vectorResult.timestamps.length,
signals: Object.keys(vectorResult.signals),
optimizations: ['vectorized_warmup', 'precomputed_indicators'],
},
};
}
private extractFinalPortfolio(warmupResult: BacktestResult): any {
// Extract the final portfolio state from warmup phase
const finalEquity = warmupResult.equity[warmupResult.equity.length - 1] || 10000;
return {
cash: finalEquity,
positions: [], // Simplified - in production would track actual positions
equity: finalEquity,
};
}
private combineResults(
warmupResult: BacktestResult,
eventResult: BacktestResult,
startTime: number
): BacktestResult {
// Combine results from both phases
const combinedTrades = [...warmupResult.trades, ...eventResult.trades];
const combinedEquity = [...warmupResult.equity, ...eventResult.equity];
const combinedDrawdown = [...(warmupResult.drawdown || []), ...(eventResult.drawdown || [])];
// Recalculate combined performance metrics
const totalPnL = combinedTrades.reduce((sum, trade) => sum + trade.pnl, 0);
const winningTrades = combinedTrades.filter(t => t.pnl > 0);
const losingTrades = combinedTrades.filter(t => t.pnl <= 0);
const grossProfit = winningTrades.reduce((sum, t) => sum + t.pnl, 0);
const grossLoss = Math.abs(losingTrades.reduce((sum, t) => sum + t.pnl, 0));
return {
backtestId: this.context.backtestId,
strategy: this.context.strategy,
symbol: this.context.symbol,
startDate: this.context.startDate,
endDate: this.context.endDate,
mode: 'hybrid',
duration: Date.now() - startTime,
trades: combinedTrades,
performance: {
totalReturn:
(combinedEquity[combinedEquity.length - 1] - combinedEquity[0]) / combinedEquity[0],
sharpeRatio: eventResult.performance.sharpeRatio, // Use event result for more accurate calculation
maxDrawdown: Math.max(...combinedDrawdown),
winRate: winningTrades.length / combinedTrades.length,
profitFactor: grossLoss !== 0 ? grossProfit / grossLoss : Infinity,
totalTrades: combinedTrades.length,
winningTrades: winningTrades.length,
losingTrades: losingTrades.length,
avgTrade: totalPnL / combinedTrades.length,
avgWin: grossProfit / winningTrades.length || 0,
avgLoss: grossLoss / losingTrades.length || 0,
largestWin: Math.max(...combinedTrades.map(t => t.pnl), 0),
largestLoss: Math.min(...combinedTrades.map(t => t.pnl), 0),
},
equity: combinedEquity,
drawdown: combinedDrawdown,
metadata: {
mode: 'hybrid',
phases: ['vectorized-warmup', 'event-driven'],
warmupPeriod: this.config.warmupPeriod,
optimizations: ['precomputed_indicators', 'hybrid_execution'],
warmupTrades: warmupResult.trades.length,
eventTrades: eventResult.trades.length,
},
};
}
async cleanup(): Promise<void> {
await super.cleanup();
await this.eventMode.cleanup();
await this.vectorizedMode.cleanup();
this.precomputedIndicators.clear();
this.logger.info('Hybrid mode cleanup completed');
}
}
export default HybridMode;

View file

@ -1,31 +0,0 @@
/**
* Live Trading Mode
* Executes orders through real brokers
*/
import { ExecutionMode, MarketData, Order, OrderResult } from '../../framework/execution-mode';
export class LiveMode extends ExecutionMode {
name = 'live';
async executeOrder(order: Order): Promise<OrderResult> {
this.logger.info('Executing live order', { orderId: order.id });
// TODO: Implement real broker integration
// This will connect to actual brokerage APIs
throw new Error('Live broker integration not implemented yet');
}
getCurrentTime(): Date {
return new Date(); // Real time
}
async getMarketData(_symbol: string): Promise<MarketData> {
// TODO: Get live market data
throw new Error('Live market data fetching not implemented yet');
}
async publishEvent(event: string, data: any): Promise<void> {
// TODO: Publish to real event bus (Dragonfly)
this.logger.debug('Publishing event', { event, data });
}
}

View file

@ -1,236 +0,0 @@
import { DataFrame } from '@stock-bot/data-frame';
import { EventBus } from '@stock-bot/event-bus';
import { getLogger } from '@stock-bot/logger';
import { VectorEngine, VectorizedBacktestResult } from '@stock-bot/vector-engine';
import { BacktestContext, BacktestResult, ExecutionMode } from '../framework/execution-mode';
export interface VectorizedModeConfig {
batchSize?: number;
enableOptimization?: boolean;
parallelProcessing?: boolean;
}
export class VectorizedMode extends ExecutionMode {
private vectorEngine: VectorEngine;
private config: VectorizedModeConfig;
private logger = getLogger('vectorized-mode');
constructor(context: BacktestContext, eventBus: EventBus, config: VectorizedModeConfig = {}) {
super(context, eventBus);
this.vectorEngine = new VectorEngine();
this.config = {
batchSize: 10000,
enableOptimization: true,
parallelProcessing: true,
...config,
};
}
async initialize(): Promise<void> {
await super.initialize();
this.logger.info('Vectorized mode initialized', {
backtestId: this.context.backtestId,
config: this.config,
});
}
async execute(): Promise<BacktestResult> {
const startTime = Date.now();
this.logger.info('Starting vectorized backtest execution');
try {
// Load all data at once for vectorized processing
const data = await this.loadHistoricalData();
// Convert to DataFrame format
const dataFrame = this.createDataFrame(data);
// Execute vectorized strategy
const strategyCode = this.generateStrategyCode();
const vectorResult = await this.vectorEngine.executeVectorizedStrategy(
dataFrame,
strategyCode
);
// Convert to standard backtest result format
const result = this.convertVectorizedResult(vectorResult, startTime);
// Emit completion event
await this.eventBus.publishBacktestUpdate(this.context.backtestId, 100, {
status: 'completed',
result,
});
this.logger.info('Vectorized backtest completed', {
backtestId: this.context.backtestId,
duration: Date.now() - startTime,
totalTrades: result.trades.length,
});
return result;
} catch (error) {
this.logger.error('Vectorized backtest failed', {
error,
backtestId: this.context.backtestId,
});
await this.eventBus.publishBacktestUpdate(this.context.backtestId, 0, {
status: 'failed',
error: error.message,
});
throw error;
}
}
private async loadHistoricalData(): Promise<any[]> {
// Load all historical data at once
// This is much more efficient than loading tick by tick
const data = [];
// Simulate loading data (in production, this would be a bulk database query)
const startTime = new Date(this.context.startDate).getTime();
const endTime = new Date(this.context.endDate).getTime();
const interval = 60000; // 1 minute intervals
for (let timestamp = startTime; timestamp <= endTime; timestamp += interval) {
// Simulate OHLCV data
const basePrice = 100 + Math.sin(timestamp / 1000000) * 10;
const volatility = 0.02;
const open = basePrice + (Math.random() - 0.5) * volatility * basePrice;
const close = open + (Math.random() - 0.5) * volatility * basePrice;
const high = Math.max(open, close) + Math.random() * volatility * basePrice;
const low = Math.min(open, close) - Math.random() * volatility * basePrice;
const volume = Math.floor(Math.random() * 10000) + 1000;
data.push({
timestamp,
symbol: this.context.symbol,
open,
high,
low,
close,
volume,
});
}
return data;
}
private createDataFrame(data: any[]): DataFrame {
return new DataFrame(data, {
columns: ['timestamp', 'symbol', 'open', 'high', 'low', 'close', 'volume'],
dtypes: {
timestamp: 'number',
symbol: 'string',
open: 'number',
high: 'number',
low: 'number',
close: 'number',
volume: 'number',
},
});
}
private generateStrategyCode(): string {
// Convert strategy configuration to vectorized strategy code
// This is a simplified example - in production you'd have a more sophisticated compiler
const strategy = this.context.strategy;
if (strategy.type === 'sma_crossover') {
return 'sma_crossover';
}
// Add more strategy types as needed
return strategy.code || 'sma_crossover';
}
private convertVectorizedResult(
vectorResult: VectorizedBacktestResult,
startTime: number
): BacktestResult {
return {
backtestId: this.context.backtestId,
strategy: this.context.strategy,
symbol: this.context.symbol,
startDate: this.context.startDate,
endDate: this.context.endDate,
mode: 'vectorized',
duration: Date.now() - startTime,
trades: vectorResult.trades.map(trade => ({
id: `trade_${trade.entryIndex}_${trade.exitIndex}`,
symbol: this.context.symbol,
side: trade.side,
entryTime: vectorResult.timestamps[trade.entryIndex],
exitTime: vectorResult.timestamps[trade.exitIndex],
entryPrice: trade.entryPrice,
exitPrice: trade.exitPrice,
quantity: trade.quantity,
pnl: trade.pnl,
commission: 0, // Simplified
slippage: 0,
})),
performance: {
totalReturn: vectorResult.metrics.totalReturns,
sharpeRatio: vectorResult.metrics.sharpeRatio,
maxDrawdown: vectorResult.metrics.maxDrawdown,
winRate: vectorResult.metrics.winRate,
profitFactor: vectorResult.metrics.profitFactor,
totalTrades: vectorResult.metrics.totalTrades,
winningTrades: vectorResult.trades.filter(t => t.pnl > 0).length,
losingTrades: vectorResult.trades.filter(t => t.pnl <= 0).length,
avgTrade: vectorResult.metrics.avgTrade,
avgWin:
vectorResult.trades.filter(t => t.pnl > 0).reduce((sum, t) => sum + t.pnl, 0) /
vectorResult.trades.filter(t => t.pnl > 0).length || 0,
avgLoss:
vectorResult.trades.filter(t => t.pnl <= 0).reduce((sum, t) => sum + t.pnl, 0) /
vectorResult.trades.filter(t => t.pnl <= 0).length || 0,
largestWin: Math.max(...vectorResult.trades.map(t => t.pnl), 0),
largestLoss: Math.min(...vectorResult.trades.map(t => t.pnl), 0),
},
equity: vectorResult.equity,
drawdown: vectorResult.metrics.drawdown,
metadata: {
mode: 'vectorized',
dataPoints: vectorResult.timestamps.length,
signals: Object.keys(vectorResult.signals),
optimizations: this.config.enableOptimization ? ['vectorized_computation'] : [],
},
};
}
async cleanup(): Promise<void> {
await super.cleanup();
this.logger.info('Vectorized mode cleanup completed');
}
// Batch processing capabilities
async batchBacktest(
strategies: Array<{ id: string; config: any }>
): Promise<Record<string, BacktestResult>> {
this.logger.info('Starting batch vectorized backtest', {
strategiesCount: strategies.length,
});
const data = await this.loadHistoricalData();
const dataFrame = this.createDataFrame(data);
const strategyConfigs = strategies.map(s => ({
id: s.id,
code: this.generateStrategyCode(),
}));
const batchResults = await this.vectorEngine.batchBacktest(dataFrame, strategyConfigs);
const results: Record<string, BacktestResult> = {};
for (const [strategyId, vectorResult] of Object.entries(batchResults)) {
results[strategyId] = this.convertVectorizedResult(vectorResult, Date.now());
}
return results;
}
}
export default VectorizedMode;

View file

@ -1,285 +0,0 @@
#!/usr/bin/env bun
/**
* Strategy Service CLI
* Command-line interface for running backtests and managing strategies
*/
import { program } from 'commander';
import { createEventBus } from '@stock-bot/event-bus';
import { getLogger } from '@stock-bot/logger';
import { EventMode } from '../backtesting/modes/event-mode';
import HybridMode from '../backtesting/modes/hybrid-mode';
import { LiveMode } from '../backtesting/modes/live-mode';
import VectorizedMode from '../backtesting/modes/vectorized-mode';
import { BacktestContext } from '../framework/execution-mode';
const logger = getLogger('strategy-cli');
interface CLIBacktestConfig {
strategy: string;
strategies: string;
symbol: string;
startDate: string;
endDate: string;
mode: 'live' | 'event' | 'vectorized' | 'hybrid';
initialCapital?: number;
config?: string;
output?: string;
verbose?: boolean;
}
async function runBacktest(options: CLIBacktestConfig): Promise<void> {
logger.info('Starting backtest from CLI', { options });
try {
// Initialize event bus
const eventBus = createEventBus({
serviceName: 'strategy-cli',
enablePersistence: false, // Disable Redis for CLI
});
// Create backtest context
const context: BacktestContext = {
backtestId: `cli_${Date.now()}`,
strategy: {
id: options.strategy,
name: options.strategy,
type: options.strategy,
code: options.strategy,
parameters: {},
},
symbol: options.symbol,
startDate: options.startDate,
endDate: options.endDate,
initialCapital: options.initialCapital || 10000,
mode: options.mode,
};
// Load additional config if provided
if (options.config) {
const configData = await loadConfig(options.config);
context.strategy.parameters = { ...context.strategy.parameters, ...configData };
}
// Create and execute the appropriate mode
let executionMode;
switch (options.mode) {
case 'live':
executionMode = new LiveMode(context, eventBus);
break;
case 'event':
executionMode = new EventMode(context, eventBus);
break;
case 'vectorized':
executionMode = new VectorizedMode(context, eventBus);
break;
case 'hybrid':
executionMode = new HybridMode(context, eventBus);
break;
default:
throw new Error(`Unknown execution mode: ${options.mode}`);
}
// Subscribe to progress updates
eventBus.subscribe('backtest.update', message => {
const { backtestId: _backtestId, progress, ...data } = message.data;
console.log(`Progress: ${progress}%`, data);
});
await executionMode.initialize();
const result = await executionMode.execute();
await executionMode.cleanup();
// Display results
displayResults(result);
// Save results if output specified
if (options.output) {
await saveResults(result, options.output);
}
await eventBus.close();
} catch (error) {
logger.error('Backtest failed', error);
process.exit(1);
}
}
async function loadConfig(configPath: string): Promise<any> {
try {
if (configPath.endsWith('.json')) {
const file = Bun.file(configPath);
return await file.json();
} else {
// Assume it's a JavaScript/TypeScript module
return await import(configPath);
}
} catch (error) {
logger.error('Failed to load config', { configPath, error });
throw new Error(`Failed to load config from ${configPath}: ${(error as Error).message}`);
}
}
function displayResults(result: any): void {
console.log('\n=== Backtest Results ===');
console.log(`Strategy: ${result.strategy.name}`);
console.log(`Symbol: ${result.symbol}`);
console.log(`Period: ${result.startDate} to ${result.endDate}`);
console.log(`Mode: ${result.mode}`);
console.log(`Duration: ${result.duration}ms`);
console.log('\n--- Performance ---');
console.log(`Total Return: ${(result.performance.totalReturn * 100).toFixed(2)}%`);
console.log(`Sharpe Ratio: ${result.performance.sharpeRatio.toFixed(3)}`);
console.log(`Max Drawdown: ${(result.performance.maxDrawdown * 100).toFixed(2)}%`);
console.log(`Win Rate: ${(result.performance.winRate * 100).toFixed(1)}%`);
console.log(`Profit Factor: ${result.performance.profitFactor.toFixed(2)}`);
console.log('\n--- Trading Stats ---');
console.log(`Total Trades: ${result.performance.totalTrades}`);
console.log(`Winning Trades: ${result.performance.winningTrades}`);
console.log(`Losing Trades: ${result.performance.losingTrades}`);
console.log(`Average Trade: ${result.performance.avgTrade.toFixed(2)}`);
console.log(`Average Win: ${result.performance.avgWin.toFixed(2)}`);
console.log(`Average Loss: ${result.performance.avgLoss.toFixed(2)}`);
console.log(`Largest Win: ${result.performance.largestWin.toFixed(2)}`);
console.log(`Largest Loss: ${result.performance.largestLoss.toFixed(2)}`);
if (result.metadata) {
console.log('\n--- Metadata ---');
Object.entries(result.metadata).forEach(([key, value]) => {
console.log(`${key}: ${Array.isArray(value) ? value.join(', ') : value}`);
});
}
}
async function saveResults(result: any, outputPath: string): Promise<void> {
try {
if (outputPath.endsWith('.json')) {
await Bun.write(outputPath, JSON.stringify(result, null, 2));
} else if (outputPath.endsWith('.csv')) {
const csv = convertTradesToCSV(result.trades);
await Bun.write(outputPath, csv);
} else {
// Default to JSON
await Bun.write(outputPath + '.json', JSON.stringify(result, null, 2));
}
logger.info(`\nResults saved to: ${outputPath}`);
} catch (error) {
logger.error('Failed to save results', { outputPath, error });
}
}
function convertTradesToCSV(trades: any[]): string {
if (trades.length === 0) {
return 'No trades executed\n';
}
const headers = Object.keys(trades[0]).join(',');
const rows = trades.map(trade =>
Object.values(trade)
.map(value => (typeof value === 'string' ? `"${value}"` : value))
.join(',')
);
return [headers, ...rows].join('\n');
}
async function listStrategies(): Promise<void> {
console.log('Available strategies:');
console.log(' sma_crossover - Simple Moving Average Crossover');
console.log(' ema_crossover - Exponential Moving Average Crossover');
console.log(' rsi_mean_reversion - RSI Mean Reversion');
console.log(' macd_trend - MACD Trend Following');
console.log(' bollinger_bands - Bollinger Bands Strategy');
// Add more as they're implemented
}
async function validateStrategy(strategy: string): Promise<void> {
console.log(`Validating strategy: ${strategy}`);
// TODO: Add strategy validation logic
// This could check if the strategy exists, has valid parameters, etc.
const validStrategies = [
'sma_crossover',
'ema_crossover',
'rsi_mean_reversion',
'macd_trend',
'bollinger_bands',
];
if (!validStrategies.includes(strategy)) {
console.warn(`Warning: Strategy '${strategy}' is not in the list of known strategies`);
console.log('Use --list-strategies to see available strategies');
} else {
console.log(`✓ Strategy '${strategy}' is valid`);
}
}
// CLI Commands
program.name('strategy-cli').description('Stock Trading Bot Strategy CLI').version('1.0.0');
program
.command('backtest')
.description('Run a backtest')
.requiredOption('-s, --strategy <strategy>', 'Strategy to test')
.requiredOption('--symbol <symbol>', 'Symbol to trade')
.requiredOption('--start-date <date>', 'Start date (YYYY-MM-DD)')
.requiredOption('--end-date <date>', 'End date (YYYY-MM-DD)')
.option('-m, --mode <mode>', 'Execution mode', 'vectorized')
.option('-c, --initial-capital <amount>', 'Initial capital', '10000')
.option('--config <path>', 'Configuration file path')
.option('-o, --output <path>', 'Output file path')
.option('-v, --verbose', 'Verbose output')
.action(async (options: CLIBacktestConfig) => {
await runBacktest(options);
});
program.command('list-strategies').description('List available strategies').action(listStrategies);
program
.command('validate')
.description('Validate a strategy')
.requiredOption('-s, --strategy <strategy>', 'Strategy to validate')
.action(async (options: CLIBacktestConfig) => {
await validateStrategy(options.strategy);
});
program
.command('compare')
.description('Compare multiple strategies')
.requiredOption('--strategies <strategies>', 'Comma-separated list of strategies')
.requiredOption('--symbol <symbol>', 'Symbol to trade')
.requiredOption('--start-date <date>', 'Start date (YYYY-MM-DD)')
.requiredOption('--end-date <date>', 'End date (YYYY-MM-DD)')
.option('-m, --mode <mode>', 'Execution mode', 'vectorized')
.option('-c, --initial-capital <amount>', 'Initial capital', '10000')
.option('-o, --output <path>', 'Output directory')
.action(async (options: CLIBacktestConfig) => {
const strategies = options.strategies.split(',').map((s: string) => s.trim());
console.log(`Comparing strategies: ${strategies.join(', ')}`);
const _results: any[] = [];
for (const strategy of strategies) {
console.log(`\nRunning ${strategy}...`);
try {
await runBacktest({
...options,
strategy,
output: options.output ? `${options.output}/${strategy}.json` : undefined,
});
} catch (error) {
console.error(`Failed to run ${strategy}:`, (error as Error).message);
}
}
console.log('\nComparison completed!');
});
// Parse command line arguments
program.parse();
export { listStrategies, runBacktest, validateStrategy };

View file

@ -1,80 +0,0 @@
/**
* Execution Mode Framework
* Base classes for different execution modes (live, event-driven, vectorized)
*/
import { getLogger } from '@stock-bot/logger';
const _logger = getLogger('execution-mode');
export interface Order {
id: string;
symbol: string;
side: 'BUY' | 'SELL';
quantity: number;
type: 'MARKET' | 'LIMIT';
price?: number;
timestamp: Date;
}
export interface OrderResult {
orderId: string;
symbol: string;
executedQuantity: number;
executedPrice: number;
commission: number;
slippage: number;
timestamp: Date;
executionTime: number;
}
export interface MarketData {
symbol: string;
timestamp: Date;
open: number;
high: number;
low: number;
close: number;
volume: number;
}
export abstract class ExecutionMode {
protected logger = getLogger(this.constructor.name);
abstract name: string;
abstract executeOrder(order: Order): Promise<OrderResult>;
abstract getCurrentTime(): Date;
abstract getMarketData(symbol: string): Promise<MarketData>;
abstract publishEvent(event: string, data: any): Promise<void>;
}
export enum BacktestMode {
LIVE = 'live',
EVENT_DRIVEN = 'event-driven',
VECTORIZED = 'vectorized',
HYBRID = 'hybrid',
}
export class ModeFactory {
static create(mode: BacktestMode, _config?: any): ExecutionMode {
switch (mode) {
case BacktestMode.LIVE:
// TODO: Import and create LiveMode
throw new Error('LiveMode not implemented yet');
case BacktestMode.EVENT_DRIVEN:
// TODO: Import and create EventBacktestMode
throw new Error('EventBacktestMode not implemented yet');
case BacktestMode.VECTORIZED:
// TODO: Import and create VectorBacktestMode
throw new Error('VectorBacktestMode not implemented yet');
case BacktestMode.HYBRID:
// TODO: Import and create HybridBacktestMode
throw new Error('HybridBacktestMode not implemented yet');
default:
throw new Error(`Unknown mode: ${mode}`);
}
}
}

View file

@ -1,89 +0,0 @@
/**
* Strategy Service - Multi-mode strategy execution and backtesting
*/
import { serve } from '@hono/node-server';
import { Hono } from 'hono';
import { loadEnvVariables } from '@stock-bot/config';
import { getLogger } from '@stock-bot/logger';
// Load environment variables
loadEnvVariables();
const app = new Hono();
const logger = getLogger('strategy-service');
const PORT = parseInt(process.env.STRATEGY_SERVICE_PORT || '3004');
// Health check endpoint
app.get('/health', c => {
return c.json({
service: 'strategy-service',
status: 'healthy',
timestamp: new Date().toISOString(),
});
});
// Strategy execution endpoints
app.post('/api/strategy/run', async c => {
const body = await c.req.json();
logger.info('Strategy run request', {
strategy: body.strategy,
mode: body.mode,
});
// TODO: Implement strategy execution
return c.json({
message: 'Strategy execution endpoint - not implemented yet',
strategy: body.strategy,
mode: body.mode,
});
});
// Backtesting endpoints
app.post('/api/backtest/event', async c => {
const body = await c.req.json();
logger.info('Event-driven backtest request', { strategy: body.strategy });
// TODO: Implement event-driven backtesting
return c.json({
message: 'Event-driven backtest endpoint - not implemented yet',
});
});
app.post('/api/backtest/vector', async c => {
const body = await c.req.json();
logger.info('Vectorized backtest request', { strategy: body.strategy });
// TODO: Implement vectorized backtesting
return c.json({
message: 'Vectorized backtest endpoint - not implemented yet',
});
});
app.post('/api/backtest/hybrid', async c => {
const body = await c.req.json();
logger.info('Hybrid backtest request', { strategy: body.strategy });
// TODO: Implement hybrid backtesting
return c.json({
message: 'Hybrid backtest endpoint - not implemented yet',
});
});
// Parameter optimization endpoint
app.post('/api/optimize', async c => {
const body = await c.req.json();
logger.info('Parameter optimization request', { strategy: body.strategy });
// TODO: Implement parameter optimization
return c.json({
message: 'Parameter optimization endpoint - not implemented yet',
});
});
// Start server
serve({
fetch: app.fetch,
port: PORT,
});
logger.info(`Strategy Service started on port ${PORT}`);

View file

@ -1,26 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": [
"node_modules",
"dist",
"**/*.test.ts",
"**/*.spec.ts",
"**/test/**",
"**/tests/**",
"**/__tests__/**"
],
"references": [
{ "path": "../../libs/types" },
{ "path": "../../libs/config" },
{ "path": "../../libs/logger" },
{ "path": "../../libs/utils" },
{ "path": "../../libs/strategy-engine" },
{ "path": "../../libs/event-bus" },
{ "path": "../../libs/shutdown" }
]
}

View file

@ -1,27 +0,0 @@
{
"extends": ["//"],
"tasks": {
"build": {
"dependsOn": [
"@stock-bot/types#build",
"@stock-bot/config#build",
"@stock-bot/logger#build",
"@stock-bot/utils#build",
"@stock-bot/strategy-engine#build",
"@stock-bot/event-bus#build",
"@stock-bot/shutdown#build"
],
"outputs": ["dist/**"],
"inputs": [
"src/**",
"package.json",
"tsconfig.json",
"!**/*.test.ts",
"!**/*.spec.ts",
"!**/test/**",
"!**/tests/**",
"!**/__tests__/**"
]
}
}
}