smaug/main.py
Claude 7e9221a914 feat: add PLAN.md and insider copytrade POC implementation
- PLAN.md: full implementation plan from issue
- config.py: configurable thresholds, API keys via .env
- ingestion/: EDGAR RSS poller + Form 4 XML parser
- db/: SQLite schema + interface (WAL mode)
- signals/: filter engine (buy/10b5-1/value/role) + cluster detector
- alerts/: Slack webhook alert with score gating
- broker/: Alpaca paper/live trade execution
- backtest/: historical signal backtesting with yfinance
- main.py: CLI entrypoint (run | fetch-once | backtest)
2026-05-04 16:15:22 +00:00

79 lines
1.9 KiB
Python

import logging
import sys
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)
logger = logging.getLogger(__name__)
def _on_new_filing(filing: dict):
from signals.filter_engine import process_filing
from alerts.slack_alert import send_slack_alert
import config
signal = process_filing(filing)
if signal is None:
return
logger.info(
f"Signal: {signal['ticker']} score={signal['score']} cluster={signal['cluster_size']}"
)
if config.SLACK_WEBHOOK_URL:
send_slack_alert(signal)
if config.ALPACA_KEY and config.ALPACA_SECRET:
from broker.alpaca_client import execute_signal
execute_signal(signal)
def cmd_run():
from db.db import init_db
from ingestion.edgar_poller import run_poller
init_db()
logger.info("Database initialized")
run_poller(on_new_filing=_on_new_filing)
def cmd_backtest():
from backtest.backtest import run_backtest, print_summary
import config
logger.info("Running backtest...")
summary = run_backtest(
db_path=config.DB_PATH,
holding_days=config.HOLDING_PERIOD_DAYS,
min_score=config.SCORE_ALERT_THRESHOLD,
min_cluster_size=config.MIN_CLUSTER_SIZE,
)
print_summary(summary)
def cmd_fetch_once():
from db.db import init_db
from ingestion.edgar_poller import fetch_and_store_new_filings
init_db()
filings = fetch_and_store_new_filings()
logger.info(f"Fetched and stored {len(filings)} new filings")
for filing in filings:
signal = _on_new_filing(filing)
COMMANDS = {
"run": cmd_run,
"backtest": cmd_backtest,
"fetch-once": cmd_fetch_once,
}
if __name__ == "__main__":
cmd = sys.argv[1] if len(sys.argv) > 1 else "run"
if cmd not in COMMANDS:
print(f"Usage: python main.py [{' | '.join(COMMANDS)}]")
sys.exit(1)
COMMANDS[cmd]()