Archive | Path Integral Formulation RSS feed for this section

📌 Path Integral Series Overview: ➡️ Part 1: Introduction to Path Integrals. Deriving the Path Integral Formulation in Quantum Mechanics

10 Mar

Introduction: From Operator Formalism to Path Integrals


Step 1: The Quantum Propagator

\boldsymbol{K(x_b, t_b; x_a, t_a) = \langle x_b | e^{-i \hat{H} (t_b - t_a)/\hbar} | x_a \rangle.}

\boldsymbol{K(x_b, t_b; x_a, t_a) = \langle x_b | (e^{-i \hat{H} \epsilon/\hbar})^N | x_a \rangle.}

\boldsymbol{K(x_b, t_b; x_a, t_a) = \int dx_1 \cdots dx_{N-1} \langle x_b | e^{-i \hat{H} \epsilon/\hbar} | x_{N-1} \rangle \cdots \langle x_1 | e^{-i \hat{H} \epsilon/\hbar} | x_a \rangle.}


Step 2: Approximating the Time Evolution Operator

\boldsymbol{e^{-i \hat{H} \epsilon / \hbar} \approx e^{-i \hat{T} \epsilon / \hbar} e^{-i \hat{V} \epsilon / \hbar} + \mathcal{O}(\epsilon^2).}

  • \boldsymbol{\hat{T} = \frac{\hat{p}^2}{2m}}​ is the kinetic energy operator,
  • \boldsymbol{\hat{V} = V(\hat{x})} is the potential energy operator.

Step 3: Inserting the Identity in the Momentum Basis

\mathbb{1} = \int \frac{dp_j}{2\pi\hbar} | p_j \rangle \langle p_j |.

\boldsymbol{\langle x_{j+1} | e^{-i \hat{H} \epsilon / \hbar} | x_j \rangle = \int \frac{dp_j}{2\pi\hbar} \langle x_{j+1} | e^{-i \hat{T} \epsilon / \hbar} | p_j \rangle \langle p_j | e^{-i \hat{V} \epsilon / \hbar} | x_j \rangle.}


\boldsymbol{e^{-i \hat{T} \epsilon / \hbar} | p_j \rangle = e^{-i p_j^2 \epsilon / 2m\hbar} | p_j \rangle.}

\boldsymbol{\langle x_{j+1} | e^{-i \hat{T} \epsilon / \hbar} | p_j \rangle = e^{-i p_j^2 \epsilon / 2m\hbar} \langle x_{j+1} | p_j \rangle.}

\boldsymbol{\langle x | p_j \rangle = \frac{1}{\sqrt{2\pi\hbar}} e^{i p_j x / \hbar}.}

\boldsymbol{\langle x_{j+1} | p_j \rangle = \frac{1}{\sqrt{2\pi\hbar}} e^{i p_j x_{j+1} / \hbar}.}

\boldsymbol{\langle x_{j+1} | e^{-i \hat{T} \epsilon / \hbar} | p_j \rangle = \frac{1}{\sqrt{2\pi\hbar}} e^{-i p_j^2 \epsilon / 2m\hbar} e^{i p_j x_{j+1} / \hbar}.}

\boldsymbol{e^{-i \hat{V} \epsilon / \hbar} | x_j \rangle = e^{-i V(x_j) \epsilon / \hbar} | x_j \rangle.}

\boldsymbol{\langle p_j | e^{-i \hat{V} \epsilon / \hbar} | x_j \rangle = e^{-i V(x_j) \epsilon / \hbar} \langle p_j | x_j \rangle.}

\boldsymbol{\langle p_j | e^{-i \hat{V} \epsilon / \hbar} | x_j \rangle = \frac{1}{\sqrt{2\pi\hbar}} e^{-i V(x_j) \epsilon / \hbar} e^{-i p_j x_j / \hbar}.}


\boldsymbol{\langle x_{j+1} | e^{-i \hat{H} \epsilon / \hbar} | x_j \rangle = \int \frac{dp_j}{2\pi\hbar} e^{i p_j (x_{j+1} - x_j)/\hbar} e^{-i (p_j^2 / 2m + V(x_j)) \epsilon / \hbar}.}


