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}