//! Integration tests for btcore backtest engine
//!
//! These tests verify that the backtest results match expected values
//! and the transaction cost model is correct.

use btcore::{
    run_backtest, BacktestConfig,
    daily_returns, cumulative_returns, sharpe_ratio, sortino_ratio, max_drawdown,
};

/// Test transaction cost calculation
///
/// Finlab's transaction cost model:
/// - Entry: fee_ratio
/// - Exit: fee_ratio + tax_ratio
#[test]
fn test_transaction_costs() {
    // Single stock, buy at 100, sell at 100 (no price change)
    let prices = vec![
        vec![100.0],
        vec![100.0],
        vec![100.0],
    ];

    let signals = vec![
        vec![true],   // Day 0: enter
        vec![false],  // Day 1: exit
    ];

    let rebalance_indices = vec![0, 1];

    let fee_ratio = 0.001425;
    let tax_ratio = 0.003;

    let config = BacktestConfig {
        fee_ratio,
        tax_ratio,
        ..Default::default()
    };
    let creturn = run_backtest(&prices, &signals, &rebalance_indices, &config);

    // Day 0: Enter position
    // Cash starts at 1.0, buy for 1.0 * (1 + fee_ratio)
    // Position value = 1.0 - fee_cost
    let expected_after_entry = 1.0 - fee_ratio;

    // Day 1: Exit position
    // Sell for position_value * (1 - fee_ratio - tax_ratio)
    let expected_after_exit = expected_after_entry * (1.0 - fee_ratio - tax_ratio);

    // Check the cumulative returns
    assert_eq!(creturn.len(), 3);

    // Day 2 should reflect the exit
    // Note: The exact value depends on implementation details of rebalancing timing
    println!("creturn: {:?}", creturn);
    println!("expected_after_exit: {}", expected_after_exit);

    // Final value should be less than 1.0 due to transaction costs
    assert!(creturn[2] < 1.0, "Expected loss due to transaction costs");
}

/// Test equal weight portfolio
#[test]
fn test_equal_weight_portfolio() {
    // 3 days, 4 stocks
    let prices = vec![
        vec![100.0, 100.0, 100.0, 100.0],  // Day 0
        vec![110.0, 90.0, 100.0, 100.0],   // Day 1: +10%, -10%, 0%, 0%
        vec![110.0, 90.0, 100.0, 100.0],   // Day 2
    ];

    // Hold all 4 stocks
    let signals = vec![vec![true, true, true, true]];
    let rebalance_indices = vec![0];

    let config = BacktestConfig {
        fee_ratio: 0.0,
        tax_ratio: 0.0,
        ..Default::default()
    };
    let creturn = run_backtest(&prices, &signals, &rebalance_indices, &config);

    // Each stock has 25% weight
    // Day 1 return: 0.25 * 10% + 0.25 * (-10%) + 0.25 * 0% + 0.25 * 0% = 0%
    // With no transaction costs, the portfolio should stay around 1.0
    assert_eq!(creturn.len(), 3);
    assert!((creturn[1] - 1.0).abs() < 0.01, "Expected ~0% return, got {}", creturn[1] - 1.0);
}

/// Test position limit
#[test]
fn test_position_limit() {
    // Finlab-compatible T+1: Signal Day 0 → Execute Day 1 → Return on Day 2
    let prices = vec![
        vec![100.0, 100.0, 100.0],  // Day 0: signal
        vec![100.0, 100.0, 100.0],  // Day 1: entry
        vec![110.0, 100.0, 100.0],  // Day 2: Stock 0 +10%
    ];

    // Hold all 3 stocks, but with 40% limit each should be capped
    let signals = vec![vec![true, true, true]];
    let rebalance_indices = vec![0];

    let config = BacktestConfig {
        fee_ratio: 0.0,
        tax_ratio: 0.0,
        position_limit: 0.4,
        ..Default::default()
    };

    let creturn = run_backtest(&prices, &signals, &rebalance_indices, &config);

    assert_eq!(creturn.len(), 3);
    // Day 0 and Day 1: no return yet
    assert!((creturn[0] - 1.0).abs() < 1e-10);
    assert!((creturn[1] - 1.0).abs() < 1e-10);
    // Day 2: With 3 stocks and 40% limit, weights should be ~33% each
    // Return should be ~3.3% (1/3 * 10%)
    let expected_return = 10.0 / 3.0 / 100.0;  // ~3.3%
    let actual_return = creturn[2] - creturn[1];
    assert!((actual_return - expected_return).abs() < 0.01);
}

