Structural Limits of OHLCV-Based Intraday Signals in MNQ Futures
Authors: Mathias Mesfin (2026) Source: arXiv:2605.04004 (preprint, công bố 05/05/2026) Tag:
moi:2026-05-16#intraday#us-futures#falsification#ohlcv#preprint
Ý tưởng cốt lõi
Mesfin (2026) tiến hành một falsification study mang tính hệ thống nhất từng được công bố trên dữ liệu intraday US futures: kiểm tra liệu các tín hiệu OHLCV phổ biến có thực sự tạo ra alpha vượt chi phí giao dịch trên hợp đồng MNQ (Micro E-mini Nasdaq 100) hay không. Mẫu là 947 phiên dữ liệu 5 phút từ 2021-2025, với 14 họ tín hiệu được test theo tiêu chuẩn institutional nghiêm ngặt: (a) out-of-sample walk-forward validation, (b) T-statistic ≥ 2.0, (c) ít nhất 30 lệnh, (d) net return dương sau chi phí cố định 2 điểm round-trip, (e) ổn định nhiều năm.
14 họ tín hiệu được test bao gồm: opening range breakouts, gap strategies, volume signals, cross-session momentum, liquidity grabs, volatility-conditioned classifiers, và news-driven strategies. Kết quả rất chấn động: KHÔNG CÓ tín hiệu nào thỏa mãn cả 5 tiêu chí cùng lúc. Gross edge khả dụng cho execution next-bar-open chỉ vào khoảng 0.07-1.50 điểm/lệnh — không đủ để bù chi phí giao dịch.
Đây không phải tin xấu cho ngành quant — mà là minh chứng quan trọng cho "alpha decay" và falsification rigor: hầu hết các backtest "thắng" trong blog/YouTube đều thiếu một hoặc nhiều tiêu chí institutional (overfitting, chi phí ảo, sample-size nhỏ, không walk-forward). Paper này cung cấp một baseline phủ định — bất kỳ ai claim alpha intraday trên US index futures phải vượt qua benchmark falsification của Mesfin để được tin.
Ứng dụng giao dịch chính
Bài học implementation:
Tiêu chí institutional cho mọi strategy backtest:
- T-stat ≥ 2.0 trên out-of-sample walk-forward.
- Hit count ≥ 30 lệnh per năm để statistical power đủ.
- Net return sau chi phí realistic (không phải fill-at-mid).
- Multi-year stability — không chỉ work in 2022 mà die in 2024.
OHLCV alone là chưa đủ: nếu chỉ dùng OHLCV để tạo signal, paper này gợi ý alpha có ý nghĩa thực tế chỉ tồn tại khi kết hợp với (a) tick/order book data, (b) cross-asset signals, (c) alternative data (news/sentiment), hoặc (d) execution alpha (better fills, không phải directional).
Falsification trước khi go-live: trước khi triển khai strategy thật, chạy qua 5 tiêu chí trên. Nếu fail bất kỳ tiêu chí nào → continue research, không trade.
Áp dụng đa thị trường
VN30F (Hợp đồng tương lai chỉ số Việt Nam)
- VN30F intraday có spread/cost tương đối thấp (~ 0.3-0.5 bps) → ngưỡng falsification có thể lỏng hơn MNQ.
- Áp dụng cùng 5 tiêu chí: 14 họ tín hiệu của Mesfin cần được port + test trên VN30F. Khả năng cao kết quả giống MNQ (no single OHLCV signal passes alone).
- Hệ quả thực tế: VN30F intraday strategy chỉ có alpha bền vững khi kết hợp với (a) flow data từ NĐT cá nhân (tick volume signed), (b) basis với cổ phiếu cơ sở, (c) calendar effects (đáo hạn).
US equity futures (ES, NQ, RTY, YM, MNQ)
- Đây là sản phẩm chính của paper. Kết luận trực tiếp: ngừng tin tưởng "100% mechanical OHLCV strategy" trên ES/NQ/MNQ.
- Hệ quả thực tế cho prop trader retail: dừng việc paid backtests có dấu hiệu overfitted (thường thấy trên TradingView, MQL5 marketplace).
- Hướng nâng cấp: thêm microstructure features (bid-ask imbalance, depth ratios) hoặc regime conditioning (VIX level, MOVE index).
Crypto spot & perpetual
- Crypto intraday có cấu trúc transaction cost rất khác: spread thường > 1 bps trên top altcoin, có thể > 10 bps trên small-cap.
- Kết luận của Mesfin "0.07-1.50 điểm gross edge" tương đương ~ 1-15 bps trên crypto majors — có thể vừa đủ cho BTC/ETH với cost taker ~ 1 bps trên perp Binance, nhưng KHÔNG đủ cho altcoin.
- Cảnh báo lớn cho crypto retail: rất nhiều "alpha bot" được bán trên Telegram dựa trên OHLCV-only — đây thường là overfitted; chạy through Mesfin's framework trước khi mua.
Cân nhắc cross-market chung
- OHLCV alone là baseline yếu: mọi strategy đáng tin phải có ÍT NHẤT 1 feature ngoài OHLCV.
- Transaction cost realistic mỗi market khác nhau — không thể copy/paste backtest framework.
- Falsification > optimization: mất 70% thời gian research để đập bỏ false signals, 30% để build real ones.
Minh họa Python
import numpy as np
import pandas as pd
def institutional_falsification_test(pnl_per_trade: pd.Series,
transaction_cost_per_trade: float = 2.0,
min_t_stat: float = 2.0,
min_trades_per_year: int = 30,
n_years_min: int = 3) -> dict:
"""
Test một chiến lược intraday theo framework Mesfin 2026.
pnl_per_trade: Series các giá trị PnL gross (điểm hoặc bps) per trade,
index là timestamp entry.
transaction_cost_per_trade: chi phí round-trip (điểm/bps).
min_t_stat: ngưỡng T-stat.
min_trades_per_year: số trade tối thiểu mỗi năm.
n_years_min: số năm tối thiểu để đảm bảo stability.
Trả về dict {'passed': bool, 'metrics': {...}, 'reasons_fail': [...]}.
"""
reasons = []
# 1. Net PnL sau cost
net_pnl = pnl_per_trade - transaction_cost_per_trade
if net_pnl.mean() <= 0:
reasons.append(f"net_pnl_mean_nonpositive: {net_pnl.mean():.4f}")
# 2. T-statistic của net PnL
n = len(net_pnl)
t_stat = net_pnl.mean() / (net_pnl.std() / np.sqrt(n)) if n > 1 else 0
if t_stat < min_t_stat:
reasons.append(f"t_stat_low: {t_stat:.2f} < {min_t_stat}")
# 3. Đủ số trade
years = (net_pnl.index.max() - net_pnl.index.min()).days / 365.25
trades_per_year = n / max(years, 1)
if trades_per_year < min_trades_per_year:
reasons.append(f"trades_per_year_low: {trades_per_year:.1f}")
# 4. Multi-year stability — Sharpe dương trong ít nhất n_years_min năm
yearly_sharpe = net_pnl.groupby(net_pnl.index.year).apply(
lambda x: x.mean() / x.std() * np.sqrt(252) if x.std() > 0 else 0
)
n_positive_years = (yearly_sharpe > 0).sum()
if n_positive_years < n_years_min:
reasons.append(f"positive_years: {n_positive_years} < {n_years_min}")
return {
'passed': len(reasons) == 0,
'metrics': {
'net_pnl_mean': float(net_pnl.mean()),
't_stat': float(t_stat),
'trades_per_year': float(trades_per_year),
'yearly_sharpe': yearly_sharpe.to_dict(),
'n_positive_years': int(n_positive_years),
'total_trades': int(n),
},
'reasons_fail': reasons
}
def walk_forward_validate(strategy_fn,
data: pd.DataFrame,
train_window: int = 252,
test_window: int = 63,
step: int = 63) -> pd.Series:
"""
Walk-forward validation: train trên train_window, test trên test_window kế tiếp.
strategy_fn(train_df, test_df) -> pd.Series PnL per trade trên test_df.
"""
all_pnl = []
start = 0
while start + train_window + test_window <= len(data):
train_df = data.iloc[start:start + train_window]
test_df = data.iloc[start + train_window:start + train_window + test_window]
pnl = strategy_fn(train_df, test_df)
all_pnl.append(pnl)
start += step
if not all_pnl:
return pd.Series(dtype=float)
return pd.concat(all_pnl)
# Pattern usage:
# pnl_oos = walk_forward_validate(my_opening_range_strategy, mnq_5min_df)
# result = institutional_falsification_test(pnl_oos, transaction_cost_per_trade=2.0)
# if not result['passed']:
# print('FAILED:', result['reasons_fail'])