Function bodies 248 total
transfer_priors function · rust · L250-L275 (26 LOC)src/engine/thompson.rs
pub fn transfer_priors(
&mut self,
source: &str,
target: &str,
) {
let priors = self.extract_priors(source);
for (ctx, arms) in priors {
for (arm, params) in arms {
let dampened = BetaParams {
alpha: 1.0 + (params.alpha - 1.0).sqrt(),
beta: 1.0 + (params.beta - 1.0).sqrt(),
};
self.arms
.entry(target.to_string())
.or_default()
.entry(ctx.clone())
.or_default()
.entry(arm)
.or_insert_with(|| {
let mut db = DecayingBeta::new(self.decay_factor);
db.params = dampened;
db
});
}
}
}test_context function · rust · L281-L287 (7 LOC)src/engine/thompson.rs
fn test_context() -> MarketContext {
MarketContext {
volatility_tier: "medium".into(),
trend_regime: "uptrend".into(),
}
}test_beta_params function · rust · L290-L295 (6 LOC)src/engine/thompson.rs
fn test_beta_params() {
let mut b = BetaParams::uniform();
assert!((b.mean() - 0.5).abs() < 1e-10);
b.update(1.0);
assert!(b.mean() > 0.5);
}test_beta_sampling_in_range function · rust · L298-L305 (8 LOC)src/engine/thompson.rs
fn test_beta_sampling_in_range() {
let b = BetaParams { alpha: 10.0, beta: 5.0 };
let mut rng = rand::thread_rng();
for _ in 0..100 {
let s = b.sample(&mut rng);
assert!(s >= 0.0 && s <= 1.0);
}
}test_decaying_beta function · rust · L308-L315 (8 LOC)src/engine/thompson.rs
fn test_decaying_beta() {
let mut db = DecayingBeta::new(0.995);
for _ in 0..100 {
db.update(0.8);
}
assert!(db.params.mean() > 0.7);
assert!((db.effective_window() - 200.0).abs() < 1.0);
}test_thompson_engine_select function · rust · L318-L328 (11 LOC)src/engine/thompson.rs
fn test_thompson_engine_select() {
let mut engine = ThompsonEngine::new(
vec!["momentum".into(), "mean_reversion".into(), "breakout".into(), "scalping".into()],
0.995,
);
let ctx = test_context();
let mut rng = rand::thread_rng();
let arm = engine.select_arm("BTCUSDT", &ctx, &mut rng);
assert!(!arm.0.is_empty());
}test_thompson_engine_learns function · rust · L331-L353 (23 LOC)src/engine/thompson.rs
fn test_thompson_engine_learns() {
let mut engine = ThompsonEngine::new(
vec!["good".into(), "bad".into()],
0.995,
);
let ctx = test_context();
// Train: "good" arm always wins
for _ in 0..100 {
engine.record_outcome("BTCUSDT", &ctx, &StrategyId("good".into()), 0.9);
engine.record_outcome("BTCUSDT", &ctx, &StrategyId("bad".into()), 0.1);
}
// Engine should now prefer "good"
let mut rng = rand::thread_rng();
let mut good_count = 0;
for _ in 0..100 {
if engine.select_arm("BTCUSDT", &ctx, &mut rng).0 == "good" {
good_count += 1;
}
}
assert!(good_count > 80, "Engine should select good arm most of the time, got {}", good_count);
}Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
test_transfer_priors function · rust · L356-L380 (25 LOC)src/engine/thompson.rs
fn test_transfer_priors() {
let mut engine = ThompsonEngine::new(
vec!["a".into(), "b".into()],
// Use higher decay so evidence doesn't decay away
0.999,
);
let ctx = test_context();
// Train on BTC with lots of observations to accumulate evidence > 10
for _ in 0..100 {
engine.record_outcome("BTCUSDT", &ctx, &StrategyId("a".into()), 0.85);
engine.record_outcome("BTCUSDT", &ctx, &StrategyId("b".into()), 0.3);
}
// Verify source has enough evidence
let src_priors = engine.extract_priors("BTCUSDT");
assert!(!src_priors.is_empty(), "BTC should have priors with sufficient evidence");
// Transfer to ETH
engine.transfer_priors("BTCUSDT", "ETHUSDT");
// ETH should now have informative priors
let eth_a = engine.get_beta("ETHUSDT", &ctx, &StrategyId("a".into()));
assert!(eth_a.params.mean() > 0.5, "Transferred prior shtest_uncertainty_detection function · rust · L383-L399 (17 LOC)src/engine/thompson.rs
fn test_uncertainty_detection() {
let mut engine = ThompsonEngine::new(
vec!["a".into(), "b".into()],
0.995,
);
let ctx = test_context();
// Uniform priors -> uncertain
assert!(engine.is_uncertain("BTCUSDT", &ctx, 0.1));
// After training, should be certain
for _ in 0..100 {
engine.record_outcome("BTCUSDT", &ctx, &StrategyId("a".into()), 0.95);
engine.record_outcome("BTCUSDT", &ctx, &StrategyId("b".into()), 0.1);
}
assert!(!engine.is_uncertain("BTCUSDT", &ctx, 0.1));
}new function · rust · L50-L67 (18 LOC)src/engine/trade_manager.rs
pub fn new() -> Self {
Self {
current_sl: None,
current_tp: None,
current_strategy: String::new(),
candles_held: 0,
max_adverse: 0.0,
max_favorable: 0.0,
cooldown_remaining: 0,
current_pattern: None,
entry_atr: 0.0,
accumulated_funding: 0.0,
entry_confidence: 0.0,
equity_at_entry: 0.0,
position_size_frac: 0.0,
exit_reason: String::new(),
}
}tick_cooldown function · rust · L70-L77 (8 LOC)src/engine/trade_manager.rs
pub fn tick_cooldown(&mut self) -> bool {
if self.cooldown_remaining > 0 {
self.cooldown_remaining -= 1;
true
} else {
false
}
}on_candle function · rust · L82-L120 (39 LOC)src/engine/trade_manager.rs
pub fn on_candle(
&mut self,
candle: &Candle,
position: &Position,
learning: &LearningEngine,
) -> TradeAction {
self.candles_held += 1;
// Track excursions
let adverse = match position.side {
PositionSide::Long => (position.entry_price - candle.low) / position.entry_price * 100.0,
PositionSide::Short => (candle.high - position.entry_price) / position.entry_price * 100.0,
PositionSide::Flat => 0.0,
};
self.max_adverse = self.max_adverse.max(adverse);
let favorable = match position.side {
PositionSide::Long => (candle.high - position.entry_price) / position.entry_price * 100.0,
PositionSide::Short => (position.entry_price - candle.low) / position.entry_price * 100.0,
PositionSide::Flat => 0.0,
};
self.max_favorable = self.max_favorable.max(favorable.max(0.0));
// Adaptive trailing stop
if self.echeck_sl_tp function · rust · L124-L151 (28 LOC)src/engine/trade_manager.rs
pub fn check_sl_tp(&self, candle: &Candle, position: &Position, learning: &LearningEngine) -> Option<f64> {
let min_hold = learning.adaptive.min_hold();
if self.candles_held < min_hold {
return None;
}
match position.side {
PositionSide::Long => {
if let Some(sl) = self.current_sl {
if candle.low <= sl { return Some(sl); }
}
if let Some(tp) = self.current_tp {
if candle.high >= tp { return Some(tp); }
}
}
PositionSide::Short => {
if let Some(sl) = self.current_sl {
if candle.high >= sl { return Some(sl); }
}
if let Some(tp) = self.current_tp {
if candle.low <= tp { return Some(tp); }
}
}
PositionSide::Flat => {}
}
None
}on_entry function · rust · L154-L163 (10 LOC)src/engine/trade_manager.rs
pub fn on_entry(&mut self, decision: &TradingDecision, atr: f64, pattern: Option<MarketContext>, equity: f64) {
self.current_sl = decision.stop_loss;
self.current_tp = decision.take_profit;
self.current_strategy = decision.strategy_name.clone();
self.current_pattern = pattern;
self.entry_atr = atr;
self.entry_confidence = decision.confidence;
self.equity_at_entry = equity;
self.position_size_frac = decision.size;
}on_exit function · rust · L166-L207 (42 LOC)src/engine/trade_manager.rs
pub fn on_exit(&mut self, symbol: &Symbol, record: &crate::backtest::portfolio::TradeRecord, learning: &mut LearningEngine) {
let adaptive_k = learning.adaptive.reward_k();
let reward = scorer::pnl_to_reward(record.pnl_pct, adaptive_k);
// Compute ATR-normalized excursions
let atr = self.entry_atr.max(1e-10);
let entry = record.entry_price;
let favorable_atr = (self.max_favorable / 100.0 * entry) / atr;
let adverse_atr = (self.max_adverse / 100.0 * entry) / atr;
// Feed adaptive parameter engine
learning.adaptive.record_trade(
record.pnl_pct,
record.holding_periods,
favorable_atr,
adverse_atr,
);
if let Some(ref pattern) = self.current_pattern {
let action = match record.side {
PositionSide::Long => "long",
PositionSide::Short => "short",
_ => return,
};
learninRepobility analyzer · published findings · https://repobility.com
reset function · rust · L210-L221 (12 LOC)src/engine/trade_manager.rs
pub fn reset(&mut self, learning: &LearningEngine) {
self.current_sl = None;
self.current_tp = None;
self.current_strategy.clear();
self.candles_held = 0;
self.max_adverse = 0.0;
self.max_favorable = 0.0;
self.current_pattern = None;
self.entry_atr = 0.0;
self.accumulated_funding = 0.0;
self.cooldown_remaining = learning.adaptive.cooldown();
}reset_with_cooldown function · rust · L224-L227 (4 LOC)src/engine/trade_manager.rs
pub fn reset_with_cooldown(&mut self, learning: &LearningEngine, cooldown: usize) {
self.reset(learning);
self.cooldown_remaining = cooldown;
}add_funding function · rust · L230-L232 (3 LOC)src/engine/trade_manager.rs
pub fn add_funding(&mut self, fee: f64) {
self.accumulated_funding += fee;
}update_trailing_stop function · rust · L235-L290 (56 LOC)src/engine/trade_manager.rs
fn update_trailing_stop(&mut self, candle: &Candle, position: &Position, learning: &LearningEngine) {
let entry = position.entry_price;
let atr = self.entry_atr;
let profit_atr = match position.side {
PositionSide::Long => (candle.close - entry) / atr,
PositionSide::Short => (entry - candle.close) / atr,
PositionSide::Flat => 0.0,
};
let trail_activation = learning.adaptive.trail_activation_atr();
let trail_dist = learning.adaptive.trail_distance_atr();
let breakeven_activation = learning.adaptive.breakeven_activation_atr();
if profit_atr > trail_activation {
let best_price = match position.side {
PositionSide::Long => candle.high,
PositionSide::Short => candle.low,
PositionSide::Flat => entry,
};
let trail_distance = trail_dist * atr;
let new_sl = match position.side {
make_learning function · rust · L297-L300 (4 LOC)src/engine/trade_manager.rs
fn make_learning() -> LearningEngine {
LearningEngine::new(vec!["BTCUSDT".into()], LearnerConfig::default())
}test_trade_manager_initial_state function · rust · L303-L308 (6 LOC)src/engine/trade_manager.rs
fn test_trade_manager_initial_state() {
let tm = TradeManager::new();
assert_eq!(tm.candles_held, 0);
assert_eq!(tm.cooldown_remaining, 0);
assert!(tm.current_sl.is_none());
}test_cooldown_ticks_down function · rust · L311-L318 (8 LOC)src/engine/trade_manager.rs
fn test_cooldown_ticks_down() {
let mut tm = TradeManager::new();
tm.cooldown_remaining = 3;
assert!(tm.tick_cooldown()); // 3 -> 2
assert!(tm.tick_cooldown()); // 2 -> 1
assert!(tm.tick_cooldown()); // 1 -> 0
assert!(!tm.tick_cooldown()); // 0 -> stays 0
}test_reset_sets_cooldown function · rust · L321-L330 (10 LOC)src/engine/trade_manager.rs
fn test_reset_sets_cooldown() {
let mut tm = TradeManager::new();
tm.candles_held = 50;
tm.max_adverse = 5.0;
let learning = make_learning();
tm.reset(&learning);
assert_eq!(tm.candles_held, 0);
assert_eq!(tm.max_adverse, 0.0);
assert!(tm.cooldown_remaining > 0);
}Source: Repobility analyzer · https://repobility.com
test_excursion_tracking function · rust · L333-L354 (22 LOC)src/engine/trade_manager.rs
fn test_excursion_tracking() {
let mut tm = TradeManager::new();
tm.entry_atr = 1000.0;
let learning = make_learning();
let pos = Position {
symbol: Symbol("BTCUSDT".into()),
side: PositionSide::Long,
entry_price: 50000.0,
size: 0.1,
unrealized_pnl: 0.0,
entry_time: 0,
};
let candle = Candle {
open_time: 0, open: 50000.0, high: 51000.0, low: 49000.0,
close: 50500.0, volume: 1000.0, close_time: 900_000,
quote_volume: 0.0, trades: 0,
};
tm.on_candle(&candle, &pos, &learning);
assert!(tm.max_favorable > 0.0);
assert!(tm.max_adverse > 0.0);
assert_eq!(tm.candles_held, 1);
}test_max_hold_exit function · rust · L357-L381 (25 LOC)src/engine/trade_manager.rs
fn test_max_hold_exit() {
let mut tm = TradeManager::new();
tm.entry_atr = 1000.0;
// Use candles_held just under max_hold, then push over
tm.candles_held = 95; // default max_hold is 96
let learning = make_learning();
let pos = Position {
symbol: Symbol("BTCUSDT".into()),
side: PositionSide::Long,
entry_price: 50000.0,
size: 0.1,
unrealized_pnl: 0.0,
entry_time: 0,
};
let candle = Candle {
open_time: 0, open: 50000.0, high: 50100.0, low: 49900.0,
close: 50000.0, volume: 1000.0, close_time: 900_000,
quote_volume: 0.0, trades: 0,
};
let action = tm.on_candle(&candle, &pos, &learning);
assert_eq!(action, TradeAction::Exit {
price: 50000.0,
reason: ExitReason::MaxHold,
});
}test_sl_tp_respects_min_hold function · rust · L384-L404 (21 LOC)src/engine/trade_manager.rs
fn test_sl_tp_respects_min_hold() {
let mut tm = TradeManager::new();
tm.current_sl = Some(49000.0);
tm.candles_held = 1; // below min_hold
let learning = make_learning();
let pos = Position {
symbol: Symbol("BTCUSDT".into()),
side: PositionSide::Long,
entry_price: 50000.0,
size: 0.1,
unrealized_pnl: 0.0,
entry_time: 0,
};
let candle = Candle {
open_time: 0, open: 49500.0, high: 49600.0, low: 48000.0,
close: 48500.0, volume: 1000.0, close_time: 900_000,
quote_volume: 0.0, trades: 0,
};
// SL at 49000, low at 48000 — would trigger, but min_hold not met
assert!(tm.check_sl_tp(&candle, &pos, &learning).is_none());
}verify function · rust · L19-L45 (27 LOC)src/engine/transfer.rs
pub fn verify(
source_symbol: String,
target_symbol: String,
source_before: f64,
source_after: f64,
target_before: f64,
target_after: f64,
baseline_cycles: u64,
transfer_cycles: u64,
) -> Self {
let improved_target = target_after > target_before;
let regressed_source = source_after < source_before - 0.01;
let promotable = improved_target && !regressed_source;
let acceleration_factor = if transfer_cycles > 0 {
baseline_cycles as f64 / transfer_cycles as f64
} else {
1.0
};
Self {
source_symbol, target_symbol,
source_before, source_after,
target_before, target_after,
improved_target, regressed_source,
promotable, acceleration_factor,
}
}record function · rust · L57-L64 (8 LOC)src/engine/transfer.rs
pub fn record(&mut self, reward: f64) {
if reward > self.best_reward_seen {
self.best_reward_seen = reward;
}
let regret = (self.best_reward_seen - reward).max(0.0);
self.total_regret += regret;
self.total_observations += 1;
}growth_rate function · rust · L67-L72 (6 LOC)src/engine/transfer.rs
pub fn growth_rate(&self) -> f64 {
if self.total_observations < 10 || self.total_regret < 1e-10 {
return 1.0;
}
self.total_regret.ln() / (self.total_observations as f64).ln()
}average_regret function · rust · L73-L77 (5 LOC)src/engine/transfer.rs
pub fn average_regret(&self) -> f64 {
if self.total_observations == 0 { return 0.0; }
self.total_regret / self.total_observations as f64
}new function · rust · L98-L105 (8 LOC)src/engine/transfer.rs
pub fn new(window_size: usize, threshold: f64) -> Self {
Self {
scores: Vec::new(),
window_size: window_size.max(3),
threshold,
consecutive_plateaus: 0,
}
}Methodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
record function · rust · L106-L109 (4 LOC)src/engine/transfer.rs
pub fn record(&mut self, score: f64) {
self.scores.push(score);
}check function · rust · L110-L136 (27 LOC)src/engine/transfer.rs
pub fn check(&mut self) -> PlateauAction {
let n = self.scores.len();
if n < self.window_size * 2 {
self.consecutive_plateaus = 0;
return PlateauAction::Continue;
}
let recent: f64 = self.scores[n - self.window_size..].iter().sum::<f64>()
/ self.window_size as f64;
let prior: f64 = self.scores[n - 2 * self.window_size..n - self.window_size]
.iter().sum::<f64>() / self.window_size as f64;
let improvement = (recent - prior).abs();
if improvement < self.threshold {
self.consecutive_plateaus += 1;
match self.consecutive_plateaus {
1 => PlateauAction::IncreaseExploration,
2..=3 => PlateauAction::TriggerTransfer,
_ => PlateauAction::InjectDiversity,
}
} else {
self.consecutive_plateaus = 0;
PlateauAction::Continue
}
}test_transfer_verification_promotable function · rust · L144-L153 (10 LOC)src/engine/transfer.rs
fn test_transfer_verification_promotable() {
let v = TransferVerification::verify(
"BTCUSDT".into(), "ETHUSDT".into(),
0.8, 0.79, // source stable
0.3, 0.7, // target improved
100, 40,
);
assert!(v.promotable);
assert!((v.acceleration_factor - 2.5).abs() < 0.01);
}test_transfer_verification_regression function · rust · L156-L164 (9 LOC)src/engine/transfer.rs
fn test_transfer_verification_regression() {
let v = TransferVerification::verify(
"BTCUSDT".into(), "ETHUSDT".into(),
0.8, 0.5, // source regressed!
0.3, 0.7,
100, 40,
);
assert!(!v.promotable);
}test_regret_tracker function · rust · L167-L180 (14 LOC)src/engine/transfer.rs
fn test_regret_tracker() {
let mut tracker = RegretTracker::default();
// Optimal arm: always 0.9
for _ in 0..100 {
tracker.record(0.9);
}
assert!(tracker.total_regret < 1e-10); // no regret when always best
// Now some bad decisions
for _ in 0..50 {
tracker.record(0.3);
}
assert!(tracker.total_regret > 0.0);
}test_plateau_detector function · rust · L183-L198 (16 LOC)src/engine/transfer.rs
fn test_plateau_detector() {
let mut pd = PlateauDetector::new(5, 0.01);
// Improving scores
for i in 0..10 {
pd.record(0.5 + i as f64 * 0.05);
}
assert_eq!(pd.check(), PlateauAction::Continue);
// Flat scores
for _ in 0..20 {
pd.record(0.95);
}
let action = pd.check();
assert_ne!(action, PlateauAction::Continue);
}calculate_metrics function · rust · L25-L101 (77 LOC)src/evaluation/metrics.rs
pub fn calculate_metrics(
equity_curve: &[f64],
trade_pnls: &[f64],
trade_holding_periods: &[usize],
days: f64,
) -> PerformanceMetrics {
let total_return_pct = if equity_curve.len() >= 2 {
(equity_curve.last().unwrap() / equity_curve[0] - 1.0) * 100.0
} else {
0.0
};
let annualized_return_pct = if days > 0.0 {
((1.0 + total_return_pct / 100.0).powf(365.0 / days) - 1.0) * 100.0
} else {
0.0
};
// Daily returns for Sharpe/Sortino
let daily_returns = compute_daily_returns(equity_curve);
let sharpe_ratio = sharpe(&daily_returns);
let sortino_ratio = sortino(&daily_returns);
let max_drawdown_pct = max_drawdown(equity_curve);
let calmar_ratio = if max_drawdown_pct > 0.01 {
annualized_return_pct / max_drawdown_pct
} else {
0.0
};
let winning: Vec<f64> = trade_pnls.iter().filter(|&&p| p > 0.0).copied().collect();
let losing: Vec<f64> = trade_pnls.iter().filter(|&&compute_daily_returns function · rust · L102-L121 (20 LOC)src/evaluation/metrics.rs
fn compute_daily_returns(equity: &[f64]) -> Vec<f64> {
if equity.len() < 2 { return vec![]; }
// Group by approximate daily intervals (96 x 15min candles per day)
let candles_per_day = 96;
let mut daily = Vec::new();
let mut i = 0;
while i + candles_per_day < equity.len() {
let ret = equity[i + candles_per_day] / equity[i] - 1.0;
daily.push(ret);
i += candles_per_day;
}
if daily.is_empty() && equity.len() >= 2 {
// Fallback: use per-candle returns
for i in 1..equity.len() {
daily.push(equity[i] / equity[i - 1] - 1.0);
}
}
daily
}Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
sharpe function · rust · L122-L132 (11 LOC)src/evaluation/metrics.rs
fn sharpe(returns: &[f64]) -> f64 {
if returns.len() < 2 { return 0.0; }
let mean = returns.iter().sum::<f64>() / returns.len() as f64;
let variance = returns.iter().map(|r| (r - mean).powi(2)).sum::<f64>()
/ (returns.len() - 1) as f64;
let std = variance.sqrt();
if std < 1e-10 { return 0.0; }
// Annualize: assume daily returns
(mean / std) * (365.0_f64).sqrt()
}sortino function · rust · L133-L144 (12 LOC)src/evaluation/metrics.rs
fn sortino(returns: &[f64]) -> f64 {
if returns.len() < 2 { return 0.0; }
let mean = returns.iter().sum::<f64>() / returns.len() as f64;
let downside_variance = returns.iter()
.filter(|&&r| r < 0.0)
.map(|r| r.powi(2))
.sum::<f64>() / returns.len() as f64;
let downside_std = downside_variance.sqrt();
if downside_std < 1e-10 { return 0.0; }
(mean / downside_std) * (365.0_f64).sqrt()
}max_drawdown function · rust · L145-L157 (13 LOC)src/evaluation/metrics.rs
fn max_drawdown(equity: &[f64]) -> f64 {
if equity.is_empty() { return 0.0; }
let mut peak = equity[0];
let mut max_dd = 0.0;
for &val in equity {
if val > peak { peak = val; }
let dd = (peak - val) / peak * 100.0;
if dd > max_dd { max_dd = dd; }
}
max_dd
}max_consecutive_neg function · rust · L158-L171 (14 LOC)src/evaluation/metrics.rs
fn max_consecutive_neg(pnls: &[f64]) -> usize {
let mut max_run = 0;
let mut current_run = 0;
for &pnl in pnls {
if pnl < 0.0 {
current_run += 1;
max_run = max_run.max(current_run);
} else {
current_run = 0;
}
}
max_run
}test_sharpe_positive function · rust · L178-L182 (5 LOC)src/evaluation/metrics.rs
fn test_sharpe_positive() {
// Vary returns slightly to have non-zero std
let returns: Vec<f64> = (0..100).map(|i| 0.001 + (i as f64 * 0.0001).sin() * 0.0002).collect();
assert!(sharpe(&returns) > 0.0);
}test_sharpe_zero_std function · rust · L185-L188 (4 LOC)src/evaluation/metrics.rs
fn test_sharpe_zero_std() {
let returns = vec![0.0; 100];
assert_eq!(sharpe(&returns), 0.0);
}test_max_drawdown function · rust · L191-L196 (6 LOC)src/evaluation/metrics.rs
fn test_max_drawdown() {
let equity = vec![100.0, 110.0, 95.0, 105.0, 80.0, 90.0];
let dd = max_drawdown(&equity);
// Peak at 110, trough at 80 -> (110-80)/110 * 100 = 27.27%
assert!((dd - 27.27).abs() < 0.1);
}test_max_drawdown_no_drawdown function · rust · L199-L202 (4 LOC)src/evaluation/metrics.rs
fn test_max_drawdown_no_drawdown() {
let equity = vec![100.0, 101.0, 102.0, 103.0];
assert_eq!(max_drawdown(&equity), 0.0);
}Repobility analyzer · published findings · https://repobility.com
test_consecutive_losses function · rust · L205-L208 (4 LOC)src/evaluation/metrics.rs
fn test_consecutive_losses() {
let pnls = vec![1.0, -1.0, -2.0, -3.0, 1.0, -1.0, -1.0];
assert_eq!(max_consecutive_neg(&pnls), 3);
}test_calculate_metrics_profitable function · rust · L211-L220 (10 LOC)src/evaluation/metrics.rs
fn test_calculate_metrics_profitable() {
let equity: Vec<f64> = (0..200).map(|i| 10000.0 + i as f64 * 10.0).collect();
let pnls = vec![100.0, -30.0, 80.0, -20.0, 150.0, -40.0, 120.0];
let periods = vec![5, 3, 8, 2, 6, 4, 7];
let metrics = calculate_metrics(&equity, &pnls, &periods, 30.0);
assert!(metrics.total_return_pct > 0.0);
assert!(metrics.win_rate > 0.5);
assert!(metrics.profit_factor > 1.0);
}test_profit_factor function · rust · L223-L229 (7 LOC)src/evaluation/metrics.rs
fn test_profit_factor() {
let pnls = vec![100.0, -50.0, 80.0, -30.0];
let equity = vec![10000.0; 2];
let metrics = calculate_metrics(&equity, &pnls, &[1; 4], 1.0);
// Gross profit = 180, gross loss = 80, PF = 2.25
assert!((metrics.profit_factor - 2.25).abs() < 0.01);
}