Indicator with timeframe parameter issue

Created at 14 Nov 2024, 13:50
How’s your experience with the cTrader Platform?
Your feedback is crucial to cTrader's development. Please take a few seconds to share your opinion and help us improve your trading experience. Thanks!
NO

Nobody

Joined 14.01.2016

Indicator with timeframe parameter issue
14 Nov 2024, 13:50


Hello,

after searching a solution for my issue without success i call for your help please…when i run the indicator with  the timeframe parameter (IndicTimeFrame) different than the chart timeframe, i have alwayse the same value returned (ie 2) and it seems the BollingerPhasys class does not work but i do not understand why…please could you tell me what am i doing wrong please ?

 

using cAlgo.API;
using cAlgo.API.Indicators;
using System.Linq;

using ClassLibraryCbotTools;

namespace cAlgo.Indicators
{
    [Indicator(IsOverlay = false, AccessRights = AccessRights.None)]
    public class BollingerPhasysIndicator : Indicator
    {

        [Parameter("Indic Timeframe", DefaultValue = "Hour", Group = "Moving Averages")]
        public TimeFrame IndicTimeframe { get; set; }

        [Output("Phasys", LineColor = "Green", Thickness = 2, PlotType = PlotType.Points)]
        public IndicatorDataSeries Phase { get; set; }

        private Bars _ms;
        private BollingerBands _boll;
        private BollingerPhasys _bollPh;

        
        protected override void Initialize()
        {
            //Chart timeframe MarketData
            _ms = MarketData.GetBars(IndicTimeframe);
            //Bollinger indicator
            _boll = Indicators.BollingerBands(_ms.ClosePrices, 20, 2, MovingAverageType.Simple);
            //BollingerPhasys Class
            _bollPh = new BollingerPhasys(ref _ms, ref _boll);
        }

