nautilus_model/ffi/instruments/
synthetic.rs1use std::ffi::c_char;
17
18use nautilus_core::{
19 UnixNanos,
20 ffi::{
21 abort_on_panic,
22 cvec::CVec,
23 parsing::{bytes_to_string_vec, string_vec_to_bytes},
24 string::{cstr_as_str, str_to_cstr},
25 },
26};
27
28use crate::{
29 identifiers::{InstrumentId, Symbol},
30 instruments::synthetic::SyntheticInstrument,
31 types::{ERROR_PRICE, Price},
32};
33
34#[unsafe(no_mangle)]
49pub unsafe extern "C" fn synthetic_instrument_new(
50 symbol: Symbol,
51 price_precision: u8,
52 components_ptr: *const c_char,
53 formula_ptr: *const c_char,
54 ts_event: u64,
55 ts_init: u64,
56) -> *mut SyntheticInstrument {
57 let components = unsafe { bytes_to_string_vec(components_ptr) }
59 .into_iter()
60 .map(InstrumentId::from)
61 .collect::<Vec<InstrumentId>>();
62 let formula = unsafe { cstr_as_str(formula_ptr) };
63 let synth = SyntheticInstrument::builder()
64 .symbol(symbol)
65 .price_precision(price_precision)
66 .components(components)
67 .formula(formula)
68 .ts_event(ts_event.into())
69 .ts_init(ts_init.into())
70 .build()
71 .unwrap();
72
73 Box::into_raw(Box::new(synth))
74}
75
76#[unsafe(no_mangle)]
85pub unsafe extern "C" fn synthetic_instrument_drop(synth: *mut SyntheticInstrument) {
86 abort_on_panic(|| {
87 assert!(!synth.is_null(), "`synth` was NULL");
88 drop(unsafe { Box::from_raw(synth) }); });
91}
92
93#[unsafe(no_mangle)]
94pub extern "C" fn synthetic_instrument_id(synth: &SyntheticInstrument) -> InstrumentId {
95 synth.id
96}
97
98#[unsafe(no_mangle)]
99pub extern "C" fn synthetic_instrument_price_precision(synth: &SyntheticInstrument) -> u8 {
100 synth.price_precision
101}
102
103#[unsafe(no_mangle)]
104#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
105pub extern "C" fn synthetic_instrument_price_increment(synth: &SyntheticInstrument) -> Price {
106 synth.price_increment
107}
108
109#[unsafe(no_mangle)]
110pub extern "C" fn synthetic_instrument_formula_to_cstr(
111 synth: &SyntheticInstrument,
112) -> *const c_char {
113 str_to_cstr(&synth.formula)
114}
115
116#[unsafe(no_mangle)]
117pub extern "C" fn synthetic_instrument_components_to_cstr(
118 synth: &SyntheticInstrument,
119) -> *const c_char {
120 let components_vec = synth
121 .components
122 .iter()
123 .map(ToString::to_string)
124 .collect::<Vec<String>>();
125
126 string_vec_to_bytes(&components_vec)
127}
128
129#[unsafe(no_mangle)]
130pub extern "C" fn synthetic_instrument_components_count(synth: &SyntheticInstrument) -> usize {
131 synth.components.len()
132}
133
134#[unsafe(no_mangle)]
135pub extern "C" fn synthetic_instrument_ts_event(synth: &SyntheticInstrument) -> UnixNanos {
136 synth.ts_event
137}
138
139#[unsafe(no_mangle)]
140pub extern "C" fn synthetic_instrument_ts_init(synth: &SyntheticInstrument) -> UnixNanos {
141 synth.ts_init
142}
143
144#[unsafe(no_mangle)]
148pub unsafe extern "C" fn synthetic_instrument_is_valid_formula(
149 formula_ptr: *const c_char,
150 components_ptr: *const c_char,
151) -> u8 {
152 if formula_ptr.is_null() || components_ptr.is_null() {
153 return 0;
154 }
155
156 let components = unsafe { bytes_to_string_vec(components_ptr) }
157 .into_iter()
158 .map(InstrumentId::from)
159 .collect::<Vec<InstrumentId>>();
160
161 let formula = unsafe { cstr_as_str(formula_ptr) };
162
163 u8::from(SyntheticInstrument::is_valid_formula_for_components(
164 formula,
165 &components,
166 ))
167}
168
169#[unsafe(no_mangle)]
177pub unsafe extern "C" fn synthetic_instrument_change_formula(
178 synth: &mut SyntheticInstrument,
179 formula_ptr: *const c_char,
180) {
181 let formula = unsafe { cstr_as_str(formula_ptr) };
182 synth.change_formula(formula).unwrap();
183}
184
185#[unsafe(no_mangle)]
186#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
187pub unsafe extern "C" fn synthetic_instrument_calculate(
192 synth: &mut SyntheticInstrument,
193 inputs_ptr: &CVec,
194) -> Price {
195 let inputs = unsafe { inputs_ptr.as_slice::<f64>() };
196
197 match synth.calculate(inputs) {
198 Ok(price) => price,
199 Err(_) => ERROR_PRICE,
200 }
201}
202
203#[cfg(test)]
204mod cvec_tests {
205 use rstest::rstest;
206
207 use super::*;
208
209 #[rstest]
210 fn test_synthetic_calculate_borrows_inputs() {
211 let mut synth = SyntheticInstrument::default();
212 let mut inputs = vec![100.0, 200.0];
213 let cvec = CVec {
214 ptr: inputs.as_mut_ptr().cast(),
215 len: inputs.len(),
216 cap: inputs.capacity(),
217 };
218
219 let price = unsafe { synthetic_instrument_calculate(&mut synth, &cvec) };
220
221 assert_eq!(price, Price::from("150.0"));
222 assert_eq!(inputs, [100.0, 200.0]);
223 }
224}