use crate::analysis::AssetAnalyzer;
use crate::asset::AssetSymbol;
use crate::config::platform::PlatformType;
use crate::error::GreedError;
use crate::output_format::OutputFormat;
use crate::platform::args::PlatformArgs;
use crate::platform::quote::Quote;
use crate::run::{GreedRunner, GreedRunnerArgs};
use chrono::{DateTime, Utc};
use std::ops::Range;
mod analysis;
mod assert;
pub mod asset;
mod bool;
pub mod config;
mod date;
mod db;
mod enum_display;
pub mod error;
mod fixture;
mod float;
pub mod history_range;
mod num;
pub mod output_format;
mod pager;
pub mod platform;
pub mod run;
mod statistics;
mod strategy;
mod tactic;
pub mod template;
mod trading_days;
pub async fn greed_loop(args: GreedRunnerArgs) -> Result<(), GreedError> {
let connection = db::bootstrap()?;
let runner = GreedRunner::from_args(args, connection).await?;
runner.run_loop().await;
Ok(())
}
pub async fn fetch_quote(
assets: &[AssetSymbol],
platform_type: &PlatformType,
platform_args: PlatformArgs,
) -> Result<(), GreedError> {
print_price_explainer();
let platform = platform::for_type(platform_type, platform_args)?;
let quotes = platform.latest_quotes(assets).await?;
for quote in quotes {
println!("{}", quote)
}
Ok(())
}
pub async fn fetch_recent_orders(
platform_args: PlatformArgs,
platform_type: &PlatformType,
) -> Result<(), GreedError> {
let platform = platform::for_type(platform_type, platform_args)?;
let orders = platform.recent_orders().await?;
println!("Recent Orders:");
for order in orders {
println!("-- {}", order.display_with_time_zone(&chrono::Local))
}
Ok(())
}
pub async fn fetch_status(
platform_args: PlatformArgs,
platform_type: &PlatformType,
show_full_status: bool,
) -> Result<(), GreedError> {
let platform = platform::for_type(platform_type, platform_args)?;
let account = platform.account().await?;
println!("Account Info: {}", account);
if !show_full_status {
return Ok(());
}
println!("Open Positions:");
let positions = platform.positions().await?;
positions
.iter()
.filter(|p| p.market_value.unwrap_or(0.0).abs() >= 0.01)
.for_each(|p| println!("-- {}", p));
println!("Open Orders:");
let orders = platform.open_orders().await?;
for order in &orders {
println!("-- {}", order);
}
Ok(())
}
pub async fn fetch_portfolio_status_history(
range: Range<DateTime<Utc>>,
format: OutputFormat,
) -> Result<(), GreedError> {
let connection = db::bootstrap()?;
let records = db::portfolio_status::select_in_range(&connection, range)?;
match format {
OutputFormat::Text => {
for record in records {
println!("{}", record);
}
}
OutputFormat::Json => {
println!("{}", serde_json::to_string_pretty(&records)?);
}
}
Ok(())
}
pub async fn fetch_stock_quote_history(
symbols: &[AssetSymbol],
range: Range<DateTime<Utc>>,
format: OutputFormat,
) -> Result<(), GreedError> {
let connection = db::bootstrap()?;
let quotes: Vec<Quote> = db::stock_quotes::select_in_range(&connection, range)?
.into_iter()
.filter(|q| matches_symbol_filter(symbols, q))
.collect();
match format {
OutputFormat::Text => {
for quote in quotes {
println!("{}", quote);
}
}
OutputFormat::Json => {
println!("{}", serde_json::to_string_pretty("es)?);
}
}
Ok(())
}
fn matches_symbol_filter(symbols: &[AssetSymbol], quote: &Quote) -> bool {
symbols.is_empty() || symbols.contains("e.symbol)
}
fn print_price_explainer() {
println!("ask price = lowest price where someone is willing to sell a share");
println!("bid price = highest price someone is willing to pay for a share");
println!("-----------");
}
pub async fn analyze_stocks(
assets: &Vec<AssetSymbol>,
platform_type: &PlatformType,
platform_args: PlatformArgs,
) -> Result<(), GreedError> {
let platform = platform::for_type(platform_type, platform_args)?;
let analyzer = AssetAnalyzer::new(platform);
let bars_by_symbol = analyzer.analyze_bars(assets).await?;
for symbol in assets {
let bars = bars_by_symbol
.get(symbol)
.ok_or(GreedError::new("missing bars"))?;
println!("{}", bars)
}
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn matches_symbol_filter_empty_list_matches_everything() {
assert!(matches_symbol_filter(
&[],
&Quote::fixture(AssetSymbol::new("VTI"))
));
}
#[test]
fn matches_symbol_filter_matches_listed_symbol() {
let symbols = vec![AssetSymbol::new("VTI"), AssetSymbol::new("AAPL")];
assert!(matches_symbol_filter(
&symbols,
&Quote::fixture(AssetSymbol::new("VTI"))
));
}
#[test]
fn matches_symbol_filter_rejects_unlisted_symbol() {
let symbols = vec![AssetSymbol::new("AAPL")];
assert!(!matches_symbol_filter(
&symbols,
&Quote::fixture(AssetSymbol::new("VTI"))
));
}
}