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