mgbam commited on
Commit
b8d0337
Β·
verified Β·
1 Parent(s): 17ad72e

Update app/app.py

Browse files
Files changed (1) hide show
  1. app/app.py +60 -60
app/app.py CHANGED
@@ -1,96 +1,96 @@
1
  """
2
- Sentinel Arbitrage Engine - v10.0 OMEGA
3
 
4
- A complete architectural overhaul using FastAPI-SocketIO for guaranteed,
5
- real-time signal delivery. This is the definitive money-spinning engine.
6
  """
7
  import asyncio
8
  import os
9
  from contextlib import asynccontextmanager
10
- import json
11
- import time
12
  from datetime import datetime, timezone
13
-
14
  import httpx
15
- import socketio
16
- from fastapi import FastAPI
17
- from fastapi.staticfiles import StaticFiles
18
 
19
- # --- RELATIVE IMPORTS FOR PACKAGE STRUCTURE ---
20
  from .price_fetcher import PriceFetcher
21
  from .arbitrage_analyzer import ArbitrageAnalyzer
22
  from .broker import signal_broker
23
 
24
- OPPORTUNITY_THRESHOLD = 0.001
25
 
26
- # --- Socket.IO Server Setup ---
27
- sio = socketio.AsyncServer(async_mode='asgi', cors_allowed_origins='*')
28
- socket_app = socketio.ASGIApp(sio)
29
-
30
- # --- Application Lifespan ---
31
  @asynccontextmanager
32
  async def lifespan(app: FastAPI):
33
- print("πŸš€ Initializing Sentinel Arbitrage Engine v10.0...")
34
  async with httpx.AsyncClient() as client:
35
  app.state.price_fetcher = PriceFetcher(client)
36
  app.state.arbitrage_analyzer = ArbitrageAnalyzer(client)
37
-
38
- arbitrage_task = asyncio.create_task(
39
- run_arbitrage_detector(app.state.price_fetcher, app.state.arbitrage_analyzer)
40
- )
41
-
42
- print("βœ… Engine is online and hunting for opportunities.")
43
  yield
44
-
45
  print("⏳ Shutting down engine...")
46
  arbitrage_task.cancel()
47
  try: await arbitrage_task
48
- except asyncio.CancelledError: print("Engine shut down gracefully.")
49
 
50
- async def run_arbitrage_detector(price_fetcher, analyzer):
51
- """The core engine loop. Directly emits events via Socket.IO."""
52
- last_opportunity_time = 0
53
  while True:
54
- try:
55
- await price_fetcher.update_prices_async()
56
- prices = price_fetcher.get_current_prices()
57
-
58
  pyth_price = prices.get("pyth")
59
  chainlink_price = prices.get("chainlink_agg")
60
 
61
- if pyth_price and chainlink_price:
62
  spread = abs(pyth_price - chainlink_price) / chainlink_price
63
  if spread > OPPORTUNITY_THRESHOLD:
64
- current_time = time.time()
65
- if current_time - last_opportunity_time > 30: # Throttle Gemini calls
66
- last_opportunity_time = current_time
67
- opportunity = {
68
- "pyth_price": pyth_price, "chainlink_price": chainlink_price, "spread_pct": spread * 100
69
- }
70
- print(f"⚑️ Discrepancy Detected: {opportunity['spread_pct']:.3f}%")
71
- briefing = await analyzer.get_alpha_briefing(opportunity)
72
- if briefing:
73
- signal = {**opportunity, **briefing, "timestamp": datetime.now(timezone.utc).isoformat()}
74
- await sio.emit('new_signal', signal)
75
- print(f"βœ… Signal Emitted: {signal['strategy']}")
76
- except Exception as e:
77
- print(f"❌ ERROR in engine loop: {e}")
78
 
79
  await asyncio.sleep(15)
80
 
81
- # --- FastAPI App Setup ---
82
- app = FastAPI(lifespan=lifespan)
83
-
84
- @sio.event
85
- async def connect(sid, environ):
86
- print(f"βœ… Client connected: {sid}")
87
 
