#![allow(unused)]
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_28() {
use finance_query::backtesting::SmaCrossover;
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ticker = Ticker::new("AAPL").await?;
let result = ticker.backtest(
SmaCrossover::new(10, 20), Interval::OneDay,
TimeRange::OneYear,
None,
).await?;
println!("Total Return: {:.2}%", result.metrics.total_return_pct);
println!("Sharpe Ratio: {:.2}", result.metrics.sharpe_ratio);
println!("Max Drawdown: {:.2}%", result.metrics.max_drawdown_pct * 100.0);
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_53() {
use finance_query::backtesting::RsiReversal;
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ticker = Ticker::new("AAPL").await?;
let result = ticker.backtest(
RsiReversal::new(14), Interval::OneDay,
TimeRange::OneYear,
None,
).await?;
let result = ticker.backtest(
RsiReversal::new(14).with_thresholds(30.0, 70.0),
Interval::OneDay,
TimeRange::OneYear,
None,
).await?;
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_83() {
use finance_query::backtesting::MacdSignal;
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ticker = Ticker::new("AAPL").await?;
let result = ticker.backtest(
MacdSignal::new(12, 26, 9), Interval::OneDay,
TimeRange::OneYear,
None,
).await?;
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_104() {
use finance_query::backtesting::BollingerMeanReversion;
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ticker = Ticker::new("AAPL").await?;
let result = ticker.backtest(
BollingerMeanReversion::new(20, 2.0), Interval::OneDay,
TimeRange::OneYear,
None,
).await?;
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_125() {
use finance_query::backtesting::SuperTrendFollow;
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ticker = Ticker::new("AAPL").await?;
let result = ticker.backtest(
SuperTrendFollow::new(10, 3.0), Interval::OneDay,
TimeRange::OneYear,
None,
).await?;
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_146() {
use finance_query::backtesting::DonchianBreakout;
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ticker = Ticker::new("AAPL").await?;
let result = ticker.backtest(
DonchianBreakout::new(20), Interval::OneDay,
TimeRange::OneYear,
None,
).await?;
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_167() {
use finance_query::backtesting::StrategyBuilder;
use finance_query::backtesting::refs::*;
use finance_query::backtesting::condition::*;
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ticker = Ticker::new("AAPL").await?;
let strategy = StrategyBuilder::new("RSI Mean Reversion")
.entry(
rsi(14)
.crosses_below(30.0)
.and(price().above_ref(sma(200)))
)
.exit(
rsi(14)
.crosses_above(70.0)
.or(stop_loss(0.05))
)
.build();
let result = ticker.backtest(
strategy,
Interval::OneDay,
TimeRange::OneYear,
None,
).await?;
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_204() {
use finance_query::backtesting::StrategyBuilder;
use finance_query::backtesting::refs::*;
use finance_query::backtesting::condition::*;
let strategy = StrategyBuilder::new("Trend-Filtered RSI")
.entry(rsi(14).crosses_below(30.0))
.exit(rsi(14).crosses_above(70.0))
.regime_filter(price().above_ref(sma(200))) .build();
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_220() {
use finance_query::backtesting::StrategyBuilder;
use finance_query::backtesting::refs::*;
use finance_query::backtesting::condition::*;
let strategy = StrategyBuilder::new("Long-Short RSI")
.entry(rsi(14).crosses_below(30.0)) .exit(rsi(14).crosses_above(70.0)) .with_short(
rsi(14).crosses_above(70.0), rsi(14).crosses_below(30.0), )
.build();
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_239() {
use finance_query::backtesting::StrategyBuilder;
use finance_query::backtesting::refs::*;
use finance_query::backtesting::condition::*;
let strategy = StrategyBuilder::new("SMA with Warmup")
.entry(price().crosses_above_ref(sma(200)))
.exit(price().crosses_below_ref(sma(200)))
.warmup(200) .build();
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_257() {
use finance_query::backtesting::{BacktestConfig, PositionSizing, SmaCrossover};
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = BacktestConfig::builder()
.initial_capital(50_000.0)
.commission_pct(0.001) .commission(1.0) .slippage_pct(0.0005) .spread_pct(0.0002) .transaction_tax_pct(0.005) .stop_loss_pct(0.05) .take_profit_pct(0.15) .trailing_stop_pct(0.03) .allow_short(true)
.position_size_pct(0.5) .max_positions(3) .bars_per_year(252.0) .risk_free_rate(0.04) .reinvest_dividends(true)
.close_at_end(true) .max_leverage(2.0) .maintenance_margin_pct(0.25) .margin_interest_rate(0.06) .short_borrow_rate(0.03) .position_sizing(PositionSizing::FixedFraction)
.build()?;
let ticker = Ticker::new("AAPL").await?;
let result = ticker.backtest(
SmaCrossover::new(10, 20),
Interval::OneDay,
TimeRange::OneYear,
Some(config),
).await?;
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_304() {
use finance_query::backtesting::BacktestConfig;
let config = BacktestConfig::zero_cost();
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_314() {
use finance_query::backtesting::BacktestConfig;
let config = BacktestConfig::builder()
.initial_capital(10_000.0)
.commission_fn(|size, price| {
let value = size * price;
if value < 1_000.0 { 1.0 } else { value * 0.0005 }
})
.build()
.unwrap();
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_349() {
use finance_query::backtesting::{BacktestConfig, BacktestEngine, SmaCrossover};
let config = BacktestConfig::builder()
.initial_capital(10_000.0)
.max_leverage(2.0)
.maintenance_margin_pct(0.25)
.margin_interest_rate(0.06)
.build()
.unwrap();
let engine = BacktestEngine::new(config);
let _ = engine.run("AAPL", &[], SmaCrossover::new(10, 20));
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_400() {
use finance_query::backtesting::{BacktestConfig, PositionSizing};
let config = BacktestConfig::builder()
.position_size_pct(0.5) .position_sizing(PositionSizing::Atr {
risk_pct: 0.02, atr_period: 14,
atr_multiple: 2.0, })
.build()
.unwrap();
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_473() {
use finance_query::backtesting::SmaCrossover;
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ticker = Ticker::new("AAPL").await?;
let result = ticker.backtest(SmaCrossover::new(10, 20), Interval::OneDay, TimeRange::OneYear, None).await?;
println!("Total Return: {:.2}%", result.metrics.total_return_pct);
println!("Annualized Return: {:.2}%", result.metrics.annualized_return_pct);
println!("Final Equity: ${:.2}", result.final_equity);
println!("Sharpe Ratio: {:.2}", result.metrics.sharpe_ratio);
println!("Sortino Ratio: {:.2}", result.metrics.sortino_ratio);
println!("Calmar Ratio: {:.2}", result.metrics.calmar_ratio);
println!("Max Drawdown: {:.2}%", result.metrics.max_drawdown_pct * 100.0);
println!("Total Trades: {}", result.metrics.total_trades);
println!("Winning Trades: {}", result.metrics.winning_trades);
println!("Losing Trades: {}", result.metrics.losing_trades);
println!("Win Rate: {:.2}%", result.metrics.win_rate * 100.0);
println!("Profit Factor: {:.2}", result.metrics.profit_factor);
println!("Avg Trade: {:.2}%", result.metrics.avg_trade_return_pct);
println!("Avg Win: {:.2}%", result.metrics.avg_win_pct);
println!("Avg Loss: {:.2}%", result.metrics.avg_loss_pct);
println!("Largest Win: {:.2}%", result.metrics.largest_win);
println!("Largest Loss: {:.2}%", result.metrics.largest_loss);
println!("Max Consec. Wins: {}", result.metrics.max_consecutive_wins);
println!("Max Consec. Losses: {}", result.metrics.max_consecutive_losses);
println!("Long Trades: {}", result.metrics.long_trades);
println!("Short Trades: {}", result.metrics.short_trades);
println!("Time in Market: {:.1}%", result.metrics.time_in_market_pct * 100.0);
println!("Total Signals: {}", result.metrics.total_signals);
println!("Executed Signals: {}", result.metrics.executed_signals);
println!("Total Commission: ${:.2}", result.metrics.total_commission);
println!("Financing Cost: ${:.2}", result.metrics.total_financing_cost);
println!("Dividend Income: ${:.2}", result.metrics.total_dividend_income);
println!("Kelly Criterion: {:.2}", result.metrics.kelly_criterion);
println!("SQN: {:.2}", result.metrics.sqn);
println!("Expectancy: {:.2}", result.metrics.expectancy);
println!("Omega Ratio: {:.2}", result.metrics.omega_ratio);
println!("Tail Ratio: {:.2}", result.metrics.tail_ratio);
println!("Recovery Factor: {:.2}", result.metrics.recovery_factor);
println!("Ulcer Index: {:.2}", result.metrics.ulcer_index);
println!("Serenity Ratio: {:.2}", result.metrics.serenity_ratio);
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_536() {
use finance_query::backtesting::SmaCrossover;
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ticker = Ticker::new("AAPL").await?;
let result = ticker.backtest(SmaCrossover::new(10, 20), Interval::OneDay, TimeRange::OneYear, None).await?;
let sharpe_30 = result.rolling_sharpe(30); let drawdowns = result.drawdown_series(); let win_rate_20 = result.rolling_win_rate(20); Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_554() {
use finance_query::backtesting::SmaCrossover;
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ticker = Ticker::new("AAPL").await?;
let result = ticker.backtest(SmaCrossover::new(10, 20), Interval::OneDay, TimeRange::TwoYears, None).await?;
let by_year = result.by_year(); let by_month = result.by_month(); let by_dow = result.by_day_of_week();
for (year, metrics) in &by_year {
println!("{year}: {:.2}%", metrics.total_return_pct);
}
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_579() {
use finance_query::backtesting::SmaCrossover;
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ticker = Ticker::new("AAPL").await?;
let result = ticker.backtest(SmaCrossover::new(10, 20), Interval::OneDay, TimeRange::OneYear, None).await?;
let tagged_trades = result.trades_by_tag("breakout");
let tagged_metrics = result.metrics_by_tag("breakout");
let all_tags = result.all_tags();
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_599() {
use finance_query::backtesting::SmaCrossover;
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ticker = Ticker::new("AAPL").await?;
let result = ticker.backtest(SmaCrossover::new(10, 20), Interval::OneDay, TimeRange::OneYear, None).await?;
for msg in &result.diagnostics {
println!("âš {msg}");
}
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_619() {
use finance_query::backtesting::Signal;
let ts = 0i64; let px = 150.0;
let market_entry = Signal::long(ts, px);
let limit_entry = Signal::buy_limit(ts, px, 148.0);
let stop_entry = Signal::buy_stop(ts, px, 152.0);
let stop_limit = Signal::buy_stop_limit(ts, px, 152.0, 153.0);
let limit_exit = Signal::sell_limit(ts, px, 160.0);
let stop_exit = Signal::sell_stop(ts, px, 145.0);
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_646() {
use finance_query::backtesting::Signal;
let (ts, px) = (0i64, 150.0);
let signal = Signal::buy_limit(ts, px, 148.0)
.expires_in_bars(5); }
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_658() {
use finance_query::backtesting::Signal;
let (ts, px) = (0i64, 150.0);
let signal = Signal::long(ts, px)
.stop_loss(0.03) .take_profit(0.10) .trailing_stop(0.02); }
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_672() {
use finance_query::backtesting::Signal;
let (ts, px) = (0i64, 150.0);
let add_to_position = Signal::scale_in(0.25, ts, px); let reduce_position = Signal::scale_out(0.50, ts, px); }
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_684() {
use finance_query::backtesting::Signal;
let (ts, px) = (0i64, 150.0);
let signal = Signal::long(ts, px)
.tag("breakout")
.tag("high-volume");
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_697() {
use finance_query::backtesting::{EnsembleStrategy, EnsembleMode, SmaCrossover, RsiReversal};
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ticker = Ticker::new("AAPL").await?;
let ensemble = EnsembleStrategy::new("Ensemble")
.add(SmaCrossover::new(10, 50), 0.6)
.add(RsiReversal::new(14), 0.4)
.mode(EnsembleMode::WeightedMajority)
.build();
let result = ticker.backtest(ensemble, Interval::OneDay, TimeRange::OneYear, None).await?;
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_729() {
use finance_query::backtesting::StrategyBuilder;
use finance_query::backtesting::refs::*;
use finance_query::backtesting::condition::*;
use finance_query::backtesting::refs::htf;
use finance_query::Interval;
let strategy = StrategyBuilder::new("HTF RSI Filter")
.entry(
rsi(14).crosses_below(30.0)
.and(htf(Interval::OneDay, rsi(14).above(40.0)))
)
.exit(rsi(14).crosses_above(70.0))
.build();
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_752() {
use finance_query::backtesting::SmaCrossover;
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ticker = Ticker::new("AAPL").await?;
let result = ticker.backtest_with_benchmark(
SmaCrossover::new(10, 50),
Interval::OneDay,
TimeRange::OneYear,
None,
"SPY", ).await?;
if let Some(bench) = &result.benchmark {
println!("Strategy return: {:.2}%", result.metrics.total_return_pct);
println!("Benchmark return: {:.2}%", bench.benchmark_return_pct);
println!("Buy & hold return: {:.2}%", bench.buy_and_hold_return_pct);
println!("Alpha: {:.4}", bench.alpha);
println!("Beta: {:.4}", bench.beta);
println!("Information Ratio: {:.4}", bench.information_ratio);
}
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_783() {
use finance_query::backtesting::{
BacktestComparison, BacktestConfig, BacktestEngine, MacdSignal, OptimizeMetric,
RsiReversal, SmaCrossover,
};
fn synthetic_candles(n: usize) -> Vec<finance_query::Candle> {
(0..n)
.map(|i| {
let close = 100.0 + (i as f64 / 4.0).sin() * 8.0;
serde_json::from_value(serde_json::json!({
"timestamp": 1_700_000_000_i64 + i as i64 * 86_400,
"open": close, "high": close + 1.0, "low": close - 1.0,
"close": close, "volume": 1_000_000_i64, "adjClose": close,
}))
.unwrap()
})
.collect()
}
let candles = synthetic_candles(1000);
let config = BacktestConfig::zero_cost();
let result_sma = BacktestEngine::new(config.clone())
.run("SYNTH", &candles, SmaCrossover::new(10, 20)).unwrap();
let result_rsi = BacktestEngine::new(config.clone())
.run("SYNTH", &candles, RsiReversal::new(14)).unwrap();
let result_macd = BacktestEngine::new(config)
.run("SYNTH", &candles, MacdSignal::new(12, 26, 9)).unwrap();
let report = BacktestComparison::new()
.add("SMA Crossover", result_sma)
.add("RSI Reversal", result_rsi)
.add("MACD Signal", result_macd)
.ranked_by(OptimizeMetric::SharpeRatio);
println!("Winner: {}", report.winner());
assert_eq!(report.table().len(), 3);
for row in report.table() {
println!(
"#{} {} — Sharpe {:.2}, Return {:.2}%",
row.rank, row.label, row.sharpe_ratio, row.total_return_pct,
);
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_838() {
use finance_query::backtesting::{
BacktestConfig, GridSearch, OptimizeMetric, ParamRange, SmaCrossover,
};
fn synthetic_candles(n: usize) -> Vec<finance_query::Candle> {
(0..n)
.map(|i| {
let close = 100.0 + (i as f64 / 4.0).sin() * 8.0;
serde_json::from_value(serde_json::json!({
"timestamp": 1_700_000_000_i64 + i as i64 * 86_400,
"open": close, "high": close + 1.0, "low": close - 1.0,
"close": close, "volume": 1_000_000_i64, "adjClose": close,
}))
.unwrap()
})
.collect()
}
let candles = synthetic_candles(1000);
let config = BacktestConfig::zero_cost();
let report = GridSearch::new()
.param("fast", ParamRange::int_range(5, 20, 5))
.param("slow", ParamRange::int_range(20, 60, 10))
.optimize_for(OptimizeMetric::SharpeRatio)
.run("SYNTH", &candles, &config, |params| {
SmaCrossover::new(
params["fast"].as_int() as usize,
params["slow"].as_int() as usize,
)
}).unwrap();
assert!(report.n_evaluations > 0);
println!("Best Sharpe: {:.2}", report.best.result.metrics.sharpe_ratio);
println!("Best params: fast={}, slow={}",
report.best.params["fast"].as_int(),
report.best.params["slow"].as_int(),
);
println!("Evaluated {} combinations", report.n_evaluations);
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_885() {
use finance_query::backtesting::{
BacktestConfig, BayesianSearch, OptimizeMetric, ParamRange, SmaCrossover,
};
fn synthetic_candles(n: usize) -> Vec<finance_query::Candle> {
(0..n)
.map(|i| {
let close = 100.0 + (i as f64 / 4.0).sin() * 8.0;
serde_json::from_value(serde_json::json!({
"timestamp": 1_700_000_000_i64 + i as i64 * 86_400,
"open": close, "high": close + 1.0, "low": close - 1.0,
"close": close, "volume": 1_000_000_i64, "adjClose": close,
}))
.unwrap()
})
.collect()
}
let candles = synthetic_candles(1000);
let config = BacktestConfig::zero_cost();
let report = BayesianSearch::new()
.param("fast", ParamRange::int_bounds(5, 50))
.param("slow", ParamRange::int_bounds(20, 200))
.max_evaluations(100)
.initial_points(10)
.ucb_beta(2.0)
.seed(42)
.optimize_for(OptimizeMetric::SharpeRatio)
.run("SYNTH", &candles, &config, |params| {
SmaCrossover::new(
params["fast"].as_int() as usize,
params["slow"].as_int() as usize,
)
}).unwrap();
assert!(!report.convergence_curve.is_empty());
println!("Convergence: {:?}", report.convergence_curve);
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_949() {
use finance_query::backtesting::{
BacktestConfig, GridSearch, OptimizeMetric, ParamRange, SmaCrossover,
};
let report = GridSearch::new()
.param("fast", ParamRange::int_range(5, 20, 5))
.param("slow", ParamRange::int_range(20, 60, 10))
.run_pareto(
"AAPL",
&[],
&BacktestConfig::default(),
&[OptimizeMetric::SharpeRatio, OptimizeMetric::MinDrawdown],
|params| {
SmaCrossover::new(
params["fast"].as_int() as usize,
params["slow"].as_int() as usize,
)
},
);
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_980() {
use finance_query::backtesting::{
BacktestConfig, GridSearch, OptimizeMetric, ParamRange, SmaCrossover, WalkForwardConfig,
};
fn synthetic_candles(n: usize) -> Vec<finance_query::Candle> {
(0..n)
.map(|i| {
let close = 100.0 + (i as f64 / 4.0).sin() * 8.0;
serde_json::from_value(serde_json::json!({
"timestamp": 1_700_000_000_i64 + i as i64 * 86_400,
"open": close, "high": close + 1.0, "low": close - 1.0,
"close": close, "volume": 1_000_000_i64, "adjClose": close,
}))
.unwrap()
})
.collect()
}
let candles = synthetic_candles(1000);
let grid = GridSearch::new()
.param("fast", ParamRange::int_range(5, 20, 5))
.param("slow", ParamRange::int_range(20, 60, 10))
.optimize_for(OptimizeMetric::SharpeRatio);
let config = BacktestConfig::builder()
.initial_capital(10_000.0)
.commission_pct(0.001)
.build()
.unwrap();
let report = WalkForwardConfig::new(grid, config)
.in_sample_bars(252) .out_of_sample_bars(63) .run("SYNTH", &candles, |params| {
SmaCrossover::new(
params["fast"].as_int() as usize,
params["slow"].as_int() as usize,
)
}).unwrap();
assert!(!report.windows.is_empty());
println!("OOS Return: {:.2}%", report.aggregate_metrics.total_return_pct);
println!("Consistency: {:.1}%", report.consistency_ratio * 100.0);
println!("Windows tested: {}", report.windows.len());
for w in &report.windows {
println!(
"Window {}: IS {:.1}% → OOS {:.1}%",
w.window,
w.in_sample.metrics.total_return_pct,
w.out_of_sample.metrics.total_return_pct,
);
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_1042() {
use finance_query::backtesting::{
BacktestConfig, BacktestEngine, MonteCarloConfig, MonteCarloMethod, SmaCrossover,
};
fn synthetic_candles(n: usize) -> Vec<finance_query::Candle> {
(0..n)
.map(|i| {
let close = 100.0 + (i as f64 / 4.0).sin() * 8.0;
serde_json::from_value(serde_json::json!({
"timestamp": 1_700_000_000_i64 + i as i64 * 86_400,
"open": close, "high": close + 1.0, "low": close - 1.0,
"close": close, "volume": 1_000_000_i64, "adjClose": close,
}))
.unwrap()
})
.collect()
}
let result = BacktestEngine::new(BacktestConfig::zero_cost())
.run("SYNTH", &synthetic_candles(1000), SmaCrossover::new(10, 20))
.unwrap();
let mc = MonteCarloConfig::new()
.seed(42)
.num_simulations(1_000)
.method(MonteCarloMethod::IidShuffle)
.run(&result);
assert!(mc.total_return.p5 <= mc.total_return.p95);
println!("Return p5: {:.2}%", mc.total_return.p5);
println!("Return p50: {:.2}%", mc.total_return.p50);
println!("Return p95: {:.2}%", mc.total_return.p95);
println!("Drawdown p95: {:.2}%", mc.max_drawdown.p95);
println!("Sharpe p50: {:.2}", mc.sharpe_ratio.p50);
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[test]
fn doc_block_line_1095() {
use finance_query::backtesting::portfolio::{
PortfolioConfig, PortfolioEngine, RebalanceMode, SymbolData,
};
use finance_query::backtesting::{BacktestConfig, SmaCrossover};
fn synthetic_candles(n: usize, base: f64) -> Vec<finance_query::Candle> {
(0..n)
.map(|i| {
let close = base + (i as f64 / 4.0).sin() * 8.0;
serde_json::from_value(serde_json::json!({
"timestamp": 1_700_000_000_i64 + i as i64 * 86_400,
"open": close, "high": close + 1.0, "low": close - 1.0,
"close": close, "volume": 1_000_000_i64, "adjClose": close,
}))
.unwrap()
})
.collect()
}
let config = PortfolioConfig::new(BacktestConfig::builder()
.initial_capital(50_000.0)
.commission_pct(0.001)
.build()
.unwrap()
)
.max_total_positions(3)
.rebalance(RebalanceMode::EqualWeight);
let symbol_data = vec![
SymbolData::new("AAPL", synthetic_candles(500, 100.0)),
SymbolData::new("MSFT", synthetic_candles(500, 300.0)),
SymbolData::new("GOOGL", synthetic_candles(500, 150.0)),
];
let result = PortfolioEngine::new(config)
.run(&symbol_data, |_sym| SmaCrossover::new(10, 50))
.unwrap();
assert_eq!(result.symbols.len(), 3);
println!("Portfolio Return: {:.2}%", result.portfolio_metrics.total_return_pct);
println!("Final Equity: ${:.2}", result.final_equity);
for (sym, sym_result) in &result.symbols {
println!("{}: {:.2}%", sym, sym_result.metrics.total_return_pct);
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_1154() {
use finance_query::backtesting::portfolio::{PortfolioConfig, RebalanceMode};
use finance_query::backtesting::{BacktestConfig, SmaCrossover};
use finance_query::{Interval, Tickers, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let tickers = Tickers::new(vec!["AAPL", "MSFT", "GOOGL"]).await?;
let config = PortfolioConfig::new(BacktestConfig::default())
.max_total_positions(3)
.rebalance(RebalanceMode::EqualWeight);
let result = tickers.backtest(
Interval::OneDay,
TimeRange::OneYear,
Some(config),
|_sym| SmaCrossover::new(10, 50),
).await?;
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_1242() {
use finance_query::{Ticker, Interval, TimeRange};
use finance_query::backtesting::{StrategyBuilder, BacktestConfig};
use finance_query::backtesting::refs::*;
use finance_query::backtesting::condition::*;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ticker = Ticker::new("AAPL").await?;
let strategy = StrategyBuilder::new("Momentum with Risk Management")
.entry(
{
let m = macd(12, 26, 9);
m.line().crosses_above_ref(m.signal_line())
.and(price().above_ref(ema(50)))
.and(volume().above_ref(sma(20)))
}
)
.exit(
{
let m = macd(12, 26, 9);
m.line().crosses_below_ref(m.signal_line())
.or(stop_loss(0.08))
.or(take_profit(0.15))
}
)
.regime_filter(price().above_ref(sma(200)))
.warmup(200)
.build();
let config = BacktestConfig::builder()
.initial_capital(100_000.0)
.commission_pct(0.001)
.slippage_pct(0.0005)
.allow_short(false)
.build()?;
let result = ticker.backtest(
strategy,
Interval::OneDay,
TimeRange::TwoYears,
Some(config),
).await?;
println!("Backtest Results for AAPL");
println!("=========================");
println!("Total Return: {:.2}%", result.metrics.total_return_pct);
println!("Sharpe Ratio: {:.2}", result.metrics.sharpe_ratio);
println!("Win Rate: {:.2}%", result.metrics.win_rate * 100.0);
println!("Total Trades: {}", result.metrics.total_trades);
println!("Max Drawdown: {:.2}%", result.metrics.max_drawdown_pct * 100.0);
Ok(())
}
}
#[cfg(feature = "backtesting")]
#[rustfmt::skip]
#[allow(dead_code)]
fn doc_block_line_1309() {
use finance_query::backtesting::{BacktestConfig, StrategyBuilder};
use finance_query::backtesting::refs::*;
use finance_query::backtesting::condition::*;
use finance_query::{Interval, Ticker, TimeRange};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = BacktestConfig::builder()
.initial_capital(10_000.0)
.commission_pct(0.001) .slippage_pct(0.0005) .allow_short(false) .build()?;
let strategy = StrategyBuilder::new("Validated Strategy")
.entry(
rsi(14).crosses_below(30.0)
.and(price().above_ref(sma(200))) .and(volume().above_ref(sma(20))) )
.exit(
rsi(14).crosses_above(70.0)
.or(stop_loss(0.05)) .or(take_profit(0.15))
)
.build();
let ticker = Ticker::new("AAPL").await?;
let result = ticker.backtest(strategy, Interval::OneDay, TimeRange::OneYear, Some(config)).await?;
Ok(())
}
}