GoshawkVortexAI commited on
Commit
7523d98
·
verified ·
1 Parent(s): 1d92f9c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +260 -6
app.py CHANGED
@@ -33,6 +33,8 @@ COMBO_MODES = ["Kapalı", "Onay filtresi (KNN + birleşik)", "Sadece birleşik s
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
  GANN_LEVELS = [0.0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0]
37
  GANN_FAN = [(8, "8x1"), (4, "4x1"), (3, "3x1"), (2, "2x1"), (1, "1x1"),
38
  (1/2, "1x2"), (1/3, "1x3"), (1/4, "1x4"), (1/8, "1x8")]
@@ -151,6 +153,201 @@ def ut_bot(df, key=1.0, period=10):
151
 
152
 
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  # ------------------------------------------- Gann Box (Zeiierman) Python portu
155
  def gann_box(df, lookback=150, reverse=False, levels=GANN_LEVELS):
156
  """Görünen aralıktaki (son `lookback` mum) en yüksek / en düşük noktadan Gann Box kurar.
@@ -631,7 +828,7 @@ TRADE_COLS = ["entry_date", "exit_date", "side", "entry_price", "exit_reason",
631
 
632
  # ---------------------------------------------------------------- grafikler
633
  def price_figure(df, ticker, trades_df, trade_ok, stop, target, ind=None, vp=None, vp_prof=None,
634
- ldc=None, combo=None, gann=None, gann_fan=True, gann_extend=True):
635
  w = df.tail(150)
636
  sub = vp is not None and vp_prof is not None
637
  if sub:
@@ -706,13 +903,15 @@ def price_figure(df, ticker, trades_df, trade_ok, stop, target, ind=None, vp=Non
706
  color=["#16a34a" if r > 0 else "#dc2626" for r in s.R],
707
  line=dict(width=1, color="white")),
708
  text=[f"R={r:.2f} · {why}" for r, why in zip(s.R, s.exit_reason)]), **kw)
 
 
709
  if gann is not None:
710
  add_gann_traces(fig, gann, df.index, extend=gann_extend, fan=gann_fan, kw=kw)
711
  if trade_ok:
712
  fig.add_hline(y=stop, line_dash="dash", line_color="#dc2626", annotation_text="SL", **kw)
713
  fig.add_hline(y=target, line_dash="dash", line_color="#16a34a", annotation_text="TP", **kw)
714
  fig.update_layout(title=f"{ticker} Günlük — son 150 mum (üçgenler: OOS sinyalleri)"
715
- + (" + Volume Profile" if sub else "") + (" + Gann Box" if gann is not None else ""),
716
  height=560, xaxis_rangeslider_visible=False)
717
  return fig
718
 
@@ -781,7 +980,8 @@ def run_engine(ticker, period, horizon, side_mode, equity, min_risk, max_risk,
781
  ldc_mode, ldc_k, ldc_back, ldc_regime, ldc_kern_lb, ldc_kern_rw,
782
  ldc_use_vol, ldc_use_regime, ldc_use_adx, ldc_kernel, ldc_exact,
783
  combo_mode, combo_votes, ind_tpsl, tpsl_smin, tpsl_smax, tpsl_minrr,
784
- gann_mode, gann_lb, gann_fan_on, gann_ext_on, gann_rev):
 
785
  try:
786
  ticker = str(ticker).strip().upper()
787
  h = int(horizon)
@@ -810,6 +1010,16 @@ def run_engine(ticker, period, horizon, side_mode, equity, min_risk, max_risk,
810
  gann_arr, gann_pos = gann_signal(df, int(gann_lb))
811
  gann_now = int(gann_arr[-1])
812
  GB = gann_box(df, int(gann_lb), bool(gann_rev))
 
 
 
 
 
 
 
 
 
 
813
 
814
  valid = X_all.notna().all(axis=1) & y_all.notna()
815
  X = X_all.loc[valid].copy()
@@ -943,6 +1153,8 @@ def run_engine(ticker, period, horizon, side_mode, equity, min_risk, max_risk,
943
  f_ = [i for i in f_ if vp_arr[i] == s_]
944
  if gann_on:
945
  f_ = [i for i in f_ if gann_arr[i] == s_]
 
 
946
  return f_ if len(f_) >= 35 else dev_pos
947
  for s in (1, -1):
948
  if allow[s]:
@@ -984,6 +1196,8 @@ def run_engine(ticker, period, horizon, side_mode, equity, min_risk, max_risk,
984
  continue
985
  if gann_on and gann_arr[i] != pred:
986
  continue
 
 
987
  if ldc_confirm and ldc_arr[i] != pred:
988
  continue
989
  if combo_on and combo_arr[i] != pred:
@@ -1076,6 +1290,9 @@ def run_engine(ticker, period, horizon, side_mode, equity, min_risk, max_risk,
1076
  "yönle aynı", int(vp_arr[-1]) == side))
1077
  if gann_on:
1078
  checks.insert(1, ("Gann Box (%50 konumu)", dname[gann_now], "yönle aynı", gann_now == side))
 
 
 
1079
  trade_ok = all(c[3] for c in checks)
1080
  if trade_ok:
1081
  signal = "LONG" if side == 1 else "SHORT"
@@ -1128,6 +1345,30 @@ def run_engine(ticker, period, horizon, side_mode, equity, min_risk, max_risk,
1128
  f"uzama sınırı {float(vp_ext):g} ATR."
1129
  + (" Hacim verisi yok → zaman bazlı profil kullanıldı." if vp_no_vol else "")
1130
  + (" Mod Kapalı: yalnızca bilgi amaçlı." if not vp_on else ""))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1131
  if GB is not None:
1132
  gpos = gann_pos[-1]
1133
  gann_md = md_table(["Seviye", "Fiyat"],
@@ -1208,6 +1449,11 @@ def run_engine(ticker, period, horizon, side_mode, equity, min_risk, max_risk,
1208
 
1209
  {vp_note}
1210
 
 
 
 
 
 
1211
  ## Gann Box (Zeiierman) — mod: {gann_mode}
1212
  {gann_md}
1213
 
@@ -1266,7 +1512,7 @@ def run_engine(ticker, period, horizon, side_mode, equity, min_risk, max_risk,
1266
  ind=(st_line, st_dir, ut_stop),
1267
  vp=vp if (vp_on or combo_on) else None, vp_prof=lp,
1268
  ldc=LD, combo=combo_arr if combo_on else None,
1269
- gann=GB, gann_fan=bool(gann_fan_on), gann_extend=bool(gann_ext_on))
1270
  eq_fig = equity_figure(curve, dds) if len(R) else None
1271
  pf_fig = pf_gauge(stats["pf"], stats["n"]) if stats else None
1272
  mc_fig = mc_figure(mc) if mc else None
@@ -1282,7 +1528,7 @@ def run_engine(ticker, period, horizon, side_mode, equity, min_risk, max_risk,
1282
  "seri korelasyonunu ve rejim değişimini tam modellemez. Pozisyon hesabı spot miktar "
1283
  "varsayar; CFD lot sözleşmesi, spread, funding, swap, komisyon, minimum lot, marjin ve "
1284
  "broker fiyatı farklı olabilir. Gap ve kayma kaybı planlanan riskin üstüne çıkarabilir. "
1285
- "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. Canlı hesap öncesi paper trading ve broker bazlı doğrulama zorunludur."
1286
  )
1287
  return (report, prob_figure(p_down, p_flat, p_up), price_fig, eq_fig, pf_fig, mc_fig,
1288
  top_df, csv_path, disclaimer)
@@ -1313,6 +1559,7 @@ with gr.Blocks(title=APP_TITLE, theme=gr.themes.Soft()) as demo:
1313
  combo_mode = gr.Radio(COMBO_MODES, value=COMBO_MODES[2],
1314
  label="Birleşik sinyal (ST+UT · Lorentzian · Volume Profile oylaması)")
1315
  gann_mode = gr.Radio(GANN_MODES, value=GANN_MODES[0], label="Gann Box modu (Zeiierman)")
 
1316
  ind_tpsl = gr.Checkbox(value=True, label="TP/SL'i indikatörlerden türet (ST / UT / VP seviyeleri)")
1317
  train_window = gr.Slider(300, 1500, value=900, step=50, label="Maksimum eğitim penceresi")
1318
  with gr.Column():
@@ -1364,6 +1611,12 @@ with gr.Blocks(title=APP_TITLE, theme=gr.themes.Soft()) as demo:
1364
  gann_fan_on = gr.Checkbox(value=True, label="Gann Fan göster")
1365
  gann_ext_on = gr.Checkbox(value=True, label="Kutuyu geleceğe uzat (Extend)")
1366
  gann_rev = gr.Checkbox(value=False, label="Reverse (yönü çevir)")
 
 
 
 
 
 
1367
  with gr.Row():
1368
  skip_overlap = gr.Checkbox(value=False, label="Açık pozisyon varken yeni sinyali atla (tek pozisyon)")
1369
  whole_units = gr.Checkbox(value=False, label="Miktarı tam sayıya yuvarla (hisse)")
@@ -1396,7 +1649,8 @@ with gr.Blocks(title=APP_TITLE, theme=gr.themes.Soft()) as demo:
1396
  ldc_mode, ldc_k, ldc_back, ldc_regime, ldc_kern_lb, ldc_kern_rw,
1397
  ldc_use_vol, ldc_use_regime, ldc_use_adx, ldc_kernel, ldc_exact,
1398
  combo_mode, combo_votes, ind_tpsl, tpsl_smin, tpsl_smax, tpsl_minrr,
1399
- gann_mode, gann_lb, gann_fan_on, gann_ext_on, gann_rev],
 
1400
  outputs=[output, prob_plot, chart, equity_plot, pf_plot, mc_plot, optimization,
1401
  csv_file, disclaimer],
1402
  )
 
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ü)"]
37
+ SMC_SRC = ["Swing yapı", "Internal yapı"]
38
  GANN_LEVELS = [0.0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0]
39
  GANN_FAN = [(8, "8x1"), (4, "4x1"), (3, "3x1"), (2, "2x1"), (1, "1x1"),
40
  (1/2, "1x2"), (1/3, "1x3"), (1/4, "1x4"), (1/8, "1x8")]
 
153
 
154
 
155
 
156
+
157
+ # ------------------------------------------- Smart Money Concepts (LuxAlgo) Python portu
158
+ def _smc_pivots(h, l, size):
159
+ """Pine leg(size) mantığı: pivotlar `size` mum sonra doğrulanır (ileriye bakma yok).
160
+ Dönüş: {doğrulama_mumu: [("H"|"L", seviye, pivot_mumu)]}"""
161
+ n = len(h)
162
+ hs, ls = pd.Series(h), pd.Series(l)
163
+ hmax = hs.rolling(size).max().to_numpy(float)
164
+ lmin = ls.rolling(size).min().to_numpy(float)
165
+ out, leg = {}, 0
166
+ for i in range(size, n):
167
+ new_leg = leg
168
+ if h[i-size] > hmax[i]:
169
+ new_leg = 0
170
+ elif l[i-size] < lmin[i]:
171
+ new_leg = 1
172
+ if new_leg != leg:
173
+ out.setdefault(i, []).append(("L", float(l[i-size]), i-size) if new_leg == 1
174
+ else ("H", float(h[i-size]), i-size))
175
+ leg = new_leg
176
+ return out
177
+
178
+
179
+ def smc_analyze(df, swing_len=50, int_len=5, eq_len=3, eq_thr=0.1, ob_keep=5):
180
+ """LuxAlgo 'Smart Money Concepts' göstergesinin Python portu: swing/internal yapı (BOS, CHoCH),
181
+ order block, EQH/EQL, Fair Value Gap, premium/discount ve strong/weak high-low.
182
+ Pine kaynağından birebir değil; her mum yalnızca kendisine kadarki veriyi kullanır."""
183
+ n = len(df)
184
+ o, h, l, c = (df[k].to_numpy(float) for k in ("Open", "High", "Low", "Close"))
185
+ atr = calc_atr(df, 200).to_numpy(float)
186
+ hv = (h-l) >= 2*atr # yüksek volatilite mumları: OB için high/low yer değiştirir
187
+ ph, pl = np.where(hv, l, h), np.where(hv, h, l)
188
+ swing_len = int(min(max(swing_len, 3), max(n//4, 3)))
189
+ int_len = int(min(max(int_len, 2), swing_len))
190
+ P_s, P_i, P_e = (_smc_pivots(h, l, k) for k in (swing_len, int_len, int(eq_len)))
191
+
192
+ new = lambda: {"lvl": np.nan, "crossed": True, "idx": -1}
193
+ sh, sl_, ih, il = new(), new(), new(), new()
194
+ s_tr = np.zeros(n, dtype=int)
195
+ i_tr = np.zeros(n, dtype=int)
196
+ top_a, bot_a = np.full(n, np.nan), np.full(n, np.nan)
197
+ st = it = 0
198
+ top, bot = h[0], l[0]
199
+ events, obs, points, eqs, fvgs = [], [], [], [], []
200
+ prev_hi = prev_lo = np.nan
201
+ eq_h = eq_l = None
202
+ dsum = 0.0
203
+ for i in range(n):
204
+ # --- swing / internal pivotlar
205
+ for kind, lvl, bi in P_s.get(i, []):
206
+ if kind == "H":
207
+ points.append((bi, lvl, "HH" if (np.isfinite(prev_hi) and lvl > prev_hi) else "LH", "H"))
208
+ prev_hi = lvl
209
+ sh.update(lvl=lvl, crossed=False, idx=bi)
210
+ top = lvl
211
+ else:
212
+ points.append((bi, lvl, "LL" if (np.isfinite(prev_lo) and lvl < prev_lo) else "HL", "L"))
213
+ prev_lo = lvl
214
+ sl_.update(lvl=lvl, crossed=False, idx=bi)
215
+ bot = lvl
216
+ for kind, lvl, bi in P_i.get(i, []):
217
+ (ih if kind == "H" else il).update(lvl=lvl, crossed=False, idx=bi)
218
+ # --- EQH / EQL
219
+ for kind, lvl, bi in P_e.get(i, []):
220
+ thr = eq_thr*atr[i] if np.isfinite(atr[i]) else 0.0
221
+ if kind == "H":
222
+ if eq_h is not None and abs(eq_h[0]-lvl) < thr:
223
+ eqs.append(("EQH", eq_h[1], eq_h[0], bi, lvl, i))
224
+ eq_h = (lvl, bi)
225
+ else:
226
+ if eq_l is not None and abs(eq_l[0]-lvl) < thr:
227
+ eqs.append(("EQL", eq_l[1], eq_l[0], bi, lvl, i))
228
+ eq_l = (lvl, bi)
229
+ # --- order block mitigasyonu (High/Low)
230
+ for ob in obs:
231
+ if ob["on"] and ((ob["bull"] and l[i] < ob["lo"]) or (not ob["bull"] and h[i] > ob["hi"])):
232
+ ob["on"] = False
233
+ # --- yapı kırılımları
234
+ if i > 0:
235
+ for internal, hp, lp in ((True, ih, il), (False, sh, sl_)):
236
+ tr = it if internal else st
237
+ extra_u = (not internal) or (ih["lvl"] != sh["lvl"])
238
+ extra_d = (not internal) or (il["lvl"] != sl_["lvl"])
239
+ if (np.isfinite(hp["lvl"]) and not hp["crossed"] and c[i] > hp["lvl"] >= c[i-1]
240
+ and extra_u):
241
+ events.append({"i": i, "kind": "CHoCH" if tr == -1 else "BOS", "bull": True,
242
+ "internal": internal, "lvl": hp["lvl"], "from": hp["idx"]})
243
+ hp["crossed"] = True
244
+ seg = pl[hp["idx"]:i]
245
+ if len(seg):
246
+ j = hp["idx"] + int(np.argmin(seg))
247
+ obs.append({"bull": True, "internal": internal, "hi": float(ph[j]),
248
+ "lo": float(pl[j]), "idx": j, "on": True})
249
+ if internal:
250
+ it = 1
251
+ else:
252
+ st = 1
253
+ if (np.isfinite(lp["lvl"]) and not lp["crossed"] and c[i] < lp["lvl"] <= c[i-1]
254
+ and extra_d):
255
+ events.append({"i": i, "kind": "CHoCH" if tr == 1 else "BOS", "bull": False,
256
+ "internal": internal, "lvl": lp["lvl"], "from": lp["idx"]})
257
+ lp["crossed"] = True
258
+ seg = ph[lp["idx"]:i]
259
+ if len(seg):
260
+ j = lp["idx"] + int(np.argmax(seg))
261
+ obs.append({"bull": False, "internal": internal, "hi": float(ph[j]),
262
+ "lo": float(pl[j]), "idx": j, "on": True})
263
+ if internal:
264
+ it = -1
265
+ else:
266
+ st = -1
267
+ s_tr[i], i_tr[i] = st, it
268
+ top, bot = max(top, h[i]), min(bot, l[i])
269
+ top_a[i], bot_a[i] = top, bot
270
+ # --- Fair Value Gap
271
+ if i >= 2:
272
+ delta = (c[i-1]-o[i-1])/(o[i-1]*100) if o[i-1] else 0.0
273
+ dsum += abs(delta)
274
+ thr = dsum/max(i, 1)*2
275
+ for g in fvgs:
276
+ if g["on"] and ((g["bull"] and l[i] < g["bot"]) or (not g["bull"] and h[i] > g["top"])):
277
+ g["on"] = False
278
+ if l[i] > h[i-2] and c[i-1] > h[i-2] and delta > thr:
279
+ fvgs.append({"bull": True, "top": float(l[i]), "bot": float(h[i-2]), "idx": i-1, "on": True})
280
+ elif h[i] < l[i-2] and c[i-1] < l[i-2] and -delta > thr:
281
+ fvgs.append({"bull": False, "top": float(l[i-2]), "bot": float(h[i]), "idx": i-1, "on": True})
282
+ live = lambda internal, bull: [o_ for o_ in obs if o_["on"] and o_["internal"] == internal
283
+ and o_["bull"] == bull][-int(ob_keep):]
284
+ return {"swing_trend": s_tr, "internal_trend": i_tr, "top": top_a, "bot": bot_a,
285
+ "events": events, "points": points, "eq": eqs,
286
+ "obs": live(True, True)+live(True, False)+live(False, True)+live(False, False),
287
+ "fvg": [g for g in fvgs if g["on"]][-8:], "strong_high": st == -1, "strong_low": st == 1}
288
+
289
+
290
+ def add_smc_traces(fig, S, index, last_n=150, kw=None):
291
+ """SMC katmanlarını son `last_n` mumluk grafiğe ekler."""
292
+ kw = kw or {}
293
+ n = len(index)
294
+ w0 = max(0, n-int(last_n))
295
+ xi = lambda k: index[min(max(int(k), w0), n-1)]
296
+ for e in S["events"]:
297
+ if e["i"] < w0:
298
+ continue
299
+ col = "#16a34a" if e["bull"] else "#dc2626"
300
+ xa, xb = xi(e["from"]), xi(e["i"])
301
+ fig.add_trace(go.Scatter(x=[xa, xb], y=[e["lvl"], e["lvl"]], mode="lines",
302
+ line=dict(width=1 if e["internal"] else 2, color=col,
303
+ dash="dash" if e["internal"] else "solid"),
304
+ showlegend=False, hoverinfo="skip"), **kw)
305
+ mid = xa + (xb-xa)/2
306
+ fig.add_trace(go.Scatter(x=[mid], y=[e["lvl"]], mode="text", text=[e["kind"]],
307
+ textposition="top center" if e["bull"] else "bottom center",
308
+ textfont=dict(size=9 if e["internal"] else 11, color=col),
309
+ showlegend=False, hoverinfo="skip"), **kw)
310
+ for ob in S["obs"]:
311
+ col = "34,197,94" if ob["bull"] else "239,68,68"
312
+ fig.add_shape(type="rect", x0=xi(ob["idx"]), x1=index[-1], y0=ob["lo"], y1=ob["hi"],
313
+ line=dict(width=1 if ob["internal"] else 0, color=f"rgb({col})"),
314
+ fillcolor=f"rgba({col},{0.12 if ob['internal'] else 0.22})", layer="below", **kw)
315
+ for g in S["fvg"]:
316
+ if g["idx"] < w0:
317
+ continue
318
+ col = "34,197,94" if g["bull"] else "239,68,68"
319
+ fig.add_shape(type="rect", x0=xi(g["idx"]), x1=index[-1], y0=g["bot"], y1=g["top"],
320
+ line=dict(width=0), fillcolor=f"rgba({col},0.10)", layer="below", **kw)
321
+ for kind, xa_i, la, xb_i, lb, _ in S["eq"]:
322
+ if xb_i < w0:
323
+ continue
324
+ fig.add_trace(go.Scatter(x=[xi(xa_i), xi(xb_i)], y=[la, lb], mode="lines+text",
325
+ text=["", kind], textposition="top center" if kind == "EQH" else "bottom center",
326
+ line=dict(width=1, dash="dot", color="#64748b"),
327
+ textfont=dict(size=10, color="#64748b"), showlegend=False,
328
+ hoverinfo="skip"), **kw)
329
+ for bi, lvl, lab, kd in S["points"]:
330
+ if bi >= w0:
331
+ fig.add_trace(go.Scatter(x=[index[bi]], y=[lvl], mode="text", text=[lab],
332
+ textposition="top center" if kd == "H" else "bottom center",
333
+ textfont=dict(size=9, color="#475569"), showlegend=False,
334
+ hoverinfo="skip"), **kw)
335
+ top, bot = float(S["top"][-1]), float(S["bot"][-1])
336
+ if np.isfinite(top) and np.isfinite(bot) and top > bot:
337
+ x0 = index[max(0, n-35)]
338
+ rng_ = top-bot
339
+ for y0, y1, col, nm in [(top, top-0.05*rng_, "239,68,68", "Premium"),
340
+ (bot+0.525*rng_, bot+0.475*rng_, "148,163,184", "Equilibrium"),
341
+ (bot+0.05*rng_, bot, "34,197,94", "Discount")]:
342
+ fig.add_shape(type="rect", x0=x0, x1=index[-1], y0=y0, y1=y1, line=dict(width=0),
343
+ fillcolor=f"rgba({col},0.16)", layer="below", **kw)
344
+ for y, nm in [(top, "Strong High" if S["strong_high"] else "Weak High"),
345
+ (bot, "Strong Low" if S["strong_low"] else "Weak Low")]:
346
+ fig.add_trace(go.Scatter(x=[x0, index[-1]], y=[y, y], mode="lines+text", text=["", nm],
347
+ textposition="top left", line=dict(width=1, color="#64748b"),
348
+ textfont=dict(size=9, color="#64748b"), showlegend=False,
349
+ hoverinfo="skip"), **kw)
350
+
351
  # ------------------------------------------- Gann Box (Zeiierman) Python portu
352
  def gann_box(df, lookback=150, reverse=False, levels=GANN_LEVELS):
353
  """Görünen aralıktaki (son `lookback` mum) en yüksek / en düşük noktadan Gann Box kurar.
 
828
 
829
  # ---------------------------------------------------------------- grafikler
830
  def price_figure(df, ticker, trades_df, trade_ok, stop, target, ind=None, vp=None, vp_prof=None,
831
+ ldc=None, combo=None, gann=None, gann_fan=True, gann_extend=True, smc=None):
832
  w = df.tail(150)
833
  sub = vp is not None and vp_prof is not None
834
  if sub:
 
903
  color=["#16a34a" if r > 0 else "#dc2626" for r in s.R],
904
  line=dict(width=1, color="white")),
