dexterm

docs › Real mode (LIVE TRADE FIRE signals)

Real mode (LIVE TRADE FIRE signals)

Run a strategy in real mode and every buy and sell it decides goes to your own executor (a Telegram trading bot of your choice, or any script) as a signed, numbered LIVE TRADE FIRE signal. dexterm still holds no key and signs nothing. Experimental; not financial advice.

What it is#

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.

Every strategy runs on paper by default. In real mode the same strategy runs the same way: the same entry rules, the same clamp on every size, the same exits, the same monitor and the same run file as paper. The difference: every buy and sell it fills on its own model of the market also goes out, first, as a LIVE TRADE FIRE signal (dexterm-trade-fire/1) to the executor you chose.

  • Your executor is a Telegram trading bot you trust, or any script of yours. It holds its own wallet and decides whether to act on a signal.
  • dexterm holds no key, signs nothing and builds no transaction. It sends the signal, keeps it in an outbox until it is delivered, and records what your executor says it did.
  • A bot cannot read another bot's messages, so the signal also goes over a channel your executor can read: a local webhook, a command, a stream file or MCP. The Telegram message to your own chat is the copy for you.

Set it up#

  1. A channel your executor can read (one or more), saved next to your Telegram setup in connectors.json:

dexterm autotrade signal channels --webhook http://127.0.0.1:8787/fire dexterm autotrade signal channels --signal-cmd '["/usr/local/bin/my-executor","--fast"]' dexterm autotrade signal channels --jsonl on dexterm autotrade signal channels --mcp on dexterm autotrade signal channels --telegram on --ttl 60

off turns one off. dexterm telegram status shows the channels too.

  1. A webhook secret (recommended for the webhook): dexterm autotrade signal secret generate --out /path/for/your/executor.secret makes a random secret, keeps it in your keychain (like the bot token) and writes it to that file (0600) for your executor. It is never printed. secret set reads one from a pipe; secret remove deletes it.
  2. Your limits: dexterm autotrade limits set perTradeSol=0.25 dailySol=1 buysPerDay=20 maxOpenPositions=3 dailyLossSol=0.5, and any of the others (Going real, safely): per trade in SOL or % of book, exposure, position size, per coin, loss stops, the drawdown breaker, max hold, floors, slippage, TTL, rate. You can set them globally, per mode, per strategy or per account, and the stricter wins. Raising any of them asks for a typed confirmation on your terminal; lowering does not. The numbers shown are placeholders. signal caps set --per-trade-sol … still sets the first four.
  3. Arm the strategy on your own terminal: dexterm autotrade signal arm my-strategy [--account main]. On a first arming it asks for your own limits (Enter keeps a placeholder). Then you see the evidence panel: the last backtest of this version, a forward-test report if there is one, the limits in force for this strategy and account, your executor's health and the channels. Then you type the phrase it shows, which confirms those limits, with its one-time code (REAL my-strategy 7KQ4). The evidence floor: arming needs a completed forward test of these exact rules with at least 10 closed paper trades (your limit minForwardTrades) and a backtest on record. Below it, or when arming now would skip a rung of the ladder, arming refuses and says what is missing; only you can go on anyway, by first typing a separate phrase it shows (OVERRIDE my-strategy 3HXR, its own one-time code). The override is journaled. No model, tool, flag or pipe can give it. Arming saves the strategy as a new version in mode real, and the arming is bound to that exact version: any later edit disarms it. Presets cannot be armed; copy one with dexterm autotrade new first.
  4. Test the channels: dexterm autotrade signal test sends a test signal (test: true, no coin: nothing to trade) through each channel and shows, per channel, delivered or not, the latency, and your executor's receipt. The dry-run executor kit answers it out of the box.
  5. Run it: dexterm autotrade run my-strategy --real. In 0.2.0 real mode runs in the foreground only: the always-on service runs paper strategies, never a real one. Real mode runs on live data only (a replay would send old prices to your executor). --sol-usd N fixes the SOL price for sizes in SOL; without it dexterm reads SOL/USD from DexScreener (keyless) and refuses to start without one.

dexterm autotrade signal disarm my-strategy puts it back on paper. dexterm autotrade run my-strategy --paper runs a real strategy's paper twin: no signal goes out. run --all stays paper: a real strategy runs on its own.

The signal#

One line of JSON, the same bytes on every channel:

{"schema":"dexterm-trade-fire/1","type":"trade","id":"9f2c…(32 hex)","nonce":42,"issuedAt":1792000000000,"expiresAt":1792000060000,"ttlSec":60,"runId":"…","accountId":"main","strategy":{"id":"my-strategy","version":"1.0.1","hash":"…"},"paper":false,"side":"buy","mint":"…","symbol":"PEPE","sizeSol":0.05,"sizeUsd":10,"pctOfBook":2,"sellFraction":null,"price":{"usd":0.0012,"mcap":1200000,"liqUsd":48000,"solUsd":200},"exitReason":null,"slippageBps":300,"maxImpactPct":3,"positionId":"…"}
  • type: trade, halted (the run ended or stopped: outstanding lists the nonces it had sent that had not expired, open the coins dexterm still counts as held for the account) or test.
  • side buy or sell. A buy states sizeSol (what to spend) and pctOfBook. A sell states sellFraction, the share of the position to sell (apply it to what you really hold), and exitReason (the exit rule that fired: hard-stop, trail, tp-1, agent-exit, flatten and so on); its sizeSol is dexterm's modelled proceeds and can be 0 for a coin that rugged.
  • nonce only ever grows, across runs and restarts. id is the idempotency key (a hash of the account, the run and the nonce). paper is always false.
  • Firewall: the only token-made text in a signal is the symbol, reduced to A-Z a-z 0-9 . _ $ - (16 characters). No name, no description, no link, no agent text, no path, no key.

What a well-behaved executor does with each signal:

  1. Check the HMAC (when you set a secret): x-dexterm-signature: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of <t>.<body> with your secret. Refuse a timestamp more than 5 minutes off.
  2. Ignore it after expiresAt. A late signal is never acted on.
  3. Act once per nonce (delivery is at least once: a repeat is normal) and ignore a nonce lower than one already acted on.
  4. Act only on the accountId it was set up for.

SignalReceiver in @dexterm/autotrade does these four checks, if your executor is written in Node.

The channels#

