bts_rs/lib.rs
1//! # BTS: BackTest Strategy for Trading Algorithms
2//!
3//! **BTS** is a high-performance Rust library for backtesting trading strategies on candlestick (OHLCV) data.
4//! It is designed for **speed, flexibility, and accuracy**, making it ideal for both **retail traders** and **algorithmic trading developers**.
5//!
6//! ## Why BTS?
7//!
8//! - **Optimized for Performance**: Uses O(1) operations on orders/positions, and parallel processing for optimization tasks.
9//! - **Technical Analysis Ready**: Seamlessly integrates with popular indicators crates for 100+ indicators (EMA, MACD, RSI, etc.).
10//! - **Risk Management**: Supports stop-loss, take-profit, and trailing stops with configurable rules.
11//! - **Realistic Simulations**: Models slippage, fees, and latency for accurate backtesting.
12//! - **Extensible**: Add custom indicators, strategies, or data sources with minimal effort.
13//!
14//! ## Core Components
15//!
16//! | Component | Description |
17//! |---------------- |----------------------------------------------------------------------------------|
18//! | **`Metrics`** | Calculates performance metrics: P&L, drawdown, Sharpe ratio, win rate, and more. |
19//! | **`Optimizer`** | Calculates bests parameters *(indicators, RR, etc...)*. |
20//! | **`Draw`** | Draw candlestick data, balance and metrics to a *SVG* or *PNG* file. |
21//! | **`Backtest`** | The engine that simulates strategy execution over historical data. |
22//!
23//! ## Features
24//!
25//! ### 1. **Technical Indicators**
26//! - Compatible with indicators crates like the [`ta`](https://crates.io/crates/ta) crate for 100+ additional indicators.
27//!
28//! ### 2. **Order Types & Exit Rules**
29//!
30//! | Order Type | Description |
31//! |-----------------------------|---------------------------------------------------------------|
32//! | **Market Order** | Executes immediately at the current price. |
33//! | **Limit Order** | Executes only at a specified price or better. |
34//! | **Take-Profit** | Closes the position when a target price is reached. |
35//! | **Stop-Loss** | Closes the position to limit losses. |
36//! | **Trailing Stop** | Dynamically adjusts the stop price based on market movements. |
37//! | **Take-Profit + Stop-Loss** | Combines both rules for risk management. |
38//!
39//! ### 3. **Performance Metrics**
40//!
41//! | Metric | Description |
42//! |----------------------|--------------------------------------------------------|
43//! | **Max Drawdown** | Largest peak-to-trough decline in account balance (%). |
44//! | **Profit Factor** | Ratio of gross profits to gross losses. |
45//! | **Sharpe Ratio** | Risk-adjusted return (higher = better). |
46//! | **Win Rate** | Percentage of winning trades. |
47//!
48//! ### 4. **Optimization Tools**
49//!
50//! - **Parallelize**: Optimize strategy parameters (e.g., EMA periods) using multi-threading.
51//!
52//! ## Getting Started
53//!
54//! ### 1. Add BTS to your project:
55//! ```toml
56//! [dependencies]
57//! bts_rs = "1.0.11"
58//! ta = "0.5.0" # Optional: For technical analysis indicators
59//! ```
60//!
61//! ### 2. Run a Simple Backtest:
62//! ```rust
63//! use std::sync::Arc;
64//!
65//! use bts_rs::prelude::*;
66//! use chrono::{DateTime, Duration};
67//!
68//! let candle = CandleBuilder::builder()
69//! .open(100.0)
70//! .high(110.0)
71//! .low(95.0)
72//! .close(105.0)
73//! .volume(1.0)
74//! .bid(0.5)
75//! .open_time(DateTime::default())
76//! .close_time(DateTime::default() + Duration::days(1))
77//! .build()
78//! .unwrap();
79//!
80//! // Initialize backtest with \$10,000
81//! let mut backtest = Backtest::new(Arc::from_iter(vec![candle]), 10_000.0, None).unwrap();
82//!
83//! // Execute a market buy order
84//! backtest
85//! .run(|bt, candle| {
86//! let order: Order = (OrderType::Market(102.0), 1.0, OrderSide::Buy).into();
87//! bt.place_order(candle, order).unwrap();
88//! // Close the position at \$104.0
89//! if let Some(position) = bt.positions().last().cloned() {
90//! bt.close_position(candle, &position, 104.0, true).unwrap();
91//! }
92//! Ok(())
93//! })
94//! .unwrap();
95//!
96//! // Print performance metrics
97//! #[cfg(feature = "metrics")]
98//! {
99//! let metrics = Metrics::from(&backtest);
100//! println!("{}", metrics);
101//! }
102//! ```
103//!
104//! ### Output:
105//!
106//! ```bash
107//! === Backtest Metrics ===
108//! Initial Balance: 10000.00
109//! Final Balance: 10018.00
110//! Profit & Loss (P&L): 0.00
111//! Fees paid: 0.00
112//!
113//! Max Drawdown: 0.20%
114//! Profit Factor: 2.00
115//! Sharpe Ratio: 1.50
116//! Win Rate: 100.00%
117//! ```
118//!
119//! ## Use Cases
120//!
121//! - **Retail Traders**: Test manual strategies before risking real capital.
122//! - **Algo Developers**: Build and optimize automated trading systems.
123//! - **Quant Researchers**: Backtest statistical arbitrage or machine learning models.
124//! - **Educational**: Teach trading concepts with a hands-on tool.
125//!
126//! ## Integrations
127//!
128//! | Crate | Purpose |
129//! |-------------------------------------------------|-------------------------------------------------------------------|
130//! | [`rayon`](https://crates.io/crates/rayon) | Parallel processing for optimization. |
131//! | [`serde`](https://crates.io/crates/serde) | Serialize/deserialize backtest results. |
132//! | [`plotters`](https://crates.io/crates/plotters) | Visualize market candlesticks data, equity curves and indicators. |
133//!
134//! ## Error Handling
135//!
136//! BTS uses custom error types to handle:
137//! - Insufficient balance.
138//! - Invalid order types.
139//! - Missing data (e.g., candles, positions) and more.
140//!
141//! Example:
142//! ```rust
143//! use std::sync::Arc;
144//!
145//! use bts_rs::prelude::*;
146//! use chrono::{DateTime, Duration};
147//!
148//! let candle = CandleBuilder::builder()
149//! .open(100.0)
150//! .high(110.0)
151//! .low(95.0)
152//! .close(105.0)
153//! .volume(1.0)
154//! .bid(0.5)
155//! .open_time(DateTime::default())
156//! .close_time(DateTime::default() + Duration::days(1))
157//! .build()
158//! .unwrap();
159//!
160//! // Initialize backtest with \$10,000
161//! let mut backtest = Backtest::new(Arc::from_iter(vec![candle]), 10_000.0, None).unwrap();
162//!
163//! // Execute a market buy order
164//! backtest
165//! .run(|bt, candle| {
166//! let order: Order = (OrderType::Market(102.0), 1.0, OrderSide::Buy).into();
167//! match bt.place_order(candle, order) {
168//! Ok(_) => println!("Order in the pool!"),
169//! Err(_) => eprintln!("Error to place an order")
170//! }
171//! Ok(())
172//! })
173//! .unwrap();
174//! ```
175//!
176//! ## Contributing
177//!
178//! Contributions are welcome! See [`CONTRIBUTING.md`](https://github.com/raonagos/bts-rs/blob/master/CONTRIBUTING.md).
179//!
180//! ## License
181//!
182//! The project is licensed under the [`MIT`](https://github.com/raonagos/bts-rs/blob/master/LICENSE).
183#![warn(missing_docs)]
184
185/// Core trading engine components: orders, positions, wallet, and backtest logic.
186pub mod engine;
187
188/// Error types for the library.
189pub mod errors;
190
191/// Utility functions and helpers.
192mod utils;
193
194/// Performance metrics: drawdown, Sharpe ratio, win rate, etc.
195#[cfg(feature = "metrics")]
196pub mod metrics;
197
198/// Strategy parameter optimization.
199#[cfg(feature = "optimizer")]
200pub mod optimizer;
201
202/// Module for visualizing backtest results and candle charts.
203#[cfg(feature = "draws")]
204pub mod draws;
205
206/// Re-exports of commonly used types and traits for convenience.
207pub mod prelude {
208 pub use super::PercentCalculus;
209 pub use crate::engine::*;
210
211 #[cfg(feature = "metrics")]
212 pub use crate::metrics::*;
213
214 #[cfg(feature = "optimizer")]
215 pub use crate::optimizer::*;
216
217 #[cfg(feature = "draws")]
218 pub use crate::draws::*;
219}
220
221use std::ops::{Add, Div, Mul, Sub};
222
223/// Trait for performing percentage-based calculations (human readable).
224///
225/// This trait provides methods to add, subtract, and calculate percentages
226/// for numeric types, enabling common financial calculations.
227pub trait PercentCalculus<Rhs = Self> {
228 /// Adds a percentage to the value.
229 ///
230 /// ### Arguments
231 /// * `rhs` - The percentage to add (e.g., 10.0 for 10%).
232 ///
233 /// ### Returns
234 /// The value increased by the given percentage.
235 fn addpercent(self, rhs: Rhs) -> Self;
236
237 /// Subtracts a percentage from the value.
238 ///
239 /// ### Arguments
240 /// * `rhs` - The percentage to subtract (e.g., 10.0 for 10%).
241 ///
242 /// ### Returns
243 /// The value decreased by the given percentage.
244 fn subpercent(self, rhs: Rhs) -> Self;
245
246 /// Calculates the absolute value of a percentage.
247 ///
248 /// ### Arguments
249 /// * `rhs` - The percentage to calculate (e.g., 10.0 for 10%).
250 ///
251 /// ### Returns
252 /// The absolute value of the given percentage.
253 fn how_many(self, rhs: Rhs) -> Self;
254
255 /// Calculates the percentage change between two values.
256 ///
257 /// ### Arguments
258 /// * `value` - The value to compare with.
259 ///
260 /// ### Returns
261 /// The percentage change from the original value to the input value.
262 fn change(self, value: Rhs) -> Self;
263}
264
265impl PercentCalculus for f64 {
266 fn addpercent(self, percent: Self) -> Self {
267 self.add(self.mul(percent.div(100.0)))
268 }
269
270 fn subpercent(self, percent: Self) -> Self {
271 self.sub(self.mul(percent.div(100.0)))
272 }
273
274 fn how_many(self, percent: Self) -> Self {
275 percent.mul(self.div(100.0))
276 }
277
278 fn change(self, value: Self) -> Self {
279 value.sub(self).div(self).mul(100.0)
280 }
281}
282
283#[cfg(test)]
284mod percent {
285 use super::*;
286
287 #[test]
288 fn add() {
289 assert_eq!(110.0, 100.0.addpercent(10.0))
290 }
291
292 #[test]
293 fn sub() {
294 assert_eq!(90.0, 100.0.subpercent(10.0))
295 }
296
297 #[test]
298 fn how_many() {
299 assert_eq!(10.0, 100.0.how_many(10.0))
300 }
301
302 #[test]
303 fn change() {
304 assert_eq!(10.0, 100.0.change(110.0))
305 }
306}