Mercurial
comparison schwab_trader/README.md @ 220:eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Sun, 02 Aug 2026 08:52:13 -0700 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 214:4c725fde6999 | 220:eb8b4230fdb9 |
|---|---|
| 1 # Schwab Trader | |
| 2 | |
| 3 Small Bazel-built helper for the Schwab Trader API. This project is only plumbing: it helps authenticate, inspect accounts, build explicit user-specified stock orders, and submit them only when a live-trade confirmation flag is present. | |
| 4 | |
| 5 It does not recommend trades, choose symbols, allocate portfolio risk, or automate a strategy. | |
| 6 | |
| 7 ## Can Schwab accounts be traded by API? | |
| 8 | |
| 9 Yes. Schwab provides the Trader API through the Schwab Developer Portal. The flow is OAuth 2.0: | |
| 10 | |
| 11 1. Create an app in the Schwab Developer Portal. | |
| 12 2. Get an app key and app secret. | |
| 13 3. Register a redirect URI. | |
| 14 4. Open the OAuth authorization URL and log in through Schwab. | |
| 15 5. Exchange the returned `code` for an access token and refresh token. | |
| 16 6. Use the access token against `https://api.schwabapi.com/trader/v1`. | |
| 17 | |
| 18 Do not use username/password scraping or browser automation. The supported path is OAuth tokens. | |
| 19 | |
| 20 Useful endpoints this project targets: | |
| 21 | |
| 22 - `GET https://api.schwabapi.com/trader/v1/accounts/accountNumbers` | |
| 23 - `GET https://api.schwabapi.com/trader/v1/accounts` | |
| 24 - `GET https://api.schwabapi.com/trader/v1/accounts/{accountHash}` | |
| 25 - `POST https://api.schwabapi.com/trader/v1/accounts/{accountHash}/orders` | |
| 26 | |
| 27 Schwab uses account hashes for trading API calls. Fetch them with `account-numbers` before placing any order. | |
| 28 | |
| 29 ## Build and test | |
| 30 | |
| 31 From the repo root: | |
| 32 | |
| 33 ```bash | |
| 34 bazel build //schwab_trader:schwab_cli | |
| 35 bazel test //schwab_trader:schwab_client_test | |
| 36 bazel build //schwab_trader:schwab_dashboard | |
| 37 bazel test //schwab_trader:dashboard_test | |
| 38 ``` | |
| 39 | |
| 40 ## Configuration | |
| 41 | |
| 42 Set these environment variables: | |
| 43 | |
| 44 ```bash | |
| 45 export SCHWAB_APP_KEY="your-schwab-app-key" | |
| 46 export SCHWAB_APP_SECRET="your-schwab-app-secret" | |
| 47 export SCHWAB_REDIRECT_URI="https://127.0.0.1" | |
| 48 export SCHWAB_TOKEN_FILE="$HOME/.config/zenbu/schwab_tokens.json" | |
| 49 ``` | |
| 50 | |
| 51 `SCHWAB_TOKEN_FILE` is optional and defaults to `~/.config/zenbu/schwab_tokens.json`. Token files are written with `0600` permissions. | |
| 52 | |
| 53 ## OAuth bootstrap | |
| 54 | |
| 55 Print the Schwab login URL: | |
| 56 | |
| 57 ```bash | |
| 58 bazel run //schwab_trader:schwab_cli -- auth-url | |
| 59 ``` | |
| 60 | |
| 61 Open it, log in through Schwab, authorize the app, then copy the full callback URL or just its `code` parameter: | |
| 62 | |
| 63 ```bash | |
| 64 bazel run //schwab_trader:schwab_cli -- token --code 'https://127.0.0.1/?code=...' | |
| 65 ``` | |
| 66 | |
| 67 Refresh later: | |
| 68 | |
| 69 ```bash | |
| 70 bazel run //schwab_trader:schwab_cli -- refresh | |
| 71 ``` | |
| 72 | |
| 73 ## Account discovery | |
| 74 | |
| 75 ```bash | |
| 76 bazel run //schwab_trader:schwab_cli -- account-numbers | |
| 77 bazel run //schwab_trader:schwab_cli -- accounts --positions | |
| 78 ``` | |
| 79 | |
| 80 ## Order dry run | |
| 81 | |
| 82 Build a stock order payload without sending it: | |
| 83 | |
| 84 ```bash | |
| 85 bazel run //schwab_trader:schwab_cli -- build-equity-order \ | |
| 86 --action BUY \ | |
| 87 --symbol AAPL \ | |
| 88 --quantity 1 \ | |
| 89 --order-type MARKET | |
| 90 ``` | |
| 91 | |
| 92 `place-equity-order` is also dry-run by default: | |
| 93 | |
| 94 ```bash | |
| 95 bazel run //schwab_trader:schwab_cli -- place-equity-order \ | |
| 96 --account-hash "$SCHWAB_ACCOUNT_HASH" \ | |
| 97 --action SELL \ | |
| 98 --symbol AAPL \ | |
| 99 --quantity 1 \ | |
| 100 --order-type LIMIT \ | |
| 101 --price 250.00 | |
| 102 ``` | |
| 103 | |
| 104 To actually submit an order, both safety flags are required: | |
| 105 | |
| 106 ```bash | |
| 107 bazel run //schwab_trader:schwab_cli -- place-equity-order \ | |
| 108 --account-hash "$SCHWAB_ACCOUNT_HASH" \ | |
| 109 --action BUY \ | |
| 110 --symbol AAPL \ | |
| 111 --quantity 1 \ | |
| 112 --order-type MARKET \ | |
| 113 --live \ | |
| 114 --confirm-live-trade | |
| 115 ``` | |
| 116 | |
| 117 Use live trading only after checking the generated JSON, account hash, symbol, quantity, order type, and Schwab API permissions. | |
| 118 | |
| 119 ## Local sentiment dashboard | |
| 120 | |
| 121 Run the local dashboard: | |
| 122 | |
| 123 ```bash | |
| 124 bazel run //schwab_trader:schwab_dashboard | |
| 125 ``` | |
| 126 | |
| 127 Then open: | |
| 128 | |
| 129 ```text | |
| 130 http://127.0.0.1:8765 | |
| 131 ``` | |
| 132 | |
| 133 The dashboard is intentionally local-first and read/paper-trade oriented. It has no live-trade endpoint. It shows: | |
| 134 | |
| 135 - Schwab environment/token status without exposing token values | |
| 136 - risk settings such as profit target, stop loss, confidence threshold, and max paper-trade dollars | |
| 137 - manually added social evidence from Reddit/X/news/etc. | |
| 138 - deterministic sentiment signals and confidence | |
| 139 - paper trades with simple risk rejection | |
| 140 - audit events | |
| 141 | |
| 142 The dashboard stores state in: | |
| 143 | |
| 144 ```text | |
| 145 ~/.local/share/zenbu/schwab_trader/dashboard.db | |
| 146 ``` | |
| 147 | |
| 148 Override it when testing: | |
| 149 | |
| 150 ```bash | |
| 151 bazel run //schwab_trader:schwab_dashboard -- --db /tmp/schwab_dashboard.db | |
| 152 ``` | |
| 153 | |
| 154 Social evidence can be added through the page or API: | |
| 155 | |
| 156 ```bash | |
| 157 curl -X POST http://127.0.0.1:8765/api/evidence \ | |
| 158 -H 'Content-Type: application/json' \ | |
| 159 -d '{"source":"reddit","symbol":"AAPL","text":"$AAPL bullish strong growth","engagement":42}' | |
| 160 ``` |