mgbam commited on
Commit
07fe08e
Β·
verified Β·
1 Parent(s): d79aa89

Update app/app.py

Browse files
Files changed (1) hide show
  1. app/app.py +35 -46
app/app.py CHANGED
@@ -1,8 +1,8 @@
1
  """
2
- Sentinel Arbitrage Engine - v16.0 FINAL (Correct Mount)
3
 
4
- The definitive, money-spinning engine. This version uses the correct
5
- FastAPI and Socket.IO mounting strategy for guaranteed execution.
6
  """
7
  import asyncio
8
  import os
@@ -16,41 +16,32 @@ import socketio
16
  from fastapi import FastAPI
17
  from fastapi.staticfiles import StaticFiles
18
 
19
- # Relative imports for our package structure
20
- from .price_fetcher import PriceFetcher
21
- from .arbitrage_analyzer import ArbitrageAnalyzer
22
 
23
  OPPORTUNITY_THRESHOLD = 0.0015
24
 
25
  # --- Socket.IO Server Setup ---
26
- # This creates the server instance that will handle all real-time communication.
27
  sio = socketio.AsyncServer(async_mode='asgi', cors_allowed_origins='*')
28
 
29
  # --- Background Engine ---
30
  async def run_arbitrage_detector(price_fetcher, analyzer):
31
- """The core engine loop; detects opportunities and emits them via Socket.IO."""
32
  while True:
33
  try:
34
  await price_fetcher.update_prices_async()
35
  all_prices = price_fetcher.get_all_prices()
36
-
37
  for asset, prices in all_prices.items():
38
- pyth_price = prices.get("pyth")
39
- chainlink_price = prices.get("chainlink_agg")
40
-
41
  if pyth_price and chainlink_price and pyth_price > 0:
42
  spread = abs(pyth_price - chainlink_price) / chainlink_price
43
  if spread > OPPORTUNITY_THRESHOLD:
44
  current_time = time.time()
45
- # Simple throttle to avoid spamming Gemini for the same opportunity
46
  if not hasattr(analyzer, 'last_call') or current_time - analyzer.last_call.get(asset, 0) > 60:
47
  analyzer.last_call = getattr(analyzer, 'last_call', {})
48
  analyzer.last_call[asset] = current_time
49
-
50
- opportunity = {
51
- "asset": asset, "pyth_price": pyth_price,
52
- "chainlink_price": chainlink_price, "spread_pct": spread * 100
53
- }
54
  print(f"⚑️ Dislocation for {asset}: {opportunity['spread_pct']:.3f}%")
55
  briefing = await analyzer.get_alpha_briefing(asset, opportunity)
56
  if briefing:
@@ -59,37 +50,29 @@ async def run_arbitrage_detector(price_fetcher, analyzer):
59
  print(f"βœ… Signal Emitted for {asset}: {signal['strategy']}")
60
  except Exception as e:
61
  print(f"❌ ERROR in engine loop: {e}")
62
-
63
  await asyncio.sleep(15)
64
 
65
- # --- FastAPI Lifespan (for background task) ---
66
- @asynccontextmanager
67
- async def lifespan(app: FastAPI):
68
- print("πŸš€ Initializing Sentinel Arbitrage Engine v16.0...")
69
- async with httpx.AsyncClient() as client:
70
- # The background task is started using the Socket.IO server's robust method
71
- sio.start_background_task(
72
- run_arbitrage_detector,
73
- PriceFetcher(client),
74
- ArbitrageAnalyzer(client)
75
- )
76
- print("βœ… Engine is online and hunting for opportunities.")
77
- yield
78
- # No explicit shutdown needed for the background task here,
79
- # as it's tied to the server process.
80
-
81
- # --- FastAPI App & Final ASGI App ---
82
- # We define the FastAPI app instance first, including its lifespan.
83
- app = FastAPI(lifespan=lifespan)
84
 
85
- # THE CRITICAL FIX: We mount the Socket.IO ASGI app ONTO the FastAPI app.
86
- # This makes FastAPI the main application.
87
- app.mount('/socket.io', socketio.ASGIApp(sio))
88
-
89
- # THE SECOND CRITICAL FIX: We mount the StaticFiles ONTO the FastAPI app as well.
90
- # This correctly assigns the responsibility of serving files to FastAPI.
91
- app.mount("/", StaticFiles(directory="static", html=True), name="static")
 
 
 
