Autonomous investment intelligence from the terminal — research, backtest, and trade via a conversational agent powered by the Claude Agent SDK.
Project description
Alo Invest
Autonomous investment intelligence from the terminal. Research companies, build and backtest strategies, deploy to paper trading — through a conversational agent powered by the Claude Agent SDK. Or schedule the daily investor cycle and let it run the book autonomously, inside walls enforced by code.
Quick start
git clone <repo>
cd alo-invest
cp .env.example .env # add your API keys
uv run alo-invest
That's it. uv run handles the virtual environment and dependencies automatically.
API keys
Add keys to .env (see .env.example):
| Key | Required | What it enables |
|---|---|---|
ANTHROPIC_API_KEY |
Yes | Powers the agent |
ALPACA_API_KEY + ALPACA_SECRET_KEY |
For trading/data | Real-time quotes, OHLCV bars, paper trading |
PERPLEXITY_API_KEY |
For research | News, sentiment, analyst views |
TAVILY_API_KEY |
For research | Deep research, SEC filings, document extraction |
BINANCE_API_KEY + BINANCE_SECRET_KEY |
For crypto | Spot trading, futures, account balances |
BINANCE_TESTNET |
For crypto paper trading | Routes all Binance orders to testnet (set true) |
NTFY_TOPIC |
For push notifications | Daily memos and kill-switch alerts pushed to your phone via ntfy.sh |
The CLI warns on startup if optional keys are missing. ANTHROPIC_API_KEY is required — the agent won't start without it. Binance price and OHLCV data work without keys; only account and order tools require them.
See CRYPTO.md for full crypto and paper trading setup.
How it works
Alo Invest runs an agentic loop via the Claude Agent SDK. You give it a goal — it plans the steps, calls tools, reads results, and iterates until done.
Data sources
| Source | What it provides | Speed |
|---|---|---|
| Alpaca MCP | Real-time quotes, OHLCV bars, account, positions, order placement | Instant |
| Binance MCP | Crypto prices, OHLCV bars, spot trading, USDT-margined futures, funding rates | Instant |
| Fundamentals CLI | Market cap, P/E, margins, sector, revenue, beta (via yfinance) | ~1-2s |
| Perplexity MCP | News, catalysts, analyst views, sentiment | 2-3s |
| Tavily MCP | Deep research, SEC filings, URL extraction | 5-10s |
| Backtest engine | Strategy simulation with integrity checks (uses yfinance for historical data) | Varies |
The agent knows which source to use for what — it won't web-search for a crypto price when Binance can return it instantly.
Workspace vs Portfolio
Workspace (always active, defaults to cwd):
- Where files, strategies, backtests, and charts are saved
Portfolio (optional, --portfolio ~/alo-invest):
- Adds:
INVEST.mdrules,config/*.yamlconstraints injected into the system prompt - Adds: memory persistence, audit logs, paper trading via Alpaca
# Initialize a portfolio when you're ready for trading
uv run alo-invest init ~/alo-invest
uv run alo-invest --portfolio ~/alo-invest
# Set this to skip --portfolio every time
export ALO_INVEST_PORTFOLIO=~/alo-invest
After init, the portfolio holds (everything the agent reads or writes lives here):
config/ risk, universe, budgets YAML (sha256-stamped)
skills/ your overrides + cohort siblings from /strategy-factory
strategies/ draft, active, rejected — runnable .py files
backtests/runs/ backtest result JSON
reports/ daily, weekly, regime, strategy (cohort memos)
data/ market cache, portfolio positions, watch tripwires,
factory exploration map
proposals/ pending, approved, executed
audit/ JSONL log of every tool call and decision
theses/ long-form research notes
Cohort memos from /strategy-factory land in reports/strategy/; the
exploration map the factory reads and appends is at
data/factory/exploration.yaml.
Modes
uv run alo-invest # paper mode (default) — can submit paper orders
uv run alo-invest --mode research # research only — orders blocked
uv run alo-invest --mode live # live trading (use with care)
Autonomous mode
Schedule the daily investor cycle and the agent runs an investor's routine unattended — review the book, grade its own predictions, assess every playbook's track record, reweight the portfolio, research one idea, and send you the daily memo as a notification:
uv run alo-invest schedule install --portfolio ~/alo-invest --at 09:00
uv run alo-invest schedule status
uv run alo-invest schedule remove
Scheduled runs are paper-only by construction — there is no flag to make the loop trade live. Live access is earned later, once the prediction journal shows the autonomous operator is well-calibrated.
Publishing to the public site
alo-invest publish run snapshots the live Alpaca paper account + portfolio state into docs/content/public/ (three JSON files: latest.json, nav-history.json, trades-30d.json). Those are the only files the Next.js site reads — push them and Vercel rebuilds with real data:
# Snapshot locally — eyeball before publishing
uv run alo-invest publish run --portfolio ~/alo-invest
# Snapshot + git commit + push (Vercel rebuilds automatically)
uv run alo-invest publish run --portfolio ~/alo-invest --push
The publisher is conservative-by-default. It only ever writes the three allowlisted files; never thesis text from predictions, audit logs, API keys, draft strategies, dossier internals, or watcher state.
Live prices without publishing (recommended). Set ALPACA_API_KEY and ALPACA_SECRET_KEY as environment variables on the Vercel project (paper keys, server-side only — never shipped to the browser). The live pages then fetch NAV, positions, the equity curve, and fills straight from Alpaca at render time, revalidated at most once a minute. No background process anywhere: nothing runs on your machine, and Vercel only fetches when someone loads the page. The committed JSON stays as the fallback (keys absent, Alpaca down) and remains the only source for cycle-derived state — funnel counts, prediction grade, strategy attribution, options max-loss — which updates whenever a cycle publishes. See docs/lib/alpaca.ts and the overlay in docs/lib/public-data.ts.
Scheduled publishing (alternative). If you'd rather not put broker keys on Vercel, publish install schedules snapshot + push as a launchd job on your machine — no model, no tokens, push script idempotent:
uv run alo-invest publish install --portfolio ~/alo-invest --at 17:00 # daily after the close
uv run alo-invest publish install --portfolio ~/alo-invest --every 30 # or intraday
uv run alo-invest publish status
uv run alo-invest publish remove
Stop publishing. Three levels, depending on what you want to keep:
# 1. Stop automatic publishing (and optionally the autonomous loop)
uv run alo-invest publish remove
uv run alo-invest schedule remove
# 2. Take the site offline immediately by reverting the public snapshot
# Restores whatever was in git for those files (the seed data, or the last good cycle)
git checkout HEAD -- docs/content/public/
git commit -m "publish: pause public site" docs/content/public/
git push
# 3. Stop the docs site from rebuilding at all
# Pause the project in the Vercel dashboard — pushes still happen, deploys don't
Removing the schedule is the right move when you're pausing the experiment. Reverting docs/content/public/ is for when you've published something you didn't mean to (a draft cycle, a bad day you want to roll back). Pausing in Vercel is for an extended hiatus where you don't want any new builds at all.
The architecture has three layers with different enforcement:
1. Invariants — enforced in code. This is the only place rules live; the model cannot route around them:
- Pre-trade risk gate — a hook intercepts every order-placement tool call (Alpaca and Binance) and validates universe, notional, position size, cash floor, and shorting before anything reaches a broker. Fails closed.
- Per-domain risk budgets (
config/budgets.yaml) — different instruments get different walls: equities have a NAV-share cap and net-exposure band; crypto perps have a gross-notional cap, leverage ceiling, and liquidation buffer.reduce_onlyorders always pass — they can only shrink risk. - NAV kill-switch — when account equity draws down past
hard_stop_pctfrom its tracked peak, positions are flattened, all order flow halts on every venue, and you get notified. Only a human clears it:alo-invest resume-trading. - Config tamper guard — risk configs are human-owned. They are hashed at approval time and verified on every order; any drift (by any path) blocks all trading until you review and run
alo-invest config-approve.
2. Evidence — enforced by data. Strategies are skills, and each is a self-documenting hypothesis: YAML frontmatter declares its domain (which risk budget it inherits), regime assumptions, and falsification condition. Each playbook carries a dossier — walk-forward results, live-vs-backtest divergence, prediction journal entries tagged to it. The cycle reads every dossier before reweighting, so the investor can never pretend not to know a strategy is unproven.
3. Judgment — the model. Which playbook fits the regime, conviction, sizing. One portfolio, one NAV, continuous reweighting — playbooks get flagged and weighted down, not killed.
CLI reference
# REPL
uv run alo-invest
uv run alo-invest --workspace ~/research
uv run alo-invest --portfolio ~/alo-invest
uv run alo-invest --think # extended thinking
# Resume
uv run alo-invest --resume latest
uv run alo-invest sessions
# One-shot
uv run alo-invest --prompt "analyze NVDA"
# Setup
uv run alo-invest init [directory]
uv run alo-invest doctor
# Autonomous operation
uv run alo-invest schedule install -p <portfolio> --at 09:00 # daily cycle (launchd, paper only)
uv run alo-invest schedule status
uv run alo-invest schedule remove
uv run alo-invest config-approve -p <portfolio> # approve risk configs after editing them
uv run alo-invest resume-trading -p <portfolio> # clear a tripped NAV kill-switch
Skills
Skills are slash commands that encode repeatable investment workflows. Built-in skills work without a portfolio; user skills in {portfolio}/skills/ override defaults.
| Skill | Purpose |
|---|---|
/analyze <ticker> |
Structured analysis: fundamentals, price action, news, risks, chart |
/crypto-analyze <symbol> |
Crypto analysis: price, funding rate, positioning, technicals, on-chain narrative, risks |
/funding-scan |
Scan funding rates across top perps — crowded positioning, basis trade candidates, market sentiment |
/basis-trade <symbol> |
Structure and execute a cash-and-carry: long spot + short perp, collect funding |
/backtest <file> |
Run backtest with verdict |
/red-team <file> |
Adversarial critique of a strategy |
/plan <task> |
Think through steps before executing |
/strategy-audit <file> |
Check for look-ahead bias, overfitting, survivorship |
/risk-check <file> |
Validate against portfolio risk limits |
/paper-deploy <file> |
Deploy strategy to Alpaca paper trading |
/post-mortem <name> |
Compare a deployed strategy's live performance against the backtest that justified it |
/scorecard |
Grade the prediction journal — hit rate, Brier score, calibration |
/weekly-review |
Portfolio performance review |
/investor-cycle |
The daily autonomous routine — safety check, review, grading, dossiers, reweighting, research, memo |
Playbook contracts
A strategy skill can declare its hypothesis as YAML frontmatter — the contract is rendered into the prompt every time the playbook is invoked, and playbook_dossier ties its track record to it:
---
name: btc-funding-harvest
domain: crypto_perps # which risk budget it inherits
regime: positive funding, low realized volatility
falsification: 30-day funding collected minus costs goes negative
---
Playbook instructions...
Writing strategies
Strategies are Python files with two supported interfaces:
Weight-based (engine handles simulation)
UNIVERSE = ["SPY", "QQQ", "GLD", "BIL"]
DESCRIPTION = "6-month momentum with cash filter"
CONFIG = {"normalize": True, "cost_bps": 5, "benchmark": "SPY"}
def generate_signals(prices, context=None):
# prices: pd.DataFrame of daily close prices
# return: pd.DataFrame of target weights
momentum = prices.pct_change(126)
...
return weights
NAV-based (full control)
def run(context):
# Full control: fetch any data, model any logic
# return: pd.Series of daily NAV starting at 1.0
...
return nav
Run either with /backtest strategies/draft/my_strategy.py or ask the agent to write one for you.
Configuration
config/policy.yaml
portfolio:
objective: long_term_growth
time_horizon_years: 5
risk_profile: moderate
execution:
default_mode: paper
live_trading_enabled: false
requirements:
backtest_before_paper: true
risk_check_before_trade: true
human_approval_for_live: true
config/universe.yaml
approved:
etfs: [SPY, QQQ, VTI, GLD, BIL, TLT]
equities: [AAPL, MSFT, NVDA, GOOGL]
forbidden: [TQQQ, SQQQ, UVXY]
config/risk.yaml
position_limits:
max_single_position_pct: 40
min_cash_pct: 5
drawdown_limits:
hard_stop_pct: -20 # NAV kill-switch threshold — flatten + halt + notify
trading_limits:
max_trade_notional_usd: 5000
allow_short_selling: false
config/budgets.yaml
Per-domain risk budgets — different walls for different risk physics, enforced mechanically on every order:
domains:
equities:
max_pct_nav: 60 # gross equities exposure cap, % of NAV
net_exposure_band: [0, 60] # post-trade net exposure must stay in this band
crypto_spot:
max_pct_nav: 10
crypto_perps:
max_pct_nav: 5 # gross perp notional cap, % of NAV
max_leverage: 2
min_liquidation_buffer_pct: 25 # required distance from mark to liquidation
Configs are human-owned. init stamps them with sha256 hashes; the risk gate verifies the stamp on every order. The agent cannot edit them, and if they drift by any other path, all order flow stops until you review and run alo-invest config-approve.
Troubleshooting
Alpaca DNS unreachable on macOS
If the investor cycle reports Alpaca as DNS-unreachable but curl https://paper-api.alpaca.markets succeeds, the issue is a stale mDNSResponder cache entry for the CNAME record. Python's socket.getaddrinfo hits it directly while system tools work around it.
Fix: flush the DNS cache and restart mDNSResponder.
sudo killall -HUP mDNSResponder
Verify with:
python3 -c "import socket; print(socket.getaddrinfo('paper-api.alpaca.markets', 443))"
Then run uv run alo-invest doctor to confirm Alpaca is green before restarting the cycle.
What Alo Invest is not
- Not a live trading bot — autonomous operation is paper-only by construction; live orders require explicit
--mode liveand human approval per trade - Not unsupervised — the autonomous cycle runs inside code-enforced walls (risk gate, domain budgets, NAV kill-switch) and reports to you daily
- Not a source of financial advice — treat all output as research, not recommendations
- Not guaranteed to beat the market — backtests don't predict future performance
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file alo_invest-0.1.0.tar.gz.
File metadata
- Download URL: alo_invest-0.1.0.tar.gz
- Upload date:
- Size: 173.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a52a6439692f6e83fdb7bd7d8643893e67245505fa33b6d37c1636366c1a7b18
|
|
| MD5 |
1cfb2da564b8b47be44871e72caf850d
|
|
| BLAKE2b-256 |
a579c180342bc38fd2c984b349445d03a1e3f27aaa57611b2ce4ca530b70169d
|
File details
Details for the file alo_invest-0.1.0-py3-none-any.whl.
File metadata
- Download URL: alo_invest-0.1.0-py3-none-any.whl
- Upload date:
- Size: 221.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1706d2dfeed4a3e34f84e562d01cf33c943ab13fd2b897a193d33366781611c
|
|
| MD5 |
3fcef06e33ea37a5e68563afba2bf6b8
|
|
| BLAKE2b-256 |
a055a218b83793490afe199dddb4ade435a57099e6dbd9bca21b8614dee432fb
|