GoshawkVortexAI commited on
Commit
1ec16ed
·
verified ·
1 Parent(s): 7c3dffa

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -382
app.py DELETED
@@ -1,382 +0,0 @@
1
- import warnings
2
- warnings.filterwarnings("ignore")
3
-
4
- import numpy as np
5
- import pandas as pd
6
- import yfinance as yf
7
- import gradio as gr
8
- import plotly.graph_objects as go
9
-
10
- from sklearn.impute import SimpleImputer
11
- from sklearn.preprocessing import StandardScaler
12
- from sklearn.pipeline import Pipeline
13
- from sklearn.neighbors import KNeighborsClassifier, NearestNeighbors
14
- from sklearn.metrics import balanced_accuracy_score
15
-
16
- APP_TITLE = "Goshawk Adaptive BTCUSD Engine"
17
- HORIZON = 5
18
- FEE_DEFAULT = 0.05
19
- SLIPPAGE_DEFAULT = 0.03
20
-
21
-
22
- def download_btc(period="5y"):
23
- df = yf.download("BTC-USD", period=period, interval="1d",
24
- auto_adjust=True, progress=False)
25
- if df is None or df.empty:
26
- raise ValueError("BTC verisi alınamadı. İnternet/veri sağlayıcısını kontrol edin.")
27
- if isinstance(df.columns, pd.MultiIndex):
28
- df.columns = df.columns.get_level_values(0)
29
- df = df[["Open", "High", "Low", "Close", "Volume"]].dropna().copy()
30
- df.index = pd.to_datetime(df.index)
31
- return df
32
-
33
-
34
- def calc_atr(df, n=14):
35
- pc = df.Close.shift(1)
36
- tr = pd.concat([(df.High-df.Low), (df.High-pc).abs(),
37
- (df.Low-pc).abs()], axis=1).max(axis=1)
38
- return tr.ewm(alpha=1/n, adjust=False).mean()
39
-
40
-
41
- def rsi(s, n=14):
42
- d = s.diff()
43
- up = d.clip(lower=0).ewm(alpha=1/n, adjust=False).mean()
44
- dn = (-d.clip(upper=0)).ewm(alpha=1/n, adjust=False).mean()
45
- rs = up / dn.replace(0, np.nan)
46
- return 100 - 100/(1+rs)
47
-
48
-
49
- def feature_engineering(df):
50
- c, v = df.Close, df.Volume
51
- x = pd.DataFrame(index=df.index)
52
- for n in [1, 2, 3, 5, 10, 20, 50]:
53
- x[f"return_{n}"] = c.pct_change(n)
54
- for n in [5, 10, 20, 50]:
55
- ema = c.ewm(span=n, adjust=False).mean()
56
- x[f"ema_distance_{n}"] = c/ema - 1
57
- x["rsi"] = rsi(c)/100
58
- atr = calc_atr(df)
59
- x["atr_pct"] = atr/c
60
- x["range_pct"] = (df.High-df.Low)/c
61
- x["body_pct"] = (df.Close-df.Open)/df.Open
62
- x["volatility_10"] = c.pct_change().rolling(10).std()
63
- x["volatility_20"] = c.pct_change().rolling(20).std()
64
- x["volume_z"] = (v-v.rolling(20).mean())/v.rolling(20).std().replace(0, np.nan)
65
- x["volume_return"] = v.pct_change()
66
- macd = c.ewm(span=12, adjust=False).mean()-c.ewm(span=26, adjust=False).mean()
67
- x["macd_pct"] = macd/c
68
- return x.replace([np.inf, -np.inf], np.nan), atr
69
-
70
-
71
- def make_labels(df, atr, horizon=HORIZON, threshold=0.20):
72
- # Label is based only on the close-to-close forward return; features are lagged/current.
73
- fwd = df.Close.shift(-horizon)/df.Close - 1
74
- band = threshold*(atr/df.Close)*np.sqrt(horizon)
75
- y = np.where(fwd > band, 1, np.where(fwd < -band, -1, 0))
76
- return pd.Series(y, index=df.index), fwd
77
-
78
-
79
- def build_pipeline(k):
80
- return Pipeline([
81
- ("imputer", SimpleImputer(strategy="median")),
82
- ("scale", StandardScaler()),
83
- ("knn", KNeighborsClassifier(n_neighbors=k, weights="distance"))
84
- ])
85
-
86
-
87
- def barrier_outcome(df, start_i, side, stop_atr, target_atr, horizon=HORIZON):
88
- entry = float(df.Close.iloc[start_i])
89
- atr = float(calc_atr(df).iloc[start_i])
90
- if not np.isfinite(atr) or atr <= 0:
91
- return None
92
- if side == 1:
93
- stop, target = entry-stop_atr*atr, entry+target_atr*atr
94
- else:
95
- stop, target = entry+stop_atr*atr, entry-target_atr*atr
96
- end = min(start_i+horizon, len(df)-1)
97
- for j in range(start_i+1, end+1):
98
- hi, lo = float(df.High.iloc[j]), float(df.Low.iloc[j])
99
- if side == 1:
100
- hit_sl, hit_tp = lo <= stop, hi >= target
101
- else:
102
- hit_sl, hit_tp = hi >= stop, lo <= target
103
- # Conservative assumption if both barriers are touched in one daily candle.
104
- if hit_sl and hit_tp:
105
- return -stop_atr*atr/entry
106
- if hit_sl:
107
- return -stop_atr*atr/entry
108
- if hit_tp:
109
- return target_atr*atr/entry
110
- final = float(df.Close.iloc[end])
111
- return side*(final-entry)/entry
112
-
113
-
114
- def optimize_barriers(df, train_end, side, fee_pct, slip_pct):
115
- # Select SL/TP from matured historical examples only.
116
- sl_grid = [0.8, 1.0, 1.2, 1.5, 1.8, 2.0, 2.5, 3.0]
117
- rr_grid = [1.0, 1.25, 1.5, 2.0, 2.5, 3.0, 4.0]
118
- first = max(60, train_end-450)
119
- candidates = []
120
- cost = 2*(fee_pct+slip_pct)/100
121
- for sl in sl_grid:
122
- for rr in rr_grid:
123
- outcomes = []
124
- for i in range(first, train_end):
125
- # Only use examples whose full forward horizon is inside training history.
126
- if i+HORIZON >= train_end:
127
- continue
128
- r = barrier_outcome(df, i, side, sl, sl*rr)
129
- if r is not None:
130
- outcomes.append(r-cost)
131
- if len(outcomes) < 35:
132
- continue
133
- arr = np.asarray(outcomes)
134
- # Penalize unstable settings; maximize average net return per trade.
135
- score = arr.mean() - 0.25*arr.std()/np.sqrt(len(arr))
136
- candidates.append((score, sl, rr, len(arr), arr.mean(), (arr > 0).mean()))
137
- if not candidates:
138
- return 1.5, 2.0, None
139
- best = max(candidates, key=lambda z: z[0])
140
- return best[1], best[2], best
141
-
142
-
143
- def regime_name(row):
144
- if row["volatility_20"] > 0.045:
145
- vol = "Yüksek volatilite"
146
- elif row["volatility_20"] < 0.018:
147
- vol = "Düşük volatilite"
148
- else:
149
- vol = "Orta volatilite"
150
- if row["ema_distance_20"] > 0.025:
151
- trend = "Yukarı trend"
152
- elif row["ema_distance_20"] < -0.025:
153
- trend = "Aşağı trend"
154
- else:
155
- trend = "Yatay / kararsız"
156
- return f"{trend} · {vol}"
157
-
158
-
159
- def run_engine(period, equity, max_risk, min_risk, max_leverage,
160
- fee_pct, slip_pct, train_window, k):
161
- try:
162
- df = download_btc(period)
163
- if len(df) < 300:
164
- raise ValueError("En az 300 günlük mum gerekli.")
165
- # Ignore today's unfinished daily candle by default.
166
- df = df.iloc[:-1].copy()
167
- X, atr = feature_engineering(df)
168
- y, fwd = make_labels(df, atr)
169
- data = X.copy()
170
- data["target"] = y
171
- data["future"] = fwd
172
- valid = data.dropna(subset=["target", "future"])
173
- valid = valid.dropna(subset=list(X.columns), how="all")
174
- if len(valid) < 180:
175
- raise ValueError("Model eğitimi için yeterli temiz örnek yok.")
176
-
177
- # Latest known candle's feature row; training labels are already matured.
178
- current_x = X.iloc[[-1]]
179
- train_end = len(valid)
180
- window = min(int(train_window), train_end-HORIZON-1)
181
- train = valid.iloc[-window:]
182
- if len(train) <= int(k):
183
- raise ValueError("K komşu sayısı eğitim örneklerinden küçük.")
184
-
185
- model = build_pipeline(int(k))
186
- model.fit(train[X.columns], train.target.astype(int))
187
- probs = model.predict_proba(current_x[X.columns])[0]
188
- classes = model.named_steps["knn"].classes_
189
- p = {int(c): float(v) for c, v in zip(classes, probs)}
190
- p_long, p_short, p_flat = p.get(1, 0), p.get(-1, 0), p.get(0, 0)
191
- direction = 1 if p_long >= p_short else -1
192
- direction_prob = max(p_long, p_short)
193
- edge = abs(p_long-p_short)
194
-
195
- # KNN neighborhood agreement and chronological holdout score.
196
- imputer, scaler = model.named_steps["imputer"], model.named_steps["scale"]
197
- z_train = scaler.transform(imputer.transform(train[X.columns]))
198
- z_now = scaler.transform(imputer.transform(current_x[X.columns]))
199
- nn = NearestNeighbors(n_neighbors=int(k)).fit(z_train)
200
- distances, indices = nn.kneighbors(z_now)
201
- analogs = train.iloc[indices[0]]
202
- agreement = float((analogs.target.astype(int) == direction).mean())
203
-
204
- test_n = min(90, max(30, len(valid)//6))
205
- hist_train = valid.iloc[:-test_n]
206
- hist_test = valid.iloc[-test_n:]
207
- eval_model = build_pipeline(int(k))
208
- eval_model.fit(hist_train[X.columns], hist_train.target.astype(int))
209
- pred = eval_model.predict(hist_test[X.columns])
210
- holdout_bacc = balanced_accuracy_score(hist_test.target.astype(int), pred)
211
-
212
- sl_mult, rr_mult, opt_stats = optimize_barriers(
213
- df, len(df)-HORIZON, direction, float(fee_pct), float(slip_pct)
214
- )
215
- entry = float(df.Close.iloc[-1])
216
- current_atr = float(atr.iloc[-1])
217
- stop_dist = sl_mult*current_atr
218
- tp_dist = stop_dist*rr_mult
219
- if direction == 1:
220
- stop, target = entry-stop_dist, entry+tp_dist
221
- else:
222
- stop, target = entry+stop_dist, entry-tp_dist
223
-
224
- # Adaptive risk: base on model edge, neighbor agreement, validation quality,
225
- # and current drawdown proxy (recent 30-day peak-to-trough close drawdown).
226
- close = df.Close
227
- recent = close.tail(30)
228
- dd = float(recent.iloc[-1]/recent.cummax().iloc[-1]-1)
229
- quality = np.clip((holdout_bacc-0.33)/0.20, 0, 1)
230
- conviction = np.clip((direction_prob-0.34)/0.35, 0, 1)
231
- neighbor_quality = np.clip((agreement-0.33)/0.50, 0, 1)
232
- risk_factor = 0.20 + 0.35*quality + 0.25*conviction + 0.20*neighbor_quality
233
- if dd < -0.12:
234
- risk_factor *= 0.35
235
- elif dd < -0.07:
236
- risk_factor *= 0.60
237
- risk_pct = float(min_risk) + (float(max_risk)-float(min_risk))*risk_factor
238
- risk_pct = float(np.clip(risk_pct, min_risk, max_risk))
239
-
240
- # No trade when evidence is weak or validation is poor.
241
- trade_ok = (
242
- direction_prob >= 0.43 and edge >= 0.12 and agreement >= 0.55
243
- and holdout_bacc >= 0.36 and p_flat < 0.60
244
- )
245
- risk_cash = float(equity)*risk_pct/100
246
- cost_rate = 2*(float(fee_pct)+float(slip_pct))/100
247
- cost_per_btc = stop_dist + entry*cost_rate
248
- btc_qty = risk_cash/cost_per_btc if cost_per_btc > 0 else 0
249
- notional = btc_qty*entry
250
- max_notional = float(equity)*float(max_leverage)
251
- leverage_limited = notional > max_notional
252
- if leverage_limited:
253
- btc_qty = max_notional/entry
254
- notional = btc_qty*entry
255
- actual_risk = btc_qty*cost_per_btc
256
- lot_size_btc = 1.0 # Change to broker's contract specification.
257
- lots = btc_qty/lot_size_btc
258
- margin = notional/max(float(max_leverage), 1)
259
-
260
- if not trade_ok:
261
- signal = "WAIT — İŞLEM AÇMA"
262
- btc_qty = notional = lots = margin = actual_risk = 0.0
263
- else:
264
- signal = "LONG" if direction == 1 else "SHORT"
265
-
266
- if opt_stats:
267
- opt_text = (
268
- f"Optimizasyon örnek sayısı: {opt_stats[3]} · "
269
- f"Geçmiş ortalama net getiri/işlem: {opt_stats[4]*100:.3f}% · "
270
- f"Geçmiş pozitif sonuç oranı: {opt_stats[5]*100:.1f}%"
271
- )
272
- else:
273
- opt_text = "TP/SL optimizasyonu için yeterli olgunlaşmış örnek bulunamadı."
274
-
275
- report = f"""
276
- # {signal}
277
-
278
- **Referans tarih:** {df.index[-1].date()} · **Referans fiyat:** ${entry:,.2f}
279
-
280
- ## Modelin öğrendikleri
281
- | Ölçüm | Sonuç |
282
- |---|---:|
283
- | P(up) | {p_long:.1%} |
284
- | P(flat) | {p_flat:.1%} |
285
- | P(down) | {p_short:.1%} |
286
- | En yakın komşu uyumu | {agreement:.1%} |
287
- | Kronolojik holdout balanced accuracy | {holdout_bacc:.3f} |
288
- | Piyasa rejimi | {regime_name(X.iloc[-1])} |
289
-
290
- ## Otomatik TP / SL
291
- | Parametre | Değer |
292
- |---|---:|
293
- | Stop mesafesi | {sl_mult:.2f} ATR |
294
- | Hedef mesafesi | {rr_mult:.2f}R |
295
- | Stop-loss | ${stop:,.2f} |
296
- | Take-profit | ${target:,.2f} |
297
- | ATR(14) | ${current_atr:,.2f} |
298
- | TP/SL geçmiş optimizasyonu | {opt_text} |
299
-
300
- ## Adaptif pozisyon ve risk
301
- | Parametre | Değer |
302
- |---|---:|
303
- | Hesap büyüklüğü | ${equity:,.2f} |
304
- | Öğrenilmiş risk | {risk_pct:.2f}% |
305
- | Risk bütçesi | ${risk_cash:,.2f} |
306
- | BTC miktarı | {btc_qty:.6f} |
307
- | Nominal pozisyon | ${notional:,.2f} |
308
- | Tahmini teminat | ${margin:,.2f} |
309
- | Tahmini stop kaybı + maliyet | ${actual_risk:,.2f} |
310
- | Son 30 günlük kapanış DD | {dd:.2%} |
311
- | Kaldıraç sınırı uygulandı mı? | {"Evet" if leverage_limited else "Hayır"} |
312
-
313
- Risk yüzdesi model kalitesi, komşu uyumu ve yakın dönem drawdown'a göre seçildi.
314
- """
315
-
316
- fig = go.Figure(data=[go.Candlestick(
317
- x=df.index[-120:], open=df.Open.tail(120), high=df.High.tail(120),
318
- low=df.Low.tail(120), close=df.Close.tail(120), name="BTCUSD"
319
- )])
320
- if trade_ok:
321
- fig.add_hline(y=stop, line_dash="dash", annotation_text="SL")
322
- fig.add_hline(y=target, line_dash="dash", annotation_text="TP")
323
- fig.update_layout(title="BTCUSD Daily", height=520,
324
- xaxis_rangeslider_visible=False)
325
-
326
- neighbor_table = analogs[["target", "future"]].copy()
327
- neighbor_table["target"] = neighbor_table.target.map({-1:"DOWN",0:"FLAT",1:"UP"})
328
- neighbor_table["future"] = neighbor_table.future.map(lambda z: f"{z:.2%}")
329
- neighbor_table.index = neighbor_table.index.strftime("%Y-%m-%d")
330
- neighbor_table = neighbor_table.reset_index().rename(
331
- columns={"index":"Tarih","target":"Sınıf","future":"5 günlük getiri"}
332
- )
333
- disclaimer = (
334
- "**Araştırma prototipi — otomatik emir göndermez.** Model her çalıştırmada "
335
- "indirilen geçmiş veriyi yeniden eğitir; canlı öğrenme/online learning değildir. "
336
- "TP/SL seçimi geçmiş örneklerde optimize edilir ve overfitting riski taşır. "
337
- "KNN skorları kalibre edilmiş olasılık değildir. Günlük OHLC mumlarında aynı "
338
- "gün TP ve SL birlikte görülürse muhafazakâr olarak SL varsayılır. Yahoo BTC-USD "
339
- "verisi broker CFD fiyatı değildir. Lot büyüklüğü, komisyon, spread, funding, "
340
- "swap, marjin ve minimum miktarı broker şartlarına göre doğrulayın. Gerçek "
341
- "hesapta kullanmadan önce out-of-sample walk-forward, paper trading ve stres "
342
- "testleri zorunludur."
343
- )
344
- return report, fig, neighbor_table, disclaimer
345
-
346
- except Exception as exc:
347
- return f"# Hata\n{type(exc).__name__}: {exc}", None, pd.DataFrame(), \
348
- "Veri bağlantısını ve parametreleri kontrol edin."
349
-
350
-
351
- with gr.Blocks(title=APP_TITLE, theme=gr.themes.Soft()) as app:
352
- gr.Markdown(
353
- "# GOSHAWK — Adaptive BTCUSD KNN Risk Engine\n"
354
- "Günlük veride KNN yön tahmini, geçmiş bariyer sonuçlarından adaptif TP/SL "
355
- "seçimi ve model kalitesine göre dinamik risk/pozisyon boyutlandırma."
356
- )
357
- with gr.Row():
358
- with gr.Column():
359
- period = gr.Dropdown(["2y", "5y", "max"], value="5y", label="Veri geçmişi")
360
- train_window = gr.Slider(250, 1500, value=900, step=50, label="Eğitim penceresi")
361
- k = gr.Slider(3, 31, value=9, step=2, label="KNN komşu sayısı")
362
- equity = gr.Number(value=10000, label="Hesap büyüklüğü (USD)")
363
- with gr.Column():
364
- min_risk = gr.Slider(0.1, 0.5, value=0.1, step=0.05, label="Minimum risk (%)")
365
- max_risk = gr.Slider(0.5, 2.0, value=0.75, step=0.05, label="Maksimum risk (%)")
366
- max_leverage = gr.Slider(1, 10, value=3, step=1, label="Azami kaldıraç")
367
- fee_pct = gr.Number(value=FEE_DEFAULT, label="Tek yön komisyon (%)")
368
- slip_pct = gr.Number(value=SLIPPAGE_DEFAULT, label="Tek yön slippage (%)")
369
- run = gr.Button("Veriyi öğren ve analiz et", variant="primary")
370
- output = gr.Markdown()
371
- chart = gr.Plot()
372
- neighbors = gr.Dataframe(label="En yakın geçmiş piyasa örnekleri", interactive=False)
373
- disclaimer = gr.Markdown()
374
- run.click(
375
- run_engine,
376
- inputs=[period, equity, max_risk, min_risk, max_leverage,
377
- fee_pct, slip_pct, train_window, k],
378
- outputs=[output, chart, neighbors, disclaimer]
379
- )
380
-
381
- if __name__ == "__main__":
382
- app.launch(server_name="0.0.0.0", server_port=7860)