finished initial backtest / engine

This commit is contained in:
Boki 2025-07-03 12:49:22 -04:00
parent 55b4ca78c9
commit c106a719e8
18 changed files with 1571 additions and 180 deletions

View file

@ -74,7 +74,10 @@ export abstract class BaseStrategy extends EventEmitter {
// Market data handling
async onMarketData(data: MarketData): Promise<void> {
if (!this.isActive) return;
if (!this.isActive) {
logger.info(`[BaseStrategy] Strategy ${this.config.id} received market data but is not active`);
return;
}
try {
// Update any indicators or state
@ -210,20 +213,27 @@ export abstract class BaseStrategy extends EventEmitter {
// Check if we already have a position
const currentPosition = this.getPosition(signal.symbol);
// Use quantity from signal metadata if provided
const quantity = signal.metadata?.quantity || this.calculatePositionSize(signal);
logger.info(`[BaseStrategy] Converting signal to order: ${signal.type} ${quantity} ${signal.symbol}, current position: ${currentPosition}`);
// Simple logic - can be overridden by specific strategies
if (signal.type === 'buy' && currentPosition <= 0) {
if (signal.type === 'buy') {
// Allow buying to open long or close short
return {
symbol: signal.symbol,
side: 'buy',
quantity: this.calculatePositionSize(signal),
quantity: quantity,
orderType: 'market',
timeInForce: 'DAY'
};
} else if (signal.type === 'sell' && currentPosition >= 0) {
} else if (signal.type === 'sell') {
// Allow selling to close long or open short
return {
symbol: signal.symbol,
side: 'sell',
quantity: this.calculatePositionSize(signal),
quantity: quantity,
orderType: 'market',
timeInForce: 'DAY'
};

View file

@ -163,6 +163,12 @@ export class StrategyManager extends EventEmitter {
private async handleMarketData(data: MarketData): Promise<void> {
// Forward to all active strategies
if (this.activeStrategies.size === 0) {
this.container.logger.info(`[StrategyManager] No active strategies to process market data! All strategies: ${Array.from(this.strategies.keys()).join(', ')}`);
} else {
this.container.logger.info(`[StrategyManager] Forwarding market data to ${this.activeStrategies.size} active strategies: ${Array.from(this.activeStrategies).join(', ')}`);
}
for (const strategyId of this.activeStrategies) {
const strategy = this.strategies.get(strategyId);
if (strategy) {
@ -267,6 +273,10 @@ export class StrategyManager extends EventEmitter {
getStrategy(strategyId: string): BaseStrategy | undefined {
return this.strategies.get(strategyId);
}
getAllStrategies(): BaseStrategy[] {
return Array.from(this.strategies.values());
}
async shutdown(): Promise<void> {
this.container.logger.info('Shutting down strategy manager...');

View file

@ -7,6 +7,7 @@ const logger = getLogger('SimpleMovingAverageCrossover');
export class SimpleMovingAverageCrossover extends BaseStrategy {
private priceHistory = new Map<string, number[]>();
private lastTradeTime = new Map<string, number>();
private barCount = new Map<string, number>();
private totalSignals = 0;
// Strategy parameters
@ -30,12 +31,17 @@ export class SimpleMovingAverageCrossover extends BaseStrategy {
// Update price history
if (!this.priceHistory.has(symbol)) {
this.priceHistory.set(symbol, []);
this.barCount.set(symbol, 0);
logger.info(`📊 Starting to track ${symbol} @ ${price}`);
}
const history = this.priceHistory.get(symbol)!;
history.push(price);
// Increment bar count
const currentBar = (this.barCount.get(symbol) || 0) + 1;
this.barCount.set(symbol, currentBar);
// Keep only needed history
if (history.length > this.SLOW_PERIOD * 2) {
history.shift();
@ -74,8 +80,9 @@ export class SimpleMovingAverageCrossover extends BaseStrategy {
const timestamp = new Date(data.data.timestamp).toISOString().split('T')[0];
// Check minimum holding period
const currentBar = this.barCount.get(symbol) || 0;
const lastTradeBar = this.lastTradeTime.get(symbol) || 0;
const barsSinceLastTrade = lastTradeBar > 0 ? history.length - lastTradeBar : Number.MAX_SAFE_INTEGER;
const barsSinceLastTrade = lastTradeBar > 0 ? currentBar - lastTradeBar : Number.MAX_SAFE_INTEGER;
// Detect crossovers FIRST
const goldenCross = prevFastMA <= prevSlowMA && fastMA > slowMA;
@ -87,7 +94,7 @@ export class SimpleMovingAverageCrossover extends BaseStrategy {
const masAreClose = Math.abs(maDiffPct) < 1.0; // Within 1%
if (history.length % this.DEBUG_INTERVAL === 0 || masAreClose || goldenCross || deathCross) {
logger.info(`${symbol} @ ${timestamp} [Bar ${history.length}]:`);
logger.info(`${symbol} @ ${timestamp} [Bar ${currentBar}]:`);
logger.info(` Price: $${currentPrice.toFixed(2)}`);
logger.info(` Fast MA (${this.FAST_PERIOD}): $${fastMA.toFixed(2)}`);
logger.info(` Slow MA (${this.SLOW_PERIOD}): $${slowMA.toFixed(2)}`);
@ -131,7 +138,7 @@ export class SimpleMovingAverageCrossover extends BaseStrategy {
}
};
this.lastTradeTime.set(symbol, history.length);
this.lastTradeTime.set(symbol, currentBar);
this.totalSignals++;
logger.info(`👉 Total signals generated: ${this.totalSignals}`);
return signal;
@ -160,7 +167,7 @@ export class SimpleMovingAverageCrossover extends BaseStrategy {
}
};
this.lastTradeTime.set(symbol, history.length);
this.lastTradeTime.set(symbol, currentBar);
this.totalSignals++;
logger.info(`👉 Total signals generated: ${this.totalSignals}`);
return signal;
@ -194,7 +201,7 @@ export class SimpleMovingAverageCrossover extends BaseStrategy {
}
};
this.lastTradeTime.set(symbol, history.length);
this.lastTradeTime.set(symbol, currentBar);
this.totalSignals++;
logger.info(`👉 Total signals generated: ${this.totalSignals}`);
return signal;
@ -222,7 +229,7 @@ export class SimpleMovingAverageCrossover extends BaseStrategy {
}
};
this.lastTradeTime.set(symbol, history.length);
this.lastTradeTime.set(symbol, currentBar);
this.totalSignals++;
logger.info(`👉 Total signals generated: ${this.totalSignals}`);
return signal;