v1.1 autonomous upgrade: continuous curation, risk engine, scheduler hardening, systemd service
This commit is contained in:
59
bot.py
59
bot.py
@@ -1,17 +1,49 @@
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
import json
|
||||
from sqlalchemy import func
|
||||
from config import settings
|
||||
from db import SessionLocal, BotDecision, TradeExecution
|
||||
from services import searx_news, ollama_decide, place_order
|
||||
from db import SessionLocal, BotDecision, TradeExecution, CuratedInsight
|
||||
from services import searx_news, ollama_decide, place_order, market_open, positions_snapshot, summarize_news_with_ollama
|
||||
|
||||
scheduler = BackgroundScheduler(timezone=settings.timezone)
|
||||
|
||||
|
||||
def _daily_spent(db):
|
||||
since = datetime.utcnow() - timedelta(hours=24)
|
||||
return float(db.query(func.coalesce(func.sum(TradeExecution.notional), 0)).filter(TradeExecution.ts >= since).scalar() or 0)
|
||||
|
||||
|
||||
def curate_cycle():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
for symbol in settings.symbols:
|
||||
news = searx_news(symbol)
|
||||
summary = summarize_news_with_ollama(symbol, news)
|
||||
row = CuratedInsight(symbol=symbol, summary=summary, sources=json.dumps(news)[:60000])
|
||||
db.add(row)
|
||||
db.commit()
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def run_cycle():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
if not market_open():
|
||||
return
|
||||
spent = _daily_spent(db)
|
||||
if spent >= settings.max_daily_notional:
|
||||
return
|
||||
|
||||
pos = positions_snapshot()
|
||||
if len(pos) >= settings.max_open_positions:
|
||||
return
|
||||
|
||||
for symbol in settings.symbols:
|
||||
if spent >= settings.max_daily_notional:
|
||||
break
|
||||
|
||||
news = searx_news(symbol)
|
||||
decision = ollama_decide(symbol, news)
|
||||
|
||||
@@ -28,8 +60,20 @@ def run_cycle():
|
||||
db.commit()
|
||||
db.refresh(drow)
|
||||
|
||||
if decision["action"] in {"buy", "sell"} and decision["confidence"] >= 0.55:
|
||||
res = place_order(symbol, decision["action"], min(settings.max_order_usd, decision["order_usd"]))
|
||||
should_trade = (
|
||||
decision["action"] in {"buy", "sell"}
|
||||
and decision["confidence"] >= settings.min_confidence
|
||||
)
|
||||
|
||||
if should_trade:
|
||||
notional = min(settings.max_order_usd, decision["order_usd"], settings.max_daily_notional - spent)
|
||||
if notional <= 0:
|
||||
drow.status = "risk_blocked"
|
||||
db.add(drow)
|
||||
db.commit()
|
||||
continue
|
||||
|
||||
res = place_order(symbol, decision["action"], notional)
|
||||
ok = bool(res and res.get("ok"))
|
||||
drow.status = "executed" if ok else "failed"
|
||||
db.add(drow)
|
||||
@@ -37,11 +81,13 @@ def run_cycle():
|
||||
symbol=symbol,
|
||||
side=decision["action"],
|
||||
qty=float((res or {}).get("json", {}).get("qty", 0) or 0),
|
||||
notional=min(settings.max_order_usd, decision["order_usd"]),
|
||||
notional=notional,
|
||||
alpaca_order_id=(res or {}).get("json", {}).get("id", ""),
|
||||
raw=json.dumps(res)[:60000],
|
||||
))
|
||||
db.commit()
|
||||
if ok:
|
||||
spent += notional
|
||||
else:
|
||||
drow.status = "skipped"
|
||||
db.add(drow)
|
||||
@@ -51,5 +97,6 @@ def run_cycle():
|
||||
|
||||
|
||||
def start_scheduler():
|
||||
scheduler.add_job(curate_cycle, "interval", minutes=settings.curate_interval_minutes, id="curate_cycle", replace_existing=True)
|
||||
scheduler.add_job(run_cycle, "interval", hours=settings.trade_interval_hours, id="trade_cycle", replace_existing=True)
|
||||
scheduler.start()
|
||||
|
||||
Reference in New Issue
Block a user