nautilus_tardis/python/
http.rs

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
16use std::str::FromStr;
17
18use chrono::DateTime;
19use nautilus_core::{
20    UnixNanos,
21    python::{IntoPyObjectNautilusExt, to_pyruntime_err, to_pyvalue_err},
22};
23use nautilus_model::python::instruments::instrument_any_to_pyobject;
24use pyo3::prelude::*;
25
26use crate::{
27    enums::Exchange,
28    http::{TardisHttpClient, query::InstrumentFilterBuilder},
29};
30
31#[pymethods]
32impl TardisHttpClient {
33    #[new]
34    #[pyo3(signature = (api_key=None, base_url=None, timeout_secs=None, normalize_symbols=true))]
35    fn py_new(
36        api_key: Option<&str>,
37        base_url: Option<&str>,
38        timeout_secs: Option<u64>,
39        normalize_symbols: bool,
40    ) -> PyResult<Self> {
41        Self::new(api_key, base_url, timeout_secs, normalize_symbols).map_err(to_pyruntime_err)
42    }
43
44    #[allow(clippy::too_many_arguments)]
45    #[pyo3(name = "instruments")]
46    #[pyo3(signature = (exchange, symbol=None, base_currency=None, quote_currency=None, instrument_type=None, contract_type=None, active=None, start=None, end=None, effective=None, ts_init=None))]
47    fn py_instruments<'py>(
48        &self,
49        exchange: String,
50        symbol: Option<String>,
51        base_currency: Option<Vec<String>>,
52        quote_currency: Option<Vec<String>>,
53        instrument_type: Option<Vec<String>>,
54        contract_type: Option<Vec<String>>,
55        active: Option<bool>,
56        start: Option<u64>,
57        end: Option<u64>,
58        effective: Option<u64>,
59        ts_init: Option<u64>,
60        py: Python<'py>,
61    ) -> PyResult<Bound<'py, PyAny>> {
62        let exchange = Exchange::from_str(&exchange).map_err(to_pyvalue_err)?;
63
64        let filter = InstrumentFilterBuilder::default()
65            .base_currency(base_currency)
66            .quote_currency(quote_currency)
67            .instrument_type(instrument_type)
68            .contract_type(contract_type)
69            .active(active)
70            .available_since(start.map(|x| DateTime::from_timestamp_nanos(x as i64)))
71            .available_to(end.map(|x| DateTime::from_timestamp_nanos(x as i64)))
72            .build()
73            .unwrap(); // SAFETY: Safe since all fields are Option
74
75        let self_clone = self.clone();
76
77        pyo3_async_runtimes::tokio::future_into_py(py, async move {
78            let instruments = self_clone
79                .instruments(
80                    exchange,
81                    symbol.as_deref(),
82                    Some(&filter),
83                    effective.map(UnixNanos::from),
84                    ts_init.map(UnixNanos::from),
85                )
86                .await
87                .map_err(to_pyruntime_err)?;
88
89            Python::with_gil(|py| {
90                let mut py_instruments = Vec::new();
91                for inst in instruments {
92                    py_instruments.push(instrument_any_to_pyobject(py, inst)?);
93                }
94                Ok(py_instruments.into_py_any_unwrap(py))
95            })
96        })
97    }
98}