/// Test stop loss functionality
#[test]
fn test_stop_loss_exit() {
    // Stock drops 15% from entry
    let prices = vec![
        vec![100.0],
        vec![95.0],   // -5%
        vec![90.0],   // -10%
        vec![84.0],   // -16% (triggers 10% stop loss)
        vec![80.0],   // Should be flat (exited)
    ];

    let signals = vec![vec![true]];
    let rebalance_indices = vec![0];

    let config = BacktestConfig {
        fee_ratio: 0.0,
        tax_ratio: 0.0,
        stop_loss: 0.10,  // 10% stop loss
        ..Default::default()
    };

    let creturn = run_backtest(&prices, &signals, &rebalance_indices, &config);

    assert_eq!(creturn.len(), 5);

    // After stop loss triggers (day 3), portfolio should be flat
    // Day 3 and Day 4 should be the same (exited position)
    println!("creturn with stop loss: {:?}", creturn);
}

/// Test take profit functionality
#[test]
fn test_take_profit_exit() {
    // Stock gains 25% from entry
    let prices = vec![
        vec![100.0],
        vec![110.0],  // +10%
        vec![120.0],  // +20%
        vec![126.0],  // +26% (triggers 20% take profit)
        vec![130.0],  // Should be flat (exited)
    ];

    let signals = vec![vec![true]];
    let rebalance_indices = vec![0];

    let config = BacktestConfig {
        fee_ratio: 0.0,
        tax_ratio: 0.0,
        take_profit: 0.20,  // 20% take profit
        ..Default::default()
    };

    let creturn = run_backtest(&prices, &signals, &rebalance_indices, &config);

    assert_eq!(creturn.len(), 5);
    println!("creturn with take profit: {:?}", creturn);
}

/// Test finlab mode stop exit with fee - exposes bug where cost_basis not updated to market value
#[test]
fn test_finlab_mode_stop_exit_with_fee() {
    // Bug: In finlab mode, stop exit uses cost_basis instead of market value
    // This causes incorrect exit value when there are fees
    let prices = vec![
        vec![100.0],  // Day 0: signal
        vec![100.0],  // Day 1: entry at 100
        vec![125.0],  // Day 2: +25%, triggers 20% take profit
        vec![130.0],  // Day 3: execute exit (T+1)
        vec![140.0],  // Day 4: should be flat (already exited)
    ];

    let signals = vec![vec![true]];
    let rebalance_indices = vec![0];

    let config = BacktestConfig {
        fee_ratio: 0.01,  // 1% fee - this exposes the bug
        tax_ratio: 0.0,
        take_profit: 0.20,  // 20% take profit
        finlab_mode: true,
        ..Default::default()
    };

    let creturn = run_backtest(&prices, &signals, &rebalance_indices, &config);

    assert_eq!(creturn.len(), 5);
    println!("creturn finlab mode stop with fee: {:?}", creturn);

    // After exit (Day 3), portfolio should be flat
    assert!((creturn[4] - creturn[3]).abs() < 1e-10,
        "Portfolio should be flat after stop exit: day3={} day4={}", creturn[3], creturn[4]);

    // Key assertion: Final value should reflect the profit from 100 -> 130 (exit price)
    // With correct exit: market_value = 130, exit_value ≈ 130 * 0.99 = 128.7
    // With buggy exit: uses cost_basis = 100, exit_value ≈ 100 * 0.99 = 99
    //
    // The final creturn should be > 1.2 (we made 25%+ profit)
    // If bug exists, it would be close to 0.99 (loss!)
    assert!(creturn[4] > 1.2,
        "Final value {} should reflect profit (>1.2), bug would give ~0.99", creturn[4]);
}

