A free, open-source day-trading platform — read the tape, work the ladder, fire orders from hotkeys.
Real-time charts · Level 2 DOM ladder · Time & Sales · Pre-market scanner · Multi-broker execution
eTape is a trading platform built the way scalpers and momentum day traders actually work: one screen with fast charts, a live order book, the tape, and one-keystroke order entry — running entirely on your own machine. No subscription, no cloud middleman, no per-month platform fee. Bring a moomoo account for market data and the broker of your choice for execution, and everything else is free and open source.
- Speed as a design rule. The engine is pure Go; the chart, ladder, and tape are canvas surfaces painted imperatively and coalesced to one repaint per frame. High-frequency market data never touches React state.
- 10-second candles, built live from raw ticks. Sub-minute momentum most retail platforms simply can't show — bucketed by exchange timestamp with live buy/sell direction. Completed candle ranges reconcile safely to OpenD's finalized one-minute envelope when its tick and K-line feeds disagree.
- A real Level 2 DOM ladder. Full-depth order book rendered on canvas, fed by moomoo's tick-and-depth feed — not a 1-level quote widget.
- A clearly labelled local LULD aid. During regular hours, supported symbols
may show display-only
LULDBoundary Rows built from the local eligible-print feed. They are never an official band, halt signal, order control, or risk control; unsupported/expired symbols remain unavailable. - Broker-agnostic execution. The same order ticket, hotkeys, and risk gates drive TradeZero, Alpaca, or the built-in simulator. Fills come back as generic events and land on your chart as markers in real time, whatever the venue.
- Ticketless hotkeys. The order ticket is optional: hotkeys use the most recently user-activated grouped Dockview panel across open windows. The target is ephemeral, never saved in a workspace or restored across a full restart, and its read-only cue shows the linked symbol and resolved venue.
- Chart Risk Entry. A preset hotkey starts a two-point buy/stop tool on the active grouped chart. Dollar, Cash %, or BP % loss budgets size linked engine-held stop-limit orders; both prices can be adjusted from the chart. Optional auto-send accepts the pair on placement or a completed draft drag; missing recent trades leave it waiting in the engine for a fresh trigger.
- Safety-gated by default. Zero venues are configured out of the box. Every order must pass a two-layer risk gate (global caps + per-venue caps: max day loss, order value, position size, open orders), and the eTape master execution switch must be explicitly armed.
- A live demo that actually feels live. No account, no setup: a synthetic market with a warm year of history, a breathing DOM, and a moving scanner board, streaming indefinitely — not a fixed prerecorded session. The universe (which symbols run, which gap, which crash) reshuffles every launch, so spotting the mover is part of the practice, not something you memorize.
- Local-first and private. Config, credentials, and local state live in
~/.eTape/on your disk. The UI is served from127.0.0.1. Your API keys talk to your broker and no one else.
Charting
- TradingView Lightweight Charts candlesticks, from 10-second bars up to daily/weekly/monthly
- Indicators: VWAP, EMA, SMA, MACD, Volume
- Drawing tools — horizontal line, trend line, extended line, rectangle — with per-tool color/width/style memory
- Live fill markers (buy/sell diamonds) and fill sounds
- Extended-hours (pre-market / after-hours) data support
Order flow
- Level 2 DOM ladder with full order-book depth
- Time & Sales tape with buy/sell coloring, virtualized over a ring buffer
Scanning & context
- Session-aware gap scanner with float, daily Vol, Session Volume, Dollar Turnover, REL VOL (Daily Rate), and inclusive Last-price bounds using the session-aware displayed price (zero disables, unknown/non-positive values fail active bounds, and admissions stay sticky), plus %-change filters, Reported Short Interest context, and per-Scanner column visibility/order
- Independent Scanner Panels with per-panel filters, sticky boards, sorting, seen state and sound; one shared quota-controlled service supplies discovery. Session Volume finds stocks outside the mover lists from the provider's top 200 current-session-volume candidates; rows show discovery freshness.
- Session-aware scanner (gainers, losers, and most active)
- Stock Info panel: fundamentals grid plus a de-duplicated live news feed with publish times and type badges; headlines open in an unmaximized reusable reader
Execution
- Order ticket with market / limit / stop / stop-limit / limit-if-touched (LIT)
- Hotkey deck: configurable one-keystroke order templates with price offsets and position sizing by buying-power % or position %
- Account, positions, open/closed orders, and trade-history panels
- Built-in paper simulator with realistic fills: orders walk the live book, partial fills, resting limit orders, configurable slippage and fill latency
Workspace
- Dockable, drag-and-drop panel layout (dockview) — arrange chart/ladder/tape/scanner however you trade, with linked symbol groups
- Type a ticker anywhere to load a symbol — the engine subscribes on demand
- In-app settings for venues, credentials, hotkeys, and appearance
flowchart TB
MOOSRV(["moomoo servers"])
subgraph MACHINE["Your machine — everything runs locally"]
OPEND["moomoo OpenD — local gateway"]
ENGINE["eTape engine — Go<br/>order books · bars · indicators · scanner<br/>SQLite journal · risk gate<br/>broker adapters · built-in simulator"]
UI["eTape UI — React + TypeScript<br/>chart · L2 DOM ladder · time & sales<br/>order ticket · hotkeys · panels"]
end
BROKERS(["broker APIs<br/>Alpaca · TradeZero"])
MOOSRV --> OPEND
OPEND -->|"quotes · ticks · depth"| ENGINE
ENGINE <-->|"orders · fills (moomoo)"| OPEND
ENGINE <-->|"WebSocket + JSON<br/>127.0.0.1:8686"| UI
ENGINE <-->|"orders · fills"| BROKERS
The engine speaks OpenD's wire protocol natively in Go (no Python SDK required), builds books/bars/indicators, persists bars and execution state to SQLite, and serves the UI over a localhost WebSocket. TypeScript types for the wire contract are generated from the Go structs, so the two sides can't silently drift.
⬇ Download the latest release — a prebuilt, self-contained binary with the UI embedded. Single file, no installer, no Go or Node toolchain required.
| Platform | Artifact | Run it |
|---|---|---|
| Windows (x64) | eTape-<version>-windows-amd64.zip |
Unzip and run etape.exe. Click "Try demo" in the app to start with a synthetic market (no setup needed) — or configure OpenD + venues in settings for live mode. README-FIRST.txt inside covers the details. |
| macOS (Apple Silicon) | eTape-<version>-macos-arm64.tar.gz |
tar xzf it, then run ./etape-darwin-arm64. Click "Try demo" in the app for a synthetic market, or configure settings for live mode. (Developers: ./etape-darwin-arm64 -demo is equivalent.) |
| Linux (Ubuntu 24.04 or 26.04 LTS, x64) | eTape-<version>-linux-amd64.tar.gz |
tar xzf it, then run ./etape-linux-amd64 in a terminal. It opens the default browser; if that fails, open http://127.0.0.1:8686 manually. Press Ctrl+C to stop it. Use ./etape-linux-amd64 -demo for a session without OpenD; live mode needs OpenD installed and configured separately. |
The Linux archive is portable and does not install a desktop launcher or OpenD.
To upgrade, stop eTape with Ctrl+C, replace the extracted executable, and start
it again; leave ~/.eTape/ in place so settings and local state remain intact.
The binaries aren't code-signed (personal-use release, no certificate), so expect a
one-time warning on first launch: Windows SmartScreen says "unrecognized app" —
click More info → Run anyway; macOS Gatekeeper blocks it — right-click →
Open (or xattr -d com.apple.quarantine etape-darwin-arm64).
Prefer building from source? The quick start below has you covered.
Demo mode, no accounts needed. Prerequisites: Go ≥ 1.26 and
Node.js 22 LTS on your PATH.
git clone https://github.com/earlisreal/eTape.git
cd eTape
./run.sh demo # Windows: run.cmd demoThis builds the UI, boots a live synthetic market — no OpenD, no broker, no
config — and opens the full app at http://127.0.0.1:8686 with a funded paper
simulator: charts warm with a year of history at every timeframe, the DOM ladder
breathes, the scanner updates as the (fictional) low-float names run, and
hotkeys are live. Place trades immediately; fills price against the live book. The
universe reshuffles every launch — pin it to a specific seed for a reproducible
session:
./run.sh demo 42 # Windows: run.cmd demo 42eTape's market data comes from moomoo OpenD, the local gateway that ships with moomoo's OpenAPI program. One-time setup:
-
Create a moomoo account at moomoo.com and enable OpenAPI access.
-
Download and install OpenD for your OS from the OpenAPI portal.
-
Launch OpenD and log in with your moomoo credentials. By default it listens on
127.0.0.1:11111, which is where eTape expects it. -
Run eTape in live mode:
./run.sh live # Windows: run.cmd live
Then just type a ticker in any panel — the engine subscribes on demand, and the scanner keeps the day's leading symbols warm automatically.
Quote entitlements (check what your moomoo account includes for US stocks):
| Your entitlement | What works |
|---|---|
| Level 1 quotes | Charts, time & sales, scanner, news — everything except book depth |
| Level 2+ depth | All of the above plus the full DOM ladder |
Notes:
- eTape only ever reads market data from OpenD. It never sends trade commands to it and never touches your moomoo trade password.
- The DOM's optional
LULDBoundary Rows are a local approximation, not an official SIP band or trading-pause indicator. They warm from eligible prints, freeze on provider/transport interruptions, and never change order or risk behavior. - US stocks only for now — one market keeps sessions, timezones, and entitlements simple.
| Venue | Environments | Status |
|---|---|---|
Built-in simulator (sim) |
paper | ✅ Realistic fills: book-walk pricing, partials, slippage & latency models |
| Alpaca | paper + live | ✅ Fully supported (REST + streaming) |
| TradeZero | live | ✅ Fully supported (REST + WebSocket) |
| moomoo | paper + live | ✅ Fully supported (native OpenD trade connection) |
Execution is off by default — with no venues configured, every order is blocked.
The easiest way to add one is in-app: Settings → Venues lets you add a venue,
enter API credentials, and test the connection; it writes the config for you (with an
automatic backup of your previous config.toml).
Credentials are stored locally in ~/.eTape/credentials.json and are only ever sent
to the broker they belong to. moomoo is the exception — it has no API key/secret at
all; it authenticates over the same local OpenD connection as market data, keyed by
account ID, and trade unlock happens once per OpenD restart in the OpenD GUI itself
(never inside eTape).
Before any order reaches a broker it must pass the two-layer risk gate — global caps (max day loss, per-symbol position value/shares) and per-venue caps (max order value, position size, open orders) — and the eTape master execution switch must be explicitly armed in the UI. Live venues trade real money; configure them deliberately. Moomoo's separate trade-password unlock remains managed in OpenD.
Alpaca is the quickest venue to get running: a free account gives you paper trading with no funding required, and eTape reuses the same keys for deep chart history.
- Create a free account at alpaca.markets. Paper trading works without depositing anything.
- Generate paper API keys (Key ID + Secret Key) from the dashboard — steps 1–2 of Alpaca's connection guide show exactly where. Live keys are separate, generated from the live dashboard, and only needed if you configure a live venue.
- Add the venue in eTape: Settings → Venues → Add venue, pick broker Alpaca, paste the Key ID and Secret Key, and hit Test connection — eTape verifies the keys and auto-detects the environment (paper/live) before saving.
- Arm the eTape master switch in the UI when you're ready to send orders; until then the gate blocks everything. OpenD's separate trade unlock is still managed in OpenD.
As a bonus, once a paper Alpaca venue is configured the engine automatically reuses
its keys (read-only) for historical chart data — full daily history plus deep
1-minute backfill from Alpaca's free market-data API — no extra setup. The Scanner's
Vol and Turnover columns use the latest base daily totals from the Moomoo
snapshot. REL VOL uses that daily volume divided by the arithmetic mean of up to
50 preceding raw Alpaca SIP daily bars; it is a full-day ratio at every time of day.
Short listing histories use the available contiguous days, while missing or
unavailable history leaves REL VOL as —. Raw profile bars are temporary and are
not added to the chart archive. REL VOL requires Alpaca SIP historical access;
with IEX, missing credentials, or an unavailable historical client the column
remains —. Live keys are deliberately never used for this.
Everything lives in ~/.eTape/ (%USERPROFILE%\.eTape\ on Windows):
| File | Purpose |
|---|---|
config.toml |
Engine config — optional; a missing file means built-in defaults |
credentials.json |
Broker API keys (managed by Settings → Venues) |
etape.db |
SQLite execution state + bar archives (created automatically) |
ticks/ |
Separate bounded tick/BBO/MD-evidence SQLite archive |
Real-feed launches automatically record existing TICKER subscriptions, including
Scanner warm-pool activity. Recording does not change candle filters. Optional
BOOK captures available top bid/ask observations; it yields to ordinary demand
and may be unavailable. Failures warn through existing sys.events while the
live feed continues. Defaults retain up to 30 ET dates within a 10 GiB physical
cap and a 2 GiB free-disk reserve; heavy traffic can retain much less history.
See archive operation and backup guidance
and bootstrap settings. -demo is excluded.
Chart-history limits are calendar spans. The default 10-second limit keeps the
current trading cycle only, beginning at the latest NYSE close/post-market start.
intraday_days applies to focused charts and generic scanner/watch archive warming;
it is not the Scanner REL VOL lookback, which requests up to the prior 50 NYSE
sessions from Alpaca SIP:
[backfill]
ten_second_days = 0
intraday_days = 70
daily_years = 0A minimal hand-written config with a paper simulator and tight risk caps:
# ~/.eTape/config.toml — every omitted field falls back to a sane default
[[venue]]
id = "sim-paper"
broker = "sim" # sim | alpaca | tradezero | moomoo
env = "paper" # paper | live
starting_balance = 100000
[gate.global]
max_day_loss = 500
max_symbol_position_value = 10000
max_symbol_position_shares = 2000
[gate.venue.sim-paper]
max_order_value = 5000
max_open_orders = 10The UI and WebSocket are served on 127.0.0.1:8686 by default ([uihub] section to
change it). On startup, eTape opens the UI in the default browser. OpenD is expected
on 127.0.0.1:11111 ([opend] section).
run.cmd mirrors ./run.sh exactly — same modes, same arguments — and needs nothing
beyond Go, Node.js, and (for live mode) OpenD for Windows. For a self-contained
distributable there's also:
cd engine && make release-windowswhich produces dist/etape-windows-amd64.exe — a single binary with the UI embedded
and a system-tray icon, no console window, no installer. make release-macos does the
same for macOS (arm64), and make release-linux produces
dist/etape-linux-amd64 for the supported Ubuntu LTS releases. The engine is pure Go
(no cgo), so cross-compiling from any OS just works. From the repository root, the
Linux release smoke check is:
bash scripts/smoke-linux-release.sh eTape-<version>-linux-amd64.tar.gzPrebuilt binaries for all three platforms are attached to the latest release.
The regular Windows build and tests do not need cgo, but go test -race does.
For the native Windows race runtime, use a MinGW-w64 compiler; Cygwin's gcc
is not compatible. MSYS2 provides the recommended
toolchain.
-
Install MSYS2, using the default
C:\msys64installation path. -
Open MSYS2 UCRT64 from the Start menu. Do not use Cygwin or the plain MSYS terminal; UCRT64 puts the native compiler first on
PATH. -
Update MSYS2 and install the compiler plus the Make executable:
pacman -Suy # If the terminal closes during the core update, reopen MSYS2 UCRT64 and run # `pacman -Suy` again before continuing. pacman -S --needed mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-make
-
Verify the toolchain from the same UCRT64 terminal:
which gcc gcc --version mingw32-make --version go version
which gccshould resolve to/ucrt64/bin/gcc, not/usr/bin/gccor aC:\cygwin64\bin\gcc.exeinstallation. -
From the repository root, run the race test:
mingw32-make -C engine test-race
Or run Go directly from
engine:go test -race -short ./...
See the MSYS2 UCRT64 environment guide for environment details. WSL/Linux and CI remain alternatives if a native Windows race toolchain is not needed.
Codebase guides: engine · UI · documentation · external APIs · performance evidence · prototypes · scripts
engine/ Go engine — feed, books, bars, scanner, brokers, risk gate, WS hub
ui/ React + TypeScript + Vite UI — panels, canvas renderers, settings
docs/ Current architecture, dependency, evidence, and planning guides
prototypes/ Python research scripts (latency benchmarks, tick aggregation, …)
| Task | Command |
|---|---|
| UI iteration w/ hot reload | ./run.sh dev [fixture] (mock engine + Vite on :5173) |
| Engine tests | cd engine && go test ./... (full) + go test -race -short ./... |
| Engine lint / vet | cd engine && golangci-lint run / go vet ./... |
| UI unit tests | cd ui && npm test |
| UI typecheck / lint | cd ui && npm run typecheck / npm run lint |
| E2E (Playwright, real engine in demo mode) | cd ui && npm run e2e |
| Regenerate TS wire types from Go | mingw32-make -C engine gen-ts (gen-ts-check to verify drift) |
The executable source of truth is .github/workflows/ci.yml.
Follow AGENTS.md for focused local checks and hosted CI
requirements. Full hosted CI continues to run on every push to main and
every pull request. The checklist below is available for broader local
verification and CI failure diagnosis; if it drifts from the workflow, follow
the workflow and update this guide.
Install the pinned linter once, then verify it before using the fast command:
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2
Get-Command golangci-lint
golangci-lint versionThe reported version must be 2.12.2. To run the full local CI-equivalent suite,
start from the repository root:
Set-Location engine
go test ./...
go test -race -short ./...
go vet ./...
golangci-lint run
# If the installed binary is not v2.12.2:
go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2 run
Set-Location ..
mingw32-make -C engine gen-ts-check
Set-Location ui
npm ci
npm run lint
npm test
npm run build
Set-Location ..
git diff --checkNative Windows go test -race requires the existing MSYS2 UCRT64
toolchain. The UI E2E suite (npm run e2e) is an
additional proportional check, not a step in the current CI workflow. For Go
changes, verify that changed files use LF line endings; for WebSocket contract
changes, verify that ui/src/gen/wsmsg.ts has no generated drift. Before every
handoff, run git diff --check, verify that no credentials or runtime data were
introduced, and list checks run and results plus every skipped required check
and its reason. Hosted CI remains authoritative for its Ubuntu and Windows
environments. Prose-only documentation changes need Markdown link validation
and git diff --check; they may be handed off while hosted CI is pending, as
described in AGENTS.md.
The Go structs are the single source of truth for the engine↔UI protocol —
ui/src/gen/wsmsg.ts is generated, never hand-edited.
- Desktop packaging (Wails)
- Smarter extended-hours order handling
eTape is a tool, not advice. Day trading involves substantial risk of loss. This software is provided as-is, without warranty of any kind; you are solely responsible for any orders placed through it and for complying with your brokers' terms. Test against the simulator or a paper account before arming a live venue.
Issues, bug reports, and pull requests are welcome. If you're adding a broker
adapter or panel, open an issue first and follow its subsystem README plus current
specifications in docs/specs/.
MIT — free to use, modify, and distribute.
