mgbam commited on
Commit
d501c8b
Β·
verified Β·
1 Parent(s): 9b02f7b

Update app/main.py

Browse files
Files changed (1) hide show
  1. app/main.py +36 -44
app/main.py CHANGED
@@ -1,9 +1,8 @@
1
  """
2
- Sentinel Arbitrage Engine - v15.0 OMEGA (Socket.IO Perfected)
3
 
4
- The definitive, money-spinning engine. This version uses a perfectly
5
- configured Socket.IO server to push real-time, multi-asset arbitrage
6
- signals directly to a dynamic frontend. This is the final architecture.
7
  """
8
  import asyncio
9
  import os
@@ -17,79 +16,72 @@ import socketio
17
  from fastapi import FastAPI
18
  from fastapi.staticfiles import StaticFiles
19
 
20
- # Relative imports for our package structure
21
  from .price_fetcher import PriceFetcher
22
  from .arbitrage_analyzer import ArbitrageAnalyzer
23
 
24
  OPPORTUNITY_THRESHOLD = 0.0015
25
 
26
  # --- Socket.IO Server Setup ---
27
- # This creates the server instance that will handle all real-time communication.
28
  sio = socketio.AsyncServer(async_mode='asgi', cors_allowed_origins='*')
29
 
30
  # --- Background Engine ---
31
  async def run_arbitrage_detector(price_fetcher, analyzer):
32
- """The core engine loop. Detects opportunities and emits them via Socket.IO."""
33
  while True:
34
  try:
35
  await price_fetcher.update_prices_async()
36
  all_prices = price_fetcher.get_all_prices()
37
-
38
  for asset, prices in all_prices.items():
39
- pyth_price = prices.get("pyth")
40
- chainlink_price = prices.get("chainlink_agg")
41
-
42
  if pyth_price and chainlink_price and pyth_price > 0:
43
  spread = abs(pyth_price - chainlink_price) / chainlink_price
44
  if spread > OPPORTUNITY_THRESHOLD:
45
  current_time = time.time()
46
- # Simple throttle to avoid spamming Gemini for the same opportunity
47
  if not hasattr(analyzer, 'last_call') or current_time - analyzer.last_call.get(asset, 0) > 60:
48
  analyzer.last_call = getattr(analyzer, 'last_call', {})
49
  analyzer.last_call[asset] = current_time
50
-
51
- opportunity = {
52
- "asset": asset, "pyth_price": pyth_price,
53
- "chainlink_price": chainlink_price, "spread_pct": spread * 100
54
- }
55
  print(f"⚑️ Dislocation for {asset}: {opportunity['spread_pct']:.3f}%")
56
  briefing = await analyzer.get_alpha_briefing(asset, opportunity)
57
  if briefing:
58
  signal = {**opportunity, **briefing, "timestamp": datetime.now(timezone.utc).isoformat()}
59
- # Directly emit to all connected clients
60
  await sio.emit('new_signal', signal)
61
  print(f"βœ… Signal Emitted for {asset}: {signal['strategy']}")
62
  except Exception as e:
63
  print(f"❌ ERROR in engine loop: {e}")
64
-
65
  await asyncio.sleep(15)
66
 
67
- # --- Socket.IO Lifespan Events ---
68
- # This is the correct way to manage background tasks with python-socketio.
69
- @sio.on('connect')
70
- async def connect(sid, environ):
71
- """Handles new client connections."""
72
- print(f"βœ… Client connected: {sid}")
73
- # Start the engine only when the first user connects.
74
- if not hasattr(sio, 'background_task') or sio.background_task.done():
75
- print("πŸš€ First client connected. Starting Sentinel Engine...")
76
- price_fetcher = PriceFetcher(httpx.AsyncClient())
77
- arbitrage_analyzer = ArbitrageAnalyzer(httpx.AsyncClient())
78
- sio.background_task = sio.start_background_task(
79
- run_arbitrage_detector, price_fetcher, arbitrage_analyzer
80
- )
81
 
82
- @sio.on('disconnect')
83
- def disconnect(sid):
84
- """Handles client disconnections."""
85
- print(f"πŸ”₯ Client disconnected: {sid}")
86
 
 
 
 
87
 
88
- # --- FastAPI App & Final ASGI App ---
89
- # Create a minimal FastAPI app just to serve the static files.
90
- fastapi_app = FastAPI()
91
- fastapi_app.mount("/", StaticFiles(directory="static", html=True), name="static")
 
 
 
92
 
