Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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 |
)
|