92
 
 
 
 
 
 
 
93
 
94
  # --- Socket.IO Event Handlers ---
95
  @sio.event
@@ -98,4 +81,10 @@ async def connect(sid, environ):
98
 
99
  @sio.event
100
  async def disconnect(sid):
101
- print(f"πŸ”₯ Client disconnected: {sid}")
 
 
 
 
 
 
 
1
  """
2
+ Sentinel Arbitrage Engine - v17.0 FINAL (Flat Structure)
3
 
4
+ This definitive version uses a flat project structure and direct imports
5
+ to conform to the environment's startup command, ensuring a successful launch.
6
  """
7
  import asyncio
8
  import os
 
16
  from fastapi import FastAPI
17
  from fastapi.staticfiles import StaticFiles
18
 
19
+ # --- DIRECT IMPORTS FOR FLAT STRUCTURE ---
20
+ from price_fetcher import PriceFetcher
21
+ from arbitrage_analyzer import ArbitrageAnalyzer
22
 
23
  OPPORTUNITY_THRESHOLD = 0.0015
24
 
25
  # --- Socket.IO Server Setup ---
 
26
  sio = socketio.AsyncServer(async_mode='asgi', cors_allowed_origins='*')
27
 
28
  # --- Background Engine ---
29
  async def run_arbitrage_detector(price_fetcher, analyzer):
30
+ # This logic is correct and does not need to change.
31
  while True:
32
  try:
33
  await price_fetcher.update_prices_async()
34
  all_prices = price_fetcher.get_all_prices()
 
35
  for asset, prices in all_prices.items():
36
+ pyth_price, chainlink_price = prices.get("pyth"), prices.get("chainlink_agg")
 
 
37
  if pyth_price and chainlink_price and pyth_price > 0:
38
  spread = abs(pyth_price - chainlink_price) / chainlink_price
39
  if spread > OPPORTUNITY_THRESHOLD:
40
  current_time = time.time()
 
41
  if not hasattr(analyzer, 'last_call') or current_time - analyzer.last_call.get(asset, 0) > 60:
42
  analyzer.last_call = getattr(analyzer, 'last_call', {})
43
  analyzer.last_call[asset] = current_time
44
+ opportunity = {"asset": asset, "pyth_price": pyth_price, "chainlink_price": chainlink_price, "spread_pct": spread * 100}
 
 
 
 
45
  print(f"⚑️ Dislocation for {asset}: {opportunity['spread_pct']:.3f}%")
46
  briefing = await analyzer.get_alpha_briefing(asset, opportunity)
47
  if briefing:
 
50
  print(f"βœ… Signal Emitted for {asset}: {signal['strategy']}")
51
  except Exception as e:
52
  print(f"❌ ERROR in engine loop: {e}")
 
53
  await asyncio.sleep(15)
54
 
55
+ # --- FastAPI App & Lifespan ---
56
+ # We now define the main 'app' object first.
57
+ app = FastAPI()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
+ @app.on_event("startup")
60
+ async def startup_event():
61
+ print("πŸš€ Initializing Sentinel Arbitrage Engine v17.0...")
62
+ # Using the older startup event which is more compatible with some environments
63
+ app.state.client = httpx.AsyncClient()
64
+ price_fetcher = PriceFetcher(app.state.client)
65
+ arbitrage_analyzer = ArbitrageAnalyzer(app.state.client)
66
+ # Start the background task
67
+ sio.background_task = sio.start_background_task(run_arbitrage_detector, price_fetcher, arbitrage_analyzer)
68
+ print("βœ… Engine is online and hunting for opportunities.")
69
 
70
+ @app.on_event("shutdown")
71
+ async def shutdown_event():
72
+ print("⏳ Shutting down engine...")
73
+ sio.background_task.cancel()
74
+ await app.state.client.aclose()
75
+ print("Engine shut down gracefully.")
76
 
77
  # --- Socket.IO Event Handlers ---
78
  @sio.event
 
81
 
82
  @sio.event
83
  async def disconnect(sid):
84
+ print(f"πŸ”₯ Client disconnected: {sid}")
85
+
86
+ # --- Mount the apps ---
87
+ # The primary app is the Socket.IO server, which wraps our FastAPI app.
88
+ app = socketio.ASGIApp(sio, other_asgi_app=app)
89
+ # Serve the static files.
90
+ sio.mount("/", StaticFiles(directory="static", html=True), name="static")