dhruv575 commited on
Commit
da24b38
·
1 Parent(s): b672768

Improvements to structure and spacing

Browse files
Files changed (3) hide show
  1. static/index.html +18 -27
  2. static/script.js +60 -43
  3. static/styles.css +22 -11
static/index.html CHANGED
@@ -4,7 +4,7 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Safe Choices - Prediction Market Simulation</title>
7
- <link rel="stylesheet" href="/static/styles.css?v=2">
8
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9
  </head>
10
  <body>
@@ -49,32 +49,19 @@
49
  <h2>Parameters</h2>
50
  </div>
51
 
 
 
 
 
 
52
  <div class="controls-grid">
53
  <!-- Base Parameters -->
54
- <div class="control-group">
55
- <label class="control-label">Starting Capital</label>
56
- <input type="number" id="startingCapital" class="control-input" value="10000" min="1000" max="100000" step="1000">
57
- <div class="control-subtitle">Initial investment amount</div>
58
- </div>
59
-
60
  <div class="control-group">
61
  <label class="control-label">Number of Simulations</label>
62
  <input type="number" id="numSimulations" class="control-input" value="100" min="10" max="100" step="10">
63
  <div class="control-subtitle">Monte Carlo runs (max 100)</div>
64
  </div>
65
 
66
- <div class="control-group">
67
- <label class="control-label">Start Date</label>
68
- <input type="date" id="startDate" class="control-input" value="2025-01-01" min="2025-01-01">
69
- <div class="control-subtitle">Simulation start date</div>
70
- </div>
71
-
72
- <div class="control-group">
73
- <label class="control-label">Max Duration</label>
74
- <input type="number" id="maxDuration" class="control-input" value="365" min="30" max="730" step="30">
75
- <div class="control-subtitle">Maximum days</div>
76
- </div>
77
-
78
  <!-- Probability Thresholds -->
79
  <div class="control-group">
80
  <label class="control-label">Min Probability 7d</label>
@@ -96,12 +83,16 @@
96
 
97
  <div class="control-group">
98
  <label class="control-label">Market Selection Skew</label>
99
- <input type="number" id="skewFactor" class="control-input" value="0.1" min="0.01" max="1.0" step="0.01">
100
- <div class="control-subtitle">Higher values favor closer markets</div>
 
 
 
 
101
  </div>
102
 
103
  <!-- Threshold-specific controls -->
104
- <div class="control-group threshold-only" style="display: none;">
105
  <label class="control-label">Target Return</label>
106
  <select id="targetReturn" class="control-input">
107
  <option value="4.14">4.14% (Treasury Rate)</option>
@@ -113,7 +104,7 @@
113
  </div>
114
 
115
  <!-- Multi-fund specific controls -->
116
- <div class="control-group multi-only" style="display: none;">
117
  <label class="control-label">Number of Funds</label>
118
  <input type="number" id="numFunds" class="control-input" value="5" min="2" max="10" step="1">
119
  <div class="control-subtitle">Independent funds (max 10)</div>
@@ -194,7 +185,7 @@
194
  </div>
195
  </div>
196
 
197
- <div class="stat-card multi-stats" style="display: none;">
198
  <div class="stat-header">
199
  <h3>Portfolio Stats</h3>
200
  </div>
@@ -214,7 +205,7 @@
214
  </div>
215
  </div>
216
 
217
- <div class="stat-card threshold-stats" style="display: none;">
218
  <div class="stat-header">
219
  <h3>Target Achievement</h3>
220
  </div>
@@ -255,7 +246,7 @@
255
  </div>
256
  </div>
257
 
258
- <div class="chart-card multi-chart" style="display: none;">
259
  <div class="chart-header">
260
  <h3>Fund Survivorship</h3>
261
  </div>
@@ -268,6 +259,6 @@
268
  </div>
269
  </div>
270
 
271
- <script src="/static/script.js?v=2"></script>
272
  </body>