\boldsymbol{K(x_b, t_b; x_a, t_a) = \lim_{N \to \infty} \int \prod_{j=1}^{N-1} dx_j \prod_{j=0}^{N-1} \frac{dp_j}{2\pi\hbar} e^{i \sum_{j=0}^{N-1} \left[ p_j (x_{j+1} - x_j) / \hbar - (p_j^2 / 2m + V(x_j)) \epsilon / \hbar \right]}.}

\boldsymbol{K(x_b, t_b; x_a, t_a) = \int \mathcal{D}x(t) \mathcal{D}p(t) e^{i \int_{t_a}^{t_b} dt , [p \dot{x} - H(p, x)] / \hbar}.}

\boldsymbol{K(x_b, t_b; x_a, t_a) = \int \mathcal{D}x(t) e^{i S[x] / \hbar},}

\boldsymbol{S[x] = \int_{t_a}^{t_b} dt \left( \frac{1}{2} m \dot{x}^2 - V(x) \right).}


  • Quantum Simulations: Many quantum computing algorithms, such as variational quantum eigensolvers (VQE) and quantum Monte Carlo methods, rely on path integral formulations to approximate quantum states.

Mathematical Utility

\boldsymbol{K(x_b, t_b; x_a, t_a) = \int \mathcal{D}x(t) \, e^{i S[x]/\hbar}}


