use crate::prelude::*;
use crate::std_prelude::*;
#[derive(Debug, Clone)]
pub struct SigOri {
pub t: dt,
pub ticker: Ticker,
pub target: NormHold,
pub price: f32,
}
pub trait IntoStatusVec {
fn into_status_vec(self) -> Vec<SigOri>;
}
impl IntoStatusVec for (Ticker, Arc<Vec<dt>>, Vec<NormHold>, av32) {
fn into_status_vec(self) -> Vec<SigOri> {
izip!(self.1.iter(), self.2, self.3.iter()).fold(vec![], |mut accu, (t, target, price)| {
let res = SigOri {
t: *t,
ticker: self.0,
target,
price: *price,
};
accu.push(res);
accu
})
}
}
impl IntoStatusVec for Vec<(Ticker, Arc<Vec<dt>>, Vec<NormHold>, av32)> {
fn into_status_vec(self) -> Vec<SigOri> {
let mut res: Vec<_> = self.into_iter().flat_map(|x| x.into_status_vec()).collect();
res.sort_by(|a, b| a.t.cmp(&b.t));
res
}
}
impl IntoStatusVec for DiStral<'_> {
fn into_status_vec(self) -> Vec<SigOri> {
let mut ptm_res_vec = self.calc(|distra: &DiStra| {
(
distra.stra.ident.ticker,
distra.di.t(),
distra
.di
.calc(&distra.stra.ptm)
.downcast_ref::<RwLock<PtmRes>>()
.unwrap()
.read()
.unwrap()
.0
.clone(),
distra.di.c(),
)
});
ptm_res_vec.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
let grp_ticker = Grp(ptm_res_vec.map(|x| x.0));
grp_ticker
.apply(&ptm_res_vec, |x| {
let ticker = x[0].0;
let time_union = x.iter().map(|v| v.1.to_vec()).collect_vec().union_vecs();
let hold_vec = x.iter().fold(
(
vec![NormHold::No; time_union.len()],
vec![None; time_union.len()],
),
|mut accu, (_, t, h, p)| {
let ri = Reindex::new(t, &time_union);
let h_vec = ri.reindex(h);
let p_vec = ri.reindex(p);
izip!(h_vec.into_iter(), p_vec.into_iter())
.enumerate()
.for_each(|(i, (h, p))| {
if let Some(c) = h {
accu.0[i] = accu.0[i].add_norm_hold(&c);
}
if let Some(c) = p {
accu.1[i] = Some(c);
}
});
accu
},
);
(
ticker,
time_union.pip(Arc::new),
hold_vec.0,
hold_vec
.1
.into_iter()
.map(|x| x.unwrap())
.collect_vec()
.pip(Arc::new),
)
})
.1
.into_status_vec()
}
}
#[derive(Debug, Clone)]
pub struct Status {
pub target: NormHold,
pub hold: NormHold,
pub price: f32,
}
impl Status {
fn net_num(&self) -> f32 {
self.target.to_num().abs() - self.hold.to_num().abs()
}
}
#[derive(Debug)]
pub struct Order {
pub open: NormOpen,
pub exit: NormExit,
}
impl Default for Order {
fn default() -> Self {
Order {
open: NormOpen::No,
exit: NormExit::No,
}
}
}
impl Order {
fn get_trade_fee(&self, price: f32, info: &TickerInfo) -> (f32, f32, f32) {
let open_num = self.open.to_num().abs();
let exit_num = self.exit.to_num().abs();
let comm = info.comm(price, open_num) + info.comm(price, exit_num);
let slip = info.slip(open_num) + info.slip(exit_num);
let money_trade = info.trade_money(open_num + exit_num, price);
(comm, slip, money_trade)
}
}
#[derive(Debug)]
pub struct Transaction {
pub t: dt,
pub ticker: Ticker,
pub pnl: f32,
pub profit: f32,
pub comm: f32,
pub slip: f32,
pub price: f32,
pub money_hold: f32,
pub money_trade: f32,
pub order: Order,
}
#[derive(Debug, Clone, Default)]
pub struct MoneyCut {
pub upper: f32,
pub hold: hm<Ticker, Status>,
}
impl MoneyCut {
pub fn get_hold_money(&self) -> f32 {
self.hold.iter().fold(0f32, |mut accu, x| {
accu += x.1.hold.to_num().abs() * x.0.info().pv * x.1.price;
accu
})
}
}
#[derive(Debug)]
pub struct HoldTrans(pub MoneyCut, pub Transaction);
pub trait BackTest {
fn calc_status(&mut self, data: &SigOri) -> HoldTrans;
fn calc_pnl(&mut self, data: &[SigOri]) -> PnlRes<dt> {
let mut t_vec = Vec::with_capacity(data.len());
let mut s_vec = init_a_matrix(data.len(), 8);
data.iter().for_each(|x| {
let hold_trans = self.calc_status(x);
let money_hold = hold_trans.0.get_hold_money();
t_vec.push(hold_trans.1.t);
s_vec[0].push(hold_trans.1.pnl);
s_vec[1].push(hold_trans.1.profit);
s_vec[2].push(money_hold);
s_vec[3].push(hold_trans.1.money_trade);
s_vec[4].push(hold_trans.1.comm + hold_trans.1.slip);
s_vec[5].push(hold_trans.1.comm);
s_vec[6].push(hold_trans.1.slip);
s_vec[7].push(0f32);
});
PnlRes(t_vec, s_vec)
}
}
impl BackTest for MoneyCut {
fn calc_status(&mut self, data: &SigOri) -> HoldTrans {
let money_in = self.get_hold_money();
let hold = self.hold.entry(data.ticker).or_insert(Status {
target: NormHold::No,
hold: NormHold::No,
price: 1f32,
});
hold.target = data.target.clone();
let left_money = self.upper - money_in;
let info = data.ticker.info();
let multi = info.pv * data.price;
let net_num = hold.net_num();
let net_money = net_num * multi;
let profit = hold.hold.to_num() * info.pv * (data.price - hold.price);
hold.price = data.price;
let order: Order =
if (hold.hold == data.target) || (left_money <= 0f32 && net_money >= 0f32) {
Order::default()
} else if left_money > 0f32 && net_money > 0f32 && left_money < net_money {
let sub_money = net_money - left_money;
let sub_num = sub_money / hold.price / info.pv;
let hold_adj = match data.target {
NormHold::Lo(x) => NormHold::Lo(x - sub_num),
NormHold::Sh(x) => NormHold::Sh(x - sub_num),
NormHold::No => panic!("what is going wrong?"),
};
let (open, exit) = hold_adj.sub_norm_hold(&hold.hold);
hold.hold = hold_adj;
Order { open, exit }
} else {
let (open, exit) = hold.target.sub_norm_hold(&hold.hold);
hold.hold = data.target.clone();
Order { open, exit }
};
let (comm, slip, money_trade) = order.get_trade_fee(data.price, &info);
let slip = slip * 0.3;
let transaction = Transaction {
t: data.t,
ticker: data.ticker,
pnl: profit - comm - slip,
profit,
comm,
slip,
price: data.price,
money_hold: hold.hold.to_num() * multi,
money_trade,
order,
};
HoldTrans(self.clone(), transaction)
}
}
#[derive(Debug, Clone)]
pub struct MoneyAdj {
pub money_cut: MoneyCut,
pub his_record: (Vec<da>, Vec<f32>),
pub back_window: usize,
pub rate: f32,
pub target_money: f32,
pub rate_record: v32,
pub ori_record: MoneyCut,
}
impl MoneyAdj {
fn update_rate(&mut self, data: &SigOri) {
self.ori_record.hold.insert(
data.ticker,
Status {
target: NormHold::No,
hold: data.target.clone(),
price: data.price,
},
);
let t_da = data.t.date();
let m = self.ori_record.get_hold_money();
if self.his_record.0.is_empty() {
self.his_record.0.push(t_da);
self.his_record.1.push(m);
self.rate = 1f32;
} else if self.his_record.0.last().unwrap() != &t_da {
self.rate = if self.his_record.1.len() <= 10 {
1f32
} else {
let dom = self.his_record.1.nlast(self.back_window).quantile(0.85);
if dom <= 10f32 {
1f32
} else {
self.target_money / dom
}
};
self.his_record.0.push(t_da);
self.his_record.1.push(m);
} else if self.his_record.1.last().unwrap() <= &m {
let l = self.his_record.1.len();
self.his_record.1[l - 1] = m;
}
self.rate_record.push(self.rate);
}
pub fn init(&mut self) {
self.money_cut.hold.clear();
self.rate_record.clear();
}
}
impl BackTest for MoneyAdj {
fn calc_status(&mut self, data: &SigOri) -> HoldTrans {
self.update_rate(data);
let data_new = SigOri {
target: &data.target * self.rate,
..data.clone()
};
self.money_cut.calc_status(&data_new)
}
}