GoshawkVortexAI commited on
Commit
a25a4a4
·
verified ·
1 Parent(s): 0b20ce2

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -575
app.py DELETED
@@ -1,575 +0,0 @@
1
- import warnings
2
- warnings.filterwarnings("ignore")
3
-
4
- import numpy as np
5
- import pandas as pd
6
- import yfinance as yf
7
- import gradio as gr
8
- import plotly.graph_objects as go
9
-
10
- from sklearn.impute import SimpleImputer
11
- from sklearn.preprocessing import StandardScaler
12
- from sklearn.pipeline import Pipeline
13
- from sklearn.neighbors import KNeighborsClassifier, NearestNeighbors
14
- from sklearn.isotonic import IsotonicRegression
15
- from sklearn.metrics import balanced_accuracy_score, accuracy_score
16
-
17
- APP_TITLE = "Goshawk BTCUSD Adaptive Risk Engine v3"
18
- HORIZON = 5
19
- K_GRID = [5, 7, 9, 11, 15, 21]
20
- SL_GRID = [0.8, 1.0, 1.2, 1.5, 1.8, 2.0, 2.5, 3.0]
21
- RR_GRID = [1.0, 1.25, 1.5, 2.0, 2.5, 3.0, 4.0]
22
- CLASSES = [-1, 0, 1]
23
-
24
-
25
- def get_btc(period):
26
- df = yf.download("BTC-USD", period=period, interval="1d",
27
- auto_adjust=True, progress=False)
28
- if df is None or df.empty:
29
- raise ValueError("Yahoo Finance BTC verisi döndürmedi.")
30
- if isinstance(df.columns, pd.MultiIndex):
31
- df.columns = df.columns.get_level_values(0)
32
- df = df[["Open", "High", "Low", "Close", "Volume"]].dropna().copy()
33
- df.index = pd.to_datetime(df.index)
34
- return df
35
-
36
-
37
- def calc_atr(df, n=14):
38
- pc = df.Close.shift(1)
39
- tr = pd.concat([(df.High-df.Low), (df.High-pc).abs(),
40
- (df.Low-pc).abs()], axis=1).max(axis=1)
41
- return tr.ewm(alpha=1/n, adjust=False).mean()
42
-
43
-
44
- def calc_rsi(s, n=14):
45
- d = s.diff()
46
- up = d.clip(lower=0).ewm(alpha=1/n, adjust=False).mean()
47
- dn = (-d.clip(upper=0)).ewm(alpha=1/n, adjust=False).mean()
48
- rs = up / dn.replace(0, np.nan)
49
- return 100 - 100/(1+rs)
50
-
51
-
52
- def make_features(df):
53
- c, v = df.Close, df.Volume
54
- x = pd.DataFrame(index=df.index)
55
- for n in [1, 2, 3, 5, 10, 20, 50]:
56
- x[f"ret_{n}"] = c.pct_change(n)
57
- for n in [5, 10, 20, 50]:
58
- x[f"ema_gap_{n}"] = c/c.ewm(span=n, adjust=False).mean()-1
59
- x["rsi_14"] = calc_rsi(c)/100
60
- x["atr_pct"] = calc_atr(df)/c
61
- x["range_pct"] = (df.High-df.Low)/c
62
- x["body_pct"] = (df.Close-df.Open)/df.Open
63
- daily_ret = c.pct_change()
64
- x["vol_10"] = daily_ret.rolling(10).std()
65
- x["vol_20"] = daily_ret.rolling(20).std()
66
- x["volume_z20"] = (v-v.rolling(20).mean())/v.rolling(20).std().replace(0, np.nan)
67
- x["volume_change"] = v.pct_change()
68
- macd = c.ewm(span=12, adjust=False).mean()-c.ewm(span=26, adjust=False).mean()
69
- x["macd_pct"] = macd/c
70
- return x.replace([np.inf, -np.inf], np.nan), calc_atr(df)
71
-
72
-
73
- def regime_labels(features):
74
- vol = features["vol_20"]
75
- trend = features["ema_gap_20"]
76
- vol_q1, vol_q2 = vol.quantile([0.33, 0.67])
77
- out = []
78
- for v, t in zip(vol, trend):
79
- vname = "LOW_VOL" if v <= vol_q1 else ("HIGH_VOL" if v >= vol_q2 else "MID_VOL")
80
- tname = "UP" if t > 0.02 else ("DOWN" if t < -0.02 else "RANGE")
81
- out.append(f"{tname}_{vname}")
82
- return pd.Series(out, index=features.index)
83
-
84
-
85
- def make_target(df, atr, horizon=HORIZON, threshold=0.20):
86
- fwd = df.Close.shift(-horizon)/df.Close-1
87
- band = threshold*(atr/df.Close)*np.sqrt(horizon)
88
- y = np.where(fwd > band, 1, np.where(fwd < -band, -1, 0))
89
- return pd.Series(y, index=df.index), fwd
90
-
91
-
92
- def pipe(k):
93
- return Pipeline([
94
- ("imputer", SimpleImputer(strategy="median")),
95
- ("scale", StandardScaler()),
96
- ("knn", KNeighborsClassifier(n_neighbors=int(k), weights="distance"))
97
- ])
98
-
99
-
100
- def tune_k(X, y):
101
- # Inner chronological validation, with horizon purge to avoid overlapping labels.
102
- cut = int(len(X)*0.80)
103
- tr_end = cut-HORIZON
104
- if tr_end <= max(K_GRID)+20 or len(X)-cut < 25:
105
- return 9, []
106
- scores = []
107
- for k in K_GRID:
108
- model = pipe(k)
109
- model.fit(X.iloc[:tr_end], y.iloc[:tr_end])
110
- pred = model.predict(X.iloc[cut:])
111
- score = balanced_accuracy_score(y.iloc[cut:], pred)
112
- scores.append((float(score), int(k)))
113
- scores.sort(reverse=True)
114
- return scores[0][1], scores
115
-
116
-
117
- def calibrate_probabilities(raw_probs, raw_classes, y_cal):
118
- # One-vs-rest isotonic calibration on a later, held-out calibration slice.
119
- # Small calibration sets fall back to raw probabilities.
120
- calibrated = np.zeros((len(raw_probs), len(CLASSES)), dtype=float)
121
- for j, cls in enumerate(CLASSES):
122
- if cls not in raw_classes:
123
- continue
124
- raw_col = raw_probs[:, list(raw_classes).index(cls)]
125
- truth = (np.asarray(y_cal) == cls).astype(int)
126
- if len(truth) >= 35 and len(np.unique(truth)) == 2:
127
- iso = IsotonicRegression(out_of_bounds="clip")
128
- iso.fit(raw_col, truth)
129
- calibrated[:, j] = iso.predict(raw_col)
130
- else:
131
- calibrated[:, j] = raw_col
132
- sums = calibrated.sum(axis=1, keepdims=True)
133
- fallback = np.zeros_like(calibrated)
134
- for j, cls in enumerate(CLASSES):
135
- if cls in raw_classes:
136
- fallback[:, j] = raw_probs[:, list(raw_classes).index(cls)]
137
- fallback_sum = fallback.sum(axis=1, keepdims=True)
138
- calibrated = np.where(sums > 0, calibrated/np.maximum(sums, 1e-12),
139
- fallback/np.maximum(fallback_sum, 1e-12))
140
- return calibrated
141
-
142
-
143
- def barrier_result(df, atr_s, i, side, sl_atr, rr, horizon=HORIZON):
144
- entry = float(df.Close.iloc[i])
145
- av = float(atr_s.iloc[i])
146
- if not np.isfinite(av) or av <= 0:
147
- return None
148
- stop_d = sl_atr*av
149
- target_d = stop_d*rr
150
- end = min(i+horizon, len(df)-1)
151
- for j in range(i+1, end+1):
152
- hi, lo = float(df.High.iloc[j]), float(df.Low.iloc[j])
153
- if side == 1:
154
- sl_hit, tp_hit = lo <= entry-stop_d, hi >= entry+target_d
155
- else:
156
- sl_hit, tp_hit = hi >= entry+stop_d, lo <= entry-target_d
157
- # Daily candles do not reveal intraday order: resolve ambiguous bars against us.
158
- if sl_hit and tp_hit:
159
- return -stop_d/entry
160
- if sl_hit:
161
- return -stop_d/entry
162
- if tp_hit:
163
- return target_d/entry
164
- return side*(float(df.Close.iloc[end])-entry)/entry
165
-
166
-
167
- def trade_r_multiple(raw_return, entry, atr_value, sl_atr, fee_pct, slip_pct):
168
- risk_fraction = sl_atr*atr_value/entry
169
- round_trip_cost = 2*(fee_pct+slip_pct)/100
170
- net_return = raw_return-round_trip_cost
171
- return net_return/max(risk_fraction, 1e-12)
172
-
173
-
174
- def optimize_barriers(df, atr_s, regimes, end_i, side, fee, slip, current_regime):
175
- # Prefer observations from the current trend/volatility regime.
176
- # If the regime has too few matured examples, fall back to all regimes.
177
- results = []
178
- start = max(80, end_i-650)
179
- regime_indices = [
180
- i for i in range(start, end_i-HORIZON)
181
- if str(regimes.iloc[i]) == str(current_regime)
182
- ]
183
- if len(regime_indices) < 35:
184
- regime_indices = list(range(start, end_i-HORIZON))
185
- regime_used = "GLOBAL_FALLBACK"
186
- else:
187
- regime_used = str(current_regime)
188
-
189
- for sl in SL_GRID:
190
- for rr in RR_GRID:
191
- vals = []
192
- for i in regime_indices:
193
- r = barrier_result(df, atr_s, i, side, sl, rr)
194
- if r is not None:
195
- vals.append(r-2*(fee+slip)/100)
196
- if len(vals) >= 35:
197
- a = np.asarray(vals)
198
- downside = np.sqrt(np.mean(np.minimum(a, 0)**2))
199
- score = a.mean()-0.35*downside
200
- results.append((score, sl, rr, len(a), a.mean(), (a > 0).mean()))
201
- if not results:
202
- return 1.5, 2.0, None, [], regime_used
203
- results.sort(reverse=True)
204
- return float(results[0][1]), float(results[0][2]), results[0], results[:10], regime_used
205
-
206
-
207
- def evaluate_oos(df, atr_s, X, y, regime_s, train_end, k, sl, rr, fee, slip):
208
- # Model fit only on pre-OOS data; all test observations are later in time.
209
- model = pipe(k)
210
- model.fit(X.iloc[:train_end], y.iloc[:train_end])
211
- test_start = train_end+HORIZON
212
- test_end = len(X)-HORIZON
213
- if test_end <= test_start+20:
214
- return [], None, None, []
215
- pred = model.predict(X.iloc[test_start:test_end])
216
- actual = y.iloc[test_start:test_end].to_numpy()
217
- bacc = balanced_accuracy_score(actual, pred)
218
- acc = accuracy_score(actual, pred)
219
- trades, details = [], []
220
- for off, p in enumerate(pred):
221
- if int(p) not in (-1, 1):
222
- continue
223
- i = test_start+off
224
- raw = barrier_result(df, atr_s, i, int(p), sl, rr)
225
- if raw is None:
226
- continue
227
- r = trade_r_multiple(raw, float(df.Close.iloc[i]), float(atr_s.iloc[i]),
228
- sl, fee, slip)
229
- trades.append(float(r))
230
- details.append((df.index[i], int(p), float(r), regime_s.iloc[i]))
231
- return trades, float(bacc), float(acc), details
232
-
233
-
234
- def risk_path_mc(r_multiples, base_risk_pct, simulations=1000, n_trades=100):
235
- # Bootstrap in R units. Equity sizing compounds and risk cuts after drawdown.
236
- if len(r_multiples) < 10:
237
- return None
238
- rng = np.random.default_rng(20261103)
239
- samples = rng.choice(np.asarray(r_multiples), size=(simulations, n_trades), replace=True)
240
- finals, maxdds, ruin = [], [], []
241
- for seq in samples:
242
- eq, peak, mdd = 1.0, 1.0, 0.0
243
- for r in seq:
244
- dd = eq/peak-1
245
- risk = base_risk_pct/100
246
- if dd <= -0.20:
247
- risk *= 0.20
248
- elif dd <= -0.15:
249
- risk *= 0.35
250
- elif dd <= -0.10:
251
- risk *= 0.50
252
- elif dd <= -0.06:
253
- risk *= 0.75
254
- # Cap one-trade loss at 1R; profit remains proportional to R.
255
- r_limited = max(float(r), -1.0)
256
- eq *= max(0.01, 1 + risk*r_limited)
257
- peak = max(peak, eq)
258
- mdd = min(mdd, eq/peak-1)
259
- finals.append(eq-1)
260
- maxdds.append(mdd)
261
- ruin.append(eq < 0.5)
262
- return {
263
- "median": float(np.median(finals)),
264
- "p05": float(np.percentile(finals, 5)),
265
- "p95": float(np.percentile(finals, 95)),
266
- "dd95": float(np.percentile(maxdds, 5)),
267
- "ruin": float(np.mean(ruin))
268
- }
269
-
270
-
271
- def adaptive_risk(min_risk, max_risk, bacc, agreement, expectancy_r, n_trades, drawdown):
272
- # Explicit deterministic policy. Not represented as reinforcement learning.
273
- quality = np.clip((bacc-0.33)/0.20, 0, 1) if bacc is not None else 0
274
- agree = np.clip((agreement-0.33)/0.50, 0, 1)
275
- edge = np.clip(expectancy_r/0.30, 0, 1)
276
- sample = np.clip(n_trades/40, 0, 1)
277
- score = 0.30*quality+0.25*agree+0.30*edge+0.15*sample
278
- risk = min_risk+(max_risk-min_risk)*score
279
- if drawdown <= -0.20:
280
- risk = min_risk
281
- elif drawdown <= -0.15:
282
- risk = min_risk+(risk-min_risk)*0.20
283
- elif drawdown <= -0.10:
284
- risk = min_risk+(risk-min_risk)*0.40
285
- elif drawdown <= -0.06:
286
- risk = min_risk+(risk-min_risk)*0.70
287
- return float(np.clip(risk, min_risk, max_risk)), float(score)
288
-
289
-
290
- def run_engine(period, equity, min_risk, max_risk, max_leverage, fee, slip, train_window):
291
- try:
292
- df = get_btc(period)
293
- if len(df) < 450:
294
- raise ValueError("En az 450 günlük mum gerekli.")
295
- # Exclude current possibly incomplete daily candle.
296
- df = df.iloc[:-1].copy()
297
- X_all, atr_all = make_features(df)
298
- y_all, _ = make_target(df, atr_all)
299
- regimes_all = regime_labels(X_all)
300
-
301
- # Align rows and purge final horizon whose labels are not mature.
302
- valid = X_all.notna().all(axis=1) & y_all.notna()
303
- X = X_all.loc[valid].iloc[:-HORIZON].copy()
304
- y = y_all.loc[X.index].astype(int).copy()
305
- dfv = df.loc[X.index].copy()
306
- atrv = atr_all.loc[X.index].copy()
307
- regv = regimes_all.loc[X.index].copy()
308
- if len(X) < 300:
309
- raise ValueError("Temiz özelliklerden sonra yeterli veri kalmadı.")
310
-
311
- # Reserve untouched final OOS block.
312
- oos_n = min(180, max(75, int(len(X)*0.22)))
313
- dev_end = len(X)-oos_n-HORIZON
314
- if dev_end < 200:
315
- raise ValueError("OOS ayrımı için yeterli geliştirme verisi yok.")
316
-
317
- dev_X, dev_y = X.iloc[:dev_end], y.iloc[:dev_end]
318
- if len(dev_X) > int(train_window):
319
- dev_X, dev_y = dev_X.iloc[-int(train_window):], dev_y.iloc[-int(train_window):]
320
- # Keep aligned development frame and indices.
321
- dev_start_idx = X.index.get_loc(dev_X.index[0])
322
- else:
323
- dev_start_idx = 0
324
-
325
- best_k, k_scores = tune_k(dev_X, dev_y)
326
- # Calibration split is chronological and separate from fit subset.
327
- cal_n = min(120, max(45, int(len(dev_X)*0.20)))
328
- fit_end = len(dev_X)-cal_n-HORIZON
329
- if fit_end <= 100:
330
- raise ValueError("Kalibrasyon için eğitim verisi yetersiz.")
331
- fit_X, fit_y = dev_X.iloc[:fit_end], dev_y.iloc[:fit_end]
332
- cal_X, cal_y = dev_X.iloc[fit_end+HORIZON:], dev_y.iloc[fit_end+HORIZON:]
333
- cal_model = pipe(best_k)
334
- cal_model.fit(fit_X, fit_y)
335
- raw_cal = cal_model.predict_proba(cal_X)
336
- calibrated_cal = calibrate_probabilities(
337
- raw_cal, cal_model.named_steps["knn"].classes_, cal_y.to_numpy()
338
- )
339
- # Current prediction from the same pre-calibration fit model.
340
- current_x = X_all.iloc[[-1]]
341
- raw_current = cal_model.predict_proba(current_x)[0:1]
342
- # Fit isotonic calibrators on held-out calibration predictions.
343
- calibrated_current = np.zeros((1, len(CLASSES)))
344
- for j, cls in enumerate(CLASSES):
345
- classes_now = list(cal_model.named_steps["knn"].classes_)
346
- if cls not in classes_now:
347
- continue
348
- col = raw_cal[:, classes_now.index(cls)]
349
- truth = (cal_y.to_numpy() == cls).astype(int)
350
- raw_val = raw_current[:, classes_now.index(cls)]
351
- if len(truth) >= 35 and len(np.unique(truth)) == 2:
352
- iso = IsotonicRegression(out_of_bounds="clip")
353
- iso.fit(col, truth)
354
- calibrated_current[0, j] = iso.predict(raw_val)[0]
355
- else:
356
- calibrated_current[0, j] = raw_val[0]
357
- if calibrated_current.sum() > 0:
358
- calibrated_current /= calibrated_current.sum()
359
- else:
360
- calibrated_current[0] = [1/3, 1/3, 1/3]
361
- pmap = {cls: float(calibrated_current[0, j]) for j, cls in enumerate(CLASSES)}
362
- p_down, p_flat, p_up = pmap[-1], pmap[0], pmap[1]
363
- side = 1 if p_up >= p_down else -1
364
-
365
- # Neighbor agreement based on fit examples only.
366
- imp, scaler = cal_model.named_steps["imputer"], cal_model.named_steps["scale"]
367
- zfit = scaler.transform(imp.transform(fit_X))
368
- zcur = scaler.transform(imp.transform(current_x))
369
- nn = NearestNeighbors(n_neighbors=best_k).fit(zfit)
370
- _, idx = nn.kneighbors(zcur)
371
- agreement = float((fit_y.iloc[idx[0]].to_numpy() == side).mean())
372
-
373
- # Optimize barriers on development data only, filtered to the current regime.
374
- current_regime = str(regimes_all.iloc[-1])
375
- dev_idx = dev_X.index
376
- dev_df = df.loc[dev_idx]
377
- dev_atr = atr_all.loc[dev_idx]
378
- dev_reg = regimes_all.loc[dev_idx]
379
- sl, rr, opt, top, regime_used = optimize_barriers(
380
- dev_df, dev_atr, dev_reg, len(dev_df), side,
381
- float(fee), float(slip), current_regime
382
- )
383
- regime_note = (
384
- f"TP/SL optimizasyon rejimi: {regime_used}. "
385
- "Yeterli rejim örneği yoksa tüm geliştirme dönemine geri dönülür."
386
- )
387
-
388
- # OOS evaluation on untouched tail. Purge gap separates train and test.
389
- oos_X = X.iloc[dev_end+HORIZON:]
390
- oos_y = y.iloc[dev_end+HORIZON:]
391
- oos_df = df.loc[oos_X.index]
392
- oos_atr = atr_all.loc[oos_X.index]
393
- oos_reg = regimes_all.loc[oos_X.index]
394
- oos_model = pipe(best_k)
395
- oos_model.fit(dev_X, dev_y)
396
- oos_pred = oos_model.predict(oos_X)
397
- oos_bacc = balanced_accuracy_score(oos_y, oos_pred)
398
- oos_acc = accuracy_score(oos_y, oos_pred)
399
- r_values, oos_details = [], []
400
- for i, pred in enumerate(oos_pred):
401
- if int(pred) not in (-1, 1):
402
- continue
403
- raw_ret = barrier_result(oos_df, oos_atr, i, int(pred), sl, rr)
404
- if raw_ret is None:
405
- continue
406
- r = trade_r_multiple(raw_ret, float(oos_df.Close.iloc[i]),
407
- float(oos_atr.iloc[i]), sl, float(fee), float(slip))
408
- r_values.append(r)
409
- oos_details.append((oos_df.index[i], int(pred), r, str(oos_reg.iloc[i])))
410
-
411
- expectancy_r = float(np.mean(r_values)) if r_values else 0.0
412
- recent = df.Close.tail(30)
413
- drawdown = float(recent.iloc[-1]/recent.cummax().iloc[-1]-1)
414
- risk_pct, risk_score = adaptive_risk(
415
- float(min_risk), float(max_risk), float(oos_bacc),
416
- agreement, expectancy_r, len(r_values), drawdown
417
- )
418
- mc = risk_path_mc(r_values, risk_pct) if r_values else None
419
-
420
- entry = float(df.Close.iloc[-1])
421
- current_atr = float(atr_all.iloc[-1])
422
- stop_d, target_d = sl*current_atr, sl*current_atr*rr
423
- stop = entry-stop_d if side == 1 else entry+stop_d
424
- target = entry+target_d if side == 1 else entry-target_d
425
-
426
- # Position size: include estimated round-trip costs in risk per BTC.
427
- risk_cash = float(equity)*risk_pct/100
428
- cost_per_btc = stop_d+entry*2*(float(fee)+float(slip))/100
429
- btc_qty = risk_cash/cost_per_btc if cost_per_btc > 0 else 0
430
- notional = btc_qty*entry
431
- max_notional = float(equity)*float(max_leverage)
432
- capped = notional > max_notional
433
- if capped:
434
- btc_qty = max_notional/entry
435
- notional = btc_qty*entry
436
- actual_risk_cash = btc_qty*cost_per_btc
437
- margin = notional/max(float(max_leverage), 1)
438
-
439
- # Strong no-trade gate based on untouched OOS results.
440
- trade_ok = (
441
- max(p_up, p_down) >= 0.43
442
- and abs(p_up-p_down) >= 0.12
443
- and agreement >= 0.55
444
- and oos_bacc >= 0.36
445
- and len(r_values) >= 15
446
- and expectancy_r > 0
447
- and p_flat < 0.60
448
- )
449
- if not trade_ok:
450
- signal = "WAIT — İŞLEM AÇMA"
451
- btc_qty = notional = margin = actual_risk_cash = 0.0
452
- else:
453
- signal = "LONG" if side == 1 else "SHORT"
454
-
455
- mc_text = (
456
- f"100 işlem · 1.000 bootstrap senaryosu · medyan: {mc['median']:.2%} · "
457
- f"%5 kötü sonuç: {mc['p05']:.2%} · %5 kuyruk max DD: {mc['dd95']:.2%} · "
458
- f"%50 sermaye altına inme frekansı: {mc['ruin']:.1%}"
459
- if mc else "Monte Carlo için yeterli OOS işlem yok."
460
- )
461
- k_text = " | ".join([f"K={k}: {s:.3f}" for s, k in sorted(k_scores, reverse=True)])
462
- report = f"""
463
- # {signal}
464
-
465
- **Son kapanmış mum:** {df.index[-1].date()} · **Referans fiyat:** ${entry:,.2f}
466
-
467
- ## Kalibre edilmiş KNN
468
- | Ölçüm | Değer |
469
- |---|---:|
470
- | P(UP) | {p_up:.1%} |
471
- | P(FLAT) | {p_flat:.1%} |
472
- | P(DOWN) | {p_down:.1%} |
473
- | Seçilen K | {best_k} |
474
- | Komşu yön uyumu | {agreement:.1%} |
475
- | OOS balanced accuracy | {oos_bacc:.3f} |
476
- | OOS accuracy | {oos_acc:.3f} |
477
- | OOS işlem sayısı | {len(r_values)} |
478
- | OOS expectancy | {expectancy_r:.3f}R / işlem |
479
- | Güncel rejim | {current_regime} |
480
-
481
- **K seçim skorları:** {k_text}
482
-
483
- ## TP / SL
484
- | Parametre | Değer |
485
- |---|---:|
486
- | SL | {sl:.2f} ATR |
487
- | TP | {rr:.2f}R |
488
- | Stop-loss | ${stop:,.2f} |
489
- | Take-profit | ${target:,.2f} |
490
- | ATR(14) | ${current_atr:,.2f} |
491
-
492
- {regime_note}
493
-
494
- ## Gerçek risk-bütçeli pozisyon
495
- | Parametre | Değer |
496
- |---|---:|
497
- | Hesap büyüklüğü | ${equity:,.2f} |
498
- | Adaptif risk | {risk_pct:.3f}% |
499
- | Risk bütçesi | ${risk_cash:,.2f} |
500
- | BTC miktarı | {btc_qty:.8f} |
501
- | Nominal pozisyon | ${notional:,.2f} |
502
- | Tahmini teminat | ${margin:,.2f} |
503
- | Tahmini stop kaybı + maliyet | ${actual_risk_cash:,.2f} |
504
- | Kaldıraç sınırı | {"Uygulandı" if capped else "Uygulanmadı"} |
505
- | Son 30 günlük kapanı�� DD | {drawdown:.2%} |
506
-
507
- ## Monte Carlo — R-multiple + gerçek risk yüzdesi
508
- {mc_text}
509
- """
510
- fig = go.Figure([go.Candlestick(
511
- x=df.index[-150:], open=df.Open.tail(150), high=df.High.tail(150),
512
- low=df.Low.tail(150), close=df.Close.tail(150), name="BTCUSD"
513
- )])
514
- if trade_ok:
515
- fig.add_hline(y=stop, line_dash="dash", annotation_text="SL")
516
- fig.add_hline(y=target, line_dash="dash", annotation_text="TP")
517
- fig.update_layout(title="BTCUSD Daily — last 150 candles", height=520,
518
- xaxis_rangeslider_visible=False)
519
-
520
- top_df = pd.DataFrame(top, columns=[
521
- "score", "SL_ATR", "TP_R", "samples", "mean_net_return", "win_rate"
522
- ])
523
- if not top_df.empty:
524
- top_df["score"] = top_df.score.map(lambda x: f"{x:.5f}")
525
- top_df["mean_net_return"] = top_df.mean_net_return.map(lambda x: f"{x:.3%}")
526
- top_df["win_rate"] = top_df.win_rate.map(lambda x: f"{x:.1%}")
527
-
528
- disclaimer = (
529
- "**Araştırma prototipi; otomatik emir göndermez.** KNN olasılık kalibrasyonu "
530
- "tek bir tarihsel kalibrasyon dilimine dayanır ve küçük örneklerde ham olasılığa "
531
- "geri döner. TP/SL optimizasyonu geliştirme döneminde yapılır; son dönem OOS "
532
- "olarak ayrılır. Rejim etiketleri geliştirme verisinin volatilite kantillerine "
533
- "dayanır; bu nedenle canlı dağılım değişebilir. Monte Carlo, OOS R-multiple "
534
- "serisini bootstrap eder ve işlem başına gerçek risk yüzdesini bileşik uygular; "
535
- "bağımsızlık varsayımı seri korelasyonunu ve rejim değişimini tam modellemez. "
536
- "Pozisyon hesabı BTC spot miktarı varsayar; CFD lot sözleşmesi, spread, funding, "
537
- "swap, komisyon, minimum lot, marjin ve broker fiyatı farklı olabilir. Gap ve "
538
- "kayma kaybı planlanan riskin üstüne çıkarabilir. Canlı hesap öncesi paper trading "
539
- "ve broker bazlı doğrulama zorunludur."
540
- )
541
- return report, fig, top_df, disclaimer
542
-
543
- except Exception as exc:
544
- return f"# Hata\n{type(exc).__name__}: {exc}", None, pd.DataFrame(), \
545
- "Veri bağlantısını ve parametreleri kontrol edin."
546
-
547
-
548
- with gr.Blocks(title=APP_TITLE, theme=gr.themes.Soft()) as demo:
549
- gr.Markdown(
550
- "# GOSHAWK — Adaptive BTCUSD Risk Engine v3\n"
551
- "Kalibre edilmiş KNN · adaptif K · TP/SL optimizasyonu · bağımsız OOS · "
552
- "R-multiple Monte Carlo · drawdown bazlı risk."
553
- )
554
- with gr.Row():
555
- with gr.Column():
556
- period = gr.Dropdown(["2y", "5y", "max"], value="5y", label="Veri geçmişi")
557
- train_window = gr.Slider(300, 1500, value=900, step=50, label="Maksimum eğitim penceresi")
558
- equity = gr.Number(value=10000, label="Hesap büyüklüğü (USD)")
559
- max_leverage = gr.Slider(1, 10, value=3, step=1, label="Azami kaldıraç")
560
- with gr.Column():
561
- min_risk = gr.Slider(0.05, 0.5, value=0.10, step=0.05, label="Minimum risk (%)")
562
- max_risk = gr.Slider(0.25, 1.5, value=0.75, step=0.05, label="Maksimum risk (%)")
563
- fee = gr.Number(value=0.05, label="Tek yön komisyon (%)")
564
- slip = gr.Number(value=0.03, label="Tek yön slippage (%)")
565
- run = gr.Button("Öğren, optimize et ve test et", variant="primary")
566
- output = gr.Markdown()
567
- chart = gr.Plot()
568
- optimization = gr.Dataframe(label="En iyi TP/SL kombinasyonları", interactive=False)
569
- disclaimer = gr.Markdown()
570
- run.click(run_engine,
571
- inputs=[period, equity, min_risk, max_risk, max_leverage, fee, slip, train_window],
572
- outputs=[output, chart, optimization, disclaimer])
573
-
574
- if __name__ == "__main__":
575
- demo.launch(server_name="0.0.0.0", server_port=7860)