mgbam's picture
Update app/main.py
bd955d7 verified
raw
history blame
3.76 kB
"""
Sentinel Arbitrage Engine - v16.0 FINAL (Correct Object Naming)
This is the definitive version with the correct object names to match
the startup script, ensuring a successful launch.
"""
import asyncio
import os
import json
import time
from contextlib import asynccontextmanager
from datetime import datetime, timezone
import httpx
import socketio
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from .price_fetcher import PriceFetcher
from .arbitrage_analyzer import ArbitrageAnalyzer
OPPORTUNITY_THRESHOLD = 0.0015
# --- Socket.IO Server Setup ---
sio = socketio.AsyncServer(async_mode='asgi', cors_allowed_origins='*')
# --- Background Engine ---
async def run_arbitrage_detector(price_fetcher, analyzer):
# This logic is correct and does not need to change.
while True:
try:
await price_fetcher.update_prices_async()
all_prices = price_fetcher.get_all_prices()
for asset, prices in all_prices.items():
pyth_price, chainlink_price = prices.get("pyth"), prices.get("chainlink_agg")
if pyth_price and chainlink_price and pyth_price > 0:
spread = abs(pyth_price - chainlink_price) / chainlink_price
if spread > OPPORTUNITY_THRESHOLD:
current_time = time.time()
if not hasattr(analyzer, 'last_call') or current_time - analyzer.last_call.get(asset, 0) > 60:
analyzer.last_call = getattr(analyzer, 'last_call', {})
analyzer.last_call[asset] = current_time
opportunity = {"asset": asset, "pyth_price": pyth_price, "chainlink_price": chainlink_price, "spread_pct": spread * 100}
print(f"⚑️ Dislocation for {asset}: {opportunity['spread_pct']:.3f}%")
briefing = await analyzer.get_alpha_briefing(asset, opportunity)
if briefing:
signal = {**opportunity, **briefing, "timestamp": datetime.now(timezone.utc).isoformat()}
await sio.emit('new_signal', signal)
print(f"βœ… Signal Emitted for {asset}: {signal['strategy']}")
except Exception as e:
print(f"❌ ERROR in engine loop: {e}")
await asyncio.sleep(15)
# --- FastAPI Lifespan (for background task) ---
@asynccontextmanager
async def lifespan(app: FastAPI):
print("πŸš€ Initializing Sentinel Arbitrage Engine v16.0...")
async with httpx.AsyncClient() as client:
price_fetcher = PriceFetcher(client)
arbitrage_analyzer = ArbitrageAnalyzer(client)
sio.background_task = sio.start_background_task(run_arbitrage_detector, price_fetcher, arbitrage_analyzer)
print("βœ… Engine is online and hunting for opportunities.")
yield
print("⏳ Shutting down engine...")
sio.background_task.cancel()
try: await sio.background_task
except asyncio.CancelledError: print("Engine shut down gracefully.")
# --- FastAPI App & Socket.IO Mount ---
# THE FIX: We name the main FastAPI instance 'app' to match the startup script.
app = FastAPI(lifespan=lifespan)
# The primary app is the Socket.IO server, which wraps our FastAPI app.
# The startup script should point to THIS object.
app = socketio.ASGIApp(sio, other_asgi_app=app)
# Serve the static files. This route is now handled by the wrapped app.
sio.mount("/", StaticFiles(directory="static", html=True), name="static")
@sio.event
async def connect(sid, environ):
print(f"βœ… Client connected: {sid}")
@sio.event
async def disconnect(sid):
print(f"πŸ”₯ Client disconnected: {sid}")