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.
As prediction markets become regulated financial products, Horizon provides a compliance module that gives hedge funds and institutional traders the infrastructure required for regulatory oversight.
Quick Start
import horizon as hz
from horizon.compliance import (
ComplianceManager,
ComplianceConfig,
RegulatoryLimitConfig,
Role,
compliance_gate,
)
# Configure compliance
config = ComplianceConfig(
enabled=True,
roles={
"alice": Role.TRADER,
"bob": Role.COMPLIANCE_OFFICER,
"charlie": Role.RISK_MANAGER,
"admin": Role.ADMIN,
},
approval_enabled=True,
approval_threshold_notional=10_000,
regulatory_limits=RegulatoryLimitConfig(
max_total_notional=1_000_000,
max_concentration_pct=25.0,
restricted_markets={"BANNED-MARKET-123"},
max_daily_volume=500_000,
max_orders_per_minute=120,
),
)
compliance = ComplianceManager(config)
# Use in pipeline
hz.run(
name="compliant_strategy",
pipeline=[compliance_gate, my_signal, my_sizer],
markets=["will-x-happen"],
compliance=compliance,
)
Architecture
The compliance module is Python-only (not on the hot path) and uses a separate SQLite database so compliance data survives strategy changes. All components are thread-safe.
ComplianceManager (orchestrator)
├── AuditTrail - SHA-256 hash chain, tamper-evident
├── TradeSurveillance - 5 real-time detectors
├── ApprovalGateway - async human-in-the-loop
├── AccessControl - RBAC with 5 roles
├── RegulatoryLimits - firm-wide position/exposure limits
├── ReportGenerator - daily, position, and compliance reports
├── ComplianceKillSwitch - RBAC-gated emergency halt
└── RetentionManager - SEC 17a-4 data retention
Audit Trail
Every compliance event is recorded with a SHA-256 hash chain. Each event links to its predecessor, creating a tamper-evident log that auditors can verify end-to-end.
# Record events (happens automatically when using ComplianceManager)
compliance.record_fill("will-btc-100k", "buy", 50.0, 0.62)
# Verify integrity
valid, events_checked = compliance.verify_audit_integrity()
assert valid # True if no tampering detected
# Export for regulatory submission
compliance.export_audit("audit_2026_q1.csv", start=q1_start, end=q1_end)
Tracked Event Types
| Event Type | When Recorded |
|---|
ORDER_SUBMITTED | Order passes compliance gate |
ORDER_FILLED | Fill recorded |
ORDER_CANCELED | Cancellation recorded |
ORDER_REJECTED | Compliance rejects order |
ORDER_AMENDED | Order amendment |
POSITION_CHANGED | Position update |
CONFIG_CHANGED | Compliance config or role change |
KILL_SWITCH_ACTIVATED | Emergency halt activated |
KILL_SWITCH_DEACTIVATED | Emergency halt deactivated |
APPROVAL_REQUESTED | Order held for review |
APPROVAL_GRANTED | Reviewer approves |
APPROVAL_DENIED | Reviewer denies |
APPROVAL_EXPIRED | Request auto-expired |
SURVEILLANCE_ALERT | Surveillance detector fires |
LIMIT_BREACH | Regulatory limit breached |
REPORT_GENERATED | Compliance report created |
DATA_PURGED | Retention purge executed |
Trade Surveillance
Real-time detection of manipulative trading patterns using sliding-window analysis. Runs once per strategy cycle via on_cycle().
Detectors
| Detector | What It Catches | Severity |
|---|
| Wash Trading | Buy + sell same market within threshold | Critical |
| Spoofing | Order placed and canceled before fill | Warning |
| Layering | N stacked orders on one side | Warning |
| Rapid-Fire | Order rate exceeding limit/minute | Warning |
| Concentration | Single market exceeding portfolio share | Warning |
config = ComplianceConfig(
surveillance_enabled=True,
surveillance_window_secs=300, # 5-minute window
wash_trade_threshold_secs=5.0, # buy+sell within 5s
spoof_cancel_threshold_secs=2.0, # order+cancel within 2s
layering_depth=3, # 3+ stacked orders
regulatory_limits=RegulatoryLimitConfig(
max_orders_per_minute=120,
max_concentration_pct=25.0,
),
)
Accessing Alerts
# Latest alerts from last cycle
alerts = compliance.surveillance.recent_alerts
# Query historical alerts
from horizon.compliance import ComplianceStore
alerts = compliance.store.query_alerts(
start=yesterday_ts,
severity="critical",
resolved=False,
limit=100,
)
# Resolve an alert
compliance.store.resolve_alert(alert_id, resolved_by="bob", notes="False positive")
Human-in-the-Loop Approval
Orders exceeding a notional threshold are held for manual review. The strategy loop is never blocked - pending orders are queued and submitted asynchronously when approved.
config = ComplianceConfig(
approval_enabled=True,
approval_threshold_notional=10_000,
approval_timeout_secs=300, # 5-minute expiry
approval_callback=my_notification_fn, # optional webhook
)
Approval Workflow
# 1. Order above threshold → held for review
result = compliance.check_order("market-x", "buy", 0.65, 20_000, actor="alice")
# result == ComplianceAction.PENDING_APPROVAL
# 2. Reviewer sees pending orders
pending = compliance.approval.pending()
# 3. Approve or deny
compliance.approval.approve(pending[0].request_id, "bob", notes="within risk budget")
# or
compliance.approval.deny(pending[0].request_id, "bob", notes="too concentrated")
# 4. Approved orders are submitted on next cycle via drain_approved()
Notification Callback
def notify_slack(request: ApprovalRequest):
"""Send approval request to Slack (or any webhook)."""
requests.post(SLACK_WEBHOOK, json={
"text": f"Order pending approval: {request.market_id} "
f"notional=${request.order_data['price'] * request.order_data['size']:,.0f} "
f"(expires in {request.expires_at - request.timestamp:.0f}s)",
})
config = ComplianceConfig(
approval_enabled=True,
approval_callback=notify_slack,
)
Role-Based Access Control (RBAC)
Five hierarchical roles control who can perform compliance-sensitive operations.
| Role | Permissions |
|---|
| Viewer | View positions, feeds, orders, audit trail, alerts, reports |
| Trader | Submit/cancel orders, view positions/feeds/orders/alerts |
| Risk Manager | Trader + kill switch, modify risk config |
| Compliance Officer | Risk Manager + approve/deny orders, resolve alerts, generate reports, modify compliance config, export audit |
| Admin | Compliance Officer + manage roles, purge data |
# Check permission
can_approve = compliance.access.check_permission("alice", "approve_order")
# Require permission (raises PermissionError if denied)
compliance.access.require_permission("bob", "approve_order")
# Assign roles (requires Admin)
compliance.access.assign_role("new_trader", Role.TRADER, assigned_by="admin")
# List current roles
roles = compliance.access.list_roles()
# {"alice": "trader", "bob": "compliance_officer", ...}
Regulatory Limits
Firm-wide limits enforced before order submission, separate from strategy-level RiskConfig.
| Limit | Description |
|---|
restricted_markets | Blacklisted market IDs - orders rejected immediately |
max_position_per_market | Per-market position size cap |
max_total_notional | Portfolio-wide notional cap |
max_concentration_pct | Max % of portfolio in one market (default 25%) |
max_daily_volume | Daily traded volume cap |
max_orders_per_minute | Order rate limit (default 120) |
max_cancel_ratio | Max cancel-to-order ratio (default 95%) |
limits = RegulatoryLimitConfig(
restricted_markets={"ILLEGAL-MARKET"},
max_position_per_market={"BTC-ABOVE-100K": 1000.0},
max_total_notional=5_000_000,
max_concentration_pct=20.0,
max_daily_volume=2_000_000,
max_orders_per_minute=60,
)
# Check utilization
util = compliance.limits.current_utilization(engine)
# {"total_notional": 45.2, "daily_volume": 12.8, "position_BTC-ABOVE-100K": 30.0}
Kill Switch
Emergency halt with RBAC enforcement. Requires RISK_MANAGER role or higher. Snapshots all open positions at activation time for post-incident review.
# Activate (cancels all orders, prevents new ones)
compliance.kill_switch.activate(
reason="Flash crash detected",
actor="charlie", # must be risk_manager+
)
# Check status
status = compliance.kill_switch.status()
# {"active": True, "activated_by": "charlie", "reason": "Flash crash detected", ...}
# Deactivate with justification
compliance.kill_switch.deactivate(
actor="charlie",
justification="Market stabilized, positions reviewed",
)
Reporting
Generate regulatory-ready reports for internal review and filing.
Daily Trade Report
report = compliance.daily_report("2026-03-12")
print(f"Orders: {report.total_orders}")
print(f"Fills: {report.total_fills}")
print(f"Volume: ${report.total_volume:,.2f}")
print(f"Alerts: {report.surveillance_alerts}")
for entry in report.by_market:
print(f" {entry['market_id']}: {entry['orders']} orders, ${entry['volume']:,.2f}")
Position Report
report = compliance.position_report()
print(f"Total notional: ${report.total_notional:,.2f}")
for market, pct in report.concentration.items():
print(f" {market}: {pct:.1f}%")
Full Compliance Report
import time
report = compliance.compliance_report(
period_start=time.time() - 86400,
period_end=time.time(),
)
print(f"Chain integrity: {report.chain_integrity}")
print(f"Kill switch events: {report.kill_switch_events}")
print(f"Surveillance alerts: {report.surveillance_summary}")
Data Retention
SEC Rule 17a-4 requires 6+ years of record retention. Default is 2,555 days (~7 years).
# Check retention status
status = compliance.retention.check_retention_status()
print(f"Total events: {status['total_events']}")
# Archive old records to CSV, then purge
result = compliance.retention.archive_and_purge(
archive_dir="/secure/compliance-archive/",
actor="admin",
)
print(f"Archived: {result['events_archived']}, Purged: {result['events_purged']}")
Pipeline Integration
Use compliance_gate as the first function in your pipeline to automatically enforce compliance on every cycle.
hz.run(
name="institutional_mm",
exchange=hz.Polymarket(...),
markets=["will-x-happen"],
pipeline=[
compliance_gate, # compliance checks first
my_signal,
my_sizer,
hz.market_maker,
],
compliance=compliance, # binds to engine automatically
)
When the kill switch is active, compliance_gate returns None, which halts the pipeline for that cycle. Surveillance runs every cycle. Stale approvals are auto-expired.
Standalone Usage
The compliance module works independently from hz.run():
from horizon.compliance import ComplianceManager, ComplianceConfig, Role
config = ComplianceConfig(
roles={"alice": Role.TRADER, "bob": Role.COMPLIANCE_OFFICER},
regulatory_limits=RegulatoryLimitConfig(
max_total_notional=1_000_000,
),
)
compliance = ComplianceManager(config)
compliance.bind_engine(engine)
# Manual pre-trade check
action = compliance.check_order("market-1", "buy", 0.55, 100, actor="alice")
if action == ComplianceAction.APPROVED:
engine.submit_quotes(...)
# Always close when done
compliance.close()
Configuration Reference
ComplianceConfig
| Field | Type | Default | Description |
|---|
enabled | bool | True | Master switch for all compliance checks |
audit_enabled | bool | True | Enable audit trail recording |
surveillance_enabled | bool | True | Enable trade surveillance |
approval_enabled | bool | False | Enable human-in-the-loop approval |
regulatory_limits | RegulatoryLimitConfig | (defaults) | Firm-wide limits |
approval_threshold_notional | float | 5000 | Notional threshold for approval |
approval_timeout_secs | float | 300 | Approval request TTL |
retention_days | int | 2555 | Data retention period (~7 years) |
db_path | str | None | None | SQLite database path |
surveillance_window_secs | float | 300 | Sliding window for surveillance |
wash_trade_threshold_secs | float | 5.0 | Buy+sell same market threshold |
spoof_cancel_threshold_secs | float | 2.0 | Order+cancel threshold |
layering_depth | int | 3 | Stacked orders threshold |
roles | dict[str, Role] | {} | Actor-to-role mapping |
kill_switch_requires_role | Role | RISK_MANAGER | Minimum role for kill switch |
approval_callback | Callable | None | None | Notification callback |
webhook_url | str | None | None | Webhook URL for alerts |
RegulatoryLimitConfig
| Field | Type | Default | Description |
|---|
max_position_per_market | dict[str, float] | {} | Per-market position limits |
max_total_notional | float | inf | Total portfolio notional cap |
max_concentration_pct | float | 25.0 | Max single-market concentration |
restricted_markets | set[str] | set() | Blacklisted market IDs |
max_daily_volume | float | inf | Daily volume cap |
max_orders_per_minute | int | 120 | Order rate limit |
max_cancel_ratio | float | 0.95 | Max cancel-to-order ratio |