自适应均线交叉波动率跟踪量化交易策略

MA交叉 ATR EMA SMA WMA HMA VWMA 趋势过滤 波动率过滤 跟踪止损 双重止盈
创建日期: 2025-05-14 10:49:04 最后修改: 2025-05-14 10:49:04
复制: 0 点击次数: 317
avatar of ianzeng123 ianzeng123
2
关注
319
关注者

自适应均线交叉波动率跟踪量化交易策略 自适应均线交叉波动率跟踪量化交易策略

策略概述

自适应均线交叉波动率跟踪量化交易策略是一种专为高频交易和短线操作设计的系统化策略。该策略核心利用快速移动均线(MA)与慢速移动均线的交叉作为主要信号触发点,同时结合多种关键过滤器和精确的风险管理工具,以捕捉小幅度但快速的价格波动。策略的可配置性极高,用户可以灵活选择均线类型(EMA、SMA、WMA、HMA、VWMA)及其周期参数,以适应不同市场节奏的交易需求。此外,该策略具备API就绪性,可无缝整合至自动化交易系统中,实现信号的快速执行,特别适合追求高频小额利润的短线交易者。

策略原理

该策略的核心逻辑分为以下几个关键部分:

  1. 入场信号:主要通过快速均线与慢速均线的交叉/穿越作为入场触发条件。用户可以灵活配置均线类型(EMA、SMA、WMA、HMA、VWMA)和周期长度,以调整信号敏感度,适应不同市场状况。

  2. 趋势过滤:策略可选择性地使用长期移动均线作为大趋势过滤器,确保交易仅在大趋势方向上进行,避免在强烈方向性市场中进行逆势短线交易。

  3. 确认过滤器

    • ATR波动率过滤器:设计用于在极度平坦或”死寂”市场中暂停入场,这些市场的波动率低于动态阈值(基于平均ATR),有助于防止在无趋势、低能量条件下的震荡。
    • 成交量过滤器:通过要求最低市场参与度(成交量与其移动平均线的比较)来验证入场信号,避免基于低流动性尖峰或不重要价格行为的入场。
  4. 风险管理套件

    • 初始波动率止损:基于ATR的初始止损为每笔交易的风险定义提供客观起点,适应近期波动性。
    • ATR跟踪止损:对动态市场至关重要,跟踪止损线会根据有利价格走势调整,旨在保护成功短线交易的利润,同时在走势逆转时相对迅速地减少损失。
    • 盈亏平衡止损(可选):达到TP1或价格移动特定ATR距离后,可自动将止损移至入场价(加缓冲),用于快速中和已表现出初步成功的交易风险。
    • 双重获利水平:设置了TP1和TP2两个获利目标,TP1设计用于快速部分获利(如50%),TP2则为剩余仓位争取更大利润空间。
  5. 仓位管理:采用固定数量仓位大小,实现精确控制每笔交易的仓位规模,对高频环境中的一致风险应用和API命令生成至关重要。

策略优势

通过深入分析代码,该策略具有以下明显优势:

  1. 高度可配置性:用户可以灵活调整各种参数,包括均线类型和周期、过滤器设置以及风险管理参数,使策略能够适应各种市场环境和交易风格。

  2. 多层次过滤机制:结合趋势、波动率和成交量过滤器,有效减少错误信号和市场噪音,提高交易质量。

  3. 完善的风险管理:策略内置多重止损机制(初始、跟踪、盈亏平衡)和双重获利目标,实现精细的风险控制和利润保护。

  4. API友好设计:清晰明确的入场和出场逻辑生成无歧义信号,便于与外部交易系统集成,实现近乎即时的订单执行。

  5. 精确仓位控制:固定数量仓位大小简化了API端点的有效载荷,使自动化执行更加可靠。

  6. 适应性强:通过参数调整,策略可以从高频短线交易模式转变为更长期的趋势跟踪模式,适应不同市场条件和个人交易偏好。

策略风险