273
  </html>
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Safe Choices - Prediction Market Simulation</title>
7
+ <link rel="stylesheet" href="/static/styles.css?v=4">
8
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9
  </head>
10
  <body>
 
49
  <h2>Parameters</h2>
50
  </div>
51
 
52
+ <!-- Hidden default values -->
53
+ <input type="hidden" id="startingCapital" value="10000">
54
+ <input type="hidden" id="startDate" value="2025-01-01">
55
+ <input type="hidden" id="maxDuration" value="365">
56
+
57
  <div class="controls-grid">
58
  <!-- Base Parameters -->
 
 
 
 
 
 
59
  <div class="control-group">
60
  <label class="control-label">Number of Simulations</label>
61
  <input type="number" id="numSimulations" class="control-input" value="100" min="10" max="100" step="10">
62
  <div class="control-subtitle">Monte Carlo runs (max 100)</div>
63
  </div>
64
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  <!-- Probability Thresholds -->
66
  <div class="control-group">
67
  <label class="control-label">Min Probability 7d</label>
 
83
 
84
  <div class="control-group">
85
  <label class="control-label">Market Selection Skew</label>
86
+ <select id="skewFactor" class="control-input">
87
+ <option value="0.1">Low</option>
88
+ <option value="0.3" selected>Medium</option>
89
+ <option value="0.5">High</option>
90
+ </select>
91
+ <div class="control-subtitle">Higher values encourage more trading</div>
92
  </div>
93
 
94
  <!-- Threshold-specific controls -->
95
+ <div class="control-group threshold-only">
96
  <label class="control-label">Target Return</label>
97
  <select id="targetReturn" class="control-input">
98
  <option value="4.14">4.14% (Treasury Rate)</option>
 
104
  </div>
105
 
106
  <!-- Multi-fund specific controls -->
107
+ <div class="control-group multi-only">
108
  <label class="control-label">Number of Funds</label>
109
  <input type="number" id="numFunds" class="control-input" value="5" min="2" max="10" step="1">
110
  <div class="control-subtitle">Independent funds (max 10)</div>
 
185
  </div>
186
  </div>
187
 
188
+ <div class="stat-card multi-stats">
189
  <div class="stat-header">
190
  <h3>Portfolio Stats</h3>
191
  </div>
 
205
  </div>
206
  </div>
207
 
208
+ <div class="stat-card threshold-stats">
209
  <div class="stat-header">
210
  <h3>Target Achievement</h3>
211
  </div>
 
246
  </div>
247
  </div>
248
 
249
+ <div class="chart-card multi-chart">
250
  <div class="chart-header">
251
  <h3>Fund Survivorship</h3>
252
  </div>
 
259
  </div>
260
  </div>
261
 
262
+ <script src="/static/script.js?v=4"></script>
263
  </body>
264
  </html>
static/script.js CHANGED
@@ -67,13 +67,13 @@ function selectSimulation(type) {
67
  });
68
  document.querySelector(`[data-sim="${type}"]`).classList.add('active');
69
 
70
- // Show/hide conditional controls
71
  document.querySelectorAll('.threshold-only').forEach(el => {
72
- el.style.display = type === 'threshold' ? 'block' : 'none';
73
  });
74
 
75
  document.querySelectorAll('.multi-only').forEach(el => {
76
- el.style.display = type === 'multi' ? 'block' : 'none';
77
  });
78
 
79
  updateEstimatedTime();
@@ -427,7 +427,7 @@ function displayResults(results) {
427
  // Type-specific statistics
428
  if (parameters.simType === 'multi') {
429
  document.querySelectorAll('.multi-stats, .multi-chart').forEach(el => {
430
- el.style.display = 'block';
431
  });
432
 
433
  document.getElementById('avgSurvivingFunds').textContent =
@@ -439,13 +439,13 @@ function displayResults(results) {
439
  document.getElementById('diversificationBenefit').textContent = diversificationBenefit;
440
  } else {
441
  document.querySelectorAll('.multi-stats, .multi-chart').forEach(el => {
442
- el.style.display = 'none';
443
  });
444
  }
445
 
446
  if (parameters.simType === 'threshold') {
447
  document.querySelectorAll('.threshold-stats').forEach(el => {
448
- el.style.display = 'block';
449
  });
450
 
451
  document.getElementById('targetReached').textContent = formatPercentage(summary.targetReachedRate || 0);
@@ -455,7 +455,7 @@ function displayResults(results) {
455
  summary.targetReachedRate > 0.5 ? 'Better' : 'Similar';
456
  } else {
457
  document.querySelectorAll('.threshold-stats').forEach(el => {
458
- el.style.display = 'none';
459
  });
460
  }
461
 
@@ -499,24 +499,23 @@ function generateCharts(results) {
499
  }
500
  }
501
 
502
- // Preset bins for return distribution (-100% to +500% in 5% increments)
503
- const RETURN_BINS = [];
504
- for (let i = -100; i <= 500; i += 5) {
505
- RETURN_BINS.push(i);
506
- }
507
-
508
- // Create empty return chart
509
  function createEmptyReturnChart() {
510
  const ctx = document.getElementById('returnChart').getContext('2d');
 
 
 
 
 
511
  return new Chart(ctx, {
512
  type: 'bar',
513
  data: {
514
- labels: RETURN_BINS.map(b => `${b}%`),
515
  datasets: [{
516
  label: 'Frequency',
517
- data: new Array(RETURN_BINS.length).fill(0),
518
- backgroundColor: 'rgba(135, 191, 255, 0.15)',
519
- borderColor: '#87BFFF',
520
  borderWidth: 1
521
  }]
522
  },
@@ -524,36 +523,49 @@ function createEmptyReturnChart() {
524
  });
525
  }
526
 
527
- // Create return distribution chart with preset bins
528
  function createReturnDistributionChart(runs) {
529
  const ctx = document.getElementById('returnChart').getContext('2d');
530
  const returns = runs.map(r => r.totalReturn * 100);
531
 
532
- // Count returns in preset bins
533
- const binCounts = new Array(RETURN_BINS.length).fill(0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
534
  returns.forEach(ret => {
535
- // Find the bin index
536
- for (let i = 0; i < RETURN_BINS.length - 1; i++) {
537
- if (ret >= RETURN_BINS[i] && ret < RETURN_BINS[i + 1]) {
538
  binCounts[i]++;
539
  break;
540
  }
541
  }
542
  // Handle values at or above the last bin
543
- if (ret >= RETURN_BINS[RETURN_BINS.length - 1]) {
544
- binCounts[RETURN_BINS.length - 1]++;
545
  }
546
  });
547
 
548
  return new Chart(ctx, {
549
  type: 'bar',
550
  data: {
551
- labels: RETURN_BINS.map(b => `${b}%`),
552
  datasets: [{
553
  label: 'Frequency',
554
  data: binCounts,
555
- backgroundColor: 'rgba(135, 191, 255, 0.15)',
556
- borderColor: '#87BFFF',
557
  borderWidth: 1
558
  }]
559
  },
@@ -578,28 +590,33 @@ function createEmptyCapitalChart() {
578
  function createCapitalEvolutionChart(runs) {
579
  const ctx = document.getElementById('capitalChart').getContext('2d');
580
 
581
- // Color palette for dark mode
582
  const colors = [
583
- { border: '#87BFFF', bg: 'rgba(135, 191, 255, 0.1)' },
584
- { border: '#87BFFF', bg: 'rgba(135, 191, 255, 0.08)' },
 
585
  { border: '#87BFFF', bg: 'rgba(135, 191, 255, 0.06)' },
586
- { border: '#87BFFF', bg: 'rgba(135, 191, 255, 0.04)' },
587
- { border: '#87BFFF', bg: 'rgba(135, 191, 255, 0.02)' }
588
  ];
589
 
590
  // Sample up to 5 runs
591
  const sampleRuns = runs.slice(0, 5);
592
  const datasets = sampleRuns.map((run, i) => {
593
- // Use capitalHistory if available, otherwise create a simple array
594
  let data = [];
595
  if (run.capitalHistory && run.capitalHistory.length > 0) {
596
- data = run.capitalHistory.map((cap, idx) => ({
597
- x: idx,
598
- y: cap
599
- }));
 
 
600
  } else {
601
- // Fallback: just show final capital
602
- data = [{ x: 0, y: run.finalCapital }];
 
 
 
603
  }
604
 
605
  return {
@@ -607,9 +624,9 @@ function createCapitalEvolutionChart(runs) {
607
  data: data,
608
  borderColor: colors[i % colors.length].border,
609
  backgroundColor: colors[i % colors.length].bg,
610
- borderWidth: 1.5,
611
  fill: false,
612
- tension: 0.4, // Smooth curves
613
  pointRadius: 0 // Hide points for cleaner look
614
  };
615
  });
 
67
  });
68
  document.querySelector(`[data-sim="${type}"]`).classList.add('active');
69
 
70
+ // Show/hide conditional controls using classes
71
  document.querySelectorAll('.threshold-only').forEach(el => {
72
+ el.classList.toggle('visible', type === 'threshold');
73
  });
74
 
75
  document.querySelectorAll('.multi-only').forEach(el => {
76
+ el.classList.toggle('visible', type === 'multi');
77
  });
78
 
79
  updateEstimatedTime();
 
427
  // Type-specific statistics
428
  if (parameters.simType === 'multi') {
429
  document.querySelectorAll('.multi-stats, .multi-chart').forEach(el => {
430
+ el.classList.add('visible');
431
  });
432
 
433
  document.getElementById('avgSurvivingFunds').textContent =
 
439
  document.getElementById('diversificationBenefit').textContent = diversificationBenefit;
440
  } else {
441
  document.querySelectorAll('.multi-stats, .multi-chart').forEach(el => {
442
+ el.classList.remove('visible');
443
  });
444
  }
445
 
446
  if (parameters.simType === 'threshold') {
447
  document.querySelectorAll('.threshold-stats').forEach(el => {
448
+ el.classList.add('visible');
449
  });
450
 
451
  document.getElementById('targetReached').textContent = formatPercentage(summary.targetReachedRate || 0);
 
455
  summary.targetReachedRate > 0.5 ? 'Better' : 'Similar';
456
  } else {
457
  document.querySelectorAll('.threshold-stats').forEach(el => {
458
+ el.classList.remove('visible');
459
  });
460
  }
461
 
 
499
  }
500
  }
501
 
502
+ // Create empty return chart with default range
 
 
 
 
 
 
503
  function createEmptyReturnChart() {
504
  const ctx = document.getElementById('returnChart').getContext('2d');
505
+ const defaultBins = [];
506
+ for (let i = -100; i <= 100; i += 2) {
507
+ defaultBins.push(i);
508
+ }
509
+
510
  return new Chart(ctx, {
511
  type: 'bar',
512
  data: {
513
+ labels: defaultBins.map(b => `${b}%`),
514
  datasets: [{
515
  label: 'Frequency',
516
+ data: new Array(defaultBins.length).fill(0),
517
+ backgroundColor: 'rgba(86, 175, 226, 0.3)',
518
+ borderColor: '#56AFE2',
519
  borderWidth: 1
520
  }]
521
  },
 
523
  });
524
  }
525
 
526
+ // Create return distribution chart with smart 2% bins
527
  function createReturnDistributionChart(runs) {
528
  const ctx = document.getElementById('returnChart').getContext('2d');
529
  const returns = runs.map(r => r.totalReturn * 100);
530
 
531
+ // Calculate smart range based on data
532
+ const minReturn = Math.min(...returns);
533
+ const maxReturn = Math.max(...returns);
534
+
535
+ // Round to nearest 10% for cleaner bounds, with padding
536
+ const binStart = Math.floor(minReturn / 10) * 10;
537
+ const binEnd = Math.min(Math.ceil(maxReturn / 10) * 10, 200); // Cap at 200%
538
+
539
+ // Generate 2% bins within the smart range
540
+ const bins = [];
541
+ for (let i = binStart; i <= binEnd; i += 2) {
542
+ bins.push(i);
543
+ }
544
+
545
+ // Count returns in bins
546
+ const binCounts = new Array(bins.length).fill(0);
547
  returns.forEach(ret => {
548
+ for (let i = 0; i < bins.length - 1; i++) {
549
+ if (ret >= bins[i] && ret < bins[i + 1]) {
 
550
  binCounts[i]++;
551
  break;
552
  }
553
  }
554
  // Handle values at or above the last bin
555
+ if (ret >= bins[bins.length - 1]) {
556
+ binCounts[bins.length - 1]++;
557
  }
558
  });
559
 
560
  return new Chart(ctx, {
561
  type: 'bar',
562
  data: {
563
+ labels: bins.map(b => `${b}%`),
564
  datasets: [{
565
  label: 'Frequency',
566
  data: binCounts,
567
+ backgroundColor: 'rgba(86, 175, 226, 0.3)',
568
+ borderColor: '#56AFE2',
569
  borderWidth: 1
570
  }]
571
  },
 
590
  function createCapitalEvolutionChart(runs) {
591
  const ctx = document.getElementById('capitalChart').getContext('2d');
592
 
593
+ // Color palette for dark mode using both blues
594
  const colors = [
595
+ { border: '#56AFE2', bg: 'rgba(86, 175, 226, 0.15)' },
596
+ { border: '#87BFFF', bg: 'rgba(135, 191, 255, 0.12)' },
597
+ { border: '#56AFE2', bg: 'rgba(86, 175, 226, 0.09)' },
598
  { border: '#87BFFF', bg: 'rgba(135, 191, 255, 0.06)' },
599
+ { border: '#56AFE2', bg: 'rgba(86, 175, 226, 0.03)' }
 
600
  ];
601
 
602
  // Sample up to 5 runs
603
  const sampleRuns = runs.slice(0, 5);
604
  const datasets = sampleRuns.map((run, i) => {
605
+ // Use capitalHistory if available
606
  let data = [];
607
  if (run.capitalHistory && run.capitalHistory.length > 0) {
608
+ // Handle both array of numbers and array of objects
609
+ data = run.capitalHistory.map((item, idx) => {
610
+ const capital = typeof item === 'object' ? item.capital : item;
611
+ const day = typeof item === 'object' ? item.day : idx;
612
+ return { x: day, y: capital };
613
+ });
614
  } else {
615
+ // Fallback: just show final capital with start and end
616
+ data = [
617
+ { x: 0, y: 10000 }, // Starting capital
618
+ { x: 1, y: run.finalCapital }
619
+ ];
620
  }
621
 
622
  return {
 
624
  data: data,
625
  borderColor: colors[i % colors.length].border,
626
  backgroundColor: colors[i % colors.length].bg,
627
+ borderWidth: 2,
628
  fill: false,
629
+ tension: 0.3, // Smooth curves
630
  pointRadius: 0 // Hide points for cleaner look
631
  };
632
  });
static/styles.css CHANGED
@@ -8,6 +8,7 @@
8
  --text-primary: #ffffff;
9
  --text-secondary: #87BFFF;
10
  --accent: #87BFFF;
 
11
  --accent-hover: #a8d0ff;
12
  --accent-light: rgba(135, 191, 255, 0.1);
13
 
@@ -150,8 +151,9 @@ body {
150
  }
151
 
152
  .sim-tab.active {
153
- border-color: var(--accent);
154
  background: var(--bg-secondary);
 
155
  }
156
 
157
  .tab-title {
@@ -260,7 +262,7 @@ body {
260
  }
261
 
262
  .run-button {
263
- background: var(--accent);
264
  color: var(--bg-primary);
265
  border: none;
266
  padding: 12px 28px;
@@ -276,7 +278,7 @@ body {
276
  }
277
 
278
  .run-button:hover:not(:disabled) {
279
- background: var(--accent-hover);
280
  }
281
 
282
  .run-button:disabled {
@@ -389,13 +391,14 @@ body {
389
  .stats-grid {
390
  display: grid;
391
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
392
- gap: calc(var(--spacing-unit) * 2);
 
393
  }
394
 
395
  .stat-card {
396
  background: var(--bg-secondary);
397
  border-radius: var(--radius-card);
398
- padding: calc(var(--spacing-unit) * 2);
399
  border: 1px solid var(--border-color);
400
  }
401
 
@@ -444,20 +447,20 @@ body {
444
  }
445
 
446
  .stat-value.neutral {
447
- color: var(--warning);
448
  }
449
 
450
  /* Charts Grid */
451
  .charts-grid {
452
  display: grid;
453
  grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
454
- gap: calc(var(--spacing-unit) * 2);
455
  }
456
 
457
  .chart-card {
458
  background: var(--bg-secondary);
459
  border-radius: var(--radius-card);
460
- padding: calc(var(--spacing-unit) * 2);
461
  border: 1px solid var(--border-color);
462
  }
463
 
@@ -562,10 +565,18 @@ body {
562
  }
563
 
564
  /* Conditional visibility classes */
565
- .single-only, .threshold-only, .multi-only {
566
- transition: opacity 0.2s ease;
 
 
 
 
567
  }
568
 
569
  .multi-stats, .threshold-stats, .multi-chart {
570
- transition: opacity 0.2s ease;
 
 
 
 
571
  }
 
8
  --text-primary: #ffffff;
9
  --text-secondary: #87BFFF;
10
  --accent: #87BFFF;
11
+ --accent-dark: #56AFE2;
12
  --accent-hover: #a8d0ff;
13
  --accent-light: rgba(135, 191, 255, 0.1);
14
 
 
151
  }
152
 
153
  .sim-tab.active {
154
+ border-color: var(--accent-dark);
155
  background: var(--bg-secondary);
156
+ border-left: 3px solid var(--accent-dark);
157
  }
158
 
159
  .tab-title {
 
262
  }
263
 
264
  .run-button {
265
+ background: var(--accent-dark);
266
  color: var(--bg-primary);
267
  border: none;
268
  padding: 12px 28px;
 
278
  }
279
 
280
  .run-button:hover:not(:disabled) {
281
+ background: var(--accent);
282
  }
283
 
284
  .run-button:disabled {
 
391
  .stats-grid {
392
  display: grid;
393
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
394
+ gap: calc(var(--spacing-unit) * 1.5);
395
+ margin-bottom: calc(var(--spacing-unit) * 1.5);
396
  }
397
 
398
  .stat-card {
399
  background: var(--bg-secondary);
400
  border-radius: var(--radius-card);
401
+ padding: calc(var(--spacing-unit) * 1.5);
402
  border: 1px solid var(--border-color);
403
  }
404
 
 
447
  }
448
 
449
  .stat-value.neutral {
450
+ color: var(--accent-dark);
451
  }
452
 
453
  /* Charts Grid */
454
  .charts-grid {
455
  display: grid;
456
  grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
457
+ gap: calc(var(--spacing-unit) * 1.5);
458
  }
459
 
460
  .chart-card {
461
  background: var(--bg-secondary);
462
  border-radius: var(--radius-card);
463
+ padding: calc(var(--spacing-unit) * 1.5);
464
  border: 1px solid var(--border-color);
465
  }
466
 
 
565
  }
566
 
567
  /* Conditional visibility classes */
568
+ .threshold-only, .multi-only {
569
+ display: none;
570
+ }
571
+
572
+ .threshold-only.visible, .multi-only.visible {
573
+ display: flex;
574
  }
575
 
576
  .multi-stats, .threshold-stats, .multi-chart {
577
+ display: none;
578
+ }
579
+
580
+ .multi-stats.visible, .threshold-stats.visible, .multi-chart.visible {
581
+ display: block;
582
  }