mirror of
https://github.com/KhizarImran/backtestingfx.git
synced 2026-08-12 19:48:05 +00:00
added lot size
This commit is contained in:
+20
-2
@@ -48,12 +48,23 @@ class _Adapter:
|
|||||||
|
|
||||||
|
|
||||||
class Backtest:
|
class Backtest:
|
||||||
def __init__(self, df, strategy_class, cash=10000.0, commission=0.0, spread=0.0):
|
def __init__(
|
||||||
|
self,
|
||||||
|
df,
|
||||||
|
strategy_class,
|
||||||
|
cash=10000.0,
|
||||||
|
commission=0.0,
|
||||||
|
spread=0.0,
|
||||||
|
contract_size=100000.0,
|
||||||
|
quote_to_account=1.0,
|
||||||
|
):
|
||||||
self._df = df
|
self._df = df
|
||||||
self._strategy_class = strategy_class
|
self._strategy_class = strategy_class
|
||||||
self._cash = cash
|
self._cash = cash
|
||||||
self._commission = commission
|
self._commission = commission
|
||||||
self._spread = spread
|
self._spread = spread
|
||||||
|
self._contract_size = contract_size
|
||||||
|
self._quote_to_account = quote_to_account
|
||||||
|
|
||||||
def _to_bars(self):
|
def _to_bars(self):
|
||||||
bars = []
|
bars = []
|
||||||
@@ -77,6 +88,13 @@ class Backtest:
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
bars = self._to_bars()
|
bars = self._to_bars()
|
||||||
engine = _rust.Engine(bars, self._cash, self._commission, self._spread) # type: ignore
|
engine = _rust.Engine( # type: ignore
|
||||||
|
bars,
|
||||||
|
self._cash,
|
||||||
|
self._commission,
|
||||||
|
self._spread,
|
||||||
|
self._contract_size,
|
||||||
|
self._quote_to_account,
|
||||||
|
)
|
||||||
strategy = self._strategy_class()
|
strategy = self._strategy_class()
|
||||||
return engine.run(_Adapter(strategy))
|
return engine.run(_Adapter(strategy))
|
||||||
|
|||||||
+93
-34
@@ -1,15 +1,16 @@
|
|||||||
|
use crate::types::{Bar, Position, Trade};
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use crate::types::{Position, Trade, Bar};
|
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
pub struct Broker{
|
pub struct Broker {
|
||||||
pub cash: f64,
|
pub cash: f64,
|
||||||
pub initial_cash: f64,
|
pub initial_cash: f64,
|
||||||
pub positions: Vec<Position>,
|
pub positions: Vec<Position>,
|
||||||
pub trade_history: Vec<Trade>,
|
pub trade_history: Vec<Trade>,
|
||||||
pub commission: f64,
|
pub commission: f64,
|
||||||
pub spread: f64
|
pub spread: f64,
|
||||||
|
pub contract_size: f64,
|
||||||
|
pub quote_to_account: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rust-internal only, not exposed to Python
|
// Rust-internal only, not exposed to Python
|
||||||
@@ -19,18 +20,26 @@ impl Broker {
|
|||||||
while i < self.positions.len() {
|
while i < self.positions.len() {
|
||||||
let fill = {
|
let fill = {
|
||||||
let p = &self.positions[i];
|
let p = &self.positions[i];
|
||||||
if p.is_long{
|
if p.is_long {
|
||||||
let sl_hit = p.stop_loss.map_or(false, |sl| bar.low <= sl);
|
let sl_hit = p.stop_loss.map_or(false, |sl| bar.low <= sl);
|
||||||
let tp_hit = p.take_profit.map_or(false, |tp| bar.high >= tp);
|
let tp_hit = p.take_profit.map_or(false, |tp| bar.high >= tp);
|
||||||
if sl_hit {p.stop_loss}
|
if sl_hit {
|
||||||
else if tp_hit {p.take_profit}
|
p.stop_loss
|
||||||
else {None}
|
} else if tp_hit {
|
||||||
|
p.take_profit
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let sl_hit = p.stop_loss.map_or(false, |sl| bar.high >= sl);
|
let sl_hit = p.stop_loss.map_or(false, |sl| bar.high >= sl);
|
||||||
let tp_hit = p.take_profit.map_or(false, |tp| bar.low <= tp);
|
let tp_hit = p.take_profit.map_or(false, |tp| bar.low <= tp);
|
||||||
if sl_hit {p.stop_loss}
|
if sl_hit {
|
||||||
else if tp_hit {p.take_profit}
|
p.stop_loss
|
||||||
else {None}
|
} else if tp_hit {
|
||||||
|
p.take_profit
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,9 +52,15 @@ impl Broker {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let pnl = if position.is_long {
|
let pnl = if position.is_long {
|
||||||
(close_price - position.entry_price) * position.lot_size
|
(close_price - position.entry_price)
|
||||||
|
* position.lot_size
|
||||||
|
* self.contract_size
|
||||||
|
* self.quote_to_account
|
||||||
} else {
|
} else {
|
||||||
(position.entry_price - close_price) * position.lot_size
|
(position.entry_price - close_price)
|
||||||
|
* position.lot_size
|
||||||
|
* self.contract_size
|
||||||
|
* self.quote_to_account
|
||||||
};
|
};
|
||||||
self.cash += pnl;
|
self.cash += pnl;
|
||||||
self.trade_history.push(Trade {
|
self.trade_history.push(Trade {
|
||||||
@@ -55,10 +70,10 @@ impl Broker {
|
|||||||
is_long: position.is_long,
|
is_long: position.is_long,
|
||||||
pnl,
|
pnl,
|
||||||
entry_timestamp: position.entry_timestamp,
|
entry_timestamp: position.entry_timestamp,
|
||||||
exit_timestamp: bar.timestamp
|
exit_timestamp: bar.timestamp,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
i+=1;
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,18 +82,33 @@ impl Broker {
|
|||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl Broker {
|
impl Broker {
|
||||||
#[new]
|
#[new]
|
||||||
pub fn new(initial_cash: f64, commission: f64, spread: f64) -> Self {
|
pub fn new(
|
||||||
|
initial_cash: f64,
|
||||||
|
commission: f64,
|
||||||
|
spread: f64,
|
||||||
|
contract_size: f64,
|
||||||
|
quote_to_account: f64,
|
||||||
|
) -> Self {
|
||||||
Broker {
|
Broker {
|
||||||
cash: initial_cash,
|
cash: initial_cash,
|
||||||
initial_cash,
|
initial_cash,
|
||||||
positions: Vec::new(),
|
positions: Vec::new(),
|
||||||
trade_history: Vec::new(),
|
trade_history: Vec::new(),
|
||||||
commission,
|
commission,
|
||||||
spread
|
spread,
|
||||||
|
contract_size,
|
||||||
|
quote_to_account,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buy(&mut self, price: f64, lot_size: f64, timestamp: i64, stop_loss: Option<f64>, take_profit: Option<f64>) {
|
pub fn buy(
|
||||||
|
&mut self,
|
||||||
|
price: f64,
|
||||||
|
lot_size: f64,
|
||||||
|
timestamp: i64,
|
||||||
|
stop_loss: Option<f64>,
|
||||||
|
take_profit: Option<f64>,
|
||||||
|
) {
|
||||||
let fill_price = price + self.spread;
|
let fill_price = price + self.spread;
|
||||||
self.cash -= self.commission * lot_size;
|
self.cash -= self.commission * lot_size;
|
||||||
self.positions.push(Position {
|
self.positions.push(Position {
|
||||||
@@ -88,11 +118,18 @@ impl Broker {
|
|||||||
is_long: true,
|
is_long: true,
|
||||||
entry_timestamp: timestamp,
|
entry_timestamp: timestamp,
|
||||||
stop_loss,
|
stop_loss,
|
||||||
take_profit
|
take_profit,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sell(&mut self, price: f64, lot_size: f64, timestamp: i64, stop_loss: Option<f64>, take_profit: Option<f64>) {
|
pub fn sell(
|
||||||
|
&mut self,
|
||||||
|
price: f64,
|
||||||
|
lot_size: f64,
|
||||||
|
timestamp: i64,
|
||||||
|
stop_loss: Option<f64>,
|
||||||
|
take_profit: Option<f64>,
|
||||||
|
) {
|
||||||
let fill_price = price - self.spread;
|
let fill_price = price - self.spread;
|
||||||
self.cash -= self.commission * lot_size;
|
self.cash -= self.commission * lot_size;
|
||||||
self.positions.push(Position {
|
self.positions.push(Position {
|
||||||
@@ -102,7 +139,7 @@ impl Broker {
|
|||||||
is_long: false,
|
is_long: false,
|
||||||
entry_timestamp: timestamp,
|
entry_timestamp: timestamp,
|
||||||
stop_loss,
|
stop_loss,
|
||||||
take_profit
|
take_profit,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,9 +154,15 @@ impl Broker {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let pnl = if position.is_long {
|
let pnl = if position.is_long {
|
||||||
(close_price - position.entry_price) * position.lot_size
|
(close_price - position.entry_price)
|
||||||
|
* position.lot_size
|
||||||
|
* self.contract_size
|
||||||
|
* self.quote_to_account
|
||||||
} else {
|
} else {
|
||||||
(position.entry_price - close_price) * position.lot_size
|
(position.entry_price - close_price)
|
||||||
|
* position.lot_size
|
||||||
|
* self.contract_size
|
||||||
|
* self.quote_to_account
|
||||||
};
|
};
|
||||||
|
|
||||||
self.cash += pnl;
|
self.cash += pnl;
|
||||||
@@ -130,7 +173,7 @@ impl Broker {
|
|||||||
pnl,
|
pnl,
|
||||||
entry_timestamp: position.entry_timestamp,
|
entry_timestamp: position.entry_timestamp,
|
||||||
exit_timestamp: timestamp,
|
exit_timestamp: timestamp,
|
||||||
exit_price: close_price
|
exit_price: close_price,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,9 +187,15 @@ impl Broker {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let pnl = if position.is_long {
|
let pnl = if position.is_long {
|
||||||
(close_price - position.entry_price) * position.lot_size
|
(close_price - position.entry_price)
|
||||||
|
* position.lot_size
|
||||||
|
* self.contract_size
|
||||||
|
* self.quote_to_account
|
||||||
} else {
|
} else {
|
||||||
(position.entry_price - close_price) * position.lot_size
|
(position.entry_price - close_price)
|
||||||
|
* position.lot_size
|
||||||
|
* self.contract_size
|
||||||
|
* self.quote_to_account
|
||||||
};
|
};
|
||||||
self.cash += pnl;
|
self.cash += pnl;
|
||||||
self.trade_history.push(Trade {
|
self.trade_history.push(Trade {
|
||||||
@@ -156,19 +205,29 @@ impl Broker {
|
|||||||
pnl,
|
pnl,
|
||||||
entry_timestamp: position.entry_timestamp,
|
entry_timestamp: position.entry_timestamp,
|
||||||
exit_timestamp: timestamp,
|
exit_timestamp: timestamp,
|
||||||
exit_price: close_price
|
exit_price: close_price,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn equity(&self, current_price: f64) -> f64 {
|
pub fn equity(&self, current_price: f64) -> f64 {
|
||||||
let unrealized: f64 = self.positions.iter().map(|p| {
|
let unrealized: f64 = self
|
||||||
if p.is_long {
|
.positions
|
||||||
(current_price - p.entry_price) * p.lot_size
|
.iter()
|
||||||
} else {
|
.map(|p| {
|
||||||
(p.entry_price - current_price) * p.lot_size
|
if p.is_long {
|
||||||
}
|
(current_price - p.entry_price)
|
||||||
}).sum();
|
* p.lot_size
|
||||||
|
* self.contract_size
|
||||||
|
* self.quote_to_account
|
||||||
|
} else {
|
||||||
|
(p.entry_price - current_price)
|
||||||
|
* p.lot_size
|
||||||
|
* self.contract_size
|
||||||
|
* self.quote_to_account
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
|
|
||||||
self.cash + unrealized
|
self.cash + unrealized
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-2
@@ -29,10 +29,23 @@ impl Engine {
|
|||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl Engine {
|
impl Engine {
|
||||||
#[new]
|
#[new]
|
||||||
pub fn new(data: Vec<Bar>, initial_cash: f64, commission: f64, spread: f64) -> Self {
|
pub fn new(
|
||||||
|
data: Vec<Bar>,
|
||||||
|
initial_cash: f64,
|
||||||
|
commission: f64,
|
||||||
|
spread: f64,
|
||||||
|
contract_size: f64,
|
||||||
|
quote_to_account: f64,
|
||||||
|
) -> Self {
|
||||||
Engine {
|
Engine {
|
||||||
data,
|
data,
|
||||||
broker: Broker::new(initial_cash, commission, spread),
|
broker: Broker::new(
|
||||||
|
initial_cash,
|
||||||
|
commission,
|
||||||
|
spread,
|
||||||
|
contract_size,
|
||||||
|
quote_to_account,
|
||||||
|
),
|
||||||
equity_curve: Vec::new(),
|
equity_curve: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,6 +67,8 @@ impl Engine {
|
|||||||
self.broker.initial_cash,
|
self.broker.initial_cash,
|
||||||
self.broker.commission,
|
self.broker.commission,
|
||||||
self.broker.spread,
|
self.broker.spread,
|
||||||
|
self.broker.contract_size,
|
||||||
|
self.broker.quote_to_account,
|
||||||
),
|
),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user