This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Passivbot is a cryptocurrency trading bot for perpetual futures markets. It uses a contrarian market-making strategy inspired by Martingale betting, written in Python for orchestration and Rust for performance-critical components (backtesting, order calculations, analysis).
# Install Rust (required)
# Visit https://www.rust-lang.org/tools/install
# Create and activate virtual environment
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Build Rust extensions (usually automatic, but can be done manually)
cd passivbot-rust
maturin develop --release
cd ..# Start live bot
python3 src/main.py -u {account_name_from_api-keys.json}
# or with custom config
python3 src/main.py path/to/config.json
# Adjust logging verbosity
python3 src/main.py path/to/config.json --debug-level {0-3}
# 0=warnings, 1=info, 2=debug, 3=trace# Run all tests
pytest
# Run specific test file
pytest tests/test_candlestick_manager.py
# Run with verbose output
pytest -v
# Run specific test
pytest tests/test_candlestick_manager.py::test_name# Run backtest with default config
python3 src/backtest.py
# Run with specific config
python3 src/backtest.py path/to/config.json
# Run suite scenarios
python3 src/backtest.py --suite
# Disable individual coin plotting
python3 src/backtest.py -dp
# Adjust logging
python3 src/backtest.py --debug-level 2# Run optimizer with default config
python3 src/optimize.py
# Run with specific config
python3 src/optimize.py configs/template.json
# Start from existing configs
python3 src/optimize.py --start configs/starting_pool/
# Fine-tune specific parameters only
python3 src/optimize.py -ft long_entry_grid_spacing_pct,long_entry_initial_qty_pct
# Run with suite scenarios
python3 src/optimize.py --suite# Interactive Pareto dashboard (recommended)
python3 src/tools/pareto_dash.py --data-root optimize_results
# Generate market-cap based coin list
python3 src/tools/generate_mcap_list.py -n 80 -m 200 -e binance,bybit -o configs/approved_coins.json
# Verify cached OHLCV data
python3 src/tools/verify_hlcvs_data.py
# Normalize JSON configs
python3 src/tools/streamline_json.py configs/template.json# Launch from passivbot root with venv activated
python3 -m jupyter labCritical principle: Rust is the source of truth.
-
Rust (
passivbot-rust/src/): All order calculation logic, backtesting engine, and analysis metrics live hereorchestrator.rs: Core order calculation engine (replaces legacy Python paths)backtest.rs: Backtesting engine that replays historical candlesanalysis.rs: Performance metrics computationentries.rs,closes.rs: Grid and trailing entry/close logicrisk.rs: Wallet exposure enforcement, unstuck calculationscoin_selection.rs: Forager mode coin filtering by volatilitypython.rs: PyO3 bindings exposing Rust functions to Python
-
Python (
src/): Orchestration, exchange communication, configuration, data management- Live bot talks to exchanges via CCXT, then calls Rust for order decisions
- Backtester prepares data, calls Rust backtest engine, plots results
- Optimizer manages genetic algorithm, evaluates candidates via Rust backtester
When implementing changes:
- Behavior changes (order logic, unstuck, risk) belong in Rust
- If fixing a bug in Python order calculation, port the fix to Rust instead
- Python patches to order logic are not acceptable; both live bot and backtester must use identical Rust code
- Fetches and caches 1m OHLCV data from exchanges
- On-disk cache in
caches/ohlcv/with Parquet storage - Handles multiple exchange data sources (Binance, Bybit, Bitget, KuCoin archives + CCXT fallback)
- Shared between live bot, backtester, and optimizer
- Uses per-symbol file locks with automatic stale cleanup for multi-process safety
- EMA warm-up calculation for indicators
- Main live trading loop
- Fetches positions, balances, open orders from exchange
- Calls Rust orchestrator via
pbr.calc_ideal_orders_orchestrator()to get desired orders - Reconciles ideal vs actual orders, creates/cancels as needed
- Manages coin approval, forced modes, graceful stops
- Runs asynchronously with exchange websocket feeds
- Replays historical 1m candles through Rust backtest engine
- Supports single-coin and multi-coin backtests
- Suite mode: evaluate multiple scenarios (date ranges, coin sets, exchanges) in one run
- Outputs metrics, plots, fills CSVs to
backtests/ - Can combine OHLCV from multiple exchanges ("best feed per coin")
- Uses NSGA-II genetic algorithm for multi-objective optimization
- Evaluates thousands of config candidates via Rust backtester
- Maintains Pareto front of non-dominated solutions
- Supports suite scenarios (optimize across multiple market conditions)
- Outputs to
optimize_results/with binary results log and Pareto JSON members - Shared-memory datasets for reduced RAM usage across worker processes
- Loads and validates JSON/HJSON configs
- Merges CLI arguments with config files
- Handles coin overrides (per-symbol parameter tweaks)
- Normalizes approved/ignored coin lists (can reference external files)
- Template config:
configs/template.json
-
Live Trading Loop:
- Fetch exchange state (positions, balances, orders, candles)
- Update CandlestickManager cache
- Compute EMAs and volatility indicators (Python)
- Call Rust orchestrator with full state → get ideal orders
- Diff ideal vs open orders, execute changes
- Sleep, repeat
-
Backtesting:
- CandlestickManager fetches/caches OHLCV for requested coins + date range
- If suite mode: prepare unified dataset across all scenarios
- Call Rust
backtest_single_coinorbacktest_multi_coinwith candle data + config - Rust engine simulates fills, updates balances, computes metrics
- Python receives results, plots equity curves, writes analysis JSON
-
Optimization:
- Load base config and bounds
- Genetic algorithm generates candidate configs
- Each candidate → full backtest via Rust engine
- Fitness = multi-objective score from metrics (e.g.,
mdg,sharpe_ratio) - Apply penalties for violated limits
- NSGA-II selection keeps Pareto front, mutates/crosses for next generation
From passivbot_agent_principles.yaml:
- Position side (long/short) →
[position_side, pos_side, pside, PositionSide, PosSide, Pside] - Order side (buy/sell) →
[side, order_side, Side, OrderSide] - Signed quantities:
buy qtyandlong pos_sizeare positive;sell qtyandshort pos_sizeare negative (exception: final exchange payload may needabs(qty)) - EMA spans are floats: Do not round derived spans (e.g.,
span2 = sqrt(span0 * span1)) - Effective min qty/cost: Entries must observe effective min qty; closes observe effective min cost (unless pos size < min qty, then close qty = pos size)
- Never rely on "what happened earlier" unless it can be rederived from exchange/state snapshot on startup
- No local ad-hoc caches that would break on restart (exception: performance-only caches that don't change behavior)
- Minimal time-based heuristics outside natural candle boundaries
- Fail loudly: Prefer exceptions/panics over silent error handling
- Assume upstream is correct: Don't silently compensate for bad data
- Clear, actionable error messages for expected failures
- Exchange fetch methods (
fetch_balance,fetch_positions,fetch_open_orders, etc.) must NOT catch exceptions. Let exceptions propagate to the caller who handles viarestart_bot_on_too_many_errors(). This ensures clean return types (noUnion[list, bool]), preserves exception context, and makes errors impossible to ignore.
- Use structured, leveled logging (
error,warn,info,debug,trace) - Every remote API call must have a debug-level log entry (endpoint, params, timing)
- Logging should be non-intrusive but detailed enough for full replay/audit
- Tests in
tests/cover optimization, backtesting, config handling, candlestick management - Write comprehensive tests for both normal and edge cases
- Include property-based or randomized tests where applicable
- Before committing changes, ensure tests pass
Current branch: refactor/merge-downloader-into-cm
- Merged downloader functionality into CandlestickManager
- Removed legacy order calculation paths (orchestrator-only)
- Removed hedging overlay
configs/template.json: Default config with all parameters documentedapi-keys.json: API credentials (copy fromapi-keys.json.example)- Configs support HJSON (JSON with comments)
- Coin overrides allow per-symbol parameter tweaks
- Suite scenarios enable multi-condition evaluation
Configuration sections form an inheritance hierarchy. When adding new parameters, place them in the section that matches which components need to read them:
| Section | Used By | Purpose |
|---|---|---|
config.live |
Live bot, Backtester, Optimizer | Runtime behavior params shared across all modes (order logic, risk, hedge_mode) |
config.backtest |
Backtester, Optimizer | Simulation-specific params (date ranges, starting_balance, data sources) |
config.optimize |
Optimizer only | Optimization process params (population_size, bounds, fitness settings) |
Rule: When in doubt, prefer config.live. A parameter that works in both live and backtest belongs in config.live, not config.backtest.
- If Rust changes are detected, recompile with
cd passivbot-rust && maturin develop --release && cd .. - Multiple bot instances can share OHLCV cache; CandlestickManager uses self-healing locks
- Backtest results go to
backtests/{exchange}/timestamp/(standalone) orbacktests/suite_runs/(suite mode) - Optimizer results go to
optimize_results/YYYY-MM-DDTHH_MM_SS_{exchanges}_{n_days}days_{coin_label}_{hash}/ - When adding dependencies, explain necessity and impact
- Before committing, simulate/dry-run changes
Maintain CHANGELOG.md as single source of truth for user-facing changes. Add entries under "Unreleased" as changes land; move to dated version heading when tagging releases.