The SDK, the command line and the backtest engine behind tradectl. Build a strategy, test it on real data, and run it live.
One curl, no package manager. This installs the tradectl command-line engine that runs your bots. The desktop Lab is a separate download. The script finds your OS and CPU, downloads the matching binary, checks its SHA256 and installs it in ~/.tradectl/bin. If that folder is not on your PATH, it prints the line to add.
Each card is a command of the tradectl CLI, plus the desktop Lab.
Run your strategy as a bot on Binance futures: tradectl run my-bot -c config.json. Paper or live is one switch in the config.
--daemon runs the bot in the backgroundtradectl stop cancels the bot's orders and closes its positionstradectl status and tradectl logs show what it is doingtradectl init makes a Rust project on the SDK; tradectl build compiles it into a plugin the bot loads.
tradectl collect records ticker, trades, candles and order book from Binance; tradectl prepare packs the days into one file.
Test the same strategy code on recorded data before it trades.
tradectl backtest simulates fills with maker and taker feestradectl sweep -r key=min:max:step tries every combination of parameters and ranks themtradectl service install makes a bot a Linux service, tradectl autostart on starts bots after a restart, and tradectl telegram link sends alerts to Telegram.
tradectl watchdog is a separate guard, best on another machine, that watches your bots and your exchange account for positions left without protection.
tradectl login and tradectl license for your plan, tradectl auth set for exchange keys, tradectl install for a ready strategy (SHA256 checked), tradectl update for a new CLI.
A desktop app for macOS, Windows and Linux to watch and control your bots: positions, orders and trades on a live chart, settings, pausing or restarting a strategy, backtests and sweeps. It connects straight to your bot over a secure port. Download the Lab →
You write the trading logic. The engine does the work that makes a live bot safe, the same in paper and live.
You say what orders you want; the engine places, amends and cancels them, follows every fill and keeps your take-profit and stop-loss in step with the position.
A stop leaves the bot flat: it cancels the bot's orders, closes its positions and checks the exchange for anything left.
tradectl stop and Ctrl-C both close out the botLimits from your config are checked before any order goes out. When a limit trips, new entries stop and open positions are still managed to the end.
Your strategy never calls an exchange API: the engine carries out its orders through one exchange interface, so the same code is meant to run on many exchanges.
Run several strategies in one process, even on the same symbol.
What you need to leave a bot running.
Write strategies in Rust with the SDK, or install a ready one. Every download is SHA256 checked.
tradectl install shot downloads one; the binary is SHA256 checkedtradectl telegram link sends your bots' alerts to Telegram1 use tradectl_sdk::*; 2 3 declare_strategy!("dip-buyer", DipBuyer::new); 4 5 pub struct DipBuyer { dip: f64, tp: f64, sl: f64 } 6 7 impl DipBuyer { 8 pub fn new(p: &Params) -> Self { 9 Self { 10 dip: p.get("dip", 0.5) / 100.0, 11 tp: p.get("tp", 0.3) / 100.0, 12 sl: p.get("sl", 0.5) / 100.0, 13 } 14 } 15 } 16 17 impl Strategy for DipBuyer { 18 // EMA of 20 one-minute bars, fed by the engine 19 fn indicators(&self) -> Vec<IndicatorRequest> { 20 vec![IndicatorRequest::new(IndicatorKind::Ema, 20, 60)] 21 } 22 23 fn on_ticker( 24 &mut self, 25 t: &TickerEvent, 26 ctx: &StrategyContext, 27 ) -> Action { 28 let Some(ema) = ctx.indicator(0) else { 29 return Action::Hold; 30 }; 31 let busy = !ctx.positions.is_empty() 32 || !ctx.entry_orders.is_empty(); 33 let dip = t.bid_price < ema * (1.0 - self.dip); 34 if busy || !ctx.can_enter || !dip { 35 return Action::Hold; 36 } 37 Action::PlaceEntry { 38 side: Side::Long, 39 price: Some(t.bid_price), 40 size: 0.01, 41 kind: OrderKind::Limit, 42 exits: vec![], 43 entry_id: None, 44 } 45 } 46 47 fn on_fill( 48 &mut self, 49 f: &FillEvent, 50 _: &StrategyContext, 51 ) -> FillResponse { 52 let mut actions = vec![]; 53 if f.is_entry { 54 let exit = |id: &str, price, kind| ExitOrder { 55 id: id.into(), 56 price, 57 size: f.quantity, 58 kind, 59 delay_ms: 0, 60 }; 61 actions.push(Action::SetExits { exits: vec![ 62 exit("tp", f.price * (1.0 + self.tp), ExitType::Limit), 63 exit("sl", f.price * (1.0 - self.sl), ExitType::Stop), 64 ]}); 65 } 66 FillResponse { actions, notify: true } 67 } 68 69 fn name(&self) -> &str { "dip-buyer" } 70 }|