尽管该策略设计精良,但仍存在一些潜在风险和挑战:

  1. 参数优化风险:由于策略包含众多可配置参数,过度优化可能导致回测结果良好但实际表现差(过拟合),投资者应在样本外数据上验证或通过前向测试来规避此风险。

  2. 交易成本影响:高频交易意味着大量交易,累积的佣金和滑点可能显著影响净盈利能力,使用前务必在设置和回测中精确计算这些成本。

  3. 信号质量波动:不同市场条件下,均线交叉信号的可靠性可能发生变化,特别是在横盘震荡或高度波动的市场中。

  4. 技术依赖性:作为API就绪型策略,其有效性部分取决于执行速度和技术稳定性,系统延迟或故障可能导致机会丢失或执行偏差。

  5. 资金规模限制:固定数量的仓位规模可能不适合所有账户规模,小型账户可能面临过度风险,而大型账户则可能无法充分利用资金。

策略优化方向

基于策略设计和潜在风险,以下是几个可能的优化方向:

  1. 自适应参数:将关键参数(如ATR乘数和均线周期)设计为基于市场条件自动调整,提高策略在不同市场阶段的适应性。

  2. 智能过滤增强:整合额外的市场状态指标(如市场结构、波动模式识别或相关资产的相关性),进一步提高过滤器的准确性。

  3. 动态仓位管理:用基于账户规模、当前波动率和近期策略表现的动态仓位计算替代固定数量的仓位,实现更智能的资金管理。

  4. 多时间框架确认:在不同时间框架上验证信号,确保交易方向与更大的市场结构一致,减少不必要的交易。

  5. 机器学习整合:使用机器学习算法分析历史信号性能,预测未来信号的成功概率,优先执行高胜率交易。

  6. 交易会话管理:添加交易时间过滤器,避开低流动性或高波动性的时段,专注于市场效率最高的交易窗口。

  7. 相关性过滤:针对多资产交易,添加与相关市场的相关性分析,避免过度暴露于特定风险因素。

总结

自适应均线交叉波动率跟踪量化交易策略是一个功能全面的高频交易系统,通过均线交叉触发信号,结合多种关键过滤器和精确的风险管理工具,专为捕捉小幅度但快速的价格波动而设计。该策略的强大之处在于其高度可配置性和完善的风险管理框架,使交易者能够根据个人风险承受能力和市场条件精细调整交易参数。

对于高频交易者而言,该策略提供了清晰的入场和出场逻辑,以及与外部执行平台的无缝集成能力,这对于在瞬息万变的市场中快速执行决策至关重要。然而,使用此策略时,应特别注意交易成本累积和过度优化的风险,确保在实际交易中保持策略的健壮性和盈利能力。

最终,该策略代表了一种平衡的方法——利用技术指标和风险管理工具的力量,同时保持足够的灵活性以适应不断变化的市场条件。通过谨慎的参数调整和持续的监控改进,这一策略可以成为量化交易组合中有价值的组成部分。

策略源码
/*backtest
start: 2024-05-14 00:00:00
end: 2025-05-12 08:00:00
period: 2d
basePeriod: 2d
exchanges: [{"eid":"Futures_Binance","currency":"DOGE_USDT"}]
*/

//@version=5
// © ArrowTrade x:ArrowTrade

// --- STRATEGY DEFINITION ---
strategy(
     title="Arrow's Flexible MA Cross Strategy [API Ready]", // Added branding
     shorttitle="ArrowFlex",                  // Added branding
     overlay=true,
     initial_capital=1000, // Example capital, user should adjust
     commission_type=strategy.commission.percent,
     commission_value=0.036, // Example commission, user MUST adjust to their broker/exchange
     slippage=2,             // Example slippage (in ticks), user should adjust based on asset/broker
     process_orders_on_close=true, // Calculates/executes on bar close. Set to false for intrabar (use with caution & specific logic)
     pyramiding=0,           // No pyramiding allowed (one entry per direction)
     default_qty_type=strategy.fixed // Defaulting to fixed quantity
     // Removed default_qty_value from here
     )

