File size: 3,568 Bytes
b159555
9b0536f
ea758f6
9b0536f
 
 
b159555
 
c6f94f2
b8d0337
acb6f17
3b67fa5
cfa3962
b159555
9b0536f
 
0323e8e
b159555
bd955d7
 
510ee6f
acb6f17
2ca2f4e
9b0536f
 
 
 
 
 
b349d30
acb6f17
9b0536f
 
073930c
cfa3962
9b0536f
cfa3962
acb6f17
9b0536f
 
 
 
 
 
acb6f17
e471e00
 
9b0536f
acb6f17
 
cfa3962
9b0536f
 
 
 
 
 
 
cfa3962
9b0536f
 
1690b33
073930c
9b0536f
 
 
 
 
 
 
 
 
bd955d7
9b0536f
 
 
 
d501c8b
9b0536f
 
 
 
3b67fa5
9b0536f
 
 
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
"""
Sentinel Arbitrage Engine - v21.0 OMEGA (Unbreakable)

This is the definitive, money-spinning engine. It uses a self-healing
background task, a transparent logging UI, and a robust Socket.IO
connection to guarantee performance and reliability.
"""
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

sio = socketio.AsyncServer(async_mode='asgi', cors_allowed_origins='*')

async def log_and_emit(message: str):
    """Helper to both print to console and emit to frontend."""
    print(message)
    await sio.emit('log_message', message)

async def run_arbitrage_detector(price_fetcher, analyzer):
    """The self-healing core engine loop."""
    await log_and_emit("Engine core starting...")
    while True:
        try:
            await log_and_emit("Updating prices from oracles...")
            await price_fetcher.update_prices_async()
            all_prices = price_fetcher.get_all_prices()

            if not any(p.get("pyth") and p.get("chainlink_agg") for p in all_prices.values()):
                 await log_and_emit("Oracle data incomplete. Waiting for next cycle.")
                 await asyncio.sleep(30)
                 continue

            for asset, prices in all_prices.items():
                pyth_price = prices.get("pyth")
                chainlink_price = 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:
                        await log_and_emit(f"⚑️ Discrepancy for {asset}: {spread:.3f}%")
                        briefing = await analyzer.get_alpha_briefing(asset, {"asset": asset, "pyth_price": pyth_price, "chainlink_price": chainlink_price, "spread_pct": spread * 100})
                        if briefing:
                            signal = {"asset": asset, "pyth_price": pyth_price, "chainlink_price": chainlink_price, "spread_pct": spread * 100, **briefing, "timestamp": datetime.now(timezone.utc).isoformat()}
                            await sio.emit('new_signal', signal)
                            await log_and_emit(f"βœ… Signal Emitted for {asset}: {signal['strategy']}")

        except Exception as e:
            await log_and_emit(f"❌ CRITICAL ERROR in engine loop: {e}. Recovering...")

        await asyncio.sleep(15)

@asynccontextmanager
async def lifespan(app: FastAPI):
    print("πŸš€ Initializing Sentinel Arbitrage Engine v21.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)
        yield
    print("βœ… Engine shut down.")

app = FastAPI(lifespan=lifespan)
socket_app = socketio.ASGIApp(sio)
app.mount('/socket.io', socket_app)
app.mount("/", StaticFiles(directory="static", html=True), name="static")

@sio.event
async def connect(sid, environ):
    await sio.emit('welcome', {'message': 'Connection to Sentinel Engine established!'}, to=sid)
    print(f"βœ… Client connected: {sid}")

@sio.event
async def disconnect(sid):
    print(f"πŸ”₯ Client disconnected: {sid}")