905
  text=[f"R={r:.2f} · {why}" for r, why in zip(s.R, s.exit_reason)]), **kw)
906
+ if smc is not None:
907
+ add_smc_traces(fig, smc, df.index, last_n=150, kw=kw)
908
  if gann is not None:
909
  add_gann_traces(fig, gann, df.index, extend=gann_extend, fan=gann_fan, kw=kw)
910
  if trade_ok:
911
  fig.add_hline(y=stop, line_dash="dash", line_color="#dc2626", annotation_text="SL", **kw)
912
  fig.add_hline(y=target, line_dash="dash", line_color="#16a34a", annotation_text="TP", **kw)
913
  fig.update_layout(title=f"{ticker} Günlük — son 150 mum (üçgenler: OOS sinyalleri)"
914
+ + (" + Volume Profile" if sub else "") + (" + Gann Box" if gann is not None else "") + (" + SMC" if smc is not None else ""),
915
  height=560, xaxis_rangeslider_visible=False)
916
  return fig
917
 
 
980
  ldc_mode, ldc_k, ldc_back, ldc_regime, ldc_kern_lb, ldc_kern_rw,
981
  ldc_use_vol, ldc_use_regime, ldc_use_adx, ldc_kernel, ldc_exact,
982
  combo_mode, combo_votes, ind_tpsl, tpsl_smin, tpsl_smax, tpsl_minrr,