ChannelHow your executor gets itWhat counts as delivered
webhookPOST of the line to http://127.0.0.1, localhost or [::1] on any port, or to an https:// URL of yours. Headers x-dexterm-signature, x-dexterm-nonce, idempotency-key. Redirects are refused.2xx; 409 means "already have it". 408, 425, 429, 5xx or no answer is retried; any other 4xx is a rejection.
--signal-cmdYour program is started with its argv (an absolute path; no shell, no PATH lookup), the line on stdin, a minimal environment (DEXTERM_SIGNAL_NONCE, DEXTERM_SIGNAL_ID, DEXTERM_SIGNAL_SIGNATURE when a secret is set; nothing else of dexterm's). 10 seconds per call.exit 0; exit 3 means "already have it"; exit 2 is a rejection; anything else is retried.
JSONL streamOne line per signal appended to ~/.dexterm/autotrade/real/signals.jsonl (0600), or the path you set. Tail it.the line is written.
MCPThe read-only MCP tool trade_fire_feed returns the signals after a nonce, plus real mode's state.the line is written to the feed.
TelegramA LIVE TRADE FIRE message to your paired chat, as plain text. For you to read, not for your executor.Telegram accepted it.

Delivery. A signal is written to the outbox (~/.dexterm/autotrade/real/outbox.jsonl) before its fill is booked, so the book never holds anything a signal did not announce. Each channel gets the signals in nonce order, retried with backoff from 1 second up to 30, until it acks, rejects one, or the signal expires (then it is marked expired and never sent late). A restart picks the outbox up where it stopped; nothing is sent twice on purpose, and your executor dedupes the rest by nonce. One process writes the outbox at a time. dexterm autotrade signal flush delivers what is pending, and signal tail lists the newest signals with their delivery and receipt state.

The dead-man. Every signal carries its TTL. A run that ends, halts or is killed sends one final halted signal with its outstanding nonces. A runner that dies without one (a crash, a power cut) gets its halted, with the reason runner-lost, the next time the outbox opens: the next real run or dexterm autotrade signal flush.

A run that ends holding a position. Ending a real run (its time limit, Ctrl-C, a stop, the kill switch, a halt, a crash) does not sell anything: what is still open is carried. dexterm keeps its real holdings in ~/.dexterm/autotrade/real/held.json, across runs. You cannot miss them: the run's last lines print the open coins (REAL POSITIONS STILL OPEN), the final halted signal lists them in open, and its Telegram copy says "these positions are still open: manage them in your executor". When no real run is running, NO signals go out, sells included: an open position gets no exit from dexterm until the next real run of its strategy, so manage it in your executor. The next real run of the same strategy and account takes them over and their exits keep firing, so a carried coin still gets its sell signal. A carried coin is never bought again while it is held, it counts against the open-position cap, and a coin another strategy holds is not bought. dexterm autotrade signal held lists the holdings; if you closed one yourself in your executor, dexterm autotrade signal release <mint> (typed confirmation) forgets it.

Receipts: what your executor did#

dexterm cannot see your executor's wallet trades, so a real position stays unconfirmed until a receipt arrives. A receipt is data only: it never makes dexterm buy, sell or change anything.

dexterm autotrade receipt 42 executed --size-sol 0.049 --price 0.00121 --tokens 40500 --tx <signature>
dexterm autotrade receipt 43 rejected --reason "slippage too high"
dexterm autotrade receipt list

Your executor can also append one line per signal to ~/.dexterm/autotrade/real/receipts.jsonl: {"schema":"dexterm-trade-receipt/1","signalId":"…","nonce":42,"accountId":"main","status":"executed","at":1792000004000,"fill":{"priceUsd":0.00121,"sizeSol":0.049,"tokens":40500,"txSig":"…"},"reason":null,"executor":"my-bot"}. A receipt for an unknown nonce, another account or a mismatched id is counted and ignored.

The exits keep firing on dexterm's own view of the market whether or not a receipt came. A receipt with a fill also says how far the real fill landed from dexterm's paper fill (paper vs real). The drift between signals and receipts (unconfirmed, rejected, failed, filled size and price against the signal) shows in receipt list, signal status, dexterm autotrade audit and the run file (receipt rows). dexterm autotrade signal reconcile --wallet <your PUBLIC address> reads that address's SOL balance and token holdings (two read-only calls) and lists the coins the real book holds that the wallet does not, and back. It never takes a key.

Safety#

  • Only you switch a strategy to real, on your own terminal, with the typed phrase. A model cannot: the mode is on the list of fields only you may change, the agent's decisions have no such verb, no MCP tool arms, and a file edited by hand to "mode": "real" is not armed. Arming, an evidence override, disarming, caps and the kill switch are all journaled in ~/.dexterm/autotrade/real/journal.jsonl.
  • The limits live outside the model (all of them). A buy is cut to the per-trade, position, exposure and price-impact limits. A buy over the daily SOL, buys per day, per coin or open positions limit is refused (open positions means every real holding on this machine, whichever run opened it). So is a buy after a loss stop, the drawdown breaker or a loss streak, or while your executor is silent. Sells are never blocked by a limit unless you set the optional sell rate, so an exit always goes out.
  • The real-mode kill switch: dexterm autotrade signal kill stops every trade-fire signal at once, sells included. It never closes a position: it prints the open ones, and a running real run halts at its next tick and sends one halted that lists them for your executor. dexterm autotrade signal unkill (typed confirmation) lifts it. The panic switch (dexterm wallet panic) halts real runs too: no new entries, exits keep going out.
  • Chat approvals stay paper. A one-tap approval never authorises a real signal: a strategy that waits for a tap (entry.decide: "agent+approve", or dexterm telegram approvals on) does not start in real mode. Your executor is the last gate.
  • The prompt firewall, the strategy clamp and the session caps apply exactly as on paper.

Where you see it#

  • The run: every fill and closed trade row of a real run is tagged real-signal and names its nonce; the start line says REAL; receipts become rows; the end has a real_halted row.
  • **The Q screen's runner view**: the mode, the account, the kill switch, the outbox depth, each channel's health, the unconfirmed positions, the limits in force (with a BUYS PAUSED line when a limit or a silent executor stops new buys) and your executor's health.
  • Telegram: every message of a real run carries the LIVE badge. A closed trade is a text line (its numbers are dexterm's model, not your executor's fills), never a PnL card.
  • The trade ledger: dexterm ledger lists a real run's trades with the mode real (--mode real), never paper. Their numbers are dexterm's model of each trade it signalled; your executor's receipts are the real fills, so there is no PnL card for them.
  • Shared views: the spectator stream and saved files keep signals and receipts out.

What it will not do#

  • Hold, import or ask for a key or a seed phrase, sign, or send a transaction.
  • Trade on a replay, or let a chat tap authorise a real trade.
  • Promise a fill. Your executor decides, at its own price, with its own fees.
  • Run more than one real strategy per machine at a time, run one inside run --all, or run one under the always-on service (in 0.2.0 real runs in the foreground only).
  • Send any signal, sells included, while no real run is running, or close a position by itself.