mgbam commited on
Commit
e471e00
Β·
verified Β·
1 Parent(s): bd955d7

Update app/main.py

Browse files
Files changed (1) hide show
  1. app/main.py +35 -35
app/main.py CHANGED
@@ -1,8 +1,8 @@
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,27 +16,32 @@ import socketio
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
@@ -51,37 +56,32 @@ async def run_arbitrage_detector(price_fetcher, analyzer):
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}")
 
1
  """
2
+ Sentinel Arbitrage Engine - v19.0 FINAL (Correct Mounting Architecture)
3
 
4
+ This version uses the official, correct architecture for combining FastAPI and
5
+ Socket.IO, resolving the 'mount' attribute error permanently.
6
  """
7
  import asyncio
8
  import os
 
16
  from fastapi import FastAPI
17
  from fastapi.staticfiles import StaticFiles
18
 
19
+ # --- RELATIVE IMPORTS ---
20
+ # This assumes your project is structured with an 'app' package.
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
+ # Create 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
  for asset, prices in all_prices.items():
38
+ pyth_price = prices.get("pyth")
39
+ chainlink_price = prices.get("chainlink_agg")
40
  if pyth_price and chainlink_price and pyth_price > 0:
41
  spread = abs(pyth_price - chainlink_price) / chainlink_price
42
  if spread > OPPORTUNITY_THRESHOLD:
43
  current_time = time.time()
44
+ # Simple throttle to avoid spamming Gemini for the same opportunity
45
  if not hasattr(analyzer, 'last_call') or current_time - analyzer.last_call.get(asset, 0) > 60:
46
  analyzer.last_call = getattr(analyzer, 'last_call', {})
47
  analyzer.last_call[asset] = current_time
 
56
  print(f"❌ ERROR in engine loop: {e}")
57
  await asyncio.sleep(15)
58
 
59
+ # --- FASTAPI APP & LIFESPAN ---
60
+ # This is the correct way to manage the background task with Socket.IO
61
+ @sio.on('connect')
62
+ async def connect(sid, environ):
63
+ print(f"βœ… Client connected: {sid}")
64
+ # Start the engine only when the first user connects.
65
+ if not hasattr(sio, 'background_task') or sio.background_task.done():
66
+ print("πŸš€ First client connected. Starting Sentinel Engine...")
67
+ # Note: We create clients here as they are not easily shared across lifespan contexts
68
+ price_fetcher = PriceFetcher(httpx.AsyncClient())
69
+ arbitrage_analyzer = ArbitrageAnalyzer(httpx.AsyncClient())
70
  sio.background_task = sio.start_background_task(run_arbitrage_detector, price_fetcher, arbitrage_analyzer)
 
 
 
 
 
 
 
 
 
 
71
 
72
+ @sio.on('disconnect')
73
+ def disconnect(sid):
74
+ print(f"πŸ”₯ Client disconnected: {sid}")
75
 
76
+ # --- FINAL APP ASSEMBLY ---
77
+ # 1. Create the main FastAPI instance.
78
+ app = FastAPI()
79
 
80
+ # 2. Create the Socket.IO ASGI application.
81
+ socket_app = socketio.ASGIApp(sio)
82
 
83
+ # 3. Mount the Socket.IO app onto the FastAPI app at the correct path.
84
+ app.mount("/socket.io", socket_app)
 
85
 
86
+ # 4. Mount the StaticFiles directory to serve index.html at the root.
87
+ app.mount("/", StaticFiles(directory="static", html=True), name="static")