Testudo
Testudo
Automated position sizing and trade execution for crypto.
Draw a position on TradingView. Hit Alt+X. The trade sizes against your risk rules, routes to your exchange, and journals everything. No manual entry. No mental math on position size.
The Problem
Most traders lose money from bad sizing, not bad direction. They oversize. They move stops. They trade without a record of what happened.
Testudo removes those variables. Position size is calculated deterministically before the order goes out. Everything gets logged.
How It Works
1. Draw on TradingView
Use the Long Position or Short Position drawing tool to mark entry, stop, and target. The browser extension reads these straight from the DOM. No copy-paste.
2. Alt+X
The extension scrapes the setup (symbol, side, entry, stop, target, timeframe), sends it to the Rust backend, and returns a calculated position size. You see the size, the risk amount, and which constraint is binding before you confirm.
3. Trade routes to your exchange
Confirm, and the order goes to your connected exchange. The engine manages the position: stop updates, target fills, partial closes. Everything is recorded.
Position Sizing Engine
The sizer takes the minimum of three independent constraints:
| Constraint | What it means |
|---|---|
| Account % risk | Never risk more than X% of account on one trade |
| Fixed risk amount | Hard cap on dollar risk regardless of account size |
| Max position size | Upper bound on total notional exposure |
The binding constraint is surfaced to the user. The logic is transparent. Kelly-optimal sizing is also available as an alternative method, configurable per account.
Analytical Dashboard
Every trade, fill, and management decision hits the journal. The dashboard shows what is actually happening.
Performance stats
Win rate, profit factor, expectancy, average R-multiple. Average win and loss sizes. Largest win and loss. Trades per day, average duration. PnL calendar heatmap.
Trading coach
A weekly digest that catches behavioral patterns in your own data. Each pattern is compared against your 30-day rolling baseline and triaged: Info, Notable, or Concerning.
| Pattern | What it catches |
|---|---|
| Sizing drift | Position sizes creeping up after wins or shrinking after losses |
| Frequency spike | Sudden jump in trade count (overtrading) |
| Session anomaly | Trading at hours you normally do not |
| Setup fatigue | Performance degrading across a session |
| Correlation stack | Unintentional concentration in correlated pairs |
| Streak risk | Revenge trading or overconfidence after streaks |
Stack and Why
| Layer | Choice | Why |
|---|---|---|
| Backend | Rust (Actix-web) | No GC pauses during live positions. rust_decimal for all money math: no f64 rounding. |
| Database | PostgreSQL (SQLx) | DECIMAL type. SKIP LOCKED for concurrent job processing. One database for journal, queue, and app state. |
| Job queue | pg-queue (built-in) | Postgres-native. SKIP LOCKED + LISTEN/NOTIFY. No Redis. One less moving part to run, monitor, and back up. |
| Extension | TypeScript, webextension-polyfill | Manifest V3. Content script scrapes TradingView DOM. Background service worker holds the WebSocket to the engine. |
| Exchange API | CCXT (via Rust FFI) + native Hyperliquid client | CCXT covers Binance, Coinbase, Kraken. Hyperliquid gets a custom Rust client for their L1-specific order types. |
| WebSocket | tokio-tungstenite | Real-time fill alerts, position updates, engine health pushed to the extension. |
| Auth | SIWE + SIWS + API keys | Sign-in with Ethereum/Solana for agent auth. API key rotation for exchange credentials. |
| Container | Podman (rootless) | Engine never runs as root. |
Design choices worth noting
rust_decimal, never f64. Floating point is not acceptable for money. Every calculation from position sizing to PnL uses fixed-point decimal. This alone rules out most languages for the hot path.
Postgres is the only infrastructure. The journal is the source of truth. The job queue runs on the same instance. No Redis, no message broker. One thing to back up. One thing to monitor.
Position sizing is deterministic. Same inputs always produce the same size. The limiting factor is surfaced. No discretion. No fuzzy logic.
Fail-closed. If the DOM scraper cannot parse the TradingView drawing, or the WebSocket drops, the trade does not happen. Halting is safer than guessing.
Roadmap
Not built yet. Things I want to add when the core is solid:
- DLT settlement: immutable on-chain trade attestation via Substrate
- Sheaf Engine: cellular sheaf topology for multi-venue market structure
- Strategy proofs: Lean 4 formal verification of trading primitives
- zk-SNARK attestation: private proof of risk-parameter compliance
Run it
# Backend
cd testudo-exchange
cp .env.example .env
cargo run --bin router
# Extension
cd testudo-extension
bun install && bun run build
# Load dist/chrome as unpacked extension