dexterm

docs › Going real, safely

Going real, safely

The whole path from a paper strategy to real trades through your own executor. Your own limits on every number, a dry-run executor kit to wire up first, executor health, paper vs real fills, and the daily digest. What dexterm does, and what it never does. Experimental; not financial advice.

The whole path#

0.2.0, experimental. Real mode can lead to real trades in a wallet you fund. dexterm never holds a key, never signs and never sends a transaction. Your executor does, by its own rules. Not financial advice.
  1. Build and test on paper. Make a strategy (from a preset, by chat, or in a file), backtest it on your recorded history, then forward-test it on paper on live data (dexterm autotrade forward start my-strategy --hours 24). Arming for real needs a completed forward test with at least 10 closed paper trades and a backtest (the evidence floor, minForwardTrades). Paper is the default for every new strategy.
  2. Wire up an executor, dry run. Copy the executor kit, start one of its executors, point a channel at it, and send a test signal. Nothing can trade yet.
  3. Set your own limits. dexterm autotrade limits set … (your limits). The defaults are placeholders, and your numbers win.
  4. Arm on your own terminal. dexterm autotrade signal arm my-strategy. On a first arming dexterm asks for your numbers, then shows the evidence (backtest, forward test), the limits in force for this strategy and account, and your executor's health. Typing the phrase it shows confirms all of it. Below the evidence floor, or past a missing rung of the ladder, it refuses unless you first type a separate OVERRIDE phrase, which is journaled.
  5. Run it real. dexterm autotrade run my-strategy --real, in the foreground (in 0.2.0 the always-on service runs paper strategies only). Every buy and sell goes to your executor as a LIVE TRADE FIRE signal, inside your limits. When no real run is running, no signal goes out, sells included: positions still open at the end are printed, listed in the halted signal and carried to the next real run; until then, manage them in your executor.
  6. Watch and calibrate. signal status, the Q screen and your Telegram chat show the limits, the executor's health and how far real fills land from paper. The digest sums it up each day or week.

At any point, dexterm autotrade signal kill stops every signal, sells included (one final halted still goes out, listing the positions still open; it closes nothing), and dexterm autotrade signal disarm my-strategy puts the strategy back on paper.

Your limits#

Every number real mode enforces is yours. They live in one file, ~/.dexterm/autotrade/real/limits.json (schema dexterm-limits/1; dexterm autotrade limits schema prints its JSON Schema), outside the model.

Sizing:

  • perTradeSol and perTradePct: the most one buy may spend, in SOL or as a percent of the book. With both set, the smaller wins.
  • dailySol and buysPerDay: what all buys may spend, and how many there may be, per local day.
  • maxOpenPositions: counted across every real holding on this machine.
  • maxExposurePct: everything open together, as a percent of the book.
  • minPositionSol and maxPositionSol: no dust orders, and a cap on one position with its adds.
  • perCoinSolPerDay and perCoinBuysPerDay: how much one coin may take in a day, re-entries included.
  • bookSol: the size of YOUR account, which the percent limits use. Unset, they use the strategy's own book.

Risk:

  • dailyLossSol: once the day's realized loss reaches it, no new buy goes out until tomorrow.
  • maxDrawdownSol: the drawdown breaker. It stops new buys until you reset it with dexterm autotrade limits breaker reset (typed). The reset also lifts a loss-streak pause and starts the streak count again.
  • lossStreak and lossStreakPauseMin: that many losing closes in a row pause new buys for that long.
  • maxHoldMinutes: the stricter of this and each exit profile's own cap.
  • minLiquidityUsd and minMcapUsd: floors on the coin. While one is set, an unknown value is refused.
  • maxSlippageBps and maxPriceImpactPct: the tolerance a signal states. A buy is cut to the price-impact cap on the pool.
  • signalTtlSec: how long your executor may act on a signal.
  • maxBuysPerMinute: the signal rate limit.

Sells: never capped, unless you set maxSellsPerMinute. Only the kill switch stops exits.

Evidence: minForwardTrades, the evidence floor for arming: the closed paper trades a completed forward test of the strategy's exact rules must have, with a backtest on record. Lowering it or turning it off (no trade minimum; the completed forward test and the backtest are still needed) is a raise.

Executor: executorSilenceMin, how long your executor may be silent before new buys pause (see executor health).

Loss limits are counted on dexterm's own fills: the paper view of every signal.

