use apca::api::v2::order::CreateReq;
use apca::api::v2::orders::{ListReq, Status};
use apca::api::v2::{account, order, orders, positions};
use apca::data::v2::bars::ListReq as BarsReq;
use apca::data::v2::{bars, last_quotes};
use apca::Client;
use async_trait::async_trait;
use chrono::Utc;
use itertools::Itertools;
use log::info;
use crate::asset::AssetSymbol;
use crate::error::GreedError;
use crate::pager;
use crate::platform::account::Account;
use crate::platform::alpaca::factory::create_alpaca_client;
use crate::platform::args::PlatformArgs;
use crate::platform::bar::bar_request::BarRequest;
use crate::platform::bar::time_frame::TimeFrame;
use crate::platform::bar::Bar;
use crate::platform::bars::Bars;
use crate::platform::order::Order;
use crate::platform::position::Position;
use crate::platform::quote::Quote;
use crate::platform::request::OrderRequest;
use crate::platform::FinancialPlatform;
mod convert_account;
mod convert_asset_class;
mod convert_bar;
mod convert_order;
mod convert_position;
mod convert_quote;
mod convert_request;
mod convert_side;
mod convert_symbol;
mod factory;
pub struct AlpacaPlatform {
client: Client,
}
impl AlpacaPlatform {
pub fn new(runner_args: &PlatformArgs) -> Result<Self, GreedError> {
Ok(Self {
client: create_alpaca_client(runner_args.is_simulated)?,
})
}
async fn replace_invalid_quotes(&self, quotes: Vec<Quote>) -> Vec<Quote> {
let mut valid_quotes = Vec::new();
for quote in quotes {
let valid_quote = self.replace_invalid_quote(quote).await;
valid_quotes.push(valid_quote);
}
valid_quotes
}
async fn replace_invalid_quote(&self, quote: Quote) -> Quote {
if quote.valid_ask() && quote.valid_bid() {
return quote;
}
info!(
"invalid quote {}, attempting to fetch prices from bar",
quote.symbol.symbol
);
let end_time = Utc::now();
let start_time = end_time - chrono::Duration::minutes(1);
let bars_request = BarRequest {
symbol: quote.symbol.clone(),
limit: Some(1),
start: start_time,
end: end_time,
timeframe: TimeFrame::OneMinute,
};
self.bars(bars_request)
.await
.map(|b| Quote::from(b))
.unwrap_or(quote)
}
}
#[async_trait]
impl FinancialPlatform for AlpacaPlatform {
async fn account(&self) -> Result<Account, GreedError> {
let alpaca_account = self.client.issue::<account::Get>(&()).await?;
Ok(alpaca_account.into())
}
async fn bars(&self, bars_request: BarRequest) -> Result<Bars, GreedError> {
let symbol = bars_request.symbol.clone();
let alpaca_request: BarsReq = bars_request.into();
let all_alpaca_bars = pager::fetch_all(|page| async {
let mut page_request = alpaca_request.clone();
page_request.page_token = page;
let response = self.client.issue::<bars::List>(&page_request).await?;
Ok((response.bars, response.next_page_token))
})
.await?;
let all_bars = all_alpaca_bars
.into_iter()
.map(|b| b.into())
.collect::<Vec<Bar>>();
Ok(Bars {
symbol,
bars: all_bars,
})
}
async fn latest_quotes(&self, symbols: &Vec<AssetSymbol>) -> Result<Vec<Quote>, GreedError> {
let symbol_strings = symbols.iter().map(|s| &s.symbol).collect::<Vec<_>>();
let latest_req = apca::data::v2::last_quotes::GetReqInit {
..Default::default()
}
.init(symbol_strings);
let result = self.client.issue::<last_quotes::Get>(&latest_req).await?;
let quotes: Vec<Quote> = result.into_iter().map(|q| q.into()).collect();
let valid_quotes = self.replace_invalid_quotes(quotes).await;
Ok(valid_quotes)
}
async fn place_order(&self, order_request: OrderRequest) -> Result<Order, GreedError> {
let order_req: CreateReq = order_request.into();
let order = self.client.issue::<order::Create>(&order_req).await?;
Ok(order.into())
}
async fn positions(&self) -> Result<Vec<Position>, GreedError> {
let alpaca_positions = self.client.issue::<positions::List>(&()).await?;
let positions: Vec<Position> = alpaca_positions.into_iter().map_into().collect();
Ok(positions)
}
async fn open_orders(&self) -> Result<Vec<Order>, GreedError> {
let orders_req = orders::ListReq {
status: Status::Open,
..ListReq::default()
};
let alpaca_orders = self.client.issue::<orders::List>(&orders_req).await?;
let orders: Vec<Order> = alpaca_orders.into_iter().map_into().collect();
Ok(orders)
}
async fn recent_orders(&self) -> Result<Vec<Order>, GreedError> {
let orders_req = orders::ListReq {
status: Status::All,
..ListReq::default()
};
let alpaca_orders = self.client.issue::<orders::List>(&orders_req).await?;
let orders: Vec<Order> = alpaca_orders.into_iter().map_into().collect();
Ok(orders)
}
}