
La stratégie de capture de tendance de renversement de volume est une méthode de trading quantitative basée sur le volume de transactions anormales et le comportement des prix, qui vise à identifier les moments critiques où le marché peut subir un renversement de direction. Le cœur de la stratégie est de trouver des lignes K avec un volume de transactions significativement supérieur à la moyenne et de prendre des décisions de négociation opposées en fonction de la direction de la tendance précédente lorsque le volume de transactions est confirmé.
Le principe central de la stratégie est basé sur le phénomène de retournement de tendance après des volumes de transactions anormaux sur le marché. La logique d’opération est la suivante:
Identifier le volume anormal de transactionsLe système détecte si la ligne K précédente a un volume de transactions significativement supérieur à la moyenne. Dans les périodes de négociation régulière (RTH), le volume de transactions doit être supérieur à 3 fois le volume de transactions moyen récent (RTH) et peut être ajusté. Après la clôture ou dans les périodes spéciales (ETH), le volume de transactions doit être supérieur à 5 fois. Le calcul du volume de transactions moyen exclut automatiquement les périodes périphériques de la RTH, les heures 4-6 après la clôture et les heures avant la clôture du dimanche.
Confirmation de la baisse des ventes: Le volume de transactions sur la ligne K actuelle doit être inférieur à celui de la ligne K précédente, indiquant que les transactions de grande taille ont pris fin.
Définition de la tendance: Déterminer la direction de la tendance en comparant le prix de clôture avant la ligne K du volume exceptionnel et la relation entre le prix et le SMA (Simple Moving Average).
Signal d’entrée de jeu:
Exécution de l’entrée:
Gestion des risquesSelon les caractéristiques de chaque variété, le système offre deux modes de paramétrage stop/stop:
Filtre par tempsLa stratégie permet de filtrer de manière sélective les signaux de négociation des 15 premières et dernières minutes de la RTH et de filtrer en permanence les signaux de la période de clôture après la fermeture de la RTH (entre 16h et 18h) et avant la fermeture du dimanche.
Capture d’un tournant décisifLa stratégie se concentre sur la capture des points de retournement du marché associés à des volumes anormaux, qui représentent généralement des changements significatifs dans l’humeur du marché et offrent des opportunités de trading à plus haut taux de victoire.
Un point d’entrée précis: Augmentation de la précision d’entrée en utilisant des unités de prix limites pour entrer dans les points hauts/bas de la ligne K de volume anormal, assurant la transaction à des niveaux de prix techniquement importants.
La quantité d’adaptation peut être identifiéeStratégie d’ajustement dynamique du volume anormal de transactions en fonction des différentes périodes de négociation (temps de négociation régulier vs après-vente / périodes spéciales) afin de mieux répondre aux conditions réelles du marché.
Une gestion des risques souple: Offre des options de stop/stop basées sur un score fixe et ATR, avec des réglages personnalisables en fonction des caractéristiques et de la volatilité de différentes variétés.
Filtrage du temps: Identifier et filtrer automatiquement les périodes de faible liquidité et d’instabilité afin d’éviter les faux signaux susceptibles de se produire à proximité des ouvertures et des fermetures du marché.
Une réponse visuelle claire: La stratégie fournit des indications visuelles intuitives sur le graphique, y compris les lignes K de volume anormal, les lignes SMA de tendance et les équilibres de stop-loss, pour faciliter la surveillance et l’analyse des traders.
Automatisation de l’exécution: Une fois les conditions remplies, le système exécute automatiquement les ordres de placement à prix limité et les paramètres de stop-loss, réduisant l’intervention humaine et conservant la discipline de la transaction.
Risque de fausse percée: le volume anormal de transactions peut conduire à une brèche à court terme d’un niveau important, mais peut ensuite se retirer rapidement, ce qui provoque un faux signal. Pour atténuer ce risque, il est possible d’envisager d’ajouter des indicateurs de confirmation, tels que la confirmation de survente / survente RSI ou la durée de la rupture.
L’impact des événements liés aux nouvelles: Les données économiques majeures ou les annonces d’entreprises peuvent entraîner des volumes de transactions anormaux, mais ces réactions durent souvent plus longtemps que la reprise immédiate. Il est recommandé de suspendre la stratégie ou d’ajouter des conditions de filtrage avant et après la publication des données économiques majeures.
Risques liés à l’évolution du marché: dans un marché en forte tendance, les transactions en contre-courant peuvent faire face à des mouvements de prix négatifs persistants. Il est possible d’envisager d’ajouter des filtres de tendance à long terme pour éviter les opérations en contre-courant dans un environnement en forte tendance.
Risque de non-remboursement du prix limite: Si le prix n’atteint pas le niveau de prix limite fixé sur la ligne K suivante, le signal de transaction peut être invalidé. Il est possible d’envisager de définir une durée maximale d’exécution, ou de passer à une exécution au prix du marché dans certaines conditions.
Risques liés à la faible liquidité: Bien que la stratégie intègre une fonction de filtrage temporel, certaines variétés peuvent avoir des problèmes de liquidité insuffisante à certains moments. Il est recommandé d’ajuster les limites de temps de négociation en fonction des caractéristiques de la variété de négociation.
Risques liés à l’optimisation des paramètres: les paramètres de la stratégie sur-optimisés peuvent entraîner une mauvaise performance future sur-adaptée aux données historiques. Il faut s’assurer que les paramètres sont dans une plage raisonnable et vérifier la robustesse de la stratégie par des tests hors échantillon.
Confirmation de plusieurs périodes: Ajout d’un filtre de tendance pour des périodes de temps plus longues, assurant un taux de victoire plus élevé dans la direction d’une tendance plus grande. Par exemple, il est possible de vérifier la direction de la tendance de la ligne solaire et de ne participer que si elle est conforme à la tendance de la ligne solaire.
Évaluation de la qualité du volume des transactionsEn plus de la taille de la quantité pure, il est possible d’envisager d’augmenter l’évaluation de la qualité du volume de transactions, par exemple l’écart entre le prix moyen pondéré par le volume de transactions (VWAP), afin de mieux comprendre le comportement du marché derrière un grand volume de transactions.
Stratégie de stop loss dynamique: réalisation d’un stop-loss dynamique basé sur la volatilité, qui ajuste automatiquement la position de stop-loss au fur et à mesure que la transaction se déplace dans la direction favorable, bloquant une partie des bénéfices. Par exemple, un stop-loss de suivi peut être utilisé ou un stop-loss de déplacement au prix de revient après la rupture du niveau critique.
Filtrage de la corrélation entre les variétés: Pour les variétés concernées (comme les indices boursiers à terme et les devises, l’or et l’argent, etc.), l’ajout d’indicateurs de confirmation des variétés concernées améliore la qualité du signal. Le signal peut être plus fiable lorsque plusieurs variétés concernées présentent simultanément un volume de transactions et un comportement de prix anormaux.
Optimisation du machine learningPar exemple, un arbre de décision ou une forêt aléatoire peut être utilisé pour prédire les meilleures actions sous une caractéristique donnée de volume anormal.
Adaptation du taux de volatilitéAjuster le critère de détermination du volume anormal de transactions et le niveau d’arrêt/stop en fonction de l’état actuel de la volatilité du marché. Dans un environnement à forte volatilité, l’augmentation du volume anormal peut déterminer la valeur limite et réduire la distance d’arrêt; dans un environnement à faible volatilité, le contraire.
Ajout de filtres de base: Ajuster les paramètres de la stratégie ou suspendre la négociation de manière saisonnière à la date de publication des données économiques majeures ou des résultats financiers trimestriels, afin d’éviter les faux signaux causés par les perturbations de l’information.
La stratégie de capture des tendances de renversement de volume de transactions est un système de trading quantitatif qui se concentre sur le volume de transactions et le comportement des prix pour capturer les retournements potentiels en identifiant les changements de l’humeur du marché après un volume de transactions anormal. La stratégie définit techniquement clairement les conditions d’entrée et de sortie et les règles de gestion des risques, et comprend un mécanisme de filtrage temporel intelligent pour éviter les périodes de faible qualité du marché.
Le principal avantage de la stratégie réside dans sa capacité à capturer avec précision les “modèles de poignée” du marché, ce qui crée souvent des opportunités de reprise à court terme lorsque les acteurs du marché affluent en masse et se retirent ensuite. La stratégie offre une méthode de négociation disciplinée en fixant avec précision les seuils de prix à des niveaux de prix critiques et en s’accompagnant d’une gestion raisonnable des arrêts et des pertes.
Cependant, l’utilisateur doit être conscient des risques potentiels de la stratégie dans les marchés à forte tendance, ainsi que de sa sensibilité aux événements de l’horizon. La stratégie peut encore optimiser sa stabilité de performance et son adaptabilité en ajoutant des confirmations à plusieurs périodes de temps, en ajustant dynamiquement les paramètres et en renforçant les mécanismes de gestion des risques.
Dans l’ensemble, la stratégie de capture de tendances de renversement de volume offre aux traders un système de négociation basé sur le comportement du marché et les principes de la psychologie, particulièrement adapté aux marchés volatiles et aux situations de volatilité des zones. Avec une configuration rationnelle et une optimisation continue, la stratégie est susceptible de devenir un outil efficace dans le portefeuille de négociation.
/*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)