use std::ffi::c_char;
use nautilus_core::{
UnixNanos,
ffi::{
abort_on_panic,
cvec::CVec,
parsing::{bytes_to_string_vec, string_vec_to_bytes},
string::{cstr_as_str, str_to_cstr},
},
};
use crate::{
identifiers::{InstrumentId, Symbol},
instruments::synthetic::SyntheticInstrument,
types::{ERROR_PRICE, Price},
};
#[unsafe(no_mangle)]
pub unsafe extern "C" fn synthetic_instrument_new(
symbol: Symbol,
price_precision: u8,
components_ptr: *const c_char,
formula_ptr: *const c_char,
ts_event: u64,
ts_init: u64,
) -> *mut SyntheticInstrument {
let components = unsafe { bytes_to_string_vec(components_ptr) }
.into_iter()
.map(InstrumentId::from)
.collect::<Vec<InstrumentId>>();
let formula = unsafe { cstr_as_str(formula_ptr) };
let synth = SyntheticInstrument::new(
symbol,
price_precision,
components,
formula,
ts_event.into(),
ts_init.into(),
);
Box::into_raw(Box::new(synth))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn synthetic_instrument_drop(synth: *mut SyntheticInstrument) {
abort_on_panic(|| {
assert!(!synth.is_null(), "`synth` was NULL");
drop(unsafe { Box::from_raw(synth) }); });
}
#[unsafe(no_mangle)]
pub extern "C" fn synthetic_instrument_id(synth: &SyntheticInstrument) -> InstrumentId {
synth.id
}
#[unsafe(no_mangle)]
pub extern "C" fn synthetic_instrument_price_precision(synth: &SyntheticInstrument) -> u8 {
synth.price_precision
}
#[unsafe(no_mangle)]
#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
pub extern "C" fn synthetic_instrument_price_increment(synth: &SyntheticInstrument) -> Price {
synth.price_increment
}
#[unsafe(no_mangle)]
pub extern "C" fn synthetic_instrument_formula_to_cstr(
synth: &SyntheticInstrument,
) -> *const c_char {
str_to_cstr(&synth.formula)
}
#[unsafe(no_mangle)]
pub extern "C" fn synthetic_instrument_components_to_cstr(
synth: &SyntheticInstrument,
) -> *const c_char {
let components_vec = synth
.components
.iter()
.map(ToString::to_string)
.collect::<Vec<String>>();
string_vec_to_bytes(&components_vec)
}
#[unsafe(no_mangle)]
pub extern "C" fn synthetic_instrument_components_count(synth: &SyntheticInstrument) -> usize {
synth.components.len()
}
#[unsafe(no_mangle)]
pub extern "C" fn synthetic_instrument_ts_event(synth: &SyntheticInstrument) -> UnixNanos {
synth.ts_event
}
#[unsafe(no_mangle)]
pub extern "C" fn synthetic_instrument_ts_init(synth: &SyntheticInstrument) -> UnixNanos {
synth.ts_init
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn synthetic_instrument_is_valid_formula(
formula_ptr: *const c_char,
components_ptr: *const c_char,
) -> u8 {
if formula_ptr.is_null() || components_ptr.is_null() {
return 0;
}
let components = unsafe { bytes_to_string_vec(components_ptr) }
.into_iter()
.map(InstrumentId::from)
.collect::<Vec<InstrumentId>>();
let formula = unsafe { cstr_as_str(formula_ptr) };
u8::from(SyntheticInstrument::is_valid_formula_for_components(
formula,
&components,
))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn synthetic_instrument_change_formula(
synth: &mut SyntheticInstrument,
formula_ptr: *const c_char,
) {
let formula = unsafe { cstr_as_str(formula_ptr) };
synth.change_formula(formula).unwrap();
}
#[unsafe(no_mangle)]
#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
pub unsafe extern "C" fn synthetic_instrument_calculate(
synth: &mut SyntheticInstrument,
inputs_ptr: &CVec,
) -> Price {
let inputs = unsafe { inputs_ptr.as_slice::<f64>() };
match synth.calculate(inputs) {
Ok(price) => price,
Err(_) => ERROR_PRICE,
}
}
#[cfg(test)]
mod cvec_tests {
use rstest::rstest;
use super::*;
#[rstest]
fn test_synthetic_calculate_borrows_inputs() {
let mut synth = SyntheticInstrument::default();
let mut inputs = vec![100.0, 200.0];
let cvec = CVec {
ptr: inputs.as_mut_ptr().cast(),
len: inputs.len(),
cap: inputs.capacity(),
};
let price = unsafe { synthetic_instrument_calculate(&mut synth, &cvec) };
assert_eq!(price, Price::from("150.0"));
assert_eq!(inputs, [100.0, 200.0]);
}
}