dhruv575 commited on
Commit
09368f6
·
1 Parent(s): 79a5ca5

Minimum Volume Filter

Browse files
Files changed (4) hide show
  1. app.py +4 -0
  2. simulation_utils.py +33 -11
  3. static/index.html +13 -2
  4. static/script.js +4 -2
app.py CHANGED
@@ -54,6 +54,7 @@ class SimulationParams(BaseModel):
54
  minProbCurrent: float
55
  daysBefore: int
56
  investmentProbability: float
 
57
  targetReturn: Optional[float] = None
58
  numFunds: Optional[int] = None
59
  kellyFraction: Optional[float] = None
@@ -116,6 +117,7 @@ async def run_simulation(params: SimulationParams):
116
  min_prob_current=params.minProbCurrent,
117
  investment_probability=params.investmentProbability,
118
  target_return=params.targetReturn,
 
119
  random_seed=seed_offset + i
120
  )
121
 
@@ -154,6 +156,7 @@ async def run_simulation(params: SimulationParams):
154
  investment_probability=params.investmentProbability,
155
  kelly_fraction=params.kellyFraction or 0.5,
156
  edge_estimate=params.edgeEstimate or 'historical',
 
157
  random_seed=seed_offset + i
158
  )
159
 
@@ -189,6 +192,7 @@ async def run_simulation(params: SimulationParams):
189
  min_prob_current=params.minProbCurrent,
190
  investment_probability=params.investmentProbability,
191
  target_return=params.targetReturn,
 
192
  random_seed=seed_offset + i
193
  )
194
 
 
54
  minProbCurrent: float
55
  daysBefore: int
56
  investmentProbability: float
57
+ minVolume: Optional[float] = 1000000 # Default 1 million
58
  targetReturn: Optional[float] = None
59
  numFunds: Optional[int] = None
60
  kellyFraction: Optional[float] = None
 
117
  min_prob_current=params.minProbCurrent,
118
  investment_probability=params.investmentProbability,
119
  target_return=params.targetReturn,
120
+ min_volume=params.minVolume,
121
  random_seed=seed_offset + i
122
  )
123
 
 
156
  investment_probability=params.investmentProbability,
157
  kelly_fraction=params.kellyFraction or 0.5,
158
  edge_estimate=params.edgeEstimate or 'historical',
159
+ min_volume=params.minVolume,
160
  random_seed=seed_offset + i
161
  )
162
 
 
192
  min_prob_current=params.minProbCurrent,
193
  investment_probability=params.investmentProbability,
194
  target_return=params.targetReturn,
195
+ min_volume=params.minVolume,
196
  random_seed=seed_offset + i
197
  )
198
 
