Aladdin Crypto Pair Strategy (MACD Enhanced) - Process Flow
This document outlines the logical steps of the aladdin_crypto_pair_strategy_macd
backtesting process, based on the provided flowchart.
1. Input Validation & Preparation
- Receive Inputs:
- Primary data:
analysis_results
(output from Gini_lamp_analyze_crypto_ratio
, containing prices, ratio, Z-score, initial signals).
- Strategy Parameters: MACD settings (Fast, Slow, Signal periods), Initial Capital, Transaction Fee %, Stop Loss %.
- Validate Inputs:
- Check if
analysis_results
has the expected structure and columns.
- Verify parameter types and ranges (e.g., capital > 0, fee >= 0, stop_loss > 0).
- If validation fails, the process stops here with an error.
- Identify Required Columns: Locate columns for Ticker 1 Price, Ticker 2 Price, Ratio (Ticker2/Ticker1), Z-Score, and Signal within the input data.
- Check Data Sufficiency: Ensure there are enough data rows to calculate the MACD indicator based on the specified periods.
- If data is insufficient, the process stops here with an error.
- Log Initial Setup: Record the starting parameters and the crypto pair being analyzed.
2. Indicator Calculation
- Sort Data: Ensure the input data is sorted chronologically by date/time.
- Calculate MACD: Compute the MACD (Moving Average Convergence Divergence) indicator, including its signal line and histogram, specifically on the Ticker2/Ticker1 price ratio.
- Combine Data: Merge the calculated MACD values back into the main dataframe, aligned by date.
- Derive Signals: Create simplified signal columns based on indicators:
Z-Score Direction
: 1 if Z-Score suggests Long Ratio (e.g., Z < threshold), -1 if Short Ratio (e.g., Z > threshold), 0 if Neutral (Hold). (Logic depends on specific Z-score strategy from input)
MACD Crossover
: 1 if MACD line crosses above Signal line (Bullish Crossover), -1 if MACD crosses below Signal line (Bearish Crossover), 0 otherwise.
3. Backtest State Initialization
- Initialize State Variables: Set up variables to track the backtest's state throughout the simulation:
cash_usd
= Initial Capital
qty_ticker1
= 0
qty_ticker2
= 0
position_type
= 0 (0: Flat, 1: Long Ratio, -1: Short Ratio)
entry_equity
= NA (Equity value at the time of trade entry)
entry_date
= NA
entry_price_T1
/ entry_price_T2
= NA
- Initialize Equity Curve DataFrame: Create an empty structure to store daily portfolio values (date, cash, quantities, prices, total equity).
- Initialize Trades Log: Create an empty list or structure to record details of each completed trade.
4. Simulation Loop Setup
- Determine Loop Start Index: Find the first row in the data where all required prices and calculated signals (MACD, Z-Score) are valid (not NA).
- If no valid starting point is found, the process stops here.
- Initialize Equity Curve Pre-Loop: Fill the equity curve DataFrame with the initial capital value for all dates *before* the determined loop start date.
- Log Simulation Start: Record the date and row index where the simulation loop begins.
5. Simulation Loop (Daily Iteration)
The following steps are performed for each day/row from the determined start index to the end of the data:
- Get Current Day Data: Extract Date, Price Ticker 1, Price Ticker 2, Z-Signal, MACD Cross signal for the current row.
- Validate Prices: Check if both Ticker 1 and Ticker 2 prices for the current day are valid (Not NA, greater than 0).
- If prices are invalid: Skip all trading logic for this day, carry forward the previous day's state (cash, quantities), record the state, update equity curve, and proceed to the next day.
- Calculate Start-of-Day (SoD) Equity: Calculate the portfolio value *before* any potential trades for the day:
SoD Equity = cash_usd + (qty_ticker1 * current_price_T1) + (qty_ticker2 * current_price_T2)
.
- Decision: Currently In a Position? (
position_type != 0
)
- IF YES (In Position): Proceed to Stop Loss Check
- Calculate Current Trade PnL %:
PnL = (SoD Equity / entry_equity) - 1
.
- Decision: PnL % < -Stop Loss %?
- IF YES (Stop Loss Triggered):
- ACTION: Trigger Stop Loss
- Liquidate Position: Simulate closing both legs (sell T1 if long, buy T1 if short; buy T2 if short, sell T2 if long) at current prices.
- Apply Exit Fees to both legs.
- Update
cash_usd
.
- Log Trade Details: Record the exit in the Trades Log with "Stop Loss" as the reason.
- Reset Position State: Set
position_type = 0
, clear entry variables (entry_equity
, entry_date
, etc.).
- Proceed directly to End-of-Day recording.
- IF NO (Stop Loss Not Triggered): Proceed to Exit Condition Check.
- Decision: Exit Conditions Met? (e.g., Z-Signal reverses direction OR MACD gives a counter-signal like bearish cross while long ratio / bullish cross while short ratio)
- IF YES (Exit Triggered):
- ACTION: Trigger Regular Exit
- Liquidate Position: Simulate closing both legs at current prices.
- Apply Exit Fees to both legs.
- Update
cash_usd
.
- Log Trade Details: Record the exit in the Trades Log with the reason (e.g., "Z Reversal", "MACD Cross").
- Reset Position State: Set
position_type = 0
, clear entry variables.
- Proceed directly to End-of-Day recording.
- IF NO (Hold Position): No action needed, proceed to End-of-Day recording.
- IF NO (Currently Flat -
position_type == 0
): Proceed to Entry Condition Check
- Decision: Entry Conditions Met? (e.g., Z-Signal indicates entry AND MACD confirms direction OR Z-Signal flips strongly)
- IF YES (Entry Triggered):
- ACTION: Trigger Entry
- Record Entry Equity: Store the current
SoD Equity
as entry_equity
.
- Allocate Capital: Determine dollar amount for each leg (e.g., 50% of current equity per leg).
- Apply Entry Fees: Deduct calculated fees for *both* legs from
cash_usd
.
- Determine Entry Type: Based on signals (Z-score, MACD), decide whether to go Long Ratio (Buy T2, Sell T1) or Short Ratio (Sell T2, Buy T1). Update
position_type
(1 or -1).
- Calculate Quantities: Based on allocated capital (less fees) and current prices, calculate
qty_ticker1
(will be negative if shorting T1) and qty_ticker2
(will be negative if shorting T2). Ensure one is positive and one is negative.
- Update Position State: Record
entry_date
, entry_price_T1
, entry_price_T2
.
- Log Entry: Print or log details of the new position opened.
- Proceed to End-of-Day recording.
- IF NO (Stay Flat): No action needed, proceed to End-of-Day recording.
- Record End-of-Day (EoD) State: Store the final
cash_usd
, qty_ticker1
, and qty_ticker2
for the current day *after* any trades and fee deductions.
- Calculate Final EoD Equity: Recalculate the total portfolio equity using the EoD state variables and current day's prices.
EoD Equity = final_cash_usd + (final_qty_T1 * price_T1) + (final_qty_T2 * price_T2)
.
- Update Equity Curve DataFrame: Record the EoD state (cash, quantities, prices, calculated equity) for the current date in the equity curve dataframe.
- Loop End Check: Is this the last row of data?
- IF NO: Proceed to the next day/row (Go back to step 1 of Section 5).
- IF YES: Exit the loop and proceed to Post-Processing.
6. Post-Processing & Performance Metrics
- Combine Trades Log: Consolidate the list of individual trade records into a single DataFrame.
- Fill Equity Curve NAs: Handle any potential missing values in the equity curve (e.g., forward fill).
- Filter Equity Curve: Ensure the equity curve data only covers the actual simulation period (from loop start date onwards).
- Check Valid Equity Data: Verify that the final equity curve contains valid numerical data.
- If no valid equity data exists (e.g., simulation failed early), return empty results and stop.
- Calculate Performance Metrics: Compute various statistics based on the equity curve and trades log:
- Total Return, Annualized Return
- Maximum Drawdown (value and percentage)
- Sharpe Ratio (typically calculated using daily equity returns)
- Trade Statistics: Win Rate, Average PnL (absolute and percentage), Profit Factor, Number of Trades, etc.
- Create Summary Table: Organize the calculated performance metrics into a structured DataFrame or summary object.
7. Output Generation
- Add Position Info to Data: Add a column to the original input dataframe indicating the
position_type
held on each day during the simulation.
- Generate Equity Curve Plot: Create a plot (e.g., using ggplot2) showing the portfolio equity over time.
- Prepare Data for Detailed Plot: Process data for a multi-panel plot (e.g., pivot longer, add shading for positions, mark trade entry/exit points).
- Generate Detailed Plot: Create the multi-panel plot showing prices, ratio, indicators (Z-score, MACD), and trading activity over time.
- Return Results: Package all outputs into a list or object containing:
- Performance Summary table/DF
- Equity Curve DataFrame
- Trades Log DataFrame
- Equity Curve Plot object
- Detailed Strategy Plot object
- Original data augmented with strategy position info
-- End of Strategy Logic --