Keltner-Kijun Short-Term Mean Reversion Averaging (No SL)

Viewing 1 post (of 1 total)
  • Author
    Posts
  • #257500 quote
    Nicolas
    Keymaster
    Master

    Hi everyone,


    I’ve been working on a grid/averaging strategy framework that I wanted to share with the community. The main value here isn’t necessarily the entry signals (which are intentionally simple and easily replaceable), but rather the **grid management functions** that can be reused in your own explorations.


    What does this strategy do?


    This is a mean reversion averaging strategy that:

    1. Enters against Keltner Band extremes filtered by the Kijun-Sen

    2. Adds to the position (pyramiding) as price moves against us

    3. Dynamically reduces the take-profit target** as the grid deepens

    4. Partially closes losing contracts** after a time delay to recover the average price faster


    The last two points are where the real value lies – these are functions you can adapt for any grid or martingale-style strategy.


    // Keltner-Kijun Short-Term Mean Reversion Averaging (No SL)
    // Nicolas
    // https://www.prorealcode.com
    // Strategy ID: 27
    // 2026-02-04 12:48:49
    // ====================================
    
    DEFPARAM CumulateOrders = True
    DEFPARAM PreLoadBars = 500
    
    //====================================================
    // Keltner + Kijun Mean Reversion Averaging (No Stop)
    // - Enters against band extremes (mean reversion)
    // - Uses KijunSen as a trend/mean filter
    // - Averages (adds) while signal persists
    // - Exits on dynamic profit target that reduces as grid deepens
    // - Repeatable time-based partial LOSS exits to recover avg price
    // - No stoploss (by design) -> control risk with MAXADDS
    //====================================================
    
    // ---- User parameters
    PROFITPOINTS   = 20        // Base take-profit in points (at grid level 0)
    MINPROFIT      = 3         // Minimum profit target floor (never go below this)
    MAXADDS        = 10        // Maximum number of add-on entries per trade (pyramiding)
    ENTRYQTY       = 1         // Initial position size
    ADDQTY         = 1         // Add-on size
    NEXTSTEPFACTOR = 1.1       // Factor to increase the grid step
    
    // ---- Repeatable partial LOSS exit parameters
    HEAVYGRIDLEVEL    = 5      // Grid level to activate loss-cutting
    MAXBARSINGRID     = 50     // Bars in loss before each partial cut
    CONTRACTSTOCUT    = 1      // Contracts to close per cut
    MAXLOSSCUTS       = 5      // Maximum total cuts allowed per trade
    MINLOSSPOINTS     = 5      // Minimum points in loss before allowing cut
    MINCONTRACTSKEEP  = 2      // Minimum contracts to keep (never cut below this)
    
    // ---- Optional behavior filters
    ALLOWLONG  = 1
    ALLOWSHORT = 1
    
    // ---- Indicators
    ku = KeltnerBandUp
    kd = KeltnerBandDown
    kj = KijunSen
    
    // ---- Internal state - Reset counters when flat
    IF not onmarket THEN
       addCountLong = 0
       addCountShort = 0
       nextStep = 1
       gridLevel = 0
       nextPROFITPOINTS = PROFITPOINTS
       barsInHeavyGrid = 0
       lossCutCount = 0
    ENDIF
    
    // ---- Signal definitions (mean reversion triggers)
    longSignal = (close < kd) AND (close < kj)
    shortSignal = (close > ku) AND (close > kj)
    
    //====================================================
    // Track time spent in heavy grid while in loss
    //====================================================
    IF onmarket AND gridLevel >= HEAVYGRIDLEVEL THEN
       // Only count bars when we're actually in loss
       IF (countofposition > 0 AND close < positionprice - MINLOSSPOINTS * pointsize) OR (countofposition < 0 AND close > positionprice + MINLOSSPOINTS * pointsize) THEN
          barsInHeavyGrid = barsInHeavyGrid + 1
       ELSE
          // Reset if price recovers near average
          barsInHeavyGrid = 0
       ENDIF
    ENDIF
    
    //====================================================
    // Entries / Averaging logic
    //====================================================
    
    // ---- LONG side (averaging down)
    IF ALLOWLONG = 1 THEN
       
       // Open initial long
       IF countofposition = 0 AND longSignal THEN
          BUY ENTRYQTY CONTRACT AT MARKET
          addCountLong = 0
          gridLevel = 0
          nextStep = 1
          nextPROFITPOINTS = PROFITPOINTS
          barsInHeavyGrid = 0
          lossCutCount = 0
       ENDIF
       
       // Add to existing long while signal persists, up to MAXADDS
       IF countofposition > 0 AND longSignal AND addCountLong < MAXADDS AND positionprice - close > nextStep * pointsize THEN
          BUY ADDQTY CONTRACT AT MARKET
          addCountLong = addCountLong + 1
          gridLevel = gridLevel + 1
          nextStep = nextStep * NEXTSTEPFACTOR
          // Hybrid approach: sqrt decay with floor
          nextPROFITPOINTS = max(MINPROFIT, round(PROFITPOINTS / sqrt(gridLevel + 1)))
       ENDIF
       
    ENDIF
    
    // ---- SHORT side (averaging up)
    IF ALLOWSHORT = 1 THEN
       
       // Open initial short
       IF countofposition = 0 AND shortSignal THEN
          SELLSHORT ENTRYQTY CONTRACT AT MARKET
          addCountShort = 0
          gridLevel = 0
          nextStep = 1
          nextPROFITPOINTS = PROFITPOINTS
          barsInHeavyGrid = 0
          lossCutCount = 0
       ENDIF
       
       // Add to existing short while signal persists, up to MAXADDS
       IF countofposition < 0 AND shortSignal AND addCountShort < MAXADDS AND close - positionprice > nextStep * pointsize THEN
          SELLSHORT ADDQTY CONTRACT AT MARKET
          addCountShort = addCountShort + 1
          gridLevel = gridLevel + 1
          nextStep = nextStep * NEXTSTEPFACTOR
          // Hybrid approach: sqrt decay with floor
          nextPROFITPOINTS = max(MINPROFIT, round(PROFITPOINTS / sqrt(gridLevel + 1)))
       ENDIF
       
    ENDIF
    
    //====================================================
    // Repeatable partial LOSS exit to recover average price
    // Each cut: closes contracts, resets timer, recalculates TP
    //====================================================
    
    // Calculate how many contracts we can cut (respect minimum keep)
    currentContracts = abs(countofposition)
    availableToCut = max(0, currentContracts - MINCONTRACTSKEEP)
    actualCut = min(CONTRACTSTOCUT, availableToCut)
    
    // ---- LONG: repeatable loss cuts
    IF countofposition > MINCONTRACTSKEEP AND gridLevel >= HEAVYGRIDLEVEL AND barsInHeavyGrid >= MAXBARSINGRID AND lossCutCount < MAXLOSSCUTS AND actualCut > 0 THEN
       // Verify we're still in significant loss
       IF close < positionprice - MINLOSSPOINTS * pointsize THEN
          // Close contracts at loss to improve remaining avg
          SELL actualCut CONTRACT AT MARKET
          lossCutCount = lossCutCount + 1
          
          // === RESET FOR NEXT CYCLE ===
          barsInHeavyGrid = 0
          gridLevel = max(0, gridLevel - actualCut)
          
          // Recalculate profit target for reduced position
          nextPROFITPOINTS = max(MINPROFIT, round(PROFITPOINTS / sqrt(gridLevel + 1)))
       ENDIF
    ENDIF
    
    // ---- SHORT: repeatable loss cuts
    IF countofposition < -MINCONTRACTSKEEP AND gridLevel >= HEAVYGRIDLEVEL AND barsInHeavyGrid >= MAXBARSINGRID AND lossCutCount < MAXLOSSCUTS AND actualCut > 0 THEN
       // Verify we're still in significant loss
       IF close > positionprice + MINLOSSPOINTS * pointsize THEN
          // Close contracts at loss to improve remaining avg
          EXITSHORT actualCut CONTRACT AT MARKET
          lossCutCount = lossCutCount + 1
          
          // === RESET FOR NEXT CYCLE ===
          barsInHeavyGrid = 0
          gridLevel = max(0, gridLevel - actualCut)
          
          // Recalculate profit target for reduced position
          nextPROFITPOINTS = max(MINPROFIT, round(PROFITPOINTS / sqrt(gridLevel + 1)))
       ENDIF
    ENDIF
    
    //====================================================
    // Exits: dynamic profit target from average entry
    //====================================================
    
    // For long positions
    IF countofposition > 0 THEN
       IF close >= positionprice + nextPROFITPOINTS * pointsize THEN
          SELL AT MARKET
       ENDIF
    ENDIF
    
    // For short positions
    IF countofposition < 0 THEN
       IF close <= positionprice - nextPROFITPOINTS * pointsize THEN
          EXITSHORT AT MARKET
       ENDIF
    ENDIF
    
    //====================================================
    // Monitoring graphs
    //====================================================
    GRAPH nextPROFITPOINTS COLOURED(0,150,255) AS "Profit Target (pts)"
    GRAPH gridLevel COLOURED(255,150,0) AS "Grid Level"
    GRAPH barsInHeavyGrid COLOURED(255,0,100) AS "Bars in Loss"
    GRAPH lossCutCount COLOURED(200,0,200) AS "Loss Cuts Done"
    


    Understanding the Key Functions


    1. Dynamic Take-Profit Reduction (Sqrt Decay)


    As the grid gets deeper, we reduce the profit target to exit faster. The formula uses square root decay:


    nextPROFITPOINTS = max(MINPROFIT, round(PROFITPOINTS / sqrt(gridLevel + 1)))


    This gives a nice progression:

    – Level 0: 20 points

    – Level 1: 14 points

    – Level 2: 12 points

    – Level 3: 10 points

    – Level 4: 9 points

    – …down to minimum of 3 points


    The sqrt function reduces the target quickly at first (when exposure grows fast) but slows down so you don’t end up with a ridiculously small target at deep grid levels.


    2. Partial Loss Exit to Recover Average Price


    This is the clever part. When you’re stuck in a heavy grid for too long, the strategy closes some contracts **at a loss** to bring the average entry price closer to current price.


    How it works for LONG positions:

    – Your positionprice is a weighted average of all buys

    – When you close contracts at current (lower) price, ProRealTime recalculates the average

    – The remaining position has a **lower** average entry price

    – Price now needs to travel less distance to hit your profit target


    **Example sequence:**


    | State | Contracts | Avg Price | Current Price | Distance to TP |

    |—————-|—————|————–|——————–|———————|

    | Before cut | 6 | 100 | 92 | 11 points |

    | After cut 1 | 5 | ~98.5 | 92 | 9.5 points |

    | After cut 2 | 4 | ~97 | 92 | 8 points |


    Yes, you realize a loss on those closed contracts, but the remaining position can now exit profitably with a smaller price recovery.



    3. Repeatable Cuts with Timer Reset


    Each time a partial cut happens:

    – barsInHeavyGrid resets to 0 (timer restarts)

    – gridLevel is reduced

    – nextPROFITPOINTS is recalculated for the new smaller position

    – The cycle can repeat up to MAXLOSSCUTS times


    This allows the strategy to progressively reduce exposure if price keeps stagnating.


    Settings Explained


    _Grid Parameters:

    PROFITPOINTS: Base TP at grid level 0 | Adapt to your instrument’s ATR |

    MINPROFIT: Floor for TP (never below this) | 2-5 points typically |

    MAXADDS: Maximum grid levels | Controls max exposure – be careful! |

    NEXTSTEPFACTOR: Grid spacing multiplier | 1.0 = fixed, >1.0 = expanding grid |


    _Partial Exit Parameters

    HEAVYGRIDLEVEL: Grid depth to activate cutting | 4-6 typically |

    MAXBARSINGRID: Bars to wait before each cut | 30-80 depending on timeframe |

    CONTRACTSTOCUT: Contracts per cut | 1-2 for gradual reduction |

    MAXLOSSCUTS: Total cuts allowed | Limits realized losses |

    MINLOSSPOINTS: Min loss before cutting allowed | Avoid cutting near breakeven |

    MINCONTRACTSKEEP: Never cut below this | Keep a core position |


    ** Tuning Profiles **


    **Aggressive recovery:**

    MAXBARSINGRID = 30

    CONTRACTSTOCUT = 2

    MAXLOSSCUTS = 8


    **Conservative approach:**

    MAXBARSINGRID = 80

    CONTRACTSTOCUT = 1

    MAXLOSSCUTS = 3

    MINLOSSPOINTS = 10


    Important Notes


    The Entries Are Dummy!


    I want to emphasize that the entry signals (Keltner + Kijun) are intentionally simple and serve as a placeholder. The real value of this code is in the grid management functions. Feel free to replace the entry logic with your own signals – the grid management, partial exit, and dynamic TP functions will work with any entry system.


    To replace entries, just modify the longSignal and shortSignal definitions:


    longSignal = (close < kd) AND (close < kj) // Replace this with your own logic
    shortSignal = (close > ku) AND (close > kj) // Replace this with your own logic
    


    _No Stop Loss by Design


    This strategy has no stop loss – risk is controlled through MAXADDS and the partial exit mechanism. This is intentional for mean reversion grids, but **be very careful with position sizing**. A grid strategy can accumulate significant exposure.


    _Reusable Functions


    The three core functions can be extracted and used in your own strategies:

    1. **Sqrt decay TP** – Reduces target as position grows

    2. **Time-based partial exit** – Cuts losers to improve average

    3. **Expanding grid steps** – NEXTSTEPFACTOR for non-linear grids


    I’d love to hear your thoughts and see how you adapt these functions for your own strategies. If you find improvements or interesting variations, please share them!


    IvƔn GonzƔlez and robertogozzi thanked this post
Viewing 1 post (of 1 total)
  • You must be logged in to reply to this topic.

šŸ“ā€ā˜ ļø The Dark Side: Grids & Martingales

New Reply
Author
author-avatar
Nicolas @nicolas Keymaster
Summary

This topic contains 1 voice and has 0 replies.

Topic Details
Forum: šŸ“ā€ā˜ ļø The Dark Side: Grids & Martingales Forum
Started: 02/04/2026
Status: Active
Attachments: 2 files
Logo Logo
Loading...