simulation_utils.py CHANGED
@@ -221,7 +221,7 @@ def calculate_investment_return(market: pd.Series, days_before: int, capital: fl
221
  else: # Market resolved False (safe bet lost)
222
  return 0.0
223
 
224
- def run_single_fund_simulation_fast(df: pd.DataFrame,
225
  starting_capital: float = 10000,
226
  start_date: str = '2025-01-01',
227
  max_duration_days: int = 365,
@@ -230,6 +230,7 @@ def run_single_fund_simulation_fast(df: pd.DataFrame,
230
  min_prob_current: float = 0.90,
231
  investment_probability: float = 0.5,
232
  target_return: Optional[float] = None,
 
233
  random_seed: Optional[int] = None) -> Dict[str, Any]:
234
  """
235
  Optimized single fund simulation using pre-indexed markets and event-driven approach.
@@ -237,10 +238,10 @@ def run_single_fund_simulation_fast(df: pd.DataFrame,
237
  random_state = np.random.RandomState(random_seed)
238
  start_dt = pd.to_datetime(start_date, utc=True)
239
  end_dt = start_dt + timedelta(days=max_duration_days)
240
-
241
  # Pre-filter and index markets by trading date for massive speedup
242
  prob_col = f'probability{days_before}d'
243
-
244
  # Filter eligible markets once upfront
245
  market_mask = (
246
  (df['probability7d'] >= min_prob_7d) &
@@ -251,6 +252,10 @@ def run_single_fund_simulation_fast(df: pd.DataFrame,
251
  (df['closingDate'] >= start_dt + timedelta(days=days_before)) &
252
  (df['closingDate'] <= end_dt + timedelta(days=days_before))
253
  )
 
 
 
 
254
 
255
  eligible_markets = df[market_mask].copy()
256
  if len(eligible_markets) == 0:
@@ -273,10 +278,11 @@ def run_single_fund_simulation_fast(df: pd.DataFrame,
273
  'min_prob_current': min_prob_current,
274
  'investment_probability': investment_probability,
275
  'target_return': target_return,
 
276
  'random_seed': random_seed
277
  }
278
  }
279
-
280
  # Calculate trading dates for each market (days_before days before resolution)
281
  eligible_markets['trading_date'] = eligible_markets['closingDate'] - timedelta(days=days_before)
282
 
@@ -397,12 +403,13 @@ def run_single_fund_simulation_fast(df: pd.DataFrame,
397
  'min_prob_current': min_prob_current,
398
  'investment_probability': investment_probability,
399
  'target_return': target_return,
 
400
  'random_seed': random_seed
401
  }
402
  }
403
 
404
  # Keep original function for backwards compatibility
405
- def run_single_fund_simulation(df: pd.DataFrame,
406
  starting_capital: float = 10000,
407
  start_date: str = '2025-01-01',
408
  max_duration_days: int = 365,
@@ -411,13 +418,14 @@ def run_single_fund_simulation(df: pd.DataFrame,
411
  min_prob_current: float = 0.90,
412
  investment_probability: float = 0.5,
413
  target_return: Optional[float] = None,
 
414
  random_seed: Optional[int] = None) -> Dict[str, Any]:
415
  """
416
  Run a single fund simulation with day-by-day investment decisions.
417
-
418
  Each day (when not already invested), the trader decides to invest with
419
  probability alpha, then selects uniformly at random from available markets.
420
-
421
  Args:
422
  df: Market data DataFrame
423
  starting_capital: Initial capital
@@ -428,8 +436,9 @@ def run_single_fund_simulation(df: pd.DataFrame,
428
  min_prob_current: Minimum probability at investment day
429
  investment_probability: Probability of investing on any given day (alpha)
430
  target_return: Target return threshold to stop trading (None = no threshold)
 
431
  random_seed: Random seed for reproducibility
432
-
433
  Returns:
434
  Dictionary containing simulation results
435
  """
@@ -444,6 +453,7 @@ def run_single_fund_simulation(df: pd.DataFrame,
444
  min_prob_current=min_prob_current,
445
  investment_probability=investment_probability,
446
  target_return=target_return,
 
447
  random_seed=random_seed
448
  )
449
 
@@ -518,6 +528,7 @@ def run_kelly_simulation(df: pd.DataFrame,
518
  investment_probability: float = 0.5,
519
  kelly_fraction: float = 0.5,
520
  edge_estimate: str = 'historical',
 
521
  random_seed: Optional[int] = None) -> Dict[str, Any]:
522
  """
523
  Run a simulation using Kelly criterion for position sizing.
@@ -536,6 +547,7 @@ def run_kelly_simulation(df: pd.DataFrame,
536
  investment_probability: Probability of attempting to invest on any given day
537
  kelly_fraction: Fraction of Kelly to use (0.5 = half Kelly, 1.0 = full Kelly)
538
  edge_estimate: Method to estimate edge ('historical', 'fixed_edge', 'fixed_edge_2')
 
539
  random_seed: Random seed for reproducibility
540
 
541
  Returns:
@@ -558,6 +570,10 @@ def run_kelly_simulation(df: pd.DataFrame,
558
  (df['closingDate'] <= end_dt + timedelta(days=days_before))
559
  )
560
 
 
 
 
 
561
  eligible_markets = df[market_mask].copy()
562
 
563
  if len(eligible_markets) == 0:
@@ -586,6 +602,7 @@ def run_kelly_simulation(df: pd.DataFrame,
586
  'investment_probability': investment_probability,
587
  'kelly_fraction': kelly_fraction,
588
  'edge_estimate': edge_estimate,
 
589
  'random_seed': random_seed
590
  }
591
  }
@@ -751,6 +768,7 @@ def run_kelly_simulation(df: pd.DataFrame,
751
  'investment_probability': investment_probability,
752
  'kelly_fraction': kelly_fraction,
753
  'edge_estimate': edge_estimate,
 
754
  'random_seed': random_seed
755
  }
756
  }
@@ -920,12 +938,13 @@ def run_multi_fund_simulation(df: pd.DataFrame,
920
  min_prob_current: float = 0.90,
921
  investment_probability: float = 0.5,
922
  target_return: Optional[float] = None,
 
923
  random_seed: Optional[int] = None) -> Dict[str, Any]:
924
  """
925
  Run a multi-fund simulation where capital is divided into independent funds.
926
-
927
  Each fund operates independently with the same investment probability (alpha).
928
-
929
  Args:
930
  df: Market data DataFrame
931
  n_funds: Number of independent funds to create
@@ -937,8 +956,9 @@ def run_multi_fund_simulation(df: pd.DataFrame,
937
  min_prob_current: Minimum probability at investment day
938
  investment_probability: Probability of investing on any given day (alpha)
939
  target_return: Target return threshold per fund (None = no threshold)
 
940
  random_seed: Random seed for reproducibility
941
-
942
  Returns:
943
  Dictionary containing multi-fund simulation results
944
  """
@@ -967,6 +987,7 @@ def run_multi_fund_simulation(df: pd.DataFrame,
967
  min_prob_current=min_prob_current,
968
  investment_probability=investment_probability,
969
  target_return=target_return,
 
970
  random_seed=fund_seed
971
  )
972
 
@@ -1029,6 +1050,7 @@ def run_multi_fund_simulation(df: pd.DataFrame,
1029
  'min_prob_current': min_prob_current,
1030
  'investment_probability': investment_probability,
1031
  'target_return': target_return,
 
1032
  'random_seed': random_seed
1033
  }
1034
  }
 
221
  else: # Market resolved False (safe bet lost)
222
  return 0.0
223
 
224
+ def run_single_fund_simulation_fast(df: pd.DataFrame,
225
  starting_capital: float = 10000,
226
  start_date: str = '2025-01-01',
227
  max_duration_days: int = 365,
 
230
  min_prob_current: float = 0.90,
231
  investment_probability: float = 0.5,
232
  target_return: Optional[float] = None,
233
+ min_volume: Optional[float] = None,
234
  random_seed: Optional[int] = None) -> Dict[str, Any]:
235
  """
236
  Optimized single fund simulation using pre-indexed markets and event-driven approach.
 
238
  random_state = np.random.RandomState(random_seed)
239
  start_dt = pd.to_datetime(start_date, utc=True)
240
  end_dt = start_dt + timedelta(days=max_duration_days)
241
+
242
  # Pre-filter and index markets by trading date for massive speedup
243
  prob_col = f'probability{days_before}d'
244
+
245
  # Filter eligible markets once upfront
246
  market_mask = (
247
  (df['probability7d'] >= min_prob_7d) &
 
252
  (df['closingDate'] >= start_dt + timedelta(days=days_before)) &
253
  (df['closingDate'] <= end_dt + timedelta(days=days_before))
254
  )
255
+
256
+ # Add volume filter if specified
257
+ if min_volume is not None and 'volume' in df.columns:
258
+ market_mask = market_mask & (df['volume'] >= min_volume)
259
 
260
  eligible_markets = df[market_mask].copy()
261
  if len(eligible_markets) == 0:
 
278
  'min_prob_current': min_prob_current,
279
  'investment_probability': investment_probability,
280
  'target_return': target_return,
281
+ 'min_volume': min_volume,
282
  'random_seed': random_seed
283
  }
284
  }
285
+
286
  # Calculate trading dates for each market (days_before days before resolution)
287
  eligible_markets['trading_date'] = eligible_markets['closingDate'] - timedelta(days=days_before)
288
 
 
403
  'min_prob_current': min_prob_current,
404
  'investment_probability': investment_probability,
405
  'target_return': target_return,
406
+ 'min_volume': min_volume,
407
  'random_seed': random_seed
408
  }
409
  }
410
 
411
  # Keep original function for backwards compatibility
412
+ def run_single_fund_simulation(df: pd.DataFrame,
413
  starting_capital: float = 10000,
414
  start_date: str = '2025-01-01',
415
  max_duration_days: int = 365,
 
418
  min_prob_current: float = 0.90,
419
  investment_probability: float = 0.5,
420
  target_return: Optional[float] = None,
421
+ min_volume: Optional[float] = None,
422
  random_seed: Optional[int] = None) -> Dict[str, Any]:
423
  """
424
  Run a single fund simulation with day-by-day investment decisions.
425
+
426
  Each day (when not already invested), the trader decides to invest with
427
  probability alpha, then selects uniformly at random from available markets.
428
+
429
  Args:
430
  df: Market data DataFrame
431
  starting_capital: Initial capital
 
436
  min_prob_current: Minimum probability at investment day
437
  investment_probability: Probability of investing on any given day (alpha)
438
  target_return: Target return threshold to stop trading (None = no threshold)
439
+ min_volume: Minimum market volume to consider (None = no filter)
440
  random_seed: Random seed for reproducibility
441
+
442
  Returns:
443
  Dictionary containing simulation results
444
  """
 
453
  min_prob_current=min_prob_current,
454
  investment_probability=investment_probability,
455
  target_return=target_return,
456
+ min_volume=min_volume,
457
  random_seed=random_seed
458
  )
459
 
 
528
  investment_probability: float = 0.5,
529
  kelly_fraction: float = 0.5,
530
  edge_estimate: str = 'historical',
531
+ min_volume: Optional[float] = None,
532
  random_seed: Optional[int] = None) -> Dict[str, Any]:
533
  """
534
  Run a simulation using Kelly criterion for position sizing.
 
547
  investment_probability: Probability of attempting to invest on any given day
548
  kelly_fraction: Fraction of Kelly to use (0.5 = half Kelly, 1.0 = full Kelly)
549
  edge_estimate: Method to estimate edge ('historical', 'fixed_edge', 'fixed_edge_2')
550
+ min_volume: Minimum market volume to consider (None = no filter)
551
  random_seed: Random seed for reproducibility
552
 
553
  Returns:
 
570
  (df['closingDate'] <= end_dt + timedelta(days=days_before))
571
  )