/// Test multiple rebalance periods
#[test]
fn test_multiple_rebalance_periods() {
    // 6 days, 2 stocks
    let prices = vec![
        vec![100.0, 100.0],  // Day 0 (rebalance 1)
        vec![110.0, 90.0],   // Day 1
        vec![120.0, 80.0],   // Day 2
        vec![120.0, 80.0],   // Day 3 (rebalance 2: switch to stock 1 only)
        vec![110.0, 100.0],  // Day 4
        vec![100.0, 120.0],  // Day 5
    ];

    // Period 1: both stocks, Period 2: only stock 1
    let signals = vec![
        vec![true, true],
        vec![false, true],
    ];

    let rebalance_indices = vec![0, 3];

    let config = BacktestConfig {
        fee_ratio: 0.001425,
        tax_ratio: 0.003,
        ..Default::default()
    };
    let creturn = run_backtest(&prices, &signals, &rebalance_indices, &config);

    assert_eq!(creturn.len(), 6);

    // Verify values are reasonable
    for (i, &val) in creturn.iter().enumerate() {
        assert!(val > 0.0, "Day {}: creturn should be positive, got {}", i, val);
        assert!(val < 2.0, "Day {}: creturn should be reasonable, got {}", i, val);
    }
}

/// Test statistics calculations
#[test]
fn test_statistics_calculation() {
    // Known price series
    let prices = vec![100.0, 102.0, 99.0, 103.0, 105.0, 104.0, 107.0, 108.0, 106.0, 110.0];

    let returns = daily_returns(&prices);
    let creturn = cumulative_returns(&returns);

    // Test basic properties
    assert_eq!(returns.len(), 10);
    assert!(returns[0].is_none());  // First return is None

    assert_eq!(creturn.len(), 10);
    assert!((creturn[0] - 1.0).abs() < 1e-10);  // Starts at 1.0

    // Final cumulative return should match
    let expected_total_return = 110.0 / 100.0;
    assert!((creturn[9] - expected_total_return).abs() < 1e-10);

    // Test Sharpe ratio
    let valid_returns: Vec<f64> = returns.iter()
        .filter_map(|r| *r)
        .collect();
    let sharpe = sharpe_ratio(&valid_returns, 0.0, 252.0);
    assert!(sharpe.is_finite(), "Sharpe ratio should be finite");

    // Test Sortino ratio
    let sortino = sortino_ratio(&valid_returns, 0.0, 252.0);
    assert!(sortino.is_finite() || sortino.is_nan(), "Sortino ratio should be valid");

    // Test max drawdown
    let mdd = max_drawdown(&creturn);
    assert!(mdd <= 0.0, "Max drawdown should be non-positive");
    assert!(mdd >= -1.0, "Max drawdown should be >= -100%");
}

/// Test empty inputs
#[test]
fn test_empty_inputs() {
    let prices: Vec<Vec<f64>> = vec![];
    let signals: Vec<Vec<bool>> = vec![];
    let rebalance_indices: Vec<usize> = vec![];

    let config = BacktestConfig {
        fee_ratio: 0.001425,
        tax_ratio: 0.003,
        ..Default::default()
    };
    let creturn = run_backtest(&prices, &signals, &rebalance_indices, &config);

    assert!(creturn.is_empty());
}

/// Test single day
#[test]
fn test_single_day() {
    let prices = vec![vec![100.0, 200.0]];
    let signals = vec![vec![true, true]];
    let rebalance_indices = vec![0];

    let config = BacktestConfig {
        fee_ratio: 0.001425,
        tax_ratio: 0.003,
        ..Default::default()
    };
    let creturn = run_backtest(&prices, &signals, &rebalance_indices, &config);

    assert_eq!(creturn.len(), 1);
    // T+1 mode: Day 0 signal not yet executed = 1.0
    assert!((creturn[0] - 1.0).abs() < 1e-10, "Day 0 should be 1.0, got {}", creturn[0]);
}

