Skip to content

Commit 0f27799

Browse files
committed
Continue Architect adapter
1 parent 2474047 commit 0f27799

File tree

11 files changed

+948
-221
lines changed

11 files changed

+948
-221
lines changed

crates/adapters/architect/bin/ws_data.rs

Lines changed: 11 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ use futures_util::StreamExt;
3939
use nautilus_architect::{
4040
common::enums::{ArchitectEnvironment, ArchitectMarketDataLevel},
4141
http::{client::ArchitectRawHttpClient, error::ArchitectHttpError},
42-
websocket::{ArchitectMdWsMessage, data::ArchitectMdWebSocketClient},
42+
websocket::{NautilusWsMessage, data::ArchitectMdWebSocketClient},
4343
};
4444
use totp_rs::{Algorithm, Secret, TOTP};
4545

@@ -179,65 +179,24 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
179179
message_count += 1;
180180

181181
match &msg {
182-
ArchitectMdWsMessage::Heartbeat(hb) => {
183-
log::debug!("Heartbeat: ts={}", hb.ts);
182+
NautilusWsMessage::Heartbeat => {
183+
log::debug!("Heartbeat");
184184
}
185-
ArchitectMdWsMessage::Ticker(ticker) => {
186-
log::info!("Ticker: {} price={} vol={}", ticker.s, ticker.p, ticker.v);
187-
}
188-
ArchitectMdWsMessage::Trade(trade) => {
189-
log::info!("Trade: {} {:?} {} @ {}", trade.s, trade.d, trade.q, trade.p);
190-
}
191-
ArchitectMdWsMessage::BookL1(book) => {
192-
let bid = book.b.first().map(|l| format!("{}@{}", l.q, l.p));
193-
let ask = book.a.first().map(|l| format!("{}@{}", l.q, l.p));
194-
log::info!(
195-
"BookL1: {} bid={} ask={}",
196-
book.s,
197-
bid.unwrap_or_default(),
198-
ask.unwrap_or_default()
199-
);
200-
}
201-
ArchitectMdWsMessage::BookL2(book) => {
202-
log::info!(
203-
"BookL2: {} {} bids, {} asks",
204-
book.s,
205-
book.b.len(),
206-
book.a.len()
207-
);
208-
}
209-
ArchitectMdWsMessage::BookL3(book) => {
210-
log::info!(
211-
"BookL3: {} {} bids, {} asks",
212-
book.s,
213-
book.b.len(),
214-
book.a.len()
215-
);
216-
}
217-
ArchitectMdWsMessage::Candle(candle) => {
218-
log::info!(
219-
"Candle: {} {} O={} H={} L={} C={}",
220-
candle.symbol,
221-
candle.width,
222-
candle.open,
223-
candle.high,
224-
candle.low,
225-
candle.close
226-
);
227-
}
228-
ArchitectMdWsMessage::Data(data) => {
229-
log::info!("Data: {} items", data.len());
185+
NautilusWsMessage::Data(data) => {
186+
for item in data {
187+
log::info!("Data: {item:?}");
188+
}
230189
}
231-
ArchitectMdWsMessage::Deltas(deltas) => {
190+
NautilusWsMessage::Deltas(deltas) => {
232191
log::info!("Deltas: {}", deltas.instrument_id);
233192
}
234-
ArchitectMdWsMessage::Bar(bar) => {
193+
NautilusWsMessage::Bar(bar) => {
235194
log::info!("Bar: {}", bar.bar_type);
236195
}
237-
ArchitectMdWsMessage::Error(err) => {
196+
NautilusWsMessage::Error(err) => {
238197
log::error!("Error: {}", err.message);
239198
}
240-
ArchitectMdWsMessage::Reconnected => {
199+
NautilusWsMessage::Reconnected => {
241200
log::warn!("Reconnected");
242201
}
243202
}

crates/adapters/architect/bin/ws_data_capture.rs

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use futures_util::StreamExt;
3131
use nautilus_architect::{
3232
common::enums::{ArchitectEnvironment, ArchitectMarketDataLevel},
3333
http::{client::ArchitectRawHttpClient, error::ArchitectHttpError},
34-
websocket::{ArchitectMdWsMessage, data::ArchitectMdWebSocketClient},
34+
websocket::{NautilusWsMessage, data::ArchitectMdWebSocketClient},
3535
};
3636
use totp_rs::{Algorithm, Secret, TOTP};
3737

@@ -171,24 +171,10 @@ async fn capture_level(
171171

172172
while let Some(msg) = stream.next().await {
173173
let (msg_type, json) = match &msg {
174-
ArchitectMdWsMessage::Heartbeat(hb) => {
175-
("h".to_string(), serde_json::to_string_pretty(hb).unwrap())
176-
}
177-
ArchitectMdWsMessage::Ticker(t) => {
178-
("t".to_string(), serde_json::to_string_pretty(t).unwrap())
179-
}
180-
ArchitectMdWsMessage::Trade(t) => {
181-
("s".to_string(), serde_json::to_string_pretty(t).unwrap())
182-
}
183-
ArchitectMdWsMessage::BookL1(b) => {
184-
("1".to_string(), serde_json::to_string_pretty(b).unwrap())
185-
}
186-
ArchitectMdWsMessage::BookL2(b) => {
187-
("2".to_string(), serde_json::to_string_pretty(b).unwrap())
188-
}
189-
ArchitectMdWsMessage::BookL3(b) => {
190-
("3".to_string(), serde_json::to_string_pretty(b).unwrap())
191-
}
174+
NautilusWsMessage::Heartbeat => ("h".to_string(), "{}".to_string()),
175+
NautilusWsMessage::Data(data) => ("data".to_string(), format!("{data:?}")),
176+
NautilusWsMessage::Deltas(deltas) => ("deltas".to_string(), format!("{deltas:?}")),
177+
NautilusWsMessage::Bar(bar) => ("bar".to_string(), format!("{bar:?}")),
192178
_ => continue,
193179
};
194180

crates/adapters/architect/src/common/parse.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,116 @@ pub use nautilus_core::serialization::{
2020
deserialize_optional_decimal_from_str, deserialize_optional_decimal_or_zero, parse_decimal,
2121
parse_optional_decimal,
2222
};
23+
use nautilus_model::{data::BarSpecification, enums::BarAggregation};
24+
25+
use super::enums::ArchitectCandleWidth;
26+
27+
/// Maps a Nautilus [`BarSpecification`] to an [`ArchitectCandleWidth`].
28+
///
29+
/// # Errors
30+
///
31+
/// Returns an error if the bar specification is not supported by Architect.
32+
pub fn map_bar_spec_to_candle_width(
33+
spec: &BarSpecification,
34+
) -> anyhow::Result<ArchitectCandleWidth> {
35+
match spec.step.get() {
36+
1 => match spec.aggregation {
37+
BarAggregation::Second => Ok(ArchitectCandleWidth::Seconds1),
38+
BarAggregation::Minute => Ok(ArchitectCandleWidth::Minutes1),
39+
BarAggregation::Hour => Ok(ArchitectCandleWidth::Hours1),
40+
BarAggregation::Day => Ok(ArchitectCandleWidth::Days1),
41+
_ => anyhow::bail!("Unsupported bar aggregation: {:?}", spec.aggregation),
42+
},
43+
5 => match spec.aggregation {
44+
BarAggregation::Second => Ok(ArchitectCandleWidth::Seconds5),
45+
BarAggregation::Minute => Ok(ArchitectCandleWidth::Minutes5),
46+
_ => anyhow::bail!(
47+
"Unsupported bar step 5 with aggregation {:?}",
48+
spec.aggregation
49+
),
50+
},
51+
15 if spec.aggregation == BarAggregation::Minute => Ok(ArchitectCandleWidth::Minutes15),
52+
step => anyhow::bail!(
53+
"Unsupported bar step: {step} with aggregation {:?}",
54+
spec.aggregation
55+
),
56+
}
57+
}
58+
59+
#[cfg(test)]
60+
mod tests {
61+
use nautilus_model::enums::PriceType;
62+
use rstest::rstest;
63+
64+
use super::*;
65+
66+
#[rstest]
67+
fn test_map_bar_spec_1_second() {
68+
let spec = BarSpecification::new(1, BarAggregation::Second, PriceType::Last);
69+
let result = map_bar_spec_to_candle_width(&spec);
70+
assert!(result.is_ok());
71+
assert!(matches!(result.unwrap(), ArchitectCandleWidth::Seconds1));
72+
}
73+
74+
#[rstest]
75+
fn test_map_bar_spec_5_second() {
76+
let spec = BarSpecification::new(5, BarAggregation::Second, PriceType::Last);
77+
let result = map_bar_spec_to_candle_width(&spec);
78+
assert!(result.is_ok());
79+
assert!(matches!(result.unwrap(), ArchitectCandleWidth::Seconds5));
80+
}
81+
82+
#[rstest]
83+
fn test_map_bar_spec_1_minute() {
84+
let spec = BarSpecification::new(1, BarAggregation::Minute, PriceType::Last);
85+
let result = map_bar_spec_to_candle_width(&spec);
86+
assert!(result.is_ok());
87+
assert!(matches!(result.unwrap(), ArchitectCandleWidth::Minutes1));
88+
}
89+
90+
#[rstest]
91+
fn test_map_bar_spec_5_minute() {
92+
let spec = BarSpecification::new(5, BarAggregation::Minute, PriceType::Last);
93+
let result = map_bar_spec_to_candle_width(&spec);
94+
assert!(result.is_ok());
95+
assert!(matches!(result.unwrap(), ArchitectCandleWidth::Minutes5));
96+
}
97+
98+
#[rstest]
99+
fn test_map_bar_spec_15_minute() {
100+
let spec = BarSpecification::new(15, BarAggregation::Minute, PriceType::Last);
101+
let result = map_bar_spec_to_candle_width(&spec);
102+
assert!(result.is_ok());
103+
assert!(matches!(result.unwrap(), ArchitectCandleWidth::Minutes15));
104+
}
105+
106+
#[rstest]
107+
fn test_map_bar_spec_1_hour() {
108+
let spec = BarSpecification::new(1, BarAggregation::Hour, PriceType::Last);
109+
let result = map_bar_spec_to_candle_width(&spec);
110+
assert!(result.is_ok());
111+
assert!(matches!(result.unwrap(), ArchitectCandleWidth::Hours1));
112+
}
113+
114+
#[rstest]
115+
fn test_map_bar_spec_1_day() {
116+
let spec = BarSpecification::new(1, BarAggregation::Day, PriceType::Last);
117+
let result = map_bar_spec_to_candle_width(&spec);
118+
assert!(result.is_ok());
119+
assert!(matches!(result.unwrap(), ArchitectCandleWidth::Days1));
120+
}
121+
122+
#[rstest]
123+
fn test_map_bar_spec_unsupported_step() {
124+
let spec = BarSpecification::new(3, BarAggregation::Minute, PriceType::Last);
125+
let result = map_bar_spec_to_candle_width(&spec);
126+
assert!(result.is_err());
127+
}
128+
129+
#[rstest]
130+
fn test_map_bar_spec_unsupported_aggregation() {
131+
let spec = BarSpecification::new(1, BarAggregation::Tick, PriceType::Last);
132+
let result = map_bar_spec_to_candle_width(&spec);
133+
assert!(result.is_err());
134+
}
135+
}

0 commit comments

Comments
 (0)