← back to krejcif__rdex

Function bodies 248 total

All specs Real LLM only Function bodies
fetch_funding_rates function · rust · L114-L165 (52 LOC)
src/data/fetcher.rs
pub async fn fetch_funding_rates(
    symbol: &str,
    start_time: i64,
    end_time: i64,
) -> Result<Vec<FundingRate>, Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let mut all_rates = Vec::new();
    let mut current_start = start_time;
    let limit = 1000; // Binance max per request

    while current_start < end_time {
        let resp = client.get(BINANCE_FUNDING_URL)
            .query(&[
                ("symbol", symbol),
                ("startTime", &current_start.to_string()),
                ("endTime", &end_time.to_string()),
                ("limit", &limit.to_string()),
            ])
            .send()
            .await?;

        if !resp.status().is_success() {
            let status = resp.status();
            let body = resp.text().await.unwrap_or_default();
            return Err(format!("Binance funding API error {}: {}", status, body).into());
        }

        let data: Vec<serde_json::Value> = resp.json().await?;
        if da
fetch_funding_last_n_days function · rust · L168-L175 (8 LOC)
src/data/fetcher.rs
pub async fn fetch_funding_last_n_days(
    symbol: &str,
    days: i64,
) -> Result<Vec<FundingRate>, Box<dyn std::error::Error>> {
    let now = Utc::now();
    let start = now - Duration::days(days);
    fetch_funding_rates(symbol, start.timestamp_millis(), now.timestamp_millis()).await
}
build_market_state function · rust · L7-L28 (22 LOC)
src/domain/crypto_domain.rs
pub fn build_market_state(
    symbol: &Symbol,
    candles: &[Candle],
    index: usize,
    lookback: usize,
) -> Option<MarketState> {
    if index >= candles.len() { return None; }

    let start = if index >= lookback { index - lookback } else { 0 };
    let history: Vec<Candle> = candles[start..=index].to_vec();

    let indicators = compute_indicators(&history);
    let current_candle = candles[index].clone();

    Some(MarketState {
        symbol: symbol.clone(),
        timestamp: current_candle.close_time,
        current_candle,
        history,
        indicators,
    })
}
extract_context function · rust · L31-L37 (7 LOC)
src/domain/crypto_domain.rs
pub fn extract_context(state: &MarketState) -> MarketContext {
    let (volatility_tier, trend_regime) = classify_regime(&state.indicators);
    MarketContext {
        volatility_tier,
        trend_regime,
    }
}
make_test_candles function · rust · L42-L55 (14 LOC)
src/domain/crypto_domain.rs
    fn make_test_candles(n: usize) -> Vec<Candle> {
        (0..n).map(|i| Candle {
            open_time: i as i64 * 900_000,
            open: 100.0 + (i as f64 * 0.1),
            high: 101.0 + (i as f64 * 0.1),
            low: 99.0 + (i as f64 * 0.1),
            close: 100.5 + (i as f64 * 0.1),
            volume: 1000.0,
            close_time: (i + 1) as i64 * 900_000 - 1,
            quote_volume: 100_000.0,
            trades: 500,
        }).collect()
    }
test_build_market_state_no_lookahead function · rust · L58-L71 (14 LOC)
src/domain/crypto_domain.rs
    fn test_build_market_state_no_lookahead() {
        let candles = make_test_candles(100);
        let state = build_market_state(
            &Symbol("BTCUSDT".into()),
            &candles,
            50,  // current index
            60,  // lookback
        ).unwrap();

        // CRITICAL: state should NOT contain any candle after index 50
        assert!(state.history.len() <= 51);
        let max_time = state.history.iter().map(|c| c.close_time).max().unwrap();
        assert!(max_time <= candles[50].close_time);
    }
test_build_market_state_at_start function · rust · L74-L83 (10 LOC)
src/domain/crypto_domain.rs
    fn test_build_market_state_at_start() {
        let candles = make_test_candles(100);
        let state = build_market_state(
            &Symbol("BTCUSDT".into()),
            &candles,
            5,
            60,
        ).unwrap();
        assert_eq!(state.history.len(), 6); // indices 0..=5
    }
Same scanner, your repo: https://repobility.com — Repobility
test_build_market_state_out_of_bounds function · rust · L86-L94 (9 LOC)
src/domain/crypto_domain.rs
    fn test_build_market_state_out_of_bounds() {
        let candles = make_test_candles(10);
        assert!(build_market_state(
            &Symbol("BTCUSDT".into()),
            &candles,
            15,
            60,
        ).is_none());
    }
test_extract_context function · rust · L97-L109 (13 LOC)
src/domain/crypto_domain.rs
    fn test_extract_context() {
        let candles = make_test_candles(100);
        let state = build_market_state(
            &Symbol("BTCUSDT".into()),
            &candles,
            99,
            60,
        ).unwrap();
        let ctx = extract_context(&state);
        // Should produce valid context strings
        assert!(!ctx.volatility_tier.is_empty());
        assert!(!ctx.trend_regime.is_empty());
    }
compute_indicators function · rust · L6-L32 (27 LOC)
src/domain/indicators.rs
pub fn compute_indicators(candles: &[Candle]) -> IndicatorSet {
    if candles.len() < 50 {
        return IndicatorSet::default();
    }

    let closes: Vec<f64> = candles.iter().map(|c| c.close).collect();
    let highs: Vec<f64> = candles.iter().map(|c| c.high).collect();
    let lows: Vec<f64> = candles.iter().map(|c| c.low).collect();
    let volumes: Vec<f64> = candles.iter().map(|c| c.volume).collect();

    let sma_20 = sma(&closes, 20);
    let sma_50 = sma(&closes, 50);
    let ema_12 = ema(&closes, 12);
    let ema_26 = ema(&closes, 26);
    let rsi_14 = rsi(&closes, 14);
    let atr_14 = atr(&highs, &lows, &closes, 14);
    let (macd_line, macd_signal, macd_histogram) = macd(&closes);
    let (bb_upper, bb_middle, bb_lower) = bollinger_bands(&closes, 20, 2.0);
    let adx_14 = adx(&highs, &lows, &closes, 14);
    let volume_sma_20 = sma(&volumes, 20);

    IndicatorSet {
        sma_20, sma_50, ema_12, ema_26, rsi_14, atr_14,
        macd_line, macd_signal, macd_histogram,
sma function · rust · L33-L38 (6 LOC)
src/domain/indicators.rs
fn sma(data: &[f64], period: usize) -> f64 {
    if data.len() < period { return 0.0; }
    let slice = &data[data.len() - period..];
    slice.iter().sum::<f64>() / period as f64
}
ema function · rust · L39-L48 (10 LOC)
src/domain/indicators.rs
fn ema(data: &[f64], period: usize) -> f64 {
    if data.len() < period { return 0.0; }
    let multiplier = 2.0 / (period as f64 + 1.0);
    let mut ema_val = sma(&data[..period], period);
    for &val in &data[period..] {
        ema_val = (val - ema_val) * multiplier + ema_val;
    }
    ema_val
}
rsi function · rust · L49-L80 (32 LOC)
src/domain/indicators.rs
fn rsi(closes: &[f64], period: usize) -> f64 {
    if closes.len() < period + 1 { return 50.0; }

    let mut avg_gain = 0.0;
    let mut avg_loss = 0.0;

    // Initial averages
    for i in 1..=period {
        let change = closes[i] - closes[i - 1];
        if change > 0.0 { avg_gain += change; }
        else { avg_loss += change.abs(); }
    }
    avg_gain /= period as f64;
    avg_loss /= period as f64;

    // Smoothed averages
    for i in (period + 1)..closes.len() {
        let change = closes[i] - closes[i - 1];
        let (gain, loss) = if change > 0.0 {
            (change, 0.0)
        } else {
            (0.0, change.abs())
        };
        avg_gain = (avg_gain * (period as f64 - 1.0) + gain) / period as f64;
        avg_loss = (avg_loss * (period as f64 - 1.0) + loss) / period as f64;
    }

    if avg_loss < 1e-10 { return 100.0; }
    let rs = avg_gain / avg_loss;
    100.0 - (100.0 / (1.0 + rs))
}
atr function · rust · L81-L99 (19 LOC)
src/domain/indicators.rs
fn atr(highs: &[f64], lows: &[f64], closes: &[f64], period: usize) -> f64 {
    if closes.len() < period + 1 { return 0.0; }

    let mut trs = Vec::with_capacity(closes.len() - 1);
    for i in 1..closes.len() {
        let tr = (highs[i] - lows[i])
            .max((highs[i] - closes[i - 1]).abs())
            .max((lows[i] - closes[i - 1]).abs());
        trs.push(tr);
    }

    if trs.len() < period { return 0.0; }
    let mut atr_val: f64 = trs[..period].iter().sum::<f64>() / period as f64;
    for &tr in &trs[period..] {
        atr_val = (atr_val * (period as f64 - 1.0) + tr) / period as f64;
    }
    atr_val
}
macd function · rust · L100-L128 (29 LOC)
src/domain/indicators.rs
fn macd(closes: &[f64]) -> (f64, f64, f64) {
    if closes.len() < 35 { return (0.0, 0.0, 0.0); }

    let ema12 = ema(closes, 12);
    let ema26 = ema(closes, 26);
    let macd_line = ema12 - ema26;

    // Build MACD line series for signal
    let mut macd_series = Vec::new();
    let mult12 = 2.0 / 13.0;
    let mult26 = 2.0 / 27.0;
    let mut e12 = sma(&closes[..12], 12);
    let mut e26 = sma(&closes[..26], 26);

    for i in 26..closes.len() {
        e12 = (closes[i] - e12) * mult12 + e12;
        e26 = (closes[i] - e26) * mult26 + e26;
        macd_series.push(e12 - e26);
    }

    let macd_signal = if macd_series.len() >= 9 {
        ema(&macd_series, 9)
    } else {
        0.0
    };

    (macd_line, macd_signal, macd_line - macd_signal)
}
Want this analysis on your repo? https://repobility.com/scan/
bollinger_bands function · rust · L129-L137 (9 LOC)
src/domain/indicators.rs
fn bollinger_bands(closes: &[f64], period: usize, num_std: f64) -> (f64, f64, f64) {
    if closes.len() < period { return (0.0, 0.0, 0.0); }
    let slice = &closes[closes.len() - period..];
    let mean = slice.iter().sum::<f64>() / period as f64;
    let variance = slice.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / period as f64;
    let std = variance.sqrt();
    (mean + num_std * std, mean, mean - num_std * std)
}
adx function · rust · L138-L193 (56 LOC)
src/domain/indicators.rs
fn adx(highs: &[f64], lows: &[f64], closes: &[f64], period: usize) -> f64 {
    if closes.len() < period * 2 + 1 { return 25.0; } // neutral default

    let mut plus_dm = Vec::new();
    let mut minus_dm = Vec::new();
    let mut tr_vals = Vec::new();

    for i in 1..closes.len() {
        let up = highs[i] - highs[i - 1];
        let down = lows[i - 1] - lows[i];

        plus_dm.push(if up > down && up > 0.0 { up } else { 0.0 });
        minus_dm.push(if down > up && down > 0.0 { down } else { 0.0 });

        let tr = (highs[i] - lows[i])
            .max((highs[i] - closes[i - 1]).abs())
            .max((lows[i] - closes[i - 1]).abs());
        tr_vals.push(tr);
    }

    if tr_vals.len() < period { return 25.0; }

    let smooth = |vals: &[f64], p: usize| -> Vec<f64> {
        let mut result = vec![vals[..p].iter().sum::<f64>()];
        for i in p..vals.len() {
            let prev = *result.last().unwrap();
            result.push(prev - prev / p as f64 + vals[i]);
        
classify_regime function · rust · L196-L224 (29 LOC)
src/domain/indicators.rs
pub fn classify_regime(indicators: &IndicatorSet) -> (String, String) {
    // Volatility tier based on BB width relative to middle
    let bb_width = if indicators.bb_middle > 0.0 {
        (indicators.bb_upper - indicators.bb_lower) / indicators.bb_middle
    } else {
        0.0
    };

    let volatility_tier = if bb_width < 0.02 {
        "low".to_string()
    } else if bb_width < 0.05 {
        "medium".to_string()
    } else {
        "high".to_string()
    };

    // Trend regime based on ADX + moving average alignment
    let trend_regime = if indicators.adx_14 > 25.0 {
        if indicators.sma_20 > indicators.sma_50 {
            "uptrend".to_string()
        } else {
            "downtrend".to_string()
        }
    } else {
        "ranging".to_string()
    };

    (volatility_tier, trend_regime)
}
make_candles function · rust · L229-L242 (14 LOC)
src/domain/indicators.rs
    fn make_candles(closes: &[f64]) -> Vec<Candle> {
        closes.iter().enumerate().map(|(i, &c)| Candle {
            open_time: i as i64 * 900_000,
            open: c - 0.5,
            high: c + 1.0,
            low: c - 1.0,
            close: c,
            volume: 1000.0,
            close_time: (i + 1) as i64 * 900_000 - 1,
            quote_volume: c * 1000.0,
            trades: 100,
        }).collect()
    }
test_sma function · rust · L245-L249 (5 LOC)
src/domain/indicators.rs
    fn test_sma() {
        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
        assert!((sma(&data, 3) - 4.0).abs() < 1e-10); // (3+4+5)/3
        assert!((sma(&data, 5) - 3.0).abs() < 1e-10);
    }
test_ema function · rust · L252-L256 (5 LOC)
src/domain/indicators.rs
    fn test_ema() {
        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0];
        let result = ema(&data, 3);
        assert!(result > 8.0 && result < 10.0); // EMA lags
    }
test_rsi_extremes function · rust · L259-L267 (9 LOC)
src/domain/indicators.rs
    fn test_rsi_extremes() {
        // All up moves -> RSI near 100
        let up: Vec<f64> = (0..30).map(|i| 100.0 + i as f64).collect();
        assert!(rsi(&up, 14) > 90.0);

        // All down moves -> RSI near 0
        let down: Vec<f64> = (0..30).map(|i| 100.0 - i as f64).collect();
        assert!(rsi(&down, 14) < 10.0);
    }
test_bollinger_bands function · rust · L270-L276 (7 LOC)
src/domain/indicators.rs
    fn test_bollinger_bands() {
        let data: Vec<f64> = (0..20).map(|_| 100.0).collect();
        let (upper, middle, lower) = bollinger_bands(&data, 20, 2.0);
        assert!((middle - 100.0).abs() < 1e-10);
        assert!((upper - 100.0).abs() < 1e-10); // zero std -> bands collapse
        assert!((lower - 100.0).abs() < 1e-10);
    }
All rows scored by the Repobility analyzer (https://repobility.com)
test_compute_indicators_sufficient_data function · rust · L279-L287 (9 LOC)
src/domain/indicators.rs
    fn test_compute_indicators_sufficient_data() {
        // Generate 60 candles of trending data
        let closes: Vec<f64> = (0..60).map(|i| 100.0 + i as f64 * 0.5).collect();
        let candles = make_candles(&closes);
        let ind = compute_indicators(&candles);
        assert!(ind.sma_20 > 0.0);
        assert!(ind.rsi_14 > 50.0); // uptrend -> RSI > 50
        assert!(ind.atr_14 > 0.0);
    }
test_compute_indicators_insufficient_data function · rust · L290-L294 (5 LOC)
src/domain/indicators.rs
    fn test_compute_indicators_insufficient_data() {
        let candles = make_candles(&[100.0; 10]);
        let ind = compute_indicators(&candles);
        assert_eq!(ind.sma_20, 0.0); // not enough data
    }
test_classify_regime function · rust · L297-L311 (15 LOC)
src/domain/indicators.rs
    fn test_classify_regime() {
        let ind = IndicatorSet {
            adx_14: 30.0,
            sma_20: 105.0,
            sma_50: 100.0,
            bb_upper: 110.0,
            bb_middle: 100.0,
            bb_lower: 90.0,
            ..Default::default()
        };
        let (vol, trend) = classify_regime(&ind);
        assert_eq!(trend, "uptrend");
        // bb_width = 20/100 = 0.2 -> high
        assert_eq!(vol, "high");
    }
datetime function · rust · L19-L22 (4 LOC)
src/domain/types.rs
    pub fn datetime(&self) -> DateTime<Utc> {
        DateTime::from_timestamp_millis(self.open_time)
            .unwrap_or_default()
    }
body_ratio function · rust · L23-L28 (6 LOC)
src/domain/types.rs
    pub fn body_ratio(&self) -> f64 {
        let range = self.high - self.low;
        if range < 1e-10 { return 0.0; }
        (self.close - self.open).abs() / range
    }
is_bullish function · rust · L29-L32 (4 LOC)
src/domain/types.rs
    pub fn is_bullish(&self) -> bool {
        self.close > self.open
    }
fmt function · rust · L40-L42 (3 LOC)
src/domain/types.rs
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
fmt function · rust · L127-L129 (3 LOC)
src/domain/types.rs
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
default function · rust · L160-L168 (9 LOC)
src/domain/types.rs
    fn default() -> Self {
        Self {
            leverage: 3.0,      // conservative 3x
            maker_fee: 0.0002,
            taker_fee: 0.0004,
            slippage_bps: 2.0,  // 2 bps slippage
            initial_equity: 10_000.0,
        }
    }
test_candle_body_ratio function · rust · L176-L185 (10 LOC)
src/domain/types.rs
    fn test_candle_body_ratio() {
        let candle = Candle {
            open_time: 0, open: 100.0, high: 110.0, low: 90.0,
            close: 105.0, volume: 1000.0, close_time: 0,
            quote_volume: 0.0, trades: 0,
        };
        let ratio = candle.body_ratio();
        assert!((ratio - 0.25).abs() < 1e-10); // |105-100| / (110-90) = 5/20
        assert!(candle.is_bullish());
    }
test_candle_bearish function · rust · L188-L195 (8 LOC)
src/domain/types.rs
    fn test_candle_bearish() {
        let candle = Candle {
            open_time: 0, open: 105.0, high: 110.0, low: 90.0,
            close: 95.0, volume: 1000.0, close_time: 0,
            quote_volume: 0.0, trades: 0,
        };
        assert!(!candle.is_bullish());
    }
test_futures_config_defaults function · rust · L198-L203 (6 LOC)
src/domain/types.rs
    fn test_futures_config_defaults() {
        let cfg = FuturesConfig::default();
        assert_eq!(cfg.leverage, 3.0);
        assert_eq!(cfg.taker_fee, 0.0004);
        assert_eq!(cfg.initial_equity, 10_000.0);
    }
default function · rust · L49-L63 (15 LOC)
src/engine/adaptive.rs
    fn default() -> Self {
        Self {
            total_trades: 0,
            total_wins: 0,
            total_candles_seen: 0,
            ema_pnl: 0.0,
            ema_pnl_sq: 4.0,           // initial variance ~4 (std ~2%)
            ema_win_duration: 15.0,
            ema_loss_duration: 8.0,
            ema_favorable: 4.0,
            ema_adverse: 2.5,
            ema_rr_ratio: 1.6,          // initial: favorable/adverse = 4.0/2.5
            ema_decay: 0.92,
        }
    }
new function · rust · L67-L73 (7 LOC)
src/engine/adaptive.rs
    pub fn new() -> Self {
        Self {
            recent_outcomes: VecDeque::new(),
            max_window: 100,
            stats: AdaptiveStats::default(),
        }
    }
record_trade function · rust · L76-L126 (51 LOC)
src/engine/adaptive.rs
    pub fn record_trade(
        &mut self,
        pnl_pct: f64,
        duration: usize,
        favorable_atr: f64,
        adverse_atr: f64,
    ) {
        let outcome = TradeOutcome {
            pnl_pct,
            duration,
            adverse_atr,
            favorable_atr,
        };

        self.recent_outcomes.push_back(outcome);
        if self.recent_outcomes.len() > self.max_window {
            self.recent_outcomes.pop_front();
        }

        let d = self.stats.ema_decay;
        self.stats.total_trades += 1;
        if pnl_pct > 0.0 {
            self.stats.total_wins += 1;
        }

        // Update EMAs
        self.stats.ema_pnl = self.stats.ema_pnl * d + pnl_pct * (1.0 - d);
        self.stats.ema_pnl_sq = self.stats.ema_pnl_sq * d + (pnl_pct * pnl_pct) * (1.0 - d);

        if pnl_pct > 0.0 {
            self.stats.ema_win_duration =
                self.stats.ema_win_duration * d + duration as f64 * (1.0 - d);
            self.stats.ema_favorable =
      
tick_candle function · rust · L127-L130 (4 LOC)
src/engine/adaptive.rs
    pub fn tick_candle(&mut self) {
        self.stats.total_candles_seen += 1;
    }
Same scanner, your repo: https://repobility.com — Repobility
pnl_std function · rust · L133-L136 (4 LOC)
src/engine/adaptive.rs
    fn pnl_std(&self) -> f64 {
        let variance = (self.stats.ema_pnl_sq - self.stats.ema_pnl * self.stats.ema_pnl).max(0.01);
        variance.sqrt()
    }
reward_k function · rust · L141-L143 (3 LOC)
src/engine/adaptive.rs
    pub fn reward_k(&self) -> f64 {
        (1.0 / self.pnl_std()).clamp(0.5, 5.0)
    }
overall_win_rate function · rust · L146-L149 (4 LOC)
src/engine/adaptive.rs
    fn overall_win_rate(&self) -> f64 {
        if self.stats.total_trades == 0 { return 0.5; }
        self.stats.total_wins as f64 / self.stats.total_trades as f64
    }
cooldown function · rust · L154-L162 (9 LOC)
src/engine/adaptive.rs
    pub fn cooldown(&self) -> usize {
        if self.stats.total_trades < 5 {
            return 15;
        }

        let recent_wr = self.recent_win_rate().clamp(0.1, 0.9);
        let cd = self.stats.ema_win_duration * (1.0 - recent_wr) / recent_wr;
        cd.round().clamp(8.0, 40.0) as usize
    }
min_hold function · rust · L166-L174 (9 LOC)
src/engine/adaptive.rs
    pub fn min_hold(&self) -> usize {
        if self.stats.total_trades < 10 {
            return 8;
        }
        // Geometric mean = sqrt(win_dur * loss_dur)
        // Balances the two duration signals naturally
        let gm = (self.stats.ema_win_duration * self.stats.ema_loss_duration).sqrt();
        gm.round().clamp(3.0, 20.0) as usize
    }
max_hold function · rust · L179-L187 (9 LOC)
src/engine/adaptive.rs
    pub fn max_hold(&self) -> usize {
        if self.stats.total_trades < 10 {
            return 96;
        }
        let wr = self.overall_win_rate().clamp(0.1, 0.9);
        let loss_rate = 1.0 - wr;
        let max = self.stats.ema_win_duration / (loss_rate * loss_rate);
        max.round().clamp(20.0, 192.0) as usize
    }
trail_activation_atr function · rust · L192-L200 (9 LOC)
src/engine/adaptive.rs
    pub fn trail_activation_atr(&self) -> f64 {
        if self.stats.total_trades < 10 {
            return 1.5;
        }
        // activation = favorable / (1 + rr_ratio)
        // With favorable=4.0 and rr=1.6: activation = 4.0/2.6 = 1.54
        let activation = self.stats.ema_favorable / (1.0 + self.stats.ema_rr_ratio);
        activation.clamp(0.5, 4.0)
    }
trail_distance_atr function · rust · L204-L211 (8 LOC)
src/engine/adaptive.rs
    pub fn trail_distance_atr(&self) -> f64 {
        if self.stats.total_trades < 10 {
            return 1.0;
        }
        let wr = self.overall_win_rate();
        let distance = self.stats.ema_adverse * (1.0 - wr);
        distance.clamp(0.3, 2.5)
    }
Want this analysis on your repo? https://repobility.com/scan/
breakeven_activation_atr function · rust · L215-L223 (9 LOC)
src/engine/adaptive.rs
    pub fn breakeven_activation_atr(&self) -> f64 {
        if self.stats.total_trades < 10 {
            return 0.75;
        }
        let total_dur = self.stats.ema_win_duration + self.stats.ema_loss_duration;
        let loss_fraction = self.stats.ema_loss_duration / total_dur.max(1.0);
        let be = self.stats.ema_adverse * loss_fraction;
        be.clamp(0.3, 2.0)
    }
exploration_coeff function · rust · L228-L240 (13 LOC)
src/engine/adaptive.rs
    pub fn exploration_coeff(&self) -> f64 {
        if self.stats.total_trades < 5 {
            return 0.4;
        }
        let trades = self.stats.total_trades as f64;
        // quality_scale = win_duration * rr_ratio (how good are our trades)
        // More quality → takes more trades to reduce exploration
        let quality_scale = self.stats.ema_win_duration * self.stats.ema_rr_ratio;
        let wr = self.overall_win_rate();
        // Start at (1-wr) — explore more when losing
        let base = 1.0 - wr;
        base / (1.0 + trades / quality_scale)
    }
kelly_fraction function · rust · L244-L259 (16 LOC)
src/engine/adaptive.rs
    pub fn kelly_fraction(&self) -> f64 {
        if self.stats.total_trades < 15 {
            return 0.35;
        }

        let wr = self.overall_win_rate();
        let rr = self.stats.ema_rr_ratio;
        // Kelly formula: f = (p * b - q) / b  where p=win_rate, b=rr_ratio, q=1-p
        let kelly_full = (wr * rr - (1.0 - wr)) / rr.max(0.01);
        // Fraction = wr * rr / (wr * rr + 1-wr) — expected gain share of outcomes
        // Purely data-derived: high EV → use more Kelly, low EV → less
        let ev_plus = wr * rr;
        let ev_minus = 1.0 - wr;
        let fraction = ev_plus / (ev_plus + ev_minus).max(0.01);
        (kelly_full * fraction).clamp(0.05, 0.50)
    }
‹ prevpage 2 / 5next ›