Horizon reads credentials and configuration from environment variables as a fallback when not passed explicitly to constructors.
Horizon API Key
| Variable | Description |
|---|
HORIZON_API_KEY | Your Horizon SDK API key (required) |
export HORIZON_API_KEY="hz_live_abc123..."
Get your key at api.mathematicalcompany.com. See the Authentication page for details.
Polymarket
| Variable | Description |
|---|
POLYMARKET_PRIVATE_KEY | Ethereum private key (hex, with or without 0x prefix) |
POLYMARKET_API_KEY | CLOB API key |
POLYMARKET_API_SECRET | CLOB API secret |
POLYMARKET_API_PASSPHRASE | CLOB API passphrase |
export POLYMARKET_PRIVATE_KEY="0x..."
export POLYMARKET_API_KEY="..."
export POLYMARKET_API_SECRET="..."
export POLYMARKET_API_PASSPHRASE="..."
hz.Polymarket() # reads from env automatically
Kalshi
| Variable | Description |
|---|
KALSHI_EMAIL | Kalshi account email |
KALSHI_PASSWORD | Kalshi account password |
KALSHI_API_KEY | Kalshi API key |
KALSHI_API_URL | Kalshi API base URL (override for demo/staging) |
export KALSHI_API_KEY="..."
# or
export KALSHI_EMAIL="..."
export KALSHI_PASSWORD="..."
hz.Kalshi() # reads from env automatically
Database
| Variable | Description | Default |
|---|
HORIZON_DB | SQLite database path | ./{strategy_name}.db |
export HORIZON_DB="/data/horizon.db"
Logging
| Variable | Description | Default |
|---|
HORIZON_LOG | Rust tracing filter level | warn |
Valid levels: debug, info, warn, error
Python Logging
Horizon uses Python’s logging module for the Python layer:
import logging
logging.basicConfig(level=logging.DEBUG)
Rust Logging
Set the Rust tracing level via environment variable:
HORIZON_LOG=debug python my_strategy.py
CLI Log Level
The --log-level flag sets both Python logging and the HORIZON_LOG environment variable:
python -m horizon run strategy.py --log-level=debug
Credential Resolution Order
Both Polymarket and Kalshi resolve credentials in this order:
Explicit constructor arguments
hz.Polymarket(private_key="0x...", api_key="...")
Environment variables
hz.Polymarket() # reads POLYMARKET_PRIVATE_KEY, etc.
Explicit arguments always take priority over environment variables.
Hot-Reload Parameters
Update strategy parameters at runtime without restarting. Three approaches: pipeline function, standalone ParamReloader, or engine runtime params.
Pipeline Function
Drop hz.hot_reload() into your pipeline to inject parameters into ctx.params every cycle.
File-based:
import horizon as hz
hz.run(
pipeline=[
hz.hot_reload(source="params.json"),
my_strategy, # access via ctx.params["spread"]
],
...
)
Write new parameters (takes effect next cycle):
hz.write_params("params.json", {"spread": 0.05, "gamma": 0.3})
Dict-based:
live_params = {"spread": 0.04}
hz.run(
pipeline=[
hz.hot_reload(source=live_params),
my_strategy,
],
...
)
# Update at runtime:
live_params["spread"] = 0.06 # Takes effect next cycle
ParamReloader
Standalone class for watching a JSON parameter file. Useful outside of hz.run().
import horizon as hz
reloader = hz.ParamReloader(
path="params.json", # Path to JSON file
poll_interval=1.0, # Seconds between file stat checks (default: 1.0)
)
# Get initial parameters (always returns a dict, never None)
params = reloader.current()
print(params) # {"spread": 0.04, "gamma": 0.3}
# Check for updates (returns new dict if file changed, None otherwise)
updated = reloader.check()
if updated is not None:
print(f"Params changed: {updated}")
| Method | Returns | Description |
|---|
current() | dict[str, float] | Most recently loaded parameters (never None) |
check() | dict[str, float] or None | New params if file changed, else None |
check() returns None on the first call if the file hasn’t changed since construction. Use current() to get the initial values.
Engine Runtime Params
Set and retrieve parameters directly on the Engine.
engine = hz.Engine()
engine.update_param("spread", 0.05)
engine.update_params_batch({"spread": 0.05, "gamma": 0.3})
params = engine.get_all_params() # {"spread": 0.05, "gamma": 0.3}
val = engine.get_param("spread") # 0.05
engine.remove_param("gamma")
These are injected into ctx.params every cycle when using hz.run().