mirror of
https://github.com/KhizarImran/backtestingfx.git
synced 2026-08-20 07:28:06 +00:00
made pyclass work for stats
This commit is contained in:
@@ -10,5 +10,6 @@ use pyo3::prelude::*;
|
|||||||
#[pymodule]
|
#[pymodule]
|
||||||
fn backtestingfx(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
fn backtestingfx(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
m.add_class::<types::Bar>()?;
|
m.add_class::<types::Bar>()?;
|
||||||
|
m.add_class::<stats::Stats>()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-17
@@ -1,18 +1,30 @@
|
|||||||
use crate::broker::Broker;
|
use crate::broker::Broker;
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
pub struct Stats {
|
pub struct Stats {
|
||||||
|
#[pyo3(get)]
|
||||||
pub initial_cash: f64,
|
pub initial_cash: f64,
|
||||||
|
#[pyo3(get)]
|
||||||
pub final_cash: f64,
|
pub final_cash: f64,
|
||||||
|
#[pyo3(get)]
|
||||||
pub total_return_pct: f64,
|
pub total_return_pct: f64,
|
||||||
|
#[pyo3(get)]
|
||||||
pub num_trades: usize,
|
pub num_trades: usize,
|
||||||
|
#[pyo3(get)]
|
||||||
pub num_wins: usize,
|
pub num_wins: usize,
|
||||||
|
#[pyo3(get)]
|
||||||
pub win_rate_pct: f64,
|
pub win_rate_pct: f64,
|
||||||
|
#[pyo3(get)]
|
||||||
pub avg_pnl: f64,
|
pub avg_pnl: f64,
|
||||||
|
#[pyo3(get)]
|
||||||
pub best_trade: f64,
|
pub best_trade: f64,
|
||||||
|
#[pyo3(get)]
|
||||||
pub worst_trade: f64,
|
pub worst_trade: f64,
|
||||||
|
#[pyo3(get)]
|
||||||
pub profit_factor: f64,
|
pub profit_factor: f64,
|
||||||
pub max_drawdown_pct: f64
|
#[pyo3(get)]
|
||||||
|
pub max_drawdown_pct: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn max_drawdown(equity_curve: &[f64]) -> f64 {
|
fn max_drawdown(equity_curve: &[f64]) -> f64 {
|
||||||
@@ -43,23 +55,44 @@ use crate::broker::Broker;
|
|||||||
let num_wins = broker.trade_history.iter().filter(|t| t.pnl > 0.0).count();
|
let num_wins = broker.trade_history.iter().filter(|t| t.pnl > 0.0).count();
|
||||||
let win_rate_pct = if num_trades > 0 {
|
let win_rate_pct = if num_trades > 0 {
|
||||||
num_wins as f64 / num_trades as f64 * 100.0
|
num_wins as f64 / num_trades as f64 * 100.0
|
||||||
} else { 0.0 };
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
let avg_pnl = if num_trades > 0 {
|
let avg_pnl = if num_trades > 0 {
|
||||||
broker.trade_history.iter().map(|t| t.pnl).sum::<f64>() / num_trades as f64
|
broker.trade_history.iter().map(|t| t.pnl).sum::<f64>() / num_trades as f64
|
||||||
} else { 0.0 };
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
let best_trade = broker.trade_history.iter().map(|t| t.pnl)
|
let best_trade = broker
|
||||||
|
.trade_history
|
||||||
|
.iter()
|
||||||
|
.map(|t| t.pnl)
|
||||||
.fold(f64::NEG_INFINITY, f64::max);
|
.fold(f64::NEG_INFINITY, f64::max);
|
||||||
let worst_trade = broker.trade_history.iter().map(|t| t.pnl)
|
let worst_trade = broker
|
||||||
|
.trade_history
|
||||||
|
.iter()
|
||||||
|
.map(|t| t.pnl)
|
||||||
.fold(f64::INFINITY, f64::min);
|
.fold(f64::INFINITY, f64::min);
|
||||||
|
|
||||||
let gross_profit: f64 = broker.trade_history.iter()
|
let gross_profit: f64 = broker
|
||||||
.filter(|t| t.pnl > 0.0).map(|t| t.pnl).sum();
|
.trade_history
|
||||||
let gross_loss: f64 = broker.trade_history.iter()
|
.iter()
|
||||||
.filter(|t| t.pnl < 0.0).map(|t| t.pnl.abs()).sum();
|
.filter(|t| t.pnl > 0.0)
|
||||||
let profit_factor = if gross_loss > 0.0 { gross_profit / gross_loss } else {
|
.map(|t| t.pnl)
|
||||||
f64::INFINITY };
|
.sum();
|
||||||
|
let gross_loss: f64 = broker
|
||||||
|
.trade_history
|
||||||
|
.iter()
|
||||||
|
.filter(|t| t.pnl < 0.0)
|
||||||
|
.map(|t| t.pnl.abs())
|
||||||
|
.sum();
|
||||||
|
let profit_factor = if gross_loss > 0.0 {
|
||||||
|
gross_profit / gross_loss
|
||||||
|
} else {
|
||||||
|
f64::INFINITY
|
||||||
|
};
|
||||||
let max_drawdown_pct = max_drawdown(equity_curve);
|
let max_drawdown_pct = max_drawdown(equity_curve);
|
||||||
|
|
||||||
Stats {
|
Stats {
|
||||||
@@ -73,14 +106,15 @@ use crate::broker::Broker;
|
|||||||
best_trade: if num_trades > 0 { best_trade } else { 0.0 },
|
best_trade: if num_trades > 0 { best_trade } else { 0.0 },
|
||||||
worst_trade: if num_trades > 0 { worst_trade } else { 0.0 },
|
worst_trade: if num_trades > 0 { worst_trade } else { 0.0 },
|
||||||
profit_factor,
|
profit_factor,
|
||||||
max_drawdown_pct
|
max_drawdown_pct,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Stats {
|
impl std::fmt::Display for Stats {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f,
|
write!(
|
||||||
|
f,
|
||||||
"--- Backtest Results ---\n\
|
"--- Backtest Results ---\n\
|
||||||
Initial Cash: {:.2}\n\
|
Initial Cash: {:.2}\n\
|
||||||
Final Cash: {:.2}\n\
|
Final Cash: {:.2}\n\
|
||||||
@@ -92,9 +126,15 @@ use crate::broker::Broker;
|
|||||||
Worst Trade: {:.5}\n\
|
Worst Trade: {:.5}\n\
|
||||||
Profit Factor: {:.2}\n\
|
Profit Factor: {:.2}\n\
|
||||||
Max Drawdown: {:.2}%",
|
Max Drawdown: {:.2}%",
|
||||||
self.initial_cash, self.final_cash, self.total_return_pct,
|
self.initial_cash,
|
||||||
self.num_trades, self.win_rate_pct, self.avg_pnl,
|
self.final_cash,
|
||||||
self.best_trade, self.worst_trade, self.profit_factor,
|
self.total_return_pct,
|
||||||
|
self.num_trades,
|
||||||
|
self.win_rate_pct,
|
||||||
|
self.avg_pnl,
|
||||||
|
self.best_trade,
|
||||||
|
self.worst_trade,
|
||||||
|
self.profit_factor,
|
||||||
self.max_drawdown_pct
|
self.max_drawdown_pct
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user