use crate::{OrderRequest, OrderResponse, OrderStatus, OrderUpdate};
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use nt_core::types::Symbol;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use thiserror::Error;
#[async_trait]
pub trait BrokerClient: Send + Sync {
async fn get_account(&self) -> Result<Account>;
async fn get_positions(&self) -> Result<Vec<Position>>;
async fn place_order(&self, order: OrderRequest) -> Result<OrderResponse>;
async fn cancel_order(&self, order_id: &str) -> Result<()>;
async fn get_order(&self, order_id: &str) -> Result<OrderResponse>;
async fn list_orders(&self, filter: OrderFilter) -> Result<Vec<OrderResponse>>;
async fn health_check(&self) -> Result<HealthStatus>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Account {
pub account_id: String,
pub cash: Decimal,
pub portfolio_value: Decimal,
pub buying_power: Decimal,
pub equity: Decimal,
pub last_equity: Decimal,
pub multiplier: String,
pub currency: String,
pub shorting_enabled: bool,
pub long_market_value: Decimal,
pub short_market_value: Decimal,
pub initial_margin: Decimal,
pub maintenance_margin: Decimal,
pub day_trading_buying_power: Decimal,
pub daytrade_count: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Position {
pub symbol: Symbol,
pub qty: i64,
pub side: PositionSide,
pub avg_entry_price: Decimal,
pub market_value: Decimal,
pub cost_basis: Decimal,
pub unrealized_pl: Decimal,
pub unrealized_plpc: Decimal,
pub current_price: Decimal,
pub lastday_price: Decimal,
pub change_today: Decimal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum PositionSide {
Long,
Short,
}
#[derive(Debug, Clone, Default)]
pub struct OrderFilter {
pub status: Option<OrderStatus>,
pub limit: Option<usize>,
pub after: Option<DateTime<Utc>>,
pub until: Option<DateTime<Utc>>,
pub direction: Option<String>,
pub nested: Option<bool>,
pub symbols: Option<Vec<String>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum HealthStatus {
Healthy,
Degraded,
Unhealthy,
}
#[derive(Debug, Error)]
pub enum BrokerError {
#[error("Insufficient funds")]
InsufficientFunds,
#[error("Invalid order: {0}")]
InvalidOrder(String),
#[error("Order not found: {0}")]
OrderNotFound(String),
#[error("Market closed")]
MarketClosed,
#[error("Rate limit exceeded")]
RateLimit,
#[error("Authentication failed: {0}")]
Auth(String),
#[error("Network error: {0}")]
Network(String),
#[error("Parse error: {0}")]
Parse(String),
#[error("Broker unavailable: {0}")]
Unavailable(String),
#[error("Order error: {0}")]
Order(String),
#[error("Operation timeout")]
Timeout,
#[error("Circuit breaker open")]
CircuitBreakerOpen,
#[error(transparent)]
Other(#[from] anyhow::Error),
}
impl From<reqwest::Error> for BrokerError {
fn from(err: reqwest::Error) -> Self {
BrokerError::Network(err.to_string())
}
}
impl From<serde_json::Error> for BrokerError {
fn from(err: serde_json::Error) -> Self {
BrokerError::Parse(err.to_string())
}
}
pub type ExecutionError = BrokerError;
pub type Result<T> = std::result::Result<T, BrokerError>;