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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +665 -210
app.py CHANGED
@@ -1,368 +1,823 @@
1
  import os
 
2
  import gradio as gr
 
3
  import pandas as pd
 
4
  import numpy as np
 
5
  import plotly.graph_objs as go
 
6
  import plotly.express as px
7
 
 
 
 
8
  os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
 
9
  try:
 
10
  gr.analytics_enabled = False
 
11
  except:
 
12
  pass
13
 
14
- # === STRATEGY PRESETS ===
15
- def get_strategy_presets():
16
- return {
17
- "Aggressive Prop Trader": {
18
- "starting_balance": 2500, "trades_min": 5, "trades_max": 10, "weeks": 12,
19
- "tp1_prob": 0.25, "tp2_prob": 0.4, "tp1_r": 1.2, "tp2_r": 2.4,
20
- "base_risk_pct": 0.015, "profit_target": None,
21
- "fatigue": 0.0, "trump_vol": 0.0,
22
- "description": "High-frequency, high-risk with strong upside potential."
23
- },
24
- "Conservative Swing Trader": {
25
- "starting_balance": 2500, "trades_min": 2, "trades_max": 5, "weeks": 12,
26
- "tp1_prob": 0.35, "tp2_prob": 0.25, "tp1_r": 0.9, "tp2_r": 1.8,
27
- "base_risk_pct": 0.01, "profit_target": None,
28
- "fatigue": 0.0, "trump_vol": 0.0,
29
- "description": "Lower frequency, prioritizes preservation and steady returns."
30
- },
31
- "Momentum Scalper": {
32
- "starting_balance": 2500, "trades_min": 4, "trades_max": 8, "weeks": 12,
33
- "tp1_prob": 0.3, "tp2_prob": 0.35, "tp1_r": 1.0, "tp2_r": 2.2,
34
- "base_risk_pct": 0.012, "profit_target": None,
35
- "fatigue": 0.0, "trump_vol": 0.0,
36
- "description": "Intraday momentum strategy for fast-paced trading windows."
37
- },
38
- "Swing Sniper": {
39
- "starting_balance": 2500, "trades_min": 2, "trades_max": 4, "weeks": 12,
40
- "tp1_prob": 0.2, "tp2_prob": 0.5, "tp1_r": 1.1, "tp2_r": 3.0,
41
- "base_risk_pct": 0.008, "profit_target": None,
42
- "fatigue": 0.0, "trump_vol": 0.0,
43
- "description": "Selective entries with high RR setups. Less frequent."
44
- },
45
- "Intraday Prop Mode": {
46
- "starting_balance": 2500, "trades_min": 3, "trades_max": 7, "weeks": 12,
47
- "tp1_prob": 0.3, "tp2_prob": 0.3, "tp1_r": 1.0, "tp2_r": 2.0,
48
- "base_risk_pct": 0.02, "profit_target": None,
49
- "fatigue": 0.0, "trump_vol": 0.0,
50
- "description": "Intraday consistency with a balanced reward profile."
51
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  }
53
 
54
- strategy_presets = get_strategy_presets()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
- # === CORE SIMULATION ===
57
  def simulate_tp_strategy_full(starting_balance, trades_min, trades_max, weeks,
 
58
  tp1_prob, tp2_prob, tp1_r, tp2_r, base_risk_pct,
 
59
  profit_target=None, fatigue=0.0, trump_vol=0.0):
 
60
  if tp1_prob + tp2_prob >= 1.0:
 
61
  return pd.DataFrame(), {"Error": "Invalid probability config. TP1 + TP2 must be < 1.0"}
62
-
 
 
 
63
  sl_prob = 1.0 - tp1_prob - tp2_prob
64
- balance, peak, drawdown = starting_balance, starting_balance, 0
 
 
 
 
 
 
65
  tp1_hits = tp2_hits = sl_hits = 0
66
- max_win_streak = max_loss_streak = cur_win_streak = cur_loss_streak = 0
67
- fatigue_multiplier = 1.0 - fatigue * 0.4
68
- trump_vol_factor = np.random.normal(1.0, 0.2 * trump_vol)
 
 
69
  log = []
70
 
 
 
 
 
 
 
 
 
 
 
71
  for week in range(1, weeks + 1):
 
72
  if profit_target and balance >= profit_target: break
 
73
  week_start = balance
 
74
  num_trades = np.random.randint(trades_min, trades_max + 1)
75
 
76
  for _ in range(num_trades):
77
- risk_amount = balance * base_risk_pct * np.random.uniform(0.9, 1.1)
78
- risk_amount *= trump_vol_factor
 
 
 
 
 
 
 
 
 
 
79
  if fatigue > 0.6 and cur_loss_streak >= 3 and np.random.rand() < fatigue * 0.25:
 
80
  outcome = "SL"
 
81
  else:
 
82
  outcome = np.random.choice(["TP1", "TP2", "SL"], p=[tp1_prob, tp2_prob, sl_prob])
83
 
 
 
 
84
  if outcome == "TP1":
 
85
  balance += risk_amount * tp1_r * fatigue_multiplier
 
86
  tp1_hits += 1
 
87
  cur_win_streak += 1
 
88
  cur_loss_streak = 0
 
89
  elif outcome == "TP2":
 
90
  balance += risk_amount * tp2_r * fatigue_multiplier
 
91
  tp2_hits += 1
 
92
  cur_win_streak += 1
 
93
  cur_loss_streak = 0
 
94
  else:
 
95
  balance -= risk_amount
 
96
  sl_hits += 1
 
97
  cur_loss_streak += 1
 
98
  cur_win_streak = 0
99
 
 
 
 
100
  max_win_streak = max(max_win_streak, cur_win_streak)
 
101
  max_loss_streak = max(max_loss_streak, cur_loss_streak)
 
102
  peak = max(peak, balance)
 
103
  drawdown = max(drawdown, (peak - balance) / peak * 100)
104
 
 
 
 
 
 
105
  log.append({
 
106
  "Week": week, "Start Balance": round(week_start, 2),
 
107
  "End Balance": round(balance, 2),
108
- "Weekly Return (%)": round((balance - week_start) / week_start * 100, 2)
 
 
109
  })
110
 
 
 
 
111
  df = pd.DataFrame(log)
 
112
  returns = df["End Balance"].pct_change().dropna()
113
- sharpe = returns.mean() / returns.std() * np.sqrt(52) if returns.std() > 0 else 0
 
 
 
 
114
  score = balance / (1 + drawdown)
 
 
 
 
115
  summary = {
 
116
  "Final Balance": round(balance, 2),
 
117
  "TP1 Hits": tp1_hits,
 
118
  "TP2 Hits": tp2_hits,
 
119
  "SL Hits": sl_hits,
 
120
  "Max Drawdown %": round(drawdown, 2),
 
121
  "Max Win Streak": max_win_streak,
 
122
  "Max Loss Streak": max_loss_streak,
123
- "Sharpe Ratio": round(sharpe, 2),
 
 
124
  "EdgeCast Score": round(score, 2)
 
125
  }
 
126
  return df, summary
127
 
128
- # === VISUALIZATION ===
 
129
  def equity_curve_plot(df, label="Equity Curve"):
 
130
  fig = go.Figure()
131
- fig.add_trace(go.Scatter(x=df["Week"], y=df["End Balance"], mode="lines+markers", name=label))
132
- fig.update_layout(title=f"πŸ“ˆ {label}", xaxis_title="Week", yaxis_title="Balance ($)", height=400)
133
- return fig
134
 
135
- def generate_histogram(metric="EdgeCast Score", runs=100):
136
- all_metrics = []
137
- for name, config in strategy_presets.items():
138
- for _ in range(runs):
139
- _, summary = simulate_tp_strategy_full(
140
- config["starting_balance"], config["trades_min"], config["trades_max"], config["weeks"],
141
- config["tp1_prob"], config["tp2_prob"], config["tp1_r"], config["tp2_r"],
142
- config["base_risk_pct"], config["profit_target"], config["fatigue"], config["trump_vol"]
143
- )
144
- if metric in summary:
145
- all_metrics.append({
146
- "Strategy": name,
147
- metric: summary[metric]
148
- })
149
-
150
- df = pd.DataFrame(all_metrics)
151
-
152
- fig = px.histogram(
153
- df,
154
- x=metric,
155
- color="Strategy",
156
- marginal="box",
157
- opacity=0.75,
158
- barmode="overlay",
159
- nbins=30,
160
- title=f"πŸ“Š Distribution of {metric} Across Strategies"
161
- )
162
 
163
- fig.update_layout(
164
- xaxis_title=metric,
165
- yaxis_title="Count",
166
- bargap=0.1,
167
- height=500
168
- )
169
 
170
  return fig
171
 
172
 
173
- def histogram_viewer_ui(metric):
174
- return generate_histogram(metric)
175
 
176
- # === ANALYSIS TOOLS ===
177
- def analytics_dashboard(rank_by="EdgeCast Score"):
178
- results = []
179
- for name, config in strategy_presets.items():
180
- _, summary = simulate_tp_strategy_full(**{k: v for k, v in config.items() if k != "description"})
181
- summary["Strategy"] = name
182
- results.append(summary)
183
- df = pd.DataFrame(results)
184
- winner_vals = {
185
- "Final Balance": df["Final Balance"].max(),
186
- "Sharpe Ratio": df["Sharpe Ratio"].max(),
187
- "EdgeCast Score": df["EdgeCast Score"].max(),
188
- "Max Drawdown %": df["Max Drawdown %"].min()
189
- }
190
- df = df.sort_values(rank_by, ascending=(rank_by == "Max Drawdown %")).reset_index(drop=True)
191
- df.insert(0, "πŸ… Rank", [f"#{i+1}" for i in df.index])
192
- for col in winner_vals:
193
- df[col] = df[col].apply(lambda x: f"{round(x,2)} πŸ†" if x == winner_vals[col] else f"{round(x,2)}")
194
- return df[["πŸ… Rank", "Strategy", "Final Balance", "Sharpe Ratio", "EdgeCast Score", "Max Drawdown %"]]
195
 
196
- def show_descriptions():
197
- return pd.DataFrame([{"Strategy": name, "Description": cfg["description"]} for name, cfg in strategy_presets.items()])
198
-
199
- def generate_risk_matrix():
200
- names = list(strategy_presets.keys())
201
- 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()}
202
- matrix = np.array([[abs(scores[a] - scores[b]) for b in names] for a in names])
203
- fig = px.imshow(matrix, x=names, y=names, text_auto=".2f", color_continuous_scale="RdYlGn_r", labels={"color": "Ξ” Score"})
204
- fig.update_layout(title="🧠 Risk Matrix (Score Ξ”)", height=600)
205
- return fig
206
 
