mgbam commited on
Commit
0323e8e
Β·
verified Β·
1 Parent(s): 7c59e8b

Update app/main.py

Browse files
Files changed (1) hide show
  1. app/main.py +22 -12
app/main.py CHANGED
@@ -1,8 +1,9 @@
1
  """
2
- Sentinel Arbitrage Engine - v20.0 FINAL (Raw Stream)
3
 
4
  This version uses a file-based log and a raw, unstoppable streaming
5
- endpoint to guarantee signal delivery. This is the final architecture.
 
6
  """
7
  import asyncio
8
  import os
@@ -11,10 +12,12 @@ import time
11
  from contextlib import asynccontextmanager
12
  from datetime import datetime, timezone
13
  import httpx
14
- from fastapi import FastAPI
15
- from fastapi.staticfiles import StaticFiles
16
  from fastapi.responses import StreamingResponse
 
17
 
 
 
18
  from .price_fetcher import PriceFetcher
19
  from .arbitrage_analyzer import ArbitrageAnalyzer
20
 
@@ -23,7 +26,8 @@ SIGNALS_FILE = "signals.log"
23
 
24
  @asynccontextmanager
25
  async def lifespan(app: FastAPI):
26
- if os.path.exists(SIGNALS_FILE): os.remove(SIGNALS_FILE)
 
27
 
28
  async with httpx.AsyncClient() as client:
29
  price_fetcher = PriceFetcher(client)
@@ -32,7 +36,7 @@ async def lifespan(app: FastAPI):
32
  app.state.engine_task = asyncio.create_task(
33
  run_arbitrage_detector(price_fetcher, arbitrage_analyzer)
34
  )
35
- print("πŸš€ Sentinel Engine v20.0 (Raw Stream) is ONLINE.")
36
  yield
37
  app.state.engine_task.cancel()
38
  print("βœ… Engine shut down.")
@@ -57,7 +61,6 @@ async def run_arbitrage_detector(price_fetcher, analyzer):
57
  briefing = await analyzer.get_alpha_briefing(asset, opportunity)
58
  if briefing:
59
  signal = {**opportunity, **briefing, "timestamp": datetime.now(timezone.utc).isoformat()}
60
- # --- THE FIX: Append as a single line to a log file ---
61
  with open(SIGNALS_FILE, "a") as f:
62
  f.write(json.dumps(signal) + "\n")
63
  print(f"βœ… Signal LOGGED for {asset}")
@@ -68,14 +71,15 @@ async def run_arbitrage_detector(price_fetcher, analyzer):
68
  # --- FastAPI App Setup ---
69
  app = FastAPI(lifespan=lifespan)
70
 
 
71
  @app.get("/api/signals/stream")
72
  async def signal_stream():
73
  """This endpoint streams the content of the signals.log file line by line."""
74
  async def event_generator():
75
- # Use an async-compatible way to tail the file
76
  try:
77
  with open(SIGNALS_FILE, 'r') as f:
78
- f.seek(0, 2) # Go to the end of the file
 
79
  while True:
80
  line = f.readline()
81
  if not line:
@@ -88,9 +92,15 @@ async def signal_stream():
88
  # Keep the connection open and wait for the file to be created
89
  while not os.path.exists(SIGNALS_FILE):
90
  await asyncio.sleep(1)
91
- # Re-run the generator now that the file exists
92
- yield from event_generator()
 
 
 
 
 
93
 
94
  return StreamingResponse(event_generator(), media_type="text/plain")
95
 
96
- app.mount("/", StaticFiles(directory="static", html=True), name="static")
 
 
1
  """
2
+ Sentinel Arbitrage Engine - v20.1 FINAL (Syntax Fix)
3
 
4
  This version uses a file-based log and a raw, unstoppable streaming
5
+ endpoint to guarantee signal delivery. This version corrects the async
6
+ generator syntax.
7
  """
8
  import asyncio
9
  import os
 
12
  from contextlib import asynccontextmanager
13
  from datetime import datetime, timezone
14
  import httpx
15
+ from fastapi import FastAPI, Request
 
16
  from fastapi.responses import StreamingResponse
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
 
 
26
 
27
  @asynccontextmanager
28
  async def lifespan(app: FastAPI):
29
+ if os.path.exists(SIGNALS_FILE):
30
+ os.remove(SIGNALS_FILE)
31
 
32
  async with httpx.AsyncClient() as client:
33
  price_fetcher = PriceFetcher(client)
 
36
  app.state.engine_task = asyncio.create_task(
37
  run_arbitrage_detector(price_fetcher, arbitrage_analyzer)
38
  )
39
+ print("πŸš€ Sentinel Engine v20.1 (Syntax Fix) is ONLINE.")
40
  yield
41
  app.state.engine_task.cancel()
42
  print("βœ… Engine shut down.")
 
61
  briefing = await analyzer.get_alpha_briefing(asset, opportunity)
62
  if briefing:
63
  signal = {**opportunity, **briefing, "timestamp": datetime.now(timezone.utc).isoformat()}
 
64
  with open(SIGNALS_FILE, "a") as f:
65
  f.write(json.dumps(signal) + "\n")
66
  print(f"βœ… Signal LOGGED for {asset}")
 
71
  # --- FastAPI App Setup ---
72
  app = FastAPI(lifespan=lifespan)
73
 
74
+ # --- API Endpoints ---
75
  @app.get("/api/signals/stream")
76
  async def signal_stream():
77
  """This endpoint streams the content of the signals.log file line by line."""
78
  async def event_generator():
 
79
  try:
80
  with open(SIGNALS_FILE, 'r') as f:
81
+ # Go to the end of the file
82
+ f.seek(0, 2)
83
  while True:
84
  line = f.readline()
85
  if not line:
 
92
  # Keep the connection open and wait for the file to be created
93
  while not os.path.exists(SIGNALS_FILE):
94
  await asyncio.sleep(1)
95
+ # ====================================================================
96
+ # THE CRITICAL FIX IS HERE
97
+ # ====================================================================
98
+ # Use 'async for' to correctly delegate to the async generator
99
+ async for line in event_generator():
100
+ yield line
101
+ # ====================================================================
102
 
103
  return StreamingResponse(event_generator(), media_type="text/plain")
104
 
105
+ # Mount static files to serve index.html at the root
106
+ app.mount("/", StaticFiles(directory="static", html=True), name="static")