Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -132,210 +132,39 @@ def equity_curve_plot(df, label="Equity Curve"):
|
|
| 132 |
fig.update_layout(title=f"๐ {label}", xaxis_title="Week", yaxis_title="Balance ($)", height=400)
|
| 133 |
return fig
|
| 134 |
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
import numpy as np
|
| 139 |
-
import plotly.graph_objs as go
|
| 140 |
-
import plotly.express as px
|
| 141 |
-
|
| 142 |
-
os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
|
| 143 |
-
try:
|
| 144 |
-
gr.analytics_enabled = False
|
| 145 |
-
except:
|
| 146 |
-
pass
|
| 147 |
-
|
| 148 |
-
# === STRATEGY PRESETS ===
|
| 149 |
-
def get_strategy_presets():
|
| 150 |
-
return {
|
| 151 |
-
"Aggressive Prop Trader": {
|
| 152 |
-
"starting_balance": 2500, "trades_min": 5, "trades_max": 10, "weeks": 12,
|
| 153 |
-
"tp1_prob": 0.25, "tp2_prob": 0.4, "tp1_r": 1.2, "tp2_r": 2.4,
|
| 154 |
-
"base_risk_pct": 0.015, "profit_target": None,
|
| 155 |
-
"fatigue": 0.0, "trump_vol": 0.0,
|
| 156 |
-
"description": "High-frequency, high-risk with strong upside potential."
|
| 157 |
-
},
|
| 158 |
-
"Conservative Swing Trader": {
|
| 159 |
-
"starting_balance": 2500, "trades_min": 2, "trades_max": 5, "weeks": 12,
|
| 160 |
-
"tp1_prob": 0.35, "tp2_prob": 0.25, "tp1_r": 0.9, "tp2_r": 1.8,
|
| 161 |
-
"base_risk_pct": 0.01, "profit_target": None,
|
| 162 |
-
"fatigue": 0.0, "trump_vol": 0.0,
|
| 163 |
-
"description": "Lower frequency, prioritizes preservation and steady returns."
|
| 164 |
-
},
|
| 165 |
-
"Momentum Scalper": {
|
| 166 |
-
"starting_balance": 2500, "trades_min": 4, "trades_max": 8, "weeks": 12,
|
| 167 |
-
"tp1_prob": 0.3, "tp2_prob": 0.35, "tp1_r": 1.0, "tp2_r": 2.2,
|
| 168 |
-
"base_risk_pct": 0.012, "profit_target": None,
|
| 169 |
-
"fatigue": 0.0, "trump_vol": 0.0,
|
| 170 |
-
"description": "Intraday momentum strategy for fast-paced trading windows."
|
| 171 |
-
},
|
| 172 |
-
"Swing Sniper": {
|
| 173 |
-
"starting_balance": 2500, "trades_min": 2, "trades_max": 4, "weeks": 12,
|
| 174 |
-
"tp1_prob": 0.2, "tp2_prob": 0.5, "tp1_r": 1.1, "tp2_r": 3.0,
|
| 175 |
-
"base_risk_pct": 0.008, "profit_target": None,
|
| 176 |
-
"fatigue": 0.0, "trump_vol": 0.0,
|
| 177 |
-
"description": "Selective entries with high RR setups. Less frequent."
|
| 178 |
-
},
|
| 179 |
-
"Intraday Prop Mode": {
|
| 180 |
-
"starting_balance": 2500, "trades_min": 3, "trades_max": 7, "weeks": 12,
|
| 181 |
-
"tp1_prob": 0.3, "tp2_prob": 0.3, "tp1_r": 1.0, "tp2_r": 2.0,
|
| 182 |
-
"base_risk_pct": 0.02, "profit_target": None,
|
| 183 |
-
"fatigue": 0.0, "trump_vol": 0.0,
|
| 184 |
-
"description": "Intraday consistency with a balanced reward profile."
|
| 185 |
-
}
|
| 186 |
-
}
|
| 187 |
-
|
| 188 |
-
strategy_presets = get_strategy_presets()
|
| 189 |
-
|
| 190 |
-
# === CORE SIMULATION ===
|
| 191 |
-
def simulate_tp_strategy_full(starting_balance, trades_min, trades_max, weeks,
|
| 192 |
-
tp1_prob, tp2_prob, tp1_r, tp2_r, base_risk_pct,
|
| 193 |
-
profit_target=None, fatigue=0.0, trump_vol=0.0):
|
| 194 |
-
if tp1_prob + tp2_prob >= 1.0:
|
| 195 |
-
return pd.DataFrame(), {"Error": "Invalid probability config. TP1 + TP2 must be < 1.0"}
|
| 196 |
-
|
| 197 |
-
sl_prob = 1.0 - tp1_prob - tp2_prob
|
| 198 |
-
balance, peak, drawdown = starting_balance, starting_balance, 0
|
| 199 |
-
tp1_hits = tp2_hits = sl_hits = 0
|
| 200 |
-
max_win_streak = max_loss_streak = cur_win_streak = cur_loss_streak = 0
|
| 201 |
-
fatigue_multiplier = 1.0 - fatigue * 0.4
|
| 202 |
-
trump_vol_factor = np.random.normal(1.0, 0.2 * trump_vol)
|
| 203 |
-
log = []
|
| 204 |
-
|
| 205 |
-
for week in range(1, weeks + 1):
|
| 206 |
-
if profit_target and balance >= profit_target: break
|
| 207 |
-
week_start = balance
|
| 208 |
-
num_trades = np.random.randint(trades_min, trades_max + 1)
|
| 209 |
-
|
| 210 |
-
for _ in range(num_trades):
|
| 211 |
-
risk_amount = balance * base_risk_pct * np.random.uniform(0.9, 1.1)
|
| 212 |
-
risk_amount *= trump_vol_factor
|
| 213 |
-
if fatigue > 0.6 and cur_loss_streak >= 3 and np.random.rand() < fatigue * 0.25:
|
| 214 |
-
outcome = "SL"
|
| 215 |
-
else:
|
| 216 |
-
outcome = np.random.choice(["TP1", "TP2", "SL"], p=[tp1_prob, tp2_prob, sl_prob])
|
| 217 |
-
|
| 218 |
-
if outcome == "TP1":
|
| 219 |
-
balance += risk_amount * tp1_r * fatigue_multiplier
|
| 220 |
-
tp1_hits += 1
|
| 221 |
-
cur_win_streak += 1
|
| 222 |
-
cur_loss_streak = 0
|
| 223 |
-
elif outcome == "TP2":
|
| 224 |
-
balance += risk_amount * tp2_r * fatigue_multiplier
|
| 225 |
-
tp2_hits += 1
|
| 226 |
-
cur_win_streak += 1
|
| 227 |
-
cur_loss_streak = 0
|
| 228 |
-
else:
|
| 229 |
-
balance -= risk_amount
|
| 230 |
-
sl_hits += 1
|
| 231 |
-
cur_loss_streak += 1
|
| 232 |
-
cur_win_streak = 0
|
| 233 |
-
|
| 234 |
-
max_win_streak = max(max_win_streak, cur_win_streak)
|
| 235 |
-
max_loss_streak = max(max_loss_streak, cur_loss_streak)
|
| 236 |
-
peak = max(peak, balance)
|
| 237 |
-
drawdown = max(drawdown, (peak - balance) / peak * 100)
|
| 238 |
-
|
| 239 |
-
log.append({
|
| 240 |
-
"Week": week, "Start Balance": round(week_start, 2),
|
| 241 |
-
"End Balance": round(balance, 2),
|
| 242 |
-
"Weekly Return (%)": round((balance - week_start) / week_start * 100, 2)
|
| 243 |
-
})
|
| 244 |
-
|
| 245 |
-
df = pd.DataFrame(log)
|
| 246 |
-
returns = df["End Balance"].pct_change().dropna()
|
| 247 |
-
sharpe = returns.mean() / returns.std() * np.sqrt(52) if returns.std() > 0 else 0
|
| 248 |
-
score = balance / (1 + drawdown)
|
| 249 |
-
summary = {
|
| 250 |
-
"Final Balance": round(balance, 2),
|
| 251 |
-
"TP1 Hits": tp1_hits,
|
| 252 |
-
"TP2 Hits": tp2_hits,
|
| 253 |
-
"SL Hits": sl_hits,
|
| 254 |
-
"Max Drawdown %": round(drawdown, 2),
|
| 255 |
-
"Max Win Streak": max_win_streak,
|
| 256 |
-
"Max Loss Streak": max_loss_streak,
|
| 257 |
-
"Sharpe Ratio": round(sharpe, 2),
|
| 258 |
-
"EdgeCast Score": round(score, 2)
|
| 259 |
-
}
|
| 260 |
-
return df, summary
|
| 261 |
-
|
| 262 |
-
# === VISUALIZATION ===
|
| 263 |
-
def equity_curve_plot(df, label="Equity Curve"):
|
| 264 |
-
fig = go.Figure()
|
| 265 |
-
fig.add_trace(go.Scatter(x=df["Week"], y=df["End Balance"], mode="lines+markers", name=label))
|
| 266 |
-
fig.update_layout(title=f"๐ {label}", xaxis_title="Week", yaxis_title="Balance ($)", height=400)
|
| 267 |
-
return fig
|
| 268 |
-
|
| 269 |
-
def generate_histogram(metric="EdgeCast Score", runs=100):
|
| 270 |
all_metrics = []
|
| 271 |
for name, config in strategy_presets.items():
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
})
|
| 283 |
|
| 284 |
df = pd.DataFrame(all_metrics)
|
| 285 |
|
| 286 |
-
|
|
|
|
| 287 |
df,
|
| 288 |
-
x=
|
|
|
|
| 289 |
color="Strategy",
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
barmode="overlay",
|
| 293 |
-
nbins=30,
|
| 294 |
-
title=f"๐ Distribution of {metric} Across Strategies"
|
| 295 |
)
|
| 296 |
|
| 297 |
fig.update_layout(
|
| 298 |
-
xaxis_title=
|
| 299 |
-
yaxis_title=
|
| 300 |
-
bargap=0.1,
|
| 301 |
height=500
|
| 302 |
)
|
| 303 |
-
|
| 304 |
-
return fig
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
def histogram_viewer_ui(metric):
|
| 308 |
-
return generate_histogram(metric)
|
| 309 |
-
|
| 310 |
-
# === ANALYSIS TOOLS ===
|
| 311 |
-
def analytics_dashboard(rank_by="EdgeCast Score"):
|
| 312 |
-
results = []
|
| 313 |
-
for name, config in strategy_presets.items():
|
| 314 |
-
_, summary = simulate_tp_strategy_full(**{k: v for k, v in config.items() if k != "description"})
|
| 315 |
-
summary["Strategy"] = name
|
| 316 |
-
results.append(summary)
|
| 317 |
-
df = pd.DataFrame(results)
|
| 318 |
-
winner_vals = {
|
| 319 |
-
"Final Balance": df["Final Balance"].max(),
|
| 320 |
-
"Sharpe Ratio": df["Sharpe Ratio"].max(),
|
| 321 |
-
"EdgeCast Score": df["EdgeCast Score"].max(),
|
| 322 |
-
"Max Drawdown %": df["Max Drawdown %"].min()
|
| 323 |
-
}
|
| 324 |
-
df = df.sort_values(rank_by, ascending=(rank_by == "Max Drawdown %")).reset_index(drop=True)
|
| 325 |
-
df.insert(0, "๐
Rank", [f"#{i+1}" for i in df.index])
|
| 326 |
-
for col in winner_vals:
|
| 327 |
-
df[col] = df[col].apply(lambda x: f"{round(x,2)} ๐" if x == winner_vals[col] else f"{round(x,2)}")
|
| 328 |
-
return df[["๐
Rank", "Strategy", "Final Balance", "Sharpe Ratio", "EdgeCast Score", "Max Drawdown %"]]
|
| 329 |
-
|
| 330 |
-
def show_descriptions():
|
| 331 |
-
return pd.DataFrame([{"Strategy": name, "Description": cfg["description"]} for name, cfg in strategy_presets.items()])
|
| 332 |
-
|
| 333 |
-
def generate_risk_matrix():
|
| 334 |
-
names = list(strategy_presets.keys())
|
| 335 |
-
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()}
|
| 336 |
-
matrix = np.array([[abs(scores[a] - scores[b]) for b in names] for a in names])
|
| 337 |
-
fig = px.imshow(matrix, x=names, y=names, text_auto=".2f", color_continuous_scale="RdYlGn_r", labels={"color": "ฮ Score"})
|
| 338 |
-
fig.update_layout(title="๐ง Risk Matrix (Score ฮ)", height=600)
|
| 339 |
return fig
|
| 340 |
|
| 341 |
# === STRATEGY RUNNERS ===
|
|
@@ -375,41 +204,6 @@ def dual_manual_battle(
|
|
| 375 |
fig.update_layout(title="โ๏ธ Manual Strategy Battle", xaxis_title="Week", yaxis_title="Balance")
|
| 376 |
return df_summary, fig
|
| 377 |
|
| 378 |
-
def get_manual_battle_interface():
|
| 379 |
-
return gr.Interface(
|
| 380 |
-
fn=dual_manual_battle,
|
| 381 |
-
inputs=[
|
| 382 |
-
# A Config
|
| 383 |
-
gr.Slider(100, 20000, 2500, label="A: Start Balance"),
|
| 384 |
-
gr.Slider(1, 10, 3, label="A: Trades Min"),
|
| 385 |
-
gr.Slider(1, 15, 7, label="A: Trades Max"),
|
| 386 |
-
gr.Slider(1, 52, 12, label="A: Weeks"),
|
| 387 |
-
gr.Slider(0, 1, 0.3, step=0.05, label="A: TP1 %"),
|
| 388 |
-
gr.Slider(0, 1, 0.3, step=0.05, label="A: TP2 %"),
|
| 389 |
-
gr.Slider(0.1, 5.0, 1.0, step=0.1, label="A: TP1 R"),
|
| 390 |
-
gr.Slider(0.1, 20.0, 2.0, step=0.1, label="A: TP2 R"),
|
| 391 |
-
gr.Slider(0.001, 0.05, 0.01, step=0.001, label="A: Risk %"),
|
| 392 |
-
gr.Slider(0, 100000, 0, step=500, label="A: Profit Target"),
|
| 393 |
-
gr.Slider(0, 1, 0.0, step=0.1, label="A: Fatigue"),
|
| 394 |
-
gr.Slider(0, 1, 0.0, step=0.1, label="A: Trump Volatility"),
|
| 395 |
-
# B Config
|
| 396 |
-
gr.Slider(100, 20000, 2500, label="B: Start Balance"),
|
| 397 |
-
gr.Slider(1, 10, 3, label="B: Trades Min"),
|
| 398 |
-
gr.Slider(1, 15, 7, label="B: Trades Max"),
|
| 399 |
-
gr.Slider(1, 52, 12, label="B: Weeks"),
|
| 400 |
-
gr.Slider(0, 1, 0.3, step=0.05, label="B: TP1 %"),
|
| 401 |
-
gr.Slider(0, 1, 0.3, step=0.05, label="B: TP2 %"),
|
| 402 |
-
gr.Slider(0.1, 5.0, 1.0, step=0.1, label="B: TP1 R"),
|
| 403 |
-
gr.Slider(0.1, 20.0, 2.0, step=0.1, label="B: TP2 R"),
|
| 404 |
-
gr.Slider(0.001, 0.05, 0.01, step=0.001, label="B: Risk %"),
|
| 405 |
-
gr.Slider(0, 100000, 0, step=500, label="B: Profit Target"),
|
| 406 |
-
gr.Slider(0, 1, 0.0, step=0.1, label="B: Fatigue"),
|
| 407 |
-
gr.Slider(0, 1, 0.0, step=0.1, label="B: Trump Volatility")
|
| 408 |
-
],
|
| 409 |
-
outputs=["dataframe", gr.Plot()],
|
| 410 |
-
title="๐งช Manual Strategy Battle"
|
| 411 |
-
)
|
| 412 |
-
|
| 413 |
# === LAUNCH INTERFACE ===
|
| 414 |
app = gr.TabbedInterface(
|
| 415 |
interface_list=[
|
|
@@ -452,7 +246,7 @@ app = gr.TabbedInterface(
|
|
| 452 |
|
| 453 |
# ๐ Line Graph Viewer Tab
|
| 454 |
gr.Interface(
|
| 455 |
-
fn=
|
| 456 |
inputs=gr.Dropdown(
|
| 457 |
choices=["Final Balance", "Sharpe Ratio", "EdgeCast Score", "Max Drawdown %"],
|
| 458 |
label="Select Metric"
|
|
|
|
| 132 |
fig.update_layout(title=f"๐ {label}", xaxis_title="Week", yaxis_title="Balance ($)", height=400)
|
| 133 |
return fig
|
| 134 |
|
| 135 |
+
# === LINE GRAPH ===
|
| 136 |
+
def line_graph_viewer_ui(metric="Final Balance"):
|
| 137 |
+
# Generate data for the line graph based on the selected metric
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
all_metrics = []
|
| 139 |
for name, config in strategy_presets.items():
|
| 140 |
+
_, summary = simulate_tp_strategy_full(
|
| 141 |
+
config["starting_balance"], config["trades_min"], config["trades_max"], config["weeks"],
|
| 142 |
+
config["tp1_prob"], config["tp2_prob"], config["tp1_r"], config["tp2_r"],
|
| 143 |
+
config["base_risk_pct"], config["profit_target"], config["fatigue"], config["trump_vol"]
|
| 144 |
+
)
|
| 145 |
+
if metric in summary:
|
| 146 |
+
all_metrics.append({
|
| 147 |
+
"Strategy": name,
|
| 148 |
+
metric: summary[metric]
|
| 149 |
+
})
|
|
|
|
| 150 |
|
| 151 |
df = pd.DataFrame(all_metrics)
|
| 152 |
|
| 153 |
+
# Generate a line graph of the selected metric
|
| 154 |
+
fig = px.line(
|
| 155 |
df,
|
| 156 |
+
x="Strategy",
|
| 157 |
+
y=metric,
|
| 158 |
color="Strategy",
|
| 159 |
+
title=f"๐ Line Graph of {metric}",
|
| 160 |
+
labels={metric: f"{metric}"}
|
|
|
|
|
|
|
|
|
|
| 161 |
)
|
| 162 |
|
| 163 |
fig.update_layout(
|
| 164 |
+
xaxis_title="Strategy",
|
| 165 |
+
yaxis_title=metric,
|
|
|
|
| 166 |
height=500
|
| 167 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
return fig
|
| 169 |
|
| 170 |
# === STRATEGY RUNNERS ===
|
|
|
|
| 204 |
fig.update_layout(title="โ๏ธ Manual Strategy Battle", xaxis_title="Week", yaxis_title="Balance")
|
| 205 |
return df_summary, fig
|
| 206 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
# === LAUNCH INTERFACE ===
|
| 208 |
app = gr.TabbedInterface(
|
| 209 |
interface_list=[
|
|
|
|
| 246 |
|
| 247 |
# ๐ Line Graph Viewer Tab
|
| 248 |
gr.Interface(
|
| 249 |
+
fn=line_graph_viewer_ui,
|
| 250 |
inputs=gr.Dropdown(
|
| 251 |
choices=["Final Balance", "Sharpe Ratio", "EdgeCast Score", "Max Drawdown %"],
|
| 252 |
label="Select Metric"
|