use crate::analytics::performance::prepare_portfolio_data;
use crate::prelude::{
Constraints, Interval, ObjectiveFunction, Portfolio, Ticker, Transaction, KLINE,
};
use std::error::Error;
pub struct TickersBuilder {
tickers: Vec<String>,
start_date: String,
end_date: String,
interval: Interval,
benchmark_symbol: Option<String>,
confidence_level: f64,
risk_free_rate: f64,
tickers_data: Option<Vec<KLINE>>,
benchmark_data: Option<KLINE>,
}
impl Default for TickersBuilder {
fn default() -> Self {
Self::new()
}
}
impl TickersBuilder {
pub fn new() -> TickersBuilder {
TickersBuilder {
tickers: Vec::new(),
start_date: String::new(),
end_date: String::new(),
interval: Interval::OneDay,
benchmark_symbol: None,
confidence_level: 0.95,
risk_free_rate: 0.0,
tickers_data: None,
benchmark_data: None,
}
}
pub fn tickers(mut self, tickers: Vec<&str>) -> TickersBuilder {
self.tickers = tickers.iter().map(|x| x.to_string()).collect();
self
}
pub fn start_date(mut self, start_date: &str) -> TickersBuilder {
self.start_date = start_date.to_string();
self
}
pub fn end_date(mut self, end_date: &str) -> TickersBuilder {
self.end_date = end_date.to_string();
self
}
pub fn interval(mut self, interval: Interval) -> TickersBuilder {
self.interval = interval;
self
}
pub fn benchmark_symbol(mut self, benchmark_symbol: &str) -> TickersBuilder {
self.benchmark_symbol = Some(benchmark_symbol.to_string());
self
}
pub fn confidence_level(mut self, confidence_level: f64) -> TickersBuilder {
self.confidence_level = confidence_level;
self
}
pub fn risk_free_rate(mut self, risk_free_rate: f64) -> TickersBuilder {
self.risk_free_rate = risk_free_rate;
self
}
pub fn tickers_data(mut self, tickers_data: Option<Vec<KLINE>>) -> TickersBuilder {
self.tickers_data = tickers_data;
self
}
pub fn benchmark_data(mut self, benchmark_data: Option<KLINE>) -> TickersBuilder {
self.benchmark_data = benchmark_data;
self
}
pub fn build(self) -> Tickers {
let benchmark_ticker: Option<Ticker> =
if let Some(benchmark_data) = self.benchmark_data.clone() {
Some(
Ticker::builder()
.ticker_data(Some(benchmark_data.clone()))
.confidence_level(self.confidence_level)
.risk_free_rate(self.risk_free_rate)
.build(),
)
} else if let Some(ref sym) = self.benchmark_symbol {
Some(
Ticker::builder()
.ticker(sym)
.start_date(&self.start_date)
.end_date(&self.end_date)
.interval(self.interval)
.confidence_level(self.confidence_level)
.risk_free_rate(self.risk_free_rate)
.build(),
)
} else {
None
};
let benchmark_data_for_tickers = benchmark_ticker
.as_ref()
.and_then(|bt| bt.ticker_data.clone());
let tickers = if let Some(tickers_data) = self.tickers_data.clone() {
tickers_data
.clone()
.into_iter()
.map(|x| {
let mut builder = Ticker::builder()
.ticker_data(Some(x.clone()))
.confidence_level(self.confidence_level)
.risk_free_rate(self.risk_free_rate);
if let Some(ref bd) = benchmark_data_for_tickers {
builder = builder.benchmark_data(Some(bd.clone()));
}
builder.build()
})
.collect::<Vec<Ticker>>()
} else {
self.tickers
.clone()
.into_iter()
.map(|x| {
let mut builder = Ticker::builder()
.ticker(&x)
.start_date(&self.start_date)
.end_date(&self.end_date)
.interval(self.interval)
.confidence_level(self.confidence_level)
.risk_free_rate(self.risk_free_rate);
if let Some(ref sym) = self.benchmark_symbol {
builder = builder.benchmark_symbol(sym);
}
builder.build()
})
.collect::<Vec<Ticker>>()
};
let benchmark_symbol = benchmark_ticker.as_ref().map(|bt| bt.ticker.clone());
Tickers {
tickers: tickers.clone(),
start_date: tickers[0].start_date.clone(),
end_date: tickers[0].end_date.clone(),
interval: tickers[0].interval,
benchmark_symbol,
confidence_level: self.confidence_level,
risk_free_rate: self.risk_free_rate,
tickers_data: self.tickers_data,
benchmark_data: self.benchmark_data,
benchmark_ticker,
}
}
}
impl Tickers {
pub fn builder() -> TickersBuilder {
TickersBuilder::new()
}
pub async fn get_ticker(self, symbol: &str) -> Result<Ticker, Box<dyn Error>> {
let ticker = self.tickers.iter().find(|x| x.ticker == symbol);
match ticker {
Some(t) => Ok(t.clone()),
None => Err("Ticker not found".into()),
}
}
pub async fn optimize(
&self,
objective_function: Option<ObjectiveFunction>,
constraints: Option<Constraints>,
) -> Result<Portfolio, Box<dyn Error>> {
let data = prepare_portfolio_data(self, self.benchmark_ticker.as_ref()).await?;
let mut portfolio = Portfolio::new_raw(
self.clone(),
data,
objective_function.unwrap_or(ObjectiveFunction::MaxSharpe),
constraints,
None,
None,
);
portfolio.optimize()?;
Ok(portfolio)
}
pub async fn portfolio_performance_stats(
&self,
weights: Vec<f64>,
transactions: Option<Vec<Transaction>>,
) -> Result<Portfolio, Box<dyn Error>> {
let data = prepare_portfolio_data(self, self.benchmark_ticker.as_ref()).await?;
let mut portfolio = Portfolio::new_raw(
self.clone(),
data,
ObjectiveFunction::MaxSharpe,
None,
Some(weights),
transactions,
);
portfolio.performance_stats()?;
Ok(portfolio)
}
}
#[derive(Debug, Clone)]
pub struct Tickers {
pub tickers: Vec<Ticker>,
pub start_date: String,
pub end_date: String,
pub interval: Interval,
pub benchmark_symbol: Option<String>,
pub confidence_level: f64,
pub risk_free_rate: f64,
pub tickers_data: Option<Vec<KLINE>>,
pub benchmark_data: Option<KLINE>,
pub benchmark_ticker: Option<Ticker>,
}