MCPcopy Create free account
hub / github.com/Open-Quant/openquant / fit_two_normal_mixture_em

Function fit_two_normal_mixture_em

crates/openquant/src/bet_sizing.rs:435–512  ·  view source on GitHub ↗
(
    samples: &[f64],
    fit_runs: usize,
    epsilon: f64,
    max_iter: usize,
)

Source from the content-addressed store, hash-verified

433///
434/// # Errors
435///
436/// [`BetSizingError::PriceDivergenceOutOfRange`] if `price_div` is outside `[-1, 1]`.
437pub fn get_w_power(price_div: f64, m_bet_size: f64) -> Result<f64, BetSizingError> {
438 if !(-1.0..=1.0).contains(&price_div) {
439 return Err(BetSizingError::PriceDivergenceOutOfRange { value: price_div });
440 }
441 let w_calc = (m_bet_size / price_div.signum()).ln() / price_div.abs().ln();
442 if w_calc < 0.0 {
443 return Ok(0.0);
444 }
445 Ok(w_calc)
446}
447
448/// Sigmoid target position `trunc(m(forecast - market) * max_pos)` (AFML Snippet 10.4).
449pub fn get_target_pos_sigmoid(
450 w_param: f64,
451 forecast_price: f64,
452 market_price: f64,
453 max_pos: f64,
454) -> f64 {
455 (bet_size_sigmoid(w_param, forecast_price - market_price) * max_pos).trunc()
456}
457
458/// Power-curve target position `trunc(m(forecast - market) * max_pos)`.
459///
460/// # Errors
461///
462/// [`BetSizingError::PriceDivergenceOutOfRange`] if `forecast_price - market_price` is
463/// outside `[-1, 1]`.
464pub fn get_target_pos_power(
465 w_param: f64,
466 forecast_price: f64,
467 market_price: f64,
468 max_pos: f64,
469) -> Result<f64, BetSizingError> {
470 Ok((bet_size_power(w_param, forecast_price - market_price)? * max_pos).trunc())
471}
472
473/// Averages `inv(k / max_pos)` over the positions `k` passed through on the way from `pos`
474/// to `t_pos`: `pos + sgn, pos + 2 sgn, ..., t_pos`, with `sgn` the sign of `t_pos - pos` and
475/// both truncated to whole units. `NaN` when the truncated positions are equal.
476fn limit_price_traversal(t_pos: f64, pos: f64, max_pos: f64, inv: impl Fn(f64) -> f64) -> f64 {
477 let target = t_pos.trunc() as i64;
478 let current = pos.trunc() as i64;
479 if target == current {
480 return f64::NAN;
481 }
482 let sgn = (target - current).signum();
483 let steps = (target - current).abs();
484 let total: f64 = (1..=steps).map(|i| inv((current + sgn * i) as f64 / max_pos)).sum();
485 total / steps as f64
486}
487
488/// Sigmoid breakeven limit price for moving from `pos` to `t_pos` with forecast `f`, width
489/// `w` and maximum position `max_pos` (AFML Snippet 10.4, extended to every direction).
490///
491/// Both positions are truncated to whole units. With `sgn` the sign of `t_pos - pos`, the
492/// result is the mean of [`inv_price_sigmoid`]`(f, w, k / max_pos)` over the positions

Callers 1

bet_size_reserve_fullFunction · 0.85

Calls 2

normal_pdfFunction · 0.85
lenMethod · 0.80

Tested by

no test coverage detected