93
- # Wrap the FastAPI app (for static files) with the Socket.IO app.
94
- # The Socket.IO server is the primary application that handles requests.
95
- combined_app = socketio.ASGIApp(sio, other_asgi_app=fastapi_app)
 
1
  """
2
+ Sentinel Arbitrage Engine - v16.0 FINAL (Correct Object Naming)
3
 
4
+ This is the definitive version with the correct object names to match
5
+ the startup script, ensuring a successful launch.
 
6
  """
7
  import asyncio
8
  import os
 
16
  from fastapi import FastAPI
17
  from fastapi.staticfiles import StaticFiles
18
 
 
19
  from .price_fetcher import PriceFetcher
20
  from .arbitrage_analyzer import ArbitrageAnalyzer
21
 
22
  OPPORTUNITY_THRESHOLD = 0.0015
23
 
24
  # --- Socket.IO Server Setup ---
 
25
  sio = socketio.AsyncServer(async_mode='asgi', cors_allowed_origins='*')
26
 
27
  # --- Background Engine ---
28
  async def run_arbitrage_detector(price_fetcher, analyzer):
29
+ # This logic is correct and does not need to change.
30
  while True:
31
  try:
32
  await price_fetcher.update_prices_async()
33
  all_prices = price_fetcher.get_all_prices()
 
34
  for asset, prices in all_prices.items():
35
+ pyth_price, chainlink_price = prices.get("pyth"), prices.get("chainlink_agg")
 
 
36
  if pyth_price and chainlink_price and pyth_price > 0:
37
  spread = abs(pyth_price - chainlink_price) / chainlink_price
38
  if spread > OPPORTUNITY_THRESHOLD:
39
  current_time = time.time()
 
40
  if not hasattr(analyzer, 'last_call') or current_time - analyzer.last_call.get(asset, 0) > 60:
41
  analyzer.last_call = getattr(analyzer, 'last_call', {})
42
  analyzer.last_call[asset] = current_time
43
+ opportunity = {"asset": asset, "pyth_price": pyth_price, "chainlink_price": chainlink_price, "spread_pct": spread * 100}
 
 
 
 
44
  print(f"⚑️ Dislocation for {asset}: {opportunity['spread_pct']:.3f}%")
45
  briefing = await analyzer.get_alpha_briefing(asset, opportunity)
46
  if briefing:
47
  signal = {**opportunity, **briefing, "timestamp": datetime.now(timezone.utc).isoformat()}
 
48
  await sio.emit('new_signal', signal)
49
  print(f"βœ… Signal Emitted for {asset}: {signal['strategy']}")
50
  except Exception as e:
51
  print(f"❌ ERROR in engine loop: {e}")
 
52
  await asyncio.sleep(15)
53
 
54
+ # --- FastAPI Lifespan (for background task) ---
55
+ @asynccontextmanager
56
+ async def lifespan(app: FastAPI):
57
+ print("πŸš€ Initializing Sentinel Arbitrage Engine v16.0...")
58
+ async with httpx.AsyncClient() as client:
59
+ price_fetcher = PriceFetcher(client)
60
+ arbitrage_analyzer = ArbitrageAnalyzer(client)
61
+ sio.background_task = sio.start_background_task(run_arbitrage_detector, price_fetcher, arbitrage_analyzer)
62
+ print("βœ… Engine is online and hunting for opportunities.")
63
+ yield
64
+ print("⏳ Shutting down engine...")
65
+ sio.background_task.cancel()
66
+ try: await sio.background_task
67
+ except asyncio.CancelledError: print("Engine shut down gracefully.")
68
 
69
+ # --- FastAPI App & Socket.IO Mount ---
70
+ # THE FIX: We name the main FastAPI instance 'app' to match the startup script.
71
+ app = FastAPI(lifespan=lifespan)
 
72
 
73
+ # The primary app is the Socket.IO server, which wraps our FastAPI app.
74
+ # The startup script should point to THIS object.
75
+ app = socketio.ASGIApp(sio, other_asgi_app=app)
76
 
77
+ # Serve the static files. This route is now handled by the wrapped app.
78
+ sio.mount("/", StaticFiles(directory="static", html=True), name="static")
79
+
80
+
81
+ @sio.event
82
+ async def connect(sid, environ):
83
+ print(f"βœ… Client connected: {sid}")
84
 
85
+ @sio.event
86
+ async def disconnect(sid):
87
+ print(f"πŸ”₯ Client disconnected: {sid}")