207
- # === STRATEGY RUNNERS ===
208
  def run_preset_strategy(style, fatigue=0.0, trump_vol=0.0):
 
 
 
 
 
209
  config = strategy_presets[style]
 
210
  df, summary = simulate_tp_strategy_full(
 
211
  config["starting_balance"], config["trades_min"], config["trades_max"], config["weeks"],
 
212
  config["tp1_prob"], config["tp2_prob"], config["tp1_r"], config["tp2_r"],
213
- config["base_risk_pct"], config["profit_target"], fatigue, trump_vol
 
 
 
 
214
  )
 
215
  return df, summary, equity_curve_plot(df, style), config["description"]
216
 
 
 
 
 
 
217
  def run_manual_sim(starting_balance, trades_min, trades_max, weeks,
 
218
  tp1_prob, tp2_prob, tp1_r, tp2_r, base_risk_pct, profit_target,
 
219
  fatigue, trump_vol):
220
- df, summary = simulate_tp_strategy_full(
221
- starting_balance, trades_min, trades_max, weeks,
222
- tp1_prob, tp2_prob, tp1_r, tp2_r, base_risk_pct,
223
- profit_target, fatigue, trump_vol
224
- )
225
- return df, summary, equity_curve_plot(df, "Manual Config")
 
 
 
 
 
 
 
 
 