// TODO: Fix trailing stop T+1 logic - test disabled until proper fix
// /// Test trailing stop functionality
// #[test]
// fn test_trailing_stop_exit() {
//     // Stock rises then falls 15% from peak
//     let prices = vec![
//         vec![100.0],
//         vec![110.0],  // +10% (new max)
//         vec![120.0],  // +20% (new max)
//         vec![125.0],  // +25% (new max)
//         vec![106.0],  // -15.2% from 125 peak (triggers 10% trailing stop)
//         vec![100.0],  // Should be flat (exited)
//     ];
//
//     let signals = vec![vec![true]];
//     let rebalance_indices = vec![0];
//
//     let config = BacktestConfig {
//         fee_ratio: 0.0,
//         tax_ratio: 0.0,
//         trail_stop: 0.10,  // 10% trailing stop
//         ..Default::default()
//     };
//
//     let creturn = run_backtest(&prices, &signals, &rebalance_indices, &config);
//
//     assert_eq!(creturn.len(), 6);
//     println!("creturn with trailing stop: {:?}", creturn);
//
//     // After trailing stop triggers (day 4), portfolio should be flat
//     // Day 5 and Day 4 should show position was exited
//     // The value should be around 1.06 (exited at 106/100)
//     assert!((creturn[5] - creturn[4]).abs() < 1e-10,
//         "Portfolio should be flat after trailing stop exit");
// }

/// Test custom weights backtest
#[test]
fn test_custom_weights_backtest() {
    // Finlab-compatible T+1: Signal Day 0 → Execute Day 1 → Return on Day 2
    let prices = vec![
        vec![100.0, 100.0, 100.0],  // Day 0: signal
        vec![100.0, 100.0, 100.0],  // Day 1: entry
        vec![110.0, 100.0, 90.0],   // Day 2: +10%, 0%, -10%
        vec![115.0, 100.0, 85.0],   // Day 3
    ];

    // 50% stock 0, 30% stock 1, 20% stock 2
    let weights = vec![vec![0.5, 0.3, 0.2]];
    let rebalance_indices = vec![0];

    let config = BacktestConfig {
        fee_ratio: 0.0,
        tax_ratio: 0.0,
        ..Default::default()
    };

    let creturn = run_backtest(&prices, &weights, &rebalance_indices, &config);

    assert_eq!(creturn.len(), 4);

    // Day 0 and Day 1: no return yet
    assert!((creturn[0] - 1.0).abs() < 1e-10);
    assert!((creturn[1] - 1.0).abs() < 1e-10);

    // Day 2 expected return: 0.5 * 10% + 0.3 * 0% + 0.2 * (-10%) = 3%
    let expected_day2 = 1.03;
    assert!((creturn[2] - expected_day2).abs() < 0.001,
        "Expected {}, got {}", expected_day2, creturn[2]);
}

/// Test custom weights with transaction costs
#[test]
fn test_custom_weights_with_fees() {
    let prices = vec![
        vec![100.0, 100.0],  // Day 0: signal
        vec![100.0, 100.0],  // Day 1: execute entry
        vec![100.0, 100.0],  // Day 2: signal exit
        vec![100.0, 100.0],  // Day 3: execute exit
    ];

    // Period 1: 60/40, Period 2: exit all
    let weights = vec![
        vec![0.6, 0.4],
        vec![0.0, 0.0],
    ];
    let rebalance_indices = vec![0, 2];

    let fee_ratio = 0.001425;
    let tax_ratio = 0.003;

    let config = BacktestConfig {
        fee_ratio,
        tax_ratio,
        ..Default::default()
    };

    let creturn = run_backtest(&prices, &weights, &rebalance_indices, &config);

    assert_eq!(creturn.len(), 4);

    // T+1 mode: Day 0 signal not yet executed = 1.0
    assert!((creturn[0] - 1.0).abs() < 1e-10,
        "Day 0: Expected 1.0, got {}", creturn[0]);

    // Day 1: Entry executed with fee, flat price
    let expected_day1 = 1.0 / (1.0 + fee_ratio);
    assert!((creturn[1] - expected_day1).abs() < 0.001,
        "Day 1: Expected ~{}, got {}", expected_day1, creturn[1]);

    // Day 2: Exit signal given but not executed, still holding
    assert!((creturn[2] - expected_day1).abs() < 0.001,
        "Day 2: Expected ~{}, got {}", expected_day1, creturn[2]);

    // Day 3: Exit executed with fee + tax
    let expected_day3 = expected_day1 * (1.0 - fee_ratio - tax_ratio);
    assert!((creturn[3] - expected_day3).abs() < 0.001,
        "Day 3: Expected ~{}, got {}", expected_day3, creturn[3]);
}