572
 
573
+ # Add volume filter if specified
574
+ if min_volume is not None and 'volume' in df.columns:
575
+ market_mask = market_mask & (df['volume'] >= min_volume)
576
+
577
  eligible_markets = df[market_mask].copy()
578
 
579
  if len(eligible_markets) == 0:
 
602
  'investment_probability': investment_probability,
603
  'kelly_fraction': kelly_fraction,
604
  'edge_estimate': edge_estimate,
605
+ 'min_volume': min_volume,
606
  'random_seed': random_seed
607
  }
608
  }
 
768
  'investment_probability': investment_probability,
769
  'kelly_fraction': kelly_fraction,
770
  'edge_estimate': edge_estimate,
771
+ 'min_volume': min_volume,
772
  'random_seed': random_seed
773
  }
774
  }
 
938
  min_prob_current: float = 0.90,
939
  investment_probability: float = 0.5,
940
  target_return: Optional[float] = None,
941
+ min_volume: Optional[float] = None,
942
  random_seed: Optional[int] = None) -> Dict[str, Any]:
943
  """
944
  Run a multi-fund simulation where capital is divided into independent funds.
945
+
946
  Each fund operates independently with the same investment probability (alpha).
947
+
948
  Args:
949
  df: Market data DataFrame
950
  n_funds: Number of independent funds to create
 
956
  min_prob_current: Minimum probability at investment day
957
  investment_probability: Probability of investing on any given day (alpha)
958
  target_return: Target return threshold per fund (None = no threshold)
959
+ min_volume: Minimum market volume to consider (None = no filter)
960
  random_seed: Random seed for reproducibility
961
+
962
  Returns:
963
  Dictionary containing multi-fund simulation results
964
  """
 