226
 
227
  def dual_manual_battle(
 
228
  sb1, tmin1, tmax1, w1, tp1a, tp2a, r1a, r2a, risk1, pt1, fat1, trump1,
 
229
  sb2, tmin2, tmax2, w2, tp1b, tp2b, r1b, r2b, risk2, pt2, fat2, trump2
 
230
  ):
 
231
  df1, s1 = simulate_tp_strategy_full(sb1, tmin1, tmax1, w1, tp1a, tp2a, r1a, r2a, risk1, pt1, fat1, trump1)
 
232
  df2, s2 = simulate_tp_strategy_full(sb2, tmin2, tmax2, w2, tp1b, tp2b, r1b, r2b, risk2, pt2, fat2, trump2)
233
- s1["Strategy"], s2["Strategy"] = "Manual A", "Manual B"
234
- df_summary = pd.DataFrame([s1, s2])[["Strategy", "Final Balance", "Sharpe Ratio", "EdgeCast Score", "Max Drawdown %"]]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  for col in ["Final Balance", "Sharpe Ratio", "EdgeCast Score"]:
236
- best = df_summary[col].astype(float).max()
237
- df_summary[col] = [f"{val} πŸ†" if float(val) == best else val for val in df_summary[col]]
 
 
 
 
 
 
 
 
 
 
 
 
238
  fig = go.Figure()
 