// ================================================================================
//  Strategy Description (for TradingView Public Library & Users)
// ================================================================================
// © ArrowTrade
//
// A configurable Moving Average Crossover strategy designed for flexibility and
// API integration.
//
// Features:
// - MA Crossover Entries: Uses configurable Fast/Slow MA crossovers for signals.
// - Trend Filter: Optional longer-term MA filter to trade only with the trend.
// - Volatility Filter: Optional ATR filter to avoid low-volatility periods.
// - Volume Filter: Optional Volume filter to confirm entries with sufficient volume.
// - Stop Loss Options:
//     - Initial Volatility Stop (ATR-based)
//     - ATR Trailing Stop
//     - Break-Even Stop (activated by TP1 hit or ATR distance)
// - Take Profit Options:
//     - Two independent TP levels (percentage-based).
//     - Configurable partial close percentage at TP1.
// - Position Sizing: Fixed quantity per trade (adjustable).
//
// Intended Use:
// While configurable for various styles (scalping to trend-following by adjusting
// parameters), this strategy is built with API automation in mind. The clear
// entry and exit logic facilitates integration with external execution platforms
// via webhooks or other methods. Parameters can be tightened (shorter MAs,
// tighter stops/TPs, specific filters) for higher-frequency signals suitable
// for scalping.
//
// Disclaimer:
// Backtesting results are hypothetical and do not guarantee future performance.
// Market conditions change constantly. Always perform your own due diligence,
// forward testing, and rigorous risk management before trading live with any
// strategy. Ensure you adjust inputs like commission, slippage, and position
// size to accurately reflect your specific broker/exchange and risk profile.
// ================================================================================


// === INPUTS (Grouped and Ordered by Importance/Function) ===

// --- 1. Core Signal & Trend Filter ---
grp_signal = "1. Core Signal & Trend Filter"
signalSource   = input.source(high, title="Signal Source", group=grp_signal, tooltip="Price source for calculating the signal MAs (e.g., close, hl2, ohlc4). 'hlc3' or 'ohlc4' can provide smoother signals.")
signalMaType   = input.string("EMA", title="Signal MA Type", options=["EMA", "SMA", "WMA", "HMA", "VWMA"], group=grp_signal, tooltip="Type of Moving Average used for the fast/slow signal lines (EMA reacts faster, SMA smoother, HMA reduces lag).")
signalFastLen  = input.int(12, title="Fast MA Period", minval=2, maxval=100, step=1, group=grp_signal, tooltip="Period for the shorter-term signal MA. Shorter periods lead to more frequent signals (potentially more noise/scalping).")
signalSlowLen  = input.int(25, title="Slow MA Period", minval=3, maxval=200, step=1, group=grp_signal, tooltip="Period for the longer-term signal MA. Must be greater than Fast MA Period. Defines the crossover signal.")
useTrendFilter = input.bool(true, title="Enable Trend Filter", group=grp_signal, tooltip="If enabled, entry signals are only taken in the direction of the longer-term trend defined by the Trend MA.")
trendMaType    = input.string("EMA", title="Trend MA Type", options=["EMA", "SMA", "WMA", "HMA", "VWMA"], group=grp_signal, tooltip="Type of Moving Average used for the trend filter.")
trendMaLen     = input.int(100, title="Trend MA Period", minval=50, maxval=500, step=10, group=grp_signal, tooltip="Period for the Trend MA. Significantly longer than signal MAs typically. Higher values filter more aggressively.")
trendMaSource  = input.source(hl2, title="Trend MA Source", group=grp_signal, tooltip="Price source for the Trend MA calculation.")

