use std::collections::HashMap;
use std::net::IpAddr;
use chrono::{DateTime, NaiveDate, Utc};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use uuid::Uuid;
use crate::broker::enums::{
ACHRelationshipStatus, AccountType, AgreementType, BankAccountType, BankStatus,
CIPApprovalStatus, CIPProvider, CIPResult, CIPStatus, CalendarSubType, ClearingBroker,
DocumentType, DriftBandSubType, EmploymentStatus, FeePaymentMethod, FundingSource,
IdentifierType, JournalEntryType, JournalStatus, PortfolioStatus, RebalancingConditionsType,
RunInitiatedFrom, RunStatus, RunType, TaxIdType, TradeDocumentSubType, TradeDocumentType,
TransferDirection, TransferStatus, TransferType, VisaType, WeightType,
};
use crate::trading::AccountStatus;
use crate::types::serde_util::empty_string_as_none;
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Contact {
pub email_address: String,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub phone_number: Option<String>,
#[serde(
default,
deserialize_with = "crate::types::serde_util::null_as_default"
)]
pub street_address: Vec<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub unit: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub city: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub state: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub postal_code: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub country: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Identity {
pub given_name: String,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub middle_name: Option<String>,
pub family_name: String,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub date_of_birth: Option<NaiveDate>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub tax_id: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub tax_id_type: Option<TaxIdType>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub country_of_citizenship: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub country_of_birth: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub country_of_tax_residence: Option<String>,
#[serde(
default,
deserialize_with = "crate::types::serde_util::null_as_default"
)]
pub funding_source: Vec<FundingSource>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "crate::types::option_decimal"
)]
pub annual_income_min: Option<Decimal>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "crate::types::option_decimal"
)]
pub annual_income_max: Option<Decimal>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "crate::types::option_decimal"
)]
pub liquid_net_worth_min: Option<Decimal>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "crate::types::option_decimal"
)]
pub liquid_net_worth_max: Option<Decimal>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "crate::types::option_decimal"
)]
pub total_net_worth_min: Option<Decimal>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "crate::types::option_decimal"
)]
pub total_net_worth_max: Option<Decimal>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub visa_type: Option<VisaType>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub visa_expiration_date: Option<NaiveDate>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub date_of_departure_from_usa: Option<NaiveDate>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub permanent_resident: Option<bool>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Disclosures {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_control_person: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_affiliated_exchange_or_finra: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_affiliated_exchange_or_iiroc: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_politically_exposed: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub immediate_family_exposed: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_discretionary: Option<bool>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub employment_status: Option<EmploymentStatus>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub employer_name: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub employer_address: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub employment_position: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Agreement {
pub agreement: AgreementType,
pub signed_at: DateTime<Utc>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub ip_address: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub revision: Option<String>,
}
impl Agreement {
#[must_use]
pub fn new(agreement: AgreementType, signed_at: DateTime<Utc>) -> Self {
Self {
agreement,
signed_at,
ip_address: None,
revision: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AccountDocument {
#[serde(default, deserialize_with = "empty_string_as_none")]
pub id: Option<Uuid>,
pub document_type: DocumentType,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub document_sub_type: Option<String>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub content: Option<String>,
#[serde(default)]
pub created_at: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TrustedContact {
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub given_name: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub family_name: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub email_address: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub phone_number: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub street_address: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub city: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub state: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub postal_code: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub country: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct KycResults {
#[serde(default)]
pub reject: Option<Value>,
#[serde(default)]
pub accept: Option<Value>,
#[serde(default)]
pub indeterminate: Option<Value>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub additional_information: Option<String>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub summary: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Account {
pub id: Uuid,
pub account_number: String,
pub status: AccountStatus,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub crypto_status: Option<AccountStatus>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub currency: Option<crate::types::SupportedCurrencies>,
#[serde(default, with = "crate::types::option_decimal")]
pub last_equity: Option<Decimal>,
pub created_at: DateTime<Utc>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub clearing_broker: Option<ClearingBroker>,
#[serde(default)]
pub contact: Option<Contact>,
#[serde(default)]
pub identity: Option<Identity>,
#[serde(default)]
pub disclosures: Option<Disclosures>,
#[serde(default)]
pub agreements: Option<Vec<Agreement>>,
#[serde(default)]
pub documents: Option<Vec<AccountDocument>>,
#[serde(default)]
pub trusted_contact: Option<TrustedContact>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub account_type: Option<AccountType>,
#[serde(default)]
pub trading_configurations: Option<Value>,
#[serde(default)]
pub kyc_results: Option<KycResults>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TradeAccount {
#[serde(flatten)]
pub account: crate::trading::TradeAccount,
#[serde(default, with = "crate::types::option_decimal")]
pub cash_withdrawable: Option<Decimal>,
#[serde(default, with = "crate::types::option_decimal")]
pub cash_transferable: Option<Decimal>,
#[serde(default)]
pub previous_close: Option<DateTime<Utc>>,
#[serde(default, with = "crate::types::option_decimal")]
pub last_long_market_value: Option<Decimal>,
#[serde(default, with = "crate::types::option_decimal")]
pub last_short_market_value: Option<Decimal>,
#[serde(default, with = "crate::types::option_decimal")]
pub last_cash: Option<Decimal>,
#[serde(default, with = "crate::types::option_decimal")]
pub last_initial_margin: Option<Decimal>,
#[serde(default, with = "crate::types::option_decimal")]
pub last_regt_buying_power: Option<Decimal>,
#[serde(default, with = "crate::types::option_decimal")]
pub last_daytrading_buying_power: Option<Decimal>,
#[serde(default, with = "crate::types::serde_util::int::option")]
pub last_daytrade_count: Option<i64>,
#[serde(default, with = "crate::types::option_decimal")]
pub last_buying_power: Option<Decimal>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub clearing_broker: Option<ClearingBroker>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Order {
#[serde(flatten)]
pub order: crate::trading::Order,
#[serde(default, with = "crate::types::option_decimal")]
pub commission: Option<Decimal>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ACHRelationship {
pub id: Uuid,
pub account_id: Uuid,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub status: ACHRelationshipStatus,
#[serde(default)]
pub account_owner_name: String,
pub bank_account_type: BankAccountType,
#[serde(default)]
pub bank_account_number: String,
#[serde(default)]
pub bank_routing_number: String,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub nickname: Option<String>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub processor_token: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Bank {
pub id: Uuid,
pub account_id: Uuid,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
#[serde(default)]
pub name: String,
pub status: BankStatus,
#[serde(default)]
pub country: String,
#[serde(default)]
pub state_province: String,
#[serde(default)]
pub postal_code: String,
#[serde(default)]
pub city: String,
#[serde(default)]
pub street_address: String,
#[serde(default)]
pub account_number: String,
#[serde(default)]
pub bank_code: String,
pub bank_code_type: IdentifierType,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Transfer {
pub id: Uuid,
pub account_id: Uuid,
pub created_at: DateTime<Utc>,
#[serde(default)]
pub updated_at: Option<DateTime<Utc>>,
#[serde(default)]
pub expires_at: Option<DateTime<Utc>>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub relationship_id: Option<Uuid>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub bank_id: Option<Uuid>,
#[serde(with = "crate::types::decimal")]
pub amount: Decimal,
#[serde(rename = "type")]
pub transfer_type: TransferType,
pub status: TransferStatus,
pub direction: TransferDirection,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub reason: Option<String>,
#[serde(default, with = "crate::types::option_decimal")]
pub requested_amount: Option<Decimal>,
#[serde(default, with = "crate::types::option_decimal")]
pub fee: Option<Decimal>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub fee_payment_method: Option<FeePaymentMethod>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub additional_information: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Journal {
pub id: Uuid,
pub from_account: Uuid,
pub to_account: Uuid,
pub entry_type: JournalEntryType,
pub status: JournalStatus,
#[serde(default, with = "crate::types::option_decimal")]
pub net_amount: Option<Decimal>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub symbol: Option<String>,
#[serde(default, with = "crate::types::option_decimal")]
pub qty: Option<Decimal>,
#[serde(default, with = "crate::types::option_decimal")]
pub price: Option<Decimal>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub description: Option<String>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub settle_date: Option<NaiveDate>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub system_date: Option<NaiveDate>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub transmitter_name: Option<String>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub transmitter_account_number: Option<String>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub transmitter_address: Option<String>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub transmitter_financial_institution: Option<String>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub transmitter_timestamp: Option<String>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub currency: Option<crate::types::SupportedCurrencies>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct BatchJournalResponse {
#[serde(flatten)]
pub journal: Journal,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub error_message: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TradeDocument {
pub id: Uuid,
#[serde(default)]
pub name: String,
#[serde(rename = "type")]
pub document_type: TradeDocumentType,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub sub_type: Option<TradeDocumentSubType>,
pub date: NaiveDate,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct W8BenDocument {
pub country_citizen: String,
pub date: NaiveDate,
pub date_of_birth: NaiveDate,
pub full_name: String,
pub ip_address: IpAddr,
pub permanent_address_city_state: String,
pub permanent_address_country: String,
pub permanent_address_street: String,
pub revision: String,
pub signer_full_name: String,
pub timestamp: DateTime<Utc>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub additional_conditions: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub foreign_tax_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ftin_not_required: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub income_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mailing_address_city_state: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mailing_address_country: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mailing_address_street: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub paragraph_number: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub percent_rate_withholding: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reference_number: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub residency: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tax_id_ssn: Option<String>,
}
impl W8BenDocument {
pub fn validate(&self) -> crate::error::Result<()> {
if self.foreign_tax_id.is_none()
&& self.tax_id_ssn.is_none()
&& self.ftin_not_required.is_none()
{
return Err(crate::Error::InvalidRequest(
"ftin_not_required must be set when neither foreign_tax_id nor tax_id_ssn is"
.to_owned(),
));
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Weight {
#[serde(rename = "type")]
pub weight_type: WeightType,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub symbol: Option<String>,
#[serde(with = "crate::types::decimal")]
pub percent: Decimal,
}
impl Weight {
#[must_use]
pub fn cash(percent: Decimal) -> Self {
Self {
weight_type: WeightType::Cash,
symbol: None,
percent: percent.round_dp(2),
}
}
#[must_use]
pub fn asset(symbol: impl Into<String>, percent: Decimal) -> Self {
Self {
weight_type: WeightType::Asset,
symbol: Some(symbol.into()),
percent: percent.round_dp(2),
}
}
pub fn validate(&self) -> crate::error::Result<()> {
if self.percent <= Decimal::ZERO {
return Err(crate::Error::InvalidRequest(
"a weight's percent must be greater than zero".to_owned(),
));
}
if self.weight_type == WeightType::Asset && self.symbol.is_none() {
return Err(crate::Error::InvalidRequest(
"an asset weight must name a symbol".to_owned(),
));
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum RebalancingSubType {
DriftBand(DriftBandSubType),
Calendar(CalendarSubType),
Unknown(String),
}
impl RebalancingSubType {
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::DriftBand(sub_type) => sub_type.as_str(),
Self::Calendar(sub_type) => sub_type.as_str(),
Self::Unknown(value) => value,
}
}
}
impl From<&str> for RebalancingSubType {
fn from(value: &str) -> Self {
if DriftBandSubType::WIRE_VALUES.contains(&value) {
Self::DriftBand(DriftBandSubType::from(value))
} else if CalendarSubType::WIRE_VALUES.contains(&value) {
Self::Calendar(CalendarSubType::from(value))
} else {
Self::Unknown(value.to_owned())
}
}
}
impl std::fmt::Display for RebalancingSubType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl Serialize for RebalancingSubType {
fn serialize<S: serde::Serializer>(
&self,
serializer: S,
) -> std::result::Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for RebalancingSubType {
fn deserialize<D: serde::Deserializer<'de>>(
deserializer: D,
) -> std::result::Result<Self, D::Error> {
let value = String::deserialize(deserializer)?;
Ok(Self::from(value.as_str()))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct RebalancingCondition {
#[serde(rename = "type")]
pub condition_type: RebalancingConditionsType,
pub sub_type: RebalancingSubType,
#[serde(
default,
with = "crate::types::option_decimal",
skip_serializing_if = "Option::is_none"
)]
pub percent: Option<Decimal>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub day: Option<String>,
}
impl RebalancingCondition {
#[must_use]
pub fn drift_band(sub_type: DriftBandSubType, percent: Decimal) -> Self {
Self {
condition_type: RebalancingConditionsType::DriftBand,
sub_type: RebalancingSubType::DriftBand(sub_type),
percent: Some(percent),
day: None,
}
}
#[must_use]
pub fn calendar(sub_type: CalendarSubType, day: Option<String>) -> Self {
Self {
condition_type: RebalancingConditionsType::Calendar,
sub_type: RebalancingSubType::Calendar(sub_type),
percent: None,
day,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Portfolio {
pub id: Uuid,
pub name: String,
#[serde(default)]
pub description: String,
pub status: PortfolioStatus,
#[serde(default, with = "crate::types::serde_util::int::option")]
pub cooldown_days: Option<i64>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
#[serde(
default,
deserialize_with = "crate::types::serde_util::null_as_default"
)]
pub weights: Vec<Weight>,
#[serde(default)]
pub rebalance_conditions: Option<Vec<RebalancingCondition>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Subscription {
pub id: Uuid,
pub account_id: Uuid,
pub portfolio_id: Uuid,
pub created_at: DateTime<Utc>,
#[serde(default)]
pub last_rebalanced_at: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SkippedOrder {
pub symbol: String,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub side: Option<crate::trading::OrderSide>,
#[serde(default, with = "crate::types::option_decimal")]
pub notional: Option<Decimal>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub currency: Option<crate::types::SupportedCurrencies>,
#[serde(default)]
pub reason: String,
#[serde(default)]
pub reason_details: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct RebalancingRun {
pub id: Uuid,
pub account_id: Uuid,
pub portfolio_id: Uuid,
#[serde(rename = "type")]
pub run_type: RunType,
#[serde(default, with = "crate::types::option_decimal")]
pub amount: Option<Decimal>,
#[serde(
default,
deserialize_with = "crate::types::serde_util::null_as_default"
)]
pub weights: Vec<Weight>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub initiated_from: Option<RunInitiatedFrom>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
#[serde(default)]
pub completed_at: Option<DateTime<Utc>>,
#[serde(default)]
pub canceled_at: Option<DateTime<Utc>>,
pub status: RunStatus,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub reason: Option<String>,
#[serde(default)]
pub orders: Option<Vec<Order>>,
#[serde(default)]
pub failed_orders: Option<Vec<Order>>,
#[serde(default)]
pub skipped_orders: Option<Vec<SkippedOrder>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SubscriptionsPage {
#[serde(
default,
deserialize_with = "crate::types::serde_util::null_as_default"
)]
pub subscriptions: Vec<Subscription>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub next_page_token: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct RunsPage {
#[serde(
default,
deserialize_with = "crate::types::serde_util::null_as_default"
)]
pub runs: Vec<RebalancingRun>,
#[serde(default, deserialize_with = "empty_string_as_none")]
pub next_page_token: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CIPKycInfo {
pub id: String,
#[serde(
default,
with = "crate::types::serde_util::int::option",
skip_serializing_if = "Option::is_none"
)]
pub risk_score: Option<i64>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub risk_level: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub risk_categories: Option<Vec<String>>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub applicant_name: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub email_address: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub nationality: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub date_of_birth: Option<DateTime<Utc>>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub address: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub postal_code: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub country_of_residency: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kyc_completed_at: Option<DateTime<Utc>>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub ip_address: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub check_initiated_at: Option<DateTime<Utc>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub check_completed_at: Option<DateTime<Utc>>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub approval_status: Option<CIPApprovalStatus>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub approved_by: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub approved_reason: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub approved_at: Option<DateTime<Utc>>,
}
impl CIPKycInfo {
#[must_use]
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
..Self::default()
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CIPDocument {
pub id: String,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub result: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub status: Option<CIPStatus>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub created_at: Option<DateTime<Utc>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub date_of_birth: Option<DateTime<Utc>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub date_of_expiry: Option<DateTime<Utc>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub document_numbers: Option<Vec<String>>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub document_type: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub first_name: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub last_name: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub gender: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub issuing_country: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub nationality: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub age_validation: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub compromised_document: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub police_record: Option<CIPStatus>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub data_comparison: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub data_comparison_breakdown: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub image_integrity: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub image_integrity_breakdown: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub visual_authenticity: Option<String>,
}
impl CIPDocument {
#[must_use]
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
..Self::default()
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CIPPhoto {
pub id: String,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub result: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub status: Option<CIPStatus>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub created_at: Option<DateTime<Utc>>,
#[serde(
default,
rename = "face_comparision",
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub face_comparison: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub face_comparison_breakdown: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub image_integrity: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub image_integrity_breakdown: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub visual_authenticity: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub visual_authenticity_breakdown: Option<String>,
}
impl CIPPhoto {
#[must_use]
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
..Self::default()
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CIPIdentity {
pub id: String,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub result: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub status: Option<CIPStatus>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub created_at: Option<DateTime<Utc>>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub matched_address: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub matched_addresses: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub sources: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub sources_breakdown: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub address: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub address_breakdown: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub date_of_birth: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub date_of_birth_breakdown: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub tax_id: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub tax_id_breakdown: Option<String>,
}
impl CIPIdentity {
#[must_use]
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
..Self::default()
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CIPWatchlist {
pub id: String,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub result: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub status: Option<CIPStatus>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub created_at: Option<DateTime<Utc>>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub records: Option<String>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub politically_exposed_person: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub sanction: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub adverse_media: Option<CIPResult>,
#[serde(
default,
deserialize_with = "empty_string_as_none",
skip_serializing_if = "Option::is_none"
)]
pub monitored_lists: Option<CIPResult>,
}
impl CIPWatchlist {
#[must_use]
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
..Self::default()
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CIPInfo {
pub id: Uuid,
pub account_id: Uuid,
#[serde(
default,
deserialize_with = "crate::types::serde_util::null_as_default",
skip_serializing_if = "Vec::is_empty"
)]
pub provider_name: Vec<CIPProvider>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub created_at: Option<DateTime<Utc>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub updated_at: Option<DateTime<Utc>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kyc: Option<Box<CIPKycInfo>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub document: Option<Box<CIPDocument>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub photo: Option<Box<CIPPhoto>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub identity: Option<Box<CIPIdentity>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub watchlist: Option<Box<CIPWatchlist>>,
}
impl CIPInfo {
#[must_use]
pub fn new(id: Uuid, account_id: Uuid) -> Self {
Self {
id,
account_id,
provider_name: Vec::new(),
created_at: None,
updated_at: None,
kyc: None,
document: None,
photo: None,
identity: None,
watchlist: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AllAccountsPositions {
pub as_of: DateTime<Utc>,
#[serde(default)]
pub positions: HashMap<String, Vec<crate::trading::Position>>,
}