Keep retirement accounts aligned with your target allocation—safely and automatically.
Balancer connects to your E*TRADE accounts, computes trades to move toward your target ETF allocation, and (optionally) executes them.
I wanted a simple and free portfolio rebalancer for retirement accounts without paying roboadvisor fees or accepting quarterly drift. I also needed a project to teach myself Python 🐍. I've been using versions of this for the last few years but am finally getting around to publishing for anyone to use.
- 🔒 Safety-first:
preview→confirm→auto, plus a global kill switch. - 🧮 Configurable: set your preferences for ETFs, target allocations, rebalance frequency, etc.
- 🔁 Loop mode: run on a schedule for hands-off upkeep.
- 🧰 Designed for non-taxable accounts (401k/IRA). Taxable version (with tax-loss harvesting + multi-ticker diversity) is on the roadmap.
DISCLAIMERS: Personal finance software, not investment advice. Use at your own risk. Always start in
previewmode and test in the E*TRADE sandbox before live trading.
- Create a developer account + app on the E*TRADE developer portal: https://developer.etrade.com/home
- Obtain consumer key & secret: https://us.etrade.com/etx/ris/apikey
- Enable sandbox first.
git clone https://github.com/JonnyAis/balancer.git
cd balancer
python -m venv .venv
# macOS / Linux
source .venv/bin/activate
# Windows PowerShell
# .\.venv\Scripts\Activate.ps1
pip install -r requirements.txtCreate a .env file (copy from template):
cp .env.example .env
# Edit the newly created .env file with your E*TRADE developer key and secret.Edit balancer/config.yaml (targets, optimizer, trading mode, filters).
See inline comments in balancer/config.yaml for every field. Key sections include:
- classes — target % and representative ETF per class
- rebalance — heuristic thresholds and integer optimizer parameters
- accounts — include/exclude filters by account description
- trading — behavior (mode, caps, etc.)
- loop — interval and toggles for run_loop.py
Example excerpt:
classes:
Large: { target_percent: 40, preferred_symbol: VOO }
Small: { target_percent: 12, preferred_symbol: VBR }
International: { target_percent: 28, preferred_symbol: IXUS }
Bonds: { target_percent: 20, preferred_symbol: BND }
trading:
mode: preview # preview | confirm | autopython -m balancer.mainSet trading.mode: confirm, rerun, answer y.
Set trading.mode: auto (use cautiously, especially if enabling loop mode).
The balancer/config.yaml file controls all behavior. Key sections:
| Section | Purpose |
|---|---|
| classes | Target % allocation + single ETF per asset class |
| rebalance | Heuristic thresholds + integer optimizer settings |
| loop | Optional periodic execution |
| accounts | Include/exclude by description |
| trading | Execution behavior & safety |
Each asset class needs a target percentage and preferred ETF symbol:
classes:
Large: { target_percent: 40, preferred_symbol: VOO }
International: { target_percent: 28, preferred_symbol: IXUS }
Bonds: { target_percent: 20, preferred_symbol: BND }
Small: { target_percent: 12, preferred_symbol: VBR }Target percentages must sum to 100.
min_percent_drift: Ignore small % drifts from target allocationmin_notional_trade: Skip trades smaller than this dollar amountmax_notional_trade: Cap individual trade size (splits large trades)account_min_turnover: Skip rebalancing if total turnover below thresholdinteger_optimizer: Controls optimizer window and turnover penalty
- Fetch positions, quotes, and cash balances
- Optimize using integer optimization (whole-share allocation)
- Print the rebalancing plan (per-class allocation table is always shown)
- If in confirm mode, prompt user for approval
- Execute sells, refresh cash, then execute buys (scaling if enabled)
- No unfunded buys are submitted
The rebalancer uses integer optimization to find the best whole-share allocation that minimizes:
Σ (allocated_value_c - target_value_c)² + turnover_penalty × Σ |Δshares_c|
Configure in config.yaml:
rebalance:
integer_optimizer:
window: 4 # Share search radius (complexity scales exponentially)
turnover_penalty: 0.0 # Penalty for changing positions (reduces churn)- Open a terminal in VS Code.
- Activate your virtual environment:
.\.venv\Scripts\Activate.ps1
- Run the program:
(Use
python run.py prod preview
confirmorautofor other modes.)
Optional (one-liner for PowerShell):
.\.venv\Scripts\Activate.ps1; python run.py prod confirm