983
+ gann_mode, gann_lb, gann_fan_on, gann_ext_on, gann_rev,
984
+ smc_mode, smc_src, smc_swing_len, smc_int_len, smc_zone, smc_draw):
985
  try:
986
  ticker = str(ticker).strip().upper()
987
  h = int(horizon)
 
1010
  gann_arr, gann_pos = gann_signal(df, int(gann_lb))
1011
  gann_now = int(gann_arr[-1])
1012
  GB = gann_box(df, int(gann_lb), bool(gann_rev))
1013
+ smc_on = smc_mode != SMC_MODES[0]
1014
+ SM = smc_analyze(df, int(smc_swing_len), int(smc_int_len)) if (smc_on or smc_draw) else None
1015
+ smc_arr = np.zeros(len(df), dtype=int)
1016
+ if SM is not None:
1017
+ smc_arr = (SM["swing_trend"] if smc_src == SMC_SRC[0] else SM["internal_trend"]).copy()
1018
+ if smc_zone: # long yalnızca discount, short yalnızca premium bölgesinde
1019
+ mid_ = (SM["top"]+SM["bot"])/2
1020
+ zone_ = np.where(df.Close.to_numpy(float) < mid_, 1, np.where(df.Close.to_numpy(float) > mid_, -1, 0))
1021
+ smc_arr = np.where(zone_ == smc_arr, smc_arr, 0).astype(int)
1022
+ smc_now = int(smc_arr[-1])
1023
 
