(
samples: &[f64],
fit_runs: usize,
epsilon: f64,
max_iter: usize,
)
| 433 | /// |
| 434 | /// # Errors |
| 435 | /// |
| 436 | /// [`BetSizingError::PriceDivergenceOutOfRange`] if `price_div` is outside `[-1, 1]`. |
| 437 | pub 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). |
| 449 | pub 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]`. |
| 464 | pub 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. |
| 476 | fn 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 |
no test coverage detected