88
- @sio.event
89
- async def disconnect(sid):
90
- print(f"πŸ”₯ Client disconnected: {sid}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
- # Mount the Socket.IO app on a specific path
93
- app.mount('/socket.io', socket_app)
 
94
 
95
- # Serve the static files (like index.html) from the 'static' directory
96
- app.mount("/", StaticFiles(directory="static", html=True), name="static")
 
 
 
 
 
 
 
 
1
  """
2
+ Sentinel Arbitrage Engine - v11.0 FINAL (Multi-Asset)
3
 
4
+ Detects and analyzes price dislocations for multiple assets across
5
+ decentralized oracles.
6
  """
7
  import asyncio
8
  import os
9
  from contextlib import asynccontextmanager
 
 
10
  from datetime import datetime, timezone
11
+ import json
12
  import httpx
13
+ from fastapi import FastAPI, Request
14
+ from fastapi.responses import HTMLResponse, StreamingResponse
15
+ from fastapi.templating import Jinja2Templates
16
 
 
17
  from .price_fetcher import PriceFetcher
18
  from .arbitrage_analyzer import ArbitrageAnalyzer
19
  from .broker import signal_broker
20
 
21
+ OPPORTUNITY_THRESHOLD = 0.0015 # 0.15% price difference to trigger a signal
22
 
 
 
 
 
 
23
  @asynccontextmanager
24
  async def lifespan(app: FastAPI):
25
+ # This setup remains the same, but the logic it runs is now multi-asset.
26
  async with httpx.AsyncClient() as client:
27
  app.state.price_fetcher = PriceFetcher(client)
28
  app.state.arbitrage_analyzer = ArbitrageAnalyzer(client)
29
+ arbitrage_task = asyncio.create_task(run_arbitrage_detector(app))
30
+ print("πŸš€ Sentinel Arbitrage Engine v11.0 (Multi-Asset) started.")
 
 
 
 
31
  yield
 
32
  print("⏳ Shutting down engine...")
33
  arbitrage_task.cancel()
34
  try: await arbitrage_task
35
+ except asyncio.CancelledError: print("Engine shut down.")
36
 
37
+ async def run_arbitrage_detector(app: FastAPI):
 
 
38
  while True:
39
+ await app.state.price_fetcher.update_prices_async()
40
+ all_prices = app.state.price_fetcher.get_all_prices()
41
+
42
+ for asset, prices in all_prices.items():
43
  pyth_price = prices.get("pyth")
44
  chainlink_price = prices.get("chainlink_agg")
45
 
46
+ if pyth_price and chainlink_price and pyth_price > 0:
47
  spread = abs(pyth_price - chainlink_price) / chainlink_price
48
  if spread > OPPORTUNITY_THRESHOLD:
49
+ opportunity = {
50
+ "asset": asset,
51
+ "pyth_price": pyth_price, "chainlink_price": chainlink_price,
52
+ "spread_pct": spread * 100
53
+ }
54
+ print(f"⚑️ Dislocation for {asset}: {opportunity['spread_pct']:.3f}%")
55
+ briefing = await app.state.arbitrage_analyzer.get_alpha_briefing(asset, opportunity)
56
+ if briefing:
57
+ signal = {**opportunity, **briefing, "timestamp": datetime.now(timezone.utc).isoformat()}
58
+ await signal_broker.queue.put(signal)
 
 
 
 
59
 
60
  await asyncio.sleep(15)
61
 
62
+ app = FastAPI(title="Sentinel Arbitrage Engine", lifespan=lifespan)
63
+ templates = Jinja2Templates(directory="templates")
 
 
 
 
64
 
65
+ def render_signal_card(payload: dict) -> str:
66
+ s = payload
67
+ time_str = datetime.fromisoformat(s['timestamp']).strftime('%H:%M:%S UTC')
68
+ pyth_class = "buy" if s['pyth_price'] < s['chainlink_price'] else "sell"
69
+ chainlink_class = "sell" if s['pyth_price'] < s['chainlink_price'] else "buy"
70
+
71
+ return f"""
72
+ <tr hx-swap-oob="afterbegin:#opportunities-table">
73
+ <td><strong>{s['asset']}/USD</strong></td>
74
+ <td><span class="{pyth_class}">Pyth Network</span><br>${s['pyth_price']:,.2f}</td>
75
+ <td><span class="{chainlink_class}">Chainlink Agg.</span><br>${s['chainlink_price']:,.2f}</td>
76
+ <td><strong>{s['spread_pct']:.3f}%</strong></td>
77
+ <td><span class="risk-{s.get('risk', 'low').lower()}">{s.get('risk', 'N/A')}</span></td>
78
+ <td>{s.get('rationale', 'N/A')}</td>
79
+ <td><button class="trade-btn">{s.get('strategy', 'N/A')}</button></td>
80
+ </tr>
81
+ <div id="last-update-time" hx-swap-oob="true">{time_str}</div>
82
+ """
83
 
84
+ @app.get("/", response_class=HTMLResponse)
85
+ async def serve_dashboard(request: Request):
86
+ return templates.TemplateResponse("index.html", {"request": request})
87
 
88
+ @app.get("/api/signals/stream")
89
+ async def signal_stream(request: Request):
90
+ async def event_generator():
91
+ while True:
92
+ payload = await signal_broker.queue.get()
93
+ html_card = render_signal_card(payload)
94
+ data_payload = html_card.replace('\n', ' ').strip()
95
+ yield f"event: message\ndata: {data_payload}\n\n"
96
+ return StreamingResponse(event_generator(), media_type="text/event-stream")