



spread_threshold. Position rebalancing keeps both sides roughly even. Risk-neutral by design.| Mode | Description |
|---|---|
| gtc_at_ask | GTC limit order at the ask price. Recovers via maker rebate if not filled. |
| single_taker | FOK market order at the ask. Immediate fill or cancel. |
| single_maker | GTC limit order at the bid. Cheaper execution, slower fills. |
| dual_hybrid | Taker on the signal side, maker on the contra side. |
rustup.rs# Unzip and enter the project unzip distinct-baguette.zip && cd distinct-baguette # Create .env from template cp .env.example .env # Edit .env with your private key and funder address # Build the trading bot cd trading_bot/trading_engine cargo build --release
.env file needs two values:PRIVATE_KEY=your_private_key_here_without_0x_prefix FUNDER_ADDRESS=your_wallet_address_here
dry_run: true by default. Verify the bot connects and evaluates markets before going live:./target/release/distinct-baguette --config configs/btc.json --dry-run
# Cross-compile for Linux (from macOS) cd trading_bot/trading_engine cargo build --release --target x86_64-unknown-linux-gnu # Deploy to a fresh VPS bash deploy/setup.sh user@your-vps-ip
| Script | Purpose |
|---|---|
| setup.sh | Full first-time setup on a fresh VPS |
| deploy.sh | Hot-swap binary update, zero-downtime restart at next window boundary |
| cleanup.sh | Complete removal (stops services, removes user, binary, configs, logs) |
| healthcheck.sh | Cron-compatible service status and log freshness check |
| Service | Host | Protocol | Purpose |
|---|---|---|---|
| Polymarket CLOB | clob.polymarket.com | HTTPS | Place/cancel orders, balances |
| Polymarket Market WS | ws-subscriptions-clob.polymarket.com | WSS | Real-time orderbook/prices |
| Polymarket User WS | ws-subscriptions-clob.polymarket.com | WSS | Order fills, status updates |
| Polymarket Auth | clob.polymarket.com/auth/ | HTTPS | API key derivation (EIP-712) |
| Gamma API | gamma-api.polymarket.com | HTTPS | Market metadata, token IDs |
| Polymarket Geoblock | polymarket.com | HTTPS | Geo-eligibility check |
| Binance WS | stream.binance.com:9443 | WSS | Price feed (aggTrade) |
| Polygon RPC | polygon-rpc.com (configurable) | HTTPS | On-chain merge/redeem |
stream.binance.com is blocked in the US. Users in restricted regions should run the bot from a non-US VPS.polygon-rpc.com can be slow under load — you can swap to Alchemy, Infura, or QuickNode in your config file (rpc_url field).| Provider | Region | Notes |
|---|---|---|
| DigitalOcean AMS3 | Amsterdam | ~1ms to CLOB, tested and recommended |
| Vultr Amsterdam | Amsterdam | Good alternative to DigitalOcean |
| AWS eu-west-1 | Ireland | Reliable, slightly higher latency |
rustup target add x86_64-unknown-linux-gnu brew install filosottile/musl-cross/musl-cross CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-musl-gcc \ cargo build --release --target x86_64-unknown-linux-gnu
| Issue | Fix |
|---|---|
| "Invalid API key" | Remove 0x prefix from private key, ensure 64 hex chars |
| "Insufficient balance" | Deposit more USDC on Polygon |
| Merge transactions fail | Send POL to your wallet on Polygon |
| Bot connects but no trades | Set "dry_run": false in config |
| ICE (compiler error) | Try: RUSTFLAGS='-Awarnings' cargo build --release |
--config. The package includes pre-tuned configs for BTC, ETH, SOL, and XRP.| Parameter | Default | Description |
|---|---|---|
| symbol | btc | Asset to trade: btc, eth, sol, xrp |
| strategy | arb | Strategy: arb, momentum, or mm |
| dry_run | false | Simulate without placing real orders |
| window | 15m | Market window: 5m, 15m, 1h |
| current_market | false | Trade current window instead of next |
| min_price | 0.0 | Only trade above this price |
| max_price | 1.0 | Only trade below this price |
| stop_before_end_ms | 0 | Stop trading N ms before window end |
| web_port | 0 | Dashboard WebSocket port (0 = disabled) |
| Parameter | Default | Description |
|---|---|---|
| max_buy_order_size | 5.0 | Shares per order |
| spread_threshold | 0.02 | Min spread to trigger ($) |
| trade_cooldown | 5000 | Cancel unfilled orders after (ms) |
| balance_factor | 0.0 | Position rebalancing aggressiveness (0-1) |
| price_bias | 0.0 | Bid bump per share of imbalance |
| Parameter | Default | Description |
|---|---|---|
| momentum_size | 10 | Shares per directional buy |
| momentum_mode | single_taker | Execution mode (gtc_at_ask, single_taker, single_maker, dual_hybrid) |
| eval_interval_ms | 2000 | Min cooldown between evaluations (ms) |
| lookback_secs | 10 | BTC price delta lookback window |
| entry_min_delta | 0.0 | Min BTC price change to trigger (%) |
| entry_delay_secs | 13 | Skip first N seconds of each window |
| burst_count | 1 | Orders per evaluation cycle |
| burst_step | 0.01 | Price increment per burst order ($) |
| stop_loss_pct | 0.0 | Sell when bid drops this far below avg cost (0 = disabled) |
| stop_loss_keep_shares | 0.0 | Shares to retain after stop-loss |
| stop_loss_cooldown_ms | 0 | Block re-buying N ms after stop-loss |
| Parameter | Default | Description |
|---|---|---|
| mm_levels | 1 | Quote levels per side |
| mm_size | 10 | Shares per level |
| mm_requote_ms | 500 | Requote interval (ms) |
| mm_min_margin | 0.005 | Min margin to quote |
| mm_level_step | 0.01 | Price step between levels ($) |
| mm_max_imbalance | 200 | Max position imbalance before pausing |
| Parameter | Default | Description |
|---|---|---|
| merge_interval_secs | 240 | Merge paired positions every N seconds |
| merge_fraction | 0.5 | Fraction of pairs to merge each cycle |
| merge_min_pairs | 10 | Min pairs before merging |
| polygon_rpc_url | https://polygon-rpc.com | Polygon RPC endpoint |
{
"symbol": "btc",
"strategy": "momentum",
"window": "15m",
"dry_run": true,
"momentum_size": 10,
"momentum_mode": "gtc_at_ask",
"lookback_secs": 2,
"entry_min_delta": 0.02,
"eval_interval_ms": 1000,
"entry_delay_secs": 8,
"merge_interval_secs": 240,
"merge_fraction": 0.5,
"web_port": 3001
}lookback_secs produces higher ROI but fewer trades per windowentry_min_delta improves quality but reduces volumeeval_interval_ms acts as a rate limiter — 1000ms is typically the sweet spot{
"lookback_secs": 2,
"entry_min_delta": 0.02,
"eval_interval_ms": 1000,
"momentum_mode": "gtc_at_ask",
"entry_delay_secs": 8,
"burst_count": 1
}{
"lookback_secs": 1,
"entry_min_delta": 0.01,
"eval_interval_ms": 1000,
"momentum_mode": "gtc_at_ask",
"entry_delay_secs": 5,
"burst_count": 3,
"burst_step": 0.01
}mm_levels is the #1 defense against adverse selectionmm_min_margin protects against stale fillsmm_requote_ms reduces stale quote exposure but increases cancel rate{
"mm_levels": 2,
"mm_size": 10,
"mm_level_step": 0.01,
"mm_requote_ms": 500,
"mm_min_margin": 0.02,
"mm_max_imbalance": 100
}{
"mm_levels": 3,
"mm_size": 15,
"mm_level_step": 0.01,
"mm_requote_ms": 250,
"mm_min_margin": 0.005,
"mm_max_imbalance": 200
}60s) to realize profits before window endmerge_fraction: 0.5 is a good default — merges half each cycle, smoothing gas costsmerge_min_pairs: 10 to avoid paying gas for tiny mergesfor lb in 1 2 3 5; do
echo "=== lookback=$lb ==="
./target/release/backtester \
--folder ../../data/sample \
--strategy momentum \
--lookback $lb \
--entry-min-delta 0.0001 \
--eval-interval 1000 \
--orderbook-lag 200
donefor lb in 1 2 3; do
for delta in 0.0001 0.0002 0.0005; do
echo "=== lookback=$lb delta=$delta ==="
./target/release/backtester \
--folder ../../data/sample \
--strategy momentum \
--lookback $lb \
--entry-min-delta $delta \
--eval-interval 1000 \
--orderbook-lag 200 2>&1 | grep -E "^(Worst|ROI)"
done
done| Metric | What It Means |
|---|---|
| Worst-case P&L | Sum of all market P&Ls (assumes every market is traded). Most conservative. |
| ROI | Worst-case P&L / total capital deployed. Higher = better capital efficiency. |
| Median P&L | Median per-market P&L. More representative of typical performance. |
| Both-profitable % | Percentage of markets where both sides are profitable. |
| Imbalance | Avg end-of-window position imbalance. Lower = less unhedged risk. |
web_port in your config.{
"web_port": 3001
}dashboard/index.html in a browser and connect to ws://localhost:3001.| Panel | Data |
|---|---|
| Position | Live UP/DOWN share counts, cost basis, realized P&L |
| Market | Current bid/ask for both sides |
| Signal | Momentum delta, BTC price, direction, action (trade/skip/rest) |
| Orders | Placed orders with price, size, and fill status |
| Merges | On-chain merge events with shares merged |
| Config | Active strategy parameters |
ssh -L 3001:localhost:3001 user@your-vps-ip
ws://localhost:3001.| Command | Description |
|---|---|
| {"cmd": "list_data"} | List recorded data files |
| {"cmd": "fetch_all"} | Download all data files |
| {"cmd": "delete_all"} | Delete all data files |
| {"cmd": "fetch_and_delete"} | Download and delete data files |
# Parse bot logs for order/fill patterns cat /var/log/distinct-baguette/btc.log | python scripts/parse_log.py # P&L analysis from live logs python scripts/analyze_live_pnl.py /var/log/distinct-baguette/btc.log
data/| Directory | Markets | Size | Period |
|---|---|---|---|
| data/sample/ | 18 | 22 MB | Jan 14 |
| data/btc/ | 3,078 | 2.4 GB | Dec 20 - Feb 17 |
| data/eth/ | 1,957 | 1.3 GB | Dec 20 - Feb 17 |
| data/sol/ | 1,964 | 1.0 GB | Dec 20 - Feb 17 |
| data/xrp/ | 1,967 | 929 MB | Dec 20 - Feb 17 |