\boldsymbol{G(x, x'; E) = \int \mathcal{D}x(t) e^{i S[x]/\hbar}}


\boldsymbol{K(E) = \int \mathcal{D}x(t) e^{i S[x]/\hbar}}


\boldsymbol{P(x,t) = \int \mathcal{D}x(t) e^{-S[x]/\hbar}}

# Reinstall necessary packages in Google Colab after execution state reset
!pip install numpy pandas matplotlib yfinance scipy

# Re-import required libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import yfinance as yf

# Fetch historical AAPL stock data from Yahoo Finance
ticker = "AAPL"
start_date = "2015-01-01"
end_date = "2024-03-01"
data = yf.download(ticker, start=start_date, end=end_date)

# Compute log returns
data['Log_Returns'] = np.log(data['Close'] / data['Close'].shift(1))
data.dropna(inplace=True)

# Define Geometric Brownian Motion (GBM) Simulation
def simulate_gbm(S0, mu, sigma, T, dt, N):
    np.random.seed(42)
    steps = int(T / dt)
    paths = np.zeros((steps, N))
    paths[0] = S0
    for t in range(1, steps):
        dW = np.random.normal(0, np.sqrt(dt), N)
        paths[t] = paths[t-1] * np.exp((mu - 0.5 * sigma**2) * dt + sigma * dW)
    return paths

# Estimate drift and volatility for GBM
mu = np.mean(data['Log_Returns'])  
sigma = np.std(data['Log_Returns'])  

# Backtest Parameters
initial_capital = 10000
T = 252  # 1 year trading days
dt = 1  # 1-day interval
N = 1000  # Monte Carlo Simulations

# Simulate GBM paths
predicted_paths = simulate_gbm(data['Close'].iloc[-1], mu, sigma, T, dt, N)
expected_price = np.mean(predicted_paths[-1])

# Backtesting: Buy if predicted price > current price
investment = initial_capital if expected_price > data['Close'].iloc[-1].item() else 0
final_value = investment * (expected_price / data['Close'].iloc[-1].item())

# Compute ROI
roi = (final_value - initial_capital) / initial_capital * 100

# Plot GBM Simulation
plt.figure(figsize=(12,6))
plt.plot(predicted_paths[:, :10], alpha=0.3)  
plt.axhline(y=expected_price, color='r', linestyle='--', label=f'Expected Price: ${expected_price:.2f}')
plt.xlabel("Days")
plt.ylabel("Price")
plt.title(f"Simulated AAPL Price Paths (GBM) - Expected Price: ${expected_price:.2f}")
plt.legend()
plt.show()

# Print Backtest Results
print(f"Initial Capital: ${initial_capital:,.2f}")
print(f"Final Portfolio Value: ${final_value:,.2f}")
print(f"Total ROI: {roi:.2f}%")

# Fixing unrealistic compounding issue in rolling GBM strategy

# Define rolling window size (e.g., 1 month ~ 21 trading days)
# Further optimization: Balanced reinvestment and stop-loss/profit-taking

# Final optimization: Smarter entries using a Moving Average (50-day) & adjusted risk management

# Compute the 50-day moving average for trend confirmation
data['50_MA'] = data['Close'].rolling(window=50).mean()

# Define rolling window size (e.g., 1 month ~ 21 trading days)
window_size = 21

# Initialize portfolio and tracking variables
portfolio_value = initial_capital
holdings = 0
investment_log = []

# Adjusted reinvestment rate (90% of portfolio reinvested per trade)
reinvestment_rate = 0.90

# Adjusted stop-loss and profit-taking thresholds
stop_loss_threshold = -0.10  # Exit if trade loses 10%
profit_take_threshold = 0.25  # Take profits if trade gains 25%

# Iterate over rolling windows
for i in range(window_size, len(data) - T, window_size):
    # Get historical data for the rolling window
    window_data = data.iloc[i - window_size:i]

    # Recalculate drift and volatility for the rolling period
    mu_rolling = np.mean(window_data['Log_Returns'])
    sigma_rolling = np.std(window_data['Log_Returns'])

    # Simulate future price paths for the next T days
    predicted_paths = simulate_gbm(window_data['Close'].iloc[-1], mu_rolling, sigma_rolling, T, dt, N)
    expected_price = np.mean(predicted_paths[-1]).item()
    last_close_price = window_data['Close'].iloc[-1].item()

    # Compute expected return percentage
    expected_return = (expected_price - last_close_price) / last_close_price

    # **New Condition: Only Buy If Price is Above 50-Day Moving Average**
    is_above_50_ma = last_close_price > data['50_MA'].iloc[i]

    # Decision: Buy if expected price is higher than the current price & above 50-day MA
    if expected_price > last_close_price and expected_return > stop_loss_threshold and is_above_50_ma:
        capital_allocated = portfolio_value * reinvestment_rate  # Invest 90% of portfolio
        holdings = capital_allocated / last_close_price

        # Apply stop-loss: exit trade if loss exceeds threshold
        if expected_return < stop_loss_threshold:
            holdings = 0  # Exit trade

        # Apply profit-taking: exit trade if gain exceeds threshold
        if expected_return > profit_take_threshold:
            holdings = 0  # Take profit

    else:
        holdings = 0

    # Update portfolio value at the end of the period with relaxed reinvestment constraints
    new_portfolio_value = holdings * expected_price if holdings > 0 else portfolio_value

    # Apply a moderate reinvestment cap to prevent excessive compounding (Max 20% growth per period)
    new_portfolio_value = min(new_portfolio_value, portfolio_value * 1.2)

    # Append to investment log
    investment_log.append((window_data.index[-1], new_portfolio_value))

    # Update portfolio value
    portfolio_value = new_portfolio_value

# Compute final ROI with optimized strategy using trend confirmation
roi_rolling_smart = (portfolio_value - initial_capital) / initial_capital * 100

# Create a dataframe for tracking investment performance
investment_df_smart = pd.DataFrame(investment_log, columns=['Date', 'Portfolio Value'])

# Plot optimized portfolio growth over time with trend confirmation
plt.figure(figsize=(12,6))
plt.plot(investment_df_smart['Date'], investment_df_smart['Portfolio Value'], label="Portfolio Value (Smart GBM Strategy)")
plt.axhline(y=initial_capital, color='r', linestyle='--', label="Initial Capital ($10,000)")
plt.xlabel("Date")
plt.ylabel("Portfolio Value ($)")
plt.title("Smart Portfolio Performance Using GBM with Trend Confirmation")
plt.legend()
plt.grid()
plt.show()

# Display final smart backtest results
rolling_backtest_results_smart = pd.DataFrame({
    "Initial Capital ($)": [initial_capital],
    "Final Portfolio Value ($)": [portfolio_value],
    "Smart ROI (%)": [roi_rolling_smart]
})

# ✅ Use standard Pandas display function for Google Colab compatibility
from IPython.display import display
display(rolling_backtest_results_smart)

# Print final optimized ROI explicitly
print("\n--- Smart Final ROI Summary ---")
print(f"Initial Capital: ${initial_capital:,.2f}")
print(f"Final Portfolio Value (Smart Strategy): ${portfolio_value:,.2f}")
print(f"Total ROI (Smart Strategy): {roi_rolling_smart:.2f}%")

###################
# Compute Sharpe Ratio for risk-adjusted return analysis

# Risk-free rate assumption (US Treasury Bonds yield ~3% annualized)
risk_free_rate = 0.03

# Convert annualized risk-free rate to daily
risk_free_daily = risk_free_rate / 252  

# Compute daily returns from investment log
investment_df_smart['Daily_Returns'] = investment_df_smart['Portfolio Value'].pct_change().dropna()

# Compute Sharpe Ratio
excess_returns = investment_df_smart['Daily_Returns'] - risk_free_daily
sharpe_ratio = excess_returns.mean() / excess_returns.std()
sharpe_ratio_annualized = sharpe_ratio * np.sqrt(252)  # Annualized Sharpe Ratio

# Display results
sharpe_results = pd.DataFrame({
    "Final Portfolio Value ($)": [portfolio_value],
    "Total ROI (%)": [roi_rolling_smart],
    "Sharpe Ratio (Annualized)": [sharpe_ratio_annualized]
})

from IPython.display import display
display(sharpe_results)

# Print Sharpe Ratio Summary
print("\n--- Sharpe Ratio Analysis ---")
print(f"Final Portfolio Value: ${portfolio_value:,.2f}")
print(f"Total ROI: {roi_rolling_smart:.2f}%")
print(f"Annualized Sharpe Ratio: {sharpe_ratio_annualized:.2f}")

# Interpretation:
# - Sharpe Ratio > 1.0: Good risk-adjusted returns.
# - Sharpe Ratio > 2.0: Excellent strategy.
# - Sharpe Ratio < 1.0: Returns may not sufficiently compensate for risk.

########## WITH FEES ###########
# Adding trading fees to the strategy (default: 0.1% per trade)
trading_fee_rate = 0.001  # 0.1% fee per transaction

# Initialize portfolio with trading fees
portfolio_value = initial_capital
holdings = 0
investment_log = []

# Iterate over rolling windows with fees
for i in range(window_size, len(data) - T, window_size):
    window_data = data.iloc[i - window_size:i]

    # Recalculate drift and volatility
    mu_rolling = np.mean(window_data['Log_Returns'])
    sigma_rolling = np.std(window_data['Log_Returns'])

    # Simulate future prices
    predicted_paths = simulate_gbm(window_data['Close'].iloc[-1], mu_rolling, sigma_rolling, T, dt, N)
    expected_price = np.mean(predicted_paths[-1]).item()
    last_close_price = window_data['Close'].iloc[-1].item()

    # Compute expected return percentage
    expected_return = (expected_price - last_close_price) / last_close_price

    # Check if price is above 50-day Moving Average
    is_above_50_ma = last_close_price > data['50_MA'].iloc[i]

    # Decision: Buy if expected price is higher & above 50-day MA
    if expected_price > last_close_price and expected_return > stop_loss_threshold and is_above_50_ma:
        capital_allocated = portfolio_value * reinvestment_rate
        holdings = capital_allocated / last_close_price

        # Apply stop-loss and profit-taking
        if expected_return < stop_loss_threshold or expected_return > profit_take_threshold:
            holdings = 0  

        # Apply trading fee for entering a trade
        portfolio_value -= capital_allocated * trading_fee_rate

    else:
        holdings = 0

    # Update portfolio value, subtracting exit fees
    new_portfolio_value = holdings * expected_price if holdings > 0 else portfolio_value
    new_portfolio_value -= new_portfolio_value * trading_fee_rate  # Apply exit fee

    # Cap growth per rolling window
    new_portfolio_value = min(new_portfolio_value, portfolio_value * 1.2)

    # Append to investment log
    investment_log.append((window_data.index[-1], new_portfolio_value))

    # Update portfolio value
    portfolio_value = new_portfolio_value

# Compute final ROI with fees
roi_rolling_fees = (portfolio_value - initial_capital) / initial_capital * 100

# Compute Sharpe Ratio with fees
investment_df_fees = pd.DataFrame(investment_log, columns=['Date', 'Portfolio Value'])
investment_df_fees['Daily_Returns'] = investment_df_fees['Portfolio Value'].pct_change().dropna()

# Compute excess returns & Sharpe Ratio with fees
excess_returns_fees = investment_df_fees['Daily_Returns'] - risk_free_daily
sharpe_ratio_fees = excess_returns_fees.mean() / excess_returns_fees.std()
sharpe_ratio_annualized_fees = sharpe_ratio_fees * np.sqrt(252)  

# Display final results with fees
backtest_results_fees = pd.DataFrame({
    "Initial Capital ($)": [initial_capital],
    "Final Portfolio Value ($)": [portfolio_value],
    "Total ROI with Fees (%)": [roi_rolling_fees],
    "Sharpe Ratio (Annualized, With Fees)": [sharpe_ratio_annualized_fees]
})

from IPython.display import display
display(backtest_results_fees)

# Print Summary
print("\n--- Final Backtest Results (Including Trading Fees) ---")
print(f"Initial Capital: ${initial_capital:,.2f}")
print(f"Final Portfolio Value (With Fees): ${portfolio_value:,.2f}")
print(f"Total ROI (With Fees): {roi_rolling_fees:.2f}%")
print(f"Sharpe Ratio (With Fees, Annualized): {sharpe_ratio_annualized_fees:.2f}")

##############################################

# Optimizing trading fees impact with dynamic trade sizing and risk-adjusted entries

# Define dynamic fee structure
def get_trading_fee(trade_size):
    """ Variable trading fee: Lower for larger trades """
    if trade_size < 5000:
        return 0.001  # 0.1% fee for small trades
    else:
        return 0.0005  # 0.05% fee for large trades

# Calculate ATR (Average True Range) for dynamic stop-loss/profit-taking
data['High-Low'] = data['High'] - data['Low']
data['High-Close'] = np.abs(data['High'] - data['Close'].shift(1))
data['Low-Close'] = np.abs(data['Low'] - data['Close'].shift(1))
data['True Range'] = data[['High-Low', 'High-Close', 'Low-Close']].max(axis=1)
data['ATR'] = data['True Range'].rolling(window=14).mean()

# Initialize portfolio and tracking variables
portfolio_value = initial_capital
holdings = 0
investment_log = []

# Iterate over rolling windows with optimized fee & risk management
for i in range(window_size, len(data) - T, window_size):
    window_data = data.iloc[i - window_size:i]

    # Recalculate drift and volatility
    mu_rolling = np.mean(window_data['Log_Returns'])
    sigma_rolling = np.std(window_data['Log_Returns'])

    # Simulate future prices
    predicted_paths = simulate_gbm(window_data['Close'].iloc[-1], mu_rolling, sigma_rolling, T, dt, N)
    expected_price = np.mean(predicted_paths[-1]).item()
    last_close_price = window_data['Close'].iloc[-1].item()

    # Compute expected return percentage
    expected_return = (expected_price - last_close_price) / last_close_price

    # Check if price is above 50-day MA & get ATR for volatility-adjusted stop-loss
    is_above_50_ma = last_close_price > data['50_MA'].iloc[i]
    current_atr = data['ATR'].iloc[i]

    # Dynamic stop-loss and profit-taking (adjusting based on volatility)
    stop_loss_dynamic = -current_atr / last_close_price  # ATR-based stop-loss
    profit_take_dynamic = current_atr * 2 / last_close_price  # ATR-based profit target

    # Decision: Buy if expected price is higher & above 50-day MA
    if expected_price > last_close_price and expected_return > stop_loss_dynamic and is_above_50_ma:
        capital_allocated = portfolio_value * reinvestment_rate  # Invest 90% of portfolio
        trade_fee = capital_allocated * get_trading_fee(capital_allocated)  # Get dynamic fee
        capital_allocated -= trade_fee  # Deduct entry fee
        holdings = capital_allocated / last_close_price

        # Apply stop-loss and profit-taking
        if expected_return < stop_loss_dynamic or expected_return > profit_take_dynamic:
            holdings = 0  # Exit trade

    else:
        holdings = 0

    # Update portfolio value, subtracting exit fees
    new_portfolio_value = holdings * expected_price if holdings > 0 else portfolio_value
    exit_fee = new_portfolio_value * get_trading_fee(new_portfolio_value)
    new_portfolio_value -= exit_fee  # Apply exit fee

    # Cap growth per rolling window (max 20% per period)
    new_portfolio_value = min(new_portfolio_value, portfolio_value * 1.2)

    # Append to investment log
    investment_log.append((window_data.index[-1], new_portfolio_value))

    # Update portfolio value
    portfolio_value = new_portfolio_value

# Compute final ROI with optimized fee & trade management
roi_rolling_final = (portfolio_value - initial_capital) / initial_capital * 100

# Compute Sharpe Ratio with optimized strategy
investment_df_final = pd.DataFrame(investment_log, columns=['Date', 'Portfolio Value'])
investment_df_final['Daily_Returns'] = investment_df_final['Portfolio Value'].pct_change().dropna()

# Compute excess returns & Sharpe Ratio with fees
excess_returns_final = investment_df_final['Daily_Returns'] - risk_free_daily
sharpe_ratio_final = excess_returns_final.mean() / excess_returns_final.std()
sharpe_ratio_annualized_final = sharpe_ratio_final * np.sqrt(252)  

# Display final results with optimized trading fee management
backtest_results_final = pd.DataFrame({
    "Initial Capital ($)": [initial_capital],
    "Final Portfolio Value ($)": [portfolio_value],
    "Total ROI (Final Optimized %)": [roi_rolling_final],
    "Sharpe Ratio (Final Optimized, Annualized)": [sharpe_ratio_annualized_final]
})

from IPython.display import display
display(backtest_results_final)

# Print Final Optimized Backtest Summary
print("\n--- Final Optimized Backtest Results (With Dynamic Trading Fees & Risk Management) ---")
print(f"Initial Capital: ${initial_capital:,.2f}")
print(f"Final Portfolio Value (Optimized): ${portfolio_value:,.2f}")
print(f"Total ROI (Optimized Strategy): {roi_rolling_final:.2f}%")
print(f"Sharpe Ratio (Optimized, Annualized): {sharpe_ratio_annualized_final:.2f}")

#############################

# Fetch historical data for BTC and TSLA from Yahoo Finance for comparison
btc_ticker = "BTC-USD"
tsla_ticker = "TSLA"
start_date = "2015-01-01"
end_date = "2024-03-01"

# Download data for BTC and TSLA
btc_data = yf.download(btc_ticker, start=start_date, end=end_date)
tsla_data = yf.download(tsla_ticker, start=start_date, end=end_date)

# Compute log returns for BTC and TSLA
btc_data['Log_Returns'] = np.log(btc_data['Close'] / btc_data['Close'].shift(1))
btc_data.dropna(inplace=True)

tsla_data['Log_Returns'] = np.log(tsla_data['Close'] / tsla_data['Close'].shift(1))
tsla_data.dropna(inplace=True)

# Calculate 200-day Moving Average for trend-based filtering
btc_data['200_MA'] = btc_data['Close'].rolling(window=200).mean()
tsla_data['200_MA'] = tsla_data['Close'].rolling(window=200).mean()

# Function to backtest strategy on a given stock
def backtest_strategy(data, asset_name):
    global portfolio_value

    # Reinitialize portfolio
    portfolio_value = initial_capital
    holdings = 0
    investment_log = []

    # Iterate over rolling windows
    for i in range(window_size, len(data) - T, window_size):
        window_data = data.iloc[i - window_size:i]

        # Recalculate drift and volatility
        mu_rolling = np.mean(window_data['Log_Returns'])
        sigma_rolling = np.std(window_data['Log_Returns'])

        # Simulate future price paths
        predicted_paths = simulate_gbm(window_data['Close'].iloc[-1], mu_rolling, sigma_rolling, T, dt, N)
        expected_price = np.mean(predicted_paths[-1]).item()
        last_close_price = window_data['Close'].iloc[-1].item()

        # Compute expected return percentage
        expected_return = (expected_price - last_close_price) / last_close_price

        # Check if price is above 200-day Moving Average
        is_above_200_ma = last_close_price > data['200_MA'].iloc[i]

        # ATR for volatility-adjusted stop-loss/profit-taking
        current_atr = data['ATR'].iloc[i] if 'ATR' in data.columns else 0.02  # Default ATR if missing
        stop_loss_dynamic = -current_atr / last_close_price  # ATR-based stop-loss
        profit_take_dynamic = current_atr * 2 / last_close_price  # ATR-based profit target

        # Decision: Buy if expected price is higher & above 200-day MA
        if expected_price > last_close_price and expected_return > stop_loss_dynamic and is_above_200_ma:
            capital_allocated = portfolio_value * reinvestment_rate  # Invest 90% of portfolio
            trade_fee = capital_allocated * get_trading_fee(capital_allocated)  # Get variable fee
            capital_allocated -= trade_fee  # Deduct entry fee
            holdings = capital_allocated / last_close_price

            # Apply stop-loss and profit-taking
            if expected_return < stop_loss_dynamic or expected_return > profit_take_dynamic:
                holdings = 0  # Exit trade

        else:
            holdings = 0

        # Update portfolio value, subtracting exit fees
        new_portfolio_value = holdings * expected_price if holdings > 0 else portfolio_value
        exit_fee = new_portfolio_value * get_trading_fee(new_portfolio_value)
        new_portfolio_value -= exit_fee  # Apply exit fee

        # Cap growth per rolling window (max 20% per period)
        new_portfolio_value = min(new_portfolio_value, portfolio_value * 1.2)

        # Append to investment log
        investment_log.append((window_data.index[-1], new_portfolio_value))

        # Update portfolio value
        portfolio_value = new_portfolio_value

    # Compute final ROI
    roi_final = (portfolio_value - initial_capital) / initial_capital * 100

    # Compute Sharpe Ratio
    investment_df = pd.DataFrame(investment_log, columns=['Date', 'Portfolio Value'])
    investment_df['Daily_Returns'] = investment_df['Portfolio Value'].pct_change().dropna()
    excess_returns = investment_df['Daily_Returns'] - risk_free_daily
    sharpe_ratio = excess_returns.mean() / excess_returns.std()
    sharpe_ratio_annualized = sharpe_ratio * np.sqrt(252)

    # Store results
    results = pd.DataFrame({
        "Asset": [asset_name],
        "Initial Capital ($)": [initial_capital],
        "Final Portfolio Value ($)": [portfolio_value],
        "Total ROI (%)": [roi_final],
        "Sharpe Ratio (Annualized)": [sharpe_ratio_annualized]
    })

    return results

# Run strategy on BTC & TSLA
btc_results = backtest_strategy(btc_data, "Bitcoin (BTC)")
tsla_results = backtest_strategy(tsla_data, "Tesla (TSLA)")

# Combine results
final_results = pd.concat([btc_results, tsla_results], ignore_index=True)

# Display final results
from IPython.display import display
display(final_results)

# Print summary
print("\n--- Strategy Backtest Results on BTC & TSLA ---")
for index, row in final_results.iterrows():
    print(f"Asset: {row['Asset']}")
    print(f"  Final Portfolio Value: ${row['Final Portfolio Value ($)']:,.2f}")
    print(f"  Total ROI: {row['Total ROI (%)']:.2f}%")
    print(f"  Sharpe Ratio: {row['Sharpe Ratio (Annualized)']:.2f}\n")


\boldsymbol{S[x] = \int dt \, L(x, \dot{x})}