/// Test weights that sum to more than 1.0 (leverage scenario)
#[test]
fn test_overweight_normalization() {
    // Finlab-compatible T+1: Signal Day 0 → Execute Day 1 → Return on Day 2
    let prices = vec![
        vec![100.0, 100.0],  // Day 0: signal
        vec![100.0, 100.0],  // Day 1: entry
        vec![110.0, 110.0],  // Day 2: Both +10%
    ];

    // Weights sum to 2.0, should be normalized to 1.0
    let weights = vec![vec![1.0, 1.0]];
    let rebalance_indices = vec![0];

    let config = BacktestConfig {
        fee_ratio: 0.0,
        tax_ratio: 0.0,
        ..Default::default()
    };

    let creturn = run_backtest(&prices, &weights, &rebalance_indices, &config);

    assert_eq!(creturn.len(), 3);
    // Day 0 and Day 1: no return yet
    assert!((creturn[0] - 1.0).abs() < 1e-10);
    assert!((creturn[1] - 1.0).abs() < 1e-10);
    // Day 2: Normalized to 0.5, 0.5 - total return = 10%
    let expected_day2 = 1.10;
    assert!((creturn[2] - expected_day2).abs() < 0.001,
        "Expected {}, got {}", expected_day2, creturn[2]);
}

/// Test partial allocation (weights sum to less than 1.0)
#[test]
fn test_partial_allocation() {
    // Finlab-compatible T+1: Signal Day 0 → Execute Day 1 → Return on Day 2
    let prices = vec![
        vec![100.0],  // Day 0: signal
        vec![100.0],  // Day 1: entry
        vec![120.0],  // Day 2: +20%
    ];

    // Only 30% allocated
    let weights = vec![vec![0.3]];
    let rebalance_indices = vec![0];

    let config = BacktestConfig {
        fee_ratio: 0.0,
        tax_ratio: 0.0,
        ..Default::default()
    };

    let creturn = run_backtest(&prices, &weights, &rebalance_indices, &config);

    assert_eq!(creturn.len(), 3);
    // Day 0 and Day 1: no return yet
    assert!((creturn[0] - 1.0).abs() < 1e-10);
    assert!((creturn[1] - 1.0).abs() < 1e-10);
    // Day 2: 30% position gains 20%, 70% cash stays flat
    // Total return: 0.3 * 20% + 0.7 * 0% = 6%
    let expected_day2 = 1.06;
    assert!((creturn[2] - expected_day2).abs() < 0.001,
        "Expected {}, got {}", expected_day2, creturn[2]);
}

/// Test weights rebalancing between periods
#[test]
fn test_weights_rebalancing() {
    // Finlab-compatible T+1:
    // Signal 0 on Day 0 → Execute Day 1 → Return on Day 2
    // Signal 1 on Day 2 → Execute Day 3 → Return on Day 4
    let prices = vec![
        vec![100.0, 100.0],  // Day 0: signal 0
        vec![100.0, 100.0],  // Day 1: execute signal 0, entry
        vec![110.0, 100.0],  // Day 2: Stock 0 +10%, signal 1
        vec![110.0, 100.0],  // Day 3: execute signal 1, switch to stock 1
        vec![110.0, 110.0],  // Day 4: Stock 1 +10%
    ];

    // Period 1: 100% stock 0, Period 2: 100% stock 1
    let weights = vec![
        vec![1.0, 0.0],
        vec![0.0, 1.0],
    ];
    let rebalance_indices = vec![0, 2];

    let config = BacktestConfig {
        fee_ratio: 0.0,
        tax_ratio: 0.0,
        ..Default::default()
    };

    let creturn = run_backtest(&prices, &weights, &rebalance_indices, &config);

    assert_eq!(creturn.len(), 5);

    // Day 0 and Day 1: no return yet
    assert!((creturn[0] - 1.0).abs() < 1e-10);
    assert!((creturn[1] - 1.0).abs() < 1e-10);

    // Day 2: gained 10% on stock 0
    assert!((creturn[2] - 1.10).abs() < 0.001, "Day 2: Expected 1.10, got {}", creturn[2]);

    // Day 3: rebalance executed - sell stock 0, buy stock 1 (no return on switch day)
    assert!((creturn[3] - 1.10).abs() < 0.001, "Day 3: Expected 1.10, got {}", creturn[3]);

    // Day 4: gained 10% on stock 1 on 1.10 base = 1.21
    assert!((creturn[4] - 1.21).abs() < 0.001,
        "Day 4: Expected 1.21, got {}", creturn[4]);
}

