ProjectsTestudo

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:

ConstraintWhat it means
Account % riskNever risk more than X% of account on one trade
Fixed risk amountHard cap on dollar risk regardless of account size
Max position sizeUpper 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.

PatternWhat it catches
Sizing driftPosition sizes creeping up after wins or shrinking after losses
Frequency spikeSudden jump in trade count (overtrading)
Session anomalyTrading at hours you normally do not
Setup fatiguePerformance degrading across a session
Correlation stackUnintentional concentration in correlated pairs
Streak riskRevenge trading or overconfidence after streaks

Stack and Why

LayerChoiceWhy
BackendRust (Actix-web)No GC pauses during live positions. rust_decimal for all money math: no f64 rounding.
DatabasePostgreSQL (SQLx)DECIMAL type. SKIP LOCKED for concurrent job processing. One database for journal, queue, and app state.
Job queuepg-queue (built-in)Postgres-native. SKIP LOCKED + LISTEN/NOTIFY. No Redis. One less moving part to run, monitor, and back up.
ExtensionTypeScript, webextension-polyfillManifest V3. Content script scrapes TradingView DOM. Background service worker holds the WebSocket to the engine.
Exchange APICCXT (via Rust FFI) + native Hyperliquid clientCCXT covers Binance, Coinbase, Kraken. Hyperliquid gets a custom Rust client for their L1-specific order types.
WebSockettokio-tungsteniteReal-time fill alerts, position updates, engine health pushed to the extension.
AuthSIWE + SIWS + API keysSign-in with Ethereum/Solana for agent auth. API key rotation for exchange credentials.
ContainerPodman (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

Related: pg-queue | Skills

Built with LogoFlowershow