// --- 2. Risk Management: Stop Loss ---
grp_stop = "2. Risk Management: Stop Loss"
useVolatilityStop    = input.bool(true, title="Enable Initial Volatility Stop", group=grp_stop, tooltip="Sets the initial stop loss based on Average True Range (ATR) at the time of entry.")
volStopAtrPeriod     = input.int(7, title="   Initial Stop ATR Period", minval=1, maxval=50, step=1, group=grp_stop, tooltip="ATR lookback period for calculating the initial stop distance.")
volStopAtrMultiplier = input.float(5, title="   Initial Stop ATR Multiplier", minval=0.5, maxval=10, step=0.1, group=grp_stop, tooltip="Multiplier for the ATR value to determine stop distance (Stop = Entry +/- ATR * Multiplier). Lower values = tighter initial stop.")
useTrailingStop      = input.bool(true, title="Enable ATR Trailing Stop", group=grp_stop, tooltip="If enabled, the stop loss will trail behind price based on current ATR, potentially locking in profits. Can override the initial/BE stop if it moves favorably.")
trailAtrPeriod       = input.int(15, title="   Trailing ATR Period", minval=1, maxval=50, step=1, group=grp_stop, tooltip="ATR lookback period for calculating the trailing distance.")
trailAtrMultiplier   = input.float(4.0, title="   Trailing ATR Multiplier", minval=0.5, maxval=10, step=0.1, group=grp_stop, tooltip="Multiplier for the current ATR to determine trailing distance. Lower values trail tighter.")
useBreakEvenStop     = input.bool(false, title="Enable Break-Even Stop", group=grp_stop, tooltip="If enabled, moves the stop loss to entry price (plus a small profit buffer) once a certain condition is met.")
beActivationChoice   = input.string("TP1 Reached", title="   BE Activation Condition", options=["TP1 Reached", "ATR Distance Moved"], group=grp_stop, tooltip="When should the Break-Even Stop activate? When TP1 is hit, or when price moves a certain ATR distance from entry?")
beActivationAtrMult  = input.float(1.5, title="   BE Activation ATR Multiplier", minval=0.1, maxval=5, step=0.1, group=grp_stop, tooltip="Used only if 'ATR Distance Moved' is selected. BE activates if price moves (Entry +/- ATR * Multiplier). Uses 'Initial Stop ATR Period'.")
beProfitTicks        = input.int(2, title="   BE Profit Buffer (Ticks)", minval=0, maxval=50, step=1, group=grp_stop, tooltip="Moves the stop to Entry Price +/- this many ticks (e.g., to cover commissions). Set to 0 for exact entry price.")

// --- 3. Risk Management: Take Profit ---
grp_tp = "3. Risk Management: Take Profit (TP)"
useTp1        = input.bool(true, title="Enable TP1", group=grp_tp, tooltip="Enable the first Take Profit level.")
tp1Pct        = input.float(1.5, title="   TP1 Target (%)", minval=0.1, maxval=20, step=0.1, group=grp_tp, tooltip="First TP target as a percentage distance from the entry price. Should be less than TP2 %.")
tp1QtyPercent = input.int(50, title="   TP1 Close Quantity (%)", minval=1, maxval=100, step=5, group=grp_tp, tooltip="Percentage of the original position size to close when TP1 is hit.")
useTp2        = input.bool(true, title="Enable TP2", group=grp_tp, tooltip="Enable the second (final) Take Profit level.")
tp2Pct        = input.float(3.0, title="   TP2 Target (%)", minval=0.2, maxval=30, step=0.1, group=grp_tp, tooltip="Second TP target as a percentage distance from the entry price. Closes the remaining position.")

// --- 4. Additional Filters ---
grp_filters = "4. Additional Filters"
useAtrFilter        = input.bool(true, title="Enable ATR Volatility Filter", group=grp_filters, tooltip="If enabled, avoids entries during periods of very low volatility (ATR below a moving average of ATR). Helps filter choppy/sideways markets.")
atrFilterPeriod     = input.int(14, title="   ATR Filter Period", minval=1, maxval=50, step=1, group=grp_filters, tooltip="Lookback period for calculating the current ATR and its average for the filter.")
atrFilterMultiplier = input.float(0.5, title="   ATR Filter Threshold Multiplier", minval=0.1, maxval=5, step=0.1, group=grp_filters, tooltip="Entry requires current ATR to be >= (Average ATR * Multiplier). Lower values filter more aggressively.")
useVolumeFilter     = input.bool(true, title="Enable Volume Filter", group=grp_filters, tooltip="If enabled, requires the volume of the entry bar to be above a moving average of volume. Acts as confirmation.")
volumeLookback      = input.int(30, title="   Volume MA Period", minval=2, maxval=100, step=1, group=grp_filters, tooltip="Lookback period for calculating the average volume.")
volumeMultiplier    = input.float(1.0, title="   Min Volume Ratio (vs Avg)", minval=0.1, maxval=5, step=0.1, group=grp_filters, tooltip="Entry requires current volume to be >= (Average Volume * Multiplier). Values >= 1 require above-average volume.")

