Skip to content

Commit

Permalink
chore: update intra account transfers
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastien Verreault committed Feb 15, 2023
1 parent 6640ef4 commit 50730c6
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 15 deletions.
61 changes: 53 additions & 8 deletions deribit-client/src/client/deribit_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ pub struct Transfer {
pub other_side: String,
pub created_timestamp: u64,
pub updated_timestamp: u64,
pub note: String,
pub note: Option<String>,
}

#[derive(Deserialize, Debug, Clone)]
Expand All @@ -163,6 +163,18 @@ pub struct TransferDetails {
pub testnet: Option<bool>,
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct TransferSubmitted {
pub jsonrpc: String,
pub result: Transfer,

pub us_in: Option<u64>,
pub us_out: Option<u64>,
pub us_diff: Option<u64>,
pub testnet: Option<bool>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct Withdrawal {
pub id: u64,
Expand All @@ -172,9 +184,11 @@ pub struct Withdrawal {
pub address: String,
pub priority: Decimal,
pub state: String,
pub transaction_id: String,
#[serde(deserialize_with = "boolean")]
pub completed: bool,
pub transaction_id: Option<String>,
pub created_timestamp: u64,
pub confirmed_timestamp: u64,
pub confirmed_timestamp: Option<u64>,
pub updated_timestamp: u64,
pub note: String,
}
Expand Down Expand Up @@ -371,6 +385,20 @@ pub struct AccountSummaryDetails {
pub testnet: Option<bool>,
}

fn boolean<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<bool, D::Error> {
Ok(match serde_json::Value::deserialize(deserializer)? {
serde_json::Value::Bool(b) => b,
serde_json::Value::String(s) => s == "yes",
serde_json::Value::Number(num) => {
num.as_i64()
.ok_or_else(|| serde::de::Error::custom("Invalid number"))?
!= 0
}
serde_json::Value::Null => false,
_ => return Err(serde::de::Error::custom("Wrong type, expected boolean")),
})
}

#[cfg(test)]
mod tests {
use rust_decimal_macros::dec;
Expand Down Expand Up @@ -437,6 +465,15 @@ mod tests {
assert_eq!(details.result.data[0].currency, Currency::BTC.to_string(),);
}

#[test]
fn submit_transfer() {
let response_text = "{\"jsonrpc\": \"2.0\",\"id\": 210,\"result\": {\"updated_timestamp\": 1550226218504,\"type\": \"subaccount\",\"state\": \"confirmed\",\"other_side\": \"MySubAccount\",\"id\": 1,\"direction\": \"payment\",\"currency\": \"ETH\",\"created_timestamp\": 1550226218504,\"amount\": 12.1234}}";
let details = serde_json::from_str::<TransferSubmitted>(response_text).unwrap();
dbg!(details.clone());
assert!(!details.result.other_side.is_empty());
assert_eq!(details.result.other_side, "MySubAccount",);
}

#[test]
fn get_withdrawals_empty() {
let response_text = "{\"jsonrpc\": \"2.0\",\"result\": {\"data\": [],\"count\": 0},\"usIn\": 1675066247615127,\"usOut\": 1675066247615279,\"usDiff\": 152,\"testnet\": true}";
Expand All @@ -446,11 +483,19 @@ mod tests {
}

#[test]
fn get_withdrawals() {
let response_text = "{\"jsonrpc\": \"2.0\",\"result\": {\"data\": [],\"count\": 0},\"usIn\": 1675066247615127,\"usOut\": 1675066247615279,\"usDiff\": 152,\"testnet\": true}";
let _details = serde_json::from_str::<WithdrawalDetails>(response_text).unwrap();
// assert!(!details.result.data.is_empty());
// assert_eq!(details.result.data[0].currency, Currency::BTC.to_string(),);
fn get_withdrawals_unconfirmed() {
let response_text = "{\"jsonrpc\":\"2.0\",\"result\":{\"data\":[{\"updated_timestamp\":1675411149209,\"transaction_id\":null,\"state\":\"unconfirmed\",\"priority\":1.0,\"note\":\"\",\"id\":21113,\"fee\":0.0001,\"currency\":\"BTC\",\"created_timestamp\":1675411149209,\"confirmed_timestamp\":null,\"completed\":0,\"amount\":0.0499,\"address\":\"bcrt1q2fnd9qslx03ka9un8cd50n7yh073rq67t0zp2z\"}],\"count\":1},\"usIn\":1675411523122145,\"usOut\":1675411523122283,\"usDiff\":138,\"testnet\":true}";
let details = serde_json::from_str::<WithdrawalDetails>(response_text).unwrap();
assert!(!details.result.data.is_empty());
assert_eq!(details.result.data[0].currency, Currency::BTC.to_string(),);
}

#[test]
fn get_withdrawals_confirmed() {
let response_text = "{\"jsonrpc\":\"2.0\",\"result\":{\"data\":[{\"updated_timestamp\":16754124,\"transaction_id\":null,\"state\":\"confirmed\",\"priority\":1.0,\"note\":\"\",\"id\":211,\"fee\":0.0001,\"currency\":\"BTC\",\"created_timestamp\":1675411,\"confirmed_timestamp\":167541,\"completed\":0,\"amount\":0.0499,\"address\":\"bcrt1q2\"}],\"count\":1},\"usIn\":1675412486,\"usOut\":167541248,\"usDiff\":147,\"testnet\":true}";
let details = serde_json::from_str::<WithdrawalDetails>(response_text).unwrap();
assert!(!details.result.data.is_empty());
assert_eq!(details.result.data[0].currency, Currency::BTC.to_string(),);
}

#[test]
Expand Down
3 changes: 3 additions & 0 deletions deribit-client/src/client/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ pub enum DeribitClientError {
#[error("DeribitClientError - DecimalConversion: {0}")]
DecimalConversion(#[from] rust_decimal::Error),

#[error("DeribitClientError - CannotConvertOrderStateFromStr")]
CannotConvertOrderStateFromStr,

#[error("DeribitClientError - UnexpectedResponse: {code:?} - {msg:?}")]
UnexpectedResponse { msg: String, code: i64 },
#[error("DeribitClientError - RequestParametersError: {code:?} - {msg:?}")]
Expand Down
82 changes: 76 additions & 6 deletions deribit-client/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ impl DeribitClient {
}

#[instrument(skip(self), err)]
pub async fn get_withdrawals(&self) -> Result<Vec<Transfer>, DeribitClientError> {
pub async fn get_withdrawals(&self) -> Result<Vec<Withdrawal>, DeribitClientError> {
let endpoint = "/private/get_withdrawals";
let params = format!("?currency={}", Currency::BTC);

Expand All @@ -152,7 +152,7 @@ impl DeribitClient {
.send()
.await?;

let details = Self::extract_response_data::<TransferDetails>(response).await?;
let details = Self::extract_response_data::<WithdrawalDetails>(response).await?;

Ok(details.result.data)
}
Expand Down Expand Up @@ -314,7 +314,7 @@ impl DeribitClient {
#[instrument(skip(self), err)]
pub async fn get_funding_account_summary(&self) -> Result<AccountSummary, DeribitClientError> {
let endpoint = "/private/get_account_summary";
let params = format!("?currency={}", Currency::BTC,);
let params = format!("?currency={}&extended=true", Currency::BTC,);

let headers = self.get_private_request_headers(KeyUsage::ForFunding)?;

Expand All @@ -334,7 +334,7 @@ impl DeribitClient {
#[instrument(skip(self), err)]
pub async fn get_trading_account_summary(&self) -> Result<AccountSummary, DeribitClientError> {
let endpoint = "/private/get_account_summary";
let params = format!("?currency={}", Currency::BTC,);
let params = format!("?currency={}&extended=true", Currency::BTC,);

let headers = self.get_private_request_headers(KeyUsage::ForTrading)?;

Expand All @@ -351,13 +351,84 @@ impl DeribitClient {
Ok(details.result)
}

#[instrument(skip(self), err)]
pub async fn get_funding_account_id(&self) -> Result<u64, DeribitClientError> {
let summary = self.get_funding_account_summary().await?;
Ok(summary.id.unwrap())
}

#[instrument(skip(self), err)]
pub async fn get_trading_account_id(&self) -> Result<u64, DeribitClientError> {
let summary = self.get_trading_account_summary().await?;
Ok(summary.id.unwrap())
}

#[instrument(skip(self), err)]
pub async fn transfer_funding_to_trading(
&self,
amount_in_btc: Decimal,
) -> Result<Transfer, DeribitClientError> {
let account_id = self.get_trading_account_id().await?;
let endpoint = "/private/submit_transfer_to_subaccount";

let params = format!(
"?currency={}&amount={}&destination={}",
Currency::BTC,
amount_in_btc,
account_id
);

let headers = self.get_private_request_headers(KeyUsage::ForFunding)?;

let response = self
.rate_limit_client(endpoint)
.await
.get(self.url_for_path(endpoint, params.as_str()))
.headers(headers)
.send()
.await?;

let details = Self::extract_response_data::<TransferSubmitted>(response).await?;

Ok(details.result)
}

#[instrument(skip(self), err)]
pub async fn transfer_trading_to_funding(
&self,
amount_in_btc: Decimal,
) -> Result<Transfer, DeribitClientError> {
let account_id = self.get_funding_account_id().await?;
let endpoint = "/private/submit_transfer_to_subaccount";

let params = format!(
"?currency={}&amount={}&destination={}",
Currency::BTC,
amount_in_btc,
account_id
);

let headers = self.get_private_request_headers(KeyUsage::ForTrading)?;

let response = self
.rate_limit_client(endpoint)
.await
.get(self.url_for_path(endpoint, params.as_str()))
.headers(headers)
.send()
.await?;

let details = Self::extract_response_data::<TransferSubmitted>(response).await?;

Ok(details.result)
}

async fn extract_response_data<T: serde::de::DeserializeOwned>(
response: Response,
) -> Result<T, DeribitClientError> {
match response.status() {
StatusCode::OK => {
let response_text = response.text().await?;
dbg!(response_text.clone());
match serde_json::from_str::<T>(&response_text) {
Ok(data) => Ok(data),
Err(err) => Err(DeribitClientError::UnexpectedResponse {
Expand All @@ -368,7 +439,6 @@ impl DeribitClient {
}
_ => {
let response_text = response.text().await?;
dbg!(response_text.clone());
let data = serde_json::from_str::<DeribitErrorResponse>(&response_text)?;
Err(DeribitClientError::from((
data.error.message,
Expand Down
40 changes: 39 additions & 1 deletion deribit-client/src/client/primitives.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use rust_decimal::Decimal;
use std::fmt::Display;
use std::{fmt::Display, str::FromStr};

use crate::DeribitClientError;

#[derive(serde::Deserialize, Debug, Clone)]
#[serde(transparent)]
Expand Down Expand Up @@ -89,6 +91,42 @@ impl Display for Priority {
}
}

#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum OrderState {
Open,
Filled,
Rejected,
Cancelled,
Untriggered,
}

impl Display for OrderState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
OrderState::Open => write!(f, "open"),
OrderState::Filled => write!(f, "filled"),
OrderState::Rejected => write!(f, "rejected"),
OrderState::Cancelled => write!(f, "cancelled"),
OrderState::Untriggered => write!(f, "untriggered"),
}
}
}

impl FromStr for OrderState {
type Err = DeribitClientError;

fn from_str(input: &str) -> Result<OrderState, Self::Err> {
match input {
"open" => Ok(OrderState::Open),
"filled" => Ok(OrderState::Filled),
"rejected" => Ok(OrderState::Rejected),
"cancelled" => Ok(OrderState::Cancelled),
"untriggered" => Ok(OrderState::Untriggered),
_ => Err(DeribitClientError::CannotConvertOrderStateFromStr),
}
}
}

#[derive(Debug, Clone)]
pub struct LastPrice {
pub usd_cents: Decimal,
Expand Down

0 comments on commit 50730c6

Please sign in to comment.