Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Need Help with Calling Base Class

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Need Help with Calling Base Class

    Hi There,

    I have this base class MartingaleBase which I found in the forum but having difficulty implementing it into my strategy. I've inherited the class in my strategy like below, created a variable for it but having a problem calling the class in the onstatechange and the on bar update method. I'm not sure how to do this. Basically I've passed the base class method getQty() as a parameter into the enter long method, but when I enable the strategy it does not load.

    Just some background to the class, it basically allows you to double down when you have a loosing trade. For example start with x quantity, if you have a winning trade keep x quantity the same. If you have a losing trade, the next trade will be 2x quantity, if you have another losing trade the next trade will be 4x and so on until you win and recover losses and the multiplier resets to 1 x again.

    Click image for larger version

Name:	image.png
Views:	295
Size:	11.0 KB
ID:	1303792

    See below snippet of the MartingaleBase code. Any help would be appreciated.

    Code:
    //This namespace holds Strategies in this folder and is required. Do not change it.
    namespace NinjaTrader.NinjaScript.Strategies
    {
        abstract public class MartingaleBase : Strategy
        {
            #region Variables
            // Double up on losing trades
            public enum eTradeResult { WINNER, LOSER, BREAK_EVEN };
            public enum MaxDoubleUp { One, Two, Four, Eight };
    
            protected List<eTradeResult> listWinLosses;    // Each element indicates whether a trade finished a winner or a loser.
            private List<double> dailyListProfits;         // Each element indicates the profit of a trade
    
            // The following are used to calculate daily (runtime profit/loss) for the current trade
            private Queue<double> entryPrices;
            private Queue<double> exitPrices;
    
            #endregion
    
            [Browsable(false)]
            public double Commission
            {
                get; set;
            }
    
            protected override void OnStateChange()
            {
                if (State == State.SetDefaults)
                {
                    Description                                    = @"The purpose of this class is to serve as a base class to easily implement the Maritngale entry strategy to any strategy.";
                    Name                                        = "MartingaleBase";
                    Calculate                                    = Calculate.OnBarClose;
                    EntriesPerDirection                            = 1;
                    EntryHandling                                = EntryHandling.AllEntries;
                    IsExitOnSessionCloseStrategy                = true;
                    ExitOnSessionCloseSeconds                    = 30;
                    IsFillLimitOnTouch                            = false;
                    MaximumBarsLookBack                            = MaximumBarsLookBack.TwoHundredFiftySix;
                    OrderFillResolution                            = OrderFillResolution.Standard;
                    Slippage                                    = 0;
                    StartBehavior                                = StartBehavior.WaitUntilFlat;
                    TimeInForce                                    = TimeInForce.Gtc;
                    TraceOrders                                    = false;
                    RealtimeErrorHandling                        = RealtimeErrorHandling.StopCancelClose;
                    StopTargetHandling                            = StopTargetHandling.PerEntryExecution;
                    BarsRequiredToTrade                            = 20;
                    // Disable this property for performance gains in Strategy Analyzer optimizations
                    // See the Help Guide for additional information
                    IsInstantiatedOnEachOptimizationIteration    = false;
                    
                    UseMartingaleQty            = true;
                    MaxExpValue                    = MaxDoubleUp.Eight;
    
                    ShowStrategyPost = true;
                    ShowStrategyPostLocation = TextPosition.TopLeft;
                    PLBoxColorGains = Brushes.Blue;
                    PLBoxColorLosses = Brushes.Red;
                    PLBoxTextColorGains = Brushes.White;
                    PLBoxTextColorLosses = Brushes.Black;
                    PLBoxOpacity = 20;
                }
                else if (State == State.Configure)
                {
                    listWinLosses = new List<eTradeResult>();
    
                    dailyListProfits = new List<double>();
    
                    entryPrices = new Queue<double>();
                    exitPrices = new Queue<double>();
    
                }
            }
    
            protected override void OnExecutionUpdate( Cbi.Execution execution, string executionId, double price, int quantity,
                                                       Cbi.MarketPosition marketPosition, string orderId, DateTime time)
            {
                Trade lastTrade = null;
                try
                {
                    #region Strategy post and session array management
                    try
                    {
                        if (execution.IsExitStrategy)
                        {
                            if( execution.Order.OrderState == OrderState.Filled
                                && execution.Order.Filled > 0 )
                            {
                                // First, set the exit object
                                for (int i = 0; i < execution.Quantity; ++i)
                                {
                                    exitPrices.Enqueue(execution.Price);
                                }
    
                                // Note: the reason why it seems like we passing in the opposite direction that you'd expect is because of my design...
                                // At this point in the vector I no longer have access to the appropriate order object.  The execution object, either the BuyToCover
                                // or the Sell is opposite of the entry, so we'll use that and do the opposite.
                                double commish = execution.Commission * 2;
                                double profitLoss = getProfitLoss(execution.Order.IsLong ? MarketPosition.Short : MarketPosition.Long, commish, execution.Quantity);  // See note above
    
                                // Fill listProfits and the appropriate sub-session
                                dailyListProfits.Add(Math.Round(profitLoss, 2));
    
                                // Post strategy profit/loss and consecutive wins/losses tally
                                postStrategyInfo(execution.Order.Time);
    
                            }
                            else if (execution.Order.OrderState == OrderState.PartFilled)
                            {
                                // All we need to do is set the exit prices
                                for (int i = 0; i < execution.Quantity; ++i)
                                {
                                    exitPrices.Enqueue(execution.Price);
                                }
    
                                // Note: the reason why it seems like we passing in the opposite direction that you'd expect is because of my design...
                                // At this point in the vector I no longer have access to the appropriate order object.  The execution object, either the BuyToCover
                                // or the Sell is opposite of the entry, so we'll use that and do the opposite.
                                double commish = execution.Commission * 2;
                                double profitLoss = getProfitLoss(execution.Order.IsLong ? MarketPosition.Short : MarketPosition.Long, commish, execution.Quantity);  // See note above
    
                                dailyListProfits.Add(Math.Round(profitLoss, 2));
                                postStrategyInfo(execution.Order.Time);
                            }
                        }
                        else if (execution.IsEntryStrategy == true)
                        {
                            // Set the commission
                            if (Commission == 0
                                && execution.Commission != 0)
                            {
                                Commission = execution.Commission * 2;
                            }
    
                            if (execution.Order.OrderState == OrderState.PartFilled)
                            {
                                // This partial fill is the initial entry.  We need to set the quantity here for how much the fill actually was
                                for (int i = 0; i < execution.Quantity; ++i)
                                {
                                    entryPrices.Enqueue(execution.Price);
                                }
                            }
                            else if (execution.Order.OrderState == OrderState.Filled)
                            {
                                for (int i = 0; i < execution.Quantity; ++i)
                                {
                                    entryPrices.Enqueue(execution.Price);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Log(string.Format("OnExecutionUpdate, threw an exception: {0}", ex.Message), LogLevel.Warning);
                    }
                    #endregion
    
                    if (execution.IsExitStrategy
                        && SystemPerformance.AllTrades.Count > 0)
                    {
                        lastTrade = SystemPerformance.AllTrades[SystemPerformance.AllTrades.Count - 1];
                        if (lastTrade != null)
                        {
                            // Fill listWinLosses, but don't consider partial fills
                            if (execution.Order.Quantity == execution.Order.Filled)
                            {
                                eTradeResult thisResult = eTradeResult.BREAK_EVEN;
    
                                // Fill listWinLosses
                                if (lastTrade.ProfitCurrency > 0)
                                {
                                    thisResult = eTradeResult.WINNER;
                                }
                                else if (lastTrade.ProfitCurrency < 0)
                                {
                                    thisResult = eTradeResult.LOSER;
                                }
                                listWinLosses.Add(thisResult);
                            }
                        }
                    }
    
                }
                catch ( Exception e  )
                {
                    Log(string.Format("Error in MartingaleBase.OnExecution: {0}", e.Message), LogLevel.Information);
                }
            }
    
            private double getProfitLoss(MarketPosition inPos, double commission, int qty)
            {
                double profitLoss = 0.00;
    
                if (inPos == MarketPosition.Long)
                {
                    for (int i = 0; i < qty; ++i)
                    {
                        if (entryPrices.Count > 0
                            && exitPrices.Count > 0)
                        {
                            double thisEntryPrice = entryPrices.Dequeue();
                            double thisExitPrice = exitPrices.Dequeue();
                            profitLoss += (thisExitPrice - thisEntryPrice) * Instrument.MasterInstrument.PointValue;
                        }
                    }
                }
                else if (inPos == MarketPosition.Short)
                {
                    for (int i = 0; i < qty; ++i)
                    {
                        if (entryPrices.Count > 0
                            && exitPrices.Count > 0)
                        {
                            double thisEntryPrice = entryPrices.Dequeue();
                            double thisExitPrice = exitPrices.Dequeue();
                            profitLoss += (thisEntryPrice - thisExitPrice) * Instrument.MasterInstrument.PointValue;
                        }
                    }
                }
    
                // Now, subtract commission
                profitLoss -= commission;
                return profitLoss;
            }
    
            #region postStrategyInfo
            /// Called by:     OnExecution()
            ///             OnBarUpdate() at the beginning of the session
            /// <summary>
            /// This method is used to post strategy wins/losses and also on the next line, consecutive wins or losses
            /// in the upper left-corner of the screen.
            /// </summary>
            protected virtual void postStrategyInfo(DateTime inTime)
            {
                string output;
                NinjaTrader.Gui.Tools.SimpleFont textFontMed = new NinjaTrader.Gui.Tools.SimpleFont("Courier", 14) { Bold = true };
    
                // 1. Post wins/losses in currency
                double currency = getDailyProfitLoss();
                if (currency < 0)
                {
                    output = "P/L: -$" + Math.Abs(Math.Round(currency, 2)).ToString();
                }
                else
                {
                    output = "P/L: $" + Math.Round(currency, 2).ToString();
                }
    
                // Color the text and the background
                Brush outlineColor, textColor;
                if (currency >= 0.00)
                {
                    outlineColor = PLBoxColorGains;
                    textColor = PLBoxTextColorGains;
                }
                else
                {
                    outlineColor = PLBoxColorLosses;
                    textColor = PLBoxTextColorLosses;
                }
    
                // Post it!  (Maybe)
                if (ShowStrategyPost)
                {
                    Draw.TextFixed( this, "msg", output, ShowStrategyPostLocation, textColor, textFontMed, outlineColor, outlineColor, PLBoxOpacity );
                }
            }
    
            protected double getDailyProfitLoss()
            {
                double currency = 0.00;
                foreach (double i in dailyListProfits)
                {
                    currency += i;
                }
    
                return currency;
            }
            #endregion
    
            protected override void OnBarUpdate()
            {
                if (Bars.IsFirstBarOfSession
                     && IsFirstTickOfBar)
                {
                    // Win/loss/consecutive trades stuff
                    listWinLosses.Clear();
                    dailyListProfits.Clear();
                    postStrategyInfo(Time[0]);
                }
            }
            
       ... continues
        
    
    }
    ​

    #2
    remaining portion of the code.


    Code:
       #region Martingale
            /// <summary>
            /// This method is used to determine what quantity we'll trade with.  We're going to trade one contract normally.
            /// But the user may want to elect to double-up on the losers.  If they elect to go that then if we get a loser,
            /// then we want to double up on the number of trades that will be taken.  That means if we get one loser, we'll
            /// trade two contracts on the next trade.  If we get two losers, we'll trade four contracts on the next trade
            /// and so on, up to a maximum of eight contracts.
            /// </summary>
            /// <returns>The number of contracts to trade</returns>
            protected virtual int getQty()
            {
                // It's always qty = 1 if we're not doubling up
                if( !UseMartingaleQty ) {
                    return 1;
                }
    
                // It's also always 1 at the beginning of the list...
                if( listWinLosses.Count == 0 ) {
                    return 1;
                }
                            
                int numbItems = listWinLosses.Count - 1;
                if( listWinLosses[numbItems] == eTradeResult.WINNER )
                {
                    return 1;
                }
                else
                {
                    return getTradeCount( getConsecutiveLosses() );
                }
            }
            
            /// <summary>
            /// This method returns the number of consecutive losses we're showing.  This number WILL NOT include
            /// the current trade for which the framework is responsible for closing, i.e. when it takes a trade in
            /// the opposite direction and has to first close the current trade.
            /// Note: In OnBarUpdate(), I attempt to control for that.
            /// </summary>
            /// <returns></returns>
            protected virtual int getConsecutiveLosses()
            {
                // Go through the list until we find a winner - the number of times we go through the loop
                // will tell us how many contracts we need to trade.
                int iConsecutiveLossCount = 0;
                int numbItems = listWinLosses.Count-1;
    
                for( int i = numbItems; i >= 0; --i )
                {
                    if( listWinLosses[i] == eTradeResult.LOSER )
                    {
                        ++iConsecutiveLossCount;
                    }
                    else
                    {
                        break;
                    }
                }
                return iConsecutiveLossCount;    
            }
            
            /// <summary>
            /// This method is a companion to getQty() and is written so it can be called by itself.
            /// </summary>
            /// <param name="inQty">Number of consecutive losses</param>
            /// <returns>The number of contracts to trade based solely on the number of consecutive losses.</returns>
            protected int getTradeCount( int inQty )
            {
                switch( MaxExpValue )
                {
                    case MaxDoubleUp.Eight:
                    {
                        switch( inQty )
                        {
                            case 0:  // sanity check - should never get here
                            case 4:
                            case 8:
                            case 12:
                            case 16:
                                return 1;
                            case 1:
                            case 5:
                            case 9:
                            case 13:
                            case 17:
                                return 2;
                            case 2:
                            case 6:
                            case 10:
                            case 14:
                            case 18:
                                return 4;
                            case 3:
                            case 7:
                            case 11:
                            case 15:
                            case 19:
                                return 8;
                            default:
                                return 1;    
                        }
                    }
                    case MaxDoubleUp.Four:
                    {
                        switch( inQty )
                        {
                            case 0:        // sanity check - shouldn't ever get here
                            case 3:
                            case 6:
                            case 9:
                            case 12:
                            case 15:
                            case 18:
                                return 1;
                            case 1:
                            case 4:
                            case 7:
                            case 10:
                            case 13:
                            case 16:
                            case 19:
                                return 2;
                            case 2:
                            case 5:
                            case 8:
                            case 11:
                            case 14:
                            case 17:
                            case 20:
                                return 4;
                            default:
                                return 1;    
                        }                        
                    }
                    case MaxDoubleUp.Two:
                    {
                        switch( inQty )
                        {
                            case 0:
                            case 2:
                            case 4:
                            case 6:
                            case 8:
                            case 10:
                            case 12:
                            case 14:
                            case 16:
                            case 18:
                            case 20:
                                return 1; // sanity check - shouldn't ever get here
                            case 1:
                            case 3:
                            case 5:
                            case 7:
                            case 9:
                            case 11:
                            case 13:
                            case 15:
                            case 17:
                            case 19:
                            case 21:
                                return 2;
                            default:
                                return 1;    
                        }                        
                    }
                    default:    // If MaxDoubleUp.One, for example
                        return 1;
                }            
            }
            #endregion
            
            #region Properties
            [NinjaScriptProperty]
            [Display(ResourceType = typeof(Custom.Resource), Name="Use Martingale qty?", Description="True to double up on the losers, false to not use the Martingale functionality.", Order=1, GroupName="Martingale params")]
            public bool UseMartingaleQty
            { get; set; }
    
            [NinjaScriptProperty]
            [Range(1, int.MaxValue)]
            [Display(ResourceType = typeof(Custom.Resource), Name="MaxExpValue", Description="Enter 1, 2, 4 or 8 as the maximum number of contracts the system will trade before it resets back to one.", Order=2, GroupName= "Martingale params")]
            public MaxDoubleUp MaxExpValue
            { get; set; }
    
            [Display(Name = "Show profit/loss?", Description = "True to show the current strategy stats in the selected position.", Order = 1, GroupName = "P/L box")]
            public bool ShowStrategyPost
            { get; set; }
    
            [Display(Name = "Profit/loss location", Description = "Location for showing the current strategy stats post.", Order = 2, GroupName = "P/L box")]
            public TextPosition ShowStrategyPostLocation
            { get; set; }
    
            [XmlIgnore]
            [Display(Name = "P/L box color (gains)", Description = "Background and border color for P/L box (gains)", Order = 3, GroupName = "P/L box")]
            public Brush PLBoxColorGains
            { get; set; }
    
            [Browsable(false)]
            public string PLBoxColorGainsSerializable
            {
                get { return Serialize.BrushToString(PLBoxColorGains); }
                set { PLBoxColorGains = Serialize.StringToBrush(value); }
            }
    
            [XmlIgnore]
            [Display(Name = "P/L box color (losses)", Description = "Background and border color for P/L box (losses)", Order = 4, GroupName = "P/L box")]
            public Brush PLBoxColorLosses
            { get; set; }
    
            [Browsable(false)]
            public string PLBoxColorLossesSerializable
            {
                get { return Serialize.BrushToString(PLBoxColorLosses); }
                set { PLBoxColorLosses = Serialize.StringToBrush(value); }
            }
    
            [XmlIgnore]
            [Display(Name = "P/L box text color (gains)", Description = "Text color for P/L box (gains)", Order = 5, GroupName = "P/L box")]
            public Brush PLBoxTextColorGains
            { get; set; }
    
            [Browsable(false)]
            public string PLBoxTextColorGainsSerializable
            {
                get { return Serialize.BrushToString(PLBoxTextColorGains); }
                set { PLBoxTextColorGains = Serialize.StringToBrush(value); }
            }
    
            [XmlIgnore]
            [Display(Name = "P/L box text color (losses)", Description = "Text color for P/L box (losses)", Order = 6, GroupName = "P/L box")]
            public Brush PLBoxTextColorLosses
            { get; set; }
    
            [Browsable(false)]
            public string PLBoxTextColorLossesSerializable
            {
                get { return Serialize.BrushToString(PLBoxTextColorLosses); }
                set { PLBoxTextColorLosses = Serialize.StringToBrush(value); }
            }
    
            [Display(ResourceType = typeof(Custom.Resource), Name = "P/L box opacity", Description = "Sets the opacity, or \"see through value\" of the P/L box.", Order = 7, GroupName = "P/L box")]
            public int PLBoxOpacity
            { get; set; }
    
            #endregion
    
        }
    }
    ​
    Attached Files

    Comment


      #3
      Hello marco231,

      Through our support we cannot suggest using custom inheritance on scripts because that does break features in the platform. You could make a normal C# class that you instantiate from the strategy and call methods from the class like how a normal C# class is used. For any NinjaScript specific properties or functions you can pass in the (this) instance of the strategy to the class. I would otherwise suggest to just take the ideas that code uses and make a strategy which uses that code directly in the strategy without doing any custom inheritance.

      Comment


        #4
        Thanks Jesse, that helped I was able to implement it.

        Comment

        Latest Posts

        Collapse

        Topics Statistics Last Post
        Started by NullPointStrategies, Today, 05:17 AM
        0 responses
        52 views
        0 likes
        Last Post NullPointStrategies  
        Started by argusthome, 03-08-2026, 10:06 AM
        0 responses
        130 views
        0 likes
        Last Post argusthome  
        Started by NabilKhattabi, 03-06-2026, 11:18 AM
        0 responses
        70 views
        0 likes
        Last Post NabilKhattabi  
        Started by Deep42, 03-06-2026, 12:28 AM
        0 responses
        44 views
        0 likes
        Last Post Deep42
        by Deep42
         
        Started by TheRealMorford, 03-05-2026, 06:15 PM
        0 responses
        48 views
        0 likes
        Last Post TheRealMorford  
        Working...
        X