// --- 5. Position Sizing ---
grp_size = "5. Position Sizing"
// Define the quantity input with its own default value
qtyValue = input.float(0.01, title="Position Size (Fixed Qty)", minval=0.0001, step=0.0001, group=grp_size, tooltip="Fixed quantity (contracts/shares/lots) per trade. Adjust based on your account size, risk tolerance, and the asset being traded. Can be overridden by API.")


// === FUNCTIONS ===
f_ma(maType, src, len) =>
    float result = na
    if maType == "SMA"
        result := ta.sma(src, len)
    else if maType == "EMA"
        result := ta.ema(src, len)
    else if maType == "WMA"
        result := ta.wma(src, len)
    else if maType == "HMA"
        result := ta.hma(src, len)
    else if maType == "VWMA"
        result := ta.vwma(src, len)
    result

// === CORE CALCULATIONS ===

// Parameter Sanity Check
if signalSlowLen <= signalFastLen and barstate.islast
    runtime.error("Signal Slow MA Period must be greater than Fast MA Period!")

// 1. Moving Averages
float fastMA = f_ma(signalMaType, signalSource, signalFastLen)
float slowMA = f_ma(signalMaType, signalSource, signalSlowLen)
float trendMA = useTrendFilter ? f_ma(trendMaType, trendMaSource, trendMaLen) : na

// 2. ATR Values
float atrValueStop = ta.atr(volStopAtrPeriod)
float atrValueTrail = ta.atr(trailAtrPeriod)
float atrValueFilter = ta.atr(atrFilterPeriod)
float atrValueBE = ta.atr(volStopAtrPeriod)

// 3. Filter Conditions
bool trendFilterOK_L = not useTrendFilter or (not na(trendMA) and signalSource > trendMA)
bool trendFilterOK_S = not useTrendFilter or (not na(trendMA) and signalSource < trendMA)
float avgAtrFilter = ta.sma(atrValueFilter, atrFilterPeriod)
bool volatilityFilterOK = not useAtrFilter or (not na(atrValueFilter) and not na(avgAtrFilter) and atrValueFilter >= avgAtrFilter * atrFilterMultiplier)
float avgVolume = ta.sma(volume, volumeLookback)
bool volumeFilterOK = not useVolumeFilter or (not na(volume) and not na(avgVolume) and volume >= avgVolume * volumeMultiplier)
bool finalFilterOK_L = trendFilterOK_L and volatilityFilterOK and volumeFilterOK
bool finalFilterOK_S = trendFilterOK_S and volatilityFilterOK and volumeFilterOK

// 4. Entry Signals
bool longCross = not na(fastMA) and not na(slowMA) and ta.crossover(fastMA, slowMA)
bool shortCross = not na(fastMA) and not na(slowMA) and ta.crossunder(fastMA, slowMA)
bool longEntrySignal = longCross and finalFilterOK_L
bool shortEntrySignal = shortCross and finalFilterOK_S

// === STRATEGY EXECUTION LOGIC ===

// --- State Variables (persisted between bars) ---
var float entryPriceVar = na
var float initialStopPrice = na
var float currentStopPrice = na
var float trailStopLevel = na
var bool isBEActive = false
var bool tp1Reached = false
var float qtyToCloseTp1_Var = na

// --- Position Status ---
bool inLong = strategy.position_size > 0
bool inShort = strategy.position_size < 0
bool inTrade = strategy.position_size != 0

// --- Reset State Variables on Trade Exit ---
if not inTrade and inTrade[1]
    entryPriceVar := na
    initialStopPrice := na
    currentStopPrice := na
    trailStopLevel := na
    isBEActive := false
    tp1Reached := false
    qtyToCloseTp1_Var := na

// --- Handle New Entries ---
if longEntrySignal and not inTrade
    strategy.entry("Long Entry", strategy.long, qty=qtyValue) // Use qtyValue from input

if shortEntrySignal and not inTrade
    strategy.entry("Short Entry", strategy.short, qty=qtyValue) // Use qtyValue from input


