zman35 commited on
Commit
14b8549
Β·
verified Β·
1 Parent(s): 3155582

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +204 -349
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import os
2
 
3
  import gradio as gr
@@ -26,102 +27,93 @@ except:
26
 
27
 
28
 
29
- # πŸ” Strategy Presets
30
-
31
- strategy_presets = {
32
-
33
- "Aggressive Prop Trader": {
34
 
35
- "starting_balance": 2500, "trades_min": 5, "trades_max": 10, "weeks": 12,
36
 
37
- "tp1_prob": 0.25, "tp2_prob": 0.4, "tp1_r": 1.2, "tp2_r": 2.4,
38
 
39
- "base_risk_pct": 0.015, "profit_target": None,
40
 
41
- "fatigue": 0.0, "trump_vol": 0.0,
42
 
43
- "description": "High-frequency, high-risk with strong upside potential."
44
 
45
- },
46
 
47
- "Conservative Swing Trader": {
48
 
49
- "starting_balance": 2500, "trades_min": 2, "trades_max": 5, "weeks": 12,
50
 
51
- "tp1_prob": 0.35, "tp2_prob": 0.25, "tp1_r": 0.9, "tp2_r": 1.8,
52
 
53
- "base_risk_pct": 0.01, "profit_target": None,
54
 
55
- "fatigue": 0.0, "trump_vol": 0.0,
56
 
57
- "description": "Lower frequency, prioritizes preservation and steady returns."
58
 
59
- },
60
 
61
- "Momentum Scalper": {
62
 
63
- "starting_balance": 2500, "trades_min": 4, "trades_max": 8, "weeks": 12,
64
 
65
- "tp1_prob": 0.3, "tp2_prob": 0.35, "tp1_r": 1.0, "tp2_r": 2.2,
66
 
67
- "base_risk_pct": 0.012, "profit_target": None,
68
 
69
- "fatigue": 0.0, "trump_vol": 0.0,
70
 
71
- "description": "Intraday momentum strategy for fast-paced trading windows."
72
 
73
- },
74
 
75
- "Swing Sniper": {
76
 
77
- "starting_balance": 2500, "trades_min": 2, "trades_max": 4, "weeks": 12,
78
 
79
- "tp1_prob": 0.2, "tp2_prob": 0.5, "tp1_r": 1.1, "tp2_r": 3.0,
80
 
81
- "base_risk_pct": 0.008, "profit_target": None,
82
 
83
- "fatigue": 0.0, "trump_vol": 0.0,
84
 
85
- "description": "Selective entries with high RR setups. Less frequent."
86
 
87
- },
88
 
89
- "Intraday Prop Mode": {
90
 
91
- "starting_balance": 2500, "trades_min": 3, "trades_max": 7, "weeks": 12,
92
 
93
- "tp1_prob": 0.3, "tp2_prob": 0.3, "tp1_r": 1.0, "tp2_r": 2.0,
94
 
95
- "base_risk_pct": 0.02, "profit_target": None,
96
 
97
- "fatigue": 0.0, "trump_vol": 0.0,
98
 
99
- "description": "Intraday consistency with a balanced reward profile."
100
 
101
- }
102
-
103
- }
104
 
105
- def get_scaled_risk_pct(balance, base_risk_pct):
106
 
107
- if balance < 5000:
108
 
109
- return base_risk_pct
110
 
111
- elif balance < 10000:
112
 
113
- return base_risk_pct * 0.75
114
 
115
- elif balance < 20000:
116
 
117
- return base_risk_pct * 0.5
118
 
119
- else:
120
 
121
- return base_risk_pct * 0.25
122
 
123
 
124
 
 
125
 
126
  def simulate_tp_strategy_full(starting_balance, trades_min, trades_max, weeks,
127
 
@@ -133,31 +125,21 @@ def simulate_tp_strategy_full(starting_balance, trades_min, trades_max, weeks,
133
 
134
  return pd.DataFrame(), {"Error": "Invalid probability config. TP1 + TP2 must be < 1.0"}
135
 
136
-
137
-
138
 
139
  sl_prob = 1.0 - tp1_prob - tp2_prob
140
 
141
- balance = starting_balance
142
-
143
- peak = balance
144
-
145
- drawdown = 0
146
 
147
  tp1_hits = tp2_hits = sl_hits = 0
148
 
149
- max_win_streak = max_loss_streak = 0
150
 
151
- cur_win_streak = cur_loss_streak = 0
152
 
153
- log = []
154
-
155
-
156
-
157
-
158
- fatigue_multiplier = 1.0 - fatigue * 0.4 # Reduce reward at high fatigue
159
 
160
- trump_vol_factor = np.random.normal(1.0, 0.2 * trump_vol) # Adds chaos
161
 
162
 
163
 
@@ -170,18 +152,14 @@ def simulate_tp_strategy_full(starting_balance, trades_min, trades_max, weeks,
170
 
171
  num_trades = np.random.randint(trades_min, trades_max + 1)
172
 
173
- for _ in range(num_trades):
174
-
175
- risk_pct = get_scaled_risk_pct(balance, base_risk_pct)
176
 
177
- risk_amount = balance * risk_pct * np.random.uniform(0.9, 1.1) # Risk % w/ some variability
178
-
179
- risk_amount *= trump_vol_factor # 🟠 Vol boost
180
 
181
 
 
182
 
 
183
 
184
- # Fatigue loss streak logic
185
 
186
  if fatigue > 0.6 and cur_loss_streak >= 3 and np.random.rand() < fatigue * 0.25:
187
 
@@ -238,15 +216,13 @@ def simulate_tp_strategy_full(starting_balance, trades_min, trades_max, weeks,
238
 
239
 
240
 
241
- weekly_return = (balance - week_start) / week_start * 100
242
-
243
  log.append({
244
 
245
  "Week": week, "Start Balance": round(week_start, 2),
246
 
247
  "End Balance": round(balance, 2),
248
 
249
- "Weekly Return (%)": round(weekly_return, 2)
250
 
251
  })
252
 
@@ -257,15 +233,10 @@ def simulate_tp_strategy_full(starting_balance, trades_min, trades_max, weeks,
257
 
258
  returns = df["End Balance"].pct_change().dropna()
259
 
260
- volatility = returns.std() * np.sqrt(52)
261
-
262
- sharpe_ratio = returns.mean() / returns.std() * np.sqrt(52) if returns.std() > 0 else 0
263
 
264
  score = balance / (1 + drawdown)
265
 
266
-
267
-
268
-
269
  summary = {
270
 
271
  "Final Balance": round(balance, 2),
@@ -282,7 +253,7 @@ def simulate_tp_strategy_full(starting_balance, trades_min, trades_max, weeks,
282
 
283
  "Max Loss Streak": max_loss_streak,
284
 
285
- "Sharpe Ratio": round(sharpe_ratio, 2),
286
 
287
  "EdgeCast Score": round(score, 2)
288
 
@@ -290,131 +261,114 @@ def simulate_tp_strategy_full(starting_balance, trades_min, trades_max, weeks,
290
 
291
  return df, summary
292
 
293
- # πŸ“ˆ Plot
294
 
295
- def equity_curve_plot(df, label="Equity Curve"):
296
 
297
- fig = go.Figure()
298
-
299
- fig.add_trace(go.Scatter(x=df["Week"], y=df["End Balance"], mode='lines+markers', name=label))
300
-
301
- fig.update_layout(title=f'πŸ“ˆ {label}', xaxis_title='Week', yaxis_title='Balance ($)', height=400)
302
-
303
- return fig
304
 
 
305
 
 
306
 
 
307
 
308
- # 🎯 Preset Tab
309
-
310
- def run_preset_strategy(style, fatigue=0.0, trump_vol=0.0):
311
-
312
- if style not in strategy_presets:
313
-
314
- return pd.DataFrame(), {}, go.Figure(), "Please select a strategy to begin."
315
-
316
- config = strategy_presets[style]
317
 
318
- df, summary = simulate_tp_strategy_full(
319
 
320
- config["starting_balance"], config["trades_min"], config["trades_max"], config["weeks"],
321
 
322
- config["tp1_prob"], config["tp2_prob"], config["tp1_r"], config["tp2_r"],
323
 
324
- config["base_risk_pct"], config["profit_target"],
325
 
326
- fatigue=fatigue, trump_vol=trump_vol
327
 
328
- )
329
 
330
- return df, summary, equity_curve_plot(df, style), config["description"]
331
 
 
332
 
 
333
 
 
334
 
335
- # πŸ› οΈ Manual Tab
336
 
337
- def run_manual_sim(starting_balance, trades_min, trades_max, weeks,
338
 
339
- tp1_prob, tp2_prob, tp1_r, tp2_r, base_risk_pct, profit_target,
340
 
341
- fatigue, trump_vol):
342
 
343
- df, summary = simulate_tp_strategy_full(starting_balance, trades_min, trades_max, weeks,
344
 
345
- tp1_prob, tp2_prob, tp1_r, tp2_r, base_risk_pct,
346
 
347
- profit_target, fatigue, trump_vol)
348
 
349
- chart = equity_curve_plot(df, "Manual Config")
350
 
351
- return df, summary, chart
352
 
353
 
354
 
355
 
356
- # βš”οΈ Manual Battle Mode (Dual Sim)
357
 
358
- def dual_manual_battle(
359
 
360
- sb1, tmin1, tmax1, w1, tp1a, tp2a, r1a, r2a, risk1, pt1, fat1, trump1,
361
 
362
- sb2, tmin2, tmax2, w2, tp1b, tp2b, r1b, r2b, risk2, pt2, fat2, trump2
363
 
364
- ):
365
 
366
- df1, s1 = simulate_tp_strategy_full(sb1, tmin1, tmax1, w1, tp1a, tp2a, r1a, r2a, risk1, pt1, fat1, trump1)
367
 
368
- df2, s2 = simulate_tp_strategy_full(sb2, tmin2, tmax2, w2, tp1b, tp2b, r1b, r2b, risk2, pt2, fat2, trump2)
369
 
 
370
 
 
371
 
 
372
 
373
- s1["Strategy"] = "Manual A"
374
 
375
- s2["Strategy"] = "Manual B"
376
 
 
377
 
 
378
 
379
 
380
- comparison_df = pd.DataFrame([s1, s2])
381
 
382
- comparison_df = comparison_df[["Strategy", "Final Balance", "Sharpe Ratio", "EdgeCast Score", "Max Drawdown %"]]
383
 
 
384
 
 
385
 
 
386
 
387
- # πŸ† Winners
388
 
389
- for col in ["Final Balance", "Sharpe Ratio", "EdgeCast Score"]:
390
 
391
- best_val = comparison_df[col].astype(float).max()
392
 
393
- comparison_df[col] = [
394
 
395
- f"{val} πŸ†" if float(val) == best_val else f"{val}" for val in comparison_df[col]
396
 
397
- ]
398
 
 
399
 
400
 
401
 
402
- # Chart
403
 
404
- fig = go.Figure()
405
 
406
- fig.add_trace(go.Scatter(x=df1["Week"], y=df1["End Balance"], name="Manual A"))
407
 
408
- fig.add_trace(go.Scatter(x=df2["Week"], y=df2["End Balance"], name="Manual B"))
409
 
410
- fig.update_layout(title="βš”οΈ Manual Strategy Battle", xaxis_title="Week", yaxis_title="Balance")
411
 
 
412
 
413
 
414
 
415
- return comparison_df, fig
416
 
417
- # πŸ“Š Leaderboard Tab
418
 
419
  def analytics_dashboard(rank_by="EdgeCast Score"):
420
 
@@ -428,16 +382,8 @@ def analytics_dashboard(rank_by="EdgeCast Score"):
428
 
429
  results.append(summary)
430
 
431
-
432
-
433
-
434
  df = pd.DataFrame(results)
435
 
436
-
437
-
438
-
439
- # Get numeric winners before formatting
440
-
441
  winner_vals = {
442
 
443
  "Final Balance": df["Final Balance"].max(),
@@ -450,369 +396,277 @@ def analytics_dashboard(rank_by="EdgeCast Score"):
450
 
451
  }
452
 
453
-
454
-
455
-
456
- # Sort leaderboard by selected metric
457
-
458
- ascending = rank_by == "Max Drawdown %"
459
-
460
- df = df.sort_values(rank_by, ascending=ascending).reset_index(drop=True)
461
-
462
-
463
-
464
-
465
- # Rank column
466
 
467
  df.insert(0, "πŸ… Rank", [f"#{i+1}" for i in df.index])
468
 
469
-
470
-
471
-
472
- # πŸ† Emoji highlights
473
-
474
  for col in winner_vals:
475
 
476
- df[col] = df[col].apply(lambda x: f"{round(x, 2)} πŸ†" if x == winner_vals[col] else f"{round(x, 2)}")
477
-
478
-
479
-
480
 
481
  return df[["πŸ… Rank", "Strategy", "Final Balance", "Sharpe Ratio", "EdgeCast Score", "Max Drawdown %"]]
482
 
483
 
484
 
485
 
486
-
487
-
488
-
489
- # πŸ“˜ Description Tab
490
-
491
  def show_descriptions():
492
 
493
- return pd.DataFrame([
494
-
495
- {"Strategy": name, "Description": config["description"]}
496
-
497
- for name, config in strategy_presets.items()
498
-
499
- ])
500
-
501
 
502
 
503
 
504
 
505
-
506
-
507
- # πŸ”¬ Risk Matrix Heatmap
508
-
509
  def generate_risk_matrix():
510
 
511
  names = list(strategy_presets.keys())
512
 
513
- scores = {
514
-
515
- name: simulate_tp_strategy_full(**{k: v for k, v in cfg.items() if k != "description"})[1]["EdgeCast Score"]
516
-
517
- for name, cfg in strategy_presets.items()
518
-
519
- }
520
-
521
- matrix = np.zeros((len(names), len(names)))
522
-
523
- for i, a in enumerate(names):
524
-
525
- for j, b in enumerate(names):
526
 
527
- matrix[i, j] = abs(scores[a] - scores[b])
528
 
 
529
 
530
-
531
-
532
- fig = px.imshow(
533
-
534
- matrix,
535
-
536
- x=names,
537
-
538
- y=names,
539
-
540
- text_auto=".2f",
541
-
542
- color_continuous_scale="RdYlGn_r",
543
-
544
- labels={"color": "Score Ξ”"},
545
-
546
- title="🧠 Risk Matrix (Ξ” Score Heatmap)"
547
-
548
- )
549
-
550
- fig.update_traces(
551
-
552
- hovertemplate="<b>%{y}</b> vs <b>%{x}</b><br>Ξ” Score: %{z:.2f}<extra></extra>"
553
-
554
- )
555
 
556
  return fig
557
 
558
 
559
 
560
 
 
561
 
 
562
 
 
563
 
564
- # πŸ₯Š Battle Strategies (Preset vs Preset)
565
-
566
- def battle_strategies(style1, style2):
567
-
568
- if style1 == "None" or style2 == "None":
569
-
570
- return pd.DataFrame([{"⚠️ Error": "Please select two valid strategies."}]), go.Figure()
571
-
572
-
573
-
574
-
575
- if style1 == style2:
576
-
577
- return pd.DataFrame([{"⚠️ Error": "Please select two different strategies."}]), go.Figure()
578
-
579
-
580
-
581
-
582
- try:
583
-
584
- df1, s1 = simulate_tp_strategy_full(**{k: v for k, v in strategy_presets[style1].items() if k != "description"})
585
-
586
- df2, s2 = simulate_tp_strategy_full(**{k: v for k, v in strategy_presets[style2].items() if k != "description"})
587
-
588
-
589
-
590
 
591
- s1["Strategy"] = style1
592
 
593
- s2["Strategy"] = style2
594
 
 
595
 
 
596
 
 
597
 
598
- comparison_df = pd.DataFrame([s1, s2])
599
 
600
- comparison_df = comparison_df[["Strategy", "Final Balance", "Sharpe Ratio", "EdgeCast Score", "Max Drawdown %"]]
601
 
602
 
 
603
 
 
604
 
605
- for col in ["Final Balance", "Sharpe Ratio", "EdgeCast Score"]:
606
 
607
- best_val = comparison_df[col].astype(float).max()
608
 
609
- comparison_df[col] = [
610
 
611
- f"{val} πŸ†" if float(val) == best_val else val for val in comparison_df[col]
612
 
613
- ]
614
 
 
615
 
 
616
 
617
 
618
- fig = go.Figure()
619
 
620
- fig.add_trace(go.Scatter(x=df1["Week"], y=df1["End Balance"], name=style1))
621
 
622
- fig.add_trace(go.Scatter(x=df2["Week"], y=df2["End Balance"], name=style2))
623
 
624
- fig.update_layout(title=f"πŸ₯Š {style1} vs {style2}", xaxis_title="Week", yaxis_title="Balance")
625
 
 
626
 
 
627
 
 
628
 
629
- return comparison_df, fig
630
 
 
631
 
 
632
 
 
633
 
634
- except Exception as e:
635
 
636
- return pd.DataFrame([{"Error": str(e)}]), go.Figure()
637
 
638
-
639
 
640
- # πŸš€ App UI Launch
641
 
642
- app = gr.TabbedInterface(
643
 
644
- interface_list=[
645
 
646
- # 🎯 Preset Mode
647
 
648
- gr.Interface(
649
 
650
- fn=run_preset_strategy,
651
 
652
- inputs=gr.Dropdown(
653
 
654
- choices=["None"] + list(strategy_presets.keys()),
655
 
656
- value="None",
657
 
658
- label="Select Strategy"
659
 
660
- ),
661
 
662
- outputs=["dataframe", "json", gr.Plot(), "text"],
663
 
664
- title="🎯 Preset Mode"
665
 
666
- ),
667
 
 
668
 
 
669
 
 
670
 
671
- # πŸ› οΈ Manual Config
672
 
673
- gr.Interface(
674
 
675
- fn=run_manual_sim,
676
 
677
- inputs=[
678
 
679
- gr.Slider(100, 20000, 2500, label="Start Balance"),
680
 
681
- gr.Slider(1, 10, 3, label="Trades Min"),
682
 
683
- gr.Slider(1, 15, 7, label="Trades Max"),
684
 
685
- gr.Slider(1, 52, 12, label="Weeks"),
686
 
687
- gr.Slider(0, 1, 0.3, step=0.05, label="TP1 %"),
688
 
689
- gr.Slider(0, 1, 0.3, step=0.05, label="TP2 %"),
690
 
691
- gr.Slider(0.1, 5.0, 1.0, step=0.1, label="TP1 R"),
692
 
693
- gr.Slider(0.1, 20.0, 2.0, step=0.1, label="TP2 R"),
694
 
695
- gr.Slider(0.001, 0.05, 0.01, step=0.001, label="Risk %"),
696
 
697
- gr.Slider(0, 100000, 0, step=500, label="Profit Target πŸ’°"),
698
 
699
- gr.Slider(0, 1, 0.0, step=0.1, label="Fatigue Level"),
700
 
701
- gr.Slider(0, 1, 0.0, step=0.1, label="Trump Volatility Index")
702
 
703
- ],
704
 
705
- outputs=["dataframe", "json", gr.Plot()],
706
 
707
- title="πŸ› οΈ Manual Config"
708
 
709
- ),
710
 
 
711
 
 
712
 
 
713
 
714
- # πŸ₯Š Battle Mode – Preset
715
 
716
- gr.Interface(
717
 
718
- fn=battle_strategies,
719
 
720
- inputs=[
721
 
722
- gr.Dropdown(choices=["None"] + list(strategy_presets.keys()), value="None", label="Strategy 1"),
723
 
724
- gr.Dropdown(choices=["None"] + list(strategy_presets.keys()), value="None", label="Strategy 2")
725
 
726
- ],
727
 
728
- outputs=["dataframe", gr.Plot()],
729
 
730
- title="πŸ₯Š Battle Mode"
731
 
732
- ),
733
 
 
734
 
 
735
 
736
 
737
- # πŸ§ͺ Manual Battle Mode
738
 
739
- gr.Interface(
740
 
741
- fn=dual_manual_battle,
742
 
743
- inputs=[
744
 
745
- gr.Textbox(label="Manual Config A (JSON format)"),
746
 
747
- gr.Textbox(label="Manual Config B (JSON format)")
748
 
749
- ],
750
 
751
- outputs=["dataframe", gr.Plot()],
752
 
753
- title="πŸ§ͺ Manual Battle Mode"
754
 
755
- ),
756
 
 
757
 
 
758
 
 
759
 
760
- # πŸ“Š Analytics Leaderboard
761
 
762
- gr.Interface(
763
 
764
- fn=analytics_dashboard,
765
 
766
- inputs=gr.Dropdown(
767
 
768
- choices=["EdgeCast Score", "Final Balance", "Sharpe Ratio", "Max Drawdown %"],
769
 
770
- value="EdgeCast Score",
771
 
772
- label="Sort leaderboard by:"
773
 
774
- ),
775
 
776
- outputs="dataframe",
777
 
778
- title="πŸ“Š Analytics Leaderboard"
779
 
780
- ),
781
 
 
782
 
 
783
 
 
784
 
785
- # πŸ“˜ Strategy Descriptions
786
 
787
- gr.Interface(
788
 
789
- fn=show_descriptions,
790
 
791
- inputs=[], outputs="dataframe",
792
 
793
- title="πŸ“˜ Strategy Descriptions"
794
 
795
- ),
796
 
 
797
 
798
 
799
 
800
- # πŸ”¬ Risk Matrix Heatmap
801
 
802
- gr.Interface(
803
 
804
- fn=generate_risk_matrix,
805
 
806
- inputs=[], outputs=gr.Plot(),
807
 
808
- title="πŸ”¬ Risk Matrix"
809
 
810
- )
811
 
812
  ],
813
 
814
- tab_names=["Preset", "Manual", "Battle", "Manual Battle", "Analytics", "Descriptions", "Risk Matrix"],
815
-
816
  title="EdgeCast – Strategy Simulation Suite"
817
 
818
  )
@@ -821,3 +675,4 @@ app = gr.TabbedInterface(
821
 
822
 
823
  app.launch()
 
 
1
+
2
  import os
3
 
4
  import gradio as gr
 
27
 
28
 
29
 
30
+ # === STRATEGY PRESETS ===
 
 
 
 
31
 
32
+ def get_strategy_presets():
33
 
34
+ return {
35
 
36
+ "Aggressive Prop Trader": {
37
 
38
+ "starting_balance": 2500, "trades_min": 5, "trades_max": 10, "weeks": 12,
39
 
40
+ "tp1_prob": 0.25, "tp2_prob": 0.4, "tp1_r": 1.2, "tp2_r": 2.4,
41
 
42
+ "base_risk_pct": 0.015, "profit_target": None,
43
 
44
+ "fatigue": 0.0, "trump_vol": 0.0,
45
 
46
+ "description": "High-frequency, high-risk with strong upside potential."
47
 
48
+ },
49
 
50
+ "Conservative Swing Trader": {
51
 
52
+ "starting_balance": 2500, "trades_min": 2, "trades_max": 5, "weeks": 12,
53
 
54
+ "tp1_prob": 0.35, "tp2_prob": 0.25, "tp1_r": 0.9, "tp2_r": 1.8,
55
 
56
+ "base_risk_pct": 0.01, "profit_target": None,
57
 
58
+ "fatigue": 0.0, "trump_vol": 0.0,
59
 
60
+ "description": "Lower frequency, prioritizes preservation and steady returns."
61
 
62
+ },
63
 
64
+ "Momentum Scalper": {
65
 
66
+ "starting_balance": 2500, "trades_min": 4, "trades_max": 8, "weeks": 12,
67
 
68
+ "tp1_prob": 0.3, "tp2_prob": 0.35, "tp1_r": 1.0, "tp2_r": 2.2,
69
 
70
+ "base_risk_pct": 0.012, "profit_target": None,
71
 
72
+ "fatigue": 0.0, "trump_vol": 0.0,
73
 
74
+ "description": "Intraday momentum strategy for fast-paced trading windows."
75
 
76
+ },
77
 
78
+ "Swing Sniper": {
79
 
80
+ "starting_balance": 2500, "trades_min": 2, "trades_max": 4, "weeks": 12,
81
 
82
+ "tp1_prob": 0.2, "tp2_prob": 0.5, "tp1_r": 1.1, "tp2_r": 3.0,
83
 
84
+ "base_risk_pct": 0.008, "profit_target": None,
85
 
86
+ "fatigue": 0.0, "trump_vol": 0.0,
87
 
88
+ "description": "Selective entries with high RR setups. Less frequent."
89
 
90
+ },
91
 
92
+ "Intraday Prop Mode": {
93
 
94
+ "starting_balance": 2500, "trades_min": 3, "trades_max": 7, "weeks": 12,
95
 
96
+ "tp1_prob": 0.3, "tp2_prob": 0.3, "tp1_r": 1.0, "tp2_r": 2.0,
97
 
98
+ "base_risk_pct": 0.02, "profit_target": None,
 
 
99
 
100
+ "fatigue": 0.0, "trump_vol": 0.0,
101
 
102
+ "description": "Intraday consistency with a balanced reward profile."
103
 
104
+ }
105
 
106
+ }
107
 
 
108
 
 
109
 
 
110
 
111
+ strategy_presets = get_strategy_presets()
112
 
 
113
 
114
 
115
 
116
+ # === CORE SIMULATION ===
117
 
118
  def simulate_tp_strategy_full(starting_balance, trades_min, trades_max, weeks,
119
 
 
125
 
126
  return pd.DataFrame(), {"Error": "Invalid probability config. TP1 + TP2 must be < 1.0"}
127
 
128
+
 
129
 
130
  sl_prob = 1.0 - tp1_prob - tp2_prob
131
 
132
+ balance, peak, drawdown = starting_balance, starting_balance, 0
 
 
 
 
133
 
134
  tp1_hits = tp2_hits = sl_hits = 0
135
 
136
+ max_win_streak = max_loss_streak = cur_win_streak = cur_loss_streak = 0
137
 
138
+ fatigue_multiplier = 1.0 - fatigue * 0.4
139
 
140
+ trump_vol_factor = np.random.normal(1.0, 0.2 * trump_vol)
 
 
 
 
 
141
 
142
+ log = []
143
 
144
 
145
 
 
152
 
153
  num_trades = np.random.randint(trades_min, trades_max + 1)
154
 
 
 
 
155
 
 
 
 
156
 
157
 
158
+ for _ in range(num_trades):
159
 
160
+ risk_amount = balance * base_risk_pct * np.random.uniform(0.9, 1.1)
161
 
162
+ risk_amount *= trump_vol_factor
163
 
164
  if fatigue > 0.6 and cur_loss_streak >= 3 and np.random.rand() < fatigue * 0.25:
165
 
 
216
 
217
 
218
 
 
 
219
  log.append({
220
 
221
  "Week": week, "Start Balance": round(week_start, 2),
222
 
223
  "End Balance": round(balance, 2),
224
 
225
+ "Weekly Return (%)": round((balance - week_start) / week_start * 100, 2)
226
 
227
  })
228
 
 
233
 
234
  returns = df["End Balance"].pct_change().dropna()
235
 
236
+ sharpe = returns.mean() / returns.std() * np.sqrt(52) if returns.std() > 0 else 0
 
 
237
 
238
  score = balance / (1 + drawdown)
239
 
 
 
 
240
  summary = {
241
 
242
  "Final Balance": round(balance, 2),
 
253
 
254
  "Max Loss Streak": max_loss_streak,
255
 
256
+ "Sharpe Ratio": round(sharpe, 2),
257
 
258
  "EdgeCast Score": round(score, 2)
259
 
 
261
 
262
  return df, summary
263
 
 
264
 
 
265
 
 
 
 
 
 
 
 
266
 
267
+ # === VISUALIZATION ===
268
 
269
+ def equity_curve_plot(df, label="Equity Curve"):
270
 
271
+ fig = go.Figure()
272
 
273
+ fig.add_trace(go.Scatter(x=df["Week"], y=df["End Balance"], mode="lines+markers", name=label))
 
 
 
 
 
 
 
 
274
 
275
+ fig.update_layout(title=f"πŸ“ˆ {label}", xaxis_title="Week", yaxis_title="Balance ($)", height=400)
276
 
277
+ return fig
278
 
 
279
 
 
280
 
 
281
 
282
+ def generate_histogram(metric="EdgeCast Score", runs=100):
283
 
284
+ all_metrics = []
285
 
286
+ for name, config in strategy_presets.items():
287
 
288
+ for _ in range(runs):
289
 
290
+ _, summary = simulate_tp_strategy_full(
291
 
292
+ config["starting_balance"], config["trades_min"], config["trades_max"], config["weeks"],
293
 
294
+ config["tp1_prob"], config["tp2_prob"], config["tp1_r"], config["tp2_r"],
295
 
296
+ config["base_risk_pct"], config["profit_target"], config["fatigue"], config["trump_vol"]
297
 
298
+ )
299
 
300
+ if metric in summary:
301
 
302
+ all_metrics.append({
303
 
304
+ "Strategy": name,
305
 
306
+ metric: summary[metric]
307
 
308
+ })
309
 
310
 
311
 
312
 
313
+ df = pd.DataFrame(all_metrics)
314
 
 
315
 
 
316
 
 
317
 
318
+ fig = px.histogram(
319
 
320
+ df,
321
 
322
+ x=metric,
323
 
324
+ color="Strategy",
325
 
326
+ marginal="box",
327
 
328
+ opacity=0.75,
329
 
330
+ barmode="overlay",
331
 
332
+ nbins=30,
333
 
334
+ title=f"πŸ“Š Distribution of {metric} Across Strategies"
335
 
336
+ )
337
 
338
 
 
339
 
 
340
 
341
+ fig.update_layout(
342
 
343
+ xaxis_title=metric,
344
 
345
+ yaxis_title="Count",
346
 
347
+ bargap=0.1,
348
 
349
+ height=500
350
 
351
+ )
352
 
 
353
 
 
354
 
 
355
 
356
+ return fig
357
 
358
 
359
 
 
360
 
 
361
 
 
362
 
 
363
 
364
+ def histogram_viewer_ui(metric):
365
 
366
+ return generate_histogram(metric)
367
 
368
 
369
 
 
370
 
371
+ # === ANALYSIS TOOLS ===
372
 
373
  def analytics_dashboard(rank_by="EdgeCast Score"):
374
 
 
382
 
383
  results.append(summary)
384
 
 
 
 
385
  df = pd.DataFrame(results)
386
 
 
 
 
 
 
387
  winner_vals = {
388
 
389
  "Final Balance": df["Final Balance"].max(),
 
396
 
397
  }
398
 
399
+ df = df.sort_values(rank_by, ascending=(rank_by == "Max Drawdown %")).reset_index(drop=True)
 
 
 
 
 
 
 
 
 
 
 
 
400
 
401
  df.insert(0, "πŸ… Rank", [f"#{i+1}" for i in df.index])
402
 
 
 
 
 
 
403
  for col in winner_vals:
404
 
405
+ df[col] = df[col].apply(lambda x: f"{round(x,2)} πŸ†" if x == winner_vals[col] else f"{round(x,2)}")
 
 
 
406
 
407
  return df[["πŸ… Rank", "Strategy", "Final Balance", "Sharpe Ratio", "EdgeCast Score", "Max Drawdown %"]]
408
 
409
 
410
 
411
 
 
 
 
 
 
412
  def show_descriptions():
413
 
414
+ return pd.DataFrame([{"Strategy": name, "Description": cfg["description"]} for name, cfg in strategy_presets.items()])
 
 
 
 
 
 
 
415
 
416
 
417
 
418
 
 
 
 
 
419
  def generate_risk_matrix():
420
 
421
  names = list(strategy_presets.keys())
422
 
423
+ scores = {n: simulate_tp_strategy_full(**{k: v for k, v in cfg.items() if k != "description"})[1]["EdgeCast Score"] for n, cfg in strategy_presets.items()}
 
 
 
 
 
 
 
 
 
 
 
 
424
 
425
+ matrix = np.array([[abs(scores[a] - scores[b]) for b in names] for a in names])
426
 
427
+ fig = px.imshow(matrix, x=names, y=names, text_auto=".2f", color_continuous_scale="RdYlGn_r", labels={"color": "Ξ” Score"})
428
 
429
+ fig.update_layout(title="🧠 Risk Matrix (Score Ξ”)", height=600)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
 
431
  return fig
432
 
433
 
434
 
435
 
436
+ # === STRATEGY RUNNERS ===
437
 
438
+ def run_preset_strategy(style, fatigue=0.0, trump_vol=0.0):
439
 
440
+ config = strategy_presets[style]
441
 
442
+ df, summary = simulate_tp_strategy_full(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
 
444
+ config["starting_balance"], config["trades_min"], config["trades_max"], config["weeks"],
445
 
446
+ config["tp1_prob"], config["tp2_prob"], config["tp1_r"], config["tp2_r"],
447
 
448
+ config["base_risk_pct"], config["profit_target"], fatigue, trump_vol
449
 
450
+ )
451
 
452
+ return df, summary, equity_curve_plot(df, style), config["description"]
453
 
 
454
 
 
455
 
456
 
457
+ def run_manual_sim(starting_balance, trades_min, trades_max, weeks,
458
 
459
+ tp1_prob, tp2_prob, tp1_r, tp2_r, base_risk_pct, profit_target,
460
 
461
+ fatigue, trump_vol):
462
 
463
+ df, summary = simulate_tp_strategy_full(
464
 
465
+ starting_balance, trades_min, trades_max, weeks,
466
 
467
+ tp1_prob, tp2_prob, tp1_r, tp2_r, base_risk_pct,
468
 
469
+ profit_target, fatigue, trump_vol
470
 
471
+ )
472
 
473
+ return df, summary, equity_curve_plot(df, "Manual Config")
474
 
475
 
 
476
 
 
477
 
478
+ def dual_manual_battle(
479
 
480
+ sb1, tmin1, tmax1, w1, tp1a, tp2a, r1a, r2a, risk1, pt1, fat1, trump1,
481
 
482
+ sb2, tmin2, tmax2, w2, tp1b, tp2b, r1b, r2b, risk2, pt2, fat2, trump2
483
 
484
+ ):
485
 
486
+ df1, s1 = simulate_tp_strategy_full(sb1, tmin1, tmax1, w1, tp1a, tp2a, r1a, r2a, risk1, pt1, fat1, trump1)
487
 
488
+ df2, s2 = simulate_tp_strategy_full(sb2, tmin2, tmax2, w2, tp1b, tp2b, r1b, r2b, risk2, pt2, fat2, trump2)
489
 
490
+ s1["Strategy"], s2["Strategy"] = "Manual A", "Manual B"
491
 
492
+ df_summary = pd.DataFrame([s1, s2])[["Strategy", "Final Balance", "Sharpe Ratio", "EdgeCast Score", "Max Drawdown %"]]
493
 
494
+ for col in ["Final Balance", "Sharpe Ratio", "EdgeCast Score"]:
495
 
496
+ best = df_summary[col].astype(float).max()
497
 
498
+ df_summary[col] = [f"{val} πŸ†" if float(val) == best else val for val in df_summary[col]]
499
 
500
+ fig = go.Figure()
501
 
502
+ fig.add_trace(go.Scatter(x=df1["Week"], y=df1["End Balance"], name="Manual A"))
503
 
504
+ fig.add_trace(go.Scatter(x=df2["Week"], y=df2["End Balance"], name="Manual B"))
505
 
506
+ fig.update_layout(title="βš”οΈ Manual Strategy Battle", xaxis_title="Week", yaxis_title="Balance")
507
 
508
+ return df_summary, fig
509
 
 
510
 
 
511
 
 
512
 
513
+ def get_manual_battle_interface():
514
 
515
+ return gr.Interface(
516
 
517
+ fn=dual_manual_battle,
518
 
519
+ inputs=[
520
 
521
+ # A Config
522
 
523
+ gr.Slider(100, 20000, 2500, label="A: Start Balance"),
524
 
525
+ gr.Slider(1, 10, 3, label="A: Trades Min"),
526
 
527
+ gr.Slider(1, 15, 7, label="A: Trades Max"),
528
 
529
+ gr.Slider(1, 52, 12, label="A: Weeks"),
530
 
531
+ gr.Slider(0, 1, 0.3, step=0.05, label="A: TP1 %"),
532
 
533
+ gr.Slider(0, 1, 0.3, step=0.05, label="A: TP2 %"),
534
 
535
+ gr.Slider(0.1, 5.0, 1.0, step=0.1, label="A: TP1 R"),
536
 
537
+ gr.Slider(0.1, 20.0, 2.0, step=0.1, label="A: TP2 R"),
538
 
539
+ gr.Slider(0.001, 0.05, 0.01, step=0.001, label="A: Risk %"),
540
 
541
+ gr.Slider(0, 100000, 0, step=500, label="A: Profit Target"),
542
 
543
+ gr.Slider(0, 1, 0.0, step=0.1, label="A: Fatigue"),
544
 
545
+ gr.Slider(0, 1, 0.0, step=0.1, label="A: Trump Volatility"),
546
 
547
+ # B Config
548
 
549
+ gr.Slider(100, 20000, 2500, label="B: Start Balance"),
550
 
551
+ gr.Slider(1, 10, 3, label="B: Trades Min"),
552
 
553
+ gr.Slider(1, 15, 7, label="B: Trades Max"),
554
 
555
+ gr.Slider(1, 52, 12, label="B: Weeks"),
556
 
557
+ gr.Slider(0, 1, 0.3, step=0.05, label="B: TP1 %"),
558
 
559
+ gr.Slider(0, 1, 0.3, step=0.05, label="B: TP2 %"),
560
 
561
+ gr.Slider(0.1, 5.0, 1.0, step=0.1, label="B: TP1 R"),
562
 
563
+ gr.Slider(0.1, 20.0, 2.0, step=0.1, label="B: TP2 R"),
564
 
565
+ gr.Slider(0.001, 0.05, 0.01, step=0.001, label="B: Risk %"),
566
 
567
+ gr.Slider(0, 100000, 0, step=500, label="B: Profit Target"),
568
 
569
+ gr.Slider(0, 1, 0.0, step=0.1, label="B: Fatigue"),
570
 
571
+ gr.Slider(0, 1, 0.0, step=0.1, label="B: Trump Volatility")
572
 
573
+ ],
574
 
575
+ outputs=["dataframe", gr.Plot()],
576
 
577
+ title="πŸ§ͺ Manual Strategy Battle"
578
 
579
+ )
580
 
 
581
 
 
582
 
 
583
 
584
+ # === LAUNCH INTERFACE ===
585
 
586
+ app = gr.TabbedInterface(
587
 
588
+ interface_list=[
589
 
590
+ gr.Interface(fn=run_preset_strategy, inputs=[
591
 
592
+ gr.Dropdown(choices=list(strategy_presets.keys()), label="Select Strategy"),
593
 
594
+ gr.Slider(0, 1, 0.0, step=0.1, label="Fatigue Level"),
595
 
596
+ gr.Slider(0, 1, 0.0, step=0.1, label="Trump Volatility Index")
597
 
598
+ ], outputs=["dataframe", "json", gr.Plot(), "text"], title="🎯 Preset Mode"),
599
 
600
 
 
601
 
 
602
 
603
+ gr.Interface(fn=run_manual_sim, inputs=[
604
 
605
+ gr.Slider(100, 20000, 2500, label="Start Balance"),
606
 
607
+ gr.Slider(1, 10, 3, label="Trades Min"),
608
 
609
+ gr.Slider(1, 15, 7, label="Trades Max"),
610
 
611
+ gr.Slider(1, 52, 12, label="Weeks"),
612
 
613
+ gr.Slider(0, 1, 0.3, step=0.05, label="TP1 %"),
614
 
615
+ gr.Slider(0, 1, 0.3, step=0.05, label="TP2 %"),
616
 
617
+ gr.Slider(0.1, 5.0, 1.0, step=0.1, label="TP1 R"),
618
 
619
+ gr.Slider(0.1, 20.0, 2.0, step=0.1, label="TP2 R"),
620
 
621
+ gr.Slider(0.001, 0.05, 0.01, step=0.001, label="Risk %"),
622
 
623
+ gr.Slider(0, 100000, 0, step=500, label="Profit Target πŸ’°"),
624
 
625
+ gr.Slider(0, 1, 0.0, step=0.1, label="Fatigue Level"),
626
 
627
+ gr.Slider(0, 1, 0.0, step=0.1, label="Trump Volatility Index")
628
 
629
+ ], outputs=["dataframe", "json", gr.Plot()], title="πŸ› οΈ Manual Config"),
630
 
 
631
 
 
632
 
 
633
 
634
+ get_manual_battle_interface(),
635
 
 
636
 
 
637
 
 
638
 
639
+ gr.Interface(fn=histogram_viewer_ui, inputs=gr.Dropdown(
640
 
641
+ choices=["Final Balance", "Sharpe Ratio", "EdgeCast Score", "Max Drawdown %"],
642
 
643
+ label="Select Metric"
644
 
645
+ ), outputs=gr.Plot(), title="πŸ“Š Histogram Viewer"),
646
 
 
647
 
 
648
 
 
649
 
650
+ gr.Interface(fn=analytics_dashboard, inputs=gr.Dropdown(
651
 
652
+ choices=["EdgeCast Score", "Final Balance", "Sharpe Ratio", "Max Drawdown %"],
653
 
654
+ label="Sort leaderboard by:"
655
 
656
+ ), outputs="dataframe", title="πŸ… Leaderboard"),
657
 
658
 
659
 
 
660
 
661
+ gr.Interface(fn=show_descriptions, inputs=[], outputs="dataframe", title="πŸ“˜ Descriptions"),
662
 
 
663
 
 
664
 
 
665
 
666
+ gr.Interface(fn=generate_risk_matrix, inputs=[], outputs=gr.Plot(), title="πŸ”¬ Risk Matrix")
667
 
668
  ],
669
 
 
 
670
  title="EdgeCast – Strategy Simulation Suite"
671
 
672
  )
 
675
 
676
 
677
  app.launch()
678
+