use std::collections::HashMap;
use std::rc::Rc;
use crate::analysis::result::BarsResult;
use crate::asset::AssetSymbol;
use crate::config::strategy::StrategyProperties;
use crate::platform::account::Account;
use crate::platform::order::Order;
use crate::platform::position::Position;
use crate::platform::quote::Quote;
#[derive(Default, PartialEq)]
pub struct TacticState {
pub account: Account,
pub bar_analysis: Rc<HashMap<AssetSymbol, BarsResult>>,
pub open_orders: HashMap<AssetSymbol, Vec<Order>>,
pub positions: HashMap<AssetSymbol, Position>,
pub quotes: HashMap<AssetSymbol, Quote>,
pub strategy_properties: StrategyProperties,
pub all_assets: Vec<AssetSymbol>,
}
impl TacticState {
pub fn new(
account: Account,
bar_analysis: Rc<HashMap<AssetSymbol, BarsResult>>,
open_orders: HashMap<AssetSymbol, Vec<Order>>,
positions: HashMap<AssetSymbol, Position>,
quotes: HashMap<AssetSymbol, Quote>,
strategy_properties: StrategyProperties,
all_assets: Vec<AssetSymbol>,
) -> Self {
Self {
account,
bar_analysis: bar_analysis.clone(),
open_orders,
positions,
quotes,
strategy_properties,
all_assets,
}
}
pub fn open_order_value(&self, symbol: &AssetSymbol) -> f64 {
let ask_price = self
.quotes
.get(&symbol)
.map_or(0.0, |quote| quote.ask_price.clone());
self.open_orders.get(&symbol).map_or(0.0, |orders| {
orders
.iter()
.map(|order| order.estimated_value(ask_price.clone()))
.reduce(|a, b| a + b)
.unwrap_or(0.0)
})
}
#[cfg(test)]
pub fn fixture() -> Self {
let spy = AssetSymbol::new("SPY");
let vti = AssetSymbol::new("VTI");
let bar_analysis = HashMap::from([
(spy.clone(), BarsResult::fixture(spy.clone())),
(vti.clone(), BarsResult::fixture(vti.clone())),
]);
let open_orders = HashMap::from([
(spy.clone(), vec![Order::fixture(spy.clone())]),
(vti.clone(), vec![Order::fixture(vti.clone())]),
]);
let positions = HashMap::from([
(spy.clone(), Position::fixture(spy.clone())),
(vti.clone(), Position::fixture(vti.clone())),
]);
let quotes = HashMap::from([
(spy.clone(), Quote::fixture(spy.clone())),
(vti.clone(), Quote::fixture(vti.clone())),
]);
let strategy_properties = StrategyProperties {
name: "test".to_string(),
portfolio_percent: 100.0,
};
let all_assets = vec![spy, vti];
Self {
account: Account::fixture(),
bar_analysis: Rc::new(bar_analysis),
open_orders,
positions,
quotes,
strategy_properties,
all_assets,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn open_order_value() {
let state = TacticState::fixture();
let open_order_value = state.open_order_value(&AssetSymbol::new("SPY"));
assert_eq!(open_order_value, 200.0)
}
#[test]
fn open_order_value_no_orders() {
let state = TacticState {
open_orders: HashMap::new(),
..TacticState::fixture()
};
let open_order_value = state.open_order_value(&AssetSymbol::new("SPY"));
assert_eq!(open_order_value, 0.0)
}
#[test]
fn open_order_value_no_quotes() {
let state = TacticState {
open_orders: HashMap::new(),
..TacticState::fixture()
};
let open_order_value = state.open_order_value(&AssetSymbol::new("SPY"));
assert_eq!(open_order_value, 0.0)
}
#[test]
fn fixture_includes_all_assets() {
let state = TacticState::fixture();
assert_eq!(state.all_assets.len(), 2);
assert!(state.all_assets.contains(&AssetSymbol::new("SPY")));
assert!(state.all_assets.contains(&AssetSymbol::new("VTI")));
}
}