-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpaper-trading-test.js
More file actions
373 lines (314 loc) Β· 14 KB
/
paper-trading-test.js
File metadata and controls
373 lines (314 loc) Β· 14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
#!/usr/bin/env node
import { config } from './src/config/config.js';
import { KalshiAPI } from './src/api/kalshiApi.js';
import { KalshiWebSocket } from './src/websocket/kalshiWebSocket.js';
import FootballMeanReversionStrategy from './src/strategies/meanReversionStrategy.js';
import logger from './src/utils/logger.js';
/**
* Paper Trading Test Runner
* Runs the football mean reversion strategy with simulated capital
* to test performance without real money risk
*/
class PaperTradingTest {
constructor() {
this.api = new KalshiAPI();
this.websocket = new KalshiWebSocket();
this.strategy = new FootballMeanReversionStrategy(config);
// Paper trading simulation settings
this.startingCapital = 10000; // $10,000 starting capital
this.currentCapital = this.startingCapital;
this.positionValue = 0;
this.totalInvested = 0;
this.tradesExecuted = 0;
this.startTime = new Date();
// Performance tracking
this.performanceHistory = [];
this.lastReport = Date.now();
this.reportInterval = 30000; // Report every 30 seconds
// Strategy tuning for faster testing
this.strategy.lookbackPeriod = 5; // Very short for fast testing
this.strategy.deviationThreshold = 1.0; // Lower threshold for more signals
this.strategy.positionSize = 100; // $100 per position
// Track data collection
this.dataCollectionStatus = new Map(); // marketId -> data count
// Connect strategy events
this.websocket.on('ticker', (tickerData) => {
this.processTicker(tickerData);
});
console.log('π§ͺ Paper Trading Test Initialized');
console.log(`π° Starting Capital: $${this.startingCapital.toLocaleString()}`);
console.log(`π Lookback Period: ${this.strategy.lookbackPeriod} data points (FAST MODE)`);
console.log(`π Deviation Threshold: ${this.strategy.deviationThreshold} std dev (SENSITIVE)`);
console.log(`π΅ Position Size: $${this.strategy.positionSize} per trade`);
console.log('β‘ Optimized for fast signal generation');
console.log('');
}
/**
* Process ticker data and execute paper trades
*/
processTicker(tickerData) {
try {
const { marketId, marketTicker, price, yesBid, yesAsk } = tickerData;
// Get market details
let marketTitle = marketTicker;
if (this.strategy.testMarkets && this.strategy.testMarkets.has(marketId)) {
marketTitle = this.strategy.testMarkets.get(marketId).title;
}
// Track data collection for this market
if (!this.dataCollectionStatus.has(marketId)) {
this.dataCollectionStatus.set(marketId, 0);
}
const dataCount = this.dataCollectionStatus.get(marketId) + 1;
this.dataCollectionStatus.set(marketId, dataCount);
// Show progress occasionally
if (dataCount <= this.strategy.lookbackPeriod && Math.random() < 0.05) {
console.log(`π Collecting data for ${marketTicker}: ${dataCount}/${this.strategy.lookbackPeriod} data points`);
}
// Check if we should trade this market (accept ANY market for testing)
const shouldTrade = this.strategy.shouldTradeMarket(marketId, marketTicker, marketTitle) ||
(this.strategy.testMarkets && this.strategy.testMarkets.has(marketId));
if (!shouldTrade) {
return;
}
// Generate trading signals
const signal = this.strategy.generateSignals(marketId, marketTicker, marketTitle, tickerData);
// Execute paper trade if signal present
if (signal) {
this.executePaperTrade(signal);
}
// Update performance tracking
this.updatePerformance();
} catch (error) {
logger.error('Error processing ticker in paper trading test', { error: error.message });
}
}
/**
* Execute a paper trade based on signal
*/
executePaperTrade(signal) {
const { action, reason, marketId, marketTicker, price, yesBid, yesAsk } = signal;
if (action === 'BUY') {
// Check if we have enough capital
const requiredCapital = this.strategy.positionSize;
if (this.currentCapital >= requiredCapital) {
// Execute buy
this.currentCapital -= requiredCapital;
this.positionValue += requiredCapital;
this.totalInvested += requiredCapital;
this.tradesExecuted++;
// Log the trade
this.logTrade('BUY', marketTicker, price, requiredCapital, reason);
// Store position details
if (!this.strategy.activePositions.has(marketId)) {
this.strategy.activePositions.set(marketId, {
side: 'LONG',
entryPrice: price,
entryTime: new Date(),
quantity: this.strategy.positionSize,
marketId,
marketTicker,
invested: requiredCapital
});
}
} else {
console.log(`β οΈ [INSUFFICIENT CAPITAL] Cannot buy ${marketTicker} - Need $${requiredCapital}, Have $${this.currentCapital.toFixed(2)}`);
}
} else if (action === 'SELL') {
const position = this.strategy.activePositions.get(marketId);
if (position) {
// Calculate profit/loss
const currentValue = (price / position.entryPrice) * position.invested;
const profit = currentValue - position.invested;
const profitPercent = (profit / position.invested) * 100;
// Execute sell
this.currentCapital += currentValue;
this.positionValue -= position.invested;
this.tradesExecuted++;
// Log the trade
this.logTrade('SELL', marketTicker, price, currentValue, reason, profit, profitPercent);
// Remove position
this.strategy.activePositions.delete(marketId);
// Update strategy stats
this.strategy.totalTrades++;
if (profit > 0) this.strategy.winningTrades++;
this.strategy.totalProfit += profit;
}
}
}
/**
* Log a trade execution
*/
logTrade(action, marketTicker, price, amount, reason, profit = null, profitPercent = null) {
const timestamp = new Date().toLocaleTimeString();
if (action === 'BUY') {
console.log(`\nπ [${timestamp}] PAPER BUY: ${marketTicker}`);
console.log(` π° Price: $${price} | Amount: $${amount}`);
console.log(` π Reason: ${reason}`);
console.log(` π³ Capital Remaining: $${this.currentCapital.toFixed(2)}`);
console.log(` π Active Positions: ${this.strategy.activePositions.size}`);
} else {
console.log(`\nπ° [${timestamp}] PAPER SELL: ${marketTicker}`);
console.log(` π΅ Price: $${price} | Amount: $${amount.toFixed(2)}`);
console.log(` π Profit: $${profit.toFixed(2)} (${profitPercent.toFixed(1)}%)`);
console.log(` π Reason: ${reason}`);
console.log(` π³ Capital: $${this.currentCapital.toFixed(2)}`);
}
}
/**
* Update performance tracking
*/
updatePerformance() {
const now = Date.now();
if (now - this.lastReport >= this.reportInterval) {
this.generatePerformanceReport();
this.lastReport = now;
}
}
/**
* Generate performance report
*/
generatePerformanceReport() {
const totalValue = this.currentCapital + this.positionValue;
const totalReturn = totalValue - this.startingCapital;
const returnPercent = (totalReturn / this.startingCapital) * 100;
const elapsed = Math.floor((Date.now() - this.startTime.getTime()) / 1000);
console.log(`\nπ === PAPER TRADING PERFORMANCE (${elapsed}s) ===`);
console.log(`π° Starting Capital: $${this.startingCapital.toLocaleString()}`);
console.log(`π³ Available Cash: $${this.currentCapital.toFixed(2)}`);
console.log(`π Position Value: $${this.positionValue.toFixed(2)}`);
console.log(`π Total Portfolio: $${totalValue.toFixed(2)}`);
console.log(`π Total Return: $${totalReturn.toFixed(2)} (${returnPercent.toFixed(2)}%)`);
console.log(`π Trades Executed: ${this.tradesExecuted}`);
console.log(`π― Active Positions: ${this.strategy.activePositions.size}`);
console.log(`π Win Rate: ${this.strategy.totalTrades > 0 ? ((this.strategy.winningTrades / this.strategy.totalTrades) * 100).toFixed(1) : 0}%`);
console.log(`ββββββββββββββββββββββββββββββββββββββββββββ\n`);
// Store performance snapshot
this.performanceHistory.push({
timestamp: new Date(),
totalValue,
totalReturn,
returnPercent,
tradesExecuted: this.tradesExecuted,
activePositions: this.strategy.activePositions.size
});
}
/**
* Initialize and start the paper trading test
*/
async start() {
try {
console.log('π Starting Paper Trading Test...\n');
// Test API connection
console.log('π‘ Connecting to Kalshi API...');
await this.api.testConnection();
console.log('β
API connected successfully\n');
// Connect WebSocket
console.log('π Connecting to Kalshi WebSocket...');
await this.websocket.connect();
console.log('β
WebSocket connected successfully\n');
// Discover and subscribe to markets
console.log('π Discovering markets...');
await this.subscribeToMarkets();
console.log('β
Subscribed to market data\n');
console.log('π Paper Trading Test is now LIVE!');
console.log('π Watching for mean reversion opportunities...');
console.log('π‘ Press Ctrl+C to stop and see final results\n');
// Set up graceful shutdown
process.on('SIGINT', () => {
this.stop();
});
} catch (error) {
console.error('β Failed to start paper trading test:', error.message);
process.exit(1);
}
}
/**
* Subscribe to markets for testing
*/
async subscribeToMarkets() {
try {
const markets = await this.api.getMarkets(1000);
if (markets.markets) {
// Find football markets
const footballMarkets = markets.markets.filter(market => {
const text = `${market.ticker} ${market.title}`.toUpperCase();
return ['NFL', 'NFLGAME', 'NFLWINS', 'NFLPLAYOFF', 'NFLMVP', 'NFLAFCCHAMP', 'NFLNFCCHAMP',
'SUPERBOWL', 'PLAYOFF', 'CHAMPIONSHIP', 'FOOTBALL'].some(keyword =>
text.includes(keyword)
);
});
// Always use active markets for more data and faster testing
const testMarkets = markets.markets.filter(market =>
market.status === 'active'
).slice(0, 50); // Use more markets for better signal opportunities
console.log(`π Using ${testMarkets.length} active markets for testing`);
console.log(`π Football markets found: ${footballMarkets.length}`);
testMarkets.slice(0, 5).forEach(market => {
console.log(` β’ ${market.ticker} - ${market.title}`);
});
// Add all test markets to strategy
testMarkets.forEach(market => {
this.strategy.addTestMarket(market.id, market.ticker, market.title);
});
// Subscribe to general ticker feed
this.websocket.subscribe('ticker', {});
}
} catch (error) {
console.error('Failed to subscribe to markets:', error.message);
}
}
/**
* Stop the paper trading test and show final results
*/
stop() {
console.log('\nπ Stopping Paper Trading Test...\n');
// Disconnect WebSocket
if (this.websocket) {
this.websocket.disconnect();
}
// Generate final report
this.generateFinalReport();
console.log('π Paper Trading Test completed. Goodbye!');
process.exit(0);
}
/**
* Generate final comprehensive report
*/
generateFinalReport() {
const totalValue = this.currentCapital + this.positionValue;
const totalReturn = totalValue - this.startingCapital;
const returnPercent = (totalReturn / this.startingCapital) * 100;
const elapsed = Math.floor((Date.now() - this.startTime.getTime()) / 1000);
const minutes = Math.floor(elapsed / 60);
const seconds = elapsed % 60;
console.log('π === FINAL PAPER TRADING RESULTS ===');
console.log(`β±οΈ Test Duration: ${minutes}m ${seconds}s`);
console.log(`π° Starting Capital: $${this.startingCapital.toLocaleString()}`);
console.log(`π³ Final Cash: $${this.currentCapital.toFixed(2)}`);
console.log(`π Position Value: $${this.positionValue.toFixed(2)}`);
console.log(`π Final Portfolio: $${totalValue.toFixed(2)}`);
console.log(`π Total Return: $${totalReturn.toFixed(2)} (${returnPercent.toFixed(2)}%)`);
console.log(`π Total Trades: ${this.tradesExecuted}`);
console.log(`π Winning Trades: ${this.strategy.winningTrades}`);
console.log(`π Win Rate: ${this.strategy.totalTrades > 0 ? ((this.strategy.winningTrades / this.strategy.totalTrades) * 100).toFixed(1) : 0}%`);
console.log(`π― Open Positions: ${this.strategy.activePositions.size}`);
if (this.strategy.activePositions.size > 0) {
console.log('\nπ Open Positions:');
for (const [marketId, position] of this.strategy.activePositions) {
console.log(` β’ ${position.marketTicker}: $${position.invested} @ $${position.entryPrice}`);
}
}
console.log('\nπ― Strategy Performance:');
console.log(` Lookback Period: ${this.strategy.lookbackPeriod} data points`);
console.log(` Deviation Threshold: ${this.strategy.deviationThreshold} std dev`);
console.log(` Position Size: $${this.strategy.positionSize}`);
console.log(` Total Strategy Profit: $${this.strategy.totalProfit.toFixed(2)}`);
console.log('βββββββββββββββββββββββββββββββββββββββββββ\n');
}
}
// Run the paper trading test
const test = new PaperTradingTest();
test.start().catch((error) => {
console.error('β Paper trading test failed:', error.message);
process.exit(1);
});