Spaces:
Running
Running
File size: 10,684 Bytes
12edcfb 9dd6d6b 12edcfb 3d8478b 12edcfb dabf7c9 12edcfb 9dd6d6b 12edcfb 9dd6d6b 0a66704 6f2b215 b0a91f6 9dd6d6b b9ac513 9dd6d6b b9ac513 9dd6d6b b9ac513 9dd6d6b b9ac513 9dd6d6b b0a91f6 9dd6d6b 12edcfb b9ac513 12edcfb 22847cc 12edcfb 8ba8c3d 5766c42 8ba8c3d 6f2b215 9dd6d6b 8ba8c3d 9dd6d6b 2bd1c15 12edcfb 5766c42 b0a91f6 b9ac513 9dd6d6b b0a91f6 12edcfb 9dd6d6b 5766c42 12edcfb 22847cc 9dd6d6b 6f2b215 b0a91f6 9dd6d6b b9ac513 b0a91f6 b9ac513 b0a91f6 b9ac513 b0a91f6 12edcfb 9dd6d6b 6f2b215 8ba8c3d 6f2b215 9dd6d6b 6f2b215 1bf16da 6f2b215 1bf16da b9ac513 6f2b215 1bf16da b9ac513 6f2b215 22847cc b9ac513 9dd6d6b 5766c42 6f2b215 7b309e2 b0a91f6 9dd6d6b b0a91f6 22847cc 12edcfb 9dd6d6b 6f2b215 b9ac513 22847cc 9dd6d6b b9ac513 8ba8c3d 22847cc b9ac513 9dd6d6b b9ac513 22847cc 3d8478b b9ac513 22847cc 6f2b215 5766c42 9dd6d6b b9ac513 22847cc 9dd6d6b b9ac513 9dd6d6b b9ac513 9dd6d6b b9ac513 9dd6d6b b9ac513 9dd6d6b b9ac513 9dd6d6b 22847cc 6f2b215 3d8478b b9ac513 22847cc 12edcfb 22847cc 5766c42 22847cc b9ac513 22847cc |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# simulation_engine/sim_runner.py
# V3.0 — Balanced-Fast: more trades, better expectancy, single-position, 1–2h time stops.
import asyncio
import os
import pandas as pd
from tqdm import tqdm
from r2 import R2Service
from data_manager import DataManager
from ml_engine.processor import MLProcessor
from learning_hub.hub_manager import LearningHubManager
try:
from .mock_kucoin import MockKuCoin
from .virtual_exchange import VirtualExchange
except ImportError:
# إذا كانت البنية مختلفة، حدّث المسارات حسب مشروعك
from simulation_engine.mock_kucoin import MockKuCoin
from simulation_engine.virtual_exchange import VirtualExchange
SIM_CONFIG = {
# نافذة الاختبار
"START_DATE": "2025-09-10",
"END_DATE": "2025-11-09",
# المحفظة والتنفيذ
"INITIAL_BALANCE": 10.0,
"FEE_RATE": 0.001, # 0.1%
# سرعة القرار وجودته
"STEP_TF": "5m", # قرار كل 5 دقائق
"ENTRY_THRESHOLD": 0.22, # عتبة دخول متوازنة لرفع العدد دون ضوضاء مفرطة
# حجم الصفقة والقيود
"POSITION_FRACTION": 0.45, # 45% من الرصيد لكل صفقة
"MAX_CONCURRENT_POSITIONS": 1, # صفقة واحدة في نفس الوقت
"MIN_TRADE_USD": 0.10,
# الخروج: هدف/وقف + إيقاف زمني مرن
"TP_PCT": 1.6, # 1.6%
"SL_PCT": 0.8, # 0.8%
"SOFT_TIME_STOP_BARS": 12, # ≈ 1 ساعة عند STEP_TF=5m: يغلق عند التعادل أو أفضل
"HARD_TIME_STOP_BARS": 24, # ≈ 2 ساعة: إغلاق قسري
"COOLDOWN_BARS": 1, # تبريد قصير لمنع إعادة الدخول الفوري
# الكون
"TEST_SYMBOLS": [
"BTC/USDT","ETH/USDT","SOL/USDT","BNB/USDT","XRP/USDT",
"DOGE/USDT","ADA/USDT","AVAX/USDT","LINK/USDT"
],
"LOCAL_DATA_DIR": "./simulation_data"
}
SIM_STATUS = {"running": False, "progress": 0.0, "current_balance": 0.0, "trades_count": 0}
TF_MS = {'5m': 300_000, '15m': 900_000, '1h': 3_600_000, '4h': 14_400_000, '1d': 86_400_000}
REQUIRED_TFS = ['5m','15m','1h','4h','1d']
FETCH_LIMIT = 500
# الحد الأدنى للشموع المطلوبة لكل إطار
MIN_CANDLES_BASE = {'5m':220,'15m':220,'1h':220,'4h':220,'1d':60}
MIN_CANDLES_REQ = dict(MIN_CANDLES_BASE)
MIN_CANDLES_REQ['1d'] = 59 # مرونة بسيطة لبدء المحاكاة
async def run_realistic_simulation():
SIM_STATUS["running"] = True
print(
f"🚀 Sim {SIM_CONFIG['START_DATE']} -> {SIM_CONFIG['END_DATE']} | symbols={len(SIM_CONFIG['TEST_SYMBOLS'])}\n"
f"STEP_TF={SIM_CONFIG['STEP_TF']} THR={SIM_CONFIG['ENTRY_THRESHOLD']} "
f"TP/SL={SIM_CONFIG['TP_PCT']}/{SIM_CONFIG['SL_PCT']} "
f"SOFT/HARD={SIM_CONFIG['SOFT_TIME_STOP_BARS']}/{SIM_CONFIG['HARD_TIME_STOP_BARS']} bars "
f"| COOLDOWN={SIM_CONFIG['COOLDOWN_BARS']} | POS_FRAC={SIM_CONFIG['POSITION_FRACTION']}",
flush=True
)
r2_service = R2Service()
data_dir = SIM_CONFIG["LOCAL_DATA_DIR"]
if not os.path.exists(data_dir):
print(f"❌ Missing data dir: {data_dir}", flush=True)
SIM_STATUS["running"] = False
return
print("🛠️ Bootstrapping...", flush=True)
mock_exchange = MockKuCoin(data_dir)
await mock_exchange.load_data(SIM_CONFIG["TEST_SYMBOLS"], REQUIRED_TFS)
# إنشاء المحفظة بمنطق الإيقاف الزمني المرن
step_ms = TF_MS.get(SIM_CONFIG["STEP_TF"], TF_MS['5m'])
virtual_wallet = VirtualExchange(
initial_balance=SIM_CONFIG["INITIAL_BALANCE"],
fee_rate=SIM_CONFIG["FEE_RATE"],
tp_pct=SIM_CONFIG["TP_PCT"],
sl_pct=SIM_CONFIG["SL_PCT"],
bar_ms=step_ms,
cooldown_bars=SIM_CONFIG["COOLDOWN_BARS"],
min_trade_usd=SIM_CONFIG["MIN_TRADE_USD"],
position_fraction=SIM_CONFIG["POSITION_FRACTION"],
max_concurrent=SIM_CONFIG["MAX_CONCURRENT_POSITIONS"],
soft_time_stop_bars=SIM_CONFIG["SOFT_TIME_STOP_BARS"],
hard_time_stop_bars=SIM_CONFIG["HARD_TIME_STOP_BARS"],
)
# تحديد مجال الزمن الآمن
min_ts, max_ts = None, None
for sym in SIM_CONFIG["TEST_SYMBOLS"]:
for tf in REQUIRED_TFS:
df = mock_exchange.data_store.get(sym, {}).get(tf)
if df is None or df.empty:
continue
mn = int(df['timestamp'].iloc[0]); mx = int(df['timestamp'].iloc[-1])
min_ts = mn if min_ts is None else min(min_ts, mn)
max_ts = mx if max_ts is None else max(max_ts, mx)
if min_ts is None or max_ts is None:
print("❌ لا توجد بيانات صالحة.", flush=True); SIM_STATUS["running"] = False; return
boot_requirements_ms = []
for tf in REQUIRED_TFS:
need = MIN_CANDLES_BASE[tf]
guard = 1 if tf == '1d' else 0
eff_need = max(need - guard, 1)
boot_requirements_ms.append(eff_need * TF_MS[tf])
boot_ms = max(boot_requirements_ms)
cfg_start = int(pd.Timestamp(SIM_CONFIG["START_DATE"]).timestamp() * 1000)
cfg_end = int(pd.Timestamp(SIM_CONFIG["END_DATE"]).timestamp() * 1000)
safe_start_ts = max(cfg_start, min_ts + boot_ms)
safe_end_ts = min(cfg_end, max_ts)
if safe_end_ts - safe_start_ts < step_ms:
print("❌ نافذة المحاكاة قصيرة.", flush=True); SIM_STATUS["running"] = False; return
print(
f"🗓️ Data span: [{pd.to_datetime(min_ts, unit='ms')} .. {pd.to_datetime(max_ts, unit='ms')}] | "
f"Sim window: [{pd.to_datetime(safe_start_ts, unit='ms')} .. {pd.to_datetime(safe_end_ts, unit='ms')}]",
flush=True
)
print("🧠 Initializing hybrid system...", flush=True)
data_manager = DataManager(None, None, r2_service=r2_service, mock_exchange=mock_exchange)
learning_hub = LearningHubManager(r2_service, None, data_manager, disable_llm=True) # NO_LLM
await learning_hub.initialize()
processor = MLProcessor(None, data_manager, learning_hub)
await processor.initialize()
# تمرير عتبة الدخول للنظام الهجين
setattr(data_manager, 'HYBRID_ENTRY_THRESHOLD', float(SIM_CONFIG["ENTRY_THRESHOLD"]))
print(f"✅ Ready. Threshold={getattr(data_manager, 'HYBRID_ENTRY_THRESHOLD', 'NA')}", flush=True)
# حلقة المحاكاة
timeline = range(safe_start_ts, safe_end_ts, step_ms)
total = len(timeline)
bar = tqdm(timeline, desc="Simulating", unit="step")
def log(msg):
try: bar.write(msg)
except Exception: print(msg, flush=True)
for i, ts in enumerate(bar):
# تحديث الوقت
mock_exchange.set_time(ts)
if i % 10 == 0:
SIM_STATUS["progress"] = (i / total) * 100
SIM_STATUS["current_balance"] = virtual_wallet.get_balance()
SIM_STATUS["trades_count"] = len(virtual_wallet.trade_history)
# إغلاقات قائمة (TP/SL/Soft/Hard)
prices = {s:(await mock_exchange.fetch_ticker(s))['last'] for s in SIM_CONFIG["TEST_SYMBOLS"]}
closed = virtual_wallet.update_positions(prices, ts)
for tr in closed:
log(f"[CLOSE] {tr['symbol']} reason={tr['close_reason']} pnl={tr['pnl_percent']:.2f}%")
await learning_hub.analyze_trade_and_learn(tr, tr['close_reason'])
# فتح مركز جديد إن لم توجد صفقات مفتوحة
if virtual_wallet.open_positions_count() < SIM_CONFIG["MAX_CONCURRENT_POSITIONS"]:
for symbol in SIM_CONFIG["TEST_SYMBOLS"]:
# تحقق توافر حزمة الفريمات
enough = True
for tf in REQUIRED_TFS:
candles = await mock_exchange.fetch_ohlcv(symbol, tf, limit=FETCH_LIMIT)
if not candles or len(candles) < MIN_CANDLES_REQ[tf]:
enough = False; break
if not enough:
continue
packet = {tf: await mock_exchange.fetch_ohlcv(symbol, tf, limit=FETCH_LIMIT) for tf in REQUIRED_TFS}
cur_price = prices.get(symbol, 0.0)
res = await processor.process_and_score_symbol_enhanced({
'symbol': symbol, 'ohlcv': packet, 'current_price': cur_price
})
if not res:
continue
final_s = float(res['enhanced_final_score'])
thr = getattr(data_manager, 'HYBRID_ENTRY_THRESHOLD', 0.75)
comps = dict(res.get('components', {})); comps['final_score'] = final_s
log(f"[SCORE] {symbol} final={final_s:.3f} thr={thr:.3f} comps={comps}")
if final_s >= thr and virtual_wallet.can_enter(symbol, ts):
if virtual_wallet.execute_buy(symbol, cur_price, ts, comps):
log(f"[BUY] {symbol} @ {cur_price} bal=${virtual_wallet.get_balance():.2f}")
break # نحافظ على صفقة واحدة فقط
# إغلاق ما تبقى عند نهاية النافذة
print("\n🏁 نهاية فترة المحاكاة. إغلاق الصفقات المتبقية...", flush=True)
final_ts = safe_end_ts
mock_exchange.set_time(final_ts)
end_prices = {s:(await mock_exchange.fetch_ticker(s))['last'] for s in SIM_CONFIG["TEST_SYMBOLS"]}
for s in list(virtual_wallet.positions.keys()):
tr = virtual_wallet.execute_sell(s, end_prices.get(s,0), final_ts, "END_OF_SIM")
if tr:
await learning_hub.analyze_trade_and_learn(tr, "END_OF_SIM")
print("💾 جاري حفظ النتائج إلى R2...", flush=True)
await r2_service.save_simulation_results(virtual_wallet.trade_history, virtual_wallet.metrics)
await learning_hub.shutdown()
SIM_STATUS.update({"running": False, "progress": 100.0})
print("\n🎉 === تقرير المحاكاة السريع ===", flush=True)
print(f"💰 الرصيد النهائي: ${virtual_wallet.get_balance():.2f} (من ${SIM_CONFIG['INITIAL_BALANCE']})", flush=True)
print(f"📈 إجمالي الربح/الخسارة: ${virtual_wallet.metrics['total_pnl_usd']:.2f}", flush=True)
print(f"🔢 عدد الصفقات: {len(virtual_wallet.trade_history)} (فوز: {virtual_wallet.metrics['wins']} | خسارة: {virtual_wallet.metrics['losses']})", flush=True)
print(f"📉 أقصى تراجع (Max Drawdown): {virtual_wallet.metrics['max_drawdown']*100:.2f}%", flush=True)
print("===============================\n", flush=True) |