Scopes, and the stricter wins.

  • A number in global replaces the placeholder default. off turns a global limit off, which is a raise.
  • real / paper (a mode), strategy:<id> and account:<id> can only tighten. Whichever scope sets the stricter number, that number is in force: a smaller cap, or a larger floor.
  • Paper runs ignore limits unless you add a paper scope. Then a single paper run (dexterm autotrade run <id> --paper, a forward test included) obeys them too, the way a real run would, on its own state (it never touches real mode's counters). Limits are in SOL, so a paper replay with a paper scope needs --sol-usd N. Running several strategies at once (run --all --paper) does not apply the paper scope yet.
  • The day's counters (buys, SOL spent, realized loss, per coin) count every real buy from this home: all strategies and accounts together. A strategy: or account: number is compared with that total, so it can only stop buys sooner, never later.
dexterm autotrade limits show --strategy my-strategy --account main   # what is in force, and where each number comes from
dexterm autotrade limits set perTradeSol=0.5 dailySol=2 dailyLossSol=0.4
dexterm autotrade limits set --scope strategy:my-strategy perTradePct=2 maxOpenPositions=1
dexterm autotrade limits explain maxExposurePct

Lowering is free and immediate. A running real run re-reads the file and uses the stricter of what it started with and what the file says now.

Raising needs you. limits set shows every change old -> new, marks each RAISE, and asks for a typed phrase with a one-time code on your own terminal. A pipe, a flag, an agent or MCP cannot give it. A raise applies from the next run.

Hand edits. If you edit limits.json by hand, a real run accepts the edit only when it lowers. One that raises anything is refused until you review it (dexterm autotrade limits diff) and accept it (dexterm autotrade limits accept, typed). Everything is journaled. Caps you set before 0.2.0 (caps.json) carry over as your global numbers, unless they differ from what dexterm last saved: then they count as a hand edit too.

The placeholders are 0.25 SOL a trade, 1 SOL a day, 20 buys a day, 3 open positions, a stop at -0.5 SOL a day, a pause after 5 losing closes in a row (60 min), at most 3 buys a minute, a pause when your executor is silent for 15 minutes, and an evidence floor of 10 closed paper trades. They are a cautious starting point, not advice. Real mode never arms on them silently: the first arming asks for your own numbers, and every arming shows the limits it confirms.

The analyst can explain, never apply. Ask it "why was that buy refused?" or "what would a 1 SOL daily cap change?". It reads the limits (trading_limits) and can propose a change as a diff card (limits_propose) with the exact command for you to run. No tool, chat command or model decision can change a limit.

The executor kit#

dexterm autotrade executor kit --out ~/my-executor copies three small executors, plain Node with no dependencies:

filechannelpoint dexterm at it
webhook-executor.mjswebhook (127.0.0.1 only)dexterm autotrade signal channels --webhook http://127.0.0.1:8787/signal
signal-cmd-executor.mjscommand hookdexterm autotrade signal channels --signal-cmd '["/abs/path/node","/abs/path/signal-cmd-executor.mjs","--account","main"]'
jsonl-tail-executor.mjsthe JSONL streamdexterm autotrade signal channels --jsonl on --jsonl-signed on

Each one handles every signal the same way:

  1. It checks the HMAC (--secret-file, the file dexterm autotrade signal secret generate --out FILE writes).
  2. It checks the account.
  3. It ignores a repeat by nonce. The seen set survives a restart.
  4. It ignores anything past its expiry.
  5. It writes a receipt and sends a heartbeat.

They are dry run: they log what they would do, and their receipts say so. There is no wallet code, no key and no chain call in the kit. To trade, you replace one function (act() in kit.mjs) with your own code, or hand the order to the Telegram trading bot you chose. The kit's telegram-bot-recipe.md explains that set-up: a bot cannot read another bot's messages, so a small relay of yours hands each signal to the bot.

--jsonl-signed on writes each stream line as an envelope carrying the same HMAC as the webhook, so a stream reader can check it too.

Prove it end to end: dexterm autotrade signal test sends a signal marked test: true that names no coin, so there is nothing in it to trade. For each channel it shows whether the signal was delivered and how long that took, then your executor's receipt (--wait SEC, --json). The Telegram copy is for you, so it waits for the next run.

Executor health#

dexterm counts three signs of life from your executor:

  • a heartbeat: a dexterm-executor-heartbeat/1 line in ~/.dexterm/autotrade/real/executor.jsonl, dexterm autotrade executor heartbeat, or a POST to the listener a real run opens with --executor-port N (127.0.0.1 only, HMAC required when a secret is set);
  • a receipt;
  • an ack on the webhook or the command hook.

dexterm autotrade executor status, signal status, the Q screen and Telegram's /status show:

  • whether your executor is alive, and when it was last heard;
  • its last receipt;
  • the median lag from a signal to its receipt.

When it goes quiet for longer than executorSilenceMin, a real run pauses new buy signals and tells you in the run log and on Telegram. Exits keep going out. When the executor is heard again, buys resume and you are told. A heartbeat is only evidence of life: it cannot raise a limit, reset the breaker, lift the kill switch or confirm a position.

Paper vs real#

When your executor's receipts carry fills (priceUsd, sizeSol, tokens), dexterm compares each one with its own paper fill for that signal:

  • slippage, positive when the real fill was worse than paper;
  • latency from the signal to its receipt;
  • partial fills;
  • rejections and failures, with their reasons.

This shows in dexterm autotrade signal status, dexterm autotrade audit, every run file's receipt rows and the digest. Use it to calibrate: if real fills land 4% worse than paper, the backtests and forward tests are 4% optimistic per trade. With few receipts the numbers say little, and the report says so.

The digest#

dexterm autotrade report [--period day|week] [--strategy ID] prints, for each strategy and mode:

  • the closed trades, and the win rate with its 95% interval;
  • the net, and the max drawdown;
  • refusals, the limits that were hit, and the limits that clamped a size;
  • receipts and paper vs real fills;
  • your executor's health.

It always ends with a note on the sample size. Five trades say almost nothing, and the interval shows how little.

--telegram sends it to your paired chat through your own bot. --json prints it as data. dexterm autotrade report --schedule daily|weekly|off makes a run with your bot on send it by itself: once a day after local midnight, or on Mondays. It is off by default.

What dexterm does, and never does#

It does:

  • run your strategy exactly like paper;
  • turn each trade into a signed, numbered, expiring signal for your executor, inside your limits;
  • keep the signal until it is delivered;
  • record what your executor reports;
  • pause new buys when your executor is silent or a loss limit trips;
  • tell you each time.

It never:

  • holds, imports or asks for a key or a seed phrase;
  • signs or sends a transaction, or builds one;
  • lets a model, a tool or a chat tap arm real mode, change a limit, lift the kill switch or reset the breaker;
  • trades a test signal;
  • promises a fill or a profit.

Your executor decides, at its own price, with its own fees.