Files
DOLPHIN/prod/exf_prefect_final.py

131 lines
4.5 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
DOLPHIN ExF - Prefect with Caching Disabled for HZ Client
===========================================================
Proper solution: Use NO_CACHE policy for tasks with Hazelcast client.
"""
from prefect import flow, task, get_run_logger
from prefect.cache_policies import NO_CACHE
from datetime import datetime, timezone
import sys
from pathlib import Path
_HERE = Path(__file__).parent
sys.path.insert(0, str(_HERE))
sys.path.insert(0, str(_HERE.parent / "external_factors"))
HZ_KEY = "exf_latest"
HZ_MAP = "DOLPHIN_FEATURES"
ACB_KEYS = ["funding_btc", "funding_eth", "dvol_btc", "dvol_eth",
"fng", "vix", "ls_btc", "taker", "oi_btc"]
@task(name="hz_push", retries=3, retry_delay_seconds=2, cache_policy=NO_CACHE)
def hz_push_task(client, payload: dict) -> bool:
"""
Push to Hazelcast - CACHING DISABLED to avoid serialization issues.
The NO_CACHE policy prevents Prefect from trying to serialize the client.
"""
import json
try:
payload = dict(payload)
payload["_pushed_at"] = datetime.now(timezone.utc).isoformat()
client.get_map(HZ_MAP).blocking().put(HZ_KEY, json.dumps(payload))
return True
except Exception as e:
return False
@flow(name="exf-prefect-final", log_prints=True)
def exf_final_flow(warmup_s: int = 25):
from realtime_exf_service import RealTimeExFService
from exf_persistence import ExFPersistenceService
from _hz_push import make_hz_client
import time
log = get_run_logger()
# Initialize
log.info("Starting RealTimeExFService...")
svc = RealTimeExFService()
svc.start()
time.sleep(warmup_s)
log.info("Starting ExFPersistenceService...")
persist = ExFPersistenceService(flush_interval_s=300)
persist.start()
log.info("Connecting to Hazelcast...")
client = make_hz_client()
log.info("Hazelcast connected")
pushes = 0
last_log = 0
log.info("=== EXF PREFECT LOOP STARTED ===")
try:
while True:
t0 = time.monotonic()
# Fetch indicators
indicators = svc.get_indicators(dual_sample=True)
staleness = indicators.pop("_staleness", {})
# Build payload
payload = {k: v for k, v in indicators.items()
if isinstance(v, (int, float, str, bool))}
payload["_staleness_s"] = {k: round(v, 1)
for k, v in staleness.items()
if isinstance(v, (int, float))}
# Check ACB
acb_present = [k for k in ACB_KEYS
if payload.get(k) is not None
and isinstance(payload[k], (int, float))
and payload[k] == payload[k]]
payload["_acb_ready"] = len(acb_present) == len(ACB_KEYS)
payload["_acb_present"] = f"{len(acb_present)}/{len(ACB_KEYS)}"
payload["_acb_missing"] = [k for k in ACB_KEYS if k not in acb_present]
payload["_ok_count"] = len([k for k in payload.keys() if not k.startswith('_')])
payload["_timestamp"] = datetime.now(timezone.utc).isoformat()
# Push to HZ - with NO_CACHE, this won't try to serialize client
try:
hz_push_task.submit(client, payload).result(timeout=3.0)
pushes += 1
except Exception as e:
log.warning(f"HZ push failed: {e}")
# Persist
persist.update_snapshot(payload)
# Log every 60s
if time.time() - last_log > 60:
stats = persist.get_stats()
log.info(
f"Status: pushes={pushes}, "
f"acb={payload['_acb_present']}, "
f"ready={payload['_acb_ready']}, "
f"files={stats.get('files_written', 0)}"
)
last_log = time.time()
# Maintain interval
elapsed = time.monotonic() - t0
sleep_time = max(0.0, 0.5 - elapsed)
if sleep_time > 0:
time.sleep(sleep_time)
except KeyboardInterrupt:
log.info("Shutdown requested")
finally:
log.info("Stopping services...")
svc.stop()
persist.stop()
client.shutdown()
log.info(f"Total pushes: {pushes}")
if __name__ == "__main__":
exf_final_flow()