A lightweight command-line tool for executing stock trades through the E*TRADE API via a simple CSV file. Authenticate once, review your orders, confirm, and execute — SELLs always run before BUYs.
- ✅ OAuth 1.0a authentication (no browser automation required)
- ✅ Token persistence — PIN entry only required once per trading day
- ✅ CSV-driven trade input — bring your own trade list
- ✅ Order preview before execution
- ✅ Automatic SELL-before-BUY ordering
- ✅ Sandbox and production environment support
- ✅ Multi-account selection
- Python 3.10+
- Windows (credential storage uses Windows Credential Manager)
- E*TRADE developer account — register here
- Microsoft C++ Build Tools (required by the
ecosdependency)
git clone https://github.com/MattVAllen/etrade-api-trader.git
cd etrade-api-trader
python -m venv venv
venv\Scripts\activate
pip install -r requirements.txtCreate an application at the E*TRADE Developer Portal to receive a consumer_key and consumer_secret. E*TRADE provides separate credentials for sandbox and production — store them separately.
Run this once in Python (in your activated virtual environment) to save your credentials to Windows Credential Manager.
Sandbox:
import keyring, json
creds = {"consumer_key": "YOUR_SANDBOX_KEY", "consumer_secret": "YOUR_SANDBOX_SECRET"}
keyring.set_password("etrade_sandbox", "credentials", json.dumps(creds))Production:
import keyring, json
creds = {"consumer_key": "YOUR_PROD_KEY", "consumer_secret": "YOUR_PROD_SECRET"}
keyring.set_password("etrade_prod", "credentials", json.dumps(creds))To update credentials later, run the same snippet with your new values — it overwrites the existing entry.
python run_trades.py trades.csvpython run_trades.py trades.csv --env prod- On the first run of each trading day, a URL is printed in the terminal — open it in your browser, log in to E*TRADE, approve access, and paste the PIN back into the terminal (~30 seconds)
- Subsequent runs the same day reuse saved tokens automatically — no PIN needed
- Your accounts are listed — enter the number to select which account to trade in
- A trade summary table is displayed for review
- Type
yesto execute — each order is previewed then placed - A final success/failure count is reported
All trades are driven by a CSV file. A Trades.template.csv is included in the repo — copy it and fill in your trades:
copy Trades.template.csv my_trades.csvOnly three columns are required — the rest are optional and will fall back to the defaults shown below.
Symbol,Action,Quantity
AAPL,SELL,10
MSFT,BUY,5
NVDA,BUY,8If a column is omitted or left blank, the following defaults apply:
| Column | Default | Options |
|---|---|---|
| PriceType | MARKET |
MARKET, LIMIT, STOP, STOP_LIMIT, MARKET_ON_CLOSE |
| LimitPrice | — | Any decimal (required if PriceType is LIMIT) |
| StopPrice | — | Any decimal (required if PriceType is STOP or STOP_LIMIT) |
| OrderTerm | GOOD_FOR_DAY |
GOOD_FOR_DAY, GOOD_UNTIL_CANCEL, IMMEDIATE_OR_CANCEL, FILL_OR_KILL |
| MarketSession | REGULAR |
REGULAR, EXTENDED |
| AllOrNone | FALSE |
TRUE, FALSE |
| RoutingDestination | AUTO |
AUTO, ARCA, NSDQ, NYSE |
Symbol,Action,Quantity,PriceType,LimitPrice,StopPrice,OrderTerm,MarketSession,AllOrNone,RoutingDestination
AAPL,SELL,10,LIMIT,185.00,,GOOD_UNTIL_CANCEL,REGULAR,FALSE,AUTO
MSFT,BUY,5,MARKET,,,GOOD_FOR_DAY,REGULAR,FALSE,AUTONotes:
- Column names with or without spaces are both accepted (
Price TypeorPriceType) - The order of rows in the CSV does not matter — SELLs always execute before BUYs regardless
This tool uses the OAuth 1.0a PIN-based flow required by E*TRADE.
| Scenario | Behavior |
|---|---|
| First run of the trading day | PIN flow — ~30 seconds |
| Subsequent runs same day | Silent token renewal — no action needed |
| After midnight Eastern time | Tokens expire — PIN flow required again |
Tokens are saved locally at:
- Sandbox:
~/.etrade/tokens.sandbox.json - Production:
~/.etrade/tokens.prod.json
If you're building on top of this or troubleshooting auth issues, these are the non-obvious implementation details:
callback_uri="oob"must be passed in the OAuth header — not as a URL query parameter- All three token endpoints (
request_token,access_token,renew) always usehttps://api.etrade.com, even in sandbox mode - Only data calls (accounts, orders) use
https://apisb.etrade.comfor sandbox - E*TRADE returns URL-encoded tokens — always decode with
urllib.parse.unquote()before use
etrade-api-trader/
├── auth/
│ ├── __init__.py
│ └── oauth_etrade.py # OAuth 1.0a authentication module
├── tests/
│ ├── test_oauth.py # Auth test (no trades placed)
│ └── test_trade.py # Trade execution test (sandbox only)
├── utils/
│ └── csv_reader.py # CSV trade reader
├── run_trades.py # Main execution script
├── requirements.txt
└── README.md
This project uses pyetrade (v2.1.1) as the E*TRADE API wrapper. Note that pyetrade is currently in maintenance-only status — it is functional but not actively developed.
Backlog and known issues live on GitHub: https://github.com/MattVAllen/etrade-api-trader/issues
This tool places real stock orders. Always test in sandbox before running in production. You are solely responsible for any trades executed. This is not financial advice.