239
  fig.add_trace(go.Scatter(x=df1["Week"], y=df1["End Balance"], name="Manual A"))
 
240
  fig.add_trace(go.Scatter(x=df2["Week"], y=df2["End Balance"], name="Manual B"))
 
241
  fig.update_layout(title="βš”οΈ Manual Strategy Battle", xaxis_title="Week", yaxis_title="Balance")
242
- return df_summary, fig
243
-
244
- def get_manual_battle_interface():
245
- return gr.Interface(
246
- fn=dual_manual_battle,
247
- inputs=[
248
- # A Config
249
- gr.Slider(100, 20000, 2500, label="A: Start Balance"),
250
- gr.Slider(1, 10, 3, label="A: Trades Min"),
251
- gr.Slider(1, 15, 7, label="A: Trades Max"),
252
- gr.Slider(1, 52, 12, label="A: Weeks"),
253
- gr.Slider(0, 1, 0.3, step=0.05, label="A: TP1 %"),
254
- gr.Slider(0, 1, 0.3, step=0.05, label="A: TP2 %"),
255
- gr.Slider(0.1, 5.0, 1.0, step=0.1, label="A: TP1 R"),
256
- gr.Slider(0.1, 20.0, 2.0, step=0.1, label="A: TP2 R"),
257
- gr.Slider(0.001, 0.05, 0.01, step=0.001, label="A: Risk %"),
258
- gr.Slider(0, 100000, 0, step=500, label="A: Profit Target"),
259
- gr.Slider(0, 1, 0.0, step=0.1, label="A: Fatigue"),
260
- gr.Slider(0, 1, 0.0, step=0.1, label="A: Trump Volatility"),
261
- # B Config
262
- gr.Slider(100, 20000, 2500, label="B: Start Balance"),
263
- gr.Slider(1, 10, 3, label="B: Trades Min"),
264
- gr.Slider(1, 15, 7, label="B: Trades Max"),
265
- gr.Slider(1, 52, 12, label="B: Weeks"),
266
- gr.Slider(0, 1, 0.3, step=0.05, label="B: TP1 %"),
267
- gr.Slider(0, 1, 0.3, step=0.05, label="B: TP2 %"),
268
- gr.Slider(0.1, 5.0, 1.0, step=0.1, label="B: TP1 R"),
269
- gr.Slider(0.1, 20.0, 2.0, step=0.1, label="B: TP2 R"),
270
- gr.Slider(0.001, 0.05, 0.01, step=0.001, label="B: Risk %"),
271
- gr.Slider(0, 100000, 0, step=500, label="B: Profit Target"),
272
- gr.Slider(0, 1, 0.0, step=0.1, label="B: Fatigue"),
273
- gr.Slider(0, 1, 0.0, step=0.1, label="B: Trump Volatility")
274
- ],
275
- outputs=["dataframe", gr.Plot()],
276
- title="πŸ§ͺ Manual Strategy Battle"
277
- )
278
 
