GoshawkVortexAI commited on
Commit
5ed4d8f
·
verified ·
1 Parent(s): ff0549d

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -1755
app.py DELETED
@@ -1,1755 +0,0 @@
1
- import warnings
2
- warnings.filterwarnings("ignore")
3
-
4
- import os
5
- import re
6
- import time
7
- import tempfile
8
-
9
- import numpy as np
10
- import pandas as pd
11
- import yfinance as yf
12
- import gradio as gr
13
- import plotly.graph_objects as go
14
- from plotly.subplots import make_subplots
15
-
16
- from sklearn.impute import SimpleImputer
17
- from sklearn.preprocessing import StandardScaler
18
- from sklearn.pipeline import Pipeline
19
- from sklearn.neighbors import KNeighborsClassifier, NearestNeighbors
20
- from sklearn.isotonic import IsotonicRegression
21
- from sklearn.metrics import balanced_accuracy_score, accuracy_score
22
-
23
- APP_TITLE = "Goshawk Adaptive Risk Engine v5"
24
- ASSETS = ["BTC-USD", "ETH-USD", "SOL-USD", "AAPL", "MSFT", "NVDA", "SPY", "QQQ",
25
- "GC=F", "EURUSD=X"]
26
- K_GRID = [5, 7, 9, 11, 15, 21]
27
- SL_GRID = [0.8, 1.0, 1.2, 1.5, 1.8, 2.0, 2.5, 3.0]
28
- RR_GRID = [1.0, 1.25, 1.5, 2.0, 2.5, 3.0, 4.0]
29
- CLASSES = [-1, 0, 1]
30
- SIDE_MODES = ["Long + Short", "Sadece Long", "Sadece Short"]
31
- IND_MODES = ["Kapalı", "Onay filtresi (KNN + ST/UT)", "Sadece indikatör"]
32
- COMBO_MODES = ["Kapalı", "Onay filtresi (KNN + birleşik)", "Sadece birleşik sinyal"]
33
- LDC_MODES = ["Kapalı", "Onay filtresi (Lorentzian)", "Sadece Lorentzian"]
34
- VP_MODES = ["Kapalı", "Onay filtresi (POC / Value Area)", "KNN özelliği + onay filtresi"]
35
- GANN_MODES = ["Kapalı", "Onay filtresi (Gann Box %50)"]
36
- SMC_MODES = ["Kapalı", "Onay filtresi (SMC yapı yönü)", "Sadece SMC (yapı kırılımı girişi)"]
37
- SMC_SRC = ["Swing yapı", "Internal yapı"]
38
- SMC_OBF = ["Atr", "Cumulative Mean Range"]
39
- GANN_LEVELS = [0.0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0]
40
- GANN_FAN = [(8, "8x1"), (4, "4x1"), (3, "3x1"), (2, "2x1"), (1, "1x1"),
41
- (1/2, "1x2"), (1/3, "1x3"), (1/4, "1x4"), (1/8, "1x8")]
42
-
43
- _CACHE = {}
44
- CACHE_TTL = 600 # saniye; Yahoo'yu gereksiz yere tekrar çağırmamak için
45
-
46
-
47
- # ---------------------------------------------------------------- veri / özellik
48
- def get_market_data(ticker, period):
49
- key, now = (ticker, period), time.time()
50
- if key in _CACHE and now - _CACHE[key][0] < CACHE_TTL:
51
- return _CACHE[key][1].copy()
52
- df = yf.download(ticker, period=period, interval="1d",
53
- auto_adjust=True, progress=False)
54
- if df is None or df.empty:
55
- raise ValueError(f"Yahoo Finance {ticker} verisi döndürmedi.")
56
- if isinstance(df.columns, pd.MultiIndex):
57
- df.columns = df.columns.get_level_values(0)
58
- df = df[["Open", "High", "Low", "Close", "Volume"]].dropna().copy()
59
- df.index = pd.to_datetime(df.index)
60
- _CACHE[key] = (now, df)
61
- return df.copy()
62
-
63
-
64
- def calc_atr(df, n=14):
65
- pc = df.Close.shift(1)
66
- tr = pd.concat([(df.High - df.Low), (df.High - pc).abs(),
67
- (df.Low - pc).abs()], axis=1).max(axis=1)
68
- return tr.ewm(alpha=1/n, adjust=False).mean()
69
-
70
-
71
- def calc_rsi(s, n=14):
72
- d = s.diff()
73
- up = d.clip(lower=0).ewm(alpha=1/n, adjust=False).mean()
74
- dn = (-d.clip(upper=0)).ewm(alpha=1/n, adjust=False).mean()
75
- rs = up / dn.replace(0, np.nan)
76
- return 100 - 100/(1+rs)
77
-
78
-
79
- def make_features(df, vp=None):
80
- c, v = df.Close, df.Volume
81
- x = pd.DataFrame(index=df.index)
82
- for n in [1, 2, 3, 5, 10, 20, 50]:
83
- x[f"ret_{n}"] = c.pct_change(n)
84
- for n in [5, 10, 20, 50]:
85
- x[f"ema_gap_{n}"] = c/c.ewm(span=n, adjust=False).mean()-1
86
- x["rsi_14"] = calc_rsi(c)/100
87
- atr = calc_atr(df)
88
- x["atr_pct"] = atr/c
89
- x["range_pct"] = (df.High-df.Low)/c
90
- x["body_pct"] = (df.Close-df.Open)/df.Open
91
- daily_ret = c.pct_change()
92
- x["vol_10"] = daily_ret.rolling(10).std()
93
- x["vol_20"] = daily_ret.rolling(20).std()
94
- x["volume_z20"] = (v-v.rolling(20).mean())/v.rolling(20).std().replace(0, np.nan)
95
- x["volume_change"] = v.pct_change()
96
- macd = c.ewm(span=12, adjust=False).mean()-c.ewm(span=26, adjust=False).mean()
97
- x["macd_pct"] = macd/c
98
- # v5: fiyat kanalı konumu ve Bollinger z-skoru
99
- x["dist_hi_20"] = c/df.High.rolling(20).max()-1
100
- x["dist_lo_20"] = c/df.Low.rolling(20).min()-1
101
- x["bb_z20"] = (c-c.rolling(20).mean())/c.rolling(20).std().replace(0, np.nan)
102
- if vp is not None: # Volume Profile özellikleri (ileriye bakmaz)
103
- x["vp_poc_gap"] = c/vp["poc"]-1
104
- x["vp_va_pos"] = (c-vp["val"])/(vp["vah"]-vp["val"]).replace(0, np.nan)
105
- x["vp_va_width"] = (vp["vah"]-vp["val"])/c
106
- return x.replace([np.inf, -np.inf], np.nan), atr
107
-
108
-
109
- def supertrend(df, period=10, mult=3.0):
110
- """TradingView ta.supertrend portu. dir: +1 yükseliş, -1 düşüş."""
111
- atr = calc_atr(df, period).to_numpy(float)
112
- h, l, c = df.High.to_numpy(float), df.Low.to_numpy(float), df.Close.to_numpy(float)
113
- hl2 = (h+l)/2
114
- n = len(c)
115
- up, dn = hl2-mult*atr, hl2+mult*atr
116
- trend = np.ones(n, dtype=int)
117
- for i in range(1, n):
118
- if c[i-1] > up[i-1]:
119
- up[i] = max(up[i], up[i-1])
120
- if c[i-1] < dn[i-1]:
121
- dn[i] = min(dn[i], dn[i-1])
122
- trend[i] = trend[i-1]
123
- if trend[i-1] == -1 and c[i] > dn[i-1]:
124
- trend[i] = 1
125
- elif trend[i-1] == 1 and c[i] < up[i-1]:
126
- trend[i] = -1
127
- return trend, np.where(trend == 1, up, dn)
128
-
129
-
130
- def ut_bot(df, key=1.0, period=10):
131
- """UT Bot Alerts (QuantNomad) portu. pos: +1 al, -1 sat, 0 henüz sinyal yok."""
132
- c = df.Close.to_numpy(float)
133
- nloss = key*calc_atr(df, period).to_numpy(float)
134
- n = len(c)
135
- stop, pos = np.zeros(n), np.zeros(n, dtype=int)
136
- stop[0] = c[0]-nloss[0]
137
- for i in range(1, n):
138
- prev = stop[i-1]
139
- if c[i] > prev and c[i-1] > prev:
140
- stop[i] = max(prev, c[i]-nloss[i])
141
- elif c[i] < prev and c[i-1] < prev:
142
- stop[i] = min(prev, c[i]+nloss[i])
143
- elif c[i] > prev:
144
- stop[i] = c[i]-nloss[i]
145
- else:
146
- stop[i] = c[i]+nloss[i]
147
- if c[i-1] < prev and c[i] > prev:
148
- pos[i] = 1
149
- elif c[i-1] > prev and c[i] < prev:
150
- pos[i] = -1
151
- else:
152
- pos[i] = pos[i-1]
153
- return pos, stop
154
-
155
-
156
-
157
-
158
- # ------------------------------------------- Smart Money Concepts (LuxAlgo) Python portu
159
- def mtf_prev_levels(df):
160
- """Önceki gün / hafta / ay high-low (tamamlanmış dönem; ileriye bakma yok), mum bazında dizi."""
161
- out = {}
162
- for key, rule in (("d", "D"), ("w", "W"), ("m", "M")):
163
- per = df.index.to_period(rule)
164
- hi = df.High.groupby(per).max().shift(1)
165
- lo = df.Low.groupby(per).min().shift(1)
166
- out["p"+key+"h"] = hi.reindex(per).to_numpy(float)
167
- out["p"+key+"l"] = lo.reindex(per).to_numpy(float)
168
- return out
169
-
170
-
171
- def _smc_pivots(h, l, size):
172
- """Pine leg(size) mantığı: pivotlar `size` mum sonra doğrulanır (ileriye bakma yok).
173
- Dönüş: {doğrulama_mumu: [("H"|"L", seviye, pivot_mumu)]}"""
174
- n = len(h)
175
- hs, ls = pd.Series(h), pd.Series(l)
176
- hmax = hs.rolling(size).max().to_numpy(float)
177
- lmin = ls.rolling(size).min().to_numpy(float)
178
- out, leg = {}, 0
179
- for i in range(size, n):
180
- new_leg = leg
181
- if h[i-size] > hmax[i]:
182
- new_leg = 0
183
- elif l[i-size] < lmin[i]:
184
- new_leg = 1
185
- if new_leg != leg:
186
- out.setdefault(i, []).append(("L", float(l[i-size]), i-size) if new_leg == 1
187
- else ("H", float(h[i-size]), i-size))
188
- leg = new_leg
189
- return out
190
-
191
-
192
- def smc_analyze(df, swing_len=50, int_len=5, eq_len=3, eq_thr=0.1, ob_keep=5,
193
- confluence=False, ob_filter="Atr"):
194
- """LuxAlgo 'Smart Money Concepts' göstergesinin Python portu: swing/internal yapı (BOS, CHoCH),
195
- order block, EQH/EQL, Fair Value Gap, premium/discount ve strong/weak high-low.
196
- Pine kaynağından birebir değil; her mum yalnızca kendisine kadarki veriyi kullanır."""
197
- n = len(df)
198
- o, h, l, c = (df[k].to_numpy(float) for k in ("Open", "High", "Low", "Close"))
199
- atr = calc_atr(df, 200).to_numpy(float)
200
- pc_ = np.r_[c[0], c[:-1]]
201
- tr_ = np.maximum(h-l, np.maximum(np.abs(h-pc_), np.abs(l-pc_)))
202
- vm = (np.cumsum(tr_)/np.arange(1, n+1)) if ob_filter == "Cumulative Mean Range" else atr
203
- hv = (h-l) >= 2*vm # yüksek volatilite mumları: OB için high/low yer değiştirir
204
- bull_bar = (h-np.maximum(c, o)) > np.minimum(c, o-l) if confluence else np.ones(n, bool)
205
- bear_bar = (h-np.minimum(c, o)) > np.maximum(c, o-l) if confluence else np.ones(n, bool)
206
- ph, pl = np.where(hv, l, h), np.where(hv, h, l)
207
- swing_len = int(min(max(swing_len, 3), max(n//4, 3)))
208
- int_len = int(min(max(int_len, 2), swing_len))
209
- P_s, P_i, P_e = (_smc_pivots(h, l, k) for k in (swing_len, int_len, int(eq_len)))
210
-
211
- new = lambda: {"lvl": np.nan, "crossed": True, "idx": -1}
212
- sh, sl_, ih, il = new(), new(), new(), new()
213
- s_tr = np.zeros(n, dtype=int)
214
- i_tr = np.zeros(n, dtype=int)
215
- top_a, bot_a = np.full(n, np.nan), np.full(n, np.nan)
216
- swh_a, swl_a = np.full(n, np.nan), np.full(n, np.nan)
217
- st = it = 0
218
- top, bot = h[0], l[0]
219
- events, obs, points, eqs, fvgs = [], [], [], [], []
220
- act = []
221
- ob_sup_a, ob_res_a = np.full(n, np.nan), np.full(n, np.nan)
222
- def _new_ob(d):
223
- obs.append(d)
224
- act.append(d)
225
- if len(act) > 60:
226
- act.pop(0)
227
- prev_hi = prev_lo = np.nan
228
- eq_h = eq_l = None
229
- dsum = 0.0
230
- for i in range(n):
231
- # --- swing / internal pivotlar
232
- for kind, lvl, bi in P_s.get(i, []):
233
- if kind == "H":
234
- points.append((bi, lvl, "HH" if (np.isfinite(prev_hi) and lvl > prev_hi) else "LH", "H"))
235
- prev_hi = lvl
236
- sh.update(lvl=lvl, crossed=False, idx=bi)
237
- top = lvl
238
- else:
239
- points.append((bi, lvl, "LL" if (np.isfinite(prev_lo) and lvl < prev_lo) else "HL", "L"))
240
- prev_lo = lvl
241
- sl_.update(lvl=lvl, crossed=False, idx=bi)
242
- bot = lvl
243
- for kind, lvl, bi in P_i.get(i, []):
244
- (ih if kind == "H" else il).update(lvl=lvl, crossed=False, idx=bi)
245
- # --- EQH / EQL
246
- for kind, lvl, bi in P_e.get(i, []):
247
- thr = eq_thr*atr[i] if np.isfinite(atr[i]) else 0.0
248
- if kind == "H":
249
- if eq_h is not None and abs(eq_h[0]-lvl) < thr:
250
- eqs.append(("EQH", eq_h[1], eq_h[0], bi, lvl, i))
251
- eq_h = (lvl, bi)
252
- else:
253
- if eq_l is not None and abs(eq_l[0]-lvl) < thr:
254
- eqs.append(("EQL", eq_l[1], eq_l[0], bi, lvl, i))
255
- eq_l = (lvl, bi)
256
- # --- order block mitigasyonu (High/Low)
257
- for ob in act:
258
- if ob["on"] and ((ob["bull"] and l[i] < ob["lo"]) or (not ob["bull"] and h[i] > ob["hi"])):
259
- ob["on"] = False
260
- act[:] = [o_ for o_ in act if o_["on"]]
261
- # --- yapı kırılımları
262
- if i > 0:
263
- for internal, hp, lp in ((True, ih, il), (False, sh, sl_)):
264
- tr = it if internal else st
265
- extra_u = (not internal) or (ih["lvl"] != sh["lvl"] and bool(bull_bar[i]))
266
- extra_d = (not internal) or (il["lvl"] != sl_["lvl"] and bool(bear_bar[i]))
267
- if (np.isfinite(hp["lvl"]) and not hp["crossed"] and c[i] > hp["lvl"] >= c[i-1]
268
- and extra_u):
269
- events.append({"i": i, "kind": "CHoCH" if tr == -1 else "BOS", "bull": True,
270
- "internal": internal, "lvl": hp["lvl"], "from": hp["idx"]})
271
- hp["crossed"] = True
272
- seg = pl[hp["idx"]:i]
273
- if len(seg):
274
- j = hp["idx"] + int(np.argmin(seg))
275
- _new_ob({"bull": True, "internal": internal, "hi": float(ph[j]),
276
- "lo": float(pl[j]), "idx": j, "on": True})
277
- if internal:
278
- it = 1
279
- else:
280
- st = 1
281
- if (np.isfinite(lp["lvl"]) and not lp["crossed"] and c[i] < lp["lvl"] <= c[i-1]
282
- and extra_d):
283
- events.append({"i": i, "kind": "CHoCH" if tr == 1 else "BOS", "bull": False,
284
- "internal": internal, "lvl": lp["lvl"], "from": lp["idx"]})
285
- lp["crossed"] = True
286
- seg = ph[lp["idx"]:i]
287
- if len(seg):
288
- j = lp["idx"] + int(np.argmax(seg))
289
- _new_ob({"bull": False, "internal": internal, "hi": float(ph[j]),
290
- "lo": float(pl[j]), "idx": j, "on": True})
291
- if internal:
292
- it = -1
293
- else:
294
- st = -1
295
- s_tr[i], i_tr[i] = st, it
296
- top, bot = max(top, h[i]), min(bot, l[i])
297
- top_a[i], bot_a[i] = top, bot
298
- swh_a[i], swl_a[i] = sh["lvl"], sl_["lvl"]
299
- ob_sup_a[i] = max((o_["lo"] for o_ in act if o_["bull"] and o_["lo"] < c[i]), default=np.nan)
300
- ob_res_a[i] = min((o_["hi"] for o_ in act if (not o_["bull"]) and o_["hi"] > c[i]), default=np.nan)
301
- # --- Fair Value Gap
302
- if i >= 2:
303
- delta = (c[i-1]-o[i-1])/(o[i-1]*100) if o[i-1] else 0.0
304
- dsum += abs(delta)
305
- thr = dsum/max(i, 1)*2
306
- for g in fvgs:
307
- if g["on"] and ((g["bull"] and l[i] < g["bot"]) or (not g["bull"] and h[i] > g["top"])):
308
- g["on"] = False
309
- if l[i] > h[i-2] and c[i-1] > h[i-2] and delta > thr:
310
- fvgs.append({"bull": True, "top": float(l[i]), "bot": float(h[i-2]), "idx": i-1, "on": True})
311
- elif h[i] < l[i-2] and c[i-1] < l[i-2] and -delta > thr:
312
- fvgs.append({"bull": False, "top": float(l[i-2]), "bot": float(h[i]), "idx": i-1, "on": True})
313
- live = lambda internal, bull: [o_ for o_ in obs if o_["on"] and o_["internal"] == internal
314
- and o_["bull"] == bull][-int(ob_keep):]
315
- return {"swing_trend": s_tr, "internal_trend": i_tr, "top": top_a, "bot": bot_a, "sw_hi": swh_a, "sw_lo": swl_a, "ob_sup": ob_sup_a, "ob_res": ob_res_a, **mtf_prev_levels(df),
316
- "events": events, "points": points, "eq": eqs,
317
- "obs": live(True, True)+live(True, False)+live(False, True)+live(False, False),
318
- "fvg": [g for g in fvgs if g["on"]][-8:], "strong_high": st == -1, "strong_low": st == 1}
319
-
320
-
321
- def add_smc_traces(fig, S, index, last_n=150, kw=None):
322
- """SMC katmanlarını son `last_n` mumluk grafiğe ekler."""
323
- kw = kw or {}
324
- n = len(index)
325
- w0 = max(0, n-int(last_n))
326
- xi = lambda k: index[min(max(int(k), w0), n-1)]
327
- for e in S["events"]:
328
- if e["i"] < w0:
329
- continue
330
- col = "#16a34a" if e["bull"] else "#dc2626"
331
- xa, xb = xi(e["from"]), xi(e["i"])
332
- fig.add_trace(go.Scatter(x=[xa, xb], y=[e["lvl"], e["lvl"]], mode="lines",
333
- line=dict(width=1 if e["internal"] else 2, color=col,
334
- dash="dash" if e["internal"] else "solid"),
335
- showlegend=False, hoverinfo="skip"), **kw)
336
- mid = xa + (xb-xa)/2
337
- fig.add_trace(go.Scatter(x=[mid], y=[e["lvl"]], mode="text", text=[e["kind"]],
338
- textposition="top center" if e["bull"] else "bottom center",
339
- textfont=dict(size=9 if e["internal"] else 11, color=col),
340
- showlegend=False, hoverinfo="skip"), **kw)
341
- for ob in S["obs"]:
342
- col = "34,197,94" if ob["bull"] else "239,68,68"
343
- fig.add_shape(type="rect", x0=xi(ob["idx"]), x1=index[-1], y0=ob["lo"], y1=ob["hi"],
344
- line=dict(width=1 if ob["internal"] else 0, color=f"rgb({col})"),
345
- fillcolor=f"rgba({col},{0.12 if ob['internal'] else 0.22})", layer="below", **kw)
346
- for g in S["fvg"]:
347
- if g["idx"] < w0:
348
- continue
349
- col = "34,197,94" if g["bull"] else "239,68,68"
350
- fig.add_shape(type="rect", x0=xi(g["idx"]), x1=index[-1], y0=g["bot"], y1=g["top"],
351
- line=dict(width=0), fillcolor=f"rgba({col},0.10)", layer="below", **kw)
352
- for kind, xa_i, la, xb_i, lb, _ in S["eq"]:
353
- if xb_i < w0:
354
- continue
355
- fig.add_trace(go.Scatter(x=[xi(xa_i), xi(xb_i)], y=[la, lb], mode="lines+text",
356
- text=["", kind], textposition="top center" if kind == "EQH" else "bottom center",
357
- line=dict(width=1, dash="dot", color="#64748b"),
358
- textfont=dict(size=10, color="#64748b"), showlegend=False,
359
- hoverinfo="skip"), **kw)
360
- for bi, lvl, lab, kd in S["points"]:
361
- if bi >= w0:
362
- fig.add_trace(go.Scatter(x=[index[bi]], y=[lvl], mode="text", text=[lab],
363
- textposition="top center" if kd == "H" else "bottom center",
364
- textfont=dict(size=9, color="#475569"), showlegend=False,
365
- hoverinfo="skip"), **kw)
366
- if S.get("show_mtf"):
367
- for k_, nm_ in (("pdh", "PDH"), ("pdl", "PDL"), ("pwh", "PWH"), ("pwl", "PWL"),
368
- ("pmh", "PMH"), ("pml", "PML")):
369
- y_ = S[k_][-1]
370
- if np.isfinite(y_):
371
- fig.add_trace(go.Scatter(x=[index[w0], index[-1]], y=[y_, y_], mode="lines+text",
372
- text=["", nm_], textposition="top left",
373
- line=dict(width=1, dash="dashdot", color="#0ea5e9"),
374
- textfont=dict(size=9, color="#0ea5e9"), showlegend=False,
375
- hoverinfo="skip"), **kw)
376
- top, bot = float(S["top"][-1]), float(S["bot"][-1])
377
- if np.isfinite(top) and np.isfinite(bot) and top > bot:
378
- x0 = index[max(0, n-35)]
379
- rng_ = top-bot
380
- for y0, y1, col, nm in [(top, top-0.05*rng_, "239,68,68", "Premium"),
381
- (bot+0.525*rng_, bot+0.475*rng_, "148,163,184", "Equilibrium"),
382
- (bot+0.05*rng_, bot, "34,197,94", "Discount")]:
383
- fig.add_shape(type="rect", x0=x0, x1=index[-1], y0=y0, y1=y1, line=dict(width=0),
384
- fillcolor=f"rgba({col},0.16)", layer="below", **kw)
385
- for y, nm in [(top, "Strong High" if S["strong_high"] else "Weak High"),
386
- (bot, "Strong Low" if S["strong_low"] else "Weak Low")]:
387
- fig.add_trace(go.Scatter(x=[x0, index[-1]], y=[y, y], mode="lines+text", text=["", nm],
388
- textposition="top left", line=dict(width=1, color="#64748b"),
389
- textfont=dict(size=9, color="#64748b"), showlegend=False,
390
- hoverinfo="skip"), **kw)
391
-
392
- # ------------------------------------------- Gann Box (Zeiierman) Python portu
393
- def gann_box(df, lookback=150, reverse=False, levels=GANN_LEVELS):
394
- """Görünen aralıktaki (son `lookback` mum) en yüksek / en düşük noktadan Gann Box kurar.
395
- Zeiierman'ın TradingView göstergesinin açıklamasına göre yazıldı (Pine kaynağı birebir değil).
396
- up: düşük, yüksekten ÖNCE geldiyse True (yükseliş kutusu). reverse: yönü çevirir."""
397
- w = df.tail(int(lookback))
398
- if len(w) < 10:
399
- return None
400
- hi_i, lo_i = int(np.argmax(w.High.to_numpy(float))), int(np.argmin(w.Low.to_numpy(float)))
401
- hi, lo = float(w.High.iloc[hi_i]), float(w.Low.iloc[lo_i])
402
- if hi <= lo:
403
- return None
404
- up = lo_i < hi_i
405
- if reverse:
406
- up = not up
407
- # kutu: pencerenin başı -> sonu; seviyeler lo + p*(hi-lo)
408
- lv = {float(p): lo + float(p)*(hi-lo) for p in levels}
409
- return {"x0": w.index[0], "x1": w.index[-1], "n": len(w), "hi": hi, "lo": lo,
410
- "up": up, "levels": lv, "hi_date": w.index[hi_i], "lo_date": w.index[lo_i]}
411
-
412
-
413
- def gann_signal(df, lookback=150):
414
- """Her mum için yalnızca geçmişe bakan Gann Box %50 konumu: kapanış orta çizgi üstünde +1,
415
- altında -1 (kutu o muma kadarki son `lookback` mumdan kurulur)."""
416
- lb = int(lookback)
417
- hi = df.High.rolling(lb, min_periods=lb).max()
418
- lo = df.Low.rolling(lb, min_periods=lb).min()
419
- mid = (hi+lo)/2
420
- c = df.Close
421
- out = np.where(c > mid, 1, np.where(c < mid, -1, 0))
422
- out = np.where(mid.isna(), 0, out)
423
- return out.astype(int), ((c-lo)/(hi-lo).replace(0, np.nan)).to_numpy(float)
424
-
425
-
426
- def add_gann_traces(fig, gb, df_index, extend=True, fan=True, kw=None):
427
- """Kutu, yüzde seviyeleri ve (isteğe bağlı) Gann Fan çizgilerini Plotly grafiğine ekler."""
428
- kw = kw or {}
429
- x0, x1, n = gb["x0"], gb["x1"], gb["n"]
430
- step = (df_index[-1]-df_index[-2]) if len(df_index) > 1 else pd.Timedelta(days=1)
431
- x_end = x1 + step*int(n*0.5) if extend else x1
432
- hi, lo = gb["hi"], gb["lo"]
433
- fig.add_shape(type="rect", x0=x0, x1=x1, y0=lo, y1=hi, line=dict(width=1, color="#64748b"),
434
- fillcolor="rgba(100,116,139,0.05)", layer="below", **kw)
435
- pal = {0.0: "#16a34a", 0.236: "#84cc16", 0.382: "#eab308", 0.5: "#f59e0b",
436
- 0.618: "#f97316", 0.786: "#ef4444", 1.0: "#dc2626"}
437
- for p, y in gb["levels"].items():
438
- fig.add_trace(go.Scatter(x=[x0, x_end], y=[y, y], mode="lines+text",
439
- text=["", f"Gann {p:.3f} · {fmt_p(y)}"], textposition="middle right",
440
- line=dict(width=1, dash="dot", color=pal.get(p, "#94a3b8")),
441
- name=f"Gann {p:.3f}", showlegend=False, hoverinfo="name+y"), **kw)
442
- if fan:
443
- # köşe: yükseliş kutusunda düşük (sol-alt), düşüşte yüksek (sol-üst); 1x1 = karşı köşeye köşegen
444
- y_start = lo if gb["up"] else hi
445
- sign = 1 if gb["up"] else -1
446
- base = (hi-lo)/max(gb["n"]-1, 1) # 1x1 eğimi (fiyat/mum)
447
- steps = (x_end - x0)/step
448
- for mult, nm in GANN_FAN:
449
- y_end = y_start + sign*base*mult*steps
450
- ya, yb = (y_start, y_end)
451
- # kutu dışına taşan kısmı kes
452
- lo_c, hi_c = lo - (hi-lo)*0.5, hi + (hi-lo)*0.5
453
- if ya == yb:
454
- continue
455
- t_hit = min(1.0, max(0.0, ((hi_c if yb > ya else lo_c)-ya)/(yb-ya)))
456
- xe = x0 + (x_end-x0)*t_hit
457
- ye = ya + (yb-ya)*t_hit
458
- fig.add_trace(go.Scatter(x=[x0, xe], y=[ya, ye], mode="lines",
459
- line=dict(width=1.6 if mult == 1 else 0.8,
460
- color="#2563eb" if mult == 1 else "#93c5fd"),
461
- name=f"Gann Fan {nm}", showlegend=(mult == 1),
462
- hoverinfo="name"), **kw)
463
-
464
-
465
- def _profile(h, l, v, bins):
466
- """Bar hacmini high-low aralığına, kutularla örtüştüğü oranda dağıtır (VPVR mantığı)."""
467
- lo, hi = float(np.min(l)), float(np.max(h))
468
- if not (np.isfinite(lo) and np.isfinite(hi)) or hi <= lo:
469
- return None
470
- v = np.where(np.isfinite(v) & (v > 0), v, 0.0)
471
- if v.sum() <= 0: # hacim verisi yok (ör. forex) -> zaman bazlı profil
472
- v = np.ones_like(v)
473
- edges = np.linspace(lo, hi, bins+1)
474
- step = (hi-lo)/bins
475
- rng = np.maximum(h-l, 1e-12)
476
- ov = np.clip(np.minimum(h[:, None], edges[None, 1:]) - np.maximum(l[:, None], edges[None, :-1]), 0, None)
477
- frac = ov/rng[:, None]
478
- zr = (h-l) <= 1e-12
479
- if zr.any():
480
- frac[zr] = 0.0
481
- frac[zr, np.clip(((h[zr]-lo)/step).astype(int), 0, bins-1)] = 1.0
482
- return edges, (frac*v[:, None]).sum(axis=0)
483
-
484
-
485
- def _value_area(edges, prof, va_pct):
486
- pi = int(np.argmax(prof))
487
- lo_i = hi_i = pi
488
- acc, target = float(prof[pi]), va_pct*float(prof.sum())
489
- nb = len(prof)
490
- while acc < target and (lo_i > 0 or hi_i < nb-1):
491
- up = prof[hi_i+1] if hi_i < nb-1 else -1.0
492
- dn = prof[lo_i-1] if lo_i > 0 else -1.0
493
- if up >= dn:
494
- hi_i += 1
495
- acc += up
496
- else:
497
- lo_i -= 1
498
- acc += dn
499
- return float((edges[pi]+edges[pi+1])/2), float(edges[lo_i]), float(edges[hi_i+1])
500
-
501
-
502
- def volume_profile(df, window=60, bins=24, va_pct=0.70):
503
- """Kayan Volume Profile: her mum için son `window` mumdan POC / VAL / VAH.
504
- Yalnızca o muma kadar olan veri kullanılır (ileriye bakma yok)."""
505
- h, l, v = (df[c].to_numpy(float) for c in ("High", "Low", "Volume"))
506
- n = len(h)
507
- out = np.full((n, 3), np.nan)
508
- for i in range(window-1, n):
509
- sl = slice(i-window+1, i+1)
510
- p = _profile(h[sl], l[sl], v[sl], bins)
511
- if p is None:
512
- continue
513
- out[i] = _value_area(p[0], p[1], va_pct)
514
- return pd.DataFrame(out, index=df.index, columns=["poc", "val", "vah"])
515
-
516
-
517
- def latest_profile(df, window, bins, va_pct):
518
- w = df.tail(int(window))
519
- p = _profile(w.High.to_numpy(float), w.Low.to_numpy(float), w.Volume.to_numpy(float), int(bins))
520
- if p is None:
521
- return None
522
- poc, val, vah = _value_area(p[0], p[1], va_pct)
523
- return p[0], p[1], poc, val, vah
524
-
525
-
526
- def vp_signal(df, atr, vp, ext_atr):
527
- """+1: long onaylı (kapanış POC üstünde ve VAH'ın ext_atr*ATR'den fazla üstünde değil),
528
- -1: short onaylı (POC altında ve VAL'ın ext_atr*ATR'den fazla altında değil), 0: onay yok."""
529
- c, a = df.Close.to_numpy(float), atr.to_numpy(float)
530
- poc, val, vah = (vp[k].to_numpy(float) for k in ("poc", "val", "vah"))
531
- with np.errstate(invalid="ignore"):
532
- ok_l = (c > poc) & ((c-vah) <= ext_atr*a)
533
- ok_s = (c < poc) & ((val-c) <= ext_atr*a)
534
- return np.where(ok_l, 1, np.where(ok_s, -1, 0)).astype(int)
535
-
536
-
537
- # ------------------------------------------- Lorentzian Classification (jdehorty) portu
538
- def adx_wilder(df, n=14):
539
- h, l, c = df.High, df.Low, df.Close
540
- up, dn = h.diff(), -l.diff()
541
- plus = pd.Series(np.where((up > dn) & (up > 0), up, 0.0), index=df.index)
542
- minus = pd.Series(np.where((dn > up) & (dn > 0), dn, 0.0), index=df.index)
543
- tr = pd.concat([(h-l), (h-c.shift(1)).abs(), (l-c.shift(1)).abs()], axis=1).max(axis=1)
544
- a = 1/n
545
- atr = tr.ewm(alpha=a, adjust=False).mean()
546
- pdi = 100*plus.ewm(alpha=a, adjust=False).mean()/atr
547
- mdi = 100*minus.ewm(alpha=a, adjust=False).mean()/atr
548
- dx = 100*(pdi-mdi).abs()/(pdi+mdi).replace(0, np.nan)
549
- return dx.ewm(alpha=a, adjust=False).mean().to_numpy(float)
550
-
551
-
552
- def _norm01(x):
553
- """Geçmiş min/max'a göre 0-1 ölçekleme (TV 'normalize'); yalnızca geçmişe bakar."""
554
- s = pd.Series(x)
555
- lo, hi = s.cummin(), s.cummax()
556
- return ((s-lo)/(hi-lo).replace(0, np.nan)).to_numpy(float)
557
-
558
-
559
- def _ldc_features(df):
560
- src = (df.High+df.Low+df.Close)/3
561
- ema = lambda s, n: s.ewm(span=max(int(n), 1), adjust=False).mean()
562
- rsi = lambda n: ema(calc_rsi(src, n)/100, 1).to_numpy(float)
563
- e1 = ema(src, 10)
564
- e2 = ema((src-e1).abs(), 10)
565
- wt1 = ema((src-e1)/(0.015*e2).replace(0, np.nan), 11)
566
- wt = _norm01((wt1-wt1.rolling(4).mean()).to_numpy(float))
567
- ma = src.rolling(20).mean()
568
- md = src.rolling(20).apply(lambda a: np.abs(a-a.mean()).mean(), raw=True)
569
- cci = _norm01(((src-ma)/(0.015*md.replace(0, np.nan))).to_numpy(float))
570
- adx = adx_wilder(df, 20)/100
571
- # varsayılan öznitelikler: RSI(14,1), WT(10,11), CCI(20,1), ADX(20,2), RSI(9,1)
572
- return np.column_stack([rsi(14), wt, cci, adx, rsi(9)]), src.to_numpy(float)
573
-
574
-
575
- def ldc_classify(df, k=8, max_back=2000, regime_thr=-0.1, kern_lb=8, kern_rw=8.0, x0=25,
576
- use_vol=True, use_regime=True, use_adx=False, adx_thr=20, use_kernel=True, exact=True):
577
- """Machine Learning: Lorentzian Classification (jdehorty) Python portu.
578
- Lorentzian mesafe = sum(log(1+|x-y|)); exact=True: gerçek k-NN, False: Pine'ın yaklaşık-NN döngüsü;
579
- etiket = 4 bar sonraki yön. Filtreler: volatilite, rejim (Kalman eğimi), ADX; kernel:
580
- Nadaraya-Watson rational quadratic. Her mum yalnızca kendisine kadarki veriyi kullanır.
581
- state: +1 / -1 (ML sinyali + kernel eğimi aynı yönde), 0 onay yok."""
582
- n = len(df)
583
- F, s = _ldc_features(df)
584
- y = np.full(n, np.nan)
585
- y[:-4] = np.sign(s[4:]-s[:-4])
586
- ok_f = np.isfinite(F).all(axis=1)
587
- cand = ok_f & np.isfinite(y) & (np.arange(n) % 4 != 0)
588
- kk = int(k)
589
- cut = int(np.floor(kk*0.75+0.5))
590
- pred = np.zeros(n)
591
- for t in range(60, n):
592
- if not ok_f[t]:
593
- continue
594
- hi_j = t-4 # etiketi t'de bilinen son örnek
595
- lo_j = max(0, hi_j-int(max_back)+1)
596
- sel = np.flatnonzero(cand[lo_j:hi_j+1]) + lo_j
597
- if len(sel) < kk:
598
- continue
599
- D = np.log1p(np.abs(F[sel]-F[t])).sum(axis=1)
600
- if exact: # gerçek k en yakın komşu (Lorentzian mesafe)
601
- nn_ = np.argpartition(D, kk-1)[:kk]
602
- pred[t] = y[sel][nn_].sum()
603
- continue
604
- last, dist, pr = -1.0, [], [] # Pine'ın kronolojik yaklaşık-NN döngüsü
605
- for d, yv in zip(D.tolist(), y[sel].tolist()):
606
- if d >= last:
607
- dist.append(d)
608
- pr.append(yv)
609
- if len(dist) > kk:
610
- last = dist[cut]
611
- dist.pop(0)
612
- pr.pop(0)
613
- pred[t] = sum(pr)
614
-
615
- # --- filtreler
616
- f_vol = (calc_atr(df, 1).to_numpy(float) > calc_atr(df, 10).to_numpy(float)) if use_vol \
617
- else np.ones(n, bool)
618
- f_reg = np.ones(n, bool)
619
- if use_regime:
620
- o4 = ((df.Open+df.High+df.Low+df.Close)/4).to_numpy(float)
621
- hl = (df.High-df.Low).to_numpy(float)
622
- v1 = v2 = kl = 0.0
623
- klmf = np.zeros(n)
624
- for i in range(n):
625
- v1 = 0.2*(o4[i]-(o4[i-1] if i else o4[i]))+0.8*v1
626
- v2 = 0.1*hl[i]+0.8*v2
627
- om = abs(v1/v2) if v2 else 0.0
628
- al = (-om**2+np.sqrt(om**4+16*om**2))/8
629
- kl = al*o4[i]+(1-al)*kl
630
- klmf[i] = kl
631
- slope = pd.Series(np.abs(np.diff(klmf, prepend=klmf[0])))
632
- ema_s = slope.ewm(span=200, adjust=False).mean()
633
- f_reg = (((slope-ema_s)/ema_s.replace(0, np.nan)) >= float(regime_thr)).fillna(False).to_numpy(bool)
634
- f_adx = (adx_wilder(df, 14) > float(adx_thr)) if use_adx else np.ones(n, bool)
635
- filt = f_vol & f_reg & f_adx
636
-
637
- sig = np.zeros(n, dtype=int)
638
- for t in range(1, n):
639
- sig[t] = 1 if (pred[t] > 0 and filt[t]) else -1 if (pred[t] < 0 and filt[t]) else sig[t-1]
640
-
641
- # --- kernel regresyon (rational quadratic), yalnızca geçmiş barlar
642
- L = int(kern_lb)+int(x0)
643
- i_ = np.arange(L+1)
644
- w = (1+i_**2/(float(kern_lb)**2*2*float(kern_rw)))**(-float(kern_rw))
645
- yhat = np.full(n, np.nan)
646
- for t in range(n):
647
- m = min(L+1, t+1)
648
- yhat[t] = (w[:m]*s[t-np.arange(m)]).sum()/w[:m].sum()
649
- bull = np.r_[False, yhat[1:] > yhat[:-1]]
650
- bear = np.r_[False, yhat[1:] < yhat[:-1]]
651
- if use_kernel:
652
- state = np.where((sig == 1) & bull, 1, np.where((sig == -1) & bear, -1, 0)).astype(int)
653
- else:
654
- state = sig.copy()
655
- state[:60] = 0
656
- return {"state": state, "signal": sig, "pred": pred, "yhat": yhat,
657
- "vol": f_vol, "reg": f_reg, "adx": f_adx}
658
-
659
-
660
- def regime_labels(features, ref_vol):
661
- # Volatilite kantilleri yalnızca geliştirme dönemi verisinden alınır (sızıntı yok).
662
- q1, q2 = ref_vol.dropna().quantile([0.33, 0.67])
663
- vol, trend = features["vol_20"], features["ema_gap_20"]
664
- vname = np.where(vol <= q1, "LOW_VOL", np.where(vol >= q2, "HIGH_VOL", "MID_VOL"))
665
- tname = np.where(trend > 0.02, "UP", np.where(trend < -0.02, "DOWN", "RANGE"))
666
- return pd.Series([f"{t}_{v}" for t, v in zip(tname, vname)], index=features.index)
667
-
668
-
669
- def make_target(df, atr, horizon, threshold=0.20):
670
- fwd = df.Close.shift(-horizon)/df.Close-1
671
- band = threshold*(atr/df.Close)*np.sqrt(horizon)
672
- y = pd.Series(np.where(fwd > band, 1, np.where(fwd < -band, -1, 0)), index=df.index)
673
- return y.where(fwd.notna() & band.notna()), fwd
674
-
675
-
676
- # ---------------------------------------------------------------- model
677
- def pipe(k):
678
- return Pipeline([
679
- ("imputer", SimpleImputer(strategy="median")),
680
- ("scale", StandardScaler()),
681
- ("knn", KNeighborsClassifier(n_neighbors=int(k), weights="distance"))
682
- ])
683
-
684
-
685
- def tune_k(X, y, h):
686
- # İç kronolojik doğrulama; örtüşen etiketleri önlemek için ufuk kadar purge.
687
- cut = int(len(X)*0.80)
688
- tr_end = cut-h
689
- if tr_end <= max(K_GRID)+20 or len(X)-cut < 25:
690
- return 9, []
691
- scores = []
692
- for k in K_GRID:
693
- model = pipe(k).fit(X.iloc[:tr_end], y.iloc[:tr_end])
694
- pred = model.predict(X.iloc[cut:])
695
- scores.append((float(balanced_accuracy_score(y.iloc[cut:], pred)), int(k)))
696
- scores.sort(reverse=True)
697
- return scores[0][1], scores
698
-
699
-
700
- def calibrate_current(raw_cal, y_cal, raw_cur, classes):
701
- # Bire-karşı-hepsi isotonic kalibrasyon; küçük kalibrasyon setinde ham olasılık.
702
- classes = list(classes)
703
- raw_vec = np.zeros(len(CLASSES))
704
- out = np.zeros(len(CLASSES))
705
- for j, cls in enumerate(CLASSES):
706
- if cls not in classes:
707
- continue
708
- ci = classes.index(cls)
709
- val = raw_cur[0, ci]
710
- raw_vec[j] = val
711
- truth = (np.asarray(y_cal) == cls).astype(int)
712
- if len(truth) >= 35 and len(np.unique(truth)) == 2:
713
- iso = IsotonicRegression(out_of_bounds="clip").fit(raw_cal[:, ci], truth)
714
- out[j] = iso.predict([val])[0]
715
- else:
716
- out[j] = val
717
- if out.sum() > 0:
718
- return out/out.sum()
719
- if raw_vec.sum() > 0:
720
- return raw_vec/raw_vec.sum()
721
- return np.full(len(CLASSES), 1/3)
722
-
723
-
724
- # ---------------------------------------------------------------- bariyer / R
725
- def stop_dist(A, i, side, sl_atr):
726
- return A["ind_sd"][side][i] if "ind_sd" in A else sl_atr*A["a"][i]
727
-
728
-
729
- def barrier_trade(A, i, side, sl_atr, rr, h):
730
- """(getiri, çıkış_pozisyonu, neden) döndürür. Aynı mumda SL+TP varsa SL kabul edilir."""
731
- entry, av = A["c"][i], A["a"][i]
732
- if not np.isfinite(av) or av <= 0 or not np.isfinite(entry):
733
- return None
734
- if "ind_sd" in A: # SL/TP indikatör seviyelerinden (ST / UT / Volume Profile)
735
- sd = A["ind_sd"][side][i]
736
- if not np.isfinite(sd) or sd <= 0:
737
- return None
738
- td = min(sd*rr, A["ind_cap"][side][i])
739
- else:
740
- sd = sl_atr*av
741
- td = sd*rr
742
- end = min(i+h, len(A["c"])-1)
743
- for j in range(i+1, end+1):
744
- hi, lo = A["h"][j], A["l"][j]
745
- if side == 1:
746
- sl_hit, tp_hit = lo <= entry-sd, hi >= entry+td
747
- else:
748
- sl_hit, tp_hit = hi >= entry+sd, lo <= entry-td
749
- if sl_hit:
750
- return -sd/entry, j, "SL"
751
- if tp_hit:
752
- return td/entry, j, "TP"
753
- return side*(A["c"][end]-entry)/entry, end, "TIME"
754
-
755
-
756
- def optimize_barriers(A, pos, regimes_arr, side, cost, current_regime, h):
757
- pos = list(pos)[-650:]
758
- sel = [i for i in pos if regimes_arr[i] == current_regime]
759
- if len(sel) < 35:
760
- sel, used = pos, "GLOBAL_FALLBACK"
761
- else:
762
- used = str(current_regime)
763
- results = []
764
- for sl in ([1.0] if "ind_sd" in A else SL_GRID):
765
- for rr in RR_GRID:
766
- vals = []
767
- for i in sel:
768
- t = barrier_trade(A, i, side, sl, rr, h)
769
- if t is not None:
770
- vals.append(t[0]-cost)
771
- if len(vals) >= 35:
772
- a = np.asarray(vals)
773
- downside = np.sqrt(np.mean(np.minimum(a, 0)**2))
774
- results.append((a.mean()-0.35*downside, sl, rr, len(a),
775
- a.mean(), (a > 0).mean()))
776
- if not results:
777
- return 1.5, 2.0, [], used
778
- results.sort(reverse=True)
779
- return float(results[0][1]), float(results[0][2]), results[:10], used
780
-
781
-
782
- # ---------------------------------------------------------------- risk / MC
783
- def dd_mult(dd):
784
- return np.select([dd <= -0.20, dd <= -0.15, dd <= -0.10, dd <= -0.06],
785
- [0.20, 0.35, 0.50, 0.75], 1.0)
786
-
787
-
788
- def adaptive_risk(min_risk, max_risk, bacc, agreement, expectancy_r, n_trades, drawdown):
789
- # Açık, deterministik politika (pekiştirmeli öğrenme değildir).
790
- quality = np.clip((bacc-0.33)/0.20, 0, 1)
791
- agree = np.clip((agreement-0.33)/0.50, 0, 1)
792
- edge = np.clip(expectancy_r/0.30, 0, 1)
793
- sample = np.clip(n_trades/40, 0, 1)
794
- score = 0.30*quality+0.25*agree+0.30*edge+0.15*sample
795
- risk = min_risk+(max_risk-min_risk)*score
796
- if drawdown <= -0.20:
797
- risk = min_risk
798
- elif drawdown <= -0.15:
799
- risk = min_risk+(risk-min_risk)*0.20
800
- elif drawdown <= -0.10:
801
- risk = min_risk+(risk-min_risk)*0.40
802
- elif drawdown <= -0.06:
803
- risk = min_risk+(risk-min_risk)*0.70
804
- return float(np.clip(risk, min_risk, max_risk)), float(score)
805
-
806
-
807
- def simulate_equity(r_values, base_risk_pct):
808
- eq, peak = 1.0, 1.0
809
- curve, dds = [1.0], [0.0]
810
- for r in r_values:
811
- risk = base_risk_pct/100*float(dd_mult(eq/peak-1))
812
- eq *= max(0.01, 1+risk*float(r))
813
- peak = max(peak, eq)
814
- curve.append(eq)
815
- dds.append(eq/peak-1)
816
- return np.array(curve), np.array(dds)
817
-
818
-
819
- def risk_path_mc(r_multiples, base_risk_pct, sims=2000, n_trades=100):
820
- # R biriminde bootstrap; kayıp 1R ile sınırlanmaz (maliyet nedeniyle ~-1.05R olabilir).
821
- if len(r_multiples) < 10:
822
- return None
823
- rng = np.random.default_rng(20261103)
824
- S = rng.choice(np.asarray(r_multiples, float), size=(sims, n_trades), replace=True)
825
- eq, peak, mdd = np.ones(sims), np.ones(sims), np.zeros(sims)
826
- paths = np.empty((n_trades+1, sims))
827
- paths[0] = 1.0
828
- for t in range(n_trades):
829
- risk = base_risk_pct/100*dd_mult(eq/peak-1)
830
- eq = eq*np.maximum(0.01, 1+risk*S[:, t])
831
- peak = np.maximum(peak, eq)
832
- mdd = np.minimum(mdd, eq/peak-1)
833
- paths[t+1] = eq
834
- fin = eq-1
835
- return {"median": float(np.median(fin)), "p05": float(np.percentile(fin, 5)),
836
- "p95": float(np.percentile(fin, 95)), "dd95": float(np.percentile(mdd, 5)),
837
- "ruin": float(np.mean(eq < 0.5)),
838
- "pct": np.percentile(paths, [5, 25, 50, 75, 95], axis=1)-1.0}
839
-
840
-
841
- # ---------------------------------------------------------------- yardımcılar
842
- def fmt_p(x):
843
- return f"{x:,.2f}" if abs(x) >= 100 else f"{x:,.4f}"
844
-
845
-
846
- def md_table(header, rows):
847
- if not rows:
848
- return "_Veri yok._"
849
- out = ["| " + " | ".join(header) + " |", "|" + "|".join(["---"]*len(header)) + "|"]
850
- out += ["| " + " | ".join(str(c) for c in r) + " |" for r in rows]
851
- return "\n".join(out)
852
-
853
-
854
- def trade_stats(R, curve, dds, trades_df):
855
- if len(R) == 0:
856
- return None
857
- wins, losses = R[R > 0].sum(), -R[R < 0].sum()
858
- reasons = trades_df.exit_reason.value_counts().to_dict()
859
- return {"n": len(R), "win": float((R > 0).mean()), "exp": float(R.mean()),
860
- "pf": float(wins/losses) if losses > 0 else float("inf"),
861
- "ret": float(curve[-1]-1), "mdd": float(dds.min()),
862
- "tp": reasons.get("TP", 0), "sl": reasons.get("SL", 0),
863
- "time": reasons.get("TIME", 0)}
864
-
865
-
866
- TRADE_COLS = ["entry_date", "exit_date", "side", "entry_price", "exit_reason",
867
- "net_return_pct", "R", "regime"]
868
-
869
-
870
- # ---------------------------------------------------------------- grafikler
871
- def price_figure(df, ticker, trades_df, trade_ok, stop, target, ind=None, vp=None, vp_prof=None,
872
- ldc=None, combo=None, gann=None, gann_fan=True, gann_extend=True, smc=None):
873
- w = df.tail(150)
874
- sub = vp is not None and vp_prof is not None
875
- if sub:
876
- fig = make_subplots(rows=1, cols=2, shared_yaxes=True,
877
- column_widths=[0.86, 0.14], horizontal_spacing=0.005)
878
- else:
879
- fig = go.Figure()
880
- kw = dict(row=1, col=1) if sub else {}
881
- fig.add_trace(go.Candlestick(x=w.index, open=w.Open, high=w.High, low=w.Low,
882
- close=w.Close, name=ticker), **kw)
883
- for n, col in [(20, "#f59e0b"), (50, "#3b82f6")]:
884
- ema = df.Close.ewm(span=n, adjust=False).mean().tail(150)
885
- fig.add_trace(go.Scatter(x=w.index, y=ema, mode="lines", name=f"EMA{n}",
886
- line=dict(width=1.2, color=col)), **kw)
887
- if ind is not None:
888
- st_line, st_dir, ut_stop = (np.asarray(a)[-150:] for a in ind)
889
- for d, col, nm in [(1, "#16a34a", "Supertrend ↑"), (-1, "#dc2626", "Supertrend ↓")]:
890
- fig.add_trace(go.Scatter(x=w.index, y=np.where(st_dir == d, st_line, np.nan),
891
- mode="lines", name=nm, line=dict(width=1.5, color=col)), **kw)
892
- fig.add_trace(go.Scatter(x=w.index, y=ut_stop, mode="lines", name="UT trailing stop",
893
- line=dict(width=1, dash="dot", color="#9333ea")), **kw)
894
- if sub:
895
- vw = vp.tail(150)
896
- for k, col, dash, nm in [("poc", "#f59e0b", "solid", "POC"),
897
- ("vah", "#64748b", "dash", "VAH"),
898
- ("val", "#64748b", "dash", "VAL")]:
899
- fig.add_trace(go.Scatter(x=w.index, y=vw[k], mode="lines", name=nm,
900
- line=dict(width=1.4, color=col, dash=dash)), **kw)
901
- edges, prof, poc, val, vah = vp_prof
902
- mid = (edges[:-1]+edges[1:])/2
903
- colors = ["#f59e0b" if edges[k] <= poc <= edges[k+1]
904
- else ("#60a5fa" if val <= mid[k] <= vah else "#cbd5e1")
905
- for k in range(len(mid))]
906
- fig.add_trace(go.Bar(x=prof, y=mid, orientation="h", width=(edges[1]-edges[0])*0.95,
907
- marker_color=colors, name="Volume Profile", showlegend=False,
908
- hoverinfo="skip"), row=1, col=2)
909
- fig.update_xaxes(showticklabels=False, row=1, col=2)
910
- if ldc is not None:
911
- yh = np.asarray(ldc["yhat"])[-150:]
912
- rising = np.r_[False, np.diff(yh) > 0]
913
- for flag, col, nm in [(True, "#16a34a", "Lorentzian kernel ↑"), (False, "#dc2626", "Lorentzian kernel ↓")]:
914
- fig.add_trace(go.Scatter(x=w.index, y=np.where(rising == flag, yh, np.nan), mode="lines",
915
- name=nm, line=dict(width=2, color=col)), **kw)
916
- st_ = np.asarray(ldc["state"])[-150:]
917
- prev = np.r_[0, st_[:-1]]
918
- for d, nm, col, ycol, off in [(1, "LDC long", "#15803d", "Low", 0.995),
919
- (-1, "LDC short", "#b91c1c", "High", 1.005)]:
920
- m_ = (st_ == d) & (prev != d)
921
- if m_.any():
922
- fig.add_trace(go.Scatter(x=w.index[m_], y=w[ycol].to_numpy()[m_]*off, mode="markers",
923
- name=nm, marker=dict(symbol="diamond", size=8, color=col,
924
- line=dict(width=1, color="white"))), **kw)
925
- if combo is not None:
926
- cs = np.asarray(combo)[-150:]
927
- pv = np.r_[0, cs[:-1]]
928
- for d, nm, col, ycol, off in [(1, "Birleşik LONG", "#f59e0b", "Low", 0.985),
929
- (-1, "Birleşik SHORT", "#7c3aed", "High", 1.015)]:
930
- m_ = (cs == d) & (pv != d)
931
- if m_.any():
932
- fig.add_trace(go.Scatter(x=w.index[m_], y=w[ycol].to_numpy()[m_]*off, mode="markers",
933
- name=nm, marker=dict(symbol="star", size=12, color=col,
934
- line=dict(width=1, color="white"))), **kw)
935
- if len(trades_df):
936
- t = trades_df[trades_df.entry_date.isin(w.index)]
937
- for side, sym, nm in [("LONG", "triangle-up", "OOS long"),
938
- ("SHORT", "triangle-down", "OOS short")]:
939
- s = t[t.side == side]
940
- if len(s):
941
- fig.add_trace(go.Scatter(
942
- x=s.entry_date, y=s.entry_price, mode="markers", name=nm,
943
- marker=dict(symbol=sym, size=9,
944
- color=["#16a34a" if r > 0 else "#dc2626" for r in s.R],
945
- line=dict(width=1, color="white")),
946
- text=[f"R={r:.2f} · {why}" for r, why in zip(s.R, s.exit_reason)]), **kw)
947
- if smc is not None:
948
- add_smc_traces(fig, smc, df.index, last_n=150, kw=kw)
949
- if gann is not None:
950
- add_gann_traces(fig, gann, df.index, extend=gann_extend, fan=gann_fan, kw=kw)
951
- if trade_ok:
952
- fig.add_hline(y=stop, line_dash="dash", line_color="#dc2626", annotation_text="SL", **kw)
953
- fig.add_hline(y=target, line_dash="dash", line_color="#16a34a", annotation_text="TP", **kw)
954
- fig.update_layout(title=f"{ticker} Günlük — son 150 mum (üçgenler: OOS sinyalleri)"
955
- + (" + Volume Profile" if sub else "") + (" + Gann Box" if gann is not None else "") + (" + SMC" if smc is not None else ""),
956
- height=560, xaxis_rangeslider_visible=False)
957
- return fig
958
-
959
-
960
- def prob_figure(p_down, p_flat, p_up):
961
- fig = go.Figure(go.Bar(x=["DOWN", "FLAT", "UP"], y=[p_down*100, p_flat*100, p_up*100],
962
- marker_color=["#dc2626", "#9ca3af", "#16a34a"],
963
- text=[f"{p*100:.1f}%" for p in (p_down, p_flat, p_up)],
964
- textposition="outside"))
965
- fig.update_layout(title="Kalibre edilmiş olasılıklar", height=300,
966
- yaxis=dict(range=[0, 100], title="%"), margin=dict(t=50, b=30))
967
- return fig
968
-
969
-
970
- def equity_figure(curve, dds):
971
- x = np.arange(len(curve))
972
- fig = make_subplots(rows=2, cols=1, shared_xaxes=True, row_heights=[0.7, 0.3],
973
- vertical_spacing=0.06)
974
- fig.add_trace(go.Scatter(x=x, y=(curve-1)*100, mode="lines", name="Kümülatif getiri %"), 1, 1)
975
- fig.add_trace(go.Scatter(x=x, y=dds*100, fill="tozeroy", name="Drawdown %",
976
- line=dict(color="#dc2626")), 2, 1)
977
- fig.update_xaxes(title_text="İşlem #", row=2, col=1)
978
- fig.update_layout(title="OOS backtest — adaptif risk ile bileşik özsermaye", height=520)
979
- return fig
980
-
981
-
982
- def pf_gauge(pf, n):
983
- shown = 3.0 if np.isinf(pf) else float(min(pf, 3.0))
984
- fig = go.Figure(go.Indicator(
985
- mode="gauge+number", value=shown,
986
- number=dict(valueformat=".2f", suffix=" (∞)" if np.isinf(pf) else ""),
987
- title=dict(text=f"Profit Factor — OOS ({n} işlem)"),
988
- gauge=dict(
989
- axis=dict(range=[0, 3]),
990
- bar=dict(color="#1d4ed8"),
991
- steps=[dict(range=[0, 1], color="#fecaca"),
992
- dict(range=[1, 1.5], color="#fef08a"),
993
- dict(range=[1.5, 3], color="#bbf7d0")],
994
- threshold=dict(line=dict(color="black", width=3), thickness=0.8, value=1.0))))
995
- fig.update_layout(height=320, margin=dict(t=70, b=20))
996
- return fig
997
-
998
-
999
- def mc_figure(mc):
1000
- p = mc["pct"]*100
1001
- x = np.arange(p.shape[1])
1002
- fig = go.Figure()
1003
- fig.add_trace(go.Scatter(x=x, y=p[0], line=dict(width=0), showlegend=False, hoverinfo="skip"))
1004
- fig.add_trace(go.Scatter(x=x, y=p[4], fill="tonexty", line=dict(width=0),
1005
- fillcolor="rgba(59,130,246,0.15)", name="%5–%95"))
1006
- fig.add_trace(go.Scatter(x=x, y=p[1], line=dict(width=0), showlegend=False, hoverinfo="skip"))
1007
- fig.add_trace(go.Scatter(x=x, y=p[3], fill="tonexty", line=dict(width=0),
1008
- fillcolor="rgba(59,130,246,0.30)", name="%25–%75"))
1009
- fig.add_trace(go.Scatter(x=x, y=p[2], line=dict(color="#1d4ed8", width=2), name="Medyan"))
1010
- fig.update_layout(title="Monte Carlo — 100 işlemlik getiri yelpazesi (%)",
1011
- xaxis_title="İşlem #", yaxis_title="Getiri %", height=460)
1012
- return fig
1013
-
1014
-
1015
- # ---------------------------------------------------------------- ana motor
1016
- def run_engine(ticker, period, horizon, side_mode, equity, min_risk, max_risk,
1017
- max_leverage, fee, slip, train_window,
1018
- p_min, p_gap, flat_max, agree_min, bacc_min, min_trades,
1019
- skip_overlap, whole_units, ind_mode, st_period, st_mult, ut_key, ut_period,
1020
- vp_mode, vp_window, vp_bins, vp_va, vp_ext,
1021
- ldc_mode, ldc_k, ldc_back, ldc_regime, ldc_kern_lb, ldc_kern_rw,
1022
- ldc_use_vol, ldc_use_regime, ldc_use_adx, ldc_kernel, ldc_exact,
1023
- combo_mode, combo_votes, ind_tpsl, tpsl_smin, tpsl_smax, tpsl_minrr,
1024
- gann_mode, gann_lb, gann_fan_on, gann_ext_on, gann_rev,
1025
- smc_mode, smc_src, smc_swing_len, smc_int_len, smc_zone, smc_draw,
1026
- smc_conf, smc_obf, smc_lvl):
1027
- try:
1028
- ticker = str(ticker).strip().upper()
1029
- h = int(horizon)
1030
- equity, fee, slip = float(equity), float(fee), float(slip)
1031
- min_risk, max_risk = float(min_risk), float(max_risk)
1032
- if max_risk < min_risk:
1033
- raise ValueError("Maksimum risk, minimum riskten küçük olamaz.")
1034
- cost = 2*(fee+slip)/100
1035
- allow = {1: side_mode != "Sadece Short", -1: side_mode != "Sadece Long"}
1036
-
1037
- df = get_market_data(ticker, period)
1038
- if len(df) < 450:
1039
- raise ValueError("En az 450 günlük mum gerekli.")
1040
- df = df.iloc[:-1].copy() # tamamlanmamış güncel mumu çıkar
1041
- vp_on = vp_mode != VP_MODES[0]
1042
- vp_feat = vp_mode == VP_MODES[2]
1043
- vp_win, vp_nb, vp_vap = int(vp_window), int(vp_bins), float(vp_va)
1044
- vp = volume_profile(df, vp_win, vp_nb, vp_vap)
1045
- X_all, atr_all = make_features(df, vp if vp_feat else None)
1046
- vp_arr = vp_signal(df, atr_all, vp, float(vp_ext))
1047
- vp_names = {1: "LONG onaylı", -1: "SHORT onaylı", 0: "onay yok"}
1048
- vp_no_vol = float(df.Volume.tail(vp_win).sum()) <= 0
1049
- lp = latest_profile(df, vp_win, vp_nb, vp_vap)
1050
- y_all, _ = make_target(df, atr_all, h)
1051
- gann_on = gann_mode != GANN_MODES[0]
1052
- gann_arr, gann_pos = gann_signal(df, int(gann_lb))
1053
- gann_now = int(gann_arr[-1])
1054
- GB = gann_box(df, int(gann_lb), bool(gann_rev))
1055
- smc_on = smc_mode != SMC_MODES[0]
1056
- smc_only = smc_mode == SMC_MODES[2]
1057
- SM = smc_analyze(df, int(smc_swing_len), int(smc_int_len), confluence=bool(smc_conf),
1058
- ob_filter=smc_obf) if (smc_on or smc_draw) else None
1059
- smc_lvl_on = bool(smc_lvl) and SM is not None and smc_on
1060
- if SM is not None:
1061
- SM["show_mtf"] = bool(smc_lvl)
1062
- smc_arr = np.zeros(len(df), dtype=int)
1063
- if SM is not None:
1064
- smc_arr = (SM["swing_trend"] if smc_src == SMC_SRC[0] else SM["internal_trend"]).copy()
1065
- if smc_zone: # long yalnızca discount, short yalnızca premium bölgesinde
1066
- mid_ = (SM["top"]+SM["bot"])/2
1067
- zone_ = np.where(df.Close.to_numpy(float) < mid_, 1, np.where(df.Close.to_numpy(float) > mid_, -1, 0))
1068
- smc_arr = np.where(zone_ == smc_arr, smc_arr, 0).astype(int)
1069
- smc_now = int(smc_arr[-1])
1070
- smc_entry = np.zeros(len(df), dtype=int) # yalnızca yapı kırılımı mumlarında giriş
1071
- if SM is not None:
1072
- want_internal = smc_src == SMC_SRC[1]
1073
- ev_arr = np.zeros(len(df), dtype=int)
1074
- for e_ in SM["events"]:
1075
- if e_["internal"] == want_internal:
1076
- ev_arr[e_["i"]] = 1 if e_["bull"] else -1
1077
- smc_entry = np.where(ev_arr == smc_arr, ev_arr, 0).astype(int)
1078
-
1079
- valid = X_all.notna().all(axis=1) & y_all.notna()
1080
- X = X_all.loc[valid].copy()
1081
- y = y_all.loc[X.index].astype(int)
1082
- if len(X) < 300:
1083
- raise ValueError("Temiz özelliklerden sonra yeterli veri kalmadı.")
1084
-
1085
- oos_n = min(180, max(75, int(len(X)*0.22)))
1086
- dev_end = len(X)-oos_n-h
1087
- if dev_end < 200:
1088
- raise ValueError("OOS ayrımı için yeterli geliştirme verisi yok.")
1089
- tw = int(train_window)
1090
- dev_X, dev_y = X.iloc[:dev_end], y.iloc[:dev_end]
1091
- if len(dev_X) > tw:
1092
- dev_X, dev_y = dev_X.iloc[-tw:], dev_y.iloc[-tw:]
1093
-
1094
- regimes_all = regime_labels(X_all, X_all.loc[dev_X.index, "vol_20"])
1095
- reg_arr = regimes_all.to_numpy()
1096
- A = {"h": df.High.to_numpy(float), "l": df.Low.to_numpy(float),
1097
- "c": df.Close.to_numpy(float), "a": atr_all.to_numpy(float)}
1098
- pos_of = lambda idx: df.index.get_indexer(idx)
1099
-
1100
- # --- indikatörler (Supertrend + UT Bot), tamamen Python'da
1101
- st_dir, st_line = supertrend(df, int(st_period), float(st_mult))
1102
- ut_pos, ut_stop = ut_bot(df, float(ut_key), int(ut_period))
1103
- ind_arr = np.where((st_dir == ut_pos) & (ut_pos != 0), ut_pos, 0).astype(int)
1104
- ind_only = ind_mode == "Sadece indikatör"
1105
- ind_confirm = ind_mode == IND_MODES[1]
1106
- ind_now = int(ind_arr[-1])
1107
-
1108
- # --- Lorentzian Classification (ML + kernel) ve birleşik sinyal (oylama)
1109
- combo_on = combo_mode != COMBO_MODES[0]
1110
- combo_only = combo_mode == COMBO_MODES[2]
1111
- ldc_on = ldc_mode != LDC_MODES[0] and not combo_only
1112
- ldc_only = ldc_mode == LDC_MODES[2] and not combo_only
1113
- ldc_confirm = ldc_mode == LDC_MODES[1] and not combo_only
1114
- if combo_only: # oylar zaten birleşik sinyale girer; tekil filtreler çift sayılmasın
1115
- ind_only = ind_confirm = vp_on = False
1116
- ind_only = ind_only and not ldc_only # iki "Sadece" modu birlikteyse Lorentzian öncelikli
1117
- if smc_only: # backtest yalnızca SMC girişlerine göre; diğer sinyal/filtre modları devre dışı
1118
- ind_only = ind_confirm = vp_on = ldc_on = ldc_only = ldc_confirm = False
1119
- combo_on = combo_only = False
1120
- need_ldc = ldc_mode != LDC_MODES[0] or combo_on
1121
- LD = ldc_classify(df, ldc_k, ldc_back, ldc_regime, ldc_kern_lb, ldc_kern_rw,
1122
- use_vol=bool(ldc_use_vol), use_regime=bool(ldc_use_regime),
1123
- use_adx=bool(ldc_use_adx), use_kernel=bool(ldc_kernel),
1124
- exact=bool(ldc_exact)) if need_ldc else None
1125
- ldc_arr = LD["state"] if LD else np.zeros(len(df), dtype=int)
1126
- ldc_now = int(ldc_arr[-1])
1127
- vp_now = int(vp_arr[-1])
1128
- votes = np.vstack([ind_arr, ldc_arr, vp_arr]) # ST+UT, Lorentzian, Volume Profile
1129
- n_long, n_short = (votes == 1).sum(axis=0), (votes == -1).sum(axis=0)
1130
- mv = int(combo_votes)
1131
- combo_arr = np.where((n_long >= mv) & (n_short == 0), 1,
1132
- np.where((n_short >= mv) & (n_long == 0), -1, 0)).astype(int)
1133
- combo_now = int(combo_arr[-1])
1134
-
1135
- # --- indikatör bazlı SL/TP: en yakın ST / UT / VP seviyesi = invalidasyon (SL),
1136
- # önündeki Value Area / POC seviyesi >= 1 ATR uzaktaysa TP üst sınırı
1137
- ind_tpsl = bool(ind_tpsl)
1138
- if ind_tpsl:
1139
- c_, a_ = A["c"], A["a"]
1140
- vpo, vpl, vph = (vp[k].to_numpy(float) for k in ("poc", "val", "vah"))
1141
-
1142
- def nearest(levels, sign): # sign=+1: seviye fiyatın altında, -1: üstünde
1143
- d = np.full(len(c_), np.inf)
1144
- for lv in levels:
1145
- dd = sign*(c_-lv)
1146
- ok = np.isfinite(dd) & (dd > 0)
1147
- d = np.where(ok & (dd < d), dd, d)
1148
- return d
1149
- smin, smax = float(tpsl_smin), float(tpsl_smax)
1150
- sd_of = lambda d: np.clip(np.where(np.isfinite(d), d+0.1*a_, 1.5*a_), smin*a_, smax*a_)
1151
- cap_of = lambda d: np.where(d >= 1.0*a_, d, np.inf)
1152
- lv_l = [st_line, ut_stop, vpl] + ([SM["sw_lo"]] if (smc_only and SM is not None) else [])
1153
- lv_s = [st_line, ut_stop, vph] + ([SM["sw_hi"]] if (smc_only and SM is not None) else [])
1154
- cap_l, cap_s = [vpo, vph], [vpo, vpl]
1155
- if smc_lvl_on: # Order Block + önceki D/W/M high-low: SL referansı ve TP sınırı
1156
- lv_l += [SM["ob_sup"], SM["pdl"], SM["pwl"], SM["pml"]]
1157
- lv_s += [SM["ob_res"], SM["pdh"], SM["pwh"], SM["pmh"]]
1158
- cap_l += [SM["ob_res"], SM["pdh"], SM["pwh"], SM["pmh"]]
1159
- cap_s += [SM["ob_sup"], SM["pdl"], SM["pwl"], SM["pml"]]
1160
- A["ind_sd"] = {1: sd_of(nearest(lv_l, 1)),
1161
- -1: sd_of(nearest(lv_s, -1))}
1162
- A["ind_cap"] = {1: cap_of(nearest(cap_l, -1)),
1163
- -1: cap_of(nearest(cap_s, 1))}
1164
-
1165
- # --- K seçimi, kalibrasyon, güncel olasılık
1166
- best_k, k_scores = tune_k(dev_X, dev_y, h)
1167
- cal_n = min(120, max(45, int(len(dev_X)*0.20)))
1168
- fit_end = len(dev_X)-cal_n-h
1169
- if fit_end <= 100:
1170
- raise ValueError("Kalibrasyon için eğitim verisi yetersiz.")
1171
- fit_X, fit_y = dev_X.iloc[:fit_end], dev_y.iloc[:fit_end]
1172
- cal_X, cal_y = dev_X.iloc[fit_end+h:], dev_y.iloc[fit_end+h:]
1173
- cal_model = pipe(best_k).fit(fit_X, fit_y)
1174
- classes_now = cal_model.named_steps["knn"].classes_
1175
- current_x = X_all.iloc[[-1]]
1176
- probs = calibrate_current(cal_model.predict_proba(cal_X), cal_y.to_numpy(),
1177
- cal_model.predict_proba(current_x), classes_now)
1178
- p_down, p_flat, p_up = (float(p) for p in probs)
1179
-
1180
- if side_mode == "Sadece Long":
1181
- side = 1
1182
- elif side_mode == "Sadece Short":
1183
- side = -1
1184
- else:
1185
- side = 1 if p_up >= p_down else -1
1186
- if ind_only and ind_now != 0 and allow[ind_now]:
1187
- side = ind_now
1188
- if ldc_only and ldc_now != 0 and allow[ldc_now]:
1189
- side = ldc_now
1190
- if combo_only and combo_now != 0 and allow[combo_now]:
1191
- side = combo_now
1192
- if smc_only and smc_now != 0 and allow[smc_now]:
1193
- side = smc_now
1194
- p_side, p_opp = (p_up, p_down) if side == 1 else (p_down, p_up)
1195
-
1196
- imp, scaler = cal_model.named_steps["imputer"], cal_model.named_steps["scale"]
1197
- nn = NearestNeighbors(n_neighbors=best_k).fit(scaler.transform(imp.transform(fit_X)))
1198
- _, idx = nn.kneighbors(scaler.transform(imp.transform(current_x)))
1199
- agreement = float((fit_y.iloc[idx[0]].to_numpy() == side).mean())
1200
- if ind_only: # indikatör modunda: son 10 mumda yönün sürekliliği
1201
- agreement = float((ind_arr[-10:] == side).mean())
1202
- if ldc_only:
1203
- agreement = float((ldc_arr[-10:] == side).mean())
1204
- if combo_only:
1205
- agreement = float((combo_arr[-10:] == side).mean())
1206
- if smc_only:
1207
- agreement = float((smc_arr[-10:] == side).mean())
1208
-
1209
- # --- yön bazlı TP/SL optimizasyonu (yalnızca geliştirme verisi)
1210
- current_regime = str(regimes_all.iloc[-1])
1211
- dev_pos = pos_of(dev_X.index)
1212
- params, top, regime_used = {}, [], "—"
1213
-
1214
- def opt_pos(s_):
1215
- f_ = list(dev_pos)
1216
- if ind_only or ind_confirm:
1217
- f_ = [i for i in f_ if ind_arr[i] == s_]
1218
- if ldc_on:
1219
- f_ = [i for i in f_ if ldc_arr[i] == s_]
1220
- if combo_on:
1221
- f_ = [i for i in f_ if combo_arr[i] == s_]
1222
- if vp_on:
1223
- f_ = [i for i in f_ if vp_arr[i] == s_]
1224
- if gann_on:
1225
- f_ = [i for i in f_ if gann_arr[i] == s_]
1226
- if smc_on and not smc_only:
1227
- f_ = [i for i in f_ if smc_arr[i] == s_]
1228
- if smc_only:
1229
- f_ = [i for i in f_ if smc_entry[i] == s_]
1230
- return f_ if len(f_) >= 35 else dev_pos
1231
- for s in (1, -1):
1232
- if allow[s]:
1233
- sl_s, rr_s, top_s, used_s = optimize_barriers(
1234
- A, opt_pos(s), reg_arr, s, cost, current_regime, h)
1235
- params[s] = (sl_s, rr_s)
1236
- if s == side:
1237
- top, regime_used = top_s, used_s
1238
- sl, rr = params[side]
1239
-
1240
- # --- dokunulmamış OOS bloğu
1241
- oos_X = X.iloc[dev_end+h:]
1242
- oos_y = y.loc[oos_X.index]
1243
- oos_pred = pipe(best_k).fit(dev_X, dev_y).predict(oos_X)
1244
- oos_bacc = float(balanced_accuracy_score(oos_y, oos_pred))
1245
- oos_acc = float(accuracy_score(oos_y, oos_pred))
1246
- oos_pos_arr = pos_of(oos_X.index)
1247
- if ldc_only: # Lorentzian yönü OOS'ta tahmin yerine geçer
1248
- oos_pred = ldc_arr[oos_pos_arr]
1249
- oos_bacc = float(balanced_accuracy_score(oos_y, oos_pred))
1250
- oos_acc = float(accuracy_score(oos_y, oos_pred))
1251
- if combo_only: # birleşik sinyal OOS'ta tahmin yerine geçer
1252
- oos_pred = combo_arr[oos_pos_arr]
1253
- oos_bacc = float(balanced_accuracy_score(oos_y, oos_pred))
1254
- oos_acc = float(accuracy_score(oos_y, oos_pred))
1255
- if ind_only: # indikatör yönü OOS'ta tahmin yerine geçer
1256
- oos_pred = ind_arr[oos_pos_arr]
1257
- oos_bacc = float(balanced_accuracy_score(oos_y, oos_pred))
1258
- oos_acc = float(accuracy_score(oos_y, oos_pred))
1259
-
1260
- if smc_only: # metrik: SMC yön durumu; işlem: yapı kırılımı mumları
1261
- oos_pred = smc_arr[oos_pos_arr]
1262
- oos_bacc = float(balanced_accuracy_score(oos_y, oos_pred))
1263
- oos_acc = float(accuracy_score(oos_y, oos_pred))
1264
- oos_pred = smc_entry[oos_pos_arr]
1265
- rows, busy = [], -1
1266
- for i, pred in zip(oos_pos_arr, oos_pred):
1267
- pred = int(pred)
1268
- if pred == 0 or not allow[pred]:
1269
- continue
1270
- if ind_confirm and ind_arr[i] != pred:
1271
- continue
1272
- if vp_on and vp_arr[i] != pred:
1273
- continue
1274
- if gann_on and gann_arr[i] != pred:
1275
- continue
1276
- if smc_on and smc_arr[i] != pred:
1277
- continue
1278
- if ldc_confirm and ldc_arr[i] != pred:
1279
- continue
1280
- if combo_on and combo_arr[i] != pred:
1281
- continue
1282
- if skip_overlap and i <= busy:
1283
- continue
1284
- sl_p, rr_p = params[pred]
1285
- t = barrier_trade(A, i, pred, sl_p, rr_p, h)
1286
- if t is None:
1287
- continue
1288
- ret, j, why = t
1289
- net = ret-cost
1290
- r = net/max(stop_dist(A, i, pred, sl_p)/A["c"][i], 1e-12)
1291
- rows.append((df.index[i], df.index[j], "LONG" if pred == 1 else "SHORT",
1292
- A["c"][i], why, net*100, r, reg_arr[i]))
1293
- busy = j
1294
- trades_df = pd.DataFrame(rows, columns=TRADE_COLS)
1295
- R = trades_df.R.to_numpy() if len(trades_df) else np.array([])
1296
- expectancy_r = float(R.mean()) if len(R) else 0.0
1297
-
1298
- recent = df.Close.tail(30)
1299
- drawdown = float(recent.iloc[-1]/recent.cummax().iloc[-1]-1)
1300
- risk_pct, _ = adaptive_risk(min_risk, max_risk, oos_bacc, agreement,
1301
- expectancy_r, len(R), drawdown)
1302
- curve, dds = simulate_equity(R, risk_pct)
1303
- stats = trade_stats(R, curve, dds, trades_df)
1304
- mc = risk_path_mc(R, risk_pct) if len(R) else None
1305
-
1306
- # --- güncel seviyeler ve pozisyon
1307
- entry, cur_atr = float(df.Close.iloc[-1]), float(atr_all.iloc[-1])
1308
- if ind_tpsl:
1309
- stop_d = float(A["ind_sd"][side][-1])
1310
- target_d = float(min(stop_d*rr, A["ind_cap"][side][-1]))
1311
- cl_ = float(A["c"][-1])
1312
- cands = {"Supertrend": float(st_line[-1]), "UT trailing stop": float(ut_stop[-1]),
1313
- "VP " + ("VAL" if side == 1 else "VAH"):
1314
- float((vp.val if side == 1 else vp.vah).iloc[-1])}
1315
- if smc_lvl_on:
1316
- kk_ = ("ob_sup", "pdl", "pwl", "pml") if side == 1 else ("ob_res", "pdh", "pwh", "pmh")
1317
- for k_, nm_ in zip(kk_, ("SMC OB", "Önceki gün", "Önceki hafta", "Önceki ay")):
1318
- cands[nm_] = float(SM[k_][-1])
1319
- if smc_only and SM is not None:
1320
- cands["SMC swing " + ("low" if side == 1 else "high")] = float(SM["sw_lo" if side == 1 else "sw_hi"][-1])
1321
- valid = {k_: side*(cl_-v_) for k_, v_ in cands.items() if np.isfinite(v_) and side*(cl_-v_) > 0}
1322
- sl_name = min(valid, key=valid.get) if valid else "ATR varsayılan (seviye yok)"
1323
- else:
1324
- stop_d, target_d = sl*cur_atr, sl*cur_atr*rr
1325
- stop = entry-stop_d if side == 1 else entry+stop_d
1326
- target = entry+target_d if side == 1 else entry-target_d
1327
-
1328
- risk_cash = equity*risk_pct/100
1329
- cost_per_unit = stop_d+entry*cost
1330
- qty = risk_cash/cost_per_unit if cost_per_unit > 0 else 0.0
1331
- max_notional = equity*float(max_leverage)
1332
- capped = qty*entry > max_notional
1333
- if capped:
1334
- qty = max_notional/entry
1335
- if whole_units:
1336
- qty = float(np.floor(qty))
1337
- notional = qty*entry
1338
- actual_risk = qty*cost_per_unit
1339
- margin = notional/max(float(max_leverage), 1)
1340
-
1341
- # --- işlem kapısı (her kontrol ayrı gösterilir)
1342
- checks = [
1343
- ("Yön izni", side_mode, "—", allow[side]),
1344
- ("P(yön)", f"{p_side:.1%}", f"≥ {p_min:.0%}", p_side >= p_min),
1345
- ("P(yön) − P(karşı)", f"{(p_side-p_opp):.1%}", f"≥ {p_gap:.0%}", (p_side-p_opp) >= p_gap),
1346
- ("P(FLAT)", f"{p_flat:.1%}", f"< {flat_max:.0%}", p_flat < flat_max),
1347
- ("Komşu yön uyumu", f"{agreement:.1%}", f"≥ {agree_min:.0%}", agreement >= agree_min),
1348
- ("OOS balanced acc.", f"{oos_bacc:.3f}", f"≥ {bacc_min:.2f}", oos_bacc >= bacc_min),
1349
- ("OOS işlem sayısı", f"{len(R)}", f"≥ {int(min_trades)}", len(R) >= int(min_trades)),
1350
- ("OOS expectancy", f"{expectancy_r:.3f}R", "> 0", expectancy_r > 0),
1351
- ]
1352
- dname = {1: "LONG ↑", -1: "SHORT ↓", 0: "belirsiz"}
1353
- ind_check = ("İndikatör yönü (ST + UT aynı)", dname[ind_now], "yönle aynı", ind_now == side)
1354
- if ind_only or ldc_only or combo_only or smc_only:
1355
- keep = ("Yön izni", "OOS balanced acc.", "OOS işlem sayısı", "OOS expectancy")
1356
- if smc_only:
1357
- keep = tuple(k_ for k_ in keep if k_ != "OOS balanced acc.")
1358
- checks = [c for c in checks if c[0] in keep]
1359
- if ind_only or ind_confirm:
1360
- checks.insert(1, ind_check)
1361
- if ldc_on:
1362
- checks.insert(1, ("Lorentzian yönü (ML + kernel)", dname[ldc_now], "yönle aynı",
1363
- ldc_now == side))
1364
- if combo_on:
1365
- nv = int(sum(1 for v_ in (ind_now, ldc_now, vp_now) if v_ == side))
1366
- no_ = int(sum(1 for v_ in (ind_now, ldc_now, vp_now) if v_ == -side))
1367
- checks.insert(1, ("Birleşik sinyal (ST+UT · Lorentzian · VP)", f"{nv}/3 oy, karşı {no_}",
1368
- f"≥ {mv} oy, karşı oy yok", combo_now == side))
1369
- if ind_tpsl:
1370
- rr_now = target_d/stop_d if stop_d > 0 else 0.0
1371
- checks.append(("İndikatör R:R (TP/SL)", f"{rr_now:.2f}", f"≥ {float(tpsl_minrr):g}",
1372
- rr_now >= float(tpsl_minrr)))
1373
- if vp_on:
1374
- checks.insert(1, ("Volume Profile (POC/VA konumu)", vp_names[int(vp_arr[-1])],
1375
- "yönle aynı", int(vp_arr[-1]) == side))
1376
- if gann_on:
1377
- checks.insert(1, ("Gann Box (%50 konumu)", dname[gann_now], "yönle aynı", gann_now == side))
1378
- if smc_on:
1379
- checks.insert(1, (f"SMC yapı yönü ({smc_src}" + (" + premium/discount)" if smc_zone else ")"),
1380
- dname[smc_now], "yönle aynı", smc_now == side))
1381
- trade_ok = all(c[3] for c in checks)
1382
- if trade_ok:
1383
- signal = "LONG" if side == 1 else "SHORT"
1384
- else:
1385
- signal = "WAIT — İŞLEM AÇMA"
1386
- qty = notional = margin = actual_risk = 0.0
1387
- gate_md = md_table(["Kontrol", "Değer", "Eşik", "Durum"],
1388
- [(n, v, t, "✅" if ok else "❌") for n, v, t, ok in checks])
1389
-
1390
- # --- rapor
1391
- if stats:
1392
- pf = "∞" if np.isinf(stats["pf"]) else f"{stats['pf']:.2f}"
1393
- oos_md = md_table(["Ölçüm", "Değer"], [
1394
- ("İşlem sayısı", stats["n"]),
1395
- ("Kazanma oranı", f"{stats['win']:.1%}"),
1396
- ("Expectancy", f"{stats['exp']:.3f}R"),
1397
- ("Profit factor", pf),
1398
- (f"Toplam getiri (risk {risk_pct:.2f}%)", f"{stats['ret']:.2%}"),
1399
- ("Maks. drawdown", f"{stats['mdd']:.2%}"),
1400
- ("Çıkış dağılımı TP / SL / süre", f"{stats['tp']} / {stats['sl']} / {stats['time']}"),
1401
- ])
1402
- g = trades_df.groupby("regime").R.agg(["count", "mean", lambda s: (s > 0).mean()])
1403
- reg_md = md_table(["Rejim", "İşlem", "Ort. R", "Kazanma"],
1404
- [(r, int(c), f"{m:.3f}", f"{w:.1%}")
1405
- for r, (c, m, w) in g.iterrows()])
1406
- else:
1407
- oos_md = reg_md = "_OOS işlem yok._"
1408
-
1409
- mc_text = (
1410
- f"100 işlem · 2.000 bootstrap senaryosu · medyan: {mc['median']:.2%} · "
1411
- f"%5 kötü sonuç: {mc['p05']:.2%} · %95 iyi sonuç: {mc['p95']:.2%} · "
1412
- f"%5 kuyruk max DD: {mc['dd95']:.2%} · "
1413
- f"%50 sermaye altına inme frekansı: {mc['ruin']:.1%}"
1414
- if mc else "Monte Carlo için yeterli OOS işlem yok.")
1415
- ind_md = md_table(["İndikatör", "Yön", "Seviye"], [
1416
- (f"Supertrend ({int(st_period)}, {float(st_mult):g})", dname[int(st_dir[-1])], fmt_p(float(st_line[-1]))),
1417
- (f"UT Bot ({float(ut_key):g}, {int(ut_period)})", dname[int(ut_pos[-1])], fmt_p(float(ut_stop[-1]))),
1418
- ("Bileşik (ikisi aynı yönde)", dname[ind_now], "—")])
1419
- if lp is not None:
1420
- _, _, poc_n, val_n, vah_n = lp
1421
- pos_txt = ("VA üstünde" if entry > vah_n else "VA altında" if entry < val_n
1422
- else "VA içinde · POC üstü" if entry > poc_n else "VA içinde · POC altı")
1423
- vp_md = md_table(["Seviye", "Fiyat"], [
1424
- ("VAH (Value Area üst)", fmt_p(vah_n)), ("POC (en yüksek hacim)", fmt_p(poc_n)),
1425
- ("VAL (Value Area alt)", fmt_p(val_n)), ("Son kapanış", f"{fmt_p(entry)} — {pos_txt}"),
1426
- ("VP yön onayı", vp_names[int(vp_arr[-1])])])
1427
- else:
1428
- vp_md = "_Profil hesaplanamadı._"
1429
- vp_note = (f"Pencere {vp_win} mum · {vp_nb} kutu · Value Area %{vp_vap*100:.0f} · "
1430
- f"uzama sınırı {float(vp_ext):g} ATR."
1431
- + (" Hacim verisi yok → zaman bazlı profil kullanıldı." if vp_no_vol else "")
1432
- + (" Mod Kapalı: yalnızca bilgi amaçlı." if not vp_on else ""))
1433
- if SM is not None:
1434
- last_ev = [e for e in SM["events"] if not e["internal"]][-1:] or [None]
1435
- last_iv = [e for e in SM["events"] if e["internal"]][-1:] or [None]
1436
- evtxt = lambda e: "—" if e is None else (f"{e['kind']} {'↑' if e['bull'] else '↓'} · {fmt_p(e['lvl'])} · "
1437
- f"{df.index[e['i']].date()}")
1438
- tp_, bt_ = float(SM["top"][-1]), float(SM["bot"][-1])
1439
- zpos = (entry-bt_)/(tp_-bt_) if tp_ > bt_ else float("nan")
1440
- zname = "—" if not np.isfinite(zpos) else ("Premium" if zpos > 0.525 else "Discount" if zpos < 0.475 else "Equilibrium")
1441
- obt = lambda b: [o_ for o_ in SM["obs"] if o_["bull"] == b and not o_["internal"]][-1:]
1442
- ob_txt = lambda b: (f"{fmt_p(obt(b)[0]['lo'])} – {fmt_p(obt(b)[0]['hi'])}" if obt(b) else "—")
1443
- smc_md = md_table(["Bileşen", "Değer"], [
1444
- ("Swing yapı yönü", dname[int(SM["swing_trend"][-1])]),
1445
- ("Internal yapı yönü", dname[int(SM["internal_trend"][-1])]),
1446
- ("Son swing kırılımı", evtxt(last_ev[0])), ("Son internal kırılımı", evtxt(last_iv[0])),
1447
- ("Premium / Discount", f"{zname}" + (f" (%{zpos*100:.0f})" if np.isfinite(zpos) else "")),
1448
- ("Strong/Weak High · Low", f"{'Strong' if SM['strong_high'] else 'Weak'} {fmt_p(tp_)} · "
1449
- f"{'Strong' if SM['strong_low'] else 'Weak'} {fmt_p(bt_)}"),
1450
- ("Aktif swing OB (bull)", ob_txt(True)), ("Aktif swing OB (bear)", ob_txt(False)),
1451
- ("Önceki gün H / L", f"{fmt_p(SM['pdh'][-1])} / {fmt_p(SM['pdl'][-1])}"),
1452
- ("Önceki hafta H / L", f"{fmt_p(SM['pwh'][-1])} / {fmt_p(SM['pwl'][-1])}"),
1453
- ("Önceki ay H / L", f"{fmt_p(SM['pmh'][-1])} / {fmt_p(SM['pml'][-1])}"),
1454
- ("Açık FVG sayısı", len(SM["fvg"])), ("SMC yön onayı", dname[smc_now])])
1455
- smc_note = (f"Swing {int(smc_swing_len)} · internal {int(smc_int_len)} mum"
1456
- + (" · premium/discount kapısı açık" if smc_zone else "")
1457
- + (" · internal confluence filtresi" if smc_conf else "")
1458
- + f" · OB filtresi: {smc_obf}"
1459
- + (" · OB + önceki D/W/M seviyeleri SL/TP'de" if smc_lvl_on else "")
1460
- + (" · Mod Kapalı: yalnızca bilgi/grafik." if not smc_on else "") + ".")
1461
- else:
1462
- smc_md, smc_note = "_Mod kapalı._", ""
1463
- if GB is not None:
1464
- gpos = gann_pos[-1]
1465
- gann_md = md_table(["Seviye", "Fiyat"],
1466
- [(f"{p:.3f}" + (" (orta)" if p == 0.5 else ""), fmt_p(y)) for p, y in sorted(GB["levels"].items(), reverse=True)]
1467
- + [("Son kapanış", f"{fmt_p(entry)} — kutu içi konum %{gpos*100:.0f}" if np.isfinite(gpos) else fmt_p(entry)),
1468
- ("Kutu yönü", "yükseliş (düşük → yüksek)" if GB["up"] else "düşüş (yüksek → düşük)"),
1469
- ("Gann yön onayı", dname[gann_now])])
1470
- gann_note = (f"Pencere {int(gann_lb)} mum · yüksek {GB['hi_date'].date()} · düşük {GB['lo_date'].date()}"
1471
- + (" · yön çevrildi (reverse)" if gann_rev else "")
1472
- + (" · Mod Kapalı: yalnızca bilgi amaçlı." if not gann_on else "") + ".")
1473
- else:
1474
- gann_md, gann_note = "_Kutu hesaplanamadı._", ""
1475
- if LD is not None:
1476
- fl = lambda on, arr: "kapalı" if not on else ("✅" if bool(arr[-1]) else "❌")
1477
- ldc_md = md_table(["Bileşen", "Değer"], [
1478
- ("Lorentzian yönü (ML + kernel)", dname[ldc_now]),
1479
- ("ML sinyali (filtreli)", dname[int(LD["signal"][-1])]),
1480
- ("ML tahmin skoru", f"{int(LD['pred'][-1]):+d} / ±{int(ldc_k)}"),
1481
- ("Kernel tahmini · eğim", f"{fmt_p(float(LD['yhat'][-1]))} · "
1482
- f"{'↑' if LD['yhat'][-1] > LD['yhat'][-2] else '↓'}"),
1483
- ("Volatilite / Rejim / ADX filtresi",
1484
- f"{fl(ldc_use_vol, LD['vol'])} / {fl(ldc_use_regime, LD['reg'])} / {fl(ldc_use_adx, LD['adx'])}")])
1485
- ldc_note = (f"{int(ldc_k)} komşu · en fazla {int(ldc_back)} geçmiş mum · kernel "
1486
- f"({int(ldc_kern_lb)}, {float(ldc_kern_rw):g}) · rejim eşiği {float(ldc_regime):g}.")
1487
- else:
1488
- ldc_md, ldc_note = "_Mod kapalı._", ""
1489
- if combo_on:
1490
- combo_md = md_table(["Kaynak", "Oy"], [
1491
- ("Supertrend + UT Bot", dname[ind_now]), ("Lorentzian (ML + kernel)", dname[ldc_now]),
1492
- ("Volume Profile (POC/VA)", dname[vp_now]),
1493
- ("**Birleşik sinyal**", f"**{dname[combo_now]}** (en az {mv} oy, karşı oy yok)")])
1494
- else:
1495
- combo_md = "_SMC-only modunda devre dışı._" if smc_only else "_Mod kapalı._"
1496
- if ind_tpsl:
1497
- capped = target_d < stop_d*rr-1e-12
1498
- tp_title = "indikatörlerden türetildi"
1499
- tpsl_note = (f"SL: en yakın invalidasyon seviyesi — **{sl_name}** (+0,1 ATR tampon, "
1500
- f"{float(tpsl_smin):g}–{float(tpsl_smax):g} ATR aralığına sıkıştırılır). "
1501
- f"TP: {rr:.2f}R katı"
1502
- + (", önündeki VA/POC/SMC OB/MTF seviyesinde sınırlandı" if capped else "")
1503
- + ". Optimize edilen tek parametre R katıdır.\n\n")
1504
- else:
1505
- tp_title, tpsl_note = "optimize", ""
1506
- k_text = " | ".join(f"K={k}: {s:.3f}" for s, k in sorted(k_scores, reverse=True)) or "—"
1507
- overlap_note = ("tek pozisyon (çakışma yok)" if skip_overlap
1508
- else "çakışan sinyaller bağımsız işlem sayıldı")
1509
- report = f"""
1510
- # {signal}
1511
-
1512
- **Son kapanmış mum:** {df.index[-1].date()} · **{ticker} referans fiyatı:** {fmt_p(entry)} · **Ufuk:** {h} gün
1513
-
1514
- ## Kapı kontrolleri
1515
- {gate_md}
1516
-
1517
- ## {ticker} — Kalibre edilmiş KNN
1518
- | Ölçüm | Değer |
1519
- |---|---:|
1520
- | P(UP) / P(FLAT) / P(DOWN) | {p_up:.1%} / {p_flat:.1%} / {p_down:.1%} |
1521
- | Seçilen K | {best_k} |
1522
- | OOS accuracy | {oos_acc:.3f} |
1523
- | Güncel rejim | {current_regime} |
1524
-
1525
- **K seçim skorları:** {k_text}
1526
-
1527
- ## İndikatörler — mod: {ind_mode}
1528
- {ind_md}
1529
-
1530
- ## Birleşik sinyal — mod: {combo_mode}
1531
- {combo_md}
1532
-
1533
- ## Lorentzian Classification — mod: {ldc_mode}
1534
- {ldc_md}
1535
-
1536
- {ldc_note}
1537
-
1538
- ## Volume Profile — mod: {vp_mode}
1539
- {vp_md}
1540
-
1541
- {vp_note}
1542
-
1543
- ## Smart Money Concepts (LuxAlgo) — mod: {smc_mode}
1544
- {smc_md}
1545
-
1546
- {smc_note}
1547
-
1548
- ## Gann Box (Zeiierman) — mod: {gann_mode}
1549
- {gann_md}
1550
-
1551
- {gann_note}
1552
-
1553
- ## TP / SL ({'LONG' if side == 1 else 'SHORT'} için {tp_title})
1554
- | Parametre | Değer |
1555
- |---|---:|
1556
- | SL | {stop_d/cur_atr:.2f} ATR |
1557
- | TP | {target_d/stop_d:.2f}R |
1558
- | Stop-loss | {fmt_p(stop)} |
1559
- | Take-profit | {fmt_p(target)} |
1560
- | ATR(14) | {fmt_p(cur_atr)} |
1561
-
1562
- {tpsl_note}TP/SL optimizasyon rejimi: {regime_used}. Yeterli rejim örneği yoksa tüm geliştirme dönemine geri dönülür.
1563
-
1564
- ## OOS backtest ({overlap_note})
1565
- {oos_md}
1566
-
1567
- **Rejim bazlı OOS:**
1568
-
1569
- {reg_md}
1570
-
1571
- ## Risk-bütçeli pozisyon ({ticker})
1572
- | Parametre | Değer |
1573
- |---|---:|
1574
- | Hesap büyüklüğü | ${equity:,.2f} |
1575
- | Adaptif risk | {risk_pct:.3f}% |
1576
- | Risk bütçesi | ${risk_cash:,.2f} |
1577
- | Varlık miktarı (birim) | {qty:.8f} |
1578
- | Nominal pozisyon | ${notional:,.2f} |
1579
- | Tahmini teminat | ${margin:,.2f} |
1580
- | Tahmini stop kaybı + maliyet | ${actual_risk:,.2f} |
1581
- | Kaldıraç sınırı | {"Uygulandı" if capped else "Uygulanmadı"} |
1582
- | Son 30 günlük kapanış DD | {drawdown:.2%} |
1583
-
1584
- ## Monte Carlo
1585
- {mc_text}
1586
- """
1587
- top_df = pd.DataFrame(top, columns=["score", "SL_ATR", "TP_R", "samples",
1588
- "mean_net_return", "win_rate"])
1589
- if ind_tpsl and not top_df.empty:
1590
- top_df["SL_ATR"] = "indikatör"
1591
- if not top_df.empty:
1592
- top_df["score"] = top_df.score.map(lambda v: f"{v:.5f}")
1593
- top_df["mean_net_return"] = top_df.mean_net_return.map(lambda v: f"{v:.3%}")
1594
- top_df["win_rate"] = top_df.win_rate.map(lambda v: f"{v:.1%}")
1595
-
1596
- csv_path = None
1597
- if len(trades_df):
1598
- safe = re.sub(r"[^A-Za-z0-9_-]", "_", ticker)
1599
- csv_path = os.path.join(tempfile.gettempdir(), f"goshawk_{safe}_oos_trades.csv")
1600
- trades_df.to_csv(csv_path, index=False)
1601
-
1602
- price_fig = price_figure(df, ticker, trades_df, trade_ok, stop, target,
1603
- ind=(st_line, st_dir, ut_stop),
1604
- vp=vp if (vp_on or combo_on) else None, vp_prof=lp,
1605
- ldc=LD, combo=combo_arr if combo_on else None,
1606
- gann=GB, gann_fan=bool(gann_fan_on), gann_extend=bool(gann_ext_on), smc=SM)
1607
- eq_fig = equity_figure(curve, dds) if len(R) else None
1608
- pf_fig = pf_gauge(stats["pf"], stats["n"]) if stats else None
1609
- mc_fig = mc_figure(mc) if mc else None
1610
-
1611
- disclaimer = (
1612
- "**Araştırma prototipi; otomatik emir göndermez.** KNN olasılık kalibrasyonu tek bir "
1613
- "tarihsel kalibrasyon dilimine dayanır ve küçük örneklerde ham olasılığa geri döner. "
1614
- "TP/SL optimizasyonu geliştirme döneminde ve yön bazında yapılır; son dönem OOS olarak "
1615
- "ayrılır. Rejim etiketleri geliştirme verisinin volatilite kantillerine dayanır; canlı "
1616
- "dağılım değişebilir. Adaptif risk yüzdesi OOS metriklerinden türetildiği için OOS "
1617
- "özsermaye eğrisi iyimser olabilir. Günlük mumda SL ve TP aynı mumda görülürse SL "
1618
- "varsayılır. Monte Carlo, OOS R-multiple serisini bootstrap eder; bağımsızlık varsayımı "
1619
- "seri korelasyonunu ve rejim değişimini tam modellemez. Pozisyon hesabı spot miktar "
1620
- "varsayar; CFD lot sözleşmesi, spread, funding, swap, komisyon, minimum lot, marjin ve "
1621
- "broker fiyatı farklı olabilir. Gap ve kayma kaybı planlanan riskin üstüne çıkarabilir. "
1622
- "Supertrend ve UT Bot, TradingView formüllerinin Python portudur; ATR başlangıcı ve veri kaynağı yüzünden TV değerlerinden küçük sapmalar olabilir. Birleşik sinyal; ST+UT, Lorentzian ve Volume Profile oylarının birleşimidir. İndikatör bazlı TP/SL, en yakın ST/UT/VP seviyesini invalidasyon sayar ve ATR aralığına sıkıştırır. Lorentzian Classification, jdehorty'nin TradingView göstergesinin Python portudur (ANN komşu seçimi, filtreler ve kernel regresyon); Pine kaynağıyla birebir aynı sonuç vermeyebilir ve yalnızca günlük mumda çalışır. Volume Profile, günlük OHLCV'den (her mumun hacmi high–low aralığına eşit dağıtılarak) yaklaşık hesaplanır; TradingView VPVR'nin alt-zaman dilimi verisiyle birebir aynı olmaz. Gann Box, Zeiierman göstergesinin açıklamasına göre yazılmış bir Python portudur (Pine kaynağı birebir alınmadı); kutu son N mumun en yüksek/en düşük noktasından kurulur, Gann Fan 1x1 köşegeni ve katlarıyla çizilir, Gann Curves/Angles eklenmedi. Onay filtresi, kapanışın kutu %50 çizgisine göre konumuna bakar ve geçmişe bakan (kayan) pencere kullanır. SMC, LuxAlgo göstergesinin açıklamasına göre yazılmış bir Python portudur (Pine kaynağı birebir değil): swing/internal yapı, BOS/CHoCH, order block (high/low mitigasyonu), EQH/EQL, FVG ve premium/discount hesaplanır; mum boyama yoktur; internal confluence ve OB filtresi, Pine formüllerinin hafızadan yazılmış karşılığıdır. OB ve önceki gün/hafta/ay high-low seviyeleri, «TP/SL'i indikatörlerden türet» açıkken en yakın seviye SL referansı ve TP sınırı olarak backtest'e girer. Sadece SMC modunda işlemler yalnızca seçilen yapıdaki (swing/internal) BOS/CHoCH kırılım mumunda, kırılım yönünde açılır; SL en yakın ST/UT/VP/SMC swing seviyesinden, TP R katından türetilir; diğer sinyal modları devre dışıdır. Pivotlar pencere kadar mum sonra doğrulanır, bu yüzden sinyal geç gelir. SMC kavramlarının istatistiksel geçerliliği kanıtlı değildir; OOS sonuçlarıyla doğrulayın. Canlı hesap öncesi paper trading ve broker bazlı doğrulama zorunludur."
1623
- )
1624
- return (report, prob_figure(p_down, p_flat, p_up), price_fig, eq_fig, pf_fig, mc_fig,
1625
- top_df, csv_path, disclaimer)
1626
-
1627
- except Exception as exc:
1628
- return (f"# Hata\n{type(exc).__name__}: {exc}", None, None, None, None, None,
1629
- pd.DataFrame(), None,
1630
- "Veri bağlantısını ve parametreleri kontrol edin.")
1631
-
1632
-
1633
- # ---------------------------------------------------------------- arayüz
1634
- with gr.Blocks(title=APP_TITLE, theme=gr.themes.Soft()) as demo:
1635
- gr.Markdown(
1636
- "# GOSHAWK — Multi-Asset Adaptive Risk Engine v5\n"
1637
- "Kalibre KNN · adaptif K · yön bazlı TP/SL · bağımsız OOS backtest · "
1638
- "R-multiple Monte Carlo · drawdown bazlı risk · kapı kontrolleri."
1639
- )
1640
- with gr.Row():
1641
- with gr.Column():
1642
- ticker = gr.Dropdown(ASSETS, value="BTC-USD", allow_custom_value=True,
1643
- label="Varlık (listeden seç ya da Yahoo sembolü yaz)")
1644
- period = gr.Dropdown(["2y", "5y", "10y", "max"], value="5y", label="Veri geçmişi")
1645
- horizon = gr.Dropdown([3, 5, 10], value=5, label="Tahmin ufku (gün)")
1646
- side_mode = gr.Radio(SIDE_MODES, value="Long + Short", label="İzin verilen yön")
1647
- ind_mode = gr.Radio(IND_MODES, value="Kapalı", label="İndikatör modu (Supertrend + UT Bot)")
1648
- vp_mode = gr.Radio(VP_MODES, value="Kapalı", label="Volume Profile modu")
1649
- ldc_mode = gr.Radio(LDC_MODES, value="Kapalı", label="Lorentzian Classification modu")
1650
- combo_mode = gr.Radio(COMBO_MODES, value=COMBO_MODES[2],
1651
- label="Birleşik sinyal (ST+UT · Lorentzian · Volume Profile oylaması)")
1652
- gann_mode = gr.Radio(GANN_MODES, value=GANN_MODES[0], label="Gann Box modu (Zeiierman)")
1653
- smc_mode = gr.Radio(SMC_MODES, value=SMC_MODES[0], label="Smart Money Concepts modu (LuxAlgo)")
1654
- ind_tpsl = gr.Checkbox(value=True, label="TP/SL'i indikatörlerden türet (ST / UT / VP seviyeleri)")
1655
- train_window = gr.Slider(300, 1500, value=900, step=50, label="Maksimum eğitim penceresi")
1656
- with gr.Column():
1657
- equity = gr.Number(value=10000, label="Hesap büyüklüğü (USD)")
1658
- max_leverage = gr.Slider(1, 10, value=3, step=1, label="Azami kaldıraç")
1659
- min_risk = gr.Slider(0.05, 0.5, value=0.10, step=0.05, label="Minimum risk (%)")
1660
- max_risk = gr.Slider(0.25, 1.5, value=0.75, step=0.05, label="Maksimum risk (%)")
1661
- fee = gr.Number(value=0.05, label="Tek yön komisyon (%)")
1662
- slip = gr.Number(value=0.03, label="Tek yön slippage (%)")
1663
- with gr.Accordion("Gelişmiş: işlem kapısı eşikleri ve simülasyon", open=False):
1664
- with gr.Row():
1665
- p_min = gr.Slider(0.34, 0.70, value=0.43, step=0.01, label="Min. P(yön)")
1666
- p_gap = gr.Slider(0.0, 0.40, value=0.12, step=0.01, label="Min. P(yön) − P(karşı)")
1667
- flat_max = gr.Slider(0.30, 0.90, value=0.60, step=0.01, label="Maks. P(FLAT)")
1668
- with gr.Row():
1669
- agree_min = gr.Slider(0.30, 0.90, value=0.55, step=0.01, label="Min. komşu uyumu")
1670
- bacc_min = gr.Slider(0.30, 0.60, value=0.36, step=0.01, label="Min. OOS balanced acc.")
1671
- min_trades = gr.Slider(5, 50, value=15, step=1, label="Min. OOS işlem sayısı")
1672
- with gr.Row():
1673
- st_period = gr.Slider(5, 30, value=10, step=1, label="Supertrend ATR periyodu")
1674
- st_mult = gr.Slider(1.0, 6.0, value=3.0, step=0.1, label="Supertrend çarpanı")
1675
- ut_key = gr.Slider(0.5, 4.0, value=1.0, step=0.1, label="UT Bot key value")
1676
- ut_period = gr.Slider(5, 30, value=10, step=1, label="UT Bot ATR periyodu")
1677
- with gr.Row():
1678
- combo_votes = gr.Slider(1, 3, value=2, step=1, label="Birleşik sinyal: min. aynı yön oyu (karşı oy yok)")
1679
- tpsl_smin = gr.Slider(0.3, 2.0, value=0.8, step=0.1, label="İndikatör SL alt sınır (ATR)")
1680
- tpsl_smax = gr.Slider(1.5, 6.0, value=3.0, step=0.1, label="İndikatör SL üst sınır (ATR)")
1681
- tpsl_minrr = gr.Slider(0.5, 3.0, value=1.0, step=0.1, label="Min. R:R (kapı)")
1682
- with gr.Row():
1683
- ldc_k = gr.Slider(1, 30, value=8, step=1, label="Lorentzian komşu sayısı")
1684
- ldc_back = gr.Slider(300, 3000, value=2000, step=100, label="Lorentzian maks. geçmiş mum")
1685
- ldc_regime = gr.Slider(-2.0, 2.0, value=-0.1, step=0.1, label="Rejim eşiği")
1686
- with gr.Row():
1687
- ldc_kern_lb = gr.Slider(3, 50, value=8, step=1, label="Kernel lookback")
1688
- ldc_kern_rw = gr.Slider(0.25, 25, value=8, step=0.25, label="Kernel göreli ağırlık")
1689
- with gr.Row():
1690
- ldc_use_vol = gr.Checkbox(value=True, label="Volatilite filtresi")
1691
- ldc_use_regime = gr.Checkbox(value=True, label="Rejim filtresi")
1692
- ldc_use_adx = gr.Checkbox(value=False, label="ADX filtresi (eşik 20)")
1693
- ldc_kernel = gr.Checkbox(value=True, label="Kernel ile onayla")
1694
- ldc_exact = gr.Checkbox(value=True, label="Gerçek en yakın komşu (kapalı: Pine yaklaşık-NN)")
1695
- with gr.Row():
1696
- vp_window = gr.Slider(20, 250, value=60, step=5, label="Volume Profile penceresi (mum)")
1697
- vp_bins = gr.Slider(12, 60, value=24, step=2, label="Fiyat kutusu sayısı")
1698
- vp_va = gr.Slider(0.50, 0.90, value=0.70, step=0.05, label="Value Area oranı")
1699
- vp_ext = gr.Slider(0.5, 4.0, value=1.5, step=0.1, label="Maks. uzama (ATR) — kovalamayı engeller")
1700
- with gr.Row():
1701
- gann_lb = gr.Slider(30, 150, value=150, step=5, label="Gann Box penceresi (mum)")
1702
- gann_fan_on = gr.Checkbox(value=True, label="Gann Fan göster")
1703
- gann_ext_on = gr.Checkbox(value=True, label="Kutuyu geleceğe uzat (Extend)")
1704
- gann_rev = gr.Checkbox(value=False, label="Reverse (yönü çevir)")
1705
- with gr.Row():
1706
- smc_src = gr.Radio(SMC_SRC, value=SMC_SRC[0], label="SMC yön kaynağı")
1707
- smc_swing_len = gr.Slider(10, 100, value=50, step=5, label="SMC swing yapı uzunluğu")
1708
- smc_int_len = gr.Slider(3, 15, value=5, step=1, label="SMC internal yapı uzunluğu")
1709
- smc_zone = gr.Checkbox(value=False, label="Premium/Discount kapısı (long=discount, short=premium)")
1710
- smc_draw = gr.Checkbox(value=True, label="SMC katmanlarını grafikte göster")
1711
- with gr.Row():
1712
- smc_conf = gr.Checkbox(value=False, label="Internal confluence filtresi (zayıf kırılımları ele)")
1713
- smc_obf = gr.Radio(SMC_OBF, value=SMC_OBF[0], label="Order Block volatilite filtresi")
1714
- smc_lvl = gr.Checkbox(value=True, label="SMC seviyeleri TP/SL'de kullan (Order Block + önceki gün/hafta/ay H-L)")
1715
- with gr.Row():
1716
- skip_overlap = gr.Checkbox(value=False, label="Açık pozisyon varken yeni sinyali atla (tek pozisyon)")
1717
- whole_units = gr.Checkbox(value=False, label="Miktarı tam sayıya yuvarla (hisse)")
1718
- run = gr.Button("Öğren, optimize et ve test et", variant="primary")
1719
-
1720
- with gr.Tabs():
1721
- with gr.Tab("Sinyal"):
1722
- output = gr.Markdown()
1723
- prob_plot = gr.Plot()
1724
- with gr.Tab("Grafik"):
1725
- chart = gr.Plot()
1726
- with gr.Tab("OOS backtest"):
1727
- pf_plot = gr.Plot()
1728
- equity_plot = gr.Plot()
1729
- with gr.Tab("Monte Carlo"):
1730
- mc_plot = gr.Plot()
1731
- with gr.Tab("TP/SL optimizasyon"):
1732
- optimization = gr.Dataframe(label="En iyi TP/SL kombinasyonları", interactive=False)
1733
- with gr.Tab("İşlem kaydı"):
1734
- csv_file = gr.File(label="OOS işlemleri (CSV)", interactive=False)
1735
- disclaimer = gr.Markdown()
1736
-
1737
- run.click(
1738
- run_engine,
1739
- inputs=[ticker, period, horizon, side_mode, equity, min_risk, max_risk,
1740
- max_leverage, fee, slip, train_window,
1741
- p_min, p_gap, flat_max, agree_min, bacc_min, min_trades,
1742
- skip_overlap, whole_units, ind_mode, st_period, st_mult, ut_key, ut_period,
1743
- vp_mode, vp_window, vp_bins, vp_va, vp_ext,
1744
- ldc_mode, ldc_k, ldc_back, ldc_regime, ldc_kern_lb, ldc_kern_rw,
1745
- ldc_use_vol, ldc_use_regime, ldc_use_adx, ldc_kernel, ldc_exact,
1746
- combo_mode, combo_votes, ind_tpsl, tpsl_smin, tpsl_smax, tpsl_minrr,
1747
- gann_mode, gann_lb, gann_fan_on, gann_ext_on, gann_rev,
1748
- smc_mode, smc_src, smc_swing_len, smc_int_len, smc_zone, smc_draw,
1749
- smc_conf, smc_obf, smc_lvl],
1750
- outputs=[output, prob_plot, chart, equity_plot, pf_plot, mc_plot, optimization,
1751
- csv_file, disclaimer],
1752
- )
1753
-
1754
- if __name__ == "__main__":
1755
- demo.launch(server_name="0.0.0.0", server_port=7860)