987
  min_prob_current=min_prob_current,
988
  investment_probability=investment_probability,
989
  target_return=target_return,
990
+ min_volume=min_volume,
991
  random_seed=fund_seed
992
  )
993
 
 
1050
  'min_prob_current': min_prob_current,
1051
  'investment_probability': investment_probability,
1052
  'target_return': target_return,
1053
+ 'min_volume': min_volume,
1054
  'random_seed': random_seed
1055
  }
1056
  }
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=10">
8
  <link rel="preconnect" href="https://fonts.googleapis.com">
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
@@ -203,6 +203,17 @@
203
  </div>
204
  </div>
205
 
 
 
 
 
 
 
 
 
 
 
 
206
  <div class="param-group">
207
  <label class="param-label">
208
  Trading Frequency
@@ -534,6 +545,6 @@
534
  </div>
535
  </main>
536
 
537
- <script src="/static/script.js?v=10"></script>
538
  </body>
539
  </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=11">
8
  <link rel="preconnect" href="https://fonts.googleapis.com">
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
 
203
  </div>
204
  </div>
205
 
206
+ <div class="param-group">
207
+ <label class="param-label">
208
+ Min Volume
209
+ <span class="param-hint" data-tooltip="Minimum market volume in USD">?</span>
210
+ </label>
211
+ <div class="param-input-wrap">
212
+ <input type="number" id="minVolume" class="param-input" value="1000000" min="100000" max="10000000" step="100000">
213
+ <span class="param-unit">$</span>
214
+ </div>
215
+ </div>
216
+
217
  <div class="param-group">
218
  <label class="param-label">
219
  Trading Frequency
 
545
  </div>
546
  </main>
547
 
548
+ <script src="/static/script.js?v=11"></script>
549
  </body>
550
  </html>
static/script.js CHANGED
@@ -170,7 +170,8 @@ function getSimulationParameters() {
170
  minProb7d: parseFloat(document.getElementById('minProb7d').value) / 100,
171
  minProbCurrent: parseFloat(document.getElementById('minProbCurrent').value) / 100,
172
  daysBefore: parseInt(document.getElementById('daysBefore').value),
173
- investmentProbability: parseFloat(document.getElementById('investmentProbability').value)
 
174
  };
175
 
176
  if (currentSimType === 'threshold') {
@@ -212,7 +213,8 @@ async function callSimulationAPI(params) {
212
  minProb7d: params.minProb7d,
213
  minProbCurrent: params.minProbCurrent,
214
  daysBefore: params.daysBefore,
215
- investmentProbability: params.investmentProbability
 
216
  };
217
 
218
  if (params.targetReturn !== undefined) {
 
170
  minProb7d: parseFloat(document.getElementById('minProb7d').value) / 100,
171
  minProbCurrent: parseFloat(document.getElementById('minProbCurrent').value) / 100,
172
  daysBefore: parseInt(document.getElementById('daysBefore').value),
173
+ investmentProbability: parseFloat(document.getElementById('investmentProbability').value),
174
+ minVolume: parseFloat(document.getElementById('minVolume').value)
175
  };
176
 
177
  if (currentSimType === 'threshold') {
 
213
  minProb7d: params.minProb7d,
214
  minProbCurrent: params.minProbCurrent,
215
  daysBefore: params.daysBefore,
216
+ investmentProbability: params.investmentProbability,
217
+ minVolume: params.minVolume
218
  };
219
 
220
  if (params.targetReturn !== undefined) {