Skip to content

Commit 7dca052

Browse files
committed
fix: mexc klines wiring
1 parent d763402 commit 7dca052

File tree

3 files changed

+152
-14
lines changed

3 files changed

+152
-14
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

v_exchanges/src/mexc/market.rs

Lines changed: 141 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,168 @@
1+
use std::collections::{BTreeMap, VecDeque};
2+
13
use adapters::{
24
Client,
35
mexc::{MexcHttpUrl, MexcOption},
46
};
5-
use v_utils::prelude::*;
7+
use jiff::Timestamp;
8+
use serde_json::json;
9+
use v_utils::{
10+
prelude::*,
11+
trades::{Kline, Ohlc},
12+
};
613

7-
use crate::ExchangeResult;
14+
use crate::{
15+
ExchangeResult, RequestRange, Symbol,
16+
core::{ExchangeInfo, Klines, PairInfo},
17+
mexc::MexcTimeframe,
18+
};
819

920
//TODO: impl spot
1021
pub async fn price(client: &Client, pair: Pair) -> ExchangeResult<f64> {
1122
let endpoint = format!("/api/v1/contract/index_price/{}", pair.fmt_mexc());
1223
let options = vec![MexcOption::HttpUrl(MexcHttpUrl::Futures)];
13-
let r: PriceResponse = client.get_no_query(&endpoint, options).await.unwrap();
24+
let r: PriceResponse = client.get_no_query(&endpoint, options).await?;
1425
Ok(r.data.into())
1526
}
1627

17-
#[allow(unused)]
18-
#[derive(Clone, Debug, Default, Deserialize, derive_new::new)]
28+
#[derive(Clone, Debug, Default, Deserialize)]
1929
struct PriceResponse {
20-
pub code: i32,
2130
pub data: PriceData,
22-
pub success: bool,
2331
}
2432

25-
#[allow(unused)]
2633
#[derive(Clone, Debug, Default, Deserialize)]
2734
#[serde(rename_all = "camelCase")]
2835
struct PriceData {
2936
index_price: f64,
30-
symbol: String,
31-
timestamp: i64,
3237
}
3338
impl From<PriceData> for f64 {
3439
fn from(data: PriceData) -> f64 {
3540
data.index_price
3641
}
3742
}
43+
44+
// klines {{{
45+
pub async fn klines(client: &Client, symbol: Symbol, tf: MexcTimeframe, range: RequestRange) -> ExchangeResult<Klines> {
46+
let mexc_symbol = symbol.pair.fmt_mexc();
47+
48+
// Convert timeframe to Mexc format: 1m -> Min1, 5m -> Min5, 1h -> Min60, 4h -> Hour4, 1d -> Day1
49+
let tf_str = tf.to_string();
50+
let interval = match tf_str.as_str() {
51+
"1m" => "Min1",
52+
"5m" => "Min5",
53+
"15m" => "Min15",
54+
"30m" => "Min30",
55+
"60m" => "Min60",
56+
"4h" => "Hour4",
57+
"1d" => "Day1",
58+
"1W" => "Week1",
59+
"1M" => "Month1",
60+
_ => return Err(eyre::eyre!("Unsupported timeframe: {}", tf_str).into()),
61+
};
62+
63+
let (start, end) = match range {
64+
RequestRange::Span { since, until } => {
65+
let s = since.as_second();
66+
let e = until.map(|t| t.as_second()).unwrap_or_else(|| Timestamp::now().as_second());
67+
(s, e)
68+
}
69+
RequestRange::Limit(n) => {
70+
let end = Timestamp::now();
71+
let start = end - tf.duration() * n as u32;
72+
(start.as_second(), end.as_second())
73+
}
74+
};
75+
76+
let endpoint = format!("/api/v1/contract/kline/{}", mexc_symbol);
77+
let params = json!({
78+
"interval": interval,
79+
"start": start,
80+
"end": end,
81+
});
82+
let options = vec![MexcOption::HttpUrl(MexcHttpUrl::Futures)];
83+
let response: KlineResponse = client.get(&endpoint, &params, options).await?;
84+
85+
let mut klines_vec = VecDeque::new();
86+
let data = response.data;
87+
88+
// Mexc returns separate arrays for each field
89+
for i in 0..data.time.len() {
90+
let ohlc = Ohlc {
91+
open: data.open[i],
92+
high: data.high[i],
93+
low: data.low[i],
94+
close: data.close[i],
95+
};
96+
97+
klines_vec.push_back(Kline {
98+
open_time: Timestamp::from_second(data.time[i]).map_err(|e| eyre::eyre!("Invalid timestamp: {}", e))?,
99+
ohlc,
100+
volume_quote: data.amount[i],
101+
trades: None,
102+
taker_buy_volume_quote: None,
103+
});
104+
}
105+
106+
Ok(Klines::new(klines_vec, *tf))
107+
}
108+
109+
#[derive(Debug, Deserialize)]
110+
struct KlineResponse {
111+
data: KlineData,
112+
}
113+
114+
#[derive(Debug, Deserialize)]
115+
struct KlineData {
116+
time: Vec<i64>,
117+
open: Vec<f64>,
118+
close: Vec<f64>,
119+
high: Vec<f64>,
120+
low: Vec<f64>,
121+
vol: Vec<f64>,
122+
amount: Vec<f64>,
123+
}
124+
//,}}}
125+
126+
// exchange_info {{{
127+
pub async fn exchange_info(client: &Client) -> ExchangeResult<ExchangeInfo> {
128+
let options = vec![MexcOption::HttpUrl(MexcHttpUrl::Futures)];
129+
let response: ContractDetailResponse = client.get_no_query("/api/v1/contract/detail", options).await?;
130+
131+
let mut pairs = BTreeMap::new();
132+
133+
for contract in response.data {
134+
// state 0 = active
135+
if contract.state != 0 {
136+
continue;
137+
}
138+
139+
let pair = Pair::new(contract.base_coin.as_str(), contract.quote_coin.as_str());
140+
141+
// priceScale is number of decimal places
142+
let price_precision = contract.price_scale as u8;
143+
144+
let pair_info = PairInfo { price_precision };
145+
pairs.insert(pair, pair_info);
146+
}
147+
148+
Ok(ExchangeInfo {
149+
server_time: Timestamp::now(),
150+
pairs,
151+
})
152+
}
153+
154+
#[derive(Debug, Deserialize)]
155+
struct ContractDetailResponse {
156+
data: Vec<ContractInfo>,
157+
}
158+
159+
#[derive(Debug, Deserialize)]
160+
#[serde(rename_all = "camelCase")]
161+
struct ContractInfo {
162+
symbol: String,
163+
base_coin: String,
164+
quote_coin: String,
165+
price_scale: i32,
166+
state: i32,
167+
}
168+
//,}}}

v_exchanges/src/mexc/mod.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ use adapters::mexc::{MexcOption, MexcOptions};
77
use derive_more::derive::{Deref, DerefMut};
88
use secrecy::SecretString;
99
use v_exchanges_adapters::{Client, GetOptions};
10-
use v_utils::trades::{Asset, Pair};
10+
use v_utils::trades::{Asset, Pair, Timeframe};
1111

1212
use crate::{
1313
Balances, ExchangeName, ExchangeResult, Instrument, Symbol,
14-
core::{AssetBalance, ExchangeImpl},
14+
core::{AssetBalance, ExchangeImpl, Klines, RequestRange},
1515
};
1616

1717
#[derive(Clone, Debug, Default, Deref, DerefMut)]
@@ -51,6 +51,13 @@ impl ExchangeImpl for Mexc {
5151
}
5252
}
5353

54+
async fn klines(&self, symbol: Symbol, tf: Timeframe, range: RequestRange) -> ExchangeResult<Klines> {
55+
match symbol.instrument {
56+
Instrument::Perp => market::klines(self, symbol, tf.try_into()?, range).await,
57+
_ => unimplemented!(),
58+
}
59+
}
60+
5461
async fn asset_balance(&self, asset: Asset, instrument: Instrument, recv_window: Option<std::time::Duration>) -> ExchangeResult<AssetBalance> {
5562
match instrument {
5663
Instrument::Perp => account::asset_balance(self, asset, recv_window).await,

0 commit comments

Comments
 (0)