1024
  valid = X_all.notna().all(axis=1) & y_all.notna()
1025
  X = X_all.loc[valid].copy()
 
1153
  f_ = [i for i in f_ if vp_arr[i] == s_]
1154
  if gann_on:
1155
  f_ = [i for i in f_ if gann_arr[i] == s_]
1156
+ if smc_on:
1157
+ f_ = [i for i in f_ if smc_arr[i] == s_]
1158
  return f_ if len(f_) >= 35 else dev_pos
1159
  for s in (1, -1):
1160
  if allow[s]:
 
1196
  continue
1197
  if gann_on and gann_arr[i] != pred:
1198
  continue
1199
+ if smc_on and smc_arr[i] != pred:
1200
+ continue
1201
  if ldc_confirm and ldc_arr[i] != pred:
1202
  continue
1203
  if combo_on and combo_arr[i] != pred:
 
1290
  "yönle aynı", int(vp_arr[-1]) == side))
1291
  if gann_on:
1292
  checks.insert(1, ("Gann Box (%50 konumu)", dname[gann_now], "yönle aynı", gann_now == side))
1293
+ if smc_on:
1294
+ checks.insert(1, (f"SMC yapı yönü ({smc_src}" + (" + premium/discount)" if smc_zone else ")"),
1295
+ dname[smc_now], "yönle aynı", smc_now == side))
1296
  trade_ok = all(c[3] for c in checks)
