Documentation Index
Fetch the complete documentation index at: https://mathematicalcompany.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Operational infrastructure that runs alongside the oversight loop. These subsystems handle notifications, accounting, strategy promotion, and automatic recovery from failures.
Alerts and Notifications
Rate-limited alert system with multi-channel delivery. Alerts fire during the oversight loop for risk events, kill switch activations, strategy failures, and settlements.
Alert Levels and Categories
| Level | Use |
|---|
INFO | Routine events (settlement detected, strategy promoted) |
WARNING | Approaching limits (80% drawdown, strategy paused) |
CRITICAL | Immediate action needed (kill switch, multiple failures) |
| Category | Triggered By |
|---|
KILL_SWITCH | Fund drawdown exceeds limit |
STRATEGY_FAILURE | Strategy thread crashes |
DRAWDOWN | Fund or strategy drawdown approaching limit |
SETTLEMENT | Market resolved, P&L booked |
CORRELATION | Cross-strategy correlation exceeds threshold |
RISK_LIMIT | VaR budget breach |
RECOVERY | Self-healing restart attempt |
PROMOTION | Strategy promoted to next stage |
REBALANCE | Capital reallocation triggered |
GENERAL | Everything else |
Rate Limiting
Each category is limited to 5 alerts per 60 seconds. Excess alerts are silently dropped to prevent notification floods during volatile periods.
Channels
from horizon.fund import FundConfig
# Webhook channel -- delivers alerts via HTTP POST
fund = FundManager(FundConfig(
total_capital=100_000,
alert_webhook_url="https://hooks.slack.com/services/...",
))
# The webhook sends JSON:
# {
# "level": "CRITICAL",
# "category": "KILL_SWITCH",
# "title": "Kill switch activated",
# "message": "Fund drawdown 15.2% exceeded limit 15.0%",
# "timestamp": "2026-03-13T14:30:00.000000"
# }
Webhook delivery runs in a background thread to avoid blocking the oversight loop. Failed deliveries are logged but do not retry.
Returns recent alerts with counts by category. Use this to check what happened while the LLM was idle.
Double-Entry Ledger
SQLite-backed accounting ledger that records every financial event as a debit/credit journal entry. Provides balance sheet and income statement views.
Accounts
| Account | Type | Tracks |
|---|
CASH | Asset | Available capital |
POSITIONS | Asset | Capital deployed in positions |
FEES_RECEIVABLE | Asset | Fees owed (internal) |
MANAGEMENT_FEES | Revenue | Annual management fee accruals |
PERFORMANCE_FEES | Revenue | Performance fee accruals |
REALIZED_PNL | Revenue | Booked profit and loss |
UNREALIZED_PNL | Revenue | Mark-to-market P&L |
Journal Entries
Every financial event creates a balanced journal entry:
| Event | Debit | Credit | Amount |
|---|
| Deposit | CASH | (external) | Deposit amount |
| Withdrawal | (external) | CASH | Withdrawal amount |
| Profitable trade | CASH | REALIZED_PNL | Profit |
| Losing trade | REALIZED_PNL | CASH | Loss |
| Management fee | MANAGEMENT_FEES | FEES_RECEIVABLE | Fee amount |
| Performance fee | PERFORMANCE_FEES | FEES_RECEIVABLE | Fee amount |
Configuration
fund = FundManager(FundConfig(
total_capital=100_000,
ledger_enabled=True, # Enable double-entry ledger
))
When enabled, the oversight loop automatically records:
- Settlement P&L when markets resolve
- Management and performance fee accruals each tick
- All amounts validated (must be finite, positive)
Returns recent journal entries, balance sheet (assets vs. liabilities), and income statement.
Reports
# Balance sheet
bs = fund.ledger.balance_sheet()
# {"CASH": 95000.0, "POSITIONS": 5000.0, "MANAGEMENT_FEES": 200.0, ...}
# Income statement
inc = fund.ledger.income_statement()
# {"REALIZED_PNL": 1500.0, "MANAGEMENT_FEES": -200.0, "PERFORMANCE_FEES": -100.0}
# Recent entries
entries = fund.ledger.recent_entries(limit=20)
Structured lifecycle that gates strategy promotion behind performance criteria. Strategies must prove themselves in paper trading before progressing to shadow mode and then live trading.
PAPER --> SHADOW --> LIVE
| Stage | Description |
|---|
| PAPER | Simulated execution, no real capital |
| SHADOW | Runs alongside live but orders not submitted |
| LIVE | Real execution with real capital |
Each transition requires meeting minimum thresholds:
| Criteria | Default | Description |
|---|
min_paper_days | 14 | Minimum days in current stage |
min_trades | 50 | Minimum number of trades |
min_sharpe | 1.0 | Minimum Sharpe ratio |
max_drawdown_pct | 20.0 | Maximum drawdown percentage |
min_win_rate | 0.45 | Minimum win rate |
Configuration
fund = FundManager(FundConfig(
total_capital=100_000,
promotion_enabled=True, # Enable promotion lifecycle
))
When enabled, the oversight loop checks promotion eligibility every 60 ticks. Promotions are recorded with a full audit trail in SQLite (strategy name, from/to stage, timestamp, metrics at promotion time).
Returns current promotion stage for each strategy and promotion history.
Persistence
Two SQLite tables track state:
promotions: current stage per strategy
promotion_history: every promotion event with timestamp and reason
Data is stored in ~/.horizon/promotion.db with WAL mode and 0o600 permissions.
Self-Healing Recovery
Automatic strategy restart with exponential backoff and circuit breaker. Detects failed strategies and stale feeds, attempts recovery without human intervention.
How It Works
- The oversight loop calls
recovery.check_strategies() every 10 ticks
- For each failed strategy, it attempts a restart via
controller.restart()
- If the restart fails, backoff doubles: 60s, 120s, 240s…
- After 3 failed attempts, the circuit breaker opens and the strategy is marked permanently failed
Circuit Breaker
| Attempt | Cooldown | Action |
|---|
| 1 | 60s | Restart strategy |
| 2 | 120s | Restart strategy |
| 3 | 240s | Restart strategy |
| 4+ | — | Circuit breaker open, no more retries |
To re-enable recovery for a permanently failed strategy:
fund.recovery.reset("strategy_name")
Feed Monitoring
The recovery manager also detects stale feeds (no update for 300+ seconds) and logs warnings. Feed staleness is informational; automatic feed reconnection is handled by the feed manager.
Configuration
fund = FundManager(FundConfig(
total_capital=100_000,
self_healing_enabled=True, # Enable self-healing
))
Recovery Events
Every recovery attempt is logged:
events = fund.recovery.event_history(limit=20)
# [
# RecoveryEvent(
# action="restart_strategy",
# target="political_mm",
# success=True,
# attempt=1,
# error=None,
# ),
# ]
stats = fund.recovery.stats()
# {"total_attempts": 5, "successes": 3, "failures": 2, "circuit_breakers_open": 1}
Configuration Summary
| Parameter | Default | Description |
|---|
alert_webhook_url | None | Webhook URL for alert delivery |
ledger_enabled | False | Enable double-entry accounting |
promotion_enabled | False | Enable paper-to-live promotion |
self_healing_enabled | False | Enable automatic recovery |
All four subsystems are off by default. Enable them individually in FundConfig. They integrate with the oversight loop automatically when enabled.