Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -257,6 +257,19 @@ def create_features(data: pd.DataFrame) -> pd.DataFrame:
|
|
| 257 |
np.sign(df['LagRet_3'])
|
| 258 |
) / 3.0
|
| 259 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 260 |
return df.dropna()
|
| 261 |
|
| 262 |
|
|
@@ -299,6 +312,10 @@ FEATURE_COLS = [
|
|
| 299 |
'Volume_Ratio', 'OBV_Signal',
|
| 300 |
# Direction composite (FIX 4)
|
| 301 |
'Direction_Score', 'Momentum_Consistency',
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
]
|
| 303 |
|
| 304 |
|
|
@@ -322,13 +339,20 @@ def build_ridge_pipeline(alpha: float) -> Pipeline:
|
|
| 322 |
def select_best_alpha(X_tr, y_tr) -> float:
|
| 323 |
"""
|
| 324 |
RidgeCV finds optimal alpha from a fine logarithmic grid.
|
| 325 |
-
|
|
|
|
|
|
|
| 326 |
"""
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 330 |
rcv = RidgeCV(alphas=alphas, cv=5, scoring='neg_mean_absolute_error')
|
| 331 |
-
rcv.fit(
|
| 332 |
return float(rcv.alpha_)
|
| 333 |
|
| 334 |
|
|
@@ -336,15 +360,17 @@ def train_ridge(df: pd.DataFrame, horizon: int = 1):
|
|
| 336 |
"""
|
| 337 |
Trains the premium Ridge model.
|
| 338 |
Target: log-return over `horizon` days (direction-aware).
|
| 339 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 340 |
"""
|
| 341 |
avail = [c for c in FEATURE_COLS if c in df.columns]
|
| 342 |
X = df[avail].values
|
| 343 |
close = df['Close'].values
|
| 344 |
|
| 345 |
-
# -
|
| 346 |
-
# log-return = log(close[t+horizon] / close[t])
|
| 347 |
-
# This directly encodes direction: positive = UP, negative = DOWN
|
| 348 |
y_logret = np.log(close[horizon:] / close[:len(close) - horizon])
|
| 349 |
|
| 350 |
X = X[:len(y_logret)]
|
|
@@ -355,7 +381,7 @@ def train_ridge(df: pd.DataFrame, horizon: int = 1):
|
|
| 355 |
y_train, y_logret_test = y_logret[:split], y_logret[split:]
|
| 356 |
close_test = close_aligned[split:]
|
| 357 |
|
| 358 |
-
# Find best alpha on training data
|
| 359 |
best_alpha = select_best_alpha(X_train, y_train)
|
| 360 |
|
| 361 |
# Build & fit pipeline
|
|
@@ -364,87 +390,33 @@ def train_ridge(df: pd.DataFrame, horizon: int = 1):
|
|
| 364 |
|
| 365 |
# Test predictions β convert log-return back to absolute price
|
| 366 |
logret_hat = pipe.predict(X_test)
|
| 367 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 368 |
y_test_abs = close_test * np.exp(y_logret_test)
|
| 369 |
|
| 370 |
mae = mean_absolute_error(y_test_abs, pred_abs)
|
| 371 |
|
| 372 |
-
return pipe, pred_abs, y_test_abs, avail, best_alpha, mae
|
| 373 |
|
| 374 |
|
| 375 |
def predict_next_ridge(pipe, df: pd.DataFrame, avail: list,
|
| 376 |
-
horizon: int = 1) -> float:
|
| 377 |
-
"""Predict absolute close price horizon days ahead using log-return."""
|
| 378 |
-
cp = float(df['Close'].iloc[-1])
|
| 379 |
-
x = df[avail].iloc[-1].values.reshape(1, -1)
|
| 380 |
-
logret = float(pipe.predict(x)[0])
|
| 381 |
-
# Scale logret by horizon for multi-day forecasts
|
| 382 |
-
return cp * np.exp(logret * horizon)
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 386 |
-
# FIBONACCI LEVELS (son swing ΓΌzerinden)
|
| 387 |
-
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 388 |
-
|
| 389 |
-
FIB_RATIOS = [0.0, 0.236, 0.382, 0.500, 0.618, 0.786, 1.0]
|
| 390 |
-
FIB_COLORS = ['#FFB800', '#FF6B35', '#7B61FF', '#00D4FF', '#00FFB2', '#E8EDF5', '#FFB800']
|
| 391 |
-
|
| 392 |
-
def compute_fibonacci_levels(df: pd.DataFrame, lookback: int = 55) -> dict:
|
| 393 |
-
"""Son `lookback` barda swing high/low bulur, Fibonacci retracement seviyeleri dΓΆner."""
|
| 394 |
-
recent = df.tail(lookback)
|
| 395 |
-
swing_high = float(recent['High'].max())
|
| 396 |
-
swing_low = float(recent['Low'].min())
|
| 397 |
-
diff = swing_high - swing_low
|
| 398 |
-
levels = {f"Fib {int(r*100)}%": swing_high - diff * r for r in FIB_RATIOS}
|
| 399 |
-
levels['_high'] = swing_high
|
| 400 |
-
levels['_low'] = swing_low
|
| 401 |
-
return levels
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
def fibonacci_direction_signal(cp: float, fib: dict) -> dict:
|
| 405 |
-
"""
|
| 406 |
-
FiyatΔ±n hangi Fibonacci bΓΆlgesinde olduΔunu tespit eder.
|
| 407 |
-
Destek/direnΓ§ yakΔ±nlΔ±ΔΔ±na gΓΆre yΓΆn skoru (+bias) ΓΌretir.
|
| 408 |
"""
|
| 409 |
-
|
| 410 |
-
diff = high - low
|
| 411 |
-
if diff < 1e-10:
|
| 412 |
-
return {'fib_score': 0, 'fib_zone': 'N/A', 'fib_bias': 'NΓTR', 'fib_nearest': 0.0}
|
| 413 |
-
|
| 414 |
-
# Normalize: fiyatΔ±n retracement iΓ§indeki yΓΌzdesi (0=low, 1=high)
|
| 415 |
-
pos = (cp - low) / diff # 0β1 arasΔ±
|
| 416 |
-
|
| 417 |
-
# Hangi seviyeye en yakΔ±n?
|
| 418 |
-
nearest_r = min(FIB_RATIOS, key=lambda r: abs(pos - (1 - r)))
|
| 419 |
-
nearest_price = high - diff * nearest_r
|
| 420 |
-
dist_pct = abs(cp - nearest_price) / diff * 100
|
| 421 |
-
|
| 422 |
-
# YΓΆn bias:
|
| 423 |
-
# Fiyat 0.618 altΔ±nda β destek bΓΆlgesi β LONG bias
|
| 424 |
-
# Fiyat 0.382 ΓΌstΓΌnde β direnΓ§ bΓΆlgesi β SHORT bias
|
| 425 |
-
# %50 bΓΆlgesi nΓΆtr
|
| 426 |
-
if pos < 0.382: # Fiyat altta, gΓΌΓ§lΓΌ destek
|
| 427 |
-
fib_score = +25
|
| 428 |
-
fib_bias = "LONG β² (Destek)"
|
| 429 |
-
elif pos < 0.50: # Alt-orta bΓΆlge
|
| 430 |
-
fib_score = +12
|
| 431 |
-
fib_bias = "LONG β² (ZayΔ±f Destek)"
|
| 432 |
-
elif pos > 0.618: # Fiyat ΓΌstte, gΓΌΓ§lΓΌ direnΓ§
|
| 433 |
-
fib_score = -25
|
| 434 |
-
fib_bias = "SHORT βΌ (DirenΓ§)"
|
| 435 |
-
elif pos > 0.50: # Γst-orta bΓΆlge
|
| 436 |
-
fib_score = -12
|
| 437 |
-
fib_bias = "SHORT βΌ (ZayΔ±f DirenΓ§)"
|
| 438 |
-
else:
|
| 439 |
-
fib_score = 0
|
| 440 |
-
fib_bias = "NΓTR (%50)"
|
| 441 |
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
|
|
|
| 448 |
|
| 449 |
|
| 450 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -510,20 +482,6 @@ def signal_score(df: pd.DataFrame, direction: str) -> dict:
|
|
| 510 |
return {'score': min(100, score), 'label': label, 'details': det}
|
| 511 |
|
| 512 |
|
| 513 |
-
def signal_score_with_fib(df: pd.DataFrame, direction: str, fib_signal: dict) -> dict:
|
| 514 |
-
"""signal_score + Fibonacci yΓΆn skoru."""
|
| 515 |
-
base = signal_score(df, direction)
|
| 516 |
-
fs = fib_signal['fib_score']
|
| 517 |
-
aligned = (direction == "LONG" and fs > 0) or (direction == "SHORT" and fs < 0)
|
| 518 |
-
contrib = abs(fs) if aligned else -(abs(fs) // 2)
|
| 519 |
-
new_score = int(np.clip(base['score'] + contrib, 0, 100))
|
| 520 |
-
label = "GΓΓLΓ β
" if new_score >= 75 else ("ORTA β οΈ" if new_score >= 45 else "ZAYIF β")
|
| 521 |
-
base['details']['Fibonacci'] = f"{fib_signal['fib_bias']} β {'+' if contrib >= 0 else ''}{contrib}p"
|
| 522 |
-
base['score'] = new_score
|
| 523 |
-
base['label'] = label
|
| 524 |
-
return base
|
| 525 |
-
|
| 526 |
-
|
| 527 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 528 |
# CHART ENGINE β phosphor-green terminal aesthetic
|
| 529 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -543,8 +501,7 @@ def _ax_style(ax, title="", ylabel=""):
|
|
| 543 |
|
| 544 |
|
| 545 |
def build_figure(df, pred_price, horizon, tp_sl,
|
| 546 |
-
test_pred_abs, y_test_abs, best_alpha, mae
|
| 547 |
-
fib_levels: dict = None) -> plt.Figure:
|
| 548 |
|
| 549 |
plt.rcParams.update({
|
| 550 |
'font.family': 'monospace',
|
|
@@ -605,18 +562,6 @@ def build_figure(df, pred_price, horizon, tp_sl,
|
|
| 605 |
ax_price.axhline(tp_sl['sl'], color=RED, lw=1.4, ls='--', alpha=0.9, zorder=6)
|
| 606 |
ax_price.axhline(cp, color=TEXT, lw=0.7, ls='-', alpha=0.35, zorder=4)
|
| 607 |
|
| 608 |
-
# ββ FIBONACCI levels ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 609 |
-
if fib_levels:
|
| 610 |
-
fib_draw = [(r, c) for r, c in zip(FIB_RATIOS, FIB_COLORS)]
|
| 611 |
-
for ratio, col in fib_draw:
|
| 612 |
-
key = f"Fib {int(ratio*100)}%"
|
| 613 |
-
if key in fib_levels:
|
| 614 |
-
fval = fib_levels[key]
|
| 615 |
-
ax_price.axhline(fval, color=col, lw=0.7, ls=':', alpha=0.55, zorder=3)
|
| 616 |
-
ax_price.text(0.5, fval, f" {key} {fval:.5f}",
|
| 617 |
-
color=col, fontsize=6.5, va='bottom', alpha=0.75,
|
| 618 |
-
fontfamily='monospace', transform=ax_price.get_yaxis_transform())
|
| 619 |
-
|
| 620 |
r = len(idx) * 0.018
|
| 621 |
for val, lbl, col in [
|
| 622 |
(tp_sl['tp'], f"TP {tp_sl['tp']:.5f} (+{tp_sl['tp_pips']:.0f}p)", GREEN),
|
|
@@ -753,10 +698,10 @@ def predict_eurusd(horizon: int = 1):
|
|
| 753 |
return "β Yetersiz veri (min 40 satΔ±r gerekli).", None
|
| 754 |
|
| 755 |
# 3. Train Ridge + backtest
|
| 756 |
-
pipe, test_pred_abs, y_test_abs, avail, best_alpha, mae = train_ridge(df, horizon)
|
| 757 |
|
| 758 |
# 4. Predict next
|
| 759 |
-
pred_price = predict_next_ridge(pipe, df, avail, horizon)
|
| 760 |
|
| 761 |
# 5. TP/SL
|
| 762 |
cp = float(df['Close'].iloc[-1])
|
|
@@ -764,12 +709,8 @@ def predict_eurusd(horizon: int = 1):
|
|
| 764 |
tp_sl = compute_tp_sl(cp, pred_price, atr,
|
| 765 |
atr_multiplier_sl=1.5, rr_ratio=2.0)
|
| 766 |
|
| 767 |
-
#
|
| 768 |
-
|
| 769 |
-
fib_signal = fibonacci_direction_signal(cp, fib)
|
| 770 |
-
|
| 771 |
-
# 6. Signal score (fibonacci dahil)
|
| 772 |
-
sig = signal_score_with_fib(df, tp_sl['direction'], fib_signal)
|
| 773 |
|
| 774 |
# 7. Backtest metrics
|
| 775 |
rmse = float(np.sqrt(np.mean((test_pred_abs - y_test_abs) ** 2)))
|
|
@@ -816,6 +757,7 @@ def predict_eurusd(horizon: int = 1):
|
|
| 816 |
f" {f'+{horizon}G TAHMΔ°N (RIDGE)':<24}: {pred_price:.5f} ({chg:+.3f}%)",
|
| 817 |
f" {'YΓN':<24}: {tp_sl['direction']}",
|
| 818 |
f" {'OPTΔ°MAL ALPHA (RidgeCV)':<24}: {best_alpha:.6f}",
|
|
|
|
| 819 |
"",
|
| 820 |
f" {'βββ RΔ°SK YΓNETΔ°MΔ° (ATR Γ 1.5 | R:R 1:2)':<{W-2}}",
|
| 821 |
f" {'GΔ°RΔ°Ε':<24}: {tp_sl['entry']:.5f}",
|
|
@@ -825,17 +767,7 @@ def predict_eurusd(horizon: int = 1):
|
|
| 825 |
f" {'RΔ°SK %':<24}: {tp_sl['risk_pct']:.3f}%",
|
| 826 |
f" {'ATR-14':<24}: {atr:.5f}",
|
| 827 |
"",
|
| 828 |
-
f" {'βββ
|
| 829 |
-
f" {'Swing High':<24}: {fib['_high']:.5f}",
|
| 830 |
-
f" {'Swing Low':<24}: {fib['_low']:.5f}",
|
| 831 |
-
f" {'Fiyat Pozisyonu':<24}: {fib_signal['fib_zone']}",
|
| 832 |
-
f" {'Fibonacci Bias':<24}: {fib_signal['fib_bias']}",
|
| 833 |
-
] + [
|
| 834 |
-
f" {' Fib ' + k.split()[1]:<24}: {v:.5f}"
|
| 835 |
-
for k, v in fib.items() if k.startswith('Fib')
|
| 836 |
-
] + [
|
| 837 |
-
"",
|
| 838 |
-
f" {'βββ SΔ°NYAL GΓCΓ (Fibonacci Dahil)':<{W-2}}",
|
| 839 |
f" {'SKOR':<24}: {sig['score']:.0f} / 100 β {sig['label']}",
|
| 840 |
]
|
| 841 |
for k, v in sig['details'].items():
|
|
@@ -872,7 +804,6 @@ def predict_eurusd(horizon: int = 1):
|
|
| 872 |
fig = build_figure(
|
| 873 |
df, pred_price, horizon, tp_sl,
|
| 874 |
test_pred_abs, y_test_abs, best_alpha, mae,
|
| 875 |
-
fib_levels=fib,
|
| 876 |
)
|
| 877 |
|
| 878 |
return text, fig
|
|
|
|
| 257 |
np.sign(df['LagRet_3'])
|
| 258 |
) / 3.0
|
| 259 |
|
| 260 |
+
# ββ Horizon-aware features (2-day dynamics) βββββββββββββββββββββββββββββββ
|
| 261 |
+
# These explicitly capture 2-day structure to boost multi-day accuracy.
|
| 262 |
+
df['LagRet_1_2sum'] = df['LagRet_1'] + df['LagRet_2'] # 2-day return sum
|
| 263 |
+
df['LagRet_1_2diff'] = df['LagRet_1'] - df['LagRet_2'] # acceleration
|
| 264 |
+
df['Vol_2'] = df['Returns'].rolling(2).std() # 2-day volatility
|
| 265 |
+
df['ROC_2'] = df['Close'].pct_change(2) # 2-day ROC
|
| 266 |
+
df['SMA3_slope'] = df['SMA_3'].diff(2) # short MA 2-day slope
|
| 267 |
+
df['EMA3_slope'] = df['EMA_3'].diff(2) # short EMA 2-day slope
|
| 268 |
+
df['ATR_delta'] = df['ATR'].diff(1) # ATR change (vol expansion/contraction)
|
| 269 |
+
df['RSI14_delta'] = df['RSI_14'].diff(2) # RSI momentum 2-day
|
| 270 |
+
df['MACD_Hist_delta']= df['MACD_Hist'].diff(2) # MACD histogram acceleration
|
| 271 |
+
df['BB_pct20_delta'] = df['BB_pct_20'].diff(2) # BB position change
|
| 272 |
+
|
| 273 |
return df.dropna()
|
| 274 |
|
| 275 |
|
|
|
|
| 312 |
'Volume_Ratio', 'OBV_Signal',
|
| 313 |
# Direction composite (FIX 4)
|
| 314 |
'Direction_Score', 'Momentum_Consistency',
|
| 315 |
+
# Horizon-aware (2-day dynamics)
|
| 316 |
+
'LagRet_1_2sum', 'LagRet_1_2diff', 'Vol_2', 'ROC_2',
|
| 317 |
+
'SMA3_slope', 'EMA3_slope', 'ATR_delta',
|
| 318 |
+
'RSI14_delta', 'MACD_Hist_delta', 'BB_pct20_delta',
|
| 319 |
]
|
| 320 |
|
| 321 |
|
|
|
|
| 339 |
def select_best_alpha(X_tr, y_tr) -> float:
|
| 340 |
"""
|
| 341 |
RidgeCV finds optimal alpha from a fine logarithmic grid.
|
| 342 |
+
FIXED: alpha is selected on poly-expanded features (same space as the
|
| 343 |
+
actual Ridge model), not on raw features. This eliminates alpha
|
| 344 |
+
scale mismatch that degraded 2-day accuracy.
|
| 345 |
"""
|
| 346 |
+
sc1 = StandardScaler()
|
| 347 |
+
poly = PolynomialFeatures(degree=2, interaction_only=True, include_bias=False)
|
| 348 |
+
sc2 = StandardScaler(with_mean=False)
|
| 349 |
+
Xp = sc2.fit_transform(poly.fit_transform(sc1.fit_transform(X_tr)))
|
| 350 |
+
|
| 351 |
+
# After poly expansion the feature count is ~n*(n-1)/2 β ridge needs
|
| 352 |
+
# higher alphas to stay regularised at that scale.
|
| 353 |
+
alphas = np.logspace(0, 6, 60)
|
| 354 |
rcv = RidgeCV(alphas=alphas, cv=5, scoring='neg_mean_absolute_error')
|
| 355 |
+
rcv.fit(Xp, y_tr)
|
| 356 |
return float(rcv.alpha_)
|
| 357 |
|
| 358 |
|
|
|
|
| 360 |
"""
|
| 361 |
Trains the premium Ridge model.
|
| 362 |
Target: log-return over `horizon` days (direction-aware).
|
| 363 |
+
|
| 364 |
+
FIXES applied:
|
| 365 |
+
- Log-return target correctly encodes h-day return (not 1-day)
|
| 366 |
+
- Residual bias correction: calibrates systematic over/under-shoot
|
| 367 |
+
- Returns pipeline, test predictions (absolute), y_test (absolute), avail_cols.
|
| 368 |
"""
|
| 369 |
avail = [c for c in FEATURE_COLS if c in df.columns]
|
| 370 |
X = df[avail].values
|
| 371 |
close = df['Close'].values
|
| 372 |
|
| 373 |
+
# h-day log-return β model learns this directly, no horizon scaling needed
|
|
|
|
|
|
|
| 374 |
y_logret = np.log(close[horizon:] / close[:len(close) - horizon])
|
| 375 |
|
| 376 |
X = X[:len(y_logret)]
|
|
|
|
| 381 |
y_train, y_logret_test = y_logret[:split], y_logret[split:]
|
| 382 |
close_test = close_aligned[split:]
|
| 383 |
|
| 384 |
+
# Find best alpha on poly-expanded training data
|
| 385 |
best_alpha = select_best_alpha(X_train, y_train)
|
| 386 |
|
| 387 |
# Build & fit pipeline
|
|
|
|
| 390 |
|
| 391 |
# Test predictions β convert log-return back to absolute price
|
| 392 |
logret_hat = pipe.predict(X_test)
|
| 393 |
+
|
| 394 |
+
# Residual bias correction: remove mean systematic bias from log-return predictions
|
| 395 |
+
# This improves directional accuracy without leaking future info (computed on train residuals)
|
| 396 |
+
train_preds = pipe.predict(X_train)
|
| 397 |
+
residual_bias = float(np.mean(y_train - train_preds)) # mean error on train set
|
| 398 |
+
logret_hat_corrected = logret_hat + residual_bias
|
| 399 |
+
|
| 400 |
+
pred_abs = close_test * np.exp(logret_hat_corrected)
|
| 401 |
y_test_abs = close_test * np.exp(y_logret_test)
|
| 402 |
|
| 403 |
mae = mean_absolute_error(y_test_abs, pred_abs)
|
| 404 |
|
| 405 |
+
return pipe, pred_abs, y_test_abs, avail, best_alpha, mae, residual_bias
|
| 406 |
|
| 407 |
|
| 408 |
def predict_next_ridge(pipe, df: pd.DataFrame, avail: list,
|
| 409 |
+
horizon: int = 1, residual_bias: float = 0.0) -> float:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 410 |
"""
|
| 411 |
+
Predict absolute close price horizon days ahead using log-return.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 412 |
|
| 413 |
+
FIXED: Removed `* horizon` scaling β model already learns h-day log-return.
|
| 414 |
+
Applies residual_bias correction from training set for calibrated prediction.
|
| 415 |
+
"""
|
| 416 |
+
cp = float(df['Close'].iloc[-1])
|
| 417 |
+
x = df[avail].iloc[-1].values.reshape(1, -1)
|
| 418 |
+
logret = float(pipe.predict(x)[0]) + residual_bias
|
| 419 |
+
return cp * np.exp(logret)
|
| 420 |
|
| 421 |
|
| 422 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 482 |
return {'score': min(100, score), 'label': label, 'details': det}
|
| 483 |
|
| 484 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 485 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 486 |
# CHART ENGINE β phosphor-green terminal aesthetic
|
| 487 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 501 |
|
| 502 |
|
| 503 |
def build_figure(df, pred_price, horizon, tp_sl,
|
| 504 |
+
test_pred_abs, y_test_abs, best_alpha, mae) -> plt.Figure:
|
|
|
|
| 505 |
|
| 506 |
plt.rcParams.update({
|
| 507 |
'font.family': 'monospace',
|
|
|
|
| 562 |
ax_price.axhline(tp_sl['sl'], color=RED, lw=1.4, ls='--', alpha=0.9, zorder=6)
|
| 563 |
ax_price.axhline(cp, color=TEXT, lw=0.7, ls='-', alpha=0.35, zorder=4)
|
| 564 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 565 |
r = len(idx) * 0.018
|
| 566 |
for val, lbl, col in [
|
| 567 |
(tp_sl['tp'], f"TP {tp_sl['tp']:.5f} (+{tp_sl['tp_pips']:.0f}p)", GREEN),
|
|
|
|
| 698 |
return "β Yetersiz veri (min 40 satΔ±r gerekli).", None
|
| 699 |
|
| 700 |
# 3. Train Ridge + backtest
|
| 701 |
+
pipe, test_pred_abs, y_test_abs, avail, best_alpha, mae, residual_bias = train_ridge(df, horizon)
|
| 702 |
|
| 703 |
# 4. Predict next
|
| 704 |
+
pred_price = predict_next_ridge(pipe, df, avail, horizon, residual_bias)
|
| 705 |
|
| 706 |
# 5. TP/SL
|
| 707 |
cp = float(df['Close'].iloc[-1])
|
|
|
|
| 709 |
tp_sl = compute_tp_sl(cp, pred_price, atr,
|
| 710 |
atr_multiplier_sl=1.5, rr_ratio=2.0)
|
| 711 |
|
| 712 |
+
# 6. Signal score
|
| 713 |
+
sig = signal_score(df, tp_sl['direction'])
|
|
|
|
|
|
|
|
|
|
|
|
|
| 714 |
|
| 715 |
# 7. Backtest metrics
|
| 716 |
rmse = float(np.sqrt(np.mean((test_pred_abs - y_test_abs) ** 2)))
|
|
|
|
| 757 |
f" {f'+{horizon}G TAHMΔ°N (RIDGE)':<24}: {pred_price:.5f} ({chg:+.3f}%)",
|
| 758 |
f" {'YΓN':<24}: {tp_sl['direction']}",
|
| 759 |
f" {'OPTΔ°MAL ALPHA (RidgeCV)':<24}: {best_alpha:.6f}",
|
| 760 |
+
f" {'BIAS DΓZELTME (log-ret)':<24}: {residual_bias:+.7f}",
|
| 761 |
"",
|
| 762 |
f" {'βββ RΔ°SK YΓNETΔ°MΔ° (ATR Γ 1.5 | R:R 1:2)':<{W-2}}",
|
| 763 |
f" {'GΔ°RΔ°Ε':<24}: {tp_sl['entry']:.5f}",
|
|
|
|
| 767 |
f" {'RΔ°SK %':<24}: {tp_sl['risk_pct']:.3f}%",
|
| 768 |
f" {'ATR-14':<24}: {atr:.5f}",
|
| 769 |
"",
|
| 770 |
+
f" {'βββ SΔ°NYAL GΓCΓ':<{W-2}}",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 771 |
f" {'SKOR':<24}: {sig['score']:.0f} / 100 β {sig['label']}",
|
| 772 |
]
|
| 773 |
for k, v in sig['details'].items():
|
|
|
|
| 804 |
fig = build_figure(
|
| 805 |
df, pred_price, horizon, tp_sl,
|
| 806 |
test_pred_abs, y_test_abs, best_alpha, mae,
|
|
|
|
| 807 |
)
|
| 808 |
|
| 809 |
return text, fig
|