use crate::node::OrderMarketParams;
use anyhow::{anyhow as err, Error};
use bigdecimal::BigDecimal;
use chrono::{DateTime, Utc};
use cosmrs::{AccountId, Denom as CosmosDenom};
use derive_more::{Add, Deref, DerefMut, Display, Div, From, Mul, Sub};
use dydx_proto::dydxprotocol::subaccounts::SubaccountId as ProtoSubaccountId;
use rand::{rng, Rng};
use serde::{Deserialize, Deserializer, Serialize};
use serde_with::{serde_as, DisplayFromStr};
use std::collections::HashMap;
use std::convert::TryFrom;
use std::{fmt, str::FromStr};
#[derive(Deserialize, Debug, Clone, Eq, Hash, PartialOrd, Ord, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct AccountWithParentSubaccountNumber {
pub address: Address,
pub parent_subaccount_number: Option<ParentSubaccountNumber>,
}
#[derive(Deserialize, Debug, Clone, Eq, Hash, PartialOrd, Ord, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct Account {
pub address: Address,
pub subaccount_number: Option<SubaccountNumber>,
}
#[derive(
Default,
Serialize,
Deserialize,
Debug,
Clone,
From,
Display,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
)]
pub struct Address(String);
impl FromStr for Address {
type Err = Error;
fn from_str(value: &str) -> Result<Self, Error> {
Ok(Self(
value.parse::<AccountId>().map_err(Error::msg)?.to_string(),
))
}
}
impl AsRef<str> for Address {
fn as_ref(&self) -> &str {
&self.0
}
}
impl From<Address> for String {
fn from(address: Address) -> Self {
address.0
}
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase", untagged)]
pub enum ApiOrderStatus {
OrderStatus(OrderStatus),
BestEffort(BestEffortOpenedStatus),
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ApiTimeInForce {
Gtt,
Fok,
Ioc,
}
#[derive(
Serialize, Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
pub struct AssetId(pub String);
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum BestEffortOpenedStatus {
BestEffortOpened,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CandleResolution {
#[serde(rename = "1MIN")]
M1,
#[serde(rename = "5MINS")]
M5,
#[serde(rename = "15MINS")]
M15,
#[serde(rename = "30MINS")]
M30,
#[serde(rename = "1HOUR")]
H1,
#[serde(rename = "4HOURS")]
H4,
#[serde(rename = "1DAY")]
D1,
}
#[derive(Clone, Debug)]
pub struct AnyId;
#[serde_as]
#[derive(Deserialize, Debug, Clone, Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ClientId(#[serde_as(as = "DisplayFromStr")] pub u32);
impl ClientId {
pub fn new(id: u32) -> Self {
ClientId(id)
}
pub fn random() -> Self {
ClientId(rng().random())
}
pub fn random_with_rng<R: Rng>(rng: &mut R) -> Self {
ClientId(rng.random())
}
}
impl From<u32> for ClientId {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<AnyId> for ClientId {
fn from(_: AnyId) -> Self {
Self::random()
}
}
#[serde_as]
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ClobPairId(#[serde_as(as = "DisplayFromStr")] pub u32);
impl From<u32> for ClobPairId {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<&u32> for ClobPairId {
fn from(value: &u32) -> Self {
ClobPairId::from(*value)
}
}
#[serde_as]
#[derive(Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ClientMetadata(#[serde_as(as = "DisplayFromStr")] pub u32);
impl From<u32> for ClientMetadata {
fn from(value: u32) -> Self {
Self(value)
}
}
#[derive(Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FillId(pub String);
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum FillType {
Limit,
Liquidated,
Liquidation,
Deleveraged,
Offsetting,
}
#[serde_as]
#[derive(
Serialize, Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
pub struct Height(#[serde_as(as = "DisplayFromStr")] pub u32);
impl Height {
pub fn ahead(&self, n: u32) -> Height {
Height(self.0 + n)
}
}
#[derive(Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Liquidity {
Taker,
Maker,
}
#[derive(Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PerpetualMarketStatus {
Active,
Paused,
CancelOnly,
PostOnly,
Initializing,
FinalSettlement,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PerpetualPositionStatus {
Open,
Closed,
Liquidated,
}
#[derive(Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PositionSide {
Long,
Short,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum MarketType {
Perpetual,
Spot,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PerpetualMarketType {
Cross,
Isolated,
}
#[derive(Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OrderId(pub String);
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum OrderStatus {
Open,
Filled,
Canceled,
BestEffortCanceled,
Untriggered,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum OrderExecution {
Default,
Ioc,
Fok,
PostOnly,
}
#[derive(Clone, Debug, Deserialize)]
pub enum OrderFlags {
#[serde(rename = "0")]
ShortTerm = 0,
#[serde(rename = "32")]
Conditional = 32,
#[serde(rename = "64")]
LongTerm = 64,
}
#[derive(
Serialize, Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
pub struct TradeId(pub String);
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum OrderSide {
Buy,
Sell,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum OrderType {
Limit,
Market,
StopLimit,
StopMarket,
TrailingStop,
TakeProfit,
TakeProfitMarket,
HardTrade,
FailedHardTrade,
TransferPlaceholder,
}
#[derive(Deserialize, Debug, Clone, Eq, Hash, PartialOrd, Ord, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct Subaccount {
pub address: Address,
pub number: SubaccountNumber,
}
impl Subaccount {
pub fn new(address: Address, number: SubaccountNumber) -> Self {
Self { address, number }
}
pub fn parent(&self) -> ParentSubaccount {
let number = ParentSubaccountNumber(self.number.0 % 128);
ParentSubaccount::new(self.address.clone(), number)
}
pub fn is_parent(&self) -> bool {
self.number.0 < 128
}
}
impl From<Subaccount> for ProtoSubaccountId {
fn from(subacc: Subaccount) -> Self {
ProtoSubaccountId {
owner: subacc.address.0,
number: subacc.number.0,
}
}
}
#[derive(Serialize, Debug, Clone, Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SubaccountNumber(pub(crate) u32);
impl<'de> Deserialize<'de> for SubaccountNumber {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct SubaccountVisitor;
impl<'de> serde::de::Visitor<'de> for SubaccountVisitor {
type Value = SubaccountNumber;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a u32 or a string containing a u32")
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(SubaccountNumber(value as u32))
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
value
.parse::<u32>()
.map(SubaccountNumber)
.map_err(|_| E::custom(format!("invalid u32 in string: {value}")))
}
}
deserializer.deserialize_any(SubaccountVisitor)
}
}
impl SubaccountNumber {
pub fn value(&self) -> u32 {
self.0
}
}
impl TryFrom<u32> for SubaccountNumber {
type Error = Error;
fn try_from(number: u32) -> Result<Self, Error> {
match number {
0..=128_000 => Ok(SubaccountNumber(number)),
_ => Err(err!("Subaccount number must be [0, 128_000]")),
}
}
}
impl TryFrom<&u32> for SubaccountNumber {
type Error = Error;
fn try_from(number: &u32) -> Result<Self, Error> {
Self::try_from(*number)
}
}
impl TryFrom<String> for SubaccountNumber {
type Error = Error;
fn try_from(number: String) -> Result<Self, Error> {
Self::try_from(number.parse::<u32>()?)
}
}
impl TryFrom<&str> for SubaccountNumber {
type Error = Error;
fn try_from(number: &str) -> Result<Self, Error> {
Self::try_from(number.parse::<u32>()?)
}
}
impl From<ParentSubaccountNumber> for SubaccountNumber {
fn from(parent: ParentSubaccountNumber) -> Self {
Self(parent.value())
}
}
#[derive(Deserialize, Debug, Clone, Eq, Hash, PartialOrd, Ord, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct ParentSubaccount {
pub address: Address,
pub number: ParentSubaccountNumber,
}
impl ParentSubaccount {
pub fn new(address: Address, number: ParentSubaccountNumber) -> Self {
Self { address, number }
}
}
impl std::cmp::PartialEq<Subaccount> for ParentSubaccount {
fn eq(&self, other: &Subaccount) -> bool {
self.address == other.address && self.number == other.number
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ParentSubaccountNumber(u32);
impl ParentSubaccountNumber {
pub fn value(&self) -> u32 {
self.0
}
}
impl TryFrom<u32> for ParentSubaccountNumber {
type Error = Error;
fn try_from(number: u32) -> Result<Self, Error> {
match number {
0..=127 => Ok(ParentSubaccountNumber(number)),
_ => Err(err!("Parent subaccount number must be [0, 127]")),
}
}
}
impl std::cmp::PartialEq<SubaccountNumber> for ParentSubaccountNumber {
fn eq(&self, other: &SubaccountNumber) -> bool {
self.0 == other.value()
}
}
#[derive(Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SubaccountId(pub String);
#[derive(
Serialize, Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
pub struct Symbol(pub String);
#[derive(Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum TradeType {
Limit,
Liquidated,
Deleveraged,
}
#[derive(Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum TransferType {
TransferIn,
TransferOut,
Deposit,
Withdrawal,
}
#[derive(
Serialize, Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
pub struct Ticker(pub String);
impl<'a> From<&'a str> for Ticker {
fn from(value: &'a str) -> Self {
Self(value.into())
}
}
const USDC_DENOM: &str = "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5";
const DYDX_DENOM: &str = "adydx";
const DYDX_TNT_DENOM: &str = "adv4tnt";
#[cfg(feature = "noble")]
const NOBLE_USDC_DENOM: &str = "uusdc";
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Denom {
#[serde(rename = "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5")]
Usdc,
#[serde(rename = "adydx")]
Dydx,
#[serde(rename = "adv4tnt")]
DydxTnt,
#[cfg(feature = "noble")]
#[serde(rename = "uusdc")]
NobleUsdc,
#[serde(untagged)]
Custom(CosmosDenom),
}
impl Denom {
pub fn gas_price(&self) -> Option<BigDecimal> {
match self {
Self::Usdc => Some(BigDecimal::new(25.into(), 3)),
Self::Dydx | Self::DydxTnt => Some(BigDecimal::new(25_000_000_000u64.into(), 0)),
#[cfg(feature = "noble")]
Self::NobleUsdc => Some(BigDecimal::new(1.into(), 1)),
_ => None,
}
}
}
impl FromStr for Denom {
type Err = Error;
fn from_str(value: &str) -> Result<Self, Error> {
match value {
USDC_DENOM => Ok(Self::Usdc),
DYDX_DENOM => Ok(Self::Dydx),
DYDX_TNT_DENOM => Ok(Self::DydxTnt),
_ => Ok(Self::Custom(
value.parse::<CosmosDenom>().map_err(Error::msg)?,
)),
}
}
}
impl AsRef<str> for Denom {
fn as_ref(&self) -> &str {
match self {
Self::Usdc => USDC_DENOM,
Self::Dydx => DYDX_DENOM,
Self::DydxTnt => DYDX_TNT_DENOM,
#[cfg(feature = "noble")]
Self::NobleUsdc => NOBLE_USDC_DENOM,
Self::Custom(denom) => denom.as_ref(),
}
}
}
impl fmt::Display for Denom {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_ref())
}
}
impl TryFrom<Denom> for CosmosDenom {
type Error = Error;
fn try_from(value: Denom) -> Result<Self, Self::Error> {
value.as_ref().parse().map_err(Self::Error::msg)
}
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct ParentSubaccountResponseObject {
pub address: Address,
pub parent_subaccount_number: SubaccountNumber,
pub equity: BigDecimal,
pub free_collateral: BigDecimal,
pub child_subaccounts: Vec<SubaccountResponseObject>,
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct SubaccountResponseObject {
pub address: Address,
pub subaccount_number: SubaccountNumber,
pub equity: BigDecimal,
pub free_collateral: BigDecimal,
pub open_perpetual_positions: PerpetualPositionsMap,
pub asset_positions: AssetPositionsMap,
pub margin_enabled: bool,
pub updated_at_height: Height,
pub latest_processed_block_height: Height,
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct AssetPositionResponseObject {
pub symbol: Symbol,
pub side: PositionSide,
pub size: Quantity,
pub subaccount_number: SubaccountNumber,
pub asset_id: AssetId,
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct PerpetualPositionResponseObject {
pub market: Ticker,
pub status: PerpetualPositionStatus,
pub side: PositionSide,
pub size: Quantity,
pub max_size: Quantity,
pub entry_price: Price,
pub realized_pnl: BigDecimal,
pub created_at: DateTime<Utc>,
pub created_at_height: Height,
pub sum_open: BigDecimal,
pub sum_close: BigDecimal,
pub net_funding: BigDecimal,
pub unrealized_pnl: BigDecimal,
pub closed_at: Option<DateTime<Utc>>,
pub exit_price: Option<Price>,
pub subaccount_number: SubaccountNumber,
}
pub type AssetPositionsMap = HashMap<Ticker, AssetPositionResponseObject>;
pub type PerpetualPositionsMap = HashMap<Ticker, PerpetualPositionResponseObject>;
#[derive(
Add,
Deserialize,
Debug,
Clone,
Div,
Display,
Deref,
DerefMut,
PartialEq,
Eq,
Mul,
PartialOrd,
Ord,
Hash,
Sub,
)]
#[serde(transparent)]
pub struct Price(pub BigDecimal);
impl<T> From<T> for Price
where
T: Into<BigDecimal>,
{
fn from(value: T) -> Self {
Self(value.into())
}
}
impl FromStr for Price {
type Err = bigdecimal::ParseBigDecimalError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse().map(Self)
}
}
#[derive(
Add,
Deserialize,
Debug,
Clone,
Div,
Display,
Deref,
DerefMut,
PartialEq,
Eq,
Mul,
PartialOrd,
Ord,
Hash,
Sub,
)]
#[serde(transparent)]
pub struct Quantity(pub BigDecimal);
impl<T> From<T> for Quantity
where
T: Into<BigDecimal>,
{
fn from(value: T) -> Self {
Self(value.into())
}
}
impl FromStr for Quantity {
type Err = bigdecimal::ParseBigDecimalError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse().map(Self)
}
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct OrderbookResponsePriceLevel {
pub price: Price,
pub size: Quantity,
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct OrderBookResponseObject {
pub bids: Vec<OrderbookResponsePriceLevel>,
pub asks: Vec<OrderbookResponsePriceLevel>,
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct OrderResponseObject {
pub client_id: ClientId,
pub client_metadata: ClientMetadata,
pub clob_pair_id: ClobPairId,
pub created_at_height: Option<Height>,
pub good_til_block: Option<Height>,
pub good_til_block_time: Option<DateTime<Utc>>,
pub id: OrderId,
pub order_flags: OrderFlags,
pub post_only: bool,
pub price: Price,
pub reduce_only: bool,
pub side: OrderSide,
pub size: Quantity,
pub status: ApiOrderStatus,
pub subaccount_id: SubaccountId,
pub subaccount_number: SubaccountNumber,
pub ticker: Ticker,
pub time_in_force: ApiTimeInForce,
pub total_filled: BigDecimal,
#[serde(rename = "type")]
pub order_type: OrderType,
pub updated_at: Option<DateTime<Utc>>,
pub updated_at_height: Option<Height>,
pub trigger_price: Option<Price>,
pub fee_ppm: Option<BigDecimal>,
pub builder_address: Option<Address>,
pub order_router_address: Option<Address>,
pub duration: Option<BigDecimal>,
pub interval: Option<BigDecimal>,
pub price_tolerance: Option<BigDecimal>,
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct TradeResponse {
pub trades: Vec<TradeResponseObject>,
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct TradeResponseObject {
pub id: TradeId,
pub created_at_height: Height,
pub created_at: DateTime<Utc>,
pub side: OrderSide,
pub price: Price,
pub size: Quantity,
#[serde(rename = "type")]
pub trade_type: TradeType,
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct PerpetualMarketResponse {
pub markets: HashMap<Ticker, PerpetualMarket>,
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct PerpetualMarket {
pub clob_pair_id: ClobPairId,
pub ticker: Ticker,
pub status: PerpetualMarketStatus,
pub oracle_price: Option<Price>,
#[serde(rename = "priceChange24H")]
pub price_change_24h: BigDecimal,
#[serde(rename = "volume24H")]
pub volume_24h: Quantity,
#[serde(rename = "trades24H")]
pub trades_24h: u64,
pub next_funding_rate: BigDecimal,
pub initial_margin_fraction: BigDecimal,
pub maintenance_margin_fraction: BigDecimal,
pub open_interest: BigDecimal,
pub atomic_resolution: i32,
pub quantum_conversion_exponent: i32,
pub tick_size: BigDecimal,
pub step_size: BigDecimal,
pub step_base_quantums: u64,
pub subticks_per_tick: u32,
pub market_type: PerpetualMarketType,
pub open_interest_lower_cap: Option<BigDecimal>,
pub open_interest_upper_cap: Option<BigDecimal>,
pub base_open_interest: BigDecimal,
#[serde(rename = "defaultFundingRate1H")]
pub default_funding_rate_1h: Option<BigDecimal>,
}
impl PerpetualMarket {
pub fn order_params(&self) -> OrderMarketParams {
OrderMarketParams {
atomic_resolution: self.atomic_resolution,
clob_pair_id: self.clob_pair_id.clone(),
oracle_price: self.oracle_price.clone(),
quantum_conversion_exponent: self.quantum_conversion_exponent,
step_base_quantums: self.step_base_quantums,
subticks_per_tick: self.subticks_per_tick,
}
}
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct CandleResponse {
pub candles: Vec<CandleResponseObject>,
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct CandleResponseObject {
pub started_at: DateTime<Utc>,
pub ticker: Ticker,
pub resolution: CandleResolution,
pub low: Price,
pub high: Price,
pub open: Price,
pub close: Price,
pub base_token_volume: Quantity,
pub usd_volume: Quantity,
pub trades: u64,
pub starting_open_interest: BigDecimal,
pub orderbook_mid_price_open: Option<Price>,
pub orderbook_mid_price_close: Option<Price>,
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
pub struct HeightResponse {
pub height: Height,
pub time: DateTime<Utc>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn denom_parse() {
let _usdc = Denom::Usdc.to_string().parse::<Denom>().unwrap();
let _dydx = Denom::Dydx.to_string().parse::<Denom>().unwrap();
let _dydx_tnt = Denom::DydxTnt.to_string().parse::<Denom>().unwrap();
let _custom: Denom = "uusdc".parse().unwrap();
}
}