Archive | Science Communication 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})}

Action-Reaction Law and Surface Forces in the Pop-Pop Boat Experiment

6 Jan

One of the most intriguing aspects of the pop-pop boat experiment, as demonstrated in the video, is its relation to Newton’s Third Law of Motion, often stated as:

“For every action, there is an equal and opposite reaction.”

This fundamental principle provides the foundation for understanding how the motion of the boat is generated. However, in the context of the experiment in this video, the application of the law becomes nuanced due to the influence of surface forces and fluid dynamics.


Action-Reaction Law in Pop-Pop Boats

In the simplest interpretation:

  1. Action: Water is expelled through the exhaust tubes due to the pressure generated by the expanding steam.
  2. Reaction: The boat moves forward as a result of the backward momentum of the expelled water.

This explains the forward motion of the boat during the expulsion phase. However, during the suction phase, water is drawn back into the tubes, which seemingly should cancel out the forward momentum. Yet, the boat still moves forward. Why?

Lord Munchausen defies physics: pulling his own boat forward in a whimsical battle against Newton’s Third Law, proving that imagination knows no limits!

The Role of Surface Forces

To understand this apparent contradiction, we must consider surface forces and momentum interactions within the system:

  1. Differential Momentum Exchange:
    • During water expulsion, the expelled water exerts momentum directly against the boat’s frame, propelling it forward.
    • During suction, water is drawn from all directions, and the opposing force is distributed over a larger area, resulting in weaker reverse momentum.
  2. Collision and Energy Dissipation:
    • As water is sucked back into the tubes, it collides with internal air pockets and the tube walls, dissipating energy. This results in a partial cancellation of the reverse momentum.
  3. Surface Interaction:
    • The tubes and the surrounding water interface act as a boundary where surface tension and viscosity influence fluid flow. These forces dampen the backward momentum during suction, further enhancing net forward motion.

Apparent Effects of Surface Forces

Surface forces also contribute to the efficiency of propulsion in several ways:

  • Collimation of Jet Streams: During expulsion, water exits the tubes in a more directed stream, producing a concentrated reaction force that maximizes forward motion.
  • Damping of Suction Dynamics: Surface tension and viscous drag smooth out oscillations, minimizing reverse momentum effects.
  • Stability and Directionality: Surface interactions stabilize the boat’s movement, preventing significant side-to-side oscillations that could waste energy.

Reconciling Theory and Observation

The experiment’s findings challenge simplified interpretations of action-reaction dynamics. By isolating the fluid interactions in the transparent boat, the video highlights that resonance and internal system dynamics dominate over simple expulsion-suction symmetry. This emphasizes the need to view the boat as a system where:

  • Net forward motion arises from asymmetric forces during the oscillatory cycle.
  • Momentum exchange within the tubes is modified by interactions at the gas-liquid boundary, including condensation effects and surface forces.

Broader Implications

The discussion of Newton’s Third Law in this context extends beyond pop-pop boats:

  • Fluid Propulsion Systems: Similar principles apply to jet engines and rockets, where nozzle design and fluid dynamics optimize thrust.
  • Heat Engines: The role of surface forces and resonance highlights the complexity of thermodynamic systems.
  • Biological Systems: Nature leverages asymmetric action-reaction mechanics in swimming organisms, where surface forces enhance propulsion efficiency.

The diagram and explanation together highlight the elegant interplay of forces and thermodynamics driving the boat.

Conclusion

While the pop-pop boat seems simple at first glance, its operation beautifully demonstrates the interplay between action-reaction forces, surface dynamics, and resonance. The experiment not only validates Newton’s Third Law (does it?^* ) but also sheds light on the subtle effects of fluid mechanics and energy dissipation, offering a richer understanding of motion in oscillatory systems. This discussion underscores the importance of revisiting fundamental laws in light of experimental nuances.

REF: https://www.youtube.com/watch?v=3AXupc7oE-g&list=LL&index=39

* The operation of the pop-pop boat provides a nuanced demonstration of Newton’s Third Law in conjunction with other physical principles, such as resonance and energy dissipation. While the experiment showcases the principle of action-reaction, it doesn’t rely solely on it to explain the net motion. The net forward motion results from asymmetric interactions and energy dissipation rather than a perfect pair of equal and opposite forces. Specifically:

  • Energy Loss and Momentum Cancellation:
    • During the suction phase, the reverse force imparted to the boat is partially canceled by energy dissipation (e.g., collision of water with air inside the tubes).
    • This leaves the forward expulsion force uncompensated, allowing the boat to move forward.
  • Surface Forces:
    • Viscosity, surface tension, and friction in the water-tube system introduce non-Newtonian effects, modifying the symmetry of action and reaction.

Thus, while the Third Law operates locally at every interaction point (e.g., between steam and water, or water and tube), the system as a whole relies on additional phenomena to achieve net forward motion.