Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Placing Long/Short Limit Orders at Volumetric POC

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

    Placing Long/Short Limit Orders at Volumetric POC

    Hi,

    I would like to EnterLongLimit and EnterShortLimit at the POC of the CurrentBar when the signal occurs. The limit order would be good until it's filled or until another signal occurs in the strategy. I've accessed the Volumetric bars in the strategy, but how to call the statement with Volumetric Bars at the POC?

    Thanks

    #2
    Hello,

    To place EnterLongLimit and EnterShortLimit orders at the Point of Control (POC) of the current bar using Volumetric bars in NinjaTrader, you need to access the POC value from the Volumetric bars. Here's a step-by-step example of how you can achieve this: Step-by-Step Example
    1. Add Volumetric Bars to Your Strategy:
      • Use the AddVolumetric method in the OnStateChange method to add Volumetric bars to your strategy.
    2. Retrieve the POC Value:
      • Access the POC value from the Volumetric bars.
    3. Place Limit Orders:
      • Use EnterLongLimit and EnterShortLimit methods to place limit orders at the POC.
    Example Code


    Here is an example of how to implement this in NinjaScript:

    Code:
    #region Using declarations
    using System;
    using NinjaTrader.Cbi;
    using NinjaTrader.Gui.Tools;
    using NinjaTrader.NinjaScript;
    using NinjaTrader.Data;
    using NinjaTrader.NinjaScript.StrategyAnalyzerColumns;
    using NinjaTrader.Gui.Chart;
    using NinjaTrader.NinjaScript.Strategies;
    using NinjaTrader.NinjaScript.Indicators;
    #endregion
    
    namespace NinjaTrader.NinjaScript.Strategies
    {
        public class POCBasedStrategy : Strategy
        {
            private int volumePeriod = 1;
            private string volumetricSeriesName = "NQ 09-21";
    
            protected override void OnStateChange()
            {
                if (State == State.SetDefaults)
                {
                    Description = @"A strategy to place limit orders at the POC of the current bar.";
                    Name = "POCBasedStrategy";
                    Calculate = Calculate.OnEachTick;
                    IsInstantiatedOnEachOptimizationIteration = false;
                    EntriesPerDirection = 1;
                    EntryHandling = EntryHandling.AllEntries;
                    IsExitOnSessionCloseStrategy = true;
                    ExitOnSessionCloseSeconds = 30;
                    StopTargetHandling = StopTargetHandling.ByStrategyPosition;
                    IsFillLimitOnTouch = false;
                    MaximumBarsLookBack = MaximumBarsLookBack.TwoHundredFiftySix;
                    StartBehavior = StartBehavior.WaitUntilFlat;
                    TimeInForce = TimeInForce.Gtc;
                    TraceOrders = false;
                    RealtimeErrorHandling = RealtimeErrorHandling.StopCancelClose;
                    BarsRequiredToTrade = 20;
                }
                else if (State == State.Configure)
                {
                    // Add Volumetric bars
                    AddVolumetric(volumetricSeriesName, BarsPeriodType.Minute, volumePeriod, MarketDataType.Last, 1, 1, 1);
                }
            }
    
            protected override void OnBarUpdate()
            {
                if (CurrentBar < BarsRequiredToTrade)
                    return;
    
                if (BarsInProgress != 1)
                    return;
    
                // Access Volumetric bars
                VolumetricBarsType volumetricBars = BarsArray[1] as VolumetricBarsType;
                if (volumetricBars == null)
                    return;
    
                // Get the POC price of the current bar
                double pocPrice = volumetricBars.GetPointOfControl(CurrentBar);
    
                // Example signal conditions
                if (CrossAbove(Close, SMA(14), 1))
                {
                    // Place a limit buy order at the POC price
                    EnterLongLimit(0, true, 1, pocPrice, "Long at POC");
                }
    
                if (CrossBelow(Close, SMA(14), 1))
                {
                    // Place a limit sell order at the POC price
                    EnterShortLimit(0, true, 1, pocPrice, "Short at POC");
                }
            }
    
            protected override void OnExecutionUpdate(Cbi.Execution execution, string executionId, double price, int quantity, MarketPosition marketPosition, string orderId, DateTime time)
            {
                // Cancel open limit orders when another signal occurs
                if (execution.Order.OrderState == OrderState.Filled)
                {
                    CancelAllOrders();
                }
            }
        }
    }
    ​
    Explanation:
    1. Adding Volumetric Bars:
      • AddVolumetric(volumetricSeriesName, BarsPeriodType.Minute, volumePeriod, MarketDataType.Last, 1, 1, 1); adds the volumetric bars to the strategy.
    2. Retrieving the POC Value:
      • VolumetricBarsType volumetricBars = BarsArray[1] as VolumetricBarsType; retrieves the volumetric bars.
      • double pocPrice = volumetricBars.GetPointOfControl(CurrentBar); gets the POC price of the current bar.
    3. Placing Limit Orders:
      • EnterLongLimit(0, true, 1, pocPrice, "Long at POC"); places a limit buy order at the POC price.
      • EnterShortLimit(0, true, 1, pocPrice, "Short at POC"); places a limit sell order at the POC price.
    4. Cancelling Orders on Signal:
      • CancelAllOrders(); cancels any open orders when a new signal occurs.

    This code should place limit orders at the POC price of the current bar when a signal occurs and cancel open limit orders when another signal occurs. Adjust the volumetricSeriesName, BarsPeriodType, and other parameters as needed for your specific use case.

    Comment


      #3
      Thank you Ryan. This is an excellent explanation and exactly what I needed.

      At the top, there's:

      private string volumetricSeriesName = "NQ 09-21";

      If I wanted the strategy to work on different instruments and not tied to just one instrument, is there another way to do this?

      Thanks

      Comment


        #4
        Hello,

        To make your strategy work with different instruments and not be tied to a specific instrument, you should avoid hardcoding the instrument name. Instead, you can use the primary instrument of the strategy. Here's how you can modify your code:
        1. Use the Primary Instrument:
          • Replace the hardcoded volumetricSeriesName with the primary instrument of the strategy.
        2. Modify the OnStateChange Method:
          • Use Instrument.FullName to get the name of the primary instrument.

        Here's the updated code:
        Code:
        #region Using declarations
        using System;
        using NinjaTrader.Cbi;
        using NinjaTrader.Gui.Tools;
        using NinjaTrader.NinjaScript;
        using NinjaTrader.Data;
        using NinjaTrader.NinjaScript.StrategyAnalyzerColumns;
        using NinjaTrader.Gui.Chart;
        using NinjaTrader.NinjaScript.Strategies;
        using NinjaTrader.NinjaScript.Indicators;
        #endregion
        
        namespace NinjaTrader.NinjaScript.Strategies
        {
            public class POCBasedStrategy : Strategy
            {
                private int volumePeriod = 1;
        
                protected override void OnStateChange()
                {
                    if (State == State.SetDefaults)
                    {
                        Description = @"A strategy to place limit orders at the POC of the current bar.";
                        Name = "POCBasedStrategy";
                        Calculate = Calculate.OnEachTick;
                        IsInstantiatedOnEachOptimizationIteration = false;
                        EntriesPerDirection = 1;
                        EntryHandling = EntryHandling.AllEntries;
                        IsExitOnSessionCloseStrategy = true;
                        ExitOnSessionCloseSeconds = 30;
                        StopTargetHandling = StopTargetHandling.ByStrategyPosition;
                        IsFillLimitOnTouch = false;
                        MaximumBarsLookBack = MaximumBarsLookBack.TwoHundredFiftySix;
                        StartBehavior = StartBehavior.WaitUntilFlat;
                        TimeInForce = TimeInForce.Gtc;
                        TraceOrders = false;
                        RealtimeErrorHandling = RealtimeErrorHandling.StopCancelClose;
                        BarsRequiredToTrade = 20;
                    }
                    else if (State == State.Configure)
                    {
                        // Add Volumetric bars using the primary instrument
                        AddVolumetric(Instrument.FullName, BarsPeriodType.Minute, volumePeriod, MarketDataType.Last, 1, 1, 1);
                    }
                }
        
                protected override void OnBarUpdate()
                {
                    if (CurrentBar < BarsRequiredToTrade)
                        return;
        
                    // Ensure we are processing the primary data series
                    if (BarsInProgress != 0)
                        return;
        
                    // Access Volumetric bars
                    VolumetricBarsType volumetricBars = BarsArray[1] as VolumetricBarsType;
                    if (volumetricBars == null)
                        return;
        
                    // Get the POC price of the current bar
                    double pocPrice = volumetricBars.GetPointOfControl(CurrentBar);
        
                    // Example signal conditions
                    if (CrossAbove(Close, SMA(14), 1))
                    {
                        // Place a limit buy order at the POC price
                        EnterLongLimit(0, true, 1, pocPrice, "Long at POC");
                    }
        
                    if (CrossBelow(Close, SMA(14), 1))
                    {
                        // Place a limit sell order at the POC price
                        EnterShortLimit(0, true, 1, pocPrice, "Short at POC");
                    }
                }
        
                protected override void OnExecutionUpdate(Cbi.Execution execution, string executionId, double price, int quantity, MarketPosition marketPosition, string orderId, DateTime time)
                {
                    // Cancel open limit orders when another signal occurs
                    if (execution.Order.OrderState == OrderState.Filled)
                    {
                        CancelAllOrders();
                    }
                }
            }
        }
        ​
        Explanation:
        1. Using the Primary Instrument:
          • The Instrument.FullName property is used to get the name of the primary instrument that the strategy is applied to.
        2. Add Volumetric Bars:
          • AddVolumetric(Instrument.FullName, BarsPeriodType.Minute, volumePeriod, MarketDataType.Last, 1, 1, 1); adds the volumetric bars for the primary instrument.
        3. Ensure Primary Data Series Processing:
          • if (BarsInProgress != 0) return; ensures that the OnBarUpdate method processes the primary data series.

        By making these changes, your strategy can work on any instrument it is applied to, instead of being tied to a specific instrument. This makes the strategy more flexible and easier to use across different instruments.

        Comment


          #5
          Thank you Ryan for a quick response. I'm receiving the following errors in the screenshot.

          Also, I've a total of 3 data series with AddVolumetric on BarsArray[1]. Instead of how you've written in the code, would the below be the correct way to write it?

          Code:
          protected override void OnBarUpdate()
          {​
          if (BarsInProgress != 0)
          return;
          
          if (CurrentBars[0] < 60 || CurrentBars[1] < 1 || CurrentBars[2] < 1)
          return;
          
          // Access Volumetric bars
          VolumetricBarsType volumetricBars = BarsArray[1] as VolumetricBarsType;
          if (volumetricBars == null)
          return;
          
          // Get the POC price of the current bar
          double pocPrice = volumetricBars.GetPointOfControl(CurrentBar);
          
          // Example signal conditions
          if (CrossAbove(Close, SMA(14), 1))
          {
          // Place a limit buy order at the POC price
          EnterLongLimit(0, true, 1, pocPrice, "Long at POC");
          }
          
          if (CrossBelow(Close, SMA(14), 1))
          {
          // Place a limit sell order at the POC price
          EnterShortLimit(0, true, 1, pocPrice, "Short at POC");
          }
          }
          
          protected override void OnExecutionUpdate(Cbi.Execution execution, string executionId, double price, int quantity, MarketPosition marketPosition, string orderId, DateTime time)
          {
          // Cancel open limit orders when another signal occurs
          if (execution.Order.OrderState == OrderState.Filled)
          {
          CancelAllOrders();
          }
          }​
          ​
          Attached Files

          Comment


            #6
            Good morning,

            Just wanted to follow-up on the above post. Also, if I copy and paste the sample code, under Declarations, I'm getting an error message for:

            using NinjaTrader.NinjaScript.StrategyAnalyzerColumns;

            The type of namespace..... please see the attached message.

            Thanks
            Attached Files

            Comment


              #7
              Hello,

              Please note the code examples provided are only examples, and will likely not be able to be copy/pasted directly into your existing script without running into compile errors or other unexpected behaviors. These are simply meant to illustrate possible functionality and demonstration of logic that can be augmented by the user to fit their script if applicable.

              In your example however, your approach to handling multiple data series and accessing volumetric bars is mostly correct. The key is ensuring you handle the logic correctly for different BarsInProgress values and checking the CurrentBars for each series. Here's a refined version of your script to ensure clarity and correctness:

              Code:
              namespace NinjaTrader.NinjaScript.Strategies { public class POCBasedStrategy : Strategy { private int volumePeriod = 1; protected override void OnStateChange() { if (State == State.SetDefaults) { Description = @"A strategy to place limit orders at the POC of the current bar."; Name = "POCBasedStrategy"; Calculate = Calculate.OnEachTick; IsInstantiatedOnEachOptimizationIteration = false; EntriesPerDirection = 1; EntryHandling = EntryHandling.AllEntries; IsExitOnSessionCloseStrategy = true; ExitOnSessionCloseSeconds = 30; StopTargetHandling = StopTargetHandling.ByStrategyPosition; IsFillLimitOnTouch = false; MaximumBarsLookBack = MaximumBarsLookBack.TwoHundredFiftySix; StartBehavior = StartBehavior.WaitUntilFlat; TimeInForce = TimeInForce.Gtc; TraceOrders = false; RealtimeErrorHandling = RealtimeErrorHandling.StopCancelClose; BarsRequiredToTrade = 20; } else if (State == State.Configure) { // Add primary data series (e.g., 1-minute bars) AddDataSeries(Data.BarsPeriodType.Minute, 1); // Add volumetric bars as secondary data series AddVolumetric("NQ 09-21", BarsPeriodType.Minute, volumePeriod, MarketDataType.Last, 1, 1, 1); // Add a third data series, e.g., a different timeframe or instrument AddDataSeries(Data.BarsPeriodType.Minute, 5); } } protected override void OnBarUpdate() { // Ensure there are enough bars to proceed for each data series if (CurrentBars[0] < 60 || CurrentBars[1] < 1 || CurrentBars[2] < 1) return; // Only process on the primary series (BarsInProgress == 0) if (BarsInProgress != 0) return; // Access Volumetric bars from BarsArray[1] VolumetricBarsType volumetricBars = BarsArray[1] as VolumetricBarsType; if (volumetricBars == null) return; // Get the POC price of the current bar double pocPrice = volumetricBars.GetPointOfControl(CurrentBar); // Example signal conditions if (CrossAbove(Close, SMA(14), 1)) { // Place a limit buy order at the POC price EnterLongLimit(0, true, 1, pocPrice, "Long at POC"); } if (CrossBelow(Close, SMA(14), 1)) { // Place a limit sell order at the POC price EnterShortLimit(0, true, 1, pocPrice, "Short at POC"); } } protected override void OnExecutionUpdate(Cbi.Execution execution, string executionId, double price, int quantity, MarketPosition marketPosition, string orderId, DateTime time) { // Cancel open limit orders when another signal occurs if (execution.Order.OrderState == OrderState.Filled) { CancelAllOrders(); } } } }
              Explanation:
              1. Ensure Enough Bars to Proceed:
                • if (CurrentBars[0] < 60 || CurrentBars[1] < 1 || CurrentBars[2] < 1) return;
                  • Ensure each data series has sufficient bars before proceeding.
              2. Process Only on the Primary Data Series:
                • if (BarsInProgress != 0) return;
                  • This ensures that the logic is executed only for the primary data series (e.g., 1-minute bars).
              3. Access Volumetric Bars:
                • VolumetricBarsType volumetricBars = BarsArray[1] as VolumetricBarsType;
                  • Access the volumetric bars from the secondary data series.
              4. Get POC Price:
                • double pocPrice = volumetricBars.GetPointOfControl(CurrentBar);
                  • Retrieve the Point of Control (POC) price for the current bar.
              5. Signal Conditions and Orders:
                • Check for signal conditions using CrossAbove and CrossBelow.
                • Place limit orders at the POC price using EnterLongLimit and EnterShortLimit.
              6. Order Cancellation:
                • Cancel open limit orders when a new signal occurs using CancelAllOrders.

              This should ensure that your strategy handles multiple data series correctly and places orders based on the POC of volumetric bars.

              An important thing to note however, is that the practice of using a variable in an AddDataSeries() / AddVolumetric() call can break optimizations and other expected behavior. I would be sure to test this thoroughly in a simulation environment, and possibly reference the limitations of doing so in the link below:





              ​​

              Comment


                #8
                Hello AgriTrdr,

                Apologies, the information provided by my colleague is not correct.

                volumetricBars.GetPointOfControl() is not a valid method.


                However you may be looking for the highest <volumetricBars>.GetTotalVolumeForPrice() for all price levels.
                Join the official NinjaScript Developer Community for comprehensive resources, documentation, and community support. Build custom indicators and automated strategies for the NinjaTrader platforms with our extensive guides and APIs.
                Chelsea B.NinjaTrader Customer Service

                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
                49 views
                0 likes
                Last Post TheRealMorford  
                Working...
                X