extern crate serde_json;
extern crate ureq;
use crate::{
client::{Client, Error},
helpers::make_params,
with_param
};
use serde::{de, Deserialize, Serialize, Serializer};
use serde_json::to_string;
use std::{collections::HashMap, fmt, io};
const MAX_LIMIT: usize = 50_000;
fn to_id<'de, D>(deserializer: D) -> Result<u64, D::Error>
where
D: de::Deserializer<'de>
{
struct JsonStringVisitor;
impl<'de> de::Visitor<'de> for JsonStringVisitor {
type Value = u64;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error
{
let mut res = [0; 8];
if v.len() <= 8 {
let bytes = &v.as_bytes();
res[0..bytes.len()].copy_from_slice(bytes);
let res = u64::from_be_bytes(res);
Ok(res)
} else if v.len() <= 20 {
let res = v.parse::<u64>().expect("Parseable u64");
Ok(res)
} else {
Err(de::Error::custom(&format!("bad trade id {}", v)))
}
}
}
deserializer.deserialize_any(JsonStringVisitor)
}
fn f64_to_u32<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
D: de::Deserializer<'de>
{
struct JsonNumberVisitor;
impl<'de> de::Visitor<'de> for JsonNumberVisitor {
type Value = u32;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a number castable to u32")
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: de::Error
{
Ok(v as u32)
}
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
where
E: de::Error
{
Ok(v as u32)
}
}
deserializer.deserialize_any(JsonNumberVisitor)
}
fn to_json<S>(value: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer
{
match to_string(value) {
Ok(v) => serializer.serialize_str(&v),
Err(e) => panic!("{}", e)
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Trade {
pub sequence_number: Option<u64>, pub tape: u8,
#[serde(deserialize_with = "to_id", default)]
pub id: u64,
#[serde(default)]
pub ticker: String,
#[serde(rename(deserialize = "sip_timestamp"))]
pub time: i64,
#[serde(rename(deserialize = "participant_timestamp"))]
pub time_participant: Option<i64>,
#[serde(rename(deserialize = "trf_timestamp"))]
pub time_trf: Option<i64>,
#[serde(default)]
pub price: f64,
#[serde(deserialize_with = "f64_to_u32", default)]
pub size: u32,
#[serde(serialize_with = "to_json", default)]
pub conditions: Vec<u8>,
#[serde(default)]
pub correction: u8,
pub exchange: u8,
#[serde(rename(deserialize = "trf_id"))]
pub trf: Option<u8>
}
#[derive(Debug, Deserialize, Serialize)]
pub struct TradesResponse {
pub results: Vec<Trade>,
pub next_url: Option<String>,
pub status: String, pub uri: Option<String>
}
pub struct TradesParams<'a> {
pub params: HashMap<&'a str, String>
}
impl<'a> TradesParams<'a> {
with_param!(timestamp, &str);
with_param!(timestamp_lt, &str);
with_param!(timestamp_lte, &str);
with_param!(timestamp_gt, &str);
with_param!(timestamp_gte, &str);
with_param!(order, &str);
with_param!(reverse, bool);
with_param!(limit, usize);
with_param!(cursor, &str);
pub fn new() -> Self {
Self {
params: HashMap::with_capacity(4)
}
}
}
impl Client {
pub fn get_trades(
&self,
symbol: &str,
params: Option<&HashMap<&str, String>>
) -> Result<TradesResponse, Error> {
let uri = format!(
"{}/v3/trades/{}{}",
self.api_uri,
symbol,
make_params(params),
);
let mut resp = self.get_response::<TradesResponse>(&uri)?;
resp.uri = Some(uri);
for row in resp.results.iter_mut() {
row.ticker = symbol.to_string();
}
Ok(resp)
}
pub fn get_all_trades(&self, symbol: &str, date: &str) -> Result<Vec<Trade>, Error> {
let mut params = TradesParams::new().limit(MAX_LIMIT).timestamp(date);
let mut res = Vec::<Trade>::new();
loop {
let page = self.get_trades(symbol, Some(¶ms.params))?;
res.extend(page.results.into_iter());
match page.next_url {
Some(next_url) => {
let split = next_url.split("cursor=").collect::<Vec<&str>>();
if split.len() != 2 {
let msg = format!("no cursor in next_url {}", next_url);
let io_error = io::Error::new(io::ErrorKind::UnexpectedEof, msg);
return Err(Error::IoError(io_error));
}
let cursor = split[1];
params = TradesParams::new().cursor(cursor);
}
None => break
};
}
Ok(res)
}
}
#[cfg(test)]
mod trades {
use crate::{client::Client, equities::trades::TradesParams};
use crate::equities::trades::MAX_LIMIT;
#[test]
fn appl_2004_works() {
let client = Client::new().unwrap();
let params = TradesParams::new()
.timestamp("2004-01-02")
.limit(MAX_LIMIT)
.params;
let trades = client.get_trades("AAPL", Some(¶ms)).unwrap();
let count = 7_452;
assert_eq!(trades.results.len(), count);
}
#[test]
fn limit_works() {
let client = Client::new().unwrap();
let limit = 500;
let params = TradesParams::new()
.limit(limit)
.timestamp("2004-01-02")
.params;
let trades = client.get_trades("AAPL", Some(¶ms)).unwrap();
assert_eq!(trades.results.len(), limit);
}
#[test]
fn get_all_works() {
let client = Client::new().unwrap();
let trades = client.get_all_trades("AAPL", "2020-01-02").unwrap();
let count = 283_504;
assert_eq!(trades.len(), count);
}
}