mod endpoints;
pub mod market_data;
pub mod parameter;
pub mod trader;
use reqwest::Client;
use crate::token::Tokener;
use crate::{error::Error, model};
use parameter::{Market, Projection, TransactionType};
#[derive(Debug)]
pub struct Api<T: Tokener> {
pub tokener: T,
client: Client,
}
impl<T: Tokener> Api<T> {
pub async fn new(tokener: T, client: Client) -> Result<Self, Error> {
let api = Api { tokener, client };
if (api.get_quote("AAPL".to_string()).await?.send().await).is_err() {
api.tokener.redo_authorization().await?;
}
Ok(api)
}
pub async fn get_quotes(
&self,
symbols: Vec<String>,
) -> Result<market_data::GetQuotesRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(market_data::GetQuotesRequest::new(
&self.client,
access_token,
symbols,
))
}
pub async fn get_quote(&self, symbol: String) -> Result<market_data::GetQuoteRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(market_data::GetQuoteRequest::new(
&self.client,
access_token,
symbol,
))
}
pub async fn get_option_chains(
&self,
symbol: String,
) -> Result<market_data::GetOptionChainsRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(market_data::GetOptionChainsRequest::new(
&self.client,
access_token,
symbol,
))
}
pub async fn get_option_expiration_chain(
&self,
symbol: String,
) -> Result<market_data::GetOptionExpirationChainRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(market_data::GetOptionExpirationChainRequest::new(
&self.client,
access_token,
symbol,
))
}
pub async fn get_price_history(
&self,
symbol: String,
) -> Result<market_data::GetPriceHistoryRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(market_data::GetPriceHistoryRequest::new(
&self.client,
access_token,
symbol,
))
}
pub async fn get_movers(&self, symbol: String) -> Result<market_data::GetMoversRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(market_data::GetMoversRequest::new(
&self.client,
access_token,
symbol,
))
}
pub async fn get_markets(
&self,
markets: Vec<Market>,
) -> Result<market_data::GetMarketsRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(market_data::GetMarketsRequest::new(
&self.client,
access_token,
markets,
))
}
pub async fn get_market(
&self,
market_id: Market,
) -> Result<market_data::GetMarketRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(market_data::GetMarketRequest::new(
&self.client,
access_token,
market_id,
))
}
pub async fn get_instruments(
&self,
symbol: String,
projection: Projection,
) -> Result<market_data::GetInstrumentsRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(market_data::GetInstrumentsRequest::new(
&self.client,
access_token,
symbol,
projection,
))
}
pub async fn get_instrument(
&self,
cusip_id: String,
) -> Result<market_data::GetInstrumentRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(market_data::GetInstrumentRequest::new(
&self.client,
access_token,
cusip_id,
))
}
pub async fn get_account_numbers(&self) -> Result<trader::GetAccountNumbersRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(trader::GetAccountNumbersRequest::new(
&self.client,
access_token,
))
}
pub async fn get_accounts(&self) -> Result<trader::GetAccountsRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(trader::GetAccountsRequest::new(&self.client, access_token))
}
pub async fn get_account(
&self,
account_number: String,
) -> Result<trader::GetAccountRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(trader::GetAccountRequest::new(
&self.client,
access_token,
account_number,
))
}
pub async fn get_account_orders(
&self,
account_number: String,
from_entered_time: chrono::DateTime<chrono::Utc>,
to_entered_time: chrono::DateTime<chrono::Utc>,
) -> Result<trader::GetAccountOrdersRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(trader::GetAccountOrdersRequest::new(
&self.client,
access_token,
account_number,
from_entered_time,
to_entered_time,
))
}
pub async fn post_account_order(
&self,
account_number: String,
body: model::OrderRequest,
) -> Result<trader::PostAccountOrderRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(trader::PostAccountOrderRequest::new(
&self.client,
access_token,
account_number,
body,
))
}
pub async fn get_account_order(
&self,
account_number: String,
order_id: i64,
) -> Result<trader::GetAccountOrderRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(trader::GetAccountOrderRequest::new(
&self.client,
access_token,
account_number,
order_id,
))
}
pub async fn delete_account_order(
&self,
account_number: String,
order_id: i64,
) -> Result<trader::DeleteAccountOrderRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(trader::DeleteAccountOrderRequest::new(
&self.client,
access_token,
account_number,
order_id,
))
}
pub async fn put_account_order(
&self,
account_number: String,
order_id: i64,
body: model::OrderRequest,
) -> Result<trader::PutAccountOrderRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(trader::PutAccountOrderRequest::new(
&self.client,
access_token,
account_number,
order_id,
body,
))
}
pub async fn get_accounts_orders(
&self,
from_entered_time: chrono::DateTime<chrono::Utc>,
to_entered_time: chrono::DateTime<chrono::Utc>,
) -> Result<trader::GetAccountsOrdersRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(trader::GetAccountsOrdersRequest::new(
&self.client,
access_token,
from_entered_time,
to_entered_time,
))
}
pub async fn post_accounts_preview_order(
&self,
account_number: String,
body: model::OrderRequest,
) -> Result<trader::PostAccountPreviewOrderRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(trader::PostAccountPreviewOrderRequest::new(
&self.client,
access_token,
account_number,
body,
))
}
pub async fn get_account_transactions(
&self,
account_number: String,
start_date: chrono::DateTime<chrono::Utc>,
end_date: chrono::DateTime<chrono::Utc>,
types: TransactionType,
) -> Result<trader::GetAccountTransactions, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(trader::GetAccountTransactions::new(
&self.client,
access_token,
account_number,
start_date,
end_date,
types,
))
}
pub async fn get_account_transaction(
&self,
account_number: String,
transaction_id: i64,
) -> Result<trader::GetAccountTransaction, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(trader::GetAccountTransaction::new(
&self.client,
access_token,
account_number,
transaction_id,
))
}
pub async fn get_user_preference(&self) -> Result<trader::GetUserPreferenceRequest, Error> {
let access_token = self.tokener.get_access_token().await?;
Ok(trader::GetUserPreferenceRequest::new(
&self.client,
access_token,
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use float_cmp::assert_approx_eq;
use pretty_assertions::assert_eq;
use std::path::PathBuf;
use crate::model::trader::order::ExecutionType;
use crate::model::trader::order_request::InstrumentRequest;
use crate::model::trader::preview_order::Instruction;
use crate::token::TokenChecker;
use crate::token::channel_messenger::ChannelMessenger;
use crate::token::channel_messenger::compound_messenger::CompoundMessenger;
use crate::token::channel_messenger::local_server::LocalServerMessenger;
use crate::token::channel_messenger::stdio_messenger::StdioMessenger;
async fn client() -> Api<TokenChecker<impl ChannelMessenger>> {
#[allow(clippy::option_env_unwrap)]
let key = option_env!("SCHWAB_API_KEY")
.expect("The environment variable SCHWAB_API_KEY sholud be set")
.to_string();
#[allow(clippy::option_env_unwrap)]
let secret = option_env!("SCHWAB_SECRET")
.expect("The environment variable SCHWAB_SECRET sholud be set")
.to_string();
#[allow(clippy::option_env_unwrap)]
let callback_url = option_env!("SCHWAB_CALLBACK_URL")
.expect("The environment variable SCHWAB_CALLBACK_URL sholud be set")
.to_string();
let path = dirs::home_dir()
.expect("home dir")
.join(".credentials")
.join("Schwab-rust.json");
let certs_dir = PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/certs"));
let messenger = CompoundMessenger::new(
LocalServerMessenger::new(&certs_dir).await,
StdioMessenger::new(),
);
let client = Client::new();
let token_checker = TokenChecker::new_with_custom_auth(
path,
key,
secret,
callback_url,
client.clone(),
messenger,
)
.await
.unwrap();
Api::new(token_checker, client).await.unwrap()
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_quotes() {
let api = client().await;
let req = api
.get_quotes(vec![
"AAPL".into(),
"EUR/USD".into(),
"/ESZ24".into(),
"$SPX".into(),
"AAAIX".into(),
get_option_chain("AAPL".to_string()).await,
])
.await
.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_quote() {
let symbols = vec![
"AAPL".to_string(),
"$SPX".to_string(),
"AAAIX".to_string(),
get_option_chain("AAPL".to_string()).await,
];
let api = client().await;
for symbol in symbols {
dbg!(&symbol);
let req = api.get_quote(symbol).await.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_option_chains() {
let api = client().await;
let mut req = api.get_option_chains("AAPL".into()).await.unwrap();
req.days_to_expiration(3)
.exp_month(parameter::Month::All)
.contract_type(parameter::ContractType::All);
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
async fn get_option_chain(symbol: String) -> String {
let api = client().await;
let req = api.get_option_chains(symbol).await.unwrap();
let rsp = req.send().await.unwrap();
if let Some(v) = rsp.call_exp_date_map.into_values().next()
&& let Some(mut v) = v.into_values().next()
{
return v.pop().expect("must exist").symbol;
}
unreachable!()
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_option_expiration_chain() {
let api = client().await;
let req = api
.get_option_expiration_chain("AAPL".into())
.await
.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_price_history() {
let api = client().await;
let req = api.get_price_history("AAPL".into()).await.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_movers() {
let api = client().await;
let req = api.get_movers("$DJI".into()).await.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_markets() {
let api = client().await;
let req = api
.get_markets(vec![Market::Equity, Market::Option])
.await
.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_market() {
let api = client().await;
let req = api.get_market(Market::Equity).await.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_instruments() {
let api = client().await;
let req = api
.get_instruments("VTI".into(), Projection::SymbolSearch)
.await
.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
let req = api
.get_instruments("AAPL".into(), Projection::Fundamental)
.await
.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
let req = api
.get_instruments("SNOW".into(), Projection::Fundamental)
.await
.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_instrument() {
let api = client().await;
let req = api.get_instrument("922908769".into()).await.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_account_numbers() {
let api = client().await;
let req = api.get_account_numbers().await.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
async fn account_number() -> String {
let api = client().await;
let req = api.get_account_numbers().await.unwrap();
let rsp = req.send().await.unwrap();
rsp[0].hash_value.clone()
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_accounts() {
let api = client().await;
let req = api.get_accounts().await.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_account() {
let api = client().await;
let req = api.get_account(account_number().await).await.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_account_orders() {
let api = client().await;
let req = api
.get_account_orders(
account_number().await,
chrono::NaiveDate::from_ymd_opt(2024, 1, 1)
.unwrap()
.and_hms_milli_opt(0, 0, 1, 444)
.unwrap()
.and_local_timezone(chrono::Utc)
.unwrap(),
chrono::NaiveDate::from_ymd_opt(2025, 1, 1)
.unwrap()
.and_hms_milli_opt(0, 0, 1, 444)
.unwrap()
.and_local_timezone(chrono::Utc)
.unwrap(),
)
.await
.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
async fn get_account_orders() -> i64 {
let api = client().await;
let req = api
.get_account_orders(
account_number().await,
chrono::NaiveDate::from_ymd_opt(2024, 5, 1)
.unwrap()
.and_hms_milli_opt(0, 0, 1, 444)
.unwrap()
.and_local_timezone(chrono::Utc)
.unwrap(),
chrono::NaiveDate::from_ymd_opt(2024, 5, 26)
.unwrap()
.and_hms_milli_opt(0, 0, 1, 444)
.unwrap()
.and_local_timezone(chrono::Utc)
.unwrap(),
)
.await
.unwrap();
let rsp = req.send().await.unwrap();
rsp[0].order_id
}
#[cfg_attr(
not(all(feature = "test_online", feature = "danger")),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[allow(clippy::too_many_lines)]
#[tokio::test]
async fn test_post_put_delete_account_order() {
let api = client().await;
let symbol = InstrumentRequest::Equity {
symbol: "VEA".to_string(),
};
let quantity = 1.0;
let price = 10.0;
let modified_price = 11.0;
let order_preview =
model::OrderRequest::limit(symbol.clone(), Instruction::Buy, quantity, price).unwrap();
let req = api
.post_accounts_preview_order(account_number().await, order_preview)
.await
.unwrap();
let preview = req.send().await.unwrap();
dbg!(preview);
let order_post =
model::OrderRequest::limit(symbol.clone(), Instruction::Buy, quantity, price).unwrap();
let req = api
.post_account_order(account_number().await, order_post.clone())
.await
.unwrap();
let order_id = req.send().await.unwrap().unwrap();
let req = api
.get_account_order(account_number().await, order_id)
.await
.unwrap();
let order_post_check = req.send().await.unwrap();
dbg!(&order_post_check);
assert_eq!(
order_post_check.session,
model::trader::order::Session::Normal
);
assert_approx_eq!(f64, order_post_check.price, price);
assert_eq!(
order_post_check.duration,
model::trader::order::Duration::Day
);
assert_eq!(
order_post_check.order_type,
model::trader::order::OrderType::Limit
);
assert_eq!(
Into::<InstrumentRequest>::into(
order_post_check.order_leg_collection[0].instrument.clone()
),
symbol
);
assert_eq!(
Into::<Instruction>::into(order_post_check.order_leg_collection[0].instruction),
Instruction::Buy
);
assert_approx_eq!(
f64,
order_post_check.order_leg_collection[0].quantity,
quantity
);
let order_id = order_post_check.order_id;
let mut order_put: model::OrderRequest = order_post_check.into();
order_put.price = Some(modified_price);
let req = api
.put_account_order(account_number().await, order_id, order_put.clone())
.await
.unwrap();
let order_id = req.send().await.unwrap().unwrap();
let req = api
.get_account_order(account_number().await, order_id)
.await
.unwrap();
let order_put_check = req.send().await.unwrap();
dbg!(&order_put_check);
assert_eq!(
order_put_check.session,
model::trader::order::Session::Normal
);
assert_approx_eq!(f64, order_put_check.price, modified_price);
assert_eq!(
order_put_check.duration,
model::trader::order::Duration::Day
);
assert_eq!(
order_put_check.order_type,
model::trader::order::OrderType::Limit
);
assert_eq!(
Into::<InstrumentRequest>::into(
order_put_check.order_leg_collection[0].instrument.clone()
),
symbol
);
assert_eq!(
Into::<Instruction>::into(order_put_check.order_leg_collection[0].instruction),
Instruction::Buy
);
assert_approx_eq!(
f64,
order_put_check.order_leg_collection[0].quantity,
quantity
);
let req = api
.delete_account_order(account_number().await, order_id)
.await
.unwrap();
req.send().await.unwrap();
let req = api
.get_account_order(account_number().await, order_id)
.await
.unwrap();
let order = req.send().await.unwrap();
dbg!(&order);
assert_eq!(
order.order_activity_collection.unwrap()[0].execution_type,
ExecutionType::Canceled
);
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_account_order() {
let api = client().await;
let req = api
.get_account_order(account_number().await, get_account_orders().await)
.await
.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_accounts_orders() {
let api = client().await;
let req = api
.get_accounts_orders(
chrono::NaiveDate::from_ymd_opt(2024, 6, 1)
.unwrap()
.and_hms_milli_opt(0, 0, 1, 444)
.unwrap()
.and_local_timezone(chrono::Utc)
.unwrap(),
chrono::NaiveDate::from_ymd_opt(2025, 5, 20)
.unwrap()
.and_hms_milli_opt(0, 0, 1, 444)
.unwrap()
.and_local_timezone(chrono::Utc)
.unwrap(),
)
.await
.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
#[cfg_attr(
not(all(feature = "test_online")),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_post_accounts_preview_order() {
let api = client().await;
let req = api
.post_accounts_preview_order(
account_number().await,
model::OrderRequest::limit(
InstrumentRequest::Equity {
symbol: "VEA".to_string(),
},
Instruction::Buy,
1.0,
10.0,
)
.unwrap(),
)
.await
.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_account_transactions() {
let api = client().await;
let req = api
.get_account_transactions(
account_number().await,
chrono::NaiveDate::from_ymd_opt(2023, 5, 1)
.unwrap()
.and_hms_milli_opt(0, 0, 1, 444)
.unwrap()
.and_local_timezone(chrono::Utc)
.unwrap(),
chrono::NaiveDate::from_ymd_opt(2024, 5, 1)
.unwrap()
.and_hms_milli_opt(0, 0, 1, 444)
.unwrap()
.and_local_timezone(chrono::Utc)
.unwrap(),
TransactionType::Trade,
)
.await
.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
async fn get_account_transactions() -> i64 {
let api = client().await;
let req = api
.get_account_transactions(
account_number().await,
chrono::NaiveDate::from_ymd_opt(2023, 5, 1)
.unwrap()
.and_hms_milli_opt(0, 0, 1, 444)
.unwrap()
.and_local_timezone(chrono::Utc)
.unwrap(),
chrono::NaiveDate::from_ymd_opt(2024, 5, 1)
.unwrap()
.and_hms_milli_opt(0, 0, 1, 444)
.unwrap()
.and_local_timezone(chrono::Utc)
.unwrap(),
TransactionType::Trade,
)
.await
.unwrap();
let rsp = req.send().await.unwrap();
rsp[0].activity_id
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_account_transaction() {
let api = client().await;
let req = api
.get_account_transaction(account_number().await, get_account_transactions().await)
.await
.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
#[cfg_attr(
not(feature = "test_online"),
ignore = r#"Without the "test_online" feature enabled, to activate it, corresponding SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_CALLBACK_URL need to be provided in the environment."#
)]
#[tokio::test]
async fn test_get_user_preference() {
let api = client().await;
let req = api.get_user_preference().await.unwrap();
let rsp = req.send().await.unwrap();
dbg!(rsp);
}
}