// --- Manage Stops and Take Profits for Open Positions ---
if inTrade
    // Initialize state on the bar immediately AFTER entry
    if na(entryPriceVar)
        entryPriceVar := strategy.position_avg_price
        float positionQty = strategy.position_size

        if not na(positionQty) and tp1QtyPercent > 0 and useTp1
            qtyToCloseTp1_Var := math.abs(positionQty * tp1QtyPercent / 100)
        else
            qtyToCloseTp1_Var := 0.0

        if useVolatilityStop and not na(atrValueStop)
            initialStopPrice := entryPriceVar + (inLong ? -1 : 1) * atrValueStop * volStopAtrMultiplier
            currentStopPrice := initialStopPrice
        else
            initialStopPrice := na
            currentStopPrice := na

        if useTrailingStop and not na(atrValueTrail)
            trailStopLevel := entryPriceVar + (inLong ? -1 : 1) * atrValueTrail * trailAtrMultiplier
        else
            trailStopLevel := na

        isBEActive := false
        tp1Reached := false

    // --- Calculations within the trade (if entry price is set) ---
    if not na(entryPriceVar)

        // 1. Calculate TP Levels for the current bar
        float tp1LevelL = na, float tp2LevelL = na, float tp1LevelS = na, float tp2LevelS = na
        if useTp1
            tp1LevelL := entryPriceVar * (1 + tp1Pct / 100)
            tp1LevelS := entryPriceVar * (1 - tp1Pct / 100)
        if useTp2
            tp2LevelL := entryPriceVar * (1 + tp2Pct / 100)
            tp2LevelS := entryPriceVar * (1 - tp2Pct / 100)

        // 2. Check and Activate Break-Even Stop
        if useBreakEvenStop and not isBEActive and not na(currentStopPrice)
            float beTriggerL = na, float beTriggerS = na
            if beActivationChoice == "TP1 Reached" and useTp1
                if not na(tp1LevelL)
                    beTriggerL := tp1LevelL
                if not na(tp1LevelS)
                    beTriggerS := tp1LevelS
            else if beActivationChoice == "ATR Distance Moved" and not na(atrValueBE)
                beTriggerL := entryPriceVar + atrValueBE * beActivationAtrMult
                beTriggerS := entryPriceVar - atrValueBE * beActivationAtrMult

            float beTargetLevel = entryPriceVar + (inLong ? 1 : -1) * beProfitTicks * syminfo.mintick

            if not na(beTriggerL) and not na(beTargetLevel) and inLong and high >= beTriggerL and beTargetLevel > currentStopPrice
                currentStopPrice := beTargetLevel
                isBEActive := true
            if not na(beTriggerS) and not na(beTargetLevel) and inShort and low <= beTriggerS and beTargetLevel < currentStopPrice
                currentStopPrice := beTargetLevel
                isBEActive := true

        // 3. Update Trailing Stop
        if useTrailingStop and not na(currentStopPrice) and not na(atrValueTrail)
            float newTrailStopL = low - atrValueTrail * trailAtrMultiplier
            float newTrailStopS = high + atrValueTrail * trailAtrMultiplier
            float prevTrail = trailStopLevel[1]
            float calculatedNewTrail = na

            if inLong
                calculatedNewTrail := na(prevTrail) ? newTrailStopL : math.max(prevTrail, newTrailStopL)
                if not na(calculatedNewTrail)
                    trailStopLevel := calculatedNewTrail
                if not na(trailStopLevel) and trailStopLevel > currentStopPrice
                    currentStopPrice := trailStopLevel
            if inShort
                calculatedNewTrail := na(prevTrail) ? newTrailStopS : math.min(prevTrail, newTrailStopS)
                if not na(calculatedNewTrail)
                    trailStopLevel := calculatedNewTrail
                if not na(trailStopLevel) and trailStopLevel < currentStopPrice
                    currentStopPrice := trailStopLevel

        // --- Execute Exits ---

        // 4. Apply Stop Loss Exit
        if not na(currentStopPrice)
            bool isTrailingActiveNow = useTrailingStop and not na(trailStopLevel) and currentStopPrice == trailStopLevel
            string stop_comment = isBEActive ? "BE Stop" : (isTrailingActiveNow ? "Trail Stop" : "Vol Stop")
            if inLong
                strategy.exit("SL Exit L", from_entry="Long Entry", stop=currentStopPrice, comment=stop_comment + " L")
            if inShort
                strategy.exit("SL Exit S", from_entry="Short Entry", stop=currentStopPrice, comment=stop_comment + " S")

        // 5. Apply Take Profit Exits
        // TP1 Exit (Partial Quantity)
        if useTp1 and not tp1Reached and not na(qtyToCloseTp1_Var) and qtyToCloseTp1_Var > 0
            if inLong and not na(tp1LevelL)
                strategy.exit("TP1 Exit L", from_entry="Long Entry", qty=qtyToCloseTp1_Var, limit=tp1LevelL, comment="TP1 Hit L")
                if high >= tp1LevelL
                    tp1Reached := true
            if inShort and not na(tp1LevelS)
                strategy.exit("TP1 Exit S", from_entry="Short Entry", qty=qtyToCloseTp1_Var, limit=tp1LevelS, comment="TP1 Hit S")
                if low <= tp1LevelS
                    tp1Reached := true

        // TP2 Exit (Remaining Quantity)
        if useTp2
            if inLong and not na(tp2LevelL)
                strategy.exit("TP2 Exit L", from_entry="Long Entry", limit=tp2LevelL, comment="TP2 Hit L")
            if inShort and not na(tp2LevelS)
                strategy.exit("TP2 Exit S", from_entry="Short Entry", limit=tp2LevelS, comment="TP2 Hit S")


