Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion crates/pyopenquant/src/bars.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use openquant::data_structures::{imbalance_bars, run_bars, standard_bars, time_bars, ImbalanceBarType, StandardBarType};
use openquant::data_structures::{
imbalance_bars, run_bars, standard_bars, time_bars, ImbalanceBarType, StandardBarType,
};
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;

Expand Down
111 changes: 80 additions & 31 deletions crates/pyopenquant/src/bet_sizing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,14 @@ fn bet_sizing_bet_size_power(w_param: f64, price_div: f64) -> PyResult<f64> {
}

#[pyfunction(name = "inv_price")]
fn bet_sizing_inv_price(forecast_price: f64, w_param: f64, m_bet_size: f64, func: String) -> PyResult<f64> {
openquant::bet_sizing::inv_price_checked(forecast_price, w_param, m_bet_size, &func).map_err(to_py_err)
fn bet_sizing_inv_price(
forecast_price: f64,
w_param: f64,
m_bet_size: f64,
func: String,
) -> PyResult<f64> {
openquant::bet_sizing::inv_price_checked(forecast_price, w_param, m_bet_size, &func)
.map_err(to_py_err)
}

#[pyfunction(name = "inv_price_sigmoid")]
Expand Down Expand Up @@ -59,22 +65,45 @@ fn bet_sizing_get_w_power(price_div: f64, m_bet_size: f64) -> PyResult<f64> {
}

#[pyfunction(name = "get_target_pos")]
fn bet_sizing_get_target_pos(w: f64, f: f64, m_p: f64, max_pos: f64, func: String) -> PyResult<f64> {
fn bet_sizing_get_target_pos(
w: f64,
f: f64,
m_p: f64,
max_pos: f64,
func: String,
) -> PyResult<f64> {
openquant::bet_sizing::get_target_pos_checked(w, f, m_p, max_pos, &func).map_err(to_py_err)
}

#[pyfunction(name = "get_target_pos_sigmoid")]
fn bet_sizing_get_target_pos_sigmoid(w_param: f64, forecast_price: f64, market_price: f64, max_pos: f64) -> f64 {
fn bet_sizing_get_target_pos_sigmoid(
w_param: f64,
forecast_price: f64,
market_price: f64,
max_pos: f64,
) -> f64 {
openquant::bet_sizing::get_target_pos_sigmoid(w_param, forecast_price, market_price, max_pos)
}

#[pyfunction(name = "get_target_pos_power")]
fn bet_sizing_get_target_pos_power(w_param: f64, forecast_price: f64, market_price: f64, max_pos: f64) -> f64 {
fn bet_sizing_get_target_pos_power(
w_param: f64,
forecast_price: f64,
market_price: f64,
max_pos: f64,
) -> f64 {
openquant::bet_sizing::get_target_pos_power(w_param, forecast_price, market_price, max_pos)
}

#[pyfunction(name = "limit_price")]
fn bet_sizing_limit_price(t_pos: f64, pos: f64, f: f64, w: f64, max_pos: f64, func: String) -> PyResult<f64> {
fn bet_sizing_limit_price(
t_pos: f64,
pos: f64,
f: f64,
w: f64,
max_pos: f64,
func: String,
) -> PyResult<f64> {
openquant::bet_sizing::limit_price_checked(t_pos, pos, f, w, max_pos, &func).map_err(to_py_err)
}

Expand All @@ -94,13 +123,15 @@ fn bet_sizing_avg_active_signals(
signal_values: Vec<f64>,
t1_timestamps: Vec<String>,
) -> PyResult<Vec<(String, f64)>> {
let signal = pair_timestamps_values(signal_timestamps, signal_values, "signal_timestamps", "signal_values")?;
let signal = pair_timestamps_values(
signal_timestamps,
signal_values,
"signal_timestamps",
"signal_values",
)?;
let t1 = parse_naive_datetimes(t1_timestamps)?;
let result = openquant::bet_sizing::avg_active_signals(&signal, &t1);
Ok(result
.into_iter()
.map(|(ts, v)| (ts.format("%Y-%m-%d %H:%M:%S").to_string(), v))
.collect())
Ok(result.into_iter().map(|(ts, v)| (ts.format("%Y-%m-%d %H:%M:%S").to_string(), v)).collect())
}

#[pyfunction(name = "bet_size_dynamic")]
Expand Down Expand Up @@ -132,7 +163,9 @@ fn bet_sizing_get_concurrent_sides(
let starts = parse_naive_datetimes(t1_starts)?;
let ends = parse_naive_datetimes(t1_ends)?;
if starts.len() != ends.len() || starts.len() != side.len() {
return Err(pyo3::exceptions::PyValueError::new_err("t1_starts/t1_ends/side length mismatch"));
return Err(pyo3::exceptions::PyValueError::new_err(
"t1_starts/t1_ends/side length mismatch",
));
}
let t1: Vec<(chrono::NaiveDateTime, chrono::NaiveDateTime)> =
starts.into_iter().zip(ends).collect();
Expand All @@ -152,15 +185,14 @@ fn bet_sizing_bet_size_budget(
let starts = parse_naive_datetimes(t1_starts)?;
let ends = parse_naive_datetimes(t1_ends)?;
if starts.len() != ends.len() || starts.len() != side.len() {
return Err(pyo3::exceptions::PyValueError::new_err("t1_starts/t1_ends/side length mismatch"));
return Err(pyo3::exceptions::PyValueError::new_err(
"t1_starts/t1_ends/side length mismatch",
));
}
let t1: Vec<(chrono::NaiveDateTime, chrono::NaiveDateTime)> =
starts.into_iter().zip(ends).collect();
let result = openquant::bet_sizing::bet_size_budget(&t1, &side);
Ok(result
.into_iter()
.map(|(ts, v)| (ts.format("%Y-%m-%d %H:%M:%S").to_string(), v))
.collect())
Ok(result.into_iter().map(|(ts, v)| (ts.format("%Y-%m-%d %H:%M:%S").to_string(), v)).collect())
}

#[pyfunction(name = "bet_size_probability")]
Expand All @@ -187,11 +219,13 @@ fn bet_sizing_bet_size_probability(
.zip(sides)
.map(|(((s, e), p), sd)| (s, e, p, sd))
.collect();
let result = openquant::bet_sizing::bet_size_probability(&events, num_classes, step_size, average_active);
Ok(result
.into_iter()
.map(|(ts, v)| (ts.format("%Y-%m-%d %H:%M:%S").to_string(), v))
.collect())
let result = openquant::bet_sizing::bet_size_probability(
&events,
num_classes,
step_size,
average_active,
);
Ok(result.into_iter().map(|(ts, v)| (ts.format("%Y-%m-%d %H:%M:%S").to_string(), v)).collect())
}

#[pyfunction(name = "mp_avg_active_signals")]
Expand All @@ -201,14 +235,16 @@ fn bet_sizing_mp_avg_active_signals(
t1_timestamps: Vec<String>,
molecule_timestamps: Vec<String>,
) -> PyResult<Vec<(String, f64)>> {
let signal = pair_timestamps_values(signal_timestamps, signal_values, "signal_timestamps", "signal_values")?;
let signal = pair_timestamps_values(
signal_timestamps,
signal_values,
"signal_timestamps",
"signal_values",
)?;
let t1 = parse_naive_datetimes(t1_timestamps)?;
let molecule = parse_naive_datetimes(molecule_timestamps)?;
let result = openquant::bet_sizing::mp_avg_active_signals(&signal, &t1, &molecule);
Ok(result
.into_iter()
.map(|(ts, v)| (ts.format("%Y-%m-%d %H:%M:%S").to_string(), v))
.collect())
Ok(result.into_iter().map(|(ts, v)| (ts.format("%Y-%m-%d %H:%M:%S").to_string(), v)).collect())
}

#[pyfunction(name = "bet_size_reserve")]
Expand All @@ -221,7 +257,9 @@ fn bet_sizing_bet_size_reserve(
let starts = parse_naive_datetimes(t1_starts)?;
let ends = parse_naive_datetimes(t1_ends)?;
if starts.len() != ends.len() || starts.len() != side.len() {
return Err(pyo3::exceptions::PyValueError::new_err("t1_starts/t1_ends/side length mismatch"));
return Err(pyo3::exceptions::PyValueError::new_err(
"t1_starts/t1_ends/side length mismatch",
));
}
let t1: Vec<(chrono::NaiveDateTime, chrono::NaiveDateTime)> =
starts.into_iter().zip(ends).collect();
Expand All @@ -242,7 +280,9 @@ fn bet_sizing_bet_size_reserve_with_fit(
let starts = parse_naive_datetimes(t1_starts)?;
let ends = parse_naive_datetimes(t1_ends)?;
if starts.len() != ends.len() || starts.len() != side.len() {
return Err(pyo3::exceptions::PyValueError::new_err("t1_starts/t1_ends/side length mismatch"));
return Err(pyo3::exceptions::PyValueError::new_err(
"t1_starts/t1_ends/side length mismatch",
));
}
let t1: Vec<(chrono::NaiveDateTime, chrono::NaiveDateTime)> =
starts.into_iter().zip(ends).collect();
Expand All @@ -266,11 +306,20 @@ fn bet_sizing_bet_size_reserve_full(
let starts = parse_naive_datetimes(t1_starts)?;
let ends = parse_naive_datetimes(t1_ends)?;
if starts.len() != ends.len() || starts.len() != side.len() {
return Err(pyo3::exceptions::PyValueError::new_err("t1_starts/t1_ends/side length mismatch"));
return Err(pyo3::exceptions::PyValueError::new_err(
"t1_starts/t1_ends/side length mismatch",
));
}
let t1: Vec<(chrono::NaiveDateTime, chrono::NaiveDateTime)> =
starts.into_iter().zip(ends).collect();
let (events, params) = openquant::bet_sizing::bet_size_reserve_full(&t1, &side, fit_runs, epsilon, max_iter, return_parameters);
let (events, params) = openquant::bet_sizing::bet_size_reserve_full(
&t1,
&side,
fit_runs,
epsilon,
max_iter,
return_parameters,
);
let out_events = events
.into_iter()
.map(|(ts, l, s, c, b)| (ts.format("%Y-%m-%d %H:%M:%S").to_string(), l, s, c, b))
Expand Down
4 changes: 1 addition & 3 deletions crates/pyopenquant/src/cla.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ fn cla_allocate(

let prices_m = asset_prices.map(matrix_from_rows).transpose()?;
let cov_m = covariance_matrix.map(matrix_from_rows).transpose()?;
let expected_ret_m = expected_returns.map(|v| {
nalgebra::DMatrix::from_vec(v.len(), 1, v)
});
let expected_ret_m = expected_returns.map(|v| nalgebra::DMatrix::from_vec(v.len(), 1, v));

cla.allocate(
prices_m.as_ref().map(|m| openquant::cla::AssetPricesInput::RawMatrix(m)),
Expand Down
4 changes: 3 additions & 1 deletion crates/pyopenquant/src/data.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use openquant::data_processing::{align_calendar_columns, clean_ohlcv_columns, quality_report_columns};
use openquant::data_processing::{
align_calendar_columns, clean_ohlcv_columns, quality_report_columns,
};
use polars::prelude::DataFrame;
use pyo3::prelude::*;
use pyo3::types::PyDict;
Expand Down
8 changes: 6 additions & 2 deletions crates/pyopenquant/src/ef3m.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use pyo3::prelude::*;
use pyo3::types::PyDict;


#[pyfunction(name = "centered_moment")]
fn ef3m_centered_moment(moments: Vec<f64>, order: usize) -> f64 {
openquant::ef3m::centered_moment(&moments, order)
Expand All @@ -21,7 +20,12 @@ fn ef3m_most_likely_parameters(
let rows: Vec<openquant::ef3m::FitResultRow> = data
.into_iter()
.map(|(mu_1, mu_2, sigma_1, sigma_2, p_1, error)| openquant::ef3m::FitResultRow {
mu_1, mu_2, sigma_1, sigma_2, p_1, error,
mu_1,
mu_2,
sigma_1,
sigma_2,
p_1,
error,
})
.collect();
let result = openquant::ef3m::most_likely_parameters(&rows, None, res);
Expand Down
3 changes: 2 additions & 1 deletion crates/pyopenquant/src/hcaa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ fn hcaa_allocate(
let returns_m = asset_returns.map(matrix_from_rows).transpose()?;
let cov_m = covariance_matrix.map(matrix_from_rows).transpose()?;

let mut hcaa = openquant::hcaa::HierarchicalClusteringAssetAllocation::new(calculate_expected_returns);
let mut hcaa =
openquant::hcaa::HierarchicalClusteringAssetAllocation::new(calculate_expected_returns);
hcaa.allocate(
&asset_names,
prices_m.as_ref(),
Expand Down
18 changes: 16 additions & 2 deletions crates/pyopenquant/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,16 @@ pub fn build_ohlcv_columns(
adj_close.len(),
)));
}
Ok(openquant::data_processing::OhlcvColumns { timestamps_us, symbols, open, high, low, close, volume, adj_close })
Ok(openquant::data_processing::OhlcvColumns {
timestamps_us,
symbols,
open,
high,
low,
close,
volume,
adj_close,
})
}

pub fn report_to_pydict(
Expand Down Expand Up @@ -226,7 +235,12 @@ pub fn build_labeling_events(
&close,
&t_events,
&target,
openquant::labeling::TripleBarrierConfig { pt, sl, min_ret, vertical_barrier_times: vbars.as_deref() },
openquant::labeling::TripleBarrierConfig {
pt,
sl,
min_ret,
vertical_barrier_times: vbars.as_deref(),
},
side_storage.as_deref(),
);
Ok((close, events))
Expand Down
54 changes: 24 additions & 30 deletions crates/pyopenquant/src/labeling.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use pyo3::prelude::*;

use crate::helpers::{
build_labeling_events, pair_timestamps_values,
parse_naive_datetimes, parse_vertical_barriers,
build_labeling_events, pair_timestamps_values, parse_naive_datetimes, parse_vertical_barriers,
};

#[pyfunction(name = "add_vertical_barrier")]
Expand All @@ -16,21 +15,20 @@ fn labeling_add_vertical_barrier(
num_seconds: i64,
) -> PyResult<Vec<(String, String)>> {
let t_events = parse_naive_datetimes(t_events)?;
let close = pair_timestamps_values(
close_timestamps,
close_prices,
"close_timestamps",
"close_prices",
)?;
let barriers =
openquant::labeling::add_vertical_barrier(&t_events, &close, num_days, num_hours, num_minutes, num_seconds);
let close =
pair_timestamps_values(close_timestamps, close_prices, "close_timestamps", "close_prices")?;
let barriers = openquant::labeling::add_vertical_barrier(
&t_events,
&close,
num_days,
num_hours,
num_minutes,
num_seconds,
);
Ok(barriers
.into_iter()
.map(|(a, b)| {
(
a.format("%Y-%m-%d %H:%M:%S").to_string(),
b.format("%Y-%m-%d %H:%M:%S").to_string(),
)
(a.format("%Y-%m-%d %H:%M:%S").to_string(), b.format("%Y-%m-%d %H:%M:%S").to_string())
})
.collect())
}
Expand Down Expand Up @@ -212,12 +210,8 @@ fn labeling_get_events(
vertical_barrier_times: Option<Vec<(String, String)>>,
side_prediction: Option<Vec<(String, f64)>>,
) -> PyResult<Vec<(String, Option<String>, f64, Option<f64>, f64, f64)>> {
let close = pair_timestamps_values(
close_timestamps,
close_prices,
"close_timestamps",
"close_prices",
)?;
let close =
pair_timestamps_values(close_timestamps, close_prices, "close_timestamps", "close_prices")?;
let t_ev = parse_naive_datetimes(t_events)?;
let target = pair_timestamps_values(
target_timestamps,
Expand Down Expand Up @@ -266,21 +260,21 @@ fn labeling_get_bins(
close_timestamps: Vec<String>,
close_prices: Vec<f64>,
) -> PyResult<Vec<(String, f64, f64, i8, Option<f64>)>> {
let close = pair_timestamps_values(
close_timestamps,
close_prices,
"close_timestamps",
"close_prices",
)?;
let close =
pair_timestamps_values(close_timestamps, close_prices, "close_timestamps", "close_prices")?;

let parsed_events: Vec<(chrono::NaiveDateTime, openquant::labeling::Event)> = events
.into_iter()
.map(|(ts_str, t1_str, trgt, side, pt, sl)| {
let ts = chrono::NaiveDateTime::parse_from_str(&ts_str, "%Y-%m-%d %H:%M:%S")
.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("invalid datetime: {e}")))?;
let ts = chrono::NaiveDateTime::parse_from_str(&ts_str, "%Y-%m-%d %H:%M:%S").map_err(
|e| pyo3::exceptions::PyValueError::new_err(format!("invalid datetime: {e}")),
)?;
let t1 = t1_str
.map(|s| chrono::NaiveDateTime::parse_from_str(&s, "%Y-%m-%d %H:%M:%S")
.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("invalid datetime: {e}"))))
.map(|s| {
chrono::NaiveDateTime::parse_from_str(&s, "%Y-%m-%d %H:%M:%S").map_err(|e| {
pyo3::exceptions::PyValueError::new_err(format!("invalid datetime: {e}"))
})
})
.transpose()?;
Ok((ts, openquant::labeling::Event { t1, trgt, side, pt, sl }))
})
Expand Down
Loading
Loading