        public override void Calculate(int index)
        {
            //Set the phase level on the last closed candle (just before the current one)
            Phase[index-1] =  _bollPh.EvalPhase();
        }
    }
    
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public enum Trend
    {
        Bullish,
        Bearish,
        Unknown,
        Weak,
        Regular,
        Strong,
        ExtraStrong
    }
    
    
            
    
    
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public class BollingerPhasys
    {
      private Bars _ms;
      private BollingerBands _bollbands;
    
    
      //Bollinger Cycle properties
      public int cycle_phase;
      public Trend cycle_trend;
      public int cycle_duration_periods;
      
      //Phase 2 Properties : 
      //ph2_Tx_last_idx = 1st candle bollinger starts Phasys 2 trend
      public int ph2_Tx_last_idx;
      //ph2_T0_last_idx = 1st Candle making bollingers diverging and officialy enter in a phase 2
      public int ph2_T0_last_idx;
      //ph2_T1_last_idx = 1st candle in the phasys 2 closing outside the bollinger bands
      public int ph2_T1_last_idx;
      public int ph2_duration_periods;
      public int ph2_power;
      public int ph2_period_min = 5;
      
      //Phase 2 Properties :
      //ph3_T3_last_idx = 1st Candle of Phase 3
      public int ph3_T3_last_idx;
      public int ph3_duration_periods;
      
      //Phase 2 Properties :
      //ph4_T4_last_idx = 1st Candle of Phase 4
      public int ph4_duration_periods = -1;
      public int ph4_T4_last_idx = -1;
    
    
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      //
      //                                                      CONSTRUCTOR
      //
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      public BollingerPhasys(ref Bars ms,  ref BollingerBands bollbands)
      {
        this._ms = ms;
        this._bollbands = bollbands;
        this.Reset();
      }
      
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      //
      //                                                      Reset Method
      //
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      public void Reset()
      {
        this.cycle_phase = -1;
        this.cycle_duration_periods = 0;
        
        this.cycle_trend = Trend.Unknown;
        
        this.ph2_Tx_last_idx = -1;
        this.ph2_T0_last_idx = -1;
        this.ph2_T1_last_idx = -1;
        this.ph2_duration_periods = 0;
        this.ph2_power = -1;
            
        this.ph3_duration_periods = 0;
        this.ph3_T3_last_idx = -1;
        
        this.ph4_duration_periods = 0;
        this.ph4_T4_last_idx = -1;
      }
    
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      //
      //                                                     EvalPhase  Method
      //Returned value  :
      // Phasys unknown or error : -1
      // Phasys 1 : 1
      // Phasys 2 : 2
      // Phasys 3 : 3
      // Phasys 4 : 4
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      public int EvalPhase(int last_index = 1)
      {
        int phase;
        
        //Return error if last_index is greater than marketseries size
        if(_ms.Count <= last_index)
            return -1;
    
        //Initialize phasys with current pulse
        phase = GetPhasysPulseByLastIdx();

        //Phasys 2 pulse : set Phasys 2 properties
        if(phase == 2)
        {
            //Set the Trend (the most recent price the better)
            this.cycle_trend = (_ms.ClosePrices.Last(0) > _bollbands.Main.Last(0)) ? Trend.Bullish : Trend.Bearish;
            
            //Find & Set the Tx (last index)
            this.ph2_Tx_last_idx = GetTxLastIdx(this.cycle_trend, last_index);
            //Find & Set the T0 (last index)
            this.ph2_T0_last_idx = GetT0FromTxLastIdx( this.ph2_Tx_last_idx);
            //Find & Set the T1 (last index)
            this.ph2_T1_last_idx = GetT1FromTxLastIdx(this.cycle_trend, this.ph2_Tx_last_idx, last_index);
            //Calculate the T2 duration (in periods)
            this.ph2_duration_periods = ph2_T0_last_idx - last_index;
        }        
        //Phasys 3 pulse
        else if(phase == 3)
        {
            //Bullish Trend (for avoiding wrong signal check one periode more earlyer)
            if(_bollbands.Main.Last(last_index+2) < _bollbands.Main.Last(last_index+1))
            {
                // Check if it is a real Phasys 3 (it should only be preceded by a Phase 2)
                if(IsPhase3Bullish(last_index)){
                    SetPropertiesFromPhase3(Trend.Bullish);
                }
                else
                    phase = 0;
            }
            else{
                //Faire Bearish trend...mais pour l'instant on renvoie 0
                // Check if it is a real Phasys 3 (it should only be preceded by a Phase 2)
                if(IsPhase3Bearish(last_index)){
                    SetPropertiesFromPhase3(Trend.Bearish);
                }
                else
                    phase = 0;
            }
        }
        //Phasys 4 pulse
        else if(phase == 4)
        {
            //Bullish Trend (for avoiding wrong signal check one periode more earlyer
            if(_bollbands.Main.Last(last_index+2) < _bollbands.Main.Last(last_index+1))
            {
                // Check if it is a real Phasys 4 (it should only be preceded by a Phase 3 itself preceded by a phase 2)
                if(IsPhase4Bullish(last_index)){
                    SetPropertiesFromPhase4(Trend.Bullish);
                }
                else
                    phase = 0;
            }
            else
            {
                // Check if it is a real Phasys 4 (it should only be preceded by a Phase 3 itself preceded by a phase 2)
                if(IsPhase4Bearish(last_index)){
                    SetPropertiesFromPhase4(Trend.Bearish);
                }
                else
                    phase = 0;
            }
        }
                
        //Exit
        return phase;
      }
      
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      //
      //                                                     GetPhasysPulseByLastIdx  Method
      // Parameters  : 
      //    * "last_index" : value of the "last index" in market series for which to identify the current phase (default 1 = the last complete period)
      // Returned value :
      //    * Value of phasys (2, 3, 4) at a given last index if a clear pulse is detected
      //    * 1 if no clear pulse detected 
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      public int GetPhasysPulseByLastIdx(int last_index = 1)
      {
        //Bollinger divergence = Phase 2
        if (_bollbands.Top.Last(last_index + 1) < _bollbands.Top.Last(last_index) && _bollbands.Bottom.Last(last_index + 1) > _bollbands.Bottom.Last(last_index))
            return 2;
        //Bollinger both rising = Phase 3
        else if  (_bollbands.Top.Last(last_index + 1) < _bollbands.Top.Last(last_index) && _bollbands.Bottom.Last(last_index + 1) < _bollbands.Bottom.Last(last_index))
            return 3;
        //Bollinger both decreasing = Phase 3
        else if  (_bollbands.Top.Last(last_index + 1) > _bollbands.Top.Last(last_index) && _bollbands.Bottom.Last(last_index + 1) > _bollbands.Bottom.Last(last_index))
            return 3;
        //Bollinger converging = Phase 4
        else if  (_bollbands.Top.Last(last_index + 1) > _bollbands.Top.Last(last_index) && _bollbands.Bottom.Last(last_index + 1) < _bollbands.Bottom.Last(last_index))
            return 4;
        //One (or both) Bollinger band is flat => no clear pulse detected => return -1
        else
            return -1;
      }
      
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      //
      //                                                     GetTxByLastIdx  Method
      // Returned value : last index of the first reverse Trending Bollinger
      //                   -1 if no reverse found
      //                  
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      public int GetTxLastIdx(Trend trend_type, int start_last_index = 1)
      {
        //Go Back up the Top Bollinger until its reversed for setting Tx & T0
        for (int i = start_last_index; i <= _ms.ClosePrices.Count - 1; i++)
        {
          //Check if Reversed identified
          if ((trend_type == Trend.Bullish && _bollbands.Top.Last(i + 1) > _bollbands.Top.Last(i)) || (trend_type == Trend.Bearish && _bollbands.Bottom.Last(i + 1) < _bollbands.Bottom.Last(i)))
            //Return last index of the reverse candle
            return  i-1;
        }
        //No case found
        return -1;
      }
      
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      //
      //                                                     GetT0FromTxByLastIdx  Method
      // Returned value : last index of the first bollinger divergence 
      //                   -1 if no reverse found
      //                  
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      public int GetT0FromTxLastIdx(int Tx_last_index = 1)
      {
        //Starting from Tx Index (should be passed by input parameter), try to find 1st Bollinger divergence
        for (int i = Tx_last_index; i >= 1; i--)
        {
          //Check if Divergence identified
          if (GetPhasysPulseByLastIdx(i) == 2)
            //Return T0 Last Idx value
            return i;
        }
        //No case found
        return -1;
      }
      
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      //
      //                                                     GetT1BullishFromTxByLastIdx  Method
      // Returned value : last index of the first candle to close outside top bollinger 
      //                   -1 if not found
      //                  
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      public int GetT1FromTxLastIdx(Trend trend_type, int start_last_index = 1, int stop_last_index = 1)
      {
        //Starting from Tx Index (should be passed by input parameter), try to find 1st Bollinger divergence
        for (int i = start_last_index; i >= stop_last_index; i--)
        {
          //Check if close price is above top bollinger
          if ((trend_type == Trend.Bullish && _ms.ClosePrices.Last(i) > _bollbands.Top.Last(i)) || (trend_type == Trend.Bearish && _ms.ClosePrices.Last(i) < _bollbands.Bottom.Last(i)) )
            //Return T0 Last Idx value
            return i;
        }
        //No case found
        return -1;
      }
      
    
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      //
      //                                                     IsPhase2  Method
      // Returned value :
      //  2 if phasys 2 identified
      //  number of other phase if identified too (wrong phasys 2)
      //  -1 if no phasys detected
      // Side effect : update class properties for Phasys 2
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      public int IsPhase2(int start_last_index = 1)
      {
        return -1;
      }
      
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      //
      //                                                     IsPhase3Bullish  Method
      // Returned value :
      //  * True if a real phasys 3 identified
      //  * False if not
      // Side effect : none
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      public bool IsPhase3Bullish(int start_last_index = 1)
      {
        //A Phase 3 Bullish is defined by :
        //  - a minimum of 5 periods of the top Bollinger in the same way of the main (mm20) bollinger
        //  - only a phase2 previously to the phase 3
        
        int trend_duration = 0;
        int pulse = 0;
        bool hasPhase2 = false;
        
        //Go Back up the Top Bollinger until its reversed 
        for (int i = start_last_index; i <= _ms.ClosePrices.Count - 1; i++)
        {
          pulse =  GetPhasysPulseByLastIdx(i);
          //Set Phase 2 found indicator
          if(pulse == 2)
            hasPhase2 = true;
          //Check if Top Bollinger is Rising (this means that we are necessarily in phase 2 or 3)
          if(_bollbands.Top.Last(i + 1) < _bollbands.Top.Last(i))
            trend_duration++;
          else
            break;
        }
        
        //If trend_duration succeed to reach the minimum to define a trend with Phase 2 reached we can say it is a Phase 3
        if(hasPhase2 && trend_duration >= ph2_period_min)
            return true;
        //Exit
        return false;
      }
      
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      //
      //                                                     IsPhase3Bearish  Method
      // Returned value :
      //  * True if a real phasys 3 identified
      //  * False if not
      // Side effect : none
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      public bool IsPhase3Bearish(int start_last_index = 1)
      {
        //A Phase 3 Bearisj is defined by :
        //  - a minimum of 5 periods of the Bottom Bollinger in the same way of the main (mm20) bollinger
        //  - only a phase2 previously to the phase 3
        
        int trend_duration = 0;
        int pulse = 0;
        bool hasPhase2 = false;
        
        //Go Back up the Top Bollinger until its reversed 
        for (int i = start_last_index; i <= _ms.ClosePrices.Count - 1; i++)
        {
          pulse =  GetPhasysPulseByLastIdx(i);
          //Set Phase 2 found indicator
          if(pulse == 2)
            hasPhase2 = true;
          //Check if Top Bollinger is Rising (this means that we are necessarily in phase 2 or 3)
          if(_bollbands.Top.Last(i + 1) > _bollbands.Top.Last(i))
            trend_duration++;
          else
            break;
        }
        
        //If trend_duration succeed to reach the minimum to define a trend with Phase 2 reached we can say it is a Phase 3
        if(hasPhase2 && trend_duration >= ph2_period_min)
            return true;
        //Exit
        return false;
      }
      
      
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      //
      //                                                     IsPhase4Bullish  Method
      // Returned value :
      //  * True if phasys 3 identified
      //  * False if no phase 3 detected
      // Side effect : none
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      public bool IsPhase4Bullish(int start_last_index = 1)
      {
        //A Phase 4 Bullish is defined by :
        //  - coming from a phasys 3 itself coming from a phasys 2

        //Go Back up history
        for (int i = start_last_index; i <= _ms.ClosePrices.Count - 1; i++)
        {
            //Pulse evaluation
            if (GetPhasysPulseByLastIdx(i) != 4){
                //We leave a phasys 4 pulses series 
                //Check from here if precedent phase was a real Phase 3
                if(IsPhase3Bullish(i))
                    return true;
                else
                    return false;
            }
        }
        
        //Exit
        return false;
      }
      
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      //
      //                                                     IsPhase4Bearish  Method
      // Returned value :
      //  * True if phasys 3 identified
      //  * False if no phase 3 detected
      // Side effect : none
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      public bool IsPhase4Bearish(int start_last_index = 1)
      {
        //A Phase 4 Bullish is defined by :
        //  - coming from a phasys 3 itself coming from a phasys 2

        //Go Back up history
        for (int i = start_last_index; i <= _ms.ClosePrices.Count - 1; i++)
        {
            //Pulse evaluation
            if (GetPhasysPulseByLastIdx(i) != 4){
                //We leave a phasys 4 pulses series 
                //Check from here if precedent phase was a real Phase 3
                if(IsPhase3Bearish(i))
                    return true;
                else
                    return false;
            }
        }
        
        //Exit
        return false;
      }
      
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      //
      //                                                     SetPropertiesFromPhase3  Method
      // Returned value : none
      // Side effect : Properties set
      //Prerequities : start_last_index should be in a real phase 3
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      public void SetPropertiesFromPhase3(Trend trend_type, int start_last_index = 1)
      {
        this.cycle_trend = trend_type;
        //Find & Set the Tx (last index)
        this.ph2_Tx_last_idx = GetTxLastIdx(this.cycle_trend, start_last_index);
        //Find & Set the T0 (last index)
        this.ph2_T0_last_idx = GetT0FromTxLastIdx( this.ph2_Tx_last_idx);
        //Find & Set the T1 (last index)
        this.ph2_T1_last_idx = GetT1FromTxLastIdx(Trend.Bullish, this.ph2_Tx_last_idx, start_last_index);
        
        //Go Back up the Top Bollinger until its reversed 
        for (int i = start_last_index; i <= _ms.ClosePrices.Count - 1; i++)
        {
            //If Bullish trend && Top Bollinger stop Rising then stop evaluation
            if(trend_type == Trend.Bullish && _bollbands.Top.Last(i + 1) > _bollbands.Top.Last(i))            
                break;
            
            //If Bearish trend && Bottom Bollinger stop Falling then stop evaluation
            if(trend_type == Trend.Bearish && _bollbands.Bottom.Last(i + 1) < _bollbands.Bottom.Last(i))            
                break;
                
            //Stop if we quit the phase 3
            if(GetPhasysPulseByLastIdx(i) != 3)
                break;
                
            //Set T3 (1st candle of phase 3)
            this.ph3_T3_last_idx = i;                
        }
        
        //Calculate the Phase 2 duration (in periods) (start at T0 and stop before T3)
        this.ph2_duration_periods = this.ph2_T0_last_idx - this.ph3_T3_last_idx;
        
        //Calculate the Phase 3 duration (in periods) 
        this.ph3_duration_periods = this.ph3_T3_last_idx - start_last_index;
        
        //Calculate Bollinger cycle duration
        this.cycle_duration_periods = this.ph2_Tx_last_idx - start_last_index;
        
        //Set the phasys
        this.cycle_phase = 3;
      }
      
      
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      //
      //                                                     SetPropertiesFromPhase4  Method
      // Returned value : none
      // Side effect : Properties set
      //Prerequities : start_last_index should be in a real phase 4
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      public void SetPropertiesFromPhase4(Trend trend_type, int start_last_index = 1)
      {
        this.cycle_trend = trend_type;
        this.ph4_duration_periods = 0;
        //Go Back up history to find the last candle of the Phase 3
        for (int i = start_last_index; i <= _ms.ClosePrices.Count - 1; i++)
        {
        
            //Calculate the Phase 4 duration
            this.ph4_duration_periods += 1;
                
            //Pulse evaluation
            if (GetPhasysPulseByLastIdx(i) != 4){
                //We leave a phasys 4 pulses series  (candle(i) is the last of Phase 3
                //Correct the Ph4 period counter
                this.ph4_duration_periods -= 1;
                //Set T4 (1st candle of phase 4)
                this.ph4_T4_last_idx = i-1;
                //=> Set properties from Phase 3
                SetPropertiesFromPhase3(trend_type, i);
                //And leave this loop
                break;
            }
        }
        //Calculate Bollinger cycle duration
        this.cycle_duration_periods = this.ph2_Tx_last_idx - start_last_index;
      }
      
      
        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //
        //                                                     FindPh2T0WithMinPerDuration  Method
        // Returned value : T0 last index
        // Side effect : none
        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        public int FindPh2T0WithMinPerDuration(int last_idx_start, int last_idx_end, int ph2_per_duration_min=3)
        {
            int ph2_per_duration = 0;
            int T0_to_return = -1; 
            for (int i = last_idx_end; i <= last_idx_start; i++)
            {
              if (_bollbands.Top.Last(i + 1) < _bollbands.Top.Last(i) && _bollbands.Bottom.Last(i + 1) > _bollbands.Bottom.Last(i))
              {
                ph2_per_duration++;
                T0_to_return = i;
              }
              else
              {
                //Phasys 2 not encoutered yet or has been reset (in case of noize)...continue till end loop or ph2 fully identifyed
                if (ph2_per_duration <= 0)
                  continue;
                else
                {
                  //if ph2 duration < min expected, continue to see if there were longer phasys 2 before (current one could be a noize)
                  if (ph2_per_duration < ph2_per_duration_min)
                  {
                    //reset counter
                    ph2_per_duration = 0;
                    T0_to_return = 0;
                    continue;
                  }
                  else
                    //last phasys 2 fully identifyed => Exit
                    return i;
                }
              }
            }
            return (ph2_per_duration >= ph2_per_duration_min ? T0_to_return : -1) ;
        }
        
    }
}

@Nobody
Replies

firemyst
09 Dec 2024, 23:52

First thing I would do is use the Print statement and write output to the logging (algo) tab to see what values and such are coming through where, so you can see what's happening.

And/or using visual studio to debug to see what the values are where.


@firemyst