1297
  if trade_ok:
1298
  signal = "LONG" if side == 1 else "SHORT"
 
1345
  f"uzama sınırı {float(vp_ext):g} ATR."
1346
  + (" Hacim verisi yok → zaman bazlı profil kullanıldı." if vp_no_vol else "")
1347
  + (" Mod Kapalı: yalnızca bilgi amaçlı." if not vp_on else ""))
1348
+ if SM is not None:
1349
+ last_ev = [e for e in SM["events"] if not e["internal"]][-1:] or [None]
1350
+ last_iv = [e for e in SM["events"] if e["internal"]][-1:] or [None]
1351
+ evtxt = lambda e: "—" if e is None else (f"{e['kind']} {'↑' if e['bull'] else '↓'} · {fmt_p(e['lvl'])} · "
1352
+ f"{df.index[e['i']].date()}")
1353
+ tp_, bt_ = float(SM["top"][-1]), float(SM["bot"][-1])
1354
+ zpos = (entry-bt_)/(tp_-bt_) if tp_ > bt_ else float("nan")
1355
+ zname = "—" if not np.isfinite(zpos) else ("Premium" if zpos > 0.525 else "Discount" if zpos < 0.475 else "Equilibrium")
1356
+ obt = lambda b: [o_ for o_ in SM["obs"] if o_["bull"] == b and not o_["internal"]][-1:]
1357
+ ob_txt = lambda b: (f"{fmt_p(obt(b)[0]['lo'])} – {fmt_p(obt(b)[0]['hi'])}" if obt(b) else "—")
1358
+ smc_md = md_table(["Bileşen", "Değer"], [
1359
+ ("Swing yapı yönü", dname[int(SM["swing_trend"][-1])]),
1360
+ ("Internal yapı yönü", dname[int(SM["internal_trend"][-1])]),
1361
+ ("Son swing kırılımı", evtxt(last_ev[0])), ("Son internal kırılımı", evtxt(last_iv[0])),
1362
+ ("Premium / Discount", f"{zname}" + (f" (%{zpos*100:.0f})" if np.isfinite(zpos) else "")),
1363
+ ("Strong/Weak High · Low", f"{'Strong' if SM['strong_high'] else 'Weak'} {fmt_p(tp_)} · "
1364
+ f"{'Strong' if SM['strong_low'] else 'Weak'} {fmt_p(bt_)}"),
1365
+ ("Aktif swing OB (bull)", ob_txt(True)), ("Aktif swing OB (bear)", ob_txt(False)),
1366
+ ("Açık FVG sayısı", len(SM["fvg"])), ("SMC yön onayı", dname[smc_now])])
1367
+ smc_note = (f"Swing {int(smc_swing_len)} · internal {int(smc_int_len)} mum"
1368
+ + (" · premium/discount kapısı açık" if smc_zone else "")
1369
+ + (" · Mod Kapalı: yalnızca bilgi/grafik." if not smc_on else "") + ".")
1370
+ else:
1371
+ smc_md, smc_note = "_Mod kapalı._", ""
1372
  if GB is not None:
1373
  gpos = gann_pos[-1]
1374
  gann_md = md_table(["Seviye", "Fiyat"],
 
1449
 
1450
  {vp_note}
1451
 
1452
+ ## Smart Money Concepts (LuxAlgo) — mod: {smc_mode}
1453
+ {smc_md}
1454
+
1455
+ {smc_note}
1456
+
1457
  ## Gann Box (Zeiierman) — mod: {gann_mode}
1458
  {gann_md}
1459
 
 
1512
  ind=(st_line, st_dir, ut_stop),
1513
  vp=vp if (vp_on or combo_on) else None, vp_prof=lp,
1514
  ldc=LD, combo=combo_arr if combo_on else None,
1515
+ gann=GB, gann_fan=bool(gann_fan_on), gann_extend=bool(gann_ext_on), smc=SM)
1516
  eq_fig = equity_figure(curve, dds) if len(R) else None
1517
  pf_fig = pf_gauge(stats["pf"], stats["n"]) if stats else None
1518
  mc_fig = mc_figure(mc) if mc else None
 
1528
  "seri korelasyonunu ve rejim değişimini tam modellemez. Pozisyon hesabı spot miktar "
1529
  "varsayar; CFD lot sözleşmesi, spread, funding, swap, komisyon, minimum lot, marjin ve "
