# =============================================================================
# Turnaround Tuesday — full backtest
# From the article: https://edgelabtrading.com/blog/turnaround-tuesday/
#
# Rules:
#   Entry: if Monday closes below the previous trading day's close,
#          buy at Monday's close.
#   Exit:  sell at the first daily close above the previous day's high.
#
# Costs: 0.05% round trip (0.025% per side). Data: dividend-adjusted, yfinance.
#
# Requirements:  pip install yfinance pandas numpy matplotlib
# Run:           python edgelab-turnaround-tuesday.py
#
# EdgeLab — edgelabtrading.com
# Backtested results are hypothetical. Not financial advice.
# =============================================================================
import warnings; warnings.filterwarnings('ignore')
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import yfinance as yf

TICKER = 'QQQ'        # try 'SPY' as validation
START = '2003-01-01'
COMM_PER_SIDE = 0.00025

# --- data --------------------------------------------------------------------
raw = yf.download(TICKER, start='2002-10-01', auto_adjust=True, progress=False)
if isinstance(raw.columns, pd.MultiIndex):
    raw.columns = [c[0].lower() for c in raw.columns]
else:
    raw.columns = [c.lower() for c in raw.columns]
df = raw[['open', 'high', 'low', 'close']].dropna()

# --- signals -----------------------------------------------------------------
prev_close = df['close'].shift(1)
prev_high = df['high'].shift(1)
entry_sig = (df.index.dayofweek == 0) & (df['close'] < prev_close)   # weak Monday
exit_sig = df['close'] > prev_high                                   # strength confirmed

df = df.loc[START:]
entry_sig = entry_sig.loc[df.index]
exit_sig = exit_sig.loc[df.index]

# --- backtest (enter/exit at close) -------------------------------------------
closes = df['close'].values
dates = df.index
equity = 100_000.0
curve = np.full(len(closes), equity)
in_trade = False
entry_px = 0.0
trades = []

for i in range(len(closes)):
    px = closes[i]
    if not in_trade:
        if entry_sig.iloc[i]:
            entry_px = px
            equity *= (1 - COMM_PER_SIDE)
            in_trade = True
            entry_date = dates[i]
    else:
        if exit_sig.iloc[i]:
            ret = px / entry_px - 1
            equity *= (1 + ret) * (1 - COMM_PER_SIDE)
            trades.append({'entry': entry_date, 'exit': dates[i], 'ret_pct': ret * 100})
            in_trade = False
    curve[i] = equity * (px / entry_px) if in_trade else equity

curve = pd.Series(curve, index=dates)
tdf = pd.DataFrame(trades)

# --- stats ---------------------------------------------------------------------
r = curve.pct_change().dropna()
days = (curve.index[-1] - curve.index[0]).days
cagr = (curve.iloc[-1] / curve.iloc[0]) ** (365.25 / days) - 1
sharpe = r.mean() / r.std() * np.sqrt(252)
max_dd = ((curve - curve.cummax()) / curve.cummax()).min()
wins = tdf[tdf.ret_pct > 0]

print(f'--- Turnaround Tuesday on {TICKER}, {dates[0].date()} to {dates[-1].date()} ---')
print(f'CAGR:          {cagr * 100:6.2f}%')
print(f'Sharpe:        {sharpe:6.2f}')
print(f'Max drawdown:  {max_dd * 100:6.2f}%')
print(f'Trades:        {len(tdf):6d}')
print(f'Win rate:      {len(wins) / len(tdf) * 100:6.1f}%')
print(f'Final equity:  ${curve.iloc[-1]:,.0f}  (from $100,000)')

# --- chart (EdgeLab classic style) ---------------------------------------------
plt.rcParams.update({
    'figure.facecolor': 'white', 'axes.facecolor': 'white',
    'axes.grid': True, 'grid.color': '#c8c8c8',
    'axes.titleweight': 'bold',
})
fig, ax = plt.subplots(figsize=(9, 5.3))
ax.plot(curve.index, curve.values, color='#1f77b4', lw=1.3,
        label=f'Turnaround Tuesday on {TICKER}')
ax.set_title(f'Turnaround Tuesday — {TICKER} ({dates[0].year}–{dates[-1].year})')
ax.set_xlabel('Year')
ax.set_ylabel('Portfolio value (start $100k)')
ax.legend(loc='upper left')
fig.savefig('turnaround_tuesday_equity.png', dpi=150, bbox_inches='tight')
print('Chart saved: turnaround_tuesday_equity.png')
