Quantitative 5-Min Daily Floor Pivots Breakout
Strategy Overview: The Quantitative 5-Min Daily Floor Pivots Breakout is a classic, structurally robust quantitative strategy that relies on time-tested support and resistance levels. Instead of lagging indicators, this algorithm computes the standard daily Floor Pivots by aggregating the previous trading day’s absolute high, low, and closing prices. By dynamically calculating the Pivot Point, Resistance 1 (R1), and Support 1 (S1) levels, it establishes objective volatility barriers for the current session. The system monitors the 5-minute timeframe, waiting patiently for intraday price action to decisively breach these critical boundaries. A close above the R1 resistance line triggers a long positional entry, anticipating a strong bullish trend expansion. Conversely, a breakdown below the S1 support level initiates a short trade to capitalize on institutional selling pressure. To tightly control risk, the framework enforces a rigid 50-point hard stop loss while aiming for a highly asymmetrical 100-point take-profit target. Built-in reversal logic seamlessly manages false breakouts by flipping the trade direction when opposite pivot levels are crossed. As a strict intraday model, it neutralizes all market exposure precisely at 15:15, ensuring zero overnight carry risk and protecting capital from unexpected morning gaps.
groupby("d").agg() to calculate the previous day’s High, Low, and Close, subsequently mapping the Floor Pivot, R1, and S1 levels onto the intraday 5-minute data.
1. Imports and Configuration
Summary: This section loads the necessary libraries and sets the core backtesting parameters, defining a 5-minute timeframe, enforcing an automatic intraday exit at 15:15, and applying a strict 50-point stop loss with a 100-point profit target.
import pandas as pd
import numpy as np
import datetime as dt
# ===================== CONFIG =====================
FILE_PATH = "Index_1_minute.csv"
DAYFIRST = True
EXIT_TIME = dt.time(15, 15)
SLIPPAGE = 0.5
# Strategy params
TIMEFRAME = "5min"
# Risk Management
STOP_LOSS_POINTS = 50.0
TARGET_POINTS = 100.0
# ==================================================
2. Data Loading and Preparation
Summary: This section ensures reliable ingestion of historical CSV data by transforming date strings into datetime objects, normalizing price data into numeric form, cleaning null entries, and aggregating the data into structured 5-minute intervals.
# ----------------- LOAD & PREP --------------------
print("Loading and preparing data...")
df = pd.read_csv(FILE_PATH)
df.columns = [c.strip().lower() for c in df.columns]
required = {"date","open","high","low","close"}
missing = required - set(df.columns)
if missing:
raise ValueError(f"CSV missing columns: {missing}. Need exactly {sorted(required)}")
df["dt"] = pd.to_datetime(df["date"], dayfirst=DAYFIRST)
df = df.sort_values("dt").reset_index(drop=True)
for c in ["open","high","low","close"]:
df[c] = pd.to_numeric(df[c], errors="coerce")
df = df.dropna(subset=["open","high","low","close","dt"]).copy()
# Resampling
df = df.set_index("dt").resample(TIMEFRAME).agg({
"open": "first",
"high": "max",
"low": "min",
"close": "last"
}).dropna().reset_index()
df["d"] = df["dt"].dt.date
df["t"] = df["dt"].dt.time
3. Daily Pivot Points Calculation
Summary: By utilizing Pandas’ powerful groupby functionality, this snippet aggregates intraday data to compute the preceding day’s absolute High, Low, and Close. From these structural extremes, it formulates the core Pivot point, R1 (Resistance), and S1 (Support) equations, merging them back onto the active intraday chart.
# ----------------- INDICATORS --------------------
print("Calculating Daily Pivot Points (R1 & S1)...")
# 1. Group by date to find daily High, Low, and Close
daily_df = df.groupby("d").agg(
day_high=("high", "max"),
day_low=("low", "min"),
day_close=("close", "last")
).reset_index()
# 2. Shift to get the PREVIOUS day's values
daily_df["prev_high"] = daily_df["day_high"].shift(1)
daily_df["prev_low"] = daily_df["day_low"].shift(1)
daily_df["prev_close"] = daily_df["day_close"].shift(1)
# 3. Calculate Floor Pivot Points
daily_df["pivot"] = (daily_df["prev_high"] + daily_df["prev_low"] + daily_df["prev_close"]) / 3
daily_df["R1"] = (2 * daily_df["pivot"]) - daily_df["prev_low"]
daily_df["S1"] = (2 * daily_df["pivot"]) - daily_df["prev_high"]
# 4. Merge the specific daily pivot levels back into the intraday timeframe dataframe
pivot_levels = daily_df[["d", "R1", "S1", "pivot"]]
df = df.merge(pivot_levels, on="d", how="left")
4. Utility Functions
Summary: Provides clean helper logic for backtesting. The stats function aggregates all executed trades into high-level metrics like Win Rate and Average PnL, whilst close_out universally structures point calculation for both long and short positions.
# -------------- UTILITIES ----------------
def stats(trades):
if not trades:
return {"trades":0,"wins":0,"win_rate":0.0,"avg_pnl":0.0,"total_pnl":0.0}
pnl = np.array([x["pnl"] for x in trades])
wins = (pnl > 0).sum()
return {
"trades": len(trades),
"wins": int(wins),
"win_rate": round(100*wins/len(trades), 2),
"avg_pnl": round(pnl.mean(), 2),
"total_pnl": round(pnl.sum(), 2),
}
def close_out(direction, entry, row_close):
return (row_close - entry) if direction=="long" else (entry - row_close)
5. The Core Backtest Engine
Summary: The central logic loop. It executes trades sequentially based on the R1/S1 breakout parameters. In addition to monitoring hard Stop-Loss/Take-Profit triggers, it facilitates an automatic 15:15 market-close liquidation. Dynamic positional flips occur instantly if price reverses through opposite pivot extremes.
# -------------- STRATEGY: PIVOT BREAKOUT --------------
def backtest_pivots(df):
print("Running backtest loop...")
trades = []
in_trade = False
direction = None
entry = None
entry_time = None
for _, r in df.iterrows():
# 1. INTRADAY TIME EXIT
if r["t"] >= EXIT_TIME:
if in_trade:
pnl = close_out(direction, entry, r["close"]) - SLIPPAGE
trades.append({
"date": r["d"], "entry_time": entry_time, "exit_time": r["dt"],
"strategy":"PIVOT_BREAKOUT", "dir":direction, "entry":entry,
"exit":r["close"], "pnl":pnl, "outcome":"TIME_EXIT"
})
in_trade = False
continue
if pd.isna(r["R1"]) or pd.isna(r["S1"]):
continue
# 2. SL / TP LOGIC
if in_trade:
sl_hit = False
tp_hit = False
exit_price = None
outcome = None
# Check conservative High/Low hits within the current candle
if direction == "long":
if r["low"] <= entry - STOP_LOSS_POINTS:
sl_hit = True
exit_price = entry - STOP_LOSS_POINTS
outcome = "STOP_LOSS"
elif r["high"] >= entry + TARGET_POINTS:
tp_hit = True
exit_price = entry + TARGET_POINTS
outcome = "TARGET"
elif direction == "short":
if r["high"] >= entry + STOP_LOSS_POINTS:
sl_hit = True
exit_price = entry + STOP_LOSS_POINTS
outcome = "STOP_LOSS"
elif r["low"] <= entry - TARGET_POINTS:
tp_hit = True
exit_price = entry - TARGET_POINTS
outcome = "TARGET"
# If stopped out or target reached, close the trade
if sl_hit or tp_hit:
pnl = close_out(direction, entry, exit_price) - SLIPPAGE
trades.append({
"date": r["d"], "entry_time": entry_time, "exit_time": r["dt"],
"strategy":"PIVOT_BREAKOUT", "dir":direction, "entry":entry,
"exit":exit_price, "pnl":pnl, "outcome":outcome
})
in_trade = False
direction = None
entry = None
entry_time = None
# Skip entry logic on the same candle we hit SL/TP
continue
# Signal Logic - Breakout beyond the Resistance 1 or Support 1
breakout_up = r["close"] > r["R1"]
breakout_down = r["close"] < r["S1"]
# 3. ENTRY LOGIC
if not in_trade:
if breakout_up:
direction = "long"
entry = r["close"]
entry_time = r["dt"]
in_trade = True
elif breakout_down:
direction = "short"
entry = r["close"]
entry_time = r["dt"]
in_trade = True
# 4. REVERSAL LOGIC
else:
if direction=="long" and breakout_down:
exit_price = r["close"]
pnl = (exit_price - entry) - SLIPPAGE
trades.append({
"date": r["d"], "entry_time": entry_time, "exit_time": r["dt"],
"strategy":"PIVOT_BREAKOUT", "dir":"long", "entry":entry,
"exit":exit_price, "pnl":pnl, "outcome":"REVERSE"
})
# Reverse to short
direction = "short"
entry = r["close"]
entry_time = r["dt"]
elif direction=="short" and breakout_up:
exit_price = r["close"]
pnl = (entry - exit_price) - SLIPPAGE
trades.append({
"date": r["d"], "entry_time": entry_time, "exit_time": r["dt"],
"strategy":"PIVOT_BREAKOUT", "dir":"short", "entry":entry,
"exit":exit_price, "pnl":pnl, "outcome":"REVERSE"
})
# Reverse to long
direction = "long"
entry = r["close"]
entry_time = r["dt"]
return trades
6. Execution and Reporting
Summary: Acts as the system’s runtime orchestrator. It triggers the backtest engine, displays terminal summaries, then structures the detailed timeline of trades into an Excel workbook complete with dedicated Weekly, Monthly, and Drawdown tracking tabs.
# -------------- EXECUTION & REPORTING --------------
pivot_trades = backtest_pivots(df)
summary = {"PIVOT_BREAKOUT": stats(pivot_trades)}
print(f"â
Backtest complete ({TIMEFRAME} candles)")
for name, s in summary.items():
print(f"\n{name} -> Trades: {s['trades']}, Wins: {s['wins']}, WinRate: {s['win_rate']}%, "
f"AvgPnL: {s['avg_pnl']}, TotalPnL: {s['total_pnl']}")
# Write results to Excel
output_file = "pivot_breakout_results.xlsx"
print(f"\nSaving results to {output_file}...")
with pd.ExcelWriter(output_file, engine="xlsxwriter") as writer:
if pivot_trades:
trades_df = pd.DataFrame(pivot_trades)
trades_df.to_excel(writer, sheet_name="PIVOT_BREAKOUT", index=False)
trades_df["date_dt"] = pd.to_datetime(trades_df["date"])
trades_df["week"] = trades_df["date_dt"].dt.to_period("W-FRI")
weekly = trades_df.groupby("week", as_index=False)["pnl"].sum()
weekly.rename(columns={"pnl": "total_points"}, inplace=True)
weekly["week"] = weekly["week"].astype(str)
weekly.to_excel(writer, sheet_name="Weekly_Report", index=False)
trades_df["month"] = trades_df["date_dt"].dt.to_period("M")
monthly = trades_df.groupby("month", as_index=False)["pnl"].sum()
monthly.rename(columns={"pnl": "total_points"}, inplace=True)
monthly["month"] = monthly["month"].astype(str)
monthly.to_excel(writer, sheet_name="Monthly_Report", index=False)
dd_rows = []
for m, mdf in trades_df.groupby("month"):
mdf = mdf.sort_values("exit_time")
cum = mdf["pnl"].cumsum()
max_dd = cum.min()
dd_rows.append({"month": str(m), "max_drawdown": max_dd})
dd_df = pd.DataFrame(dd_rows)
dd_df.to_excel(writer, sheet_name="Drawdown_Report", index=False)
pd.DataFrame([{"strategy": k, **v} for k, v in summary.items()]).to_excel(
writer, sheet_name="Summary", index=False
)
print("Process finished successfully.")
Browse the Full Quantitative Repository:
- â Intraday 10/30 Moving Average Crossover
- â Index 5-Min Bollinger Bands Mean Reversion
- â Algorithmic 5-Min EMA & MACD Confluence
- â Equity 5-Min RSI Momentum & EMA Trend
- â Volatility 5-Min Donchian Channel Breakout
- â Global Macro 30-Min Inside Bar Breakout
- â Institutional 15-Min Opening Range Breakout
- â Quantitative 5-Min Daily Floor Pivots Breakout
- â Price Action 30-Min Candlestick Engulfing Pattern
- â Systematic 5-Min Pin Bar Reversal (Hammer & Shooting Star)