1530
  "broker fiyatı farklı olabilir. Gap ve kayma kaybı planlanan riskin üstüne çıkarabilir. "
1531
+ "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; MTF önceki high/low ve mum boyama yoktur. 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."
1532
  )
1533
  return (report, prob_figure(p_down, p_flat, p_up), price_fig, eq_fig, pf_fig, mc_fig,
1534
  top_df, csv_path, disclaimer)
 
1559
  combo_mode = gr.Radio(COMBO_MODES, value=COMBO_MODES[2],
1560
  label="Birleşik sinyal (ST+UT · Lorentzian · Volume Profile oylaması)")
1561
  gann_mode = gr.Radio(GANN_MODES, value=GANN_MODES[0], label="Gann Box modu (Zeiierman)")
1562
+ smc_mode = gr.Radio(SMC_MODES, value=SMC_MODES[0], label="Smart Money Concepts modu (LuxAlgo)")
1563
  ind_tpsl = gr.Checkbox(value=True, label="TP/SL'i indikatörlerden türet (ST / UT / VP seviyeleri)")
1564
  train_window = gr.Slider(300, 1500, value=900, step=50, label="Maksimum eğitim penceresi")
1565
  with gr.Column():
 
1611
  gann_fan_on = gr.Checkbox(value=True, label="Gann Fan göster")
