
取引量反転トレンドキャプチャ戦略は,異常な取引量と価格行動に基づく定量化取引方法で,市場の方向転換が起こる可能性がある重要な瞬間を識別することを目的としています.この戦略の核心は,取引量が大幅に平均より高い取引量を持つK線を探し,取引量が減少していることを確認すると,以前のトレンド方向に従って逆の取引決定を行います.これは,大取引量後の市場の心理的な変化,すなわち,多くの介入後に通常短期的な反転が起こるいわゆる”中指形”の市場を利用しています.この戦略は,市場への入場価格を制限し,固定数点またはATRに基づくストップとストップのレベルを設定し,同時に,市場が低流動的な時期を回避するためにタイムフィルタ機能を含んでいます.
この戦略の核心原理は,市場異常取引量後のトレンド反転現象に基づいている.具体的操作の論理は以下の通りである.
異常な取引量を特定するシステムでは,前Kラインが平均より著しく高い取引量を持っているかどうかを検出します.通常の取引時間 (RTH) では,取引量は最近の平均取引量の3倍 (調整可能) を超える必要があります. 閉店後のまたは特別な時間 (ETH) では,5倍以上 (調整可能) を超える必要があります. 平均取引量計算は,RTHの限界時間,閉店後の4-6時間,そして日曜日の閉店前の時間を自動的に排除します.
取引量減少を確認:現在のK線は,前回のK線より低い取引量で,大額取引が終了したことを示す必要があります.
トレンドの方向性: 異常取引量K線前の閉盘価格とSMA (単純移動平均) の関係を比較して,トレンドの方向を決定する.
逆転入場信号:
入力実行:
リスク管理: 系統は,異なる品種特性に応じて,2種類の停止/停止設定を提供します.
タイムフィルター戦略は,RTHの最初の15分間の取引信号を選択的にフィルターし,取引終了後の営業時間 (4-6時) と日曜日の営業前の信号を常にフィルターします.
重要な転換点を捉える戦略は,通常,市場情緒の顕著な変化を表す異常取引量に伴う市場転換点を捕捉することに焦点を当てており,より高い勝率の取引機会を提供します.
入り口の正確な位置: 制限価格単元を使用して,異常取引量K線の高点/低点で入場し,技術的に重要な価格レベルで取引を確実にし,入場精度を向上させる.
適応の種類を特定する戦略: 異なる取引時間 (通常取引時間 vs 閉盤後/特別時間) に応じて異常取引量の判断基準を動的に調整し,市場実況に適合する.
リスク管理の柔軟性: 固定ポイント数とATRに基づくストップ/ストップオプションを提供し,異なる品種の特性と波動性に応じて個別設定することができます.
スマートタイムフィルター: 低流動性や不安定な取引時間を自動で識別し,フィルタリングし,市場開盤と閉盤の近くで容易に見られる偽信号を避ける.
明確な視覚的フィードバック戦略: グラフ上の直感的な視覚指示,異常取引量K線高輝,トレンドSMA線,ストップダストストップの平衡を含む,トレーダーの監視と分析を便利にする.
自動実行条件を満たすと,システムは自動で制限注文とストップ・ストップの設定を実行し,人間の介入を軽減し,取引の規律を維持します.
偽の突破の危険性異常な取引量により,短期的に重要なレベルを突破する可能性があるが,その後,誤ったシグナルを発生させるため,迅速に引き下がる可能性がある.このリスクを緩和するために,RSIの超買い/超売り確認または突破の持続時間要求などの確認指標を追加することを検討することができます.
ニュース・ドライブの影響:重要な経済データや企業の発表によって異常な取引量が生じることがありますが,これらの反応はすぐに逆転するのではなく,長期間続く傾向があります.重要な経済データが出る前に,戦略を一時停止するか,フィルタリング条件を追加することをお勧めします.
市場環境の変化のリスク: 強いトレンドの市場では,逆向きの取引は,継続的に不利な価格動向に直面する可能性があります. 強いトレンドの環境で逆向きの操作を避けるために,長期トレンドフィルターを追加することを検討することができます.
限価券の未取引リスク:価格が次のK線で設定された限値レベルに触れない場合,取引シグナルは無効になる可能性があります. 最大有効期限を設定することを考慮することができます.
低流動性のリスク: 策略には時間フィルタ機能が含まれていますが,特定の時期には,特定の品種が流動性の不足に直面する可能性があります. 取引品種の特性に合わせて取引時間の制限を調整することをお勧めします.
パラメータ最適化のリスク: 過度な最適化策略のパラメータは,過去データに適合し過ぎて,将来的に不良なパフォーマンスを引き起こす可能性があります.パラメータを合理的な範囲で確保し,サンプル外テストによって戦略の安定性を検証する必要があります.
複数のタイムサイクルを確認:より高い時間周期のトレンドフィルターを追加し,より大きなトレンド方向でより高い勝利率を確保する.例えば,日線トレンド方向をチェックし,日線トレンドと一致するときにのみ参加することができます.
取引量の質評価純量エネルギー大小に加えて,取引量の質の評価,例えば取引量加重平均価格 (VWAP) の偏差を増加させることを考慮して,大きな取引量の背後にある市場行動をよりよく理解することができます.
ダイナミック・ストップ・ローズ戦略:変動率に基づくダイナミックなストップを実現し,取引が有利な方向に進むにつれて自動的にストップポジションを調整し,利益の一部をロックする.例えば,追跡ストップを使用するか,重要なレベルを突破した後にストップをコスト価格に移動することができます.
多種関連性フィルター関連品種 (株価指数期貨と現金,金と銀など) について,関連品種の確認指標を追加することで,信号の質が向上する.複数の関連品種が同時に異常な取引量と価格行動が発生する場合,信号はより信頼性がある可能性があります.
機械学習の最適化: 機械学習アルゴリズムの分析により,歴史データから最も成功した異常取引量パターンの特性を分析し,エントリー条件とパラメータを動的に調整します.例えば,決定木またはランダムフォレストを使用して,与えられた異常取引量特性の下で最適な行動を予測できます.
変動率調整:市場の現在の波動率状態に応じて異常取引量の判定基準と止損/ストップのレベルを調整する.高い波動率の環境で異常量を高めることで値が判定され,ストップの距離が減少する.低波動率の環境では,その逆である.
基本的なフィルターを追加: 重要な経済データ発表日や四半期決算の日に戦略パラメータを季節的に調整するか,ニュース面の干渉による偽信号を避けるために取引を一時的に停止する.
取引量逆転トレンドキャプチャ戦略は,取引量と価格行動に焦点を当てた定量化取引システムであり,異常な取引量後の市場情緒の変化を識別して潜在的な逆転点を捕捉します. この戦略は,技術的に入場,出場条件とリスク管理ルールを明確に定義し,市場が低品質な時期を回避するためにスマートな時間フィルタリングメカニズムが含まれています.
戦略の核心的な優点は,市場の”中指形”を正確に捉えることであり,市場参加者が大量に涌入し,その後撤退するときに,短期的な逆転の機会が形成される傾向がある.この戦略は,限値単一の正確な位置を重要な価格レベルに設定し,合理的なストップ・ローズ・ストップ管理と組み合わせることで,規律的な取引方法を提供します.
しかし,使用者は,強いトレンドの市場における戦略の潜在的リスク,およびニュース面イベントへの感受性を注意すべきである. 戦略は,複数の時間周期の確認,動的にパラメータを調整し,リスク管理メカニズムを強化することによって,そのパフォーマンスの安定性と適応性をさらに最適化することができる.
全体として,取引量反転トレンドキャプチャ戦略は,市場行動と心理学の原理に基づく取引システムを提供し,特に波動的な市場と区間の揺れ動いている状況に適しています.合理的な設定と継続的な最適化により,この戦略は取引ポートフォリオの有効なツールになる可能性があります.
/*backtest
start: 2024-05-13 00:00:00
end: 2025-05-11 08:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/
//@version=5
// Strategy Title Reflects Latest Logic
strategy(title="Middle Finger Trading Strategy",
shorttitle="Middle_Finger",
overlay=true,
pyramiding=0, // Only one entry at a time
default_qty_type=strategy.percent_of_equity,
default_qty_value=1, // Trade 1% of equity
commission_value=0.04, // Example commission (adjust as needed)
commission_type=strategy.commission.percent,
initial_capital = 10000, // Example starting capital
process_orders_on_close=false // Important for limit orders to potentially fill intra-bar
)
// --- Inputs ---
// Volume Settings Group
grp_vol = "Volume Settings"
float rthHugeVolMultiplier = input.float(3.0, title="1. RTH Huge Vol. Multiplier (> Avg)", minval=1.1, step=0.1, group=grp_vol, tooltip="Multiplier for core RTH (9:45-15:44 ET)")
float ethHugeVolMultiplier = input.float(5.0, title="2. ETH/Excluded Huge Vol. Multiplier (> Avg)", minval=1.1, step=0.1, group=grp_vol, tooltip="Multiplier for ETH and first/last 15min RTH (default 5x)")
int volLookback = input.int(20, title="3. Volume SMA Lookback", minval=1, group=grp_vol, tooltip="Lookback for calculating the filtered average volume (Used ONLY for identifying the HUGE spike)")
// Removed normalVolMultiplier as it's no longer used for entry confirmation
// Trend Settings Group
grp_trend = "Trend Settings"
int trendLookback = input.int(20, title="1. Trend SMA Lookback", minval=2, group=grp_trend, tooltip="Lookback period for the Simple Moving Average used to determine the trend before the spike")
// Risk Management Group
grp_risk = "Risk Management (SL/TP)"
string nqTargetTickerId = input.string("CME:NQ1!", title="1. Target Ticker ID for Fixed NQ Points", group=grp_risk, tooltip="Specify the exact Ticker ID (e.g., CME:NQ1!, TVC:NDX) for fixed SL/TP. Found in Symbol Info.")
float nqFixedStopPoints = input.float(20.0, title="2. Fixed SL Points (for Target Ticker)", group=grp_risk, minval=0.1, step=0.1)
float nqFixedTpPoints = input.float(50.0, title="3. Fixed TP Points (for Target Ticker)", group=grp_risk, minval=0.1, step=0.1)
// General SL/TP Settings (used if NOT the target ticker)
bool useAtrStops = input.bool(true, title="4. Use ATR for SL/TP (Other Tickers)?", group=grp_risk)
int atrLookback = input.int(14, title="5. ATR Lookback", group=grp_risk, inline="atr_other")
float atrStopMultiplier = input.float(2.0, title="6. ATR SL Multiplier", group=grp_risk, inline="atr_other", minval=0.1, step=0.1)
float atrTpMultiplier = input.float(4.0, title="7. ATR TP Multiplier", group=grp_risk, inline="atr_other", minval=0.1, step=0.1)
float fixedStopPoints = input.float(100.0, title="6. Fixed SL Points (Other Tickers)", group=grp_risk, inline="fixed_other", minval=1)
float fixedTpPoints = input.float(200.0, title="7. Fixed TP Points (Other Tickers)", group=grp_risk, inline="fixed_other", minval=1)
// Time Filter Settings Group
grp_time = "Time Filter (ET)"
bool enableEntryFilterRthEdges = input.bool(true, title="1. Filter Entries First/Last 15 Min RTH (ET)?", group=grp_time, tooltip="If checked, ignores entries from 9:30-9:44 ET and 15:45-15:59 ET. Avg Vol calc *always* filters these times, 4-6PM ET, and Sun pre-6PM ET.")
string targetTimezone = "America/New_York" // Specify Eastern Time zone
// --- Time Calculation Function ---
isTimeInSession(t, tz, sessionString) =>
not na(time(timeframe.period, sessionString, tz))
// --- Time Context Functions ---
getTimeContext(t, tz) =>
h = hour(t, tz)
m = minute(t, tz)
d = dayofweek(t, tz)
// Core RTH: 9:45 AM to 15:44 PM ET (Mon-Fri)
bool isCoreRTH = d >= dayofweek.monday and d <= dayofweek.friday and
((h == 9 and m >= 45) or (h >= 10 and h <= 14) or (h == 15 and m <= 44))
// Excluded RTH Edges: 9:30-9:44 ET and 15:45-15:59 ET (Mon-Fri)
bool isExcludedRTH = d >= dayofweek.monday and d <= dayofweek.friday and
((h == 9 and m >= 30 and m <= 44) or (h == 15 and m >= 45))
// After Hours Closed: 4:00 PM to 5:59 PM ET (Mon-Fri)
bool isAfterHoursClosed = d >= dayofweek.monday and d <= dayofweek.friday and
(h >= 16 and h < 18)
// Sunday Pre-Market: Sunday before 6:00 PM ET
bool isSundayPreMarket = d == dayofweek.sunday and h < 18
// Combine ALL periods where activity should be ignored or volume excluded from avg
bool isExcludedPeriod = isExcludedRTH or isAfterHoursClosed or isSundayPreMarket
[isCoreRTH, isExcludedRTH, isAfterHoursClosed, isSundayPreMarket, isExcludedPeriod]
// --- Get Time Context for Current and Previous Bar ---
[isCurrentBarCoreRTH, isCurrentBarExcludedRTH, isCurrentBarAfterHoursClosed, isCurrentBarSundayPreMarket, isCurrentBarExcludedPeriod] = getTimeContext(time, targetTimezone)
[isPreviousBarCoreRTH, isPreviousBarExcludedRTH, isPreviousBarAfterHoursClosed, isPreviousBarSundayPreMarket, isPreviousBarExcludedPeriod] = getTimeContext(time[1], targetTimezone)
// --- Calculations ---
// Volume Averaging: Exclude RTH edges, 4-6 PM ET, and Sunday Pre-6 PM ET ALWAYS
// This average is *only* used to define the huge volume spike threshold
bool excludeCurrentVolFromAvg = isCurrentBarExcludedPeriod
float volumeForAvgCalc = excludeCurrentVolFromAvg ? na : volume
float avgVolume = ta.sma(volumeForAvgCalc, volLookback)
// Dynamic Huge Volume Multiplier: Based on *previous* bar's time (Core RTH or not)
float activeHugeVolMultiplier = isPreviousBarCoreRTH ? rthHugeVolMultiplier : ethHugeVolMultiplier
// Use avgVolume[1] as current avgVolume excludes current bar, and we compare previous volume to avg *before* it
float hugeVolThreshold = nz(avgVolume[1]) * activeHugeVolMultiplier
// --- MODIFIED Volume Conditions ---
// 1. Check if the *previous* bar had huge volume compared to its preceding average
bool isHugeVolumePrevBar = volume[1] > hugeVolThreshold and hugeVolThreshold > 0
// 2. Check if the *current* bar's volume is simply lower than the previous (huge) bar's volume
bool isVolumeLowerThanSpike = volume < volume[1]
// Trend Condition
float priceSma = ta.sma(close, trendLookback)
// Ensure trend condition uses close[2] vs sma[2] (trend state *before* the spike bar)
bool isBullishTrendBeforeSpike = close[2] > nz(priceSma[2])
bool isBearishTrendBeforeSpike = close[2] < nz(priceSma[2])
// --- Entry Time Filtering ---
// Always filter After Hours Closed and Sunday Pre-Market.
// Optionally filter RTH Edges based on input.
bool shouldFilterRthEdges = enableEntryFilterRthEdges and isCurrentBarExcludedRTH
bool isIgnoreEntryTime = shouldFilterRthEdges or isCurrentBarAfterHoursClosed or isCurrentBarSundayPreMarket
// --- MODIFIED Base Conditions ---
// Uses the simplified `isVolumeLowerThanSpike` check
bool baseLongCondition = isBearishTrendBeforeSpike and isHugeVolumePrevBar and isVolumeLowerThanSpike
bool baseShortCondition = isBullishTrendBeforeSpike and isHugeVolumePrevBar and isVolumeLowerThanSpike
// Final Conditions (Apply Time Filter)
bool finalLongCondition = baseLongCondition and not isIgnoreEntryTime
bool finalShortCondition = baseShortCondition and not isIgnoreEntryTime
// --- Stop Loss & Take Profit Calculation (Conditional Logic) ---
// This part remains the same
float atrValue = ta.atr(atrLookback)
float tickValue = syminfo.mintick
int stopLossTicks = 100 // Default fallback SL ticks
int takeProfitTicks = 200 // Default fallback TP ticks
// Check if the current symbol matches the target ticker ID
bool isTargetTicker = str.upper(syminfo.tickerid) == str.upper(nqTargetTickerId) // Case-insensitive comparison
if (isTargetTicker and tickValue > 0)
// --- Target Ticker Logic (e.g., NQ Fixed Points) ---
float ticksPerPoint = 1.0 / tickValue
stopLossTicks := math.max(1, math.round(nqFixedStopPoints * ticksPerPoint))
takeProfitTicks := math.max(1, math.round(nqFixedTpPoints * ticksPerPoint))
else if tickValue > 0 // Use only if tickValue is valid
// --- Standard Logic (Other Tickers: ATR or Fixed) ---
float stopLossDistance = useAtrStops ? atrValue * atrStopMultiplier : fixedStopPoints * tickValue
float takeProfitDistance = useAtrStops ? atrValue * atrTpMultiplier : fixedTpPoints * tickValue
// Calculate ticks, ensuring it's at least 1 tick
stopLossTicks := na(stopLossDistance) ? 100 : math.max(1, math.round(stopLossDistance / tickValue))
takeProfitTicks := na(takeProfitDistance) ? 200 : math.max(1, math.round(takeProfitDistance / tickValue))
// Final check to ensure SL/TP are not na
stopLossTicks := nz(stopLossTicks, 100)
takeProfitTicks := nz(takeProfitTicks, 200)
// --- Strategy Execution ---
// Uses Limit Orders based on previous bar's low/high - Remains the same
float limitEntryPriceLong = low[1] // Target entry at the low of the huge volume bar
float limitEntryPriceShort = high[1] // Target entry at the high of the huge volume bar
if (finalLongCondition and strategy.position_size == 0)
strategy.cancel("S") // Cancel any pending short limit order first
strategy.entry("L", strategy.long, limit = limitEntryPriceLong)
strategy.exit("L SL/TP", from_entry="L", loss=stopLossTicks, profit=takeProfitTicks)
if (finalShortCondition and strategy.position_size == 0)
strategy.cancel("L") // Cancel any pending long limit order first
strategy.entry("S", strategy.short, limit = limitEntryPriceShort)
strategy.exit("S SL/TP", from_entry="S", loss=stopLossTicks, profit=takeProfitTicks)
// --- Plotting & Visuals ---
plot(avgVolume, title="Filtered Avg Volume", color=color.new(color.blue, 60), style=plot.style_line)
// Removed the plot for the normal volume threshold as it's no longer used
// Highlight huge volume bar (previous bar that triggered the signal)
bgcolor(isHugeVolumePrevBar[1] ? color.new(color.yellow, 85) : na, title="Huge Volume Bar [-1]")
// Highlight bars excluded from volume average calculation
bgcolor(excludeCurrentVolFromAvg ? color.new(color.teal, 90) : na, title="Vol Excluded from Avg Calc")
// Highlight bars where entries are ignored due to time filters
bgcolor(isIgnoreEntryTime and (baseLongCondition or baseShortCondition) ? color.new(color.gray, 75) : na, title="Entry Time Filtered Bar")
// --- MODIFIED Highlight base conditions met ---
// Reflects the updated base conditions using isVolumeLowerThanSpike
bgcolor(baseLongCondition and not isIgnoreEntryTime ? color.new(color.green, 90) : na, title="Base Long Condition Met")
bgcolor(baseShortCondition and not isIgnoreEntryTime ? color.new(color.red, 90) : na, title="Base Short Condition Met")
plot(priceSma, title="Trend SMA", color=color.gray)
// Plot SL/TP levels for visualization - Remains the same
var float entryPrice = na
var float slLevel = na
var float tpLevel = na
if (strategy.opentrades > 0 and strategy.opentrades[1] == 0) // Just entered a trade
entryPrice := strategy.opentrades.entry_price(0)
if (strategy.position_size > 0) // Long
slLevel := entryPrice - stopLossTicks * tickValue
tpLevel := entryPrice + takeProfitTicks * tickValue
else // Short
slLevel := entryPrice + stopLossTicks * tickValue
tpLevel := entryPrice - takeProfitTicks * tickValue
else if (strategy.opentrades == 0 and strategy.opentrades[1] > 0) // Position closed
entryPrice := na
slLevel := na
tpLevel := na
else if (strategy.opentrades > 0) // Position still open
entryPrice := strategy.opentrades.entry_price(0)
if (strategy.position_size > 0) // Long
slLevel := entryPrice - stopLossTicks * tickValue
tpLevel := entryPrice + takeProfitTicks * tickValue
else // Short
slLevel := entryPrice + stopLossTicks * tickValue
tpLevel := entryPrice - takeProfitTicks * tickValue
plot(strategy.opentrades > 0 ? slLevel : na, title="Stop Loss Level", color=color.red, style=plot.style_linebr)
plot(strategy.opentrades > 0 ? tpLevel : na, title="Take Profit Level", color=color.green, style=plot.style_linebr)
// Optional Debugging Plots
// plotchar(isHugeVolumePrevBar, "HugeVol[1]", "H", location.bottom, color.yellow, size=size.tiny)
// plotchar(isVolumeLowerThanSpike, "VolLow", "v", location.bottom, color.purple, size=size.tiny) // Changed char
// plotchar(finalLongCondition, "FinalLong", "L", location.top, color.green, size=size.tiny)
// plotchar(finalShortCondition, "FinalShort", "S", location.top, color.red, size=size.tiny)
// plot(finalLongCondition ? limitEntryPriceLong : na, "Long Limit Target", color.lime, style=plot.style_circles, linewidth=2)
// plot(finalShortCondition ? limitEntryPriceShort : na, "Short Limit Target", color.fuchsia, style=plot.style_circles, linewidth=2)