/// Test stop_trading_next_period=true (default behavior)
/// After stop loss triggers, the stock cannot re-enter in the next period
#[test]
fn test_stop_trading_next_period_true() {
    // Stock drops 15% then recovers, with rebalance trying to re-enter
    let prices = vec![
        vec![100.0],  // Day 0: signal
        vec![100.0],  // Day 1: entry
        vec![84.0],   // Day 2: -16% triggers 10% stop loss, exit
        vec![100.0],  // Day 3: signal (try to re-enter)
        vec![100.0],  // Day 4: cannot re-enter because stop_trading_next_period=true
        vec![110.0],  // Day 5: should remain flat (not holding)
    ];

    let signals = vec![
        vec![true],   // Day 0: enter
        vec![true],   // Day 3: try to re-enter
    ];
    let rebalance_indices = vec![0, 3];

    let config = BacktestConfig {
        fee_ratio: 0.0,
        tax_ratio: 0.0,
        stop_loss: 0.10,  // 10% stop loss
        stop_trading_next_period: true,  // default: cannot re-enter
        ..Default::default()
    };

    let creturn = run_backtest(&prices, &signals, &rebalance_indices, &config);

    assert_eq!(creturn.len(), 6);
    println!("creturn with stop_trading_next_period=true: {:?}", creturn);

    // After stop loss on day 2, position should be flat
    // Day 4-5 should remain flat because re-entry is blocked
    assert!((creturn[4] - creturn[3]).abs() < 1e-10,
        "Day 4 should be flat (re-entry blocked), got {} vs {}", creturn[4], creturn[3]);
    assert!((creturn[5] - creturn[4]).abs() < 1e-10,
        "Day 5 should be flat (re-entry blocked), got {} vs {}", creturn[5], creturn[4]);
}

/// Test stop_trading_next_period=false
/// After stop loss triggers, the stock CAN re-enter in the next period
#[test]
fn test_stop_trading_next_period_false() {
    // Stock drops 15% then recovers, with rebalance trying to re-enter
    let prices = vec![
        vec![100.0],  // Day 0: signal
        vec![100.0],  // Day 1: entry
        vec![84.0],   // Day 2: -16% triggers 10% stop loss, exit
        vec![100.0],  // Day 3: signal (try to re-enter)
        vec![100.0],  // Day 4: CAN re-enter because stop_trading_next_period=false
        vec![110.0],  // Day 5: should have +10% return (holding position)
    ];

    let signals = vec![
        vec![true],   // Day 0: enter
        vec![true],   // Day 3: try to re-enter
    ];
    let rebalance_indices = vec![0, 3];

    let config = BacktestConfig {
        fee_ratio: 0.0,
        tax_ratio: 0.0,
        stop_loss: 0.10,  // 10% stop loss
        stop_trading_next_period: false,  // CAN re-enter
        ..Default::default()
    };

    let creturn = run_backtest(&prices, &signals, &rebalance_indices, &config);

    assert_eq!(creturn.len(), 6);
    println!("creturn with stop_trading_next_period=false: {:?}", creturn);

    // After re-entry on day 4, day 5 should show return
    // The portfolio should have gained on day 5 (110 vs 100 = +10%)
    let day5_return = creturn[5] / creturn[4];
    assert!(day5_return > 1.05,
        "Day 5 should show positive return from re-entered position, got {} (ratio: {})",
        creturn[5], day5_return);
}