// === PLOTTING ===

// 1. Moving Averages
plot(fastMA, "Fast MA", color=color.new(color.aqua, 0), linewidth=1)
plot(slowMA, "Slow MA", color=color.new(color.fuchsia, 0), linewidth=1)
plot(useTrendFilter and not na(trendMA) ? trendMA : na, "Trend MA", color=color.new(color.gray, 0), linewidth=2, style=plot.style_cross)

// 2. Active Stop Loss Level
color stopColor = color.new(color.red, 0)
bool isTrailingActivePlot = useTrailingStop and not na(trailStopLevel) and not na(currentStopPrice) and currentStopPrice == trailStopLevel
if isBEActive
    stopColor := color.new(color.orange, 0)
else if isTrailingActivePlot
    stopColor := color.new(color.blue, 0)
plot(inTrade and not na(currentStopPrice) ? currentStopPrice : na, "Active Stop Loss", stopColor, style=plot.style_linebr, linewidth=2)

// 3. Take Profit Levels
float plot_tp1LevelL = na, float plot_tp1LevelS = na, float plot_tp2LevelL = na, float plot_tp2LevelS = na
if not na(entryPriceVar)
    if useTp1
        plot_tp1LevelL := entryPriceVar * (1 + tp1Pct / 100)
        plot_tp1LevelS := entryPriceVar * (1 - tp1Pct / 100)
    if useTp2
        plot_tp2LevelL := entryPriceVar * (1 + tp2Pct / 100)
        plot_tp2LevelS := entryPriceVar * (1 - tp2Pct / 100)
plot(inTrade and useTp1 and not na(inLong ? plot_tp1LevelL : plot_tp1LevelS) ? (inLong ? plot_tp1LevelL : plot_tp1LevelS) : na, "TP1 Level", color=color.new(color.green, 30), style=plot.style_linebr, linewidth=1)
plot(inTrade and useTp2 and not na(inLong ? plot_tp2LevelL : plot_tp2LevelS) ? (inLong ? plot_tp2LevelL : plot_tp2LevelS) : na, "TP2 Level", color=color.new(color.green, 0), style=plot.style_linebr, linewidth=1)

// 4. Entry Signal Markers
plotshape(longEntrySignal, title="Long Entry Signal", location=location.belowbar, color=color.new(color.green, 0), style=shape.triangleup, size=size.small)
plotshape(shortEntrySignal, title="Short Entry Signal", location=location.abovebar, color=color.new(color.red, 0), style=shape.triangledown, size=size.small)

// 5. Background Color Filters
bgcolor(useTrendFilter and not na(trendMA) and inTrade ? (inLong ? color.new(color.blue, 90) : color.new(color.red, 90)) : na, title="Trend Filter Active")
bgcolor(useAtrFilter and not volatilityFilterOK ? color.new(color.gray, 85) : na, title="Low Volatility Filter Active")
bgcolor(useVolumeFilter and not volumeFilterOK ? color.new(color.yellow, 90) : na, title="Low Volume Filter Active")
相关推荐