{"id":27194710,"url":"https://github.com/jensnesten/rust_bt","last_synced_at":"2025-04-09T19:04:17.023Z","repository":{"id":276364215,"uuid":"929069000","full_name":"jensnesten/rust_bt","owner":"jensnesten","description":"High performance, low-latency backtesting engine for testing quantitative trading strategies on historical and live data in Rust ","archived":false,"fork":false,"pushed_at":"2025-03-28T12:58:45.000Z","size":8633,"stargazers_count":1,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-28T13:42:44.731Z","etag":null,"topics":["algorithmic-trading","backtesting","backtesting-engine","high-frequency-trading","low-latency","quantitative-finance","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jensnesten.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-02-07T18:46:30.000Z","updated_at":"2025-03-28T12:58:49.000Z","dependencies_parsed_at":"2025-03-18T13:22:29.117Z","dependency_job_id":"26a14579-55de-48dd-b2c6-bf6384baa421","html_url":"https://github.com/jensnesten/rust_bt","commit_stats":null,"previous_names":["jensnesten/rust_bt"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jensnesten%2Frust_bt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jensnesten%2Frust_bt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jensnesten%2Frust_bt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jensnesten%2Frust_bt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jensnesten","download_url":"https://codeload.github.com/jensnesten/rust_bt/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248094986,"owners_count":21046770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algorithmic-trading","backtesting","backtesting-engine","high-frequency-trading","low-latency","quantitative-finance","rust"],"created_at":"2025-04-09T19:02:28.015Z","updated_at":"2025-04-09T19:04:17.015Z","avatar_url":"https://github.com/jensnesten.png","language":"Rust","funding_links":[],"categories":["Recently Updated","Python"],"sub_categories":["[Apr 09, 2025](/content/2025/04/09/README.md)","Trading \u0026 Backtesting"],"readme":"# rust_bt 🦀\n\nA high performance, low-latency backtesting engine for testing quantitative trading strategies in Rust. The engine is designed to be used in conjunction with a strategy that implements the `Strategy` trait. The strategy is responsible for making trading decisions based on the `Broker` struct. For backtesting on historical data OHLC data is required, and for live trading bid/ask data is required.\n\nIt's barebones by design, and is intended to be expanded upon to align with the relevant market microstructure and fit the type of products you intend to trade.  \n\n## Overview\n\n- High performance, low-latency \n- Flexible, modular design\n- Complete backtesting and live testing engine \n- Market microstructure simulation, including bid-ask spread, slippage, commissions, etc.\n- Detailed trade and position management, fractional orders\n- Contingent orders (SL/TP)\n- Margin and leverage management for complex instruments\n- Pairs trading, trading multiple instruments\n- Plotting and statistics\n\n\n## Components\n\n- **rust_core**: The central trading engine  \n  - Implements the core trading logic for backtesting and live trading\n  - Houses strategies, orderbook logic, and data handling\n  - Handles position management, margin and leverage\n\n\n- **rust_live**: The live trading interface  \n  - Connects the core trading logic to real-time data and execution\n  - Handles live data streaming \n  - To run the live trading engine, navigate to the `rust_live` directory and run `cargo run`\n\n- **rust_bt**: The backtesting interface  \n  - Connects the core trading logic to historical data and starts the backtest\n  - To run the backtest, navigate to the `rust_bt` directory and run `cargo run`\n\n\n- **rust_ml**: The machine learning interface  \n  - Loads models and runs inference on live and historical data\n  - Tools for converting scalers and pytorch models to rust\n\n\n### How It Works\n\nthe strategies are implemented in **rust_core**, but they are adapted to suit different operational environments:\n\n- **Backtesting Strategies**  \n  backtesting strategies use the standard engine types such as `Broker`, `OhlcData`, `Order`, and `Strategy`.\n  these types are designed to work with preloaded historical market data, allowing the simulation of trades over past time periods. the backtesting engine in **rust_bt** orchestrates the process, ensuring that trades are simulated in a controlled, time-sequential manner.\n\n- **Live Trading Strategies**  \n  live strategies are implemented with dedicated live engine types like `LiveBroker`, `LiveData`, `Order`, and `LiveStrategy`.  \n  These types are specifically designed to handle streaming market data and execute orders as market conditions evolve, ensuring that order placement, execution, and statistics (like pnl) update in real time.\n\nthis design ensures that while the core trading logic remains consistent in **rust_core**, each operational mode (backtest or live) uses the appropriate interface to manage data, process orders, and update trade statistics optimally. \n\n```mermaid\nflowchart TD\n    %% External Data Sources\n    HCSV[\"Historical Data CSV\"]:::external\n    LAPI[\"Live Data API\"]:::external\n\n    %% Backtesting Interface (rust_bt)\n    subgraph \"Backtesting Interface (rust_bt)\"\n        BT[\"Backtesting Interface\"]:::interface\n        BT_Main[\"Backtesting Main\"]:::interface\n    end\n\n    %% Live Trading Interface (rust_live)\n    subgraph \"Live Trading Interface (rust_live)\"\n        LIVE[\"Live Trading Interface\"]:::interface\n        Live_Stream[\"Live Data Stream\"]:::interface\n        Live_Server[\"Trading Server\"]:::interface\n        Live_Main[\"Live Main\"]:::interface\n    end\n\n    %% Machine Learning Module (rust_ml)\n    subgraph \"Machine Learning Module (rust_ml)\"\n        ML[\"Machine Learning Module\"]:::interface\n        ML_Inference[\"Inference Module\"]:::interface\n        ML_Models[\"Pretrained Models\"]:::interface\n        ML_Tools[\"Model Conversion Tools\"]:::interface\n        ML_Util[\"ML Utilities\"]:::interface\n    end\n\n    %% Core Trading Engine (rust_core)\n    subgraph \"Core Trading Engine (rust_core)\"\n        CORE[\"Core Engine\"]:::core\n        CORE_Data[\"Data Handling\"]:::core\n        CORE_Strategies[\"Strategies\"]:::core\n        CORE_Position[\"Position \u0026 Trade Management\"]:::core\n        CORE_Hedging[\"Hedging \u0026 Utilities\"]:::core\n    end\n\n    %% Data Flow Arrows\n    HCSV --\u003e|\"input\"| BT_Main\n    LAPI --\u003e|\"stream\"| Live_Stream\n\n    BT_Main --\u003e|\"simulate\"| BT\n    BT --\u003e|\"feeds\"| CORE\n\n    Live_Stream --\u003e|\"channels\"| LIVE\n    Live_Server --\u003e LIVE\n    Live_Main --\u003e LIVE\n    LIVE --\u003e|\"executes\"| CORE\n\n    ML_Inference --\u003e ML\n    ML_Models --\u003e ML\n    ML_Tools --\u003e ML\n    ML_Util --\u003e ML\n    ML --\u003e|\"signals\"| CORE\n\n    %% Internal Core Flow\n    CORE_Data --\u003e|\"processes\"| CORE_Strategies\n    CORE_Strategies --\u003e|\"executes\"| CORE_Position\n    CORE_Position --\u003e|\"manages\"| CORE_Hedging\n    %% Core engine central connection\n    CORE --- CORE_Data\n    CORE --- CORE_Strategies\n    CORE --- CORE_Position\n    CORE --- CORE_Hedging\n\n    %% Click Events for rust_core\n    click CORE \"https://github.com/jensnesten/rust_bt/tree/main/rust_core/\"\n    click CORE_Data \"https://github.com/jensnesten/rust_bt/blob/main/rust_core/src/data_handler/mod.rs\"\n    click CORE_Strategies \"https://github.com/jensnesten/rust_bt/tree/main/rust_core/src/strategies/\"\n    click CORE_Position \"https://github.com/jensnesten/rust_bt/blob/main/rust_core/src/position/mod.rs\"\n    click CORE_Hedging \"https://github.com/jensnesten/rust_bt/blob/main/rust_core/src/hedging/mod.rs\"\n\n    %% Click Events for rust_bt\n    click BT \"https://github.com/jensnesten/rust_bt/tree/main/rust_bt/\"\n    click BT_Main \"https://github.com/jensnesten/rust_bt/blob/main/rust_bt/src/main.rs\"\n    click HCSV \"https://github.com/jensnesten/rust_bt/blob/main/rust_bt/data/SP500_DJIA_2m_clean.csv\"\n\n    %% Click Events for rust_live\n    click LIVE \"https://github.com/jensnesten/rust_bt/tree/main/rust_live/\"\n    click Live_Stream \"https://github.com/jensnesten/rust_bt/blob/main/rust_live/src/stream.rs\"\n    click Live_Server \"https://github.com/jensnesten/rust_bt/blob/main/rust_live/src/server.rs\"\n    click Live_Main \"https://github.com/jensnesten/rust_bt/blob/main/rust_live/src/main.rs\"\n\n    %% Click Events for rust_ml\n    click ML \"https://github.com/jensnesten/rust_bt/tree/main/rust_ml/\"\n    click ML_Inference \"https://github.com/jensnesten/rust_bt/blob/main/rust_ml/src/inference/mod.rs\"\n    click ML_Models \"https://github.com/jensnesten/rust_bt/tree/main/rust_ml/src/models/\"\n    click ML_Tools \"https://github.com/jensnesten/rust_bt/tree/main/rust_ml/src/tools/\"\n    click ML_Util \"https://github.com/jensnesten/rust_bt/blob/main/rust_ml/src/scaler.rs\"\n\n    %% Styles\n    classDef core fill:#ffe6aa,stroke:#b58900,stroke-width:2px;\n    classDef interface fill:#cce5ff,stroke:#003366,stroke-width:2px;\n    classDef external fill:#d5f5e3,stroke:#27ae60,stroke-width:2px;\n```\n\n## Backtesting \n\nStrategies are implemented by creating a new struct in `rust_core/src/strategies/` that implements the `Strategy` trait:\n\n```rust\nuse crate::engine::{Broker, OhlcData, Order, Strategy};\npub struct MyStrategy;\n\nimpl Strategy for MyStrategy {\n    fn init(\u0026mut self, broker: \u0026mut Broker, data: \u0026OhlcData) {\n        // initialization can precompute indicators, etc..\n    }\n\n    fn next(\u0026mut self, broker: \u0026mut Broker, index: usize) {\n        // implement the strategy logic here\n    }\n}\n```\n\n### Opening a position\nThe `Broker` struct provides the following core functionality:\n\n- `new_order(order: Order)`: Places a new order\n- `closed_trades(trade: Trade)`: Closes a trade\n- `close_all_trades()`: Closes all trades\n- `cash += closed_trade.pnl()`: Updates the cash balance\n\nOrders are processed on every tick, and the `next` method is called on every tick.\n\nTo create a buy order we need to specify the size, and optionally the stop loss, take profit, limit, parent trade and instrument (to trade multiple instruments, default is 1).\n\n```rust\nlet order = Order {\n    size: trade.size,\n    sl: None,\n    tp: None,\n    limit: None,\n    stop: None,\n    parent_trade: None,\n    instrument: 1,\n};\nbroker.new_order(order);\nself.positions.register_position(trade.size); // track order with PositionManager (optional)\n```\n### PositionManager\nThe `PositionManager` provides a simple interface for handling all types of positions:\n\n```rust\nuse crate::position::PositionManager;\nlet mut positions = PositionManager::new(3); // allow max 3 positions per side (Long and Short)\npositions.register_position(trade.size); // register a long position\npositions.register_position(-trade.size); // register a short position\npositions.close_position(trade.size); // register closing a long position\npositions.close_position(-trade.size); // register closing a short position\n```\nThe `PositionManager` doesnt open or close positions, it simply tracks them in parallel for more granular control. This allows for more complex order management, which then enables us to implement more sophisticated hedging techniques in real-time. \n\n### Closing a position\nTo close a position we use the `Trade` struct. After closing each trade we need to update the cash balance and add the trade to the closed trades vector - alongside with updating the position manager if used:\n\n```rust\nlet trade = broker.trades.remove(0); //closes first position in trades vector\nlet closed_trade = Trade {\n    size: trade.size,\n    entry_price: trade.entry_price,\n    entry_index: trade.entry_index,\n    exit_price: Some(price),\n    exit_index: Some(index),\n    sl_order: trade.sl_order,\n    tp_order: trade.tp_order,\n    instrument: trade.instrument,\n};\nbroker.cash += closed_trade.pnl();\nbroker.closed_trades.push(closed_trade);\nself.positions.close_position(trade.size);\n```\n\nTo close all positions we need to delete each element in the `trades` vector and update our stats accordingly. We do this by calling the `close_all_trades` method from the `Broker` struct.\n\n### Plotting\n\nThe `backtest.plot()` function is used to plot the equity curve. It takes a slice of (naivedatetime, equity_value) tuples and an output file path.\n\n```rust\nif let Err(e) = backtest.plot(\"output_equity_plot.png\") {\n    eprintln!(\"error generating plot: {}\", e);\n}\n```\n\n## Live Trading \n\nStrategies are implemented in the same way as for backtesting, but the `next` method is called on every tick of the live data, where every 'tick' is a data event. Here we use the LiveStrategy trait:\n\n```rust\nuse crate::live_engine::{LiveBroker, LiveData, Order, LiveStrategy};\npub struct MyStrategy;\n\nimpl LiveStrategy for MyLiveStrategy {\n    fn init(\u0026mut self, broker: \u0026mut LiveBroker, data: \u0026LiveData) {\n        // Nothing to do here when trading live\n    }\n\n    fn next(\u0026mut self, broker: \u0026mut LiveBroker, index: usize) {\n        // implement the live strategy logic here\n    }\n}\n```\n\n### Streaming\n\nThe live engine is designed to handle streaming data from a live data source. The `LiveData` struct has been updated to offer a hybrid approach: it keeps a full history of ticks as well as a current snapshot for each instrument. Our backend is currently set up to receive data from Saxo Bank's SaxoOpenAPI, but this can be easily extended to other data sources by modifying `rust_live/src/stream.rs`. To run as is, you need developer access to the SaxoOpenAPI and an API token.\n\nEvery tick, which represents a snapshot for one instrument, is stored in a vector of `TickSnapshot`. Simultaneously, the latest tick for each instrument is maintained in a hashmap for quick access:\n  \n```rust\npub struct TickSnapshot {\n    pub instrument: String,\n    pub date: String,\n    pub ask: f64,\n    pub bid: f64,\n}\n\n/// Hybrid live data: keeps a full history of ticks as well as a current snapshot per instrument.\n#[derive(Clone, Debug, Serialize, Deserialize)]\npub struct LiveData {\n    pub ticks: Vec\u003cTickSnapshot\u003e,\n    pub current: HashMap\u003cString, TickSnapshot\u003e,\n}\n```\nHere the first parameter of the hashmap is a string corresponding to the symbol of the instrument.\n\nFor example, in `/rust_live/main.rs` you can define instrument symbols like this:\n\n```rust\n// create a channel for live data\nlet (tx, mut rx) = mpsc::unbounded_channel::\u003cLiveData\u003e();\n\nlet reference_id1 = \"US500\";\nlet uic1 = 4913;\nlet reference_id2 = \"DJIA\";\nlet uic2 = 4911;\n```\n\nThe `reference_id` strings (e.g., \"US500\", \"DJIA\") represent the symbols of the instruments, which the user can set to uniquely identify each data stream.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjensnesten%2Frust_bt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjensnesten%2Frust_bt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjensnesten%2Frust_bt/lists"}