Skip to main content

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.md rules, config/*.yaml constraints 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_only orders always pass — they can only shrink risk.
  • NAV kill-switch — when account equity draws down past hard_stop_pct from 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 live and 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

alo_invest-0.1.0.tar.gz (173.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

alo_invest-0.1.0-py3-none-any.whl (221.0 kB view details)

Uploaded Python 3

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

Hashes for alo_invest-0.1.0.tar.gz
Algorithm Hash digest
SHA256 a52a6439692f6e83fdb7bd7d8643893e67245505fa33b6d37c1636366c1a7b18
MD5 1cfb2da564b8b47be44871e72caf850d
BLAKE2b-256 a579c180342bc38fd2c984b349445d03a1e3f27aaa57611b2ce4ca530b70169d

See more details on using hashes here.

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

Hashes for alo_invest-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f1706d2dfeed4a3e34f84e562d01cf33c943ab13fd2b897a193d33366781611c
MD5 3fcef06e33ea37a5e68563afba2bf6b8
BLAKE2b-256 a055a218b83793490afe199dddb4ade435a57099e6dbd9bca21b8614dee432fb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page