You see the setup forming on the SPY chart. You know exactly where you want to enter, exactly where you're wrong, exactly where you'll take profit β all in SPY dollars, the chart you actually understand.
Then you tab over to your broker. Pick an expiration. Scroll the strike ladder. Compute how many contracts $1,000 buys. Set the stop in option premium dollars (not the SPY level you actually understand). Submit. Watch a market-maker tap your stop on a wick fifteen seconds after the open.
OptionsCanvas fixes both halves of that.
- Your stops live locally. Not on the broker order book. Not visible to the HFTs that profit from hunting them. We only send a market order at the moment the underlying actually breaches your level.
- You never touch the options chain. You drag your entry, SL, and TP directly on the underlying's chart β we pick the strike, the DTE, the contract count, and project the option premium and P&L in real time as you drag.
Two-keystroke execution. Runs 100% on your machine. The first open-source options platform that does this.
options-canvas_silent_720p.mp4
- Trades short-dated options β whether that's 0 DTE on SPY or a month out on AAPL β directly off the underlying chart
- Wants to think in underlying price (the chart you actually read), not in option premium
- Has been stop-hunted one too many times and never wants a resting stop in the broker's book again
- Refuses to click 7 times to get a trade on
- Needs hotkeys, flatten-all, and one-gesture brackets β not modal dialogs
Works on any optionable US equity or ETF your broker supports β index ETFs, single names, leveraged ETFs, whatever. Configure your watchlist and the platform pulls expirations + strike increments straight from the broker.
If that's you, keep reading.
Drag the Buy, SL, and TP pills directly on the underlying chart. No ticket window. No popups. The price line is the control.
- Drag Buy away from live price β flips into a limit-trigger with an anchored
ENTRYline - Drag TP or SL up or down β level updates live, projected premium and P&L update with it
![]() |
![]() |
| Drag SL/TP on the underlying β premium + P&L recompute as you drag. | Drag Buy off live price β becomes a limit-trigger with an anchored ENTRY line. |
Stops and targets do not sit in your broker's order book. They live in your local machine and only become a real market order at the instant the underlying actually breaches your level.
- No resting stop for HFTs to sweep
- No "stop tag + reverse" 15 seconds after the open
- The broker sees a market order after the breach, never before
- You stop providing free liquidity to the venue
You set SL @ 449.50
β
βΌ
βββββββββββββββββββββββββ
β trading_engine β β stop lives here, in memory + SQLite
β holds SL locally β (broker never sees it)
βββββββββββββ¬ββββββββββββ
β
β polls underlying quote
βΌ
underlying β€ 449.50 ?
β
β yes
βΌ
βββββββββββββββββββββββββ
β market SELL fires β β broker (first time the
β to broker β order book sees anything)
βββββββββββββββββββββββββ
You set levels on the underlying chart (e.g. your ticker at 449.50). OptionsCanvas does the contract math.
- Auto-picks the contract β DTE from your configured presets (0 DTE, weeklies, monthlies β whatever you list), ATM strike from the live chain, both pulled fresh from the broker
- Black-Scholes overlay projects the option premium and your P&L while you drag, so you see "if the underlying hits 451, this call is worth $2.40 and you make $640"
- Strike increments and expirations come from the broker as source of truth β no manual contract config, no stale chains
One screenshot, the whole pitch: SL and TP labeled in underlying dollars (not option premium). One-tap Smart button auto-sets stops from ATR. The contract SPY260526C00746000, the 3-contract size, and the $823.50 cost β all computed for you. Hit the big green button, you're in. Hit "Close All Positions", you're flat.
Built so a trade is two keystrokes, not seven clicks.
Bβ Buy CALL at market (ATM, auto-sized)Sβ Buy PUT at marketFβ Flatten all positions, instantlyShift+Bβ Bracket draw mode: click entry, drag to TP, release β doneCβ Toggle CALL/PUT β’β/ββ strike up/down1β5β preset position sizes ($500 / $1k / $2k / $5k / $10k)Alt + 1..6β 1m / 5m / 15m / 30m / 1h / 1d timeframeAlt + β/ββ previous/next symbol β’?β cheatsheet
Hit Shift+B, click entry, drag to where you want profit. Direction of drag picks CALL vs PUT. Default 2:1 R:R is auto-applied. Release to send. That's the whole interaction.
- ATR-based SL (1.5Γ ATR-14) reconciled against swing high/low lookback β picks the more conservative
- 2:1 R:R TP by default, override per trade
- Smart sizing presets so you stop fat-fingering 100 contracts at 3:59pm
TradingView Lightweight Charts under the hood. Multi-symbol, multi-timeframe.
- Indicators: VWAP, SMA, EMA, RSI, Volume β toggle per chart
- Drawing tools: trend lines, rectangles, horizontals
- Dark theme, 60fps, no garbage
- Right-click context menu with everything one click away
(The hero image at the top of this page is exactly this β chart, indicators, draggable Buy/SL/TP pills, side panel, broker pill, Day P&L β all rendered live.)
- Runs 100% on your machine β Flask on
127.0.0.1:5001 - Your keys live in
config/config.json, your state inassisted_trading/state/trading.db - Zero telemetry. The only outbound traffic is to your broker.
- No SaaS, no login, no "free tier", no one watching your levels
- Per-symbol position caps and max simultaneous positions
- Configurable trading hours (auto-blocks orders outside the window)
- Auto-sell on SL breach (toggle off if you prefer manual)
- Daily trade limits and accept-partial-fill behavior, all in JSON config
Stop maintaining your trading journal in a spreadsheet. OptionsCanvas writes every fill, every close, every realized P&L to your local SQLite DB as it happens.
- Per-trade row β option symbol, contracts closed, exit price, exit time, realized P&L
- Daily aggregate β total trades, win/loss count, gross profit/loss, net P&L, largest win/loss
- Plain SQLite at
assisted_trading/state/trading.dbβ query withpandas.read_sql, DuckDB, Datasette, Jupyter, whatever you already use for analysis - JSON snapshots under
assisted_trading/journal/<date>/trades.jsonfor human-readable review - REST endpoint
GET /api/journal?start_date=...&end_date=...for building your own dashboards - It's your data, on your disk β nothing leaves the machine
Currently two brokers ship out of the box:
- Alpaca β production-tested. The reference implementation used during development.
- Tradier β implementation complete but not yet validated end-to-end against a live Tradier sandbox account. Working through that now β see Broker support status below.
The broker layer is an abstract interface (broker_interface.py) with a declarative registry (broker_registry.py) β adding IBKR, Tastytrade etc. is one new file in backend/ and one entry in the registry. The wizard UI renders dynamically from the registry, so a new broker shows up without any frontend edits.
Prereqs: Python 3.10+ (or Docker) and a free paper-trading account from a supported broker β Alpaca (recommended) or Tradier (experimental β see Broker support status). Both take ~2 minutes to create and don't risk real money.
-
Download β grab the latest ZIP (or
git clone) and unzip it. -
Launch β pick whichever is easiest:
- Windows: double-click
OptionsCanvas.bat - macOS / Linux:
./optionscanvas.sh(first time:chmod +x optionscanvas.sh) - Docker (any OS):
docker compose up -dβ see Docker below
On the first native run it creates a Python venv and installs deps (~2 min). Subsequent launches are instant.
- Windows: double-click
-
Follow the wizard β your browser opens to the setup wizard:
- Pick a broker (Alpaca recommended; Tradier supported but not yet end-to-end tested) β paste keys β "Test connection"
- Pick a trading universe: Recommended 30 (default), Full 110, or your own custom watchlist
- Click "Start trading"
That's it. The trading UI takes over and you're live on paper money.
The wizard also runs idempotently β you can revisit http://localhost:5001/setup later to re-onboard symbols or rotate keys.
Docker
Single command, any OS:
docker compose up -d # build + start
# open http://localhost:5001/setup
docker compose logs -f # tail logs
docker compose down # stopconfig/ and assisted_trading/state/ are bind-mounted, so your broker keys and trading DB live in the repo dir and survive container rebuilds.
Power-user / manual setup
If you'd rather skip the launcher and do it by hand:
git clone https://github.com/calesthio/OptionsCanvas.git
cd OptionsCanvas
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
python assisted_trading/run_platform.py # opens browser to /setup wizardOr skip the wizard entirely (Alpaca-only path β Tradier users should use the wizard):
cp config/config.example.json config/config.json # then paste your Alpaca keys
python scripts/onboard_symbol.py # default: 30 Tier-1 names
python scripts/onboard_symbol.py --all # full 110-name universe
python scripts/onboard_symbol.py AAPL MSFT NVDA # custom tickers
python assisted_trading/run_platform.pyThe Tier-1 default covers SPY, QQQ, NVDA, TSLA, IWM, the Mag-7, top semis (AMD, MU, INTC, AVGO, SOXL/SOXS), high-flow retail names (PLTR, HOOD, MSTR, COIN, F, AAL), sector ETFs (XLE, XLF), and leveraged QQQ (TQQQ/SQQQ). --all adds 80 more across financials, pharma, energy, consumer, China, vol, and bonds.
Optional β browser tests (Playwright):
npm install && npx playwright install chromium| Key | Action |
|---|---|
B / S |
Buy CALL / Buy PUT at market |
F |
Flatten all positions |
Shift+B |
Bracket-draw mode (click entry, drag to TP) |
C |
Toggle CALL β PUT |
β / β |
Strike up / down |
1β5 |
Position size: $500 / $1k / $2k / $5k / $10k |
Alt + 1..6 |
1m / 5m / 15m / 30m / 1h / 1d |
Alt + β / β |
Previous / next symbol |
D / T |
Draw horizontal / trend line |
Esc |
Cancel drawing / bracket mode |
? |
Show full shortcuts modal |
| Broker | Status | Notes |
|---|---|---|
| Alpaca | β Production-tested | The reference broker used during development. All flows (validate, chart data, option chains, place/cancel orders, positions, server-side stops) exercised continuously against a paper account. |
| Tradier | π§ͺ Implementation complete, end-to-end testing in progress | All BrokerInterface methods implemented against Tradier's documented REST API. Verified against the sandbox endpoint at a unit level (auth, error handling, response parsing) but not yet validated through a full sandbox trading session. Use at your own risk until this row turns green; please file issues if you see anything off. |
Adding a new broker requires one file in assisted_trading/backend/<name>_broker.py (implementing BrokerInterface) and one entry in broker_registry.py β no frontend or trading-engine edits. Contributions welcome.
config/config.jsonβ broker credentials + paper/live mode + global defaults (gitignored, never commit). Written by the setup wizard; you almost never need to edit it by hand.assisted_trading/config/assisted_trading_config.jsonβ enabled symbols, DTE presets, position-size presets, max positions, trading hours, auto-sell, entry-order type- Broker accounts β free paper accounts at Alpaca or Tradier. Paper and live use separate API keys on both brokers β generate the right pair from each broker's API settings page.
- Frontend (vanilla JS) β
ChartManagerowns Lightweight Charts.OrderPanelOnChartrenders draggable pills.ChartTradingControllerbridges chart β side panel.BlackScholesCalculatorprojects premium + P&L.BracketOrderDrawerhandles one-gesture brackets.KeyboardShortcutManagerfor hotkeys.SmartDefaultsfor ATR-based SL/TP. The setup wizard (setup.html) renders dynamically from/api/setup/brokers. - Backend (Flask + SocketIO) β
chart_api_server.pyexposes REST + WS.trading_engine.pyruns the loop: entries, fill monitoring, SL/TP breach checks, with a TTL cache + graceful fallback on transient broker errors.order_manager.py+position_manager_v2.pypersist to SQLite.state_machine.pyenforces invariants.setup_routes.pypowers the first-run wizard. - Broker layer β
broker_interface.py(abstract base) +broker_registry.py(declarative metadata, drives wizard UI) +broker_factory.py(builds instances) +alpaca_broker.pyandtradier_broker.py(concrete impls). Add a broker: drop one file inbackend/, add one entry in the registry. - State β SQLite at
assisted_trading/state/trading.db, auto-migrates on boot.
More: docs/ARCHITECTURE.md.
pip install -r requirements-test.txt # one-time
pytest -q # ~190 unit + property + integration tests
python assisted_trading/run_platform.py & # then, in another shell:
npx playwright test tests/browser/chart_trading.spec.jsScreenshots in this README are captured by tests/browser/_screenshots.spec.js against the live app.
PRs welcome β see CONTRIBUTING.md. Keep changes focused, run the suites, respect the broker abstraction and state machine.
A star on the repo genuinely helps β it's how other options traders find it. Solo dev project with no marketing budget, so word-of-mouth is all there is. If you end up using OptionsCanvas day-to-day, opening an issue with what's working / not working is even more valuable than a star.
AGPL-3.0-or-later. Β© 2026 calesthio. See LICENSE.
Plain English: run a modified version as a network service β you must share the modified source. Fork it privately for your own trading β you're fine.
Third-party components and their licenses (TradingView Lightweight Chartsβ’, Alpaca-py, Flask, Socket.IO, etc.) are catalogued in THIRD_PARTY_NOTICES.md.
- Paper trading is the default β the setup wizard's mode selector defaults to "Paper" on every supported broker. Live trading is supported (switch the toggle to "Live" in the wizard, or edit
config/config.json'sbroker.mode), but the launcher, server, and trading UI all surface loud warnings (terminal banner, server log, red broker pill, app-wide red border,[LIVE]browser-tab prefix) so you can't end up there by accident. - Options trading involves substantial risk of loss. You can lose 100% of premium; in some configurations more.
- Not financial advice. A tool, not a recommendation. Indicators, projected P&L, defaults β none of it is advice.
- No warranty. Authors and contributors are not liable for losses, missed fills, slippage, broker outages, software bugs, or stops that fail to trigger β paper or live.
- If you don't understand exactly what an order will do before you place it β don't place it.



