Function bodies 248 total
pnl_to_reward function · rust · L5-L8 (4 LOC)src/evaluation/scorer.rs
pub fn pnl_to_reward(pnl_pct: f64, adaptive_k: f64) -> f64 {
let k = adaptive_k.clamp(0.5, 5.0);
1.0 / (1.0 + (-k * pnl_pct).exp())
}score_trade function · rust · L11-L13 (3 LOC)src/evaluation/scorer.rs
pub fn score_trade(pnl_pct: f64, _max_adverse_pct: f64, _holding_periods: usize, adaptive_k: f64) -> f64 {
pnl_to_reward(pnl_pct, adaptive_k)
}test_pnl_to_reward_positive function · rust · L20-L23 (4 LOC)src/evaluation/scorer.rs
fn test_pnl_to_reward_positive() {
assert!(pnl_to_reward(5.0, 2.0) > 0.7);
assert!(pnl_to_reward(10.0, 2.0) > 0.9);
}test_pnl_to_reward_negative function · rust · L26-L29 (4 LOC)src/evaluation/scorer.rs
fn test_pnl_to_reward_negative() {
assert!(pnl_to_reward(-5.0, 2.0) < 0.3);
assert!(pnl_to_reward(-10.0, 2.0) < 0.1);
}test_pnl_to_reward_zero function · rust · L32-L34 (3 LOC)src/evaluation/scorer.rs
fn test_pnl_to_reward_zero() {
assert!((pnl_to_reward(0.0, 2.0) - 0.5).abs() < 0.01);
}test_score_trade_good function · rust · L37-L40 (4 LOC)src/evaluation/scorer.rs
fn test_score_trade_good() {
let score = score_trade(3.0, 0.3, 8, 2.0);
assert!(score > 0.7);
}test_score_trade_bad function · rust · L43-L46 (4 LOC)src/evaluation/scorer.rs
fn test_score_trade_bad() {
let score = score_trade(-3.0, 6.0, 200, 2.0);
assert!(score < 0.3);
}Want this analysis on your repo? https://repobility.com/scan/
main function · rust · L33-L50 (18 LOC)src/main.rs
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
let cli = Cli::parse();
match cli.command {
Commands::Backtest { days, symbols, equity, leverage } => {
run_backtest(days, &symbols, equity, leverage).await?;
}
Commands::Fetch { days, symbols } => {
run_fetch(days, &symbols).await?;
}
}
Ok(())
}run_fetch function · rust · L51-L70 (20 LOC)src/main.rs
async fn run_fetch(days: i64, symbols: &str) -> Result<(), Box<dyn std::error::Error>> {
let symbols: Vec<&str> = symbols.split(',').collect();
for symbol in &symbols {
let candles = rdex::data::fetch_last_n_days(symbol, days).await?;
rdex::data::save_to_csv(
&candles,
&rdex::data::cache_path(symbol, "data"),
)?;
println!("Fetched and cached {} candles for {}", candles.len(), symbol);
let funding = rdex::data::fetch_funding_last_n_days(symbol, days).await?;
rdex::data::save_funding_csv(
&funding,
&rdex::data::funding_cache_path(symbol, "data"),
)?;
println!("Fetched and cached {} funding rates for {}", funding.len(), symbol);
}
Ok(())
}run_backtest function · rust · L71-L213 (143 LOC)src/main.rs
async fn run_backtest(
days: i64,
symbols_str: &str,
equity: f64,
leverage: f64,
) -> Result<(), Box<dyn std::error::Error>> {
use rdex::domain::*;
use rdex::engine::*;
use rdex::backtest::*;
let symbols: Vec<String> = symbols_str.split(',').map(|s| s.to_string()).collect();
println!("=== RDEX Self-Learning Crypto Trading System ===");
println!("Symbols: {:?}", symbols);
println!("Days: {}, Equity: ${}, Leverage: {}x", days, equity, leverage);
// Fetch candle data + funding rates
let mut all_data: Vec<(String, Vec<Candle>)> = Vec::new();
let mut all_funding: std::collections::HashMap<String, Vec<FundingRate>> = std::collections::HashMap::new();
for symbol in &symbols {
let candles = rdex::data::load_or_fetch(symbol, days, "data").await?;
println!("{}: {} candles ({:.1} days)", symbol, candles.len(),
candles.len() as f64 / 96.0);
let funding = rdex::data::load_or_fetch_funding(symbol, dagenerate_signal function · rust · L9-L51 (43 LOC)src/strategy/arms.rs
fn generate_signal(&self, state: &MarketState) -> TradingDecision {
let ind = &state.indicators;
// Need sufficient indicator data
if ind.atr_14 < 1e-10 || ind.sma_20 < 1e-10 {
return hold_decision("momentum");
}
let price = state.current_candle.close;
let atr = ind.atr_14;
// Momentum conditions — strict filters to avoid overtrading
let strong_trend = ind.adx_14 > 30.0;
let macd_bullish = ind.macd_histogram > 0.0 && ind.macd_line > ind.macd_signal;
let rsi_ok_long = ind.rsi_14 > 45.0 && ind.rsi_14 < 70.0;
let rsi_ok_short = ind.rsi_14 < 55.0 && ind.rsi_14 > 30.0;
let above_sma = price > ind.sma_20 && ind.sma_20 > ind.sma_50;
let below_sma = price < ind.sma_20 && ind.sma_20 < ind.sma_50;
let volume_confirms = state.current_candle.volume > ind.volume_sma_20 * 1.2;
if strong_trend && macd_bullish && rsi_ok_long && above_sma && volume_confirms {
generate_signal function · rust · L59-L98 (40 LOC)src/strategy/arms.rs
fn generate_signal(&self, state: &MarketState) -> TradingDecision {
let ind = &state.indicators;
if ind.atr_14 < 1e-10 || ind.bb_middle < 1e-10 {
return hold_decision("mean_reversion");
}
let price = state.current_candle.close;
let atr = ind.atr_14;
let ranging = ind.adx_14 < 25.0;
// Mean reversion: strict — need BOTH BB and RSI confirmation
let at_lower_bb = price <= ind.bb_lower;
let at_upper_bb = price >= ind.bb_upper;
let rsi_oversold = ind.rsi_14 < 25.0;
let rsi_overbought = ind.rsi_14 > 75.0;
if ranging && at_lower_bb && rsi_oversold {
TradingDecision {
signal: TradeSignal::Long,
size: position_size_conservative(atr, price),
stop_loss: Some(price - 1.5 * atr),
take_profit: Some(ind.bb_middle),
confidence: 0.75,
strategy_name: "mean_reversion".into(),
generate_signal function · rust · L106-L145 (40 LOC)src/strategy/arms.rs
fn generate_signal(&self, state: &MarketState) -> TradingDecision {
let ind = &state.indicators;
if ind.atr_14 < 1e-10 || ind.bb_middle < 1e-10 || state.history.len() < 20 {
return hold_decision("breakout");
}
let price = state.current_candle.close;
let atr = ind.atr_14;
// Detect breakout: BB squeeze then expansion + volume surge
let bb_width = (ind.bb_upper - ind.bb_lower) / ind.bb_middle;
let squeeze = bb_width < 0.03; // tight bands
let volume_surge = state.current_candle.volume > ind.volume_sma_20 * 1.5;
let strong_candle = state.current_candle.body_ratio() > 0.6;
// Breakout above upper BB with volume
if price > ind.bb_upper && (volume_surge || squeeze) && strong_candle {
TradingDecision {
signal: TradeSignal::Long,
size: position_size(30.0, atr, price), // moderate size
stop_loss: Some(ind.bb_middle),
generate_signal function · rust · L153-L193 (41 LOC)src/strategy/arms.rs
fn generate_signal(&self, state: &MarketState) -> TradingDecision {
let ind = &state.indicators;
if ind.atr_14 < 1e-10 || ind.ema_12 < 1e-10 {
return hold_decision("scalping");
}
let price = state.current_candle.close;
let atr = ind.atr_14;
// Quick scalp: EMA crossover + MACD alignment
let ema_bull_cross = ind.ema_12 > ind.ema_26
&& (ind.ema_12 - ind.ema_26) / ind.ema_26 < 0.002; // just crossed
let ema_bear_cross = ind.ema_12 < ind.ema_26
&& (ind.ema_26 - ind.ema_12) / ind.ema_26 < 0.002;
let macd_confirms_long = ind.macd_histogram > 0.0;
let macd_confirms_short = ind.macd_histogram < 0.0;
if ema_bull_cross && macd_confirms_long {
TradingDecision {
signal: TradeSignal::Long,
size: 0.3, // smaller size for scalps
stop_loss: Some(price - 1.0 * atr),
take_profit: Some(price + 1.5 *all_strategies function · rust · L197-L204 (8 LOC)src/strategy/arms.rs
pub fn all_strategies() -> Vec<Box<dyn TradingStrategy>> {
vec![
Box::new(MomentumStrategy),
Box::new(MeanReversionStrategy),
Box::new(BreakoutStrategy),
Box::new(ScalpingStrategy),
]
}Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
hold_decision function · rust · L205-L215 (11 LOC)src/strategy/arms.rs
fn hold_decision(name: &str) -> TradingDecision {
TradingDecision {
signal: TradeSignal::Hold,
size: 0.0,
stop_loss: None,
take_profit: None,
confidence: 0.0,
strategy_name: name.into(),
}
}position_size function · rust · L218-L226 (9 LOC)src/strategy/arms.rs
fn position_size(adx: f64, atr: f64, price: f64) -> f64 {
// Risk 1-2% of equity per trade, scaled by trend strength
let risk_per_unit = atr * 2.0 / price; // SL distance as fraction
if risk_per_unit < 1e-10 { return 0.0; }
let base_risk = 0.02; // 2% equity risk
let kelly_fraction = (base_risk / risk_per_unit).min(0.5);
let trend_scale = (adx / 50.0).min(1.0);
(kelly_fraction * trend_scale).clamp(0.05, 0.5)
}position_size_conservative function · rust · L227-L233 (7 LOC)src/strategy/arms.rs
fn position_size_conservative(atr: f64, price: f64) -> f64 {
let risk_per_unit = atr * 1.5 / price;
if risk_per_unit < 1e-10 { return 0.0; }
let base_risk = 0.01; // 1% for mean reversion (tighter)
(base_risk / risk_per_unit).clamp(0.05, 0.3)
}make_market_state function · rust · L239-L253 (15 LOC)src/strategy/arms.rs
fn make_market_state(price: f64, ind: IndicatorSet) -> MarketState {
let candle = Candle {
open_time: 0, open: price - 0.5, high: price + 1.0,
low: price - 1.0, close: price, volume: 2000.0,
close_time: 900_000, quote_volume: price * 2000.0, trades: 500,
};
MarketState {
symbol: Symbol("BTCUSDT".into()),
timestamp: 900_000,
current_candle: candle.clone(),
history: vec![candle],
indicators: ind,
}
}test_momentum_long_signal function · rust · L256-L270 (15 LOC)src/strategy/arms.rs
fn test_momentum_long_signal() {
let ind = IndicatorSet {
adx_14: 35.0, sma_20: 99.0, sma_50: 95.0,
ema_12: 99.0, ema_26: 97.0, rsi_14: 55.0,
atr_14: 2.0, macd_line: 1.0, macd_signal: 0.5,
macd_histogram: 0.5, bb_upper: 105.0, bb_middle: 100.0,
bb_lower: 95.0, volume_sma_20: 1000.0,
};
let state = make_market_state(100.0, ind);
let decision = MomentumStrategy.generate_signal(&state);
assert_eq!(decision.signal, TradeSignal::Long);
assert!(decision.stop_loss.unwrap() < 100.0);
assert!(decision.take_profit.unwrap() > 100.0);
assert!(decision.size > 0.0 && decision.size <= 0.5);
}test_mean_reversion_at_lower_bb function · rust · L273-L284 (12 LOC)src/strategy/arms.rs
fn test_mean_reversion_at_lower_bb() {
let ind = IndicatorSet {
adx_14: 15.0, // ranging
rsi_14: 22.0, // oversold (below 25 threshold)
bb_upper: 110.0, bb_middle: 100.0, bb_lower: 90.0,
atr_14: 2.0, sma_20: 100.0, sma_50: 100.0,
..Default::default()
};
let state = make_market_state(89.5, ind);
let decision = MeanReversionStrategy.generate_signal(&state);
assert_eq!(decision.signal, TradeSignal::Long);
}test_hold_when_no_signal function · rust · L287-L303 (17 LOC)src/strategy/arms.rs
fn test_hold_when_no_signal() {
let ind = IndicatorSet {
adx_14: 20.0, rsi_14: 50.0,
bb_upper: 105.0, bb_middle: 100.0, bb_lower: 95.0,
atr_14: 2.0, sma_20: 100.0, sma_50: 100.0,
ema_12: 100.0, ema_26: 100.0,
macd_histogram: 0.0, volume_sma_20: 1000.0,
..Default::default()
};
let state = make_market_state(100.0, ind);
for strategy in all_strategies() {
let d = strategy.generate_signal(&state);
assert_eq!(d.signal, TradeSignal::Hold,
"Strategy {} should hold in neutral market", strategy.name());
}
}test_all_strategies_exist function · rust · L306-L314 (9 LOC)src/strategy/arms.rs
fn test_all_strategies_exist() {
let strats = all_strategies();
assert_eq!(strats.len(), 4);
let names: Vec<&str> = strats.iter().map(|s| s.name()).collect();
assert!(names.contains(&"momentum"));
assert!(names.contains(&"mean_reversion"));
assert!(names.contains(&"breakout"));
assert!(names.contains(&"scalping"));
}Repobility — the code-quality scanner for AI-generated software · https://repobility.com
test_position_sizing_bounds function · rust · L317-L321 (5 LOC)src/strategy/arms.rs
fn test_position_sizing_bounds() {
assert!(position_size(50.0, 100.0, 50000.0) > 0.0);
assert!(position_size(50.0, 100.0, 50000.0) <= 0.5);
assert!(position_size_conservative(100.0, 50000.0) <= 0.3);
}extract function · rust · L32-L111 (80 LOC)src/strategy/features.rs
pub fn extract(state: &MarketState) -> Option<Self> {
let ind = &state.indicators;
let c = &state.current_candle;
if ind.atr_14 < 1e-10 || ind.sma_20 < 1e-10 || ind.bb_middle < 1e-10 {
return None;
}
let price = c.close;
let atr = ind.atr_14;
// Trend: distance from SMAs normalized by ATR, compressed via tanh
let dist_sma20 = (price - ind.sma_20) / atr;
let dist_sma50 = if ind.sma_50 > 1e-10 {
(price - ind.sma_50) / atr
} else {
dist_sma20
};
let ma_spread = if ind.sma_50 > 1e-10 {
(ind.sma_20 - ind.sma_50) / atr
} else {
0.0
};
let trend = (dist_sma20 * 0.4 + dist_sma50 * 0.3 + ma_spread * 0.3).tanh();
// Momentum: RSI + MACD histogram
let rsi_norm = (ind.rsi_14 - 50.0) / 50.0;
let macd_norm = (ind.macd_histogram / atr).clamp(-2.0, 2.0) / 2.0;
let momentum = (rsi_norm make_state function · rust · L137-L172 (36 LOC)src/strategy/features.rs
fn make_state(price: f64) -> MarketState {
let candle = Candle {
open_time: 0, open: price - 0.5, high: price + 1.0,
low: price - 1.0, close: price, volume: 2000.0,
close_time: 900_000, quote_volume: price * 2000.0, trades: 500,
};
let mut history = Vec::new();
for i in 0..10 {
history.push(Candle {
open_time: i * 900_000,
open: price - 5.0 + i as f64 * 0.5,
high: price - 4.0 + i as f64 * 0.5,
low: price - 6.0 + i as f64 * 0.5,
close: price - 5.0 + i as f64 * 0.5 + 0.3,
volume: 1500.0,
close_time: (i + 1) * 900_000 - 1,
quote_volume: 100_000.0, trades: 400,
});
}
history.push(candle.clone());
MarketState {
symbol: Symbol("BTCUSDT".into()),
timestamp: 900_000,
current_candle: candle,
history,
test_feature_extraction function · rust · L175-L187 (13 LOC)src/strategy/features.rs
fn test_feature_extraction() {
let state = make_state(50000.0);
let f = MarketFeatures::extract(&state).unwrap();
assert!(f.trend >= -1.0 && f.trend <= 1.0);
assert!(f.momentum >= -1.0 && f.momentum <= 1.0);
assert!(f.volatility >= 0.0 && f.volatility <= 1.0);
assert!(f.volume >= 0.0 && f.volume <= 1.0);
assert!(f.candle_character >= -1.0 && f.candle_character <= 1.0);
assert!(f.bb_position >= 0.0 && f.bb_position <= 1.0);
assert!(f.short_momentum >= -1.0 && f.short_momentum <= 1.0);
assert!(f.atr > 0.0);
assert!(f.price > 0.0);
}test_feature_extraction_insufficient_data function · rust · L190-L203 (14 LOC)src/strategy/features.rs
fn test_feature_extraction_insufficient_data() {
let state = MarketState {
symbol: Symbol("BTCUSDT".into()),
timestamp: 0,
current_candle: Candle {
open_time: 0, open: 100.0, high: 101.0, low: 99.0,
close: 100.5, volume: 1000.0, close_time: 0,
quote_volume: 0.0, trades: 0,
},
history: vec![],
indicators: IndicatorSet::default(),
};
assert!(MarketFeatures::extract(&state).is_none());
}test_as_array_length function · rust · L206-L210 (5 LOC)src/strategy/features.rs
fn test_as_array_length() {
let state = make_state(50000.0);
let f = MarketFeatures::extract(&state).unwrap();
assert_eq!(f.as_array().len(), 7);
}new function · rust · L22-L28 (7 LOC)src/strategy/patterns.rs
fn new(initial_median: f64) -> Self {
Self {
recent: Vec::new(),
median: initial_median,
max_samples: 3000,
}
}observe function · rust · L29-L39 (11 LOC)src/strategy/patterns.rs
fn observe(&mut self, val: f64) {
self.recent.push(val);
if self.recent.len() > self.max_samples {
let half = self.max_samples / 2;
self.recent.drain(0..half);
}
if self.recent.len() >= 20 {
self.update_median();
}
}Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
update_median function · rust · L40-L45 (6 LOC)src/strategy/patterns.rs
fn update_median(&mut self) {
let mut sorted = self.recent.clone();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
self.median = sorted[sorted.len() / 2];
}bin function · rust · L46-L49 (4 LOC)src/strategy/patterns.rs
fn bin(&self, val: f64) -> u8 {
if val <= self.median { 0 } else { 1 }
}sample_count function · rust · L50-L53 (4 LOC)src/strategy/patterns.rs
fn sample_count(&self) -> usize {
self.recent.len()
}new function · rust · L57-L66 (10 LOC)src/strategy/patterns.rs
pub fn new() -> Self {
// 4 key features: trend, momentum, volatility, volume
let binners = vec![
MedianBinner::new(0.0), // trend: negative/positive
MedianBinner::new(0.0), // momentum: weak/strong
MedianBinner::new(0.5), // volatility: low/high
MedianBinner::new(0.5), // volume: low/high
];
Self { binners }
}observe function · rust · L69-L74 (6 LOC)src/strategy/patterns.rs
pub fn observe(&mut self, features: &MarketFeatures) {
let vals = features.key_features();
for (binner, &val) in self.binners.iter_mut().zip(vals.iter()) {
binner.observe(val);
}
}discretize function · rust · L77-L89 (13 LOC)src/strategy/patterns.rs
pub fn discretize(&self, features: &MarketFeatures) -> MarketContext {
let vals = features.key_features();
let bins: Vec<u8> = self.binners.iter()
.zip(vals.iter())
.map(|(binner, &val)| binner.bin(val))
.collect();
// Encode as compact pattern: "TMVV" where T=trend, M=momentum, V=vol, V=volume
MarketContext {
volatility_tier: format!("{}{}", bins[0], bins[1]),
trend_regime: format!("{}{}", bins[2], bins[3]),
}
}pattern_key function · rust · L92-L94 (3 LOC)src/strategy/patterns.rs
pub fn pattern_key(ctx: &MarketContext) -> String {
format!("{}_{}", ctx.volatility_tier, ctx.trend_regime)
}sample_count function · rust · L95-L98 (4 LOC)src/strategy/patterns.rs
pub fn sample_count(&self) -> usize {
self.binners.first().map(|b| b.sample_count()).unwrap_or(0)
}Want this analysis on your repo? https://repobility.com/scan/
is_warmed_up function · rust · L99-L102 (4 LOC)src/strategy/patterns.rs
pub fn is_warmed_up(&self) -> bool {
self.sample_count() >= 20
}make_features function · rust · L108-L116 (9 LOC)src/strategy/patterns.rs
fn make_features(trend: f64, momentum: f64) -> MarketFeatures {
MarketFeatures {
trend, momentum,
volatility: 0.5, volume: 0.5, candle_character: 0.0,
bb_position: 0.5, short_momentum: 0.0,
atr: 100.0, price: 50000.0,
}
}test_discretizer_produces_valid_context function · rust · L119-L124 (6 LOC)src/strategy/patterns.rs
fn test_discretizer_produces_valid_context() {
let disc = PatternDiscretizer::new();
let ctx = disc.discretize(&make_features(0.5, 0.3));
assert_eq!(ctx.volatility_tier.len(), 2);
assert_eq!(ctx.trend_regime.len(), 2);
}test_discretizer_adapts_boundaries function · rust · L127-L137 (11 LOC)src/strategy/patterns.rs
fn test_discretizer_adapts_boundaries() {
let mut disc = PatternDiscretizer::new();
for i in 0..100 {
disc.observe(&make_features(0.5 + i as f64 * 0.005, 0.3));
}
assert!(disc.is_warmed_up());
let ctx_low = disc.discretize(&make_features(0.3, 0.0));
let ctx_high = disc.discretize(&make_features(0.9, 0.0));
assert_ne!(ctx_low.volatility_tier, ctx_high.volatility_tier);
}test_discretizer_deterministic function · rust · L140-L144 (5 LOC)src/strategy/patterns.rs
fn test_discretizer_deterministic() {
let disc = PatternDiscretizer::new();
let f = make_features(0.3, -0.2);
assert_eq!(disc.discretize(&f), disc.discretize(&f));
}test_pattern_key function · rust · L147-L153 (7 LOC)src/strategy/patterns.rs
fn test_pattern_key() {
let ctx = MarketContext {
volatility_tier: "01".into(),
trend_regime: "10".into(),
};
assert_eq!(PatternDiscretizer::pattern_key(&ctx), "01_10");
}test_16_patterns_max function · rust · L156-L177 (22 LOC)src/strategy/patterns.rs
fn test_16_patterns_max() {
let disc = PatternDiscretizer::new();
let mut patterns = std::collections::HashSet::new();
// Try extreme combinations
for t in [-1.0, 1.0] {
for m in [-1.0, 1.0] {
for v in [0.0, 1.0] {
for vol in [0.0, 1.0] {
let f = MarketFeatures {
trend: t, momentum: m,
volatility: v, volume: vol,
candle_character: 0.0, bb_position: 0.5,
short_momentum: 0.0, atr: 100.0, price: 50000.0,
};
let ctx = disc.discretize(&f);
patterns.insert(PatternDiscretizer::pattern_key(&ctx));
}
}
}
}
assert!(patterns.len() <= 16);
}detect_regime function · rust · L12-L24 (13 LOC)src/strategy/regime.rs
pub fn detect_regime(ind: &IndicatorSet) -> MarketRegime {
let trend_strength = ind.adx_14;
let ma_bullish = ind.sma_20 > ind.sma_50;
match (trend_strength > 25.0, trend_strength > 40.0, ma_bullish) {
(_, true, true) => MarketRegime::StrongUptrend,
(true, false, true) => MarketRegime::WeakUptrend,
(_, true, false) => MarketRegime::StrongDowntrend,
(true, false, false) => MarketRegime::WeakDowntrend,
(false, _, _) => MarketRegime::Ranging,
}
}Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
test_regime_detection function · rust · L31-L43 (13 LOC)src/strategy/regime.rs
fn test_regime_detection() {
let mut ind = IndicatorSet::default();
ind.adx_14 = 45.0;
ind.sma_20 = 105.0;
ind.sma_50 = 100.0;
assert_eq!(detect_regime(&ind), MarketRegime::StrongUptrend);
ind.sma_20 = 95.0;
assert_eq!(detect_regime(&ind), MarketRegime::StrongDowntrend);
ind.adx_14 = 15.0;
assert_eq!(detect_regime(&ind), MarketRegime::Ranging);
}‹ prevpage 5 / 5