| Flag | Default | Description |
|---|---|---|
| --folder | data/sample | Directory with market JSON files |
| --strategy | arb | Strategy: arb, momentum, mm |
| --orderbook-lag | 0 | Simulated execution latency (ms) |
| --klines | External klines.jsonl file path | |
| --start-ts / --end-ts | Unix timestamp filters | |
| --lookback | 10 | BTC price lookback (seconds) |
| --entry-min-delta | 0.0 | Min BTC delta to trigger (decimal %) |
| --eval-interval | 2000 | Evaluation interval (ms) |
| --spread | 0.02 | Spread threshold for arb ($) |
| --mm-levels | 1 | MM quote levels per side |
| --mm-size | 10 | MM shares per level |
| --mm-requote | 500 | MM requote interval (ms) |
| --mm-fill-model | deterministic | Fill model: deterministic, probabilistic, latency |
| --mm-fill-prob | 0.20 | Fill probability (probabilistic/latency model) |
| --mm-latency | 200 | Cancel-replace latency (ms, latency model) |
# Momentum strategy on sample data ./target/release/backtester \ --folder ../../data/sample \ --strategy momentum \ --lookback 2 \ --entry-min-delta 0.0001 \ --eval-interval 1000 \ --orderbook-lag 200 # Market-making strategy ./target/release/backtester \ --folder ../../data/sample \ --strategy mm \ --mm-levels 2 \ --mm-size 10 \ --mm-requote 500 \ --mm-fill-model latency \ --mm-latency 100 # Arb (spread capture) strategy ./target/release/backtester \ --folder ../../data/sample \ --strategy arb \ --spread 0.02 \ --orderbook-lag 200
# List available dates python scripts/download_data.py --list-dates # Download one day python scripts/download_data.py --date 2026-01-16 # Download everything python scripts/download_data.py --all
| Strategy | Config | P&L | ROI |
|---|---|---|---|
| Momentum | lookback=2s, delta=0.01%, eval=1000ms | +$7,086 | +4.61% |
| Momentum | lookback=3s, delta=0.01%, eval=1000ms | +$6,706 | +2.96% |
| MM | 2 levels, $0.01 step, prob fill model | +$34,000 | +0.86% |
| MM + Cancel | threshold=0.005%, lookback=1s | +$89 median/mkt | +32% vs baseline |
Version history. Re-download anytime to get the latest build.