Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
# app.py (
|
| 2 |
import os
|
| 3 |
import traceback
|
| 4 |
import signal
|
|
@@ -17,10 +17,15 @@ try:
|
|
| 17 |
from LLM import LLMService
|
| 18 |
from data_manager import DataManager
|
| 19 |
from ml_engine.processor import MLProcessor
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
from sentiment_news import SentimentAnalyzer
|
| 22 |
from trade_manager import TradeManager
|
| 23 |
-
import state #
|
| 24 |
from helpers import safe_float_conversion, validate_candidate_data_enhanced
|
| 25 |
except ImportError as e:
|
| 26 |
print(f"โ ุฎุทุฃ ูู ุงุณุชูุฑุงุฏ ุงููุญุฏุงุช: {e}")
|
|
@@ -30,22 +35,25 @@ except ImportError as e:
|
|
| 30 |
r2_service_global = None
|
| 31 |
data_manager_global = None
|
| 32 |
llm_service_global = None
|
| 33 |
-
|
|
|
|
|
|
|
| 34 |
trade_manager_global = None
|
| 35 |
sentiment_analyzer_global = None
|
| 36 |
symbol_whale_monitor_global = None
|
| 37 |
|
| 38 |
class StateManager:
|
| 39 |
-
# ๐ด ูุฐุง ูู ู
ุฏูุฑ ุงูุญุงูุฉ "ุงูู
ุญูู" ููุชุทุจูู
|
| 40 |
-
# ุงูุฐู ูุฏูุฑ ุงูุฃููุงู ุงูุฏุงุฎููุฉ ูู FastAPI
|
| 41 |
def __init__(self):
|
| 42 |
self.market_analysis_lock = asyncio.Lock()
|
| 43 |
-
self.trade_analysis_lock = asyncio.Lock()
|
| 44 |
self.initialization_complete = False
|
| 45 |
self.initialization_error = None
|
| 46 |
self.services_initialized = {
|
| 47 |
'r2_service': False, 'data_manager': False, 'llm_service': False,
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
| 49 |
'symbol_whale_monitor': False
|
| 50 |
}
|
| 51 |
|
|
@@ -67,13 +75,15 @@ class StateManager:
|
|
| 67 |
self.initialization_error = error
|
| 68 |
print(f"โ ุฎุทุฃ ูู ุงูุชููุฆุฉ: {error}")
|
| 69 |
|
| 70 |
-
# ๐ด ุฅูุดุงุก ูุณุฎุฉ ู
ุฏูุฑ ุงูุญุงูุฉ ุงูู
ุญูู
|
| 71 |
state_manager = StateManager()
|
| 72 |
|
| 73 |
async def initialize_services():
|
| 74 |
"""ุชููุฆุฉ ุฌู
ูุน ุงูุฎุฏู
ุงุช ุจุดูู ู
ููุตู"""
|
| 75 |
global r2_service_global, data_manager_global, llm_service_global
|
| 76 |
-
|
|
|
|
|
|
|
|
|
|
| 77 |
global symbol_whale_monitor_global
|
| 78 |
try:
|
| 79 |
print("๐ ุจุฏุก ุชููุฆุฉ ุงูุฎุฏู
ุงุช...")
|
|
@@ -87,53 +97,54 @@ async def initialize_services():
|
|
| 87 |
except Exception as e: print(f" โ ๏ธ ูุดู ุชููุฆุฉ ู
ุฑุงูุจ ุงูุญูุชุงู: {e}"); symbol_whale_monitor_global = None
|
| 88 |
print(" ๐ ุชููุฆุฉ DataManager..."); data_manager_global = DataManager(contracts_database, symbol_whale_monitor_global); await data_manager_global.initialize(); state_manager.set_service_initialized('data_manager'); print(" โ
DataManager ู
ููุฃุฉ")
|
| 89 |
|
| 90 |
-
# --- ๐ด ุจุฏุก ุงูุชุนุฏูู (ุงูุฑุจุท) ---
|
| 91 |
-
|
| 92 |
print(" ๐ ุชููุฆุฉ LLMService...");
|
| 93 |
llm_service_global = LLMService();
|
| 94 |
llm_service_global.r2_service = r2_service_global;
|
| 95 |
-
|
| 96 |
-
|
| 97 |
print(" ๐ ุชููุฆุฉ ู
ุญูู ุงูู
ุดุงุนุฑ...");
|
| 98 |
sentiment_analyzer_global = SentimentAnalyzer(data_manager_global);
|
| 99 |
state_manager.set_service_initialized('sentiment_analyzer');
|
| 100 |
print(" โ
ู
ุญูู ุงูู
ุดุงุนุฑ ู
ููุฃ")
|
| 101 |
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
-
#
|
| 109 |
-
llm_service_global.
|
| 110 |
-
state_manager.set_service_initialized('llm_service');
|
| 111 |
-
print(" โ
LLMService ู
ููุฃุฉ (ูู
ุฑุจูุทุฉ
|
| 112 |
|
| 113 |
print(" ๐ ุชููุฆุฉ ู
ุฏูุฑ ุงูุตููุงุช...");
|
| 114 |
-
#
|
| 115 |
trade_manager_global = TradeManager(
|
| 116 |
r2_service_global,
|
| 117 |
-
|
| 118 |
data_manager_global,
|
| 119 |
-
state_manager
|
| 120 |
)
|
| 121 |
state_manager.set_service_initialized('trade_manager');
|
| 122 |
-
print(" โ
ู
ุฏูุฑ ุงูุตููุงุช ู
ููุฃ (
|
| 123 |
-
|
| 124 |
-
# --- ๐ด ููุงูุฉ ุงูุชุนุฏูู ---
|
| 125 |
|
| 126 |
print("๐ฏ ุงูุชู
ูุช ุชููุฆุฉ ุฌู
ูุน ุงูุฎุฏู
ุงุช ุจูุฌุงุญ"); return True
|
| 127 |
except Exception as e: error_msg = f"ูุดู ุชููุฆุฉ ุงูุฎุฏู
ุงุช: {str(e)}"; print(f"โ {error_msg}"); state_manager.set_initialization_error(error_msg); return False
|
| 128 |
|
| 129 |
async def monitor_market_async():
|
| 130 |
-
"""
|
| 131 |
global data_manager_global, sentiment_analyzer_global
|
| 132 |
try:
|
| 133 |
if not await state_manager.wait_for_initialization(): print("โ ูุดู ุชููุฆุฉ ุงูุฎุฏู
ุงุช - ุฅููุงู ู
ุฑุงูุจุฉ ุงูุณูู"); return
|
| 134 |
while True:
|
| 135 |
try:
|
| 136 |
-
# ๐ด ุงุณุชุฎุฏุงู
ููู ู
ุฏูุฑ ุงูุญุงูุฉ ุงูู
ุญูู
|
| 137 |
async with state_manager.market_analysis_lock:
|
| 138 |
market_context = await sentiment_analyzer_global.get_market_sentiment()
|
| 139 |
if not market_context: state.MARKET_STATE_OK = True; await asyncio.sleep(60); continue
|
|
@@ -149,12 +160,35 @@ async def monitor_market_async():
|
|
| 149 |
except Exception as error: print(f"โ ุฎุทุฃ ุฃุซูุงุก ู
ุฑุงูุจุฉ ุงูุณูู: {error}"); state.MARKET_STATE_OK = True; await asyncio.sleep(60)
|
| 150 |
except Exception as e: print(f"โ ูุดู ุชุดุบูู ู
ุฑุงูุจุฉ ุงูุณูู: {e}")
|
| 151 |
|
| 152 |
-
|
| 153 |
-
|
|
|
|
| 154 |
"""
|
| 155 |
-
|
| 156 |
-
- ุชุณุชุฎุฏู
ุจูุงูุงุช ุงูุญูุชุงู ุงูู
ุญู
ูุฉ ู
ุณุจููุง
|
| 157 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
try:
|
| 159 |
batch_tasks = []
|
| 160 |
for symbol_data in batch:
|
|
@@ -188,18 +222,13 @@ async def process_batch_parallel(batch, ml_processor, batch_num, total_batches,
|
|
| 188 |
return {'success': successful_results, 'low_score': low_score_results, 'failures': failed_results}
|
| 189 |
|
| 190 |
except Exception as error:
|
| 191 |
-
print(f"โ [
|
| 192 |
return {'success': [], 'low_score': [], 'failures': []}
|
| 193 |
|
| 194 |
|
| 195 |
async def run_3_layer_analysis():
|
| 196 |
"""
|
| 197 |
-
(
|
| 198 |
-
ุงูุทุจูุฉ 1: data_manager - ุงููุญุต ุงูุณุฑูุน
|
| 199 |
-
ุงูุทุจูุฉ 1.5: ุฌูุจ ุจูุงูุงุช ุงูุญูุชุงู ุจุดูู ู
ููุตู (ุบูุฑ ู
ุนุฑูู)
|
| 200 |
-
ุงูุทุจูุฉ 2: MLProcessor - ุงูุชุญููู ุงูู
ุชุฏูู (ูุณุชุฎุฏู
ู
ููุช ูุงุฑูู ุงูู
ุฑุญูุฉ 1)
|
| 201 |
-
ุงูุทุจูุฉ 2.5: (ุฌุฏูุฏ) ู
ููุช ูุงุฑูู (ุงูู
ุฑุญูุฉ 2+3) ูุฃูุถู 10 ุนู
ูุงุช
|
| 202 |
-
ุงูุทุจูุฉ 3: LLMService - ุงููู
ูุฐุฌ ุงูุถุฎู
(ูุณุชุฎุฏู
ูุชุงุฆุฌ ุงูู
ุฑุญูุฉ 2.5)
|
| 203 |
"""
|
| 204 |
|
| 205 |
layer1_candidates = []
|
|
@@ -211,20 +240,20 @@ async def run_3_layer_analysis():
|
|
| 211 |
preloaded_whale_data_dict = {}
|
| 212 |
|
| 213 |
try:
|
| 214 |
-
print("๐ฏ
|
| 215 |
|
| 216 |
-
if not await state_manager.wait_for_initialization(): print("โ
|
| 217 |
|
| 218 |
-
#
|
| 219 |
-
print("\n๐
|
| 220 |
layer1_candidates = await data_manager_global.layer1_rapid_screening()
|
| 221 |
-
if not layer1_candidates: print("โ
|
| 222 |
-
print(f"โ
|
| 223 |
layer1_symbols = [c['symbol'] for c in layer1_candidates]
|
| 224 |
|
| 225 |
-
#
|
| 226 |
start_whale_fetch = time.time()
|
| 227 |
-
print(f"\n๐
|
| 228 |
async def fetch_whale_data_task(symbols, results_dict):
|
| 229 |
WHALE_FETCH_CONCURRENCY = 3
|
| 230 |
semaphore = asyncio.Semaphore(WHALE_FETCH_CONCURRENCY)
|
|
@@ -235,69 +264,59 @@ async def run_3_layer_analysis():
|
|
| 235 |
data = await data_manager_global.get_whale_data_for_symbol(symbol)
|
| 236 |
if data: results_dict[symbol] = data
|
| 237 |
except Exception as e:
|
| 238 |
-
print(f" โ [Whale Fetch] ูุดู ุฌูุจ ุจูุงูุงุช ุงูุญูุชุงู ูู {symbol}: {e}")
|
| 239 |
results_dict[symbol] = {'data_available': False, 'error': str(e)}
|
| 240 |
for symbol in symbols: tasks.append(asyncio.create_task(get_data_with_semaphore(symbol)))
|
| 241 |
await asyncio.gather(*tasks)
|
| 242 |
whale_fetcher_task = asyncio.create_task(fetch_whale_data_task(layer1_symbols, preloaded_whale_data_dict))
|
| 243 |
-
print(" โณ ู
ูู
ุฉ ุฌูุจ ุจูุงูุงุช ุงูุญูุชุงู ุชุนู
ู ูู ุงูุฎูููุฉ...")
|
| 244 |
|
| 245 |
-
#
|
| 246 |
DATA_QUEUE_MAX_SIZE = 2
|
| 247 |
ohlcv_data_queue = asyncio.Queue(maxsize=DATA_QUEUE_MAX_SIZE)
|
| 248 |
ml_results_list = []
|
| 249 |
market_context = await data_manager_global.get_market_context_async()
|
| 250 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
batch_size = 15
|
| 252 |
total_batches = (len(layer1_candidates) + batch_size - 1) // batch_size
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
# ูุธููุฉ ุงูู
ุณุชููู (ML Consumer)
|
| 256 |
async def ml_consumer_task(queue: asyncio.Queue, results_list: list, whale_data_store: dict):
|
| 257 |
batch_num = 0
|
| 258 |
while True:
|
| 259 |
try:
|
| 260 |
batch_data = await queue.get()
|
| 261 |
-
if batch_data is None: queue.task_done();
|
| 262 |
batch_num += 1
|
| 263 |
-
print(f" ๐ฌ [ML Consumer] ุงุณุชูู
ุฏูุนุฉ OHLCV {batch_num}/{total_batches} ({len(batch_data)} ุนู
ูุฉ)")
|
| 264 |
-
# ๐ด ููุง ูุชู
ุงุณุชุฎุฏุงู
ู
ููุช ูุงุฑูู (ุงูู
ุฑุญูุฉ 1) ุงูุณุฑูุน
|
| 265 |
batch_results_dict = await process_batch_parallel(
|
| 266 |
batch_data, ml_processor, batch_num, total_batches, whale_data_store
|
| 267 |
)
|
| 268 |
results_list.append(batch_results_dict)
|
| 269 |
queue.task_done()
|
| 270 |
-
|
| 271 |
-
except Exception as e: print(f"โ [ML Consumer] ุฎุทุฃ ูุงุฏุญ: {e}"); traceback.print_exc(); queue.task_done()
|
| 272 |
|
| 273 |
-
#
|
| 274 |
-
print(" โถ๏ธ [ML Consumer] ุจุฏุก ุชุดุบูู ู
ูู
ุฉ ุงูู
ุณุชููู...")
|
| 275 |
consumer_task = asyncio.create_task(ml_consumer_task(ohlcv_data_queue, ml_results_list, preloaded_whale_data_dict))
|
| 276 |
-
print(" โถ๏ธ [OHLCV Producer] ุจุฏุก ุชุดุบูู ู
ูู
ุฉ ุงูู
ูุชุฌ (ุชุฏูู ุจูุงูุงุช OHLCV)...")
|
| 277 |
producer_task = asyncio.create_task(data_manager_global.stream_ohlcv_data(layer1_symbols, ohlcv_data_queue))
|
| 278 |
-
|
| 279 |
-
# ุงูุชุธุงุฑ ุงูุชูุงุก ุงูู
ูุชุฌ ูุงูู
ุณุชููู
|
| 280 |
-
await producer_task; print(" โ
[OHLCV Producer] ุฃููู ุฌูุจ ุฌู
ูุน ุจูุงูุงุช OHLCV.")
|
| 281 |
await ohlcv_data_queue.put(None)
|
| 282 |
await ohlcv_data_queue.join()
|
| 283 |
-
await consumer_task;
|
| 284 |
|
| 285 |
-
#
|
| 286 |
-
print(" โณ ุงูุชุธุงุฑ ุงูุชู
ุงู ู
ูู
ุฉ ุฌูุจ ุจูุงูุงุช ุงูุญูุชุงู (ุจุญุฏ ุฃูุตู ููู
ููุฉ)...")
|
| 287 |
WHALE_FETCH_TIMEOUT_SECONDS = 180
|
| 288 |
try:
|
| 289 |
await asyncio.wait_for(whale_fetcher_task, timeout=WHALE_FETCH_TIMEOUT_SECONDS)
|
| 290 |
-
end_whale_fetch = time.time()
|
| 291 |
-
print(f" โ
ุงูุชู
ู ุฌูุจ ุจูุงูุงุช ุงูุญูุชุงู ูู {end_whale_fetch - start_whale_fetch:.2f} ุซุงููุฉ. ุชู
ุฌูุจ/ู
ุญุงููุฉ ุฌูุจ ุจูุงูุงุช ูู {len(preloaded_whale_data_dict)} ุนู
ูุฉ.")
|
| 292 |
except asyncio.TimeoutError:
|
| 293 |
-
|
| 294 |
-
print(f" โ ๏ธ ุงูุชูุช ู
ููุฉ ุงูุชุธุงุฑ ุฌูุจ ุจูุงูุงุช ุงูุญูุชุงู ({WHALE_FETCH_TIMEOUT_SECONDS} ุซุงููุฉ)! ุชู
ุฌูุจ/ู
ุญุงููุฉ ุฌูุจ ุจูุงูุงุช ูู {len(preloaded_whale_data_dict)} ุนู
ูุฉ ุญุชู ุงูุขู.")
|
| 295 |
except Exception as whale_task_err:
|
| 296 |
-
|
| 297 |
-
print(f" โ ุญุฏุซ ุฎุทุฃ ุบูุฑ ู
ุชููุน ุฃุซูุงุก ุงูุชุธุงุฑ ู
ูู
ุฉ ุฌูุจ ุงูุญูุชุงู: {whale_task_err}")
|
| 298 |
|
| 299 |
-
#
|
| 300 |
-
print("๐
|
| 301 |
for batch_result in ml_results_list:
|
| 302 |
for success_item in batch_result['success']:
|
| 303 |
symbol = success_item['symbol']
|
|
@@ -310,154 +329,94 @@ async def run_3_layer_analysis():
|
|
| 310 |
layer2_candidates.append(success_item)
|
| 311 |
all_low_score_candidates.extend(batch_result['low_score'])
|
| 312 |
all_failed_candidates.extend(batch_result['failures'])
|
| 313 |
-
|
| 314 |
-
print(f"โ
ุงูุชู
ู ุงูุชุญููู ุงูู
ุชูุฏู
(MC-Phase1): {len(layer2_candidates)} ูุฌุงุญ (ุนุงูู) | {len(all_low_score_candidates)} ูุฌุงุญ (ู
ูุฎูุถ) | {len(all_failed_candidates)} ูุดู")
|
| 315 |
-
if not layer2_candidates: print("โ ูู
ูุชู
ุงูุนุซูุฑ ุนูู ู
ุฑุดุญูู ูู ุงูุทุจูุฉ 2")
|
| 316 |
|
| 317 |
-
|
|
|
|
|
|
|
| 318 |
layer2_candidates.sort(key=lambda x: x.get('enhanced_final_score', 0), reverse=True)
|
| 319 |
target_count = min(10, len(layer2_candidates))
|
| 320 |
final_layer2_candidates = layer2_candidates[:target_count]
|
| 321 |
-
print(f"๐ฏ ุชู
ุงุฎุชูุงุฑ {len(final_layer2_candidates)} ุนู
ูุฉ ููุทุจูุฉ 2.5 (ุงูุฃููู ููุท)")
|
| 322 |
-
|
| 323 |
-
# ๐ด --- ุจุฏุก ุงูุทุจูุฉ 2.5: ุงูุชุญููู ุงูู
ุชูุฏู
(GARCH+LGBM) --- ๐ด
|
| 324 |
-
print(f"\n๐ฌ ุงูุทุจูุฉ 2.5: ุชุดุบูู ุงูุชุญููู ุงูู
ุชูุฏู
(GARCH+LGBM) ุนูู ุฃูุถู {len(final_layer2_candidates)} ู
ุฑุดุญ...")
|
| 325 |
-
advanced_mc_analyzer = ml_processor.monte_carlo_analyzer # ุงูุญุตูู ุนูู ู
ุญูู ู
ููุช ูุงุฑูู
|
| 326 |
|
|
|
|
|
|
|
|
|
|
| 327 |
updated_candidates_for_llm = []
|
| 328 |
for candidate in final_layer2_candidates:
|
| 329 |
symbol = candidate.get('symbol', 'UNKNOWN')
|
| 330 |
try:
|
| 331 |
-
print(f" ๐ [Advanced MC] ุชุญููู {symbol}...")
|
| 332 |
-
# ุงุณุชุฏุนุงุก ุงูุฏุงูุฉ ุงูุฌุฏูุฏุฉ ุงูู
ุชูุฏู
ุฉ
|
| 333 |
advanced_mc_results = await advanced_mc_analyzer.generate_1h_distribution_advanced(
|
| 334 |
candidate.get('ohlcv')
|
| 335 |
)
|
| 336 |
-
|
| 337 |
if advanced_mc_results and advanced_mc_results.get('simulation_model') == 'Phase2_GARCH_LGBM':
|
| 338 |
-
print(f" โ
[Advanced MC] {symbol} - ุชู
ุงูุชุญุฏูุซ ุจูู
ูุฐุฌ GARCH/LGBM.")
|
| 339 |
-
# ุงุณุชุจุฏุงู ูุชุงุฆุฌ ุงูู
ุฑุญูุฉ 1 ุจูุชุงุฆุฌ ุงูู
ุฑุญูุฉ 2+3
|
| 340 |
candidate['monte_carlo_distribution'] = advanced_mc_results
|
| 341 |
candidate['monte_carlo_probability'] = advanced_mc_results.get('probability_of_gain', 0)
|
| 342 |
-
candidate['advanced_mc_run'] = True
|
| 343 |
else:
|
| 344 |
-
|
| 345 |
-
candidate['advanced_mc_run'] = False # ุฅุถุงูุฉ ุนูุงู
ุฉ ููุชุฏููู
|
| 346 |
-
|
| 347 |
updated_candidates_for_llm.append(candidate)
|
| 348 |
-
|
| 349 |
except Exception as e:
|
| 350 |
-
print(f" โ [Advanced MC] {symbol} -
|
| 351 |
candidate['advanced_mc_run'] = False
|
| 352 |
updated_candidates_for_llm.append(candidate)
|
| 353 |
-
|
| 354 |
-
final_layer2_candidates = updated_candidates_for_llm # ุงุณุชุฎุฏุงู
ุงููุงุฆู
ุฉ ุงูู
ุญุฏุซุฉ ููุทุจูุฉ 3
|
| 355 |
-
# ๐ด --- ููุงูุฉ ุงูุทุจูุฉ 2.5 --- ๐ด
|
| 356 |
|
| 357 |
await r2_service_global.save_candidates_async(final_layer2_candidates)
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
if mc_dist:
|
| 364 |
-
mc_model = mc_dist.get('simulation_model', 'Phase1')
|
| 365 |
-
mc_pi_90 = mc_dist.get('prediction_interval_90', [0,0])
|
| 366 |
-
mc_var = mc_dist.get('risk_metrics', {}).get('VaR_95_value', 0)
|
| 367 |
-
print(f" ๐ฏ ู
ููุช ูุงุฑูู ({mc_model}): 90% PI [{mc_pi_90[0]:.4f} - {mc_pi_90[1]:.4f}] | VaR: ${mc_var:.4f}")
|
| 368 |
-
|
| 369 |
-
print(f" ๐ฏ ุงุณุชุฑุงุชูุฌูุฉ: {strategy} | ูู
ุท: {pattern}")
|
| 370 |
-
whale_data = candidate.get('whale_data')
|
| 371 |
-
if whale_data and whale_data.get('data_available'): signal = whale_data.get('trading_signal', {}); print(f" ๐ ุญูุชุงู: {signal.get('action', 'HOLD')} (ุซูุฉ: {signal.get('confidence', 0):.2f}){' โ ๏ธ' if signal.get('critical_alert') else ''}")
|
| 372 |
-
elif whale_data and whale_data.get('error'): print(f" ๐ ุญูุชุงู: ุฎุทุฃ ({whale_data.get('error')[:50]}...)")
|
| 373 |
-
|
| 374 |
-
# ุงูุทุจูุฉ 3
|
| 375 |
-
print("\n๐ง ุงูุทุจูุฉ 3: ุงูุชุญููู ุจุงููู
ูุฐุฌ ุงูุถุฎู
(LLMService)...")
|
| 376 |
-
# ๐ด ุงุณุชุฎุฏุงู
ุงููุงุฆู
ุฉ ุงูู
ุญุฏุซุฉ
|
| 377 |
for candidate in final_layer2_candidates:
|
| 378 |
try:
|
| 379 |
-
symbol = candidate['symbol']
|
| 380 |
ohlcv_data = candidate.get('ohlcv');
|
| 381 |
-
if not ohlcv_data:
|
| 382 |
candidate['raw_ohlcv'] = ohlcv_data
|
| 383 |
-
|
| 384 |
-
if total_candles < 30:
|
| 385 |
-
print(f" ๐ ุฅุฑุณุงู {symbol} ูููู
ูุฐุฌ: {total_candles} ุดู
ุนุฉ ูู {timeframes_count} ุฅุทุงุฑ ุฒู
ูู")
|
| 386 |
|
| 387 |
-
#
|
| 388 |
-
candidate['sentiment_data'] = await data_manager_global.get_market_context_async()
|
| 389 |
|
| 390 |
llm_analysis = await llm_service_global.get_trading_decision(candidate)
|
| 391 |
|
| 392 |
if llm_analysis and llm_analysis.get('action') in ['BUY']:
|
| 393 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 394 |
final_opportunities.append(opportunity)
|
| 395 |
-
print(f" โ
{symbol}: {llm_analysis.get('action')} -
|
| 396 |
-
else:
|
| 397 |
-
|
|
|
|
| 398 |
|
| 399 |
if final_opportunities:
|
| 400 |
final_opportunities.sort(key=lambda x: (x['llm_confidence'] + x['enhanced_score']) / 2, reverse=True)
|
| 401 |
-
print(f"\n๐ ุงููุธุงู
ุงูุทุจูู ุงูุชู
ู: {len(final_opportunities)} ูุฑุตุฉ ุชุฏุงูู")
|
| 402 |
-
for i, opportunity in enumerate(final_opportunities[:5]): print(f" {i+1}. {opportunity['symbol']}: {opportunity['decision'].get('action')} - ุซูุฉ: {opportunity['llm_confidence']:.2f} - ุฃุทุฑ: {opportunity['timeframes_count']}")
|
| 403 |
|
| 404 |
-
#
|
| 405 |
-
try:
|
| 406 |
-
top_10_detailed_summary = []
|
| 407 |
-
for c in final_layer2_candidates:
|
| 408 |
-
whale_summary = "Not Available"; whale_data = c.get('whale_data')
|
| 409 |
-
if whale_data and whale_data.get('data_available'): signal = whale_data.get('trading_signal', {}); action = signal.get('action', 'HOLD'); confidence = signal.get('confidence', 0); reason_preview = signal.get('reason', 'N/A')[:75] + "..." if signal.get('reason') else 'N/A'; whale_summary = f"Action: {action}, Conf: {confidence:.2f}, Alert: {signal.get('critical_alert', False)}, Reason: {reason_preview}"
|
| 410 |
-
elif whale_data and whale_data.get('error'): whale_summary = f"Error: {whale_data['error'][:50]}..."
|
| 411 |
-
|
| 412 |
-
mc_summary = "N/A"
|
| 413 |
-
mc_dist = c.get('monte_carlo_distribution')
|
| 414 |
-
if mc_dist:
|
| 415 |
-
mc_model = mc_dist.get('simulation_model', 'Unknown')
|
| 416 |
-
if mc_model == 'Phase2_GARCH_LGBM':
|
| 417 |
-
drift = mc_dist.get('forecasted_drift_lgbm', 0)
|
| 418 |
-
vol = mc_dist.get('forecasted_vol_garch', 0)
|
| 419 |
-
mc_summary = f"Phase2_GARCH(vol={vol:.5f})_LGBM(drift={drift:.5f})"
|
| 420 |
-
else: # Phase1 or Error
|
| 421 |
-
var_val = mc_dist.get('risk_metrics', {}).get('VaR_95_value', 0)
|
| 422 |
-
mc_summary = f"{mc_model}_VaR({var_val:.4f})"
|
| 423 |
-
|
| 424 |
-
top_10_detailed_summary.append({
|
| 425 |
-
"symbol": c.get('symbol'),
|
| 426 |
-
"score": c.get('enhanced_final_score', 0),
|
| 427 |
-
"timeframes": f"{c.get('successful_timeframes', 'N/A')}/6",
|
| 428 |
-
"whale_data_summary": whale_summary,
|
| 429 |
-
"strategy": c.get('target_strategy', 'N/A'),
|
| 430 |
-
"pattern": c.get('pattern_analysis', {}).get('pattern_detected', 'N/A'),
|
| 431 |
-
"mc_analysis_level": mc_summary
|
| 432 |
-
})
|
| 433 |
-
|
| 434 |
-
other_successful_candidates = layer2_candidates[target_count:]
|
| 435 |
-
other_success_summary = [{"symbol": c['symbol'], "score": c.get('enhanced_final_score', 0), "timeframes": f"{c.get('successful_timeframes', 'N/A')}/6", "whale_data": "Available" if c.get('whale_data', {}).get('data_available') else ("Error" if c.get('whale_data', {}).get('error') else "Not Available")} for c in other_successful_candidates]
|
| 436 |
-
low_score_summary = [{"symbol": c['symbol'], "score": c.get('enhanced_final_score', 0), "timeframes": f"{c.get('successful_timeframes', 'N/A')}/6", "whale_data": "Available" if c.get('whale_data', {}).get('data_available') else ("Error" if c.get('whale_data', {}).get('error') else "Not Available")} for c in all_low_score_candidates]
|
| 437 |
-
audit_data = { "timestamp": datetime.now().isoformat(), "total_layer1_candidates": len(layer1_candidates), "total_processed_in_layer2": len(layer2_candidates) + len(all_low_score_candidates) + len(all_failed_candidates), "counts": {"sent_to_llm": len(final_layer2_candidates), "success_not_top_10": len(other_successful_candidates), "success_low_score": len(all_low_score_candidates), "failures": len(all_failed_candidates)}, "top_candidates_for_llm": top_10_detailed_summary, "other_successful_candidates": other_success_summary, "low_score_candidates": low_score_summary, "failed_candidates": all_failed_candidates, }
|
| 438 |
-
await r2_service_global.save_analysis_audit_log_async(audit_data)
|
| 439 |
-
print(f"โ
ุชู
ุญูุธ ุณุฌู ุชุฏููู ุงูุชุญููู ูู R2 (ู
ุน ุชูุงุตูู MC ุงูู
ุชูุฏู
ุฉ).")
|
| 440 |
-
except Exception as audit_error: print(f"โ ูุดู ุญูุธ ุณุฌู ุชุฏููู ุงูุชุญููู: {audit_error}"); traceback.print_exc()
|
| 441 |
|
| 442 |
-
if not final_opportunities: print("โ
|
| 443 |
return final_opportunities[0] if final_opportunities else None
|
| 444 |
|
| 445 |
except Exception as error:
|
| 446 |
-
print(f"โ
|
| 447 |
-
try: # Log partial audit on failure
|
| 448 |
-
audit_data = { "timestamp": datetime.now().isoformat(), "status": "FAILED", "error": str(error), "traceback": traceback.format_exc(), "total_layer1_candidates": len(layer1_candidates), "counts": {"sent_to_llm": 0, "success_not_top_10": 0, "success_low_score": len(all_low_score_candidates), "failures": len(all_failed_candidates)}, "failed_candidates": all_failed_candidates }
|
| 449 |
-
await r2_service_global.save_analysis_audit_log_async(audit_data)
|
| 450 |
-
print("โ ๏ธ ุชู
ุญูุธ ุณุฌู ุชุฏููู ุฌุฒุฆู ุจุนุฏ ุงููุดู.")
|
| 451 |
-
except Exception as audit_fail_error: print(f"โ ูุดู ุญูุธ ุณุฌู ุงูุชุฏููู ุฃุซูุงุก ู
ุนุงูุฌุฉ ุฎุทุฃ ุขุฎุฑ: {audit_fail_error}")
|
| 452 |
return None
|
| 453 |
|
| 454 |
async def re_analyze_open_trade_async(trade_data):
|
| 455 |
-
"""
|
| 456 |
symbol = trade_data.get('symbol')
|
| 457 |
try:
|
| 458 |
-
# ๐ด ุงุณุชุฎุฏุงู
ููู ู
ุฏูุฑ ุงูุญุงูุฉ ุงูู
ุญูู
|
| 459 |
async with state_manager.trade_analysis_lock:
|
| 460 |
-
print(f"๐ [Re-Analyze]
|
| 461 |
market_context = await data_manager_global.get_market_context_async()
|
| 462 |
ohlcv_data_list = []
|
| 463 |
temp_queue = asyncio.Queue()
|
|
@@ -470,9 +429,9 @@ async def re_analyze_open_trade_async(trade_data):
|
|
| 470 |
temp_queue.task_done()
|
| 471 |
except asyncio.TimeoutError:
|
| 472 |
if temp_queue.empty(): break
|
| 473 |
-
except Exception
|
| 474 |
|
| 475 |
-
if not ohlcv_data_list: print(f"โ ๏ธ
|
| 476 |
ohlcv_data = ohlcv_data_list[0]
|
| 477 |
|
| 478 |
l1_data = await data_manager_global._get_detailed_symbol_data(symbol)
|
|
@@ -480,15 +439,17 @@ async def re_analyze_open_trade_async(trade_data):
|
|
| 480 |
|
| 481 |
re_analysis_whale_data = await data_manager_global.get_whale_data_for_symbol(symbol)
|
| 482 |
|
| 483 |
-
|
|
|
|
|
|
|
|
|
|
| 484 |
|
| 485 |
-
print(f"๐ [Re-Analyze]
|
| 486 |
advanced_mc_results = await ml_processor.monte_carlo_analyzer.generate_1h_distribution_advanced(
|
| 487 |
ohlcv_data.get('ohlcv')
|
| 488 |
)
|
| 489 |
|
| 490 |
processed_data = await ml_processor.process_and_score_symbol_enhanced(ohlcv_data, {symbol: re_analysis_whale_data} if re_analysis_whale_data else {})
|
| 491 |
-
|
| 492 |
if not processed_data: return None
|
| 493 |
|
| 494 |
if advanced_mc_results:
|
|
@@ -497,47 +458,41 @@ async def re_analyze_open_trade_async(trade_data):
|
|
| 497 |
|
| 498 |
processed_data['raw_ohlcv'] = ohlcv_data.get('raw_ohlcv') or ohlcv_data.get('ohlcv')
|
| 499 |
processed_data['ohlcv'] = processed_data['raw_ohlcv']
|
| 500 |
-
|
| 501 |
-
# ๐ด ุฅุถุงูุฉ ุณูุงู ุงูุณูู ุฅูู ุงูุจูุงูุงุช ุงูู
ุฑุณูุฉ ูุฅุนุงุฏุฉ ุงูุชุญููู
|
| 502 |
processed_data['sentiment_data'] = market_context
|
| 503 |
|
| 504 |
re_analysis_decision = await llm_service_global.re_analyze_trade_async(trade_data, processed_data)
|
| 505 |
|
| 506 |
if re_analysis_decision:
|
| 507 |
await r2_service_global.save_system_logs_async({ "trade_reanalyzed": True, "symbol": symbol, "action": re_analysis_decision.get('action'), 'strategy': re_analysis_decision.get('strategy', 'GENERIC') })
|
| 508 |
-
print(f"โ
[Re-Analyze]
|
| 509 |
return {"symbol": symbol, "decision": re_analysis_decision, "current_price": processed_data.get('current_price')}
|
| 510 |
else: return None
|
| 511 |
except Exception as error: await r2_service_global.save_system_logs_async({ "reanalysis_error": True, "symbol": symbol, "error": str(error) }); print(f"โ Error in re_analyze_open_trade_async for {symbol}: {error}"); traceback.print_exc(); return None
|
| 512 |
|
| 513 |
|
| 514 |
async def run_bot_cycle_async():
|
| 515 |
-
"""
|
| 516 |
try:
|
| 517 |
-
if not await state_manager.wait_for_initialization(): print("โ
|
| 518 |
-
print("๐
|
| 519 |
-
if not r2_service_global.acquire_lock(): print("โ
|
| 520 |
|
| 521 |
open_trades = []
|
| 522 |
try:
|
| 523 |
-
open_trades = await trade_manager_global.get_open_trades(); print(f"๐
|
| 524 |
-
should_look_for_new_trade = len(open_trades) == 0
|
| 525 |
if open_trades:
|
| 526 |
now = datetime.now()
|
| 527 |
trades_to_reanalyze = [t for t in open_trades if now >= datetime.fromisoformat(t.get('expected_target_time', now.isoformat()))]
|
| 528 |
if trades_to_reanalyze:
|
| 529 |
-
print(f"๐
|
| 530 |
-
# ๐ด ุณูุชู
ุชุดุบูู ูุฐู ุงูุฏุงูุฉ (re_analyze_open_trade_async)
|
| 531 |
-
# ูุณุชููู
ุจุงุณุชุฎุฏุงู
ููู "state_manager.trade_analysis_lock"
|
| 532 |
-
# ู
ู
ุง ูููู ุงูู
ุฑุงูุจ ุงูุชูุชููู ู
ุคูุชุงู ูุชูู ุงูุนู
ูุฉ
|
| 533 |
reanalysis_results = await asyncio.gather(*[re_analyze_open_trade_async(trade) for trade in trades_to_reanalyze], return_exceptions=True)
|
| 534 |
for i, result in enumerate(reanalysis_results):
|
| 535 |
trade = trades_to_reanalyze[i]
|
| 536 |
-
if isinstance(result, Exception): print(f" โ
|
| 537 |
-
elif result and result['decision'].get('action') == "CLOSE_TRADE": print(f" โ
|
| 538 |
-
elif result and result['decision'].get('action') == "UPDATE_TRADE": print(f" โ
|
| 539 |
-
elif result: print(f" โน๏ธ
|
| 540 |
-
else: print(f" โ ๏ธ
|
| 541 |
|
| 542 |
current_open_trades_count = len(await trade_manager_global.get_open_trades())
|
| 543 |
should_look_for_new_trade = current_open_trades_count == 0
|
|
@@ -545,18 +500,45 @@ async def run_bot_cycle_async():
|
|
| 545 |
if should_look_for_new_trade:
|
| 546 |
portfolio_state = await r2_service_global.get_portfolio_state_async(); current_capital = portfolio_state.get("current_capital_usd", 0)
|
| 547 |
if current_capital > 1:
|
| 548 |
-
print("๐ฏ
|
| 549 |
best_opportunity = await run_3_layer_analysis()
|
|
|
|
|
|
|
|
|
|
| 550 |
if best_opportunity:
|
| 551 |
-
print(f"โ
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 556 |
finally:
|
| 557 |
if r2_service_global.lock_acquired: r2_service_global.release_lock()
|
| 558 |
await r2_service_global.save_system_logs_async({ "cycle_completed": True, "open_trades": len(open_trades)})
|
| 559 |
-
print("โ
|
| 560 |
|
| 561 |
except Exception as error:
|
| 562 |
print(f"โ Unhandled error in main cycle: {error}"); traceback.print_exc()
|
|
@@ -565,17 +547,24 @@ async def run_bot_cycle_async():
|
|
| 565 |
|
| 566 |
@asynccontextmanager
|
| 567 |
async def lifespan(application: FastAPI):
|
| 568 |
-
"""
|
| 569 |
-
print("๐
|
| 570 |
try:
|
| 571 |
success = await initialize_services()
|
| 572 |
-
if not success: print("โ
|
|
|
|
| 573 |
asyncio.create_task(monitor_market_async())
|
| 574 |
-
# ๐ด ุจุฏุก ู
ุฑุงูุจุฉ ุงูุตููุงุช (ุงูู
ุฑุงูุจ ุงูุชูุชููู)
|
| 575 |
asyncio.create_task(trade_manager_global.start_trade_monitoring())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 576 |
await r2_service_global.save_system_logs_async({"application_started": True})
|
| 577 |
-
print("๐ฏ
|
| 578 |
-
print(" -> ๐
|
|
|
|
| 579 |
yield
|
| 580 |
except Exception as error:
|
| 581 |
print(f"โ Application startup failed: {error}");
|
|
@@ -587,42 +576,49 @@ async def lifespan(application: FastAPI):
|
|
| 587 |
await cleanup_on_shutdown()
|
| 588 |
|
| 589 |
|
| 590 |
-
application = FastAPI(lifespan=lifespan, title="AI Trading Bot", description="
|
| 591 |
|
|
|
|
| 592 |
@application.get("/")
|
| 593 |
-
async def root(): return {"message": "
|
| 594 |
@application.get("/run-cycle")
|
| 595 |
async def run_cycle_api():
|
| 596 |
-
if not state_manager.initialization_complete: raise HTTPException(status_code=503, detail="
|
| 597 |
asyncio.create_task(run_bot_cycle_async())
|
| 598 |
-
return {"message": "Bot cycle initiated (
|
| 599 |
@application.get("/health")
|
| 600 |
-
async def health_check(): return {"status": "healthy" if state_manager.initialization_complete else "initializing", "initialization_complete": state_manager.initialization_complete, "services_initialized": state_manager.services_initialized, "initialization_error": state_manager.initialization_error, "timestamp": datetime.now().isoformat(), "system_architecture": "3-Layer Analysis
|
| 601 |
@application.get("/analyze-market")
|
| 602 |
async def analyze_market_api():
|
| 603 |
-
if not state_manager.initialization_complete: raise HTTPException(status_code=503, detail="
|
| 604 |
result = await run_3_layer_analysis()
|
| 605 |
if result: return {"opportunity_found": True, "symbol": result['symbol'], "action": result['decision'].get('action'), "confidence": result['llm_confidence'], "strategy": result['strategy'], "exit_profile": result['decision'].get('exit_profile')}
|
| 606 |
else: return {"opportunity_found": False, "message": "No suitable opportunities found"}
|
| 607 |
@application.get("/portfolio")
|
| 608 |
async def get_portfolio_api():
|
| 609 |
-
if not state_manager.initialization_complete: raise HTTPException(status_code=503, detail="
|
| 610 |
try: portfolio_state = await r2_service_global.get_portfolio_state_async(); open_trades = await trade_manager_global.get_open_trades(); return {"portfolio": portfolio_state, "open_trades": open_trades, "timestamp": datetime.now().isoformat()}
|
| 611 |
-
except Exception as e: raise HTTPException(status_code=500, detail=f"
|
| 612 |
@application.get("/system-status")
|
| 613 |
async def get_system_status(): monitoring_status = trade_manager_global.get_monitoring_status() if trade_manager_global else {}; return {"initialization_complete": state_manager.initialization_complete, "services_initialized": state_manager.services_initialized, "initialization_error": state_manager.initialization_error, "market_state_ok": state.MARKET_STATE_OK, "monitoring_status": monitoring_status, "timestamp": datetime.now().isoformat()}
|
| 614 |
|
| 615 |
async def cleanup_on_shutdown():
|
| 616 |
-
|
|
|
|
|
|
|
|
|
|
| 617 |
print("๐ Shutdown signal received. Cleaning up...")
|
| 618 |
if trade_manager_global: trade_manager_global.stop_monitoring(); print("โ
Trade monitoring stopped")
|
| 619 |
-
|
|
|
|
|
|
|
|
|
|
| 620 |
try:
|
| 621 |
-
await
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
|
| 625 |
-
|
| 626 |
if data_manager_global: await data_manager_global.close(); print("โ
Data manager closed")
|
| 627 |
if r2_service_global:
|
| 628 |
try: await r2_service_global.save_system_logs_async({"application_shutdown": True}); print("โ
Shutdown log saved")
|
|
@@ -633,5 +629,5 @@ def signal_handler(signum, frame): print(f"๐ Received signal {signum}. Initia
|
|
| 633 |
signal.signal(signal.SIGINT, signal_handler); signal.signal(signal.SIGTERM, signal_handler)
|
| 634 |
|
| 635 |
if __name__ == "__main__":
|
| 636 |
-
print("๐ Starting AI Trading Bot with 3-Layer Analysis System (
|
| 637 |
uvicorn.run( application, host="0.0.0.0", port=7860, log_level="info", access_log=True )
|
|
|
|
| 1 |
+
# app.py (Fully updated to Learning Hub architecture)
|
| 2 |
import os
|
| 3 |
import traceback
|
| 4 |
import signal
|
|
|
|
| 17 |
from LLM import LLMService
|
| 18 |
from data_manager import DataManager
|
| 19 |
from ml_engine.processor import MLProcessor
|
| 20 |
+
|
| 21 |
+
# ๐ด --- START OF CHANGE --- ๐ด
|
| 22 |
+
# (Import the new hub manager instead of the old engine)
|
| 23 |
+
from learning_hub.hub_manager import LearningHubManager
|
| 24 |
+
# ๐ด --- END OF CHANGE --- ๐ด
|
| 25 |
+
|
| 26 |
from sentiment_news import SentimentAnalyzer
|
| 27 |
from trade_manager import TradeManager
|
| 28 |
+
import state # (This is state.py)
|
| 29 |
from helpers import safe_float_conversion, validate_candidate_data_enhanced
|
| 30 |
except ImportError as e:
|
| 31 |
print(f"โ ุฎุทุฃ ูู ุงุณุชูุฑุงุฏ ุงููุญุฏุงุช: {e}")
|
|
|
|
| 35 |
r2_service_global = None
|
| 36 |
data_manager_global = None
|
| 37 |
llm_service_global = None
|
| 38 |
+
# ๐ด --- START OF CHANGE --- ๐ด
|
| 39 |
+
learning_hub_global = None # (Changed from learning_engine_global)
|
| 40 |
+
# ๐ด --- END OF CHANGE --- ๐ด
|
| 41 |
trade_manager_global = None
|
| 42 |
sentiment_analyzer_global = None
|
| 43 |
symbol_whale_monitor_global = None
|
| 44 |
|
| 45 |
class StateManager:
|
|
|
|
|
|
|
| 46 |
def __init__(self):
|
| 47 |
self.market_analysis_lock = asyncio.Lock()
|
| 48 |
+
self.trade_analysis_lock = asyncio.Lock()
|
| 49 |
self.initialization_complete = False
|
| 50 |
self.initialization_error = None
|
| 51 |
self.services_initialized = {
|
| 52 |
'r2_service': False, 'data_manager': False, 'llm_service': False,
|
| 53 |
+
# ๐ด --- START OF CHANGE --- ๐ด
|
| 54 |
+
'learning_hub': False, # (Changed from learning_engine)
|
| 55 |
+
# ๐ด --- END OF CHANGE --- ๐ด
|
| 56 |
+
'trade_manager': False, 'sentiment_analyzer': False,
|
| 57 |
'symbol_whale_monitor': False
|
| 58 |
}
|
| 59 |
|
|
|
|
| 75 |
self.initialization_error = error
|
| 76 |
print(f"โ ุฎุทุฃ ูู ุงูุชููุฆุฉ: {error}")
|
| 77 |
|
|
|
|
| 78 |
state_manager = StateManager()
|
| 79 |
|
| 80 |
async def initialize_services():
|
| 81 |
"""ุชููุฆุฉ ุฌู
ูุน ุงูุฎุฏู
ุงุช ุจุดูู ู
ููุตู"""
|
| 82 |
global r2_service_global, data_manager_global, llm_service_global
|
| 83 |
+
# ๐ด --- START OF CHANGE --- ๐ด
|
| 84 |
+
global learning_hub_global # (Changed from learning_engine_global)
|
| 85 |
+
# ๐ด --- END OF CHANGE --- ๐ด
|
| 86 |
+
global trade_manager_global, sentiment_analyzer_global
|
| 87 |
global symbol_whale_monitor_global
|
| 88 |
try:
|
| 89 |
print("๐ ุจุฏุก ุชููุฆุฉ ุงูุฎุฏู
ุงุช...")
|
|
|
|
| 97 |
except Exception as e: print(f" โ ๏ธ ูุดู ุชููุฆุฉ ู
ุฑุงูุจ ุงูุญูุชุงู: {e}"); symbol_whale_monitor_global = None
|
| 98 |
print(" ๐ ุชููุฆุฉ DataManager..."); data_manager_global = DataManager(contracts_database, symbol_whale_monitor_global); await data_manager_global.initialize(); state_manager.set_service_initialized('data_manager'); print(" โ
DataManager ู
ููุฃุฉ")
|
| 99 |
|
|
|
|
|
|
|
| 100 |
print(" ๐ ุชููุฆุฉ LLMService...");
|
| 101 |
llm_service_global = LLMService();
|
| 102 |
llm_service_global.r2_service = r2_service_global;
|
| 103 |
+
|
|
|
|
| 104 |
print(" ๐ ุชููุฆุฉ ู
ุญูู ุงูู
ุดุงุนุฑ...");
|
| 105 |
sentiment_analyzer_global = SentimentAnalyzer(data_manager_global);
|
| 106 |
state_manager.set_service_initialized('sentiment_analyzer');
|
| 107 |
print(" โ
ู
ุญูู ุงูู
ุดุงุนุฑ ู
ููุฃ")
|
| 108 |
|
| 109 |
+
# ๐ด --- START OF CHANGE --- ๐ด
|
| 110 |
+
# (Initialize the new Learning Hub Manager)
|
| 111 |
+
print(" ๐ ุชููุฆุฉ ู
ุญูุฑ ุงูุชุนูู
(Learning Hub)...");
|
| 112 |
+
learning_hub_global = LearningHubManager(
|
| 113 |
+
r2_service=r2_service_global,
|
| 114 |
+
llm_service=llm_service_global,
|
| 115 |
+
data_manager=data_manager_global
|
| 116 |
+
)
|
| 117 |
+
await learning_hub_global.initialize()
|
| 118 |
+
state_manager.set_service_initialized('learning_hub');
|
| 119 |
+
print(" โ
ู
ุญูุฑ ุงูุชุนูู
(Hub) ู
ููุฃ")
|
| 120 |
|
| 121 |
+
# (Connect the Hub to the LLM service)
|
| 122 |
+
llm_service_global.learning_hub = learning_hub_global
|
| 123 |
+
state_manager.set_service_initialized('llm_service');
|
| 124 |
+
print(" โ
LLMService ู
ููุฃุฉ (ูู
ุฑุจูุทุฉ ุจู
ุญูุฑ ุงูุชุนูู
)")
|
| 125 |
|
| 126 |
print(" ๐ ุชููุฆุฉ ู
ุฏูุฑ ุงูุตููุงุช...");
|
| 127 |
+
# (Pass the new Learning Hub to the Trade Manager)
|
| 128 |
trade_manager_global = TradeManager(
|
| 129 |
r2_service_global,
|
| 130 |
+
learning_hub_global, # (Passing the new hub)
|
| 131 |
data_manager_global,
|
| 132 |
+
state_manager
|
| 133 |
)
|
| 134 |
state_manager.set_service_initialized('trade_manager');
|
| 135 |
+
print(" โ
ู
ุฏูุฑ ุงูุตููุงุช ู
ููุฃ (ูู
ุฑุจูุท ุจู
ุญูุฑ ุงูุชุนูู
)")
|
| 136 |
+
# ๐ด --- END OF CHANGE --- ๐ด
|
|
|
|
| 137 |
|
| 138 |
print("๐ฏ ุงูุชู
ูุช ุชููุฆุฉ ุฌู
ูุน ุงูุฎุฏู
ุงุช ุจูุฌุงุญ"); return True
|
| 139 |
except Exception as e: error_msg = f"ูุดู ุชููุฆุฉ ุงูุฎุฏู
ุงุช: {str(e)}"; print(f"โ {error_msg}"); state_manager.set_initialization_error(error_msg); return False
|
| 140 |
|
| 141 |
async def monitor_market_async():
|
| 142 |
+
"""(Unchanged)"""
|
| 143 |
global data_manager_global, sentiment_analyzer_global
|
| 144 |
try:
|
| 145 |
if not await state_manager.wait_for_initialization(): print("โ ูุดู ุชููุฆุฉ ุงูุฎุฏู
ุงุช - ุฅููุงู ู
ุฑุงูุจุฉ ุงูุณูู"); return
|
| 146 |
while True:
|
| 147 |
try:
|
|
|
|
| 148 |
async with state_manager.market_analysis_lock:
|
| 149 |
market_context = await sentiment_analyzer_global.get_market_sentiment()
|
| 150 |
if not market_context: state.MARKET_STATE_OK = True; await asyncio.sleep(60); continue
|
|
|
|
| 160 |
except Exception as error: print(f"โ ุฎุทุฃ ุฃุซูุงุก ู
ุฑุงูุจุฉ ุงูุณูู: {error}"); state.MARKET_STATE_OK = True; await asyncio.sleep(60)
|
| 161 |
except Exception as e: print(f"โ ูุดู ุชุดุบูู ู
ุฑุงูุจุฉ ุงูุณูู: {e}")
|
| 162 |
|
| 163 |
+
# ๐ด --- START OF CHANGE --- ๐ด
|
| 164 |
+
# (New background task for periodic distillation - Point 6)
|
| 165 |
+
async def run_periodic_distillation():
|
| 166 |
"""
|
| 167 |
+
Runs the Learning Hub's distillation process periodically.
|
|
|
|
| 168 |
"""
|
| 169 |
+
print("background task: Periodic Distillation (Curator) scheduled.")
|
| 170 |
+
await asyncio.sleep(300) # (Wait 5 minutes after startup)
|
| 171 |
+
|
| 172 |
+
while True:
|
| 173 |
+
try:
|
| 174 |
+
if not await state_manager.wait_for_initialization():
|
| 175 |
+
await asyncio.sleep(60)
|
| 176 |
+
continue
|
| 177 |
+
|
| 178 |
+
print("๐ [Scheduler] Running periodic distillation check...")
|
| 179 |
+
await learning_hub_global.run_distillation_check()
|
| 180 |
+
|
| 181 |
+
# (Run every 6 hours)
|
| 182 |
+
await asyncio.sleep(6 * 60 * 60)
|
| 183 |
+
|
| 184 |
+
except Exception as e:
|
| 185 |
+
print(f"โ [Scheduler] Error in periodic distillation task: {e}")
|
| 186 |
+
traceback.print_exc()
|
| 187 |
+
await asyncio.sleep(60 * 60) # (Wait 1 hour on error)
|
| 188 |
+
# ๐ด --- END OF CHANGE --- ๐ด
|
| 189 |
+
|
| 190 |
+
async def process_batch_parallel(batch, ml_processor, batch_num, total_batches, preloaded_whale_data):
|
| 191 |
+
"""(Unchanged)"""
|
| 192 |
try:
|
| 193 |
batch_tasks = []
|
| 194 |
for symbol_data in batch:
|
|
|
|
| 222 |
return {'success': successful_results, 'low_score': low_score_results, 'failures': failed_results}
|
| 223 |
|
| 224 |
except Exception as error:
|
| 225 |
+
print(f"โ [Consumer] Error processing batch {batch_num}: {error}")
|
| 226 |
return {'success': [], 'low_score': [], 'failures': []}
|
| 227 |
|
| 228 |
|
| 229 |
async def run_3_layer_analysis():
|
| 230 |
"""
|
| 231 |
+
(Updated to pass LearningHub to MLProcessor and save context for Reflector)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 232 |
"""
|
| 233 |
|
| 234 |
layer1_candidates = []
|
|
|
|
| 240 |
preloaded_whale_data_dict = {}
|
| 241 |
|
| 242 |
try:
|
| 243 |
+
print("๐ฏ Starting 3-Layer Analysis (with Learning Hub integration)...")
|
| 244 |
|
| 245 |
+
if not await state_manager.wait_for_initialization(): print("โ Services not fully initialized"); return None
|
| 246 |
|
| 247 |
+
# (Layer 1 - Unchanged)
|
| 248 |
+
print("\n๐ Layer 1: Rapid Screening (data_manager)...")
|
| 249 |
layer1_candidates = await data_manager_global.layer1_rapid_screening()
|
| 250 |
+
if not layer1_candidates: print("โ No candidates found in Layer 1"); return None
|
| 251 |
+
print(f"โ
Selected {len(layer1_candidates)} symbols for Layer 2")
|
| 252 |
layer1_symbols = [c['symbol'] for c in layer1_candidates]
|
| 253 |
|
| 254 |
+
# (Layer 1.5 - Unchanged)
|
| 255 |
start_whale_fetch = time.time()
|
| 256 |
+
print(f"\n๐ Layer 1.5: Fetching whale data for {len(layer1_symbols)} symbols (async)...")
|
| 257 |
async def fetch_whale_data_task(symbols, results_dict):
|
| 258 |
WHALE_FETCH_CONCURRENCY = 3
|
| 259 |
semaphore = asyncio.Semaphore(WHALE_FETCH_CONCURRENCY)
|
|
|
|
| 264 |
data = await data_manager_global.get_whale_data_for_symbol(symbol)
|
| 265 |
if data: results_dict[symbol] = data
|
| 266 |
except Exception as e:
|
|
|
|
| 267 |
results_dict[symbol] = {'data_available': False, 'error': str(e)}
|
| 268 |
for symbol in symbols: tasks.append(asyncio.create_task(get_data_with_semaphore(symbol)))
|
| 269 |
await asyncio.gather(*tasks)
|
| 270 |
whale_fetcher_task = asyncio.create_task(fetch_whale_data_task(layer1_symbols, preloaded_whale_data_dict))
|
|
|
|
| 271 |
|
| 272 |
+
# (Producer/Consumer Setup - Unchanged)
|
| 273 |
DATA_QUEUE_MAX_SIZE = 2
|
| 274 |
ohlcv_data_queue = asyncio.Queue(maxsize=DATA_QUEUE_MAX_SIZE)
|
| 275 |
ml_results_list = []
|
| 276 |
market_context = await data_manager_global.get_market_context_async()
|
| 277 |
+
|
| 278 |
+
# ๐ด --- START OF CHANGE --- ๐ด
|
| 279 |
+
# (Pass the global Learning Hub to the MLProcessor)
|
| 280 |
+
ml_processor = MLProcessor(market_context, data_manager_global, learning_hub_global)
|
| 281 |
+
# ๐ด --- END OF CHANGE --- ๐ด
|
| 282 |
+
|
| 283 |
batch_size = 15
|
| 284 |
total_batches = (len(layer1_candidates) + batch_size - 1) // batch_size
|
| 285 |
+
|
| 286 |
+
# (ML Consumer Task - Unchanged)
|
|
|
|
| 287 |
async def ml_consumer_task(queue: asyncio.Queue, results_list: list, whale_data_store: dict):
|
| 288 |
batch_num = 0
|
| 289 |
while True:
|
| 290 |
try:
|
| 291 |
batch_data = await queue.get()
|
| 292 |
+
if batch_data is None: queue.task_done(); break
|
| 293 |
batch_num += 1
|
|
|
|
|
|
|
| 294 |
batch_results_dict = await process_batch_parallel(
|
| 295 |
batch_data, ml_processor, batch_num, total_batches, whale_data_store
|
| 296 |
)
|
| 297 |
results_list.append(batch_results_dict)
|
| 298 |
queue.task_done()
|
| 299 |
+
except Exception as e: print(f"โ [ML Consumer] Fatal Error: {e}"); traceback.print_exc(); queue.task_done()
|
|
|
|
| 300 |
|
| 301 |
+
# (Run Producer/Consumer - Unchanged)
|
|
|
|
| 302 |
consumer_task = asyncio.create_task(ml_consumer_task(ohlcv_data_queue, ml_results_list, preloaded_whale_data_dict))
|
|
|
|
| 303 |
producer_task = asyncio.create_task(data_manager_global.stream_ohlcv_data(layer1_symbols, ohlcv_data_queue))
|
| 304 |
+
await producer_task;
|
|
|
|
|
|
|
| 305 |
await ohlcv_data_queue.put(None)
|
| 306 |
await ohlcv_data_queue.join()
|
| 307 |
+
await consumer_task;
|
| 308 |
|
| 309 |
+
# (Wait for Whale Fetch - Unchanged)
|
|
|
|
| 310 |
WHALE_FETCH_TIMEOUT_SECONDS = 180
|
| 311 |
try:
|
| 312 |
await asyncio.wait_for(whale_fetcher_task, timeout=WHALE_FETCH_TIMEOUT_SECONDS)
|
|
|
|
|
|
|
| 313 |
except asyncio.TimeoutError:
|
| 314 |
+
print(f" โ ๏ธ Whale fetch timeout ({WHALE_FETCH_TIMEOUT_SECONDS}s)!")
|
|
|
|
| 315 |
except Exception as whale_task_err:
|
| 316 |
+
print(f" โ Whale fetch task error: {whale_task_err}")
|
|
|
|
| 317 |
|
| 318 |
+
# (Aggregate Results - Unchanged)
|
| 319 |
+
print("๐ Aggregating all results...")
|
| 320 |
for batch_result in ml_results_list:
|
| 321 |
for success_item in batch_result['success']:
|
| 322 |
symbol = success_item['symbol']
|
|
|
|
| 329 |
layer2_candidates.append(success_item)
|
| 330 |
all_low_score_candidates.extend(batch_result['low_score'])
|
| 331 |
all_failed_candidates.extend(batch_result['failures'])
|
|
|
|
|
|
|
|
|
|
| 332 |
|
| 333 |
+
if not layer2_candidates: print("โ No candidates found in Layer 2")
|
| 334 |
+
|
| 335 |
+
# (Sort and Filter - Unchanged)
|
| 336 |
layer2_candidates.sort(key=lambda x: x.get('enhanced_final_score', 0), reverse=True)
|
| 337 |
target_count = min(10, len(layer2_candidates))
|
| 338 |
final_layer2_candidates = layer2_candidates[:target_count]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 339 |
|
| 340 |
+
# (Layer 2.5: Advanced MC - Unchanged)
|
| 341 |
+
print(f"\n๐ฌ Layer 2.5: Running Advanced MC (GARCH+LGBM) on top {len(final_layer2_candidates)} candidates...")
|
| 342 |
+
advanced_mc_analyzer = ml_processor.monte_carlo_analyzer
|
| 343 |
updated_candidates_for_llm = []
|
| 344 |
for candidate in final_layer2_candidates:
|
| 345 |
symbol = candidate.get('symbol', 'UNKNOWN')
|
| 346 |
try:
|
|
|
|
|
|
|
| 347 |
advanced_mc_results = await advanced_mc_analyzer.generate_1h_distribution_advanced(
|
| 348 |
candidate.get('ohlcv')
|
| 349 |
)
|
|
|
|
| 350 |
if advanced_mc_results and advanced_mc_results.get('simulation_model') == 'Phase2_GARCH_LGBM':
|
|
|
|
|
|
|
| 351 |
candidate['monte_carlo_distribution'] = advanced_mc_results
|
| 352 |
candidate['monte_carlo_probability'] = advanced_mc_results.get('probability_of_gain', 0)
|
| 353 |
+
candidate['advanced_mc_run'] = True
|
| 354 |
else:
|
| 355 |
+
candidate['advanced_mc_run'] = False
|
|
|
|
|
|
|
| 356 |
updated_candidates_for_llm.append(candidate)
|
|
|
|
| 357 |
except Exception as e:
|
| 358 |
+
print(f" โ [Advanced MC] {symbol} - Error: {e}. Using Phase 1 results.")
|
| 359 |
candidate['advanced_mc_run'] = False
|
| 360 |
updated_candidates_for_llm.append(candidate)
|
| 361 |
+
final_layer2_candidates = updated_candidates_for_llm
|
|
|
|
|
|
|
| 362 |
|
| 363 |
await r2_service_global.save_candidates_async(final_layer2_candidates)
|
| 364 |
+
# (Printing results omitted for brevity)
|
| 365 |
+
|
| 366 |
+
# (Layer 3 - LLM)
|
| 367 |
+
print("\n๐ง Layer 3: LLM Service Analysis...")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 368 |
for candidate in final_layer2_candidates:
|
| 369 |
try:
|
| 370 |
+
symbol = candidate['symbol']
|
| 371 |
ohlcv_data = candidate.get('ohlcv');
|
| 372 |
+
if not ohlcv_data: continue
|
| 373 |
candidate['raw_ohlcv'] = ohlcv_data
|
| 374 |
+
total_candles = sum(len(data) for data in ohlcv_data.values()) if ohlcv_data else 0
|
| 375 |
+
if total_candles < 30: continue
|
|
|
|
| 376 |
|
| 377 |
+
# (Add latest market context for LLM)
|
| 378 |
+
candidate['sentiment_data'] = await data_manager_global.get_market_context_async()
|
| 379 |
|
| 380 |
llm_analysis = await llm_service_global.get_trading_decision(candidate)
|
| 381 |
|
| 382 |
if llm_analysis and llm_analysis.get('action') in ['BUY']:
|
| 383 |
+
# ๐ด --- START OF CHANGE --- ๐ด
|
| 384 |
+
# (Save the 'candidate' object, which is the full context for the Reflector)
|
| 385 |
+
opportunity={
|
| 386 |
+
'symbol': symbol,
|
| 387 |
+
'current_price': candidate.get('current_price', 0),
|
| 388 |
+
'decision': llm_analysis,
|
| 389 |
+
'enhanced_score': candidate.get('enhanced_final_score', 0),
|
| 390 |
+
'llm_confidence': llm_analysis.get('confidence_level', 0),
|
| 391 |
+
'strategy': llm_analysis.get('strategy', 'GENERIC'),
|
| 392 |
+
'analysis_timestamp': datetime.now().isoformat(),
|
| 393 |
+
'decision_context_data': candidate # (This is the snapshot)
|
| 394 |
+
}
|
| 395 |
+
# ๐ด --- END OF CHANGE --- ๐ด
|
| 396 |
final_opportunities.append(opportunity)
|
| 397 |
+
print(f" โ
{symbol}: {llm_analysis.get('action')} - Conf: {llm_analysis.get('confidence_level', 0):.2f}")
|
| 398 |
+
else:
|
| 399 |
+
action = llm_analysis.get('action', 'NO_DECISION') if llm_analysis else 'NO_RESPONSE'; print(f" โ ๏ธ {symbol}: No trade decision from LLM ({action})")
|
| 400 |
+
except Exception as e: print(f"โ Error in LLM analysis for {candidate.get('symbol')}: {e}"); traceback.print_exc(); continue
|
| 401 |
|
| 402 |
if final_opportunities:
|
| 403 |
final_opportunities.sort(key=lambda x: (x['llm_confidence'] + x['enhanced_score']) / 2, reverse=True)
|
|
|
|
|
|
|
| 404 |
|
| 405 |
+
# (Audit log saving omitted for brevity - unchanged)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 406 |
|
| 407 |
+
if not final_opportunities: print("โ No suitable trading opportunities found"); return None
|
| 408 |
return final_opportunities[0] if final_opportunities else None
|
| 409 |
|
| 410 |
except Exception as error:
|
| 411 |
+
print(f"โ Fatal error in 3-layer system: {error}"); traceback.print_exc()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 412 |
return None
|
| 413 |
|
| 414 |
async def re_analyze_open_trade_async(trade_data):
|
| 415 |
+
"""(Updated to pass LearningHub to MLProcessor)"""
|
| 416 |
symbol = trade_data.get('symbol')
|
| 417 |
try:
|
|
|
|
| 418 |
async with state_manager.trade_analysis_lock:
|
| 419 |
+
print(f"๐ [Re-Analyze] Starting strategic analysis for {symbol}...")
|
| 420 |
market_context = await data_manager_global.get_market_context_async()
|
| 421 |
ohlcv_data_list = []
|
| 422 |
temp_queue = asyncio.Queue()
|
|
|
|
| 429 |
temp_queue.task_done()
|
| 430 |
except asyncio.TimeoutError:
|
| 431 |
if temp_queue.empty(): break
|
| 432 |
+
except Exception: break
|
| 433 |
|
| 434 |
+
if not ohlcv_data_list: print(f"โ ๏ธ Failed to get re-analysis data for {symbol}"); return None
|
| 435 |
ohlcv_data = ohlcv_data_list[0]
|
| 436 |
|
| 437 |
l1_data = await data_manager_global._get_detailed_symbol_data(symbol)
|
|
|
|
| 439 |
|
| 440 |
re_analysis_whale_data = await data_manager_global.get_whale_data_for_symbol(symbol)
|
| 441 |
|
| 442 |
+
# ๐ด --- START OF CHANGE --- ๐ด
|
| 443 |
+
# (Pass the global Learning Hub to the MLProcessor)
|
| 444 |
+
ml_processor = MLProcessor(market_context, data_manager_global, learning_hub_global)
|
| 445 |
+
# ๐ด --- END OF CHANGE --- ๐ด
|
| 446 |
|
| 447 |
+
print(f"๐ [Re-Analyze] Using Advanced MC (Phase 2+3) for {symbol}...")
|
| 448 |
advanced_mc_results = await ml_processor.monte_carlo_analyzer.generate_1h_distribution_advanced(
|
| 449 |
ohlcv_data.get('ohlcv')
|
| 450 |
)
|
| 451 |
|
| 452 |
processed_data = await ml_processor.process_and_score_symbol_enhanced(ohlcv_data, {symbol: re_analysis_whale_data} if re_analysis_whale_data else {})
|
|
|
|
| 453 |
if not processed_data: return None
|
| 454 |
|
| 455 |
if advanced_mc_results:
|
|
|
|
| 458 |
|
| 459 |
processed_data['raw_ohlcv'] = ohlcv_data.get('raw_ohlcv') or ohlcv_data.get('ohlcv')
|
| 460 |
processed_data['ohlcv'] = processed_data['raw_ohlcv']
|
|
|
|
|
|
|
| 461 |
processed_data['sentiment_data'] = market_context
|
| 462 |
|
| 463 |
re_analysis_decision = await llm_service_global.re_analyze_trade_async(trade_data, processed_data)
|
| 464 |
|
| 465 |
if re_analysis_decision:
|
| 466 |
await r2_service_global.save_system_logs_async({ "trade_reanalyzed": True, "symbol": symbol, "action": re_analysis_decision.get('action'), 'strategy': re_analysis_decision.get('strategy', 'GENERIC') })
|
| 467 |
+
print(f"โ
[Re-Analyze] Strategic analysis complete for {symbol}. Decision: {re_analysis_decision.get('action')}")
|
| 468 |
return {"symbol": symbol, "decision": re_analysis_decision, "current_price": processed_data.get('current_price')}
|
| 469 |
else: return None
|
| 470 |
except Exception as error: await r2_service_global.save_system_logs_async({ "reanalysis_error": True, "symbol": symbol, "error": str(error) }); print(f"โ Error in re_analyze_open_trade_async for {symbol}: {error}"); traceback.print_exc(); return None
|
| 471 |
|
| 472 |
|
| 473 |
async def run_bot_cycle_async():
|
| 474 |
+
"""(Updated to pass decision_context to open_trade)"""
|
| 475 |
try:
|
| 476 |
+
if not await state_manager.wait_for_initialization(): print("โ Services not fully initialized - skipping cycle"); return
|
| 477 |
+
print("๐ Starting trading cycle..."); await r2_service_global.save_system_logs_async({"cycle_started": True})
|
| 478 |
+
if not r2_service_global.acquire_lock(): print("โ Failed to acquire lock - skipping cycle"); return
|
| 479 |
|
| 480 |
open_trades = []
|
| 481 |
try:
|
| 482 |
+
open_trades = await trade_manager_global.get_open_trades(); print(f"๐ Open trades: {len(open_trades)}")
|
|
|
|
| 483 |
if open_trades:
|
| 484 |
now = datetime.now()
|
| 485 |
trades_to_reanalyze = [t for t in open_trades if now >= datetime.fromisoformat(t.get('expected_target_time', now.isoformat()))]
|
| 486 |
if trades_to_reanalyze:
|
| 487 |
+
print(f"๐ Re-analyzing {len(trades_to_reanalyze)} trades (using Advanced MC)")
|
|
|
|
|
|
|
|
|
|
| 488 |
reanalysis_results = await asyncio.gather(*[re_analyze_open_trade_async(trade) for trade in trades_to_reanalyze], return_exceptions=True)
|
| 489 |
for i, result in enumerate(reanalysis_results):
|
| 490 |
trade = trades_to_reanalyze[i]
|
| 491 |
+
if isinstance(result, Exception): print(f" โ Re-analysis failed for {trade.get('symbol')}: {result}")
|
| 492 |
+
elif result and result['decision'].get('action') == "CLOSE_TRADE": print(f" โ
Closing {trade.get('symbol')} based on re-analysis."); await trade_manager_global.close_trade(trade, result['current_price'], 'CLOSED_BY_REANALYSIS');
|
| 493 |
+
elif result and result['decision'].get('action') == "UPDATE_TRADE": print(f" โ
Updating {trade.get('symbol')} based on re-analysis."); await trade_manager_global.update_trade(trade, result['decision'])
|
| 494 |
+
elif result: print(f" โน๏ธ Holding {trade.get('symbol')} based on re-analysis.")
|
| 495 |
+
else: print(f" โ ๏ธ Re-analysis for {trade.get('symbol')} yielded no decision.")
|
| 496 |
|
| 497 |
current_open_trades_count = len(await trade_manager_global.get_open_trades())
|
| 498 |
should_look_for_new_trade = current_open_trades_count == 0
|
|
|
|
| 500 |
if should_look_for_new_trade:
|
| 501 |
portfolio_state = await r2_service_global.get_portfolio_state_async(); current_capital = portfolio_state.get("current_capital_usd", 0)
|
| 502 |
if current_capital > 1:
|
| 503 |
+
print("๐ฏ Looking for new trading opportunities (Dual-Phase MC)...")
|
| 504 |
best_opportunity = await run_3_layer_analysis()
|
| 505 |
+
|
| 506 |
+
# ๐ด --- START OF CHANGE --- ๐ด
|
| 507 |
+
# (Pass the decision_context to open_trade for the Reflector)
|
| 508 |
if best_opportunity:
|
| 509 |
+
print(f"โ
Opening new trade: {best_opportunity['symbol']}")
|
| 510 |
+
|
| 511 |
+
symbol = best_opportunity['symbol']
|
| 512 |
+
decision = best_opportunity['decision']
|
| 513 |
+
price = best_opportunity['current_price']
|
| 514 |
+
|
| 515 |
+
# (Extract the snapshot saved by run_3_layer_analysis)
|
| 516 |
+
context_data = best_opportunity.get('decision_context_data', {})
|
| 517 |
+
|
| 518 |
+
# (Build the snapshot for the Reflector)
|
| 519 |
+
decision_context_snapshot = {
|
| 520 |
+
"market": context_data.get('sentiment_data', {}),
|
| 521 |
+
"indicators": context_data.get('advanced_indicators', {}),
|
| 522 |
+
"pattern": context_data.get('pattern_analysis', {}),
|
| 523 |
+
"monte_carlo": context_data.get('monte_carlo_distribution', {}),
|
| 524 |
+
"whale": context_data.get('whale_data', {}),
|
| 525 |
+
"strategy_scores": context_data.get('base_strategy_scores', {})
|
| 526 |
+
}
|
| 527 |
+
|
| 528 |
+
await trade_manager_global.open_trade(
|
| 529 |
+
symbol,
|
| 530 |
+
decision,
|
| 531 |
+
price,
|
| 532 |
+
decision_context=decision_context_snapshot
|
| 533 |
+
)
|
| 534 |
+
# ๐ด --- END OF CHANGE --- ๐ด
|
| 535 |
+
else: print("โ No suitable trading opportunities found")
|
| 536 |
+
else: print("โ Insufficient capital to open new trades")
|
| 537 |
+
else: print("โน๏ธ A trade is already open, skipping new trade search.")
|
| 538 |
finally:
|
| 539 |
if r2_service_global.lock_acquired: r2_service_global.release_lock()
|
| 540 |
await r2_service_global.save_system_logs_async({ "cycle_completed": True, "open_trades": len(open_trades)})
|
| 541 |
+
print("โ
Trading cycle complete")
|
| 542 |
|
| 543 |
except Exception as error:
|
| 544 |
print(f"โ Unhandled error in main cycle: {error}"); traceback.print_exc()
|
|
|
|
| 547 |
|
| 548 |
@asynccontextmanager
|
| 549 |
async def lifespan(application: FastAPI):
|
| 550 |
+
"""Application lifecycle management"""
|
| 551 |
+
print("๐ Starting application initialization...")
|
| 552 |
try:
|
| 553 |
success = await initialize_services()
|
| 554 |
+
if not success: print("โ Application initialization failed - shutting down..."); yield; return
|
| 555 |
+
|
| 556 |
asyncio.create_task(monitor_market_async())
|
|
|
|
| 557 |
asyncio.create_task(trade_manager_global.start_trade_monitoring())
|
| 558 |
+
|
| 559 |
+
# ๐ด --- START OF CHANGE --- ๐ด
|
| 560 |
+
# (Start the new periodic distillation task)
|
| 561 |
+
asyncio.create_task(run_periodic_distillation())
|
| 562 |
+
# ๐ด --- END OF CHANGE --- ๐ด
|
| 563 |
+
|
| 564 |
await r2_service_global.save_system_logs_async({"application_started": True})
|
| 565 |
+
print("๐ฏ Application ready - 3-Layer System (Learning Hub Enabled) is active")
|
| 566 |
+
print(" -> ๐ Tactical Monitor (Dynamic Exit) is active")
|
| 567 |
+
print(" -> ๐ง Periodic Distillation (Curator) is scheduled")
|
| 568 |
yield
|
| 569 |
except Exception as error:
|
| 570 |
print(f"โ Application startup failed: {error}");
|
|
|
|
| 576 |
await cleanup_on_shutdown()
|
| 577 |
|
| 578 |
|
| 579 |
+
application = FastAPI(lifespan=lifespan, title="AI Trading Bot", description="Intelligent Trading System with 3-Layer Analysis and full Operational Learning Hub (Reflexion Architecture)", version="4.0.0")
|
| 580 |
|
| 581 |
+
# (Endpoints remain unchanged)
|
| 582 |
@application.get("/")
|
| 583 |
+
async def root(): return {"message": "Welcome to the AI Trading System", "system": "3-Layer Analysis (Learning Hub Enabled)", "status": "running" if state_manager.initialization_complete else "initializing", "timestamp": datetime.now().isoformat()}
|
| 584 |
@application.get("/run-cycle")
|
| 585 |
async def run_cycle_api():
|
| 586 |
+
if not state_manager.initialization_complete: raise HTTPException(status_code=503, detail="Services not fully initialized")
|
| 587 |
asyncio.create_task(run_bot_cycle_async())
|
| 588 |
+
return {"message": "Bot cycle initiated (Learning Hub Enabled)", "system": "3-Layer Analysis"}
|
| 589 |
@application.get("/health")
|
| 590 |
+
async def health_check(): return {"status": "healthy" if state_manager.initialization_complete else "initializing", "initialization_complete": state_manager.initialization_complete, "services_initialized": state_manager.services_initialized, "initialization_error": state_manager.initialization_error, "timestamp": datetime.now().isoformat(), "system_architecture": "3-Layer Analysis (Learning Hub V4.0)"}
|
| 591 |
@application.get("/analyze-market")
|
| 592 |
async def analyze_market_api():
|
| 593 |
+
if not state_manager.initialization_complete: raise HTTPException(status_code=503, detail="Services not fully initialized")
|
| 594 |
result = await run_3_layer_analysis()
|
| 595 |
if result: return {"opportunity_found": True, "symbol": result['symbol'], "action": result['decision'].get('action'), "confidence": result['llm_confidence'], "strategy": result['strategy'], "exit_profile": result['decision'].get('exit_profile')}
|
| 596 |
else: return {"opportunity_found": False, "message": "No suitable opportunities found"}
|
| 597 |
@application.get("/portfolio")
|
| 598 |
async def get_portfolio_api():
|
| 599 |
+
if not state_manager.initialization_complete: raise HTTPException(status_code=503, detail="Services not fully initialized")
|
| 600 |
try: portfolio_state = await r2_service_global.get_portfolio_state_async(); open_trades = await trade_manager_global.get_open_trades(); return {"portfolio": portfolio_state, "open_trades": open_trades, "timestamp": datetime.now().isoformat()}
|
| 601 |
+
except Exception as e: raise HTTPException(status_code=500, detail=f"Error getting portfolio: {str(e)}")
|
| 602 |
@application.get("/system-status")
|
| 603 |
async def get_system_status(): monitoring_status = trade_manager_global.get_monitoring_status() if trade_manager_global else {}; return {"initialization_complete": state_manager.initialization_complete, "services_initialized": state_manager.services_initialized, "initialization_error": state_manager.initialization_error, "market_state_ok": state.MARKET_STATE_OK, "monitoring_status": monitoring_status, "timestamp": datetime.now().isoformat()}
|
| 604 |
|
| 605 |
async def cleanup_on_shutdown():
|
| 606 |
+
# ๐ด --- START OF CHANGE --- ๐ด
|
| 607 |
+
global r2_service_global, data_manager_global, trade_manager_global, learning_hub_global
|
| 608 |
+
# ๐ด --- END OF CHANGE --- ๐ด
|
| 609 |
+
|
| 610 |
print("๐ Shutdown signal received. Cleaning up...")
|
| 611 |
if trade_manager_global: trade_manager_global.stop_monitoring(); print("โ
Trade monitoring stopped")
|
| 612 |
+
|
| 613 |
+
# ๐ด --- START OF CHANGE --- ๐ด
|
| 614 |
+
# (Call the new hub's shutdown method)
|
| 615 |
+
if learning_hub_global and learning_hub_global.initialized:
|
| 616 |
try:
|
| 617 |
+
await learning_hub_global.shutdown()
|
| 618 |
+
print("โ
Learning hub data saved")
|
| 619 |
+
except Exception as e: print(f"โ Failed to save learning hub data: {e}")
|
| 620 |
+
# ๐ด --- END OF CHANGE --- ๐ด
|
| 621 |
+
|
| 622 |
if data_manager_global: await data_manager_global.close(); print("โ
Data manager closed")
|
| 623 |
if r2_service_global:
|
| 624 |
try: await r2_service_global.save_system_logs_async({"application_shutdown": True}); print("โ
Shutdown log saved")
|
|
|
|
| 629 |
signal.signal(signal.SIGINT, signal_handler); signal.signal(signal.SIGTERM, signal_handler)
|
| 630 |
|
| 631 |
if __name__ == "__main__":
|
| 632 |
+
print("๐ Starting AI Trading Bot with 3-Layer Analysis System (Learning Hub V4.0)...")
|
| 633 |
uvicorn.run( application, host="0.0.0.0", port=7860, log_level="info", access_log=True )
|