/// Test retain_cost_when_rebalance=false (default)
/// Entry prices should be reset on every rebalance, affecting stop loss calculation
#[test]
fn test_retain_cost_when_rebalance_false() {
    // Stock: 100 -> 105 -> 110 (rebalance) -> 100 (should NOT trigger stop loss)
    // Because entry price is reset to 110 on rebalance, 100 is only -9% drop
    let prices = vec![
        vec![100.0],  // Day 0: signal
        vec![100.0],  // Day 1: entry at 100
        vec![105.0],  // Day 2: +5%
        vec![110.0],  // Day 3: +10% total, rebalance signal, entry price reset to 110
        vec![110.0],  // Day 4: rebalance executed
        vec![100.0],  // Day 5: drop from 110 to 100 = -9%, should NOT trigger 10% stop loss
    ];

    let signals = vec![
        vec![true],   // Day 0: enter
        vec![true],   // Day 3: rebalance (same position)
    ];
    let rebalance_indices = vec![0, 3];

    let config = BacktestConfig {
        fee_ratio: 0.0,
        tax_ratio: 0.0,
        stop_loss: 0.10,  // 10% stop loss
        retain_cost_when_rebalance: false,  // reset entry price on rebalance
        ..Default::default()
    };

    let creturn = run_backtest(&prices, &signals, &rebalance_indices, &config);

    assert_eq!(creturn.len(), 6);
    println!("creturn with retain_cost_when_rebalance=false: {:?}", creturn);

    // Day 5: drop from 110 to 100 = -9%, below 10% stop loss threshold
    // Since entry price was reset to 110 on rebalance, stop loss should NOT trigger
    // Portfolio should reflect the loss (approximately 100/110 of day 4 value)
    let day5_change = creturn[5] / creturn[4];
    assert!(day5_change > 0.85 && day5_change < 0.95,
        "Day 5 should show ~9% loss (no stop loss triggered), got ratio: {}", day5_change);
}

/// Test retain_cost_when_rebalance=true
/// Entry prices should be retained across rebalances, affecting stop loss calculation
#[test]
fn test_retain_cost_when_rebalance_true() {
    // Stock: 100 -> 105 -> 110 (rebalance) -> 100 -> 88 (should trigger stop loss)
    // Because entry price is retained at 100, 88 is -12% drop from original entry
    let prices = vec![
        vec![100.0],  // Day 0: signal
        vec![100.0],  // Day 1: entry at 100
        vec![105.0],  // Day 2: +5%
        vec![110.0],  // Day 3: +10%, rebalance signal, entry price RETAINED at 100
        vec![110.0],  // Day 4: rebalance executed
        vec![88.0],   // Day 5: drop from 100 to 88 = -12%, should trigger 10% stop loss
    ];

    let signals = vec![
        vec![true],   // Day 0: enter
        vec![true],   // Day 3: rebalance (same position)
    ];
    let rebalance_indices = vec![0, 3];

    let config = BacktestConfig {
        fee_ratio: 0.0,
        tax_ratio: 0.0,
        stop_loss: 0.10,  // 10% stop loss
        retain_cost_when_rebalance: true,  // keep original entry price
        ..Default::default()
    };

    let creturn = run_backtest(&prices, &signals, &rebalance_indices, &config);

    assert_eq!(creturn.len(), 6);
    println!("creturn with retain_cost_when_rebalance=true: {:?}", creturn);

    // Day 5: drop from original entry 100 to 88 = -12%, triggers 10% stop loss
    // Position should be exited, so returns should reflect the exit
    // The exact return depends on when stop loss is calculated vs exit price
    // Key point: with retain_cost_when_rebalance=true, stop loss triggers earlier
    // because we compare against original entry price (100), not rebalanced price (110)

    // Verify that return is closer to 88/100 = 0.88 (stop triggered at original entry)
    // rather than 88/110 = 0.8 (if measured from rebalance)
    assert!(creturn[5] < creturn[4] * 0.92,
        "Day 5 should show significant loss due to stop loss, got ratio: {}", creturn[5] / creturn[4]);
}

// Note: Long format tests moved to btcore/src/simulation/long.rs unit tests
// and Python integration tests (tests/test_long_format.py)