1612
  gann_ext_on = gr.Checkbox(value=True, label="Kutuyu geleceğe uzat (Extend)")
1613
  gann_rev = gr.Checkbox(value=False, label="Reverse (yönü çevir)")
1614
+ with gr.Row():
1615
+ smc_src = gr.Radio(SMC_SRC, value=SMC_SRC[0], label="SMC yön kaynağı")
1616
+ smc_swing_len = gr.Slider(10, 100, value=50, step=5, label="SMC swing yapı uzunluğu")
1617
+ smc_int_len = gr.Slider(3, 15, value=5, step=1, label="SMC internal yapı uzunluğu")
1618
+ smc_zone = gr.Checkbox(value=False, label="Premium/Discount kapısı (long=discount, short=premium)")
1619
+ smc_draw = gr.Checkbox(value=True, label="SMC katmanlarını grafikte göster")
1620
  with gr.Row():
1621
  skip_overlap = gr.Checkbox(value=False, label="Açık pozisyon varken yeni sinyali atla (tek pozisyon)")
1622
  whole_units = gr.Checkbox(value=False, label="Miktarı tam sayıya yuvarla (hisse)")
 
1649
  ldc_mode, ldc_k, ldc_back, ldc_regime, ldc_kern_lb, ldc_kern_rw,
1650
  ldc_use_vol, ldc_use_regime, ldc_use_adx, ldc_kernel, ldc_exact,
1651
  combo_mode, combo_votes, ind_tpsl, tpsl_smin, tpsl_smax, tpsl_minrr,
1652
+ gann_mode, gann_lb, gann_fan_on, gann_ext_on, gann_rev,
1653
+ smc_mode, smc_src, smc_swing_len, smc_int_len, smc_zone, smc_draw],
1654
  outputs=[output, prob_plot, chart, equity_plot, pf_plot, mc_plot, optimization,
1655
  csv_file, disclaimer],
1656
  )