279
- # === LAUNCH INTERFACE ===
280
- app = gr.TabbedInterface(
281
- interface_list=[
282
 
283
- # 🎯 Preset Strategy Tab
284
- gr.Interface(
285
- fn=run_preset_strategy,
286
- inputs=[
287
- gr.Dropdown(choices=list(strategy_presets.keys()), label="Select Strategy"),
288
- gr.Slider(0, 1, 0.0, step=0.1, label="Fatigue Level"),
289
- gr.Slider(0, 1, 0.0, step=0.1, label="Trump Volatility Index")
290
- ],
291
- outputs=["dataframe", "json", gr.Plot(), "text"],
292
- title="🎯 Preset Strategy Mode"
293
- ),
294
 
295
- # πŸ› οΈ Manual Config Tab
296
- gr.Interface(
297
- fn=run_manual_sim,
298
- inputs=[
299
- gr.Slider(100, 20000, 2500, label="Start Balance"),
300
- gr.Slider(1, 10, 3, label="Trades Min"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  gr.Slider(1, 15, 7, label="Trades Max"),
 
302
  gr.Slider(1, 52, 12, label="Weeks"),
 
303
  gr.Slider(0, 1, 0.3, step=0.05, label="TP1 %"),
 
304
  gr.Slider(0, 1, 0.3, step=0.05, label="TP2 %"),
 
305
  gr.Slider(0.1, 5.0, 1.0, step=0.1, label="TP1 R"),
 
306
  gr.Slider(0.1, 20.0, 2.0, step=0.1, label="TP2 R"),
 
307
  gr.Slider(0.001, 0.05, 0.01, step=0.001, label="Risk %"),
 
308
  gr.Slider(0, 100000, 0, step=500, label="Profit Target πŸ’°"),
 
309
  gr.Slider(0, 1, 0.0, step=0.1, label="Fatigue Level"),
 
310
  gr.Slider(0, 1, 0.0, step=0.1, label="Trump Volatility Index")
 
311
  ],
 
312
  outputs=["dataframe", "json", gr.Plot()],
313
- title="πŸ› οΈ Manual Config Mode"
 
 
314
  ),
315
 
316
- # πŸ§ͺ Manual Battle Tab
317
- get_manual_battle_interface(),
318
 
319
- # πŸ“Š Line Graph Viewer Tab
 
 
 
320
  gr.Interface(
321
- fn=histogram_viewer_ui,
322
- inputs=gr.Dropdown(
323
- choices=["Final Balance", "Sharpe Ratio", "EdgeCast Score", "Max Drawdown %"],
324
- label="Select Metric"
325
- ),
326
- outputs=gr.Plot(),
327
- title="πŸ“Š Line Graph Viewer"
 
 
 
 
 
 
 
 
328
  ),
329
 
330
- # πŸ“Š Leaderboard Tab
 
 
 
 
331
  gr.Interface(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  fn=analytics_dashboard,
 
333
  inputs=gr.Dropdown(
 
334
  choices=["EdgeCast Score", "Final Balance", "Sharpe Ratio", "Max Drawdown %"],
 
335
  value="EdgeCast Score",
 
336
  label="Sort leaderboard by:"
 
337
  ),
 
338
  outputs="dataframe",
339
- title="πŸ… Strategy Leaderboard"
 
 
340
  ),
341
 
342
- # πŸ“˜ Descriptions Tab
 
 
 
 
343
  gr.Interface(
 
344
  fn=show_descriptions,
 
345
  inputs=[], outputs="dataframe",
 
346
  title="πŸ“˜ Strategy Descriptions"
 
347
  ),
348
 
349
- # πŸ”¬ Risk Matrix Tab
 
 
 
 
350
  gr.Interface(
 
351
  fn=generate_risk_matrix,
 
352
  inputs=[], outputs=gr.Plot(),
 
353
  title="πŸ”¬ Risk Matrix"
 
354
  )
 
355
  ],
356
- tab_names=[
357
- "Preset Strategy Mode",
358
- "Manual Config Mode",
359
- "Manual Battle Mode",
360
- "Line Graph Viewer",
361
- "Strategy Leaderboard",
362
- "Strategy Descriptions",
363
- "Risk Matrix"
364
- ],
365
  title="EdgeCast – Strategy Simulation Suite"
 
366
  )
367
 
 
 
 
368
  app.launch()
 
1
  import os
2
+
3
  import gradio as gr
4
+
5
  import pandas as pd
6
+
7
  import numpy as np
8
+
9
  import plotly.graph_objs as go
10
+
11
  import plotly.express as px
12
 
13
+
14
+
15
+
16
  os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
17
+
18
  try:
19
+
20
  gr.analytics_enabled = False
21
+
22
  except:
23
+
24
  pass
25
 
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
+
128
  tp1_prob, tp2_prob, tp1_r, tp2_r, base_risk_pct,
129
+
130
  profit_target=None, fatigue=0.0, trump_vol=0.0):
131
+
132
  if tp1_prob + tp2_prob >= 1.0:
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
+
164
+
165
  for week in range(1, weeks + 1):
166
+
167
  if profit_target and balance >= profit_target: break
168
+
169
  week_start = balance
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
+
188
  outcome = "SL"
189
+
190
  else:
191
+
192
  outcome = np.random.choice(["TP1", "TP2", "SL"], p=[tp1_prob, tp2_prob, sl_prob])
193
 
194
+
195
+
196
+
197
  if outcome == "TP1":
198
+
199
  balance += risk_amount * tp1_r * fatigue_multiplier
200
+
201
  tp1_hits += 1
202
+
203
  cur_win_streak += 1
204
+
205
  cur_loss_streak = 0
206
+
207
  elif outcome == "TP2":
208
+
209
  balance += risk_amount * tp2_r * fatigue_multiplier
210
+
211
  tp2_hits += 1
212
+
213
  cur_win_streak += 1
214
+
215
  cur_loss_streak = 0
216
+
217
  else:
218
+
219
  balance -= risk_amount
220
+
221
  sl_hits += 1
222
+
223
  cur_loss_streak += 1
224
+
225
  cur_win_streak = 0
226
 
227
+
228
+
229
+
230
  max_win_streak = max(max_win_streak, cur_win_streak)
231
+
232
  max_loss_streak = max(max_loss_streak, cur_loss_streak)
233
+
234
  peak = max(peak, balance)
235
+
236
  drawdown = max(drawdown, (peak - balance) / peak * 100)
237
 
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
 
253
+
254
+
255
+
256
  df = pd.DataFrame(log)
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),
272
+
273
  "TP1 Hits": tp1_hits,
274
+
275
  "TP2 Hits": tp2_hits,
276
+
277
  "SL Hits": sl_hits,
278
+
279
  "Max Drawdown %": round(drawdown, 2),
280
+
281
  "Max Win Streak": max_win_streak,
282
+
283
  "Max Loss Streak": max_loss_streak,
284
+
285
+ "Sharpe Ratio": round(sharpe_ratio, 2),
286
+
287
  "EdgeCast Score": round(score, 2)
288
+
289
  }
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
+
421
+ results = []
422
+
423
+ for name, config in strategy_presets.items():
424
+
425
+ _, summary = simulate_tp_strategy_full(**{k: v for k, v in config.items() if k != "description"})
426
+
427
+ summary["Strategy"] = name
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(),
444
+
445
+ "Sharpe Ratio": df["Sharpe Ratio"].max(),
446
+
447
+ "EdgeCast Score": df["EdgeCast Score"].max(),
448
+
449
+ "Max Drawdown %": df["Max Drawdown %"].min()
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
  )
819
 
820
+
821
+
822
+
823
  app.launch()