Skip to content

Commit a638d9d

Browse files
committed
Improve databento crate feature flagging
1 parent 741ab73 commit a638d9d

File tree

6 files changed

+219
-17
lines changed

6 files changed

+219
-17
lines changed

Cargo.lock

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

crates/adapters/databento/Cargo.toml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ name = "nautilus_databento"
1818
crate-type = ["rlib", "cdylib"]
1919

2020
[features]
21-
default = []
21+
default = ["live"]
2222
extension-module = [
2323
"pyo3/extension-module",
2424
"nautilus-core/extension-module",
2525
"nautilus-model/extension-module",
2626
]
27+
live = ["nautilus-live", "nautilus-system", "dotenvy", "tracing-subscriber", "pyo3"]
2728
python = [
2829
"pyo3",
2930
"pyo3-async-runtimes",
@@ -40,11 +41,14 @@ rustdoc-args = ["--cfg", "docsrs"]
4041
nautilus-common = { workspace = true }
4142
nautilus-core = { workspace = true, features = ["python"] }
4243
nautilus-data = { workspace = true }
44+
nautilus-live = { workspace = true, optional = true }
4345
nautilus-model = { workspace = true }
46+
nautilus-system = { workspace = true, optional = true }
4447

4548
ahash = { workspace = true }
4649
anyhow = { workspace = true }
4750
async-trait = { workspace = true }
51+
dotenvy = { workspace = true, optional = true }
4852
indexmap = { workspace = true }
4953
itoa = { workspace = true }
5054
log = { workspace = true }
@@ -55,6 +59,7 @@ serde_json = { workspace = true }
5559
strum = { workspace = true }
5660
tokio = { workspace = true }
5761
tracing = { workspace = true }
62+
tracing-subscriber = { workspace = true, optional = true }
5863
ustr = { workspace = true }
5964
databento = "0.26.0"
6065
fallible-streaming-iterator = "0.1.9"
@@ -65,9 +70,13 @@ nautilus-testkit = { workspace = true }
6570
criterion = { workspace = true }
6671
rstest = { workspace = true }
6772
tracing-test = { workspace = true }
68-
tracing-subscriber = { workspace = true }
6973

7074
[[bin]]
7175
name = "databento-sandbox"
7276
path = "bin/sandbox.rs"
7377
required-features = ["python"]
78+
79+
[[bin]]
80+
name = "databento-node-test"
81+
path = "bin/node_test.rs"
82+
required-features = ["live"]
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// -------------------------------------------------------------------------------------------------
2+
// Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3+
// https://nautechsystems.io
4+
//
5+
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6+
// You may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
// -------------------------------------------------------------------------------------------------
15+
16+
use std::path::PathBuf;
17+
18+
use nautilus_common::enums::Environment;
19+
use nautilus_core::env::get_env_var;
20+
use nautilus_databento::factories::{DatabentoDataClientFactory, DatabentoLiveClientConfig};
21+
use nautilus_live::node::LiveNode;
22+
use nautilus_model::identifiers::TraderId;
23+
use tokio::time::Duration;
24+
25+
// Run with `cargo run -p nautilus-databento --bin databento-node-test --features live`
26+
27+
#[tokio::main]
28+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
29+
// TODO: Initialize Python interpreter only if python feature is enabled
30+
// #[cfg(feature = "python")]
31+
pyo3::prepare_freethreaded_python();
32+
33+
tracing_subscriber::fmt()
34+
.with_max_level(tracing::Level::INFO)
35+
.init();
36+
37+
dotenvy::dotenv().ok();
38+
39+
let environment = Environment::Live;
40+
let trader_id = TraderId::default();
41+
let node_name = "DATABENTO-TESTER-001".to_string();
42+
43+
// Get Databento API key from environment
44+
let api_key = get_env_var("DATABENTO_API_KEY").unwrap_or_else(|_| {
45+
println!("⚠️ DATABENTO_API_KEY not found, using placeholder");
46+
"db-placeholder-key".to_string()
47+
});
48+
49+
// Determine publishers file path
50+
let publishers_filepath = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("publishers.json");
51+
if !publishers_filepath.exists() {
52+
println!(
53+
"⚠️ Publishers file not found at: {}",
54+
publishers_filepath.display()
55+
);
56+
println!(" This is expected in CI/test environments");
57+
}
58+
59+
// Configure Databento client
60+
let databento_config = DatabentoLiveClientConfig::new(
61+
api_key,
62+
publishers_filepath,
63+
true, // use_exchange_as_venue
64+
true, // bars_timestamp_on_close
65+
);
66+
67+
let client_factory = Box::new(DatabentoDataClientFactory::new());
68+
69+
// Build the live node with Databento data client
70+
let mut node = LiveNode::builder(node_name, trader_id, environment)?
71+
.with_load_state(false)
72+
.with_save_state(false)
73+
.add_data_client(
74+
Some("DATABENTO".to_string()), // Use custom name
75+
client_factory,
76+
Box::new(databento_config),
77+
)?
78+
.build()?;
79+
80+
node.start().await?;
81+
82+
// Let it run briefly to ensure all components are properly initialized
83+
tokio::time::sleep(Duration::from_millis(100)).await;
84+
85+
node.stop().await?;
86+
87+
Ok(())
88+
}
89+
90+
#[cfg(not(feature = "live"))]
91+
fn main() {
92+
println!("⚠️ databento-node-test binary requires the 'live' feature to be enabled.");
93+
println!(
94+
" Run with: cargo run -p nautilus-databento --bin databento-node-test --features live"
95+
);
96+
std::process::exit(1);
97+
}

crates/adapters/databento/src/factories.rs

Lines changed: 93 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,67 @@
1515

1616
//! Factory functions for creating Databento clients and components.
1717
18-
use std::path::PathBuf;
18+
use std::{any::Any, cell::RefCell, path::PathBuf, rc::Rc};
1919

20-
use nautilus_core::time::AtomicTime;
20+
use nautilus_common::{cache::Cache, clock::Clock};
21+
use nautilus_core::time::{AtomicTime, get_atomic_clock_realtime};
22+
use nautilus_data::client::DataClient;
2123
use nautilus_model::identifiers::ClientId;
24+
use nautilus_system::factories::{ClientConfig, DataClientFactory};
2225

2326
use crate::{
2427
data::{DatabentoDataClient, DatabentoDataClientConfig},
2528
historical::DatabentoHistoricalClient,
2629
};
2730

31+
/// Configuration for Databento data clients used with `LiveNode`.
32+
#[derive(Debug, Clone)]
33+
pub struct DatabentoLiveClientConfig {
34+
/// Databento API key.
35+
pub api_key: String,
36+
/// Path to publishers.json file.
37+
pub publishers_filepath: PathBuf,
38+
/// Whether to use exchange as venue for GLBX instruments.
39+
pub use_exchange_as_venue: bool,
40+
/// Whether to timestamp bars on close.
41+
pub bars_timestamp_on_close: bool,
42+
}
43+
44+
impl DatabentoLiveClientConfig {
45+
/// Creates a new [`DatabentoLiveClientConfig`] instance.
46+
#[must_use]
47+
pub const fn new(
48+
api_key: String,
49+
publishers_filepath: PathBuf,
50+
use_exchange_as_venue: bool,
51+
bars_timestamp_on_close: bool,
52+
) -> Self {
53+
Self {
54+
api_key,
55+
publishers_filepath,
56+
use_exchange_as_venue,
57+
bars_timestamp_on_close,
58+
}
59+
}
60+
}
61+
62+
impl ClientConfig for DatabentoLiveClientConfig {
63+
fn as_any(&self) -> &dyn Any {
64+
self
65+
}
66+
}
67+
2868
/// Factory for creating Databento data clients.
29-
#[cfg_attr(feature = "python", pyo3::pyclass)]
3069
#[derive(Debug)]
3170
pub struct DatabentoDataClientFactory;
3271

3372
impl DatabentoDataClientFactory {
73+
/// Creates a new [`DatabentoDataClientFactory`] instance.
74+
#[must_use]
75+
pub const fn new() -> Self {
76+
Self
77+
}
78+
3479
/// Creates a new [`DatabentoDataClient`] instance.
3580
///
3681
/// # Errors
@@ -68,20 +113,58 @@ impl DatabentoDataClientFactory {
68113
}
69114
}
70115

116+
impl Default for DatabentoDataClientFactory {
117+
fn default() -> Self {
118+
Self::new()
119+
}
120+
}
121+
122+
impl DataClientFactory for DatabentoDataClientFactory {
123+
fn create(
124+
&self,
125+
name: &str,
126+
config: &dyn ClientConfig,
127+
_cache: Rc<RefCell<Cache>>,
128+
_clock: Rc<RefCell<dyn Clock>>,
129+
) -> anyhow::Result<Box<dyn DataClient>> {
130+
let databento_config = config
131+
.as_any()
132+
.downcast_ref::<DatabentoLiveClientConfig>()
133+
.ok_or_else(|| {
134+
anyhow::anyhow!(
135+
"Invalid config type for DatabentoDataClientFactory. Expected DatabentoLiveClientConfig, got {:?}",
136+
config
137+
)
138+
})?;
139+
140+
let client_id = ClientId::from(name);
141+
let config = DatabentoDataClientConfig::new(
142+
databento_config.api_key.clone(),
143+
databento_config.publishers_filepath.clone(),
144+
databento_config.use_exchange_as_venue,
145+
databento_config.bars_timestamp_on_close,
146+
);
147+
148+
let client = DatabentoDataClient::new(client_id, config, get_atomic_clock_realtime())?;
149+
Ok(Box::new(client))
150+
}
151+
152+
fn name(&self) -> &'static str {
153+
"DATABENTO"
154+
}
155+
156+
fn config_type(&self) -> &'static str {
157+
"DatabentoLiveClientConfig"
158+
}
159+
}
160+
71161
/// Factory for creating Databento historical clients.
72162
#[derive(Debug)]
73163
pub struct DatabentoHistoricalClientFactory;
74164

75165
impl DatabentoHistoricalClientFactory {
76166
/// Creates a new [`DatabentoHistoricalClient`] instance.
77167
///
78-
/// # Arguments
79-
///
80-
/// * `api_key` - Databento API key for authentication
81-
/// * `publishers_filepath` - Path to the publishers.json configuration file
82-
/// * `use_exchange_as_venue` - Whether to use exchange codes as venues for GLBX instruments
83-
/// * `clock` - Atomic clock for timestamping
84-
///
85168
/// # Errors
86169
///
87170
/// Returns an error if the client cannot be created or publisher configuration cannot be loaded.

crates/adapters/databento/src/lib.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,21 @@
3838
#![deny(rustdoc::broken_intra_doc_links)]
3939

4040
pub mod common;
41-
pub mod data;
4241
pub mod decode;
4342
pub mod enums;
44-
pub mod factories;
4543
pub mod historical;
46-
pub mod live;
4744
pub mod loader;
4845
pub mod symbology;
4946
pub mod types;
5047

5148
#[cfg(feature = "python")]
5249
pub mod python;
50+
51+
#[cfg(feature = "live")]
52+
pub mod data;
53+
54+
#[cfg(feature = "live")]
55+
pub mod factories;
56+
57+
#[cfg(feature = "live")]
58+
pub mod live;

crates/adapters/databento/src/python/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
1818
pub mod enums;
1919
pub mod historical;
20-
pub mod live;
2120
pub mod loader;
2221
pub mod types;
2322

23+
#[cfg(feature = "live")]
24+
pub mod live;
25+
2426
use pyo3::prelude::*;
2527

2628
/// Loaded as `nautilus_pyo3.databento`
@@ -36,7 +38,9 @@ pub fn databento(_: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
3638
m.add_class::<super::types::DatabentoStatistics>()?;
3739
m.add_class::<super::types::DatabentoImbalance>()?;
3840
m.add_class::<super::loader::DatabentoDataLoader>()?;
39-
m.add_class::<live::DatabentoLiveClient>()?;
4041
m.add_class::<historical::DatabentoHistoricalClient>()?;
42+
43+
#[cfg(feature = "live")]
44+
m.add_class::<live::DatabentoLiveClient>()?;
4145
Ok(())
4246
}

0 commit comments

Comments
 (0)