smalinin commited on
Commit
3b4d11f
·
verified ·
1 Parent(s): 5915675

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +656 -19
index.html CHANGED
@@ -1,19 +1,656 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Backtrader Strategy Simulator</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
17
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
18
+ min-height: 100vh;
19
+ color: #333;
20
+ }
21
+
22
+ .header {
23
+ background: rgba(255, 255, 255, 0.95);
24
+ backdrop-filter: blur(10px);
25
+ padding: 1rem 2rem;
26
+ box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
27
+ display: flex;
28
+ justify-content: space-between;
29
+ align-items: center;
30
+ }
31
+
32
+ .header h1 {
33
+ color: #2c3e50;
34
+ font-size: 1.8rem;
35
+ font-weight: 700;
36
+ }
37
+
38
+ .header a {
39
+ color: #667eea;
40
+ text-decoration: none;
41
+ font-weight: 600;
42
+ transition: color 0.3s ease;
43
+ }
44
+
45
+ .header a:hover {
46
+ color: #764ba2;
47
+ }
48
+
49
+ .container {
50
+ max-width: 1400px;
51
+ margin: 2rem auto;
52
+ padding: 0 1rem;
53
+ }
54
+
55
+ .controls-panel {
56
+ background: rgba(255, 255, 255, 0.95);
57
+ backdrop-filter: blur(10px);
58
+ border-radius: 20px;
59
+ padding: 2rem;
60
+ margin-bottom: 2rem;
61
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
62
+ }
63
+
64
+ .controls-grid {
65
+ display: grid;
66
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
67
+ gap: 1.5rem;
68
+ margin-bottom: 2rem;
69
+ }
70
+
71
+ .control-group {
72
+ display: flex;
73
+ flex-direction: column;
74
+ }
75
+
76
+ .control-group label {
77
+ font-weight: 600;
78
+ margin-bottom: 0.5rem;
79
+ color: #2c3e50;
80
+ }
81
+
82
+ .control-group input {
83
+ padding: 0.75rem;
84
+ border: 2px solid #e1e8ed;
85
+ border-radius: 10px;
86
+ font-size: 1rem;
87
+ transition: border-color 0.3s ease;
88
+ }
89
+
90
+ .control-group input:focus {
91
+ outline: none;
92
+ border-color: #667eea;
93
+ }
94
+
95
+ .btn {
96
+ background: linear-gradient(135deg, #667eea, #764ba2);
97
+ color: white;
98
+ border: none;
99
+ padding: 1rem 2rem;
100
+ border-radius: 50px;
101
+ font-size: 1rem;
102
+ font-weight: 600;
103
+ cursor: pointer;
104
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
105
+ }
106
+
107
+ .btn:hover {
108
+ transform: translateY(-2px);
109
+ box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
110
+ }
111
+
112
+ .btn:active {
113
+ transform: translateY(0);
114
+ }
115
+
116
+ .chart-container {
117
+ background: rgba(255, 255, 255, 0.95);
118
+ backdrop-filter: blur(10px);
119
+ border-radius: 20px;
120
+ padding: 2rem;
121
+ margin-bottom: 2rem;
122
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
123
+ }
124
+
125
+ .metrics-grid {
126
+ display: grid;
127
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
128
+ gap: 1.5rem;
129
+ }
130
+
131
+ .metric-card {
132
+ background: rgba(255, 255, 255, 0.95);
133
+ backdrop-filter: blur(10px);
134
+ border-radius: 20px;
135
+ padding: 1.5rem;
136
+ text-align: center;
137
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
138
+ transition: transform 0.2s ease;
139
+ }
140
+
141
+ .metric-card:hover {
142
+ transform: translateY(-5px);
143
+ }
144
+
145
+ .metric-value {
146
+ font-size: 2rem;
147
+ font-weight: 700;
148
+ margin-bottom: 0.5rem;
149
+ }
150
+
151
+ .metric-label {
152
+ color: #7f8c8d;
153
+ font-size: 0.9rem;
154
+ text-transform: uppercase;
155
+ letter-spacing: 1px;
156
+ }
157
+
158
+ .positive {
159
+ color: #27ae60;
160
+ }
161
+
162
+ .negative {
163
+ color: #e74c3c;
164
+ }
165
+
166
+ .trades-table {
167
+ background: rgba(255, 255, 255, 0.95);
168
+ backdrop-filter: blur(10px);
169
+ border-radius: 20px;
170
+ padding: 2rem;
171
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
172
+ overflow-x: auto;
173
+ }
174
+
175
+ table {
176
+ width: 100%;
177
+ border-collapse: collapse;
178
+ margin-top: 1rem;
179
+ }
180
+
181
+ th, td {
182
+ padding: 1rem;
183
+ text-align: left;
184
+ border-bottom: 1px solid #ecf0f1;
185
+ }
186
+
187
+ th {
188
+ background: #f8f9fa;
189
+ font-weight: 600;
190
+ color: #2c3e50;
191
+ }
192
+
193
+ tr:hover {
194
+ background: #f8f9fa;
195
+ }
196
+
197
+ .strategy-info {
198
+ background: rgba(255, 255, 255, 0.95);
199
+ backdrop-filter: blur(10px);
200
+ border-radius: 20px;
201
+ padding: 2rem;
202
+ margin-bottom: 2rem;
203
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
204
+ }
205
+
206
+ .strategy-info h3 {
207
+ color: #2c3e50;
208
+ margin-bottom: 1rem;
209
+ }
210
+
211
+ .strategy-info p {
212
+ line-height: 1.6;
213
+ color: #555;
214
+ }
215
+
216
+ @media (max-width: 768px) {
217
+ .header {
218
+ flex-direction: column;
219
+ gap: 1rem;
220
+ text-align: center;
221
+ }
222
+
223
+ .controls-grid {
224
+ grid-template-columns: 1fr;
225
+ }
226
+
227
+ .metrics-grid {
228
+ grid-template-columns: repeat(2, 1fr);
229
+ }
230
+ }
231
+
232
+ .loading {
233
+ display: none;
234
+ text-align: center;
235
+ padding: 2rem;
236
+ color: #667eea;
237
+ }
238
+
239
+ .spinner {
240
+ border: 4px solid #f3f3f3;
241
+ border-top: 4px solid #667eea;
242
+ border-radius: 50%;
243
+ width: 40px;
244
+ height: 40px;
245
+ animation: spin 1s linear infinite;
246
+ margin: 0 auto 1rem;
247
+ }
248
+
249
+ @keyframes spin {
250
+ 0% { transform: rotate(0deg); }
251
+ 100% { transform: rotate(360deg); }
252
+ }
253
+ </style>
254
+ </head>
255
+ <body>
256
+ <div class="header">
257
+ <h1>🚀 Backtrader Strategy Simulator</h1>
258
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">Built with anycoder</a>
259
+ </div>
260
+
261
+ <div class="container">
262
+ <div class="strategy-info">
263
+ <h3>📈 Moving Average Crossover Strategy</h3>
264
+ <p>This simulator implements a classic dual moving average crossover strategy. The strategy generates buy signals when the short-term moving average crosses above the long-term moving average, and sell signals when it crosses below. This approach aims to capture trending movements in the market while filtering out short-term noise.</p>
265
+ </div>
266
+
267
+ <div class="controls-panel">
268
+ <h3 style="margin-bottom: 1.5rem; color: #2c3e50;">Strategy Parameters</h3>
269
+ <div class="controls-grid">
270
+ <div class="control-group">
271
+ <label for="shortMA">Short MA Period</label>
272
+ <input type="number" id="shortMA" value="10" min="1" max="100">
273
+ </div>
274
+ <div class="control-group">
275
+ <label for="longMA">Long MA Period</label>
276
+ <input type="number" id="longMA" value="20" min="1" max="200">
277
+ </div>
278
+ <div class="control-group">
279
+ <label for="initialCapital">Initial Capital ($)</label>
280
+ <input type="number" id="initialCapital" value="10000" min="1000" step="100">
281
+ </div>
282
+ <div class="control-group">
283
+ <label for="dataPoints">Data Points</label>
284
+ <input type="number" id="dataPoints" value="100" min="50" max="500">
285
+ </div>
286
+ </div>
287
+ <button class="btn" onclick="runBacktest()">▶️ Run Backtest</button>
288
+ </div>
289
+
290
+ <div class="loading" id="loading">
291
+ <div class="spinner"></div>
292
+ <p>Running backtest simulation...</p>
293
+ </div>
294
+
295
+ <div class="chart-container">
296
+ <canvas id="priceChart"></canvas>
297
+ </div>
298
+
299
+ <div class="metrics-grid" id="metricsGrid" style="display: none;">
300
+ <div class="metric-card">
301
+ <div class="metric-value" id="totalReturn">-</div>
302
+ <div class="metric-label">Total Return</div>
303
+ </div>
304
+ <div class="metric-card">
305
+ <div class="metric-value" id="sharpeRatio">-</div>
306
+ <div class="metric-label">Sharpe Ratio</div>
307
+ </div>
308
+ <div class="metric-card">
309
+ <div class="metric-value" id="maxDrawdown">-</div>
310
+ <div class="metric-label">Max Drawdown</div>
311
+ </div>
312
+ <div class="metric-card">
313
+ <div class="metric-value" id="winRate">-</div>
314
+ <div class="metric-label">Win Rate</div>
315
+ </div>
316
+ <div class="metric-card">
317
+ <div class="metric-value" id="totalTrades">-</div>
318
+ <div class="metric-label">Total Trades</div>
319
+ </div>
320
+ <div class="metric-card">
321
+ <div class="metric-value" id="finalValue">-</div>
322
+ <div class="metric-label">Final Value</div>
323
+ </div>
324
+ </div>
325
+
326
+ <div class="trades-table" id="tradesTable" style="display: none;">
327
+ <h3 style="margin-bottom: 1rem; color: #2c3e50;">Trade History</h3>
328
+ <table>
329
+ <thead>
330
+ <tr>
331
+ <th>Trade #</th>
332
+ <th>Date</th>
333
+ <th>Action</th>
334
+ <th>Price</th>
335
+ <th>Quantity</th>
336
+ <th>Portfolio Value</th>
337
+ <th>P&L</th>
338
+ </tr>
339
+ </thead>
340
+ <tbody id="tradesTableBody">
341
+ </tbody>
342
+ </table>
343
+ </div>
344
+ </div>
345
+
346
+ <script>
347
+ // Global variables
348
+ let chart = null;
349
+ let currentData = [];
350
+ let trades = [];
351
+
352
+ // Generate sample price data with trend and noise
353
+ function generatePriceData(dataPoints) {
354
+ const data = [];
355
+ let price = 100 + Math.random() * 50; // Start with random price between 100-150
356
+
357
+ for (let i = 0; i < dataPoints; i++) {
358
+ // Add trend component and random walk
359
+ const trend = Math.sin(i * 0.1) * 2 + Math.random() * 3 - 1.5;
360
+ price += trend;
361
+ price = Math.max(price, 50); // Minimum price floor
362
+
363
+ data.push({
364
+ index: i,
365
+ price: parseFloat(price.toFixed(2)),
366
+ date: new Date(2024, 0, 1 + i).toISOString().split('T')[0]
367
+ });
368
+ }
369
+ return data;
370
+ }
371
+
372
+ // Calculate moving average
373
+ function calculateMA(data, period) {
374
+ return data.map((item, index) => {
375
+ if (index < period - 1) return null;
376
+
377
+ const sum = data.slice(index - period + 1, index + 1)
378
+ .reduce((acc, curr) => acc + curr.price, 0);
379
+ return parseFloat((sum / period).toFixed(2));
380
+ });
381
+ }
382
+
383
+ // Backtrader Strategy Implementation
384
+ class MovingAverageCrossoverStrategy {
385
+ constructor(shortMA, longMA, initialCapital) {
386
+ this.shortMA = shortMA;
387
+ this.longMA = longMA;
388
+ this.initialCapital = initialCapital;
389
+ this.position = 0; // 0 = no position, 1 = long position
390
+ this.cash = initialCapital;
391
+ this.shares = 0;
392
+ this.entryPrice = 0;
393
+ this.trades = [];
394
+ this.portfolioHistory = [];
395
+ }
396
+
397
+ next(dataPoint, shortMAValue, longMAValue, index) {
398
+ // Skip if we don't have enough data for MAs
399
+ if (!shortMAValue || !longMAValue) return;
400
+
401
+ const portfolioValue = this.cash + (this.shares * dataPoint.price);
402
+ this.portfolioHistory.push(portfolioValue);
403
+
404
+ // Buy signal: Short MA crosses above Long MA
405
+ if (shortMAValue > longMAValue && this.position === 0) {
406
+ this.buy(dataPoint, index);
407
+ }
408
+ // Sell signal: Short MA crosses below Long MA
409
+ else if (shortMAValue < longMAValue && this.position === 1) {
410
+ this.sell(dataPoint, index);
411
+ }
412
+ }
413
+
414
+ buy(dataPoint, index) {
415
+ this.shares = Math.floor(this.cash / dataPoint.price);
416
+ this.cash = this.cash - (this.shares * dataPoint.price);
417
+ this.position = 1;
418
+ this.entryPrice = dataPoint.price;
419
+
420
+ const trade = {
421
+ id: this.trades.length + 1,
422
+ date: dataPoint.date,
423
+ action: 'BUY',
424
+ price: dataPoint.price,
425
+ quantity: this.shares,
426
+ portfolioValue: this.cash + (this.shares * dataPoint.price),
427
+ pnl: 0
428
+ };
429
+
430
+ this.trades.push(trade);
431
+ }
432
+
433
+ sell(dataPoint, index) {
434
+ const pnl = (dataPoint.price - this.entryPrice) * this.shares;
435
+ this.cash = this.cash + (this.shares * dataPoint.price);
436
+ this.position = 0;
437
+
438
+ const trade = {
439
+ id: this.trades.length + 1,
440
+ date: dataPoint.date,
441
+ action: 'SELL',
442
+ price: dataPoint.price,
443
+ quantity: this.shares,
444
+ portfolioValue: this.cash,
445
+ pnl: parseFloat(pnl.toFixed(2))
446
+ };
447
+
448
+ this.trades.push(trade);
449
+ }
450
+
451
+ getMetrics() {
452
+ const finalValue = this.cash + (this.shares * currentData[currentData.length - 1]?.price || 0);
453
+ const totalReturn = ((finalValue - this.initialCapital) / this.initialCapital * 100);
454
+
455
+ // Calculate Sharpe ratio
456
+ const returns = [];
457
+ for (let i = 1; i < this.portfolioHistory.length; i++) {
458
+ const dailyReturn = (this.portfolioHistory[i] - this.portfolioHistory[i-1]) / this.portfolioHistory[i-1];
459
+ returns.push(dailyReturn);
460
+ }
461
+
462
+ const avgReturn = returns.reduce((a, b) => a + b, 0) / returns.length;
463
+ const stdDev = Math.sqrt(returns.reduce((sum, ret) => sum + Math.pow(ret - avgReturn, 2), 0) / returns.length);
464
+ const sharpeRatio = stdDev === 0 ? 0 : (avgReturn / stdDev) * Math.sqrt(252);
465
+
466
+ // Calculate max drawdown
467
+ let peak = this.initialCapital;
468
+ let maxDrawdown = 0;
469
+ this.portfolioHistory.forEach(value => {
470
+ if (value > peak) peak = value;
471
+ const drawdown = ((peak - value) / peak) * 100;
472
+ maxDrawdown = Math.max(maxDrawdown, drawdown);
473
+ });
474
+
475
+ // Calculate win rate
476
+ const profitableTrades = this.trades.filter(trade => trade.pnl > 0).length;
477
+ const sellTrades = this.trades.filter(trade => trade.action === 'SELL');
478
+ const winRate = sellTrades.length > 0 ? (profitableTrades / sellTrades.length * 100) : 0;
479
+
480
+ return {
481
+ totalReturn: parseFloat(totalReturn.toFixed(2)),
482
+ sharpeRatio: parseFloat(sharpeRatio.toFixed(2)),
483
+ maxDrawdown: parseFloat(maxDrawdown.toFixed(2)),
484
+ winRate: parseFloat(winRate.toFixed(1)),
485
+ totalTrades: sellTrades.length,
486
+ finalValue: parseFloat(finalValue.toFixed(2))
487
+ };
488
+ }
489
+ }
490
+
491
+ // Run the backtest
492
+ function runBacktest() {
493
+ const shortMA = parseInt(document.getElementById('shortMA').value);
494
+ const longMA = parseInt(document.getElementById('longMA').value);
495
+ const initialCapital = parseInt(document.getElementById('initialCapital').value);
496
+ const dataPoints = parseInt(document.getElementById('dataPoints').value);
497
+
498
+ if (shortMA >= longMA) {
499
+ alert('Short MA period must be less than Long MA period!');
500
+ return;
501
+ }
502
+
503
+ // Show loading
504
+ document.getElementById('loading').style.display = 'block';
505
+
506
+ setTimeout(() => {
507
+ // Generate data
508
+ currentData = generatePriceData(dataPoints);
509
+
510
+ // Calculate moving averages
511
+ const shortMAValues = calculateMA(currentData, shortMA);
512
+ const longMAValues = calculateMA(currentData, longMA);
513
+
514
+ // Run strategy
515
+ const strategy = new MovingAverageCrossoverStrategy(shortMA, longMA, initialCapital);
516
+
517
+ currentData.forEach((dataPoint, index) => {
518
+ strategy.next(dataPoint, shortMAValues[index], longMAValues[index], index);
519
+ });
520
+
521
+ trades = strategy.trades;
522
+
523
+ // Display results
524
+ displayChart(currentData, shortMAValues, longMAValues);
525
+ displayMetrics(strategy.getMetrics());
526
+ displayTrades(trades);
527
+
528
+ // Hide loading
529
+ document.getElementById('loading').style.display = 'none';
530
+ }, 1000);
531
+ }
532
+
533
+ // Display the price chart
534
+ function displayChart(data, shortMAValues, longMAValues) {
535
+ const ctx = document.getElementById('priceChart').getContext('2d');
536
+
537
+ if (chart) {
538
+ chart.destroy();
539
+ }
540
+
541
+ chart = new Chart(ctx, {
542
+ type: 'line',
543
+ data: {
544
+ labels: data.map(d => d.date),
545
+ datasets: [{
546
+ label: 'Price',
547
+ data: data.map(d => d.price),
548
+ borderColor: '#667eea',
549
+ backgroundColor: 'rgba(102, 126, 234, 0.1)',
550
+ borderWidth: 2,
551
+ fill: true
552
+ }, {
553
+ label: `Short MA (${document.getElementById('shortMA').value})`,
554
+ data: shortMAValues,
555
+ borderColor: '#27ae60',
556
+ backgroundColor: 'transparent',
557
+ borderWidth: 2
558
+ }, {
559
+ label: `Long MA (${document.getElementById('longMA').value})`,
560
+ data: longMAValues,
561
+ borderColor: '#e74c3c',
562
+ backgroundColor: 'transparent',
563
+ borderWidth: 2
564
+ }]
565
+ },
566
+ options: {
567
+ responsive: true,
568
+ interaction: {
569
+ intersect: false,
570
+ mode: 'index'
571
+ },
572
+ plugins: {
573
+ legend: {
574
+ position: 'top',
575
+ },
576
+ tooltip: {
577
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
578
+ titleColor: 'white',
579
+ bodyColor: 'white'
580
+ }
581
+ },
582
+ scales: {
583
+ y: {
584
+ beginAtZero: false,
585
+ grid: {
586
+ color: 'rgba(0, 0, 0, 0.1)'
587
+ }
588
+ },
589
+ x: {
590
+ grid: {
591
+ color: 'rgba(0, 0, 0, 0.1)'
592
+ }
593
+ }
594
+ }
595
+ }
596
+ });
597
+ }
598
+
599
+ // Display performance metrics
600
+ function displayMetrics(metrics) {
601
+ const metricsGrid = document.getElementById('metricsGrid');
602
+ const metricsElements = {
603
+ totalReturn: document.getElementById('totalReturn'),
604
+ sharpeRatio: document.getElementById('sharpeRatio'),
605
+ maxDrawdown: document.getElementById('maxDrawdown'),
606
+ winRate: document.getElementById('winRate'),
607
+ totalTrades: document.getElementById('totalTrades'),
608
+ finalValue: document.getElementById('finalValue')
609
+ };
610
+
611
+ metricsElements.totalReturn.textContent = `${metrics.totalReturn}%`;
612
+ metricsElements.totalReturn.className = `metric-value ${metrics.totalReturn >= 0 ? 'positive' : 'negative'}`;
613
+
614
+ metricsElements.sharpeRatio.textContent = metrics.sharpeRatio;
615
+ metricsElements.sharpeRatio.className = `metric-value ${metrics.sharpeRatio >= 0 ? 'positive' : 'negative'}`;
616
+
617
+ metricsElements.maxDrawdown.textContent = `${metrics.maxDrawdown}%`;
618
+ metricsElements.maxDrawdown.className = 'metric-value negative';
619
+
620
+ metricsElements.winRate.textContent = `${metrics.winRate}%`;
621
+ metricsElements.winRate.className = `metric-value ${metrics.winRate >= 50 ? 'positive' : 'negative'}`;
622
+
623
+ metricsElements.totalTrades.textContent = metrics.totalTrades;
624
+ metricsElements.finalValue.textContent = `$${metrics.finalValue.toLocaleString()}`;
625
+
626
+ metricsGrid.style.display = 'grid';
627
+ }
628
+
629
+ // Display trades table
630
+ function displayTrades(trades) {
631
+ const tbody = document.getElementById('tradesTableBody');
632
+ tbody.innerHTML = '';
633
+
634
+ trades.forEach(trade => {
635
+ const row = tbody.insertRow();
636
+ row.innerHTML = `
637
+ <td>${trade.id}</td>
638
+ <td>${trade.date}</td>
639
+ <td style="font-weight: 600; color: ${trade.action === 'BUY' ? '#27ae60' : '#e74c3c'}">${trade.action}</td>
640
+ <td>$${trade.price}</td>
641
+ <td>${trade.quantity}</td>
642
+ <td>$${trade.portfolioValue.toLocaleString()}</td>
643
+ <td style="color: ${trade.pnl >= 0 ? '#27ae60' : '#e74c3c'}">${trade.pnl !== 0 ? '$' + trade.pnl.toFixed(2) : '-'}</td>
644
+ `;
645
+ });
646
+
647
+ document.getElementById('tradesTable').style.display = 'block';
648
+ }
649
+
650
+ // Run initial backtest when page loads
651
+ window.addEventListener('load', function() {
652
+ runBacktest();
653
+ });
654
+ </script>
655
+ </body>
656
+ </html>