Your interactive trading laboratory!
 • 
9 users online

Trouble placing a stop order when using limit buy orders
This is a Feature Request with 3 votes

It seems that I have a simple issue placing a stop order 10% below the price of a limit order

if (HasOpenPosition(bars, PositionType.Long) == false)
{
    PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, bars.Close[idx]);
    // here i would like to place the stop order - someting like bars.Close[idx] -10%
} 
else 
{
} 

Thanks for your help!

Attachment

Cancel

Responses

Hey Merlin, I see you want to be able to enter exit orders on the same trading day (bar) that an entry order executes in the backtest. It's currently not possible because of the way QS deals with signals and then placing orders on the following day. It processes exits first, to free up trading capital, followed by entries. So your same-bar exit won't execute since it cannot find a matching position. I have an idea for how to make it work however so I will turn this topic into a feature request.

Hey Merlin, I see you want to be able to enter exit orders on the same trading day (bar) that an entry order executes in the backtest. It's currently not possible because of the way QS deals with signals and then placing orders on the following day. It processes exits first, to free up trading capital, followed by entries. So your same-bar exit won't execute since it cannot find a matching position. I have an idea for how to make it work however so I will turn this topic into a feature request.

Hi Glitch,

Could make sense. There are indeed cases I'm using bracket orders where I place a limit-buy, stop-loss and profit-taker order at the same time. Thanks for your help...

Hi Glitch, Could make sense. There are indeed cases I'm using bracket orders where I place a limit-buy, stop-loss and profit-taker order at the same time. Thanks for your help...

Thanks Glitch, good feature. I can't put my finger on additional use cases at the moment, but it seems like it might be valuable for other purposes. It seems a little like the use of "promises" in some coding languages, where you get a Promise object when you do some asynchronous task (e.g., place a long trade), and then you can use that Promise object to perform other related tasks (e.g., tie the long trade to the stop limit trade). If the Promise trade goes unfilled, then any trades based on that Promise also go unfilled.

Thanks Glitch, good feature. I can't put my finger on additional use cases at the moment, but it seems like it might be valuable for other purposes. It seems a little like the use of "promises" in some coding languages, where you get a Promise object when you do some asynchronous task (e.g., place a long trade), and then you can use that Promise object to perform other related tasks (e.g., tie the long trade to the stop limit trade). If the Promise trade goes unfilled, then any trades based on that Promise also go unfilled.

Yes I was thinking of a similar concept, the ability to link a Transaction to a previously submitted one, so that if the parent is filed, the child Transaction(s) will get placed at that point.

Yes I was thinking of a similar concept, the ability to link a Transaction to a previously submitted one, so that if the parent is filed, the child Transaction(s) will get placed at that point.

Actually I had to take another look because I remember the backtester is more robust that I recalled! It will process bracket orders in most cases. The code below will exit the stop and/or limit on the same bar as entry. The reason it isn't working for your limit order is because limit orders are processed further down the chain, so the same-bar exits don't get a chance to process. I will take a look at what can be done about this.

using QuantaculaBacktest;
using System;
using QuantaculaCore;
using QuantaculaIndicators;
using System.Drawing;
using System.Collections.Generic;

namespace Quantacula
{
    public class MyModel1 : UserModelBase
    {
        //create indicators and other objects here, this is executed prior to the main trading loop
        public override void Initialize(BarHistory bars)
        {
        }

        //execute the strategy rules here, this is executed once for each bar in the backtest history
        public override void Execute(BarHistory bars, int idx)
        {
			if (idx == bars.Count - 21)
			{
				PlaceTrade(bars, TransactionType.Buy, OrderType.Market);
				PlaceTrade(bars, TransactionType.Sell, OrderType.Stop, bars.Close[idx] * 0.98);
				PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, bars.Close[idx] * 1.05);
			}
        }

        //declare private variables below

    }
}

It's tricky though because QS won't know if the stop was hit on the daily bar before or after the limit was reached. This is actually the reason why it doesn't process these orders in the first place. Will give this some thought!

Actually I had to take another look because I remember the backtester is more robust that I recalled! It will process bracket orders in most cases. The code below will exit the stop and/or limit on the same bar as entry. The reason it isn't working for your limit order is because limit orders are processed further down the chain, so the same-bar exits don't get a chance to process. I will take a look at what can be done about this. [CODE] using QuantaculaBacktest; using System; using QuantaculaCore; using QuantaculaIndicators; using System.Drawing; using System.Collections.Generic; namespace Quantacula { public class MyModel1 : UserModelBase { //create indicators and other objects here, this is executed prior to the main trading loop public override void Initialize(BarHistory bars) { } //execute the strategy rules here, this is executed once for each bar in the backtest history public override void Execute(BarHistory bars, int idx) { if (idx == bars.Count - 21) { PlaceTrade(bars, TransactionType.Buy, OrderType.Market); PlaceTrade(bars, TransactionType.Sell, OrderType.Stop, bars.Close[idx] * 0.98); PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, bars.Close[idx] * 1.05); } } //declare private variables below } } [/CODE] It's tricky though because QS won't know if the stop was hit on the daily bar before or after the limit was reached. This is actually the reason why it doesn't process these orders in the first place. Will give this some thought!

OK, we've come full circle. Actually the same-bar stop after a limit order is already working. Run this code on SPY daily to see a same-bar exit. Perhaps the problem is you're not placing the stop exit order also on remaining bars of data in Execute?

using QuantaculaBacktest;
using System;
using QuantaculaCore;
using QuantaculaIndicators;
using System.Drawing;
using System.Collections.Generic;

namespace Quantacula
{
    public class MyModel1 : UserModelBase
    {
        //create indicators and other objects here, this is executed prior to the main trading loop
        public override void Initialize(BarHistory bars)
        {
        }

        //execute the strategy rules here, this is executed once for each bar in the backtest history
        public override void Execute(BarHistory bars, int idx)
        {
			if (bars.DateTimes[idx] != runDate)
				return;
			PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, 287);
			PlaceTrade(bars, TransactionType.Sell, OrderType.Stop, 284);
        }

		//declare private variables below
		private DateTime runDate = new DateTime(2019, 8, 13);
    }
}
OK, we've come full circle. Actually the same-bar stop after a limit order is already working. Run this code on SPY daily to see a same-bar exit. Perhaps the problem is you're not placing the stop exit order also on remaining bars of data in Execute? [CODE] using QuantaculaBacktest; using System; using QuantaculaCore; using QuantaculaIndicators; using System.Drawing; using System.Collections.Generic; namespace Quantacula { public class MyModel1 : UserModelBase { //create indicators and other objects here, this is executed prior to the main trading loop public override void Initialize(BarHistory bars) { } //execute the strategy rules here, this is executed once for each bar in the backtest history public override void Execute(BarHistory bars, int idx) { if (bars.DateTimes[idx] != runDate) return; PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, 287); PlaceTrade(bars, TransactionType.Sell, OrderType.Stop, 284); } //declare private variables below private DateTime runDate = new DateTime(2019, 8, 13); } } [/CODE]

screen shot

screen shot

Here's a sample with ASH in Sep 2008. A simple Bollinger Band System using limit orders. The stop is calculated using an atr(14) * 2. In the chart you can see that the stop at 17.1$ isn't triggered an the trade falls for 69%.

public override void Execute(BarHistory bars, int idx)
{
  if (idx < 20)
    return;
      
  if (HasOpenPosition(bars, PositionType.Long) == false)
  {				
      if (bars.Low[idx] <= lowerBollingerBand[idx] && bars.Close[idx] > lowerBollingerBand[idx])
      {
        double atrStop = ATR.Series(bars, 14)[idx -1] * 2;
        SetBackgroundColor(idx, Color.LightGreen, "Price");
        DrawBarAnnotation(Math.Round((bars.Close[idx] - atrStop),1).ToString(), idx, true, Color.Black, 7);
        
        PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, bars.Close[idx]);
        PlaceTrade(bars, TransactionType.Sell, OrderType.Stop, bars.Close[idx] - atrStop);
      }
  }
  else
  {
    SetBackgroundColor(idx, Color.LightPink, "Price");
    PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, upperBollingerBand[idx]);
  }
}
Here's a sample with ASH in Sep 2008. A simple Bollinger Band System using limit orders. The stop is calculated using an atr(14) * 2. In the chart you can see that the stop at 17.1$ isn't triggered an the trade falls for 69%. [CODE] public override void Execute(BarHistory bars, int idx) { if (idx < 20) return; if (HasOpenPosition(bars, PositionType.Long) == false) { if (bars.Low[idx] <= lowerBollingerBand[idx] && bars.Close[idx] > lowerBollingerBand[idx]) { double atrStop = ATR.Series(bars, 14)[idx -1] * 2; SetBackgroundColor(idx, Color.LightGreen, "Price"); DrawBarAnnotation(Math.Round((bars.Close[idx] - atrStop),1).ToString(), idx, true, Color.Black, 7); PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, bars.Close[idx]); PlaceTrade(bars, TransactionType.Sell, OrderType.Stop, bars.Close[idx] - atrStop); } } else { SetBackgroundColor(idx, Color.LightPink, "Price"); PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, upperBollingerBand[idx]); } } [/CODE]

Hey Merlin, the problem here is that you are only placing the sell order on the same bar as the entry order. PlaceTrade in Quantacula acts as a Day order, not a Good Til Cancel, so you'd need to also place that order in the other branch of your if statement.

Hey Merlin, the problem here is that you are only placing the sell order on the same bar as the entry order. **PlaceTrade** in Quantacula acts as a Day order, not a Good Til Cancel, so you'd need to also place that order in the other branch of your **if** statement.

That looks better but in one ugly case, I've got a -63% slip...

That looks better but in one ugly case, I've got a -63% slip...

Hey Merlin, I wouldn't be able to help without seeing the full code.

Hey Merlin, I wouldn't be able to help without seeing the full code.

@Glitch. Here it is:

using QuantaculaBacktest;
using System;
using QuantaculaCore;
using QuantaculaIndicators;
using System.Drawing;
using System.Collections.Generic;

namespace Quantacula
{
    public class MyModel14 : UserModelBase
    {
      private BBLower lowerBollingerBand;
      private BBUpper upperBollingerBand;
      private ATR atr;
      
      double atrStop;
	    	    
      public override void Initialize(BarHistory bars)
      {
          lowerBollingerBand = new BBLower(bars.Close, 20, 2.0);
          upperBollingerBand = new BBUpper(bars.Close, 20, 2.0);
          PlotIndicator(lowerBollingerBand, Color.Navy, PlotStyles.Bands);

          atr = new ATR(bars, 14);
          Plot(atr);
      }

      public override void Execute(BarHistory bars, int idx)
      {
        if (idx < 20)
          return;
            
        if (HasOpenPosition(bars, PositionType.Long) == false)
        {				
            if (bars.Low[idx] <= lowerBollingerBand[idx] && bars.Close[idx] > lowerBollingerBand[idx])
            {
              atrStop = ATR.Series(bars, 14)[idx - 1] * 2;
              DrawBarAnnotation(Math.Round((bars.Close[idx] - atrStop), 1).ToString(), idx, true, Color.Black, 7);

              SetBackgroundColor(idx, Color.LightGreen, "Price");
              PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, bars.Close[idx]);
            }
        }
        else
        {
          PlaceTrade(bars, TransactionType.Sell, OrderType.Stop, bars.Close[idx] - atrStop);

          SetBackgroundColor(idx, Color.LightPink, "Price");
          PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, upperBollingerBand[idx]);
        }
      }
    }
}
@Glitch. Here it is: [CODE] using QuantaculaBacktest; using System; using QuantaculaCore; using QuantaculaIndicators; using System.Drawing; using System.Collections.Generic; namespace Quantacula { public class MyModel14 : UserModelBase { private BBLower lowerBollingerBand; private BBUpper upperBollingerBand; private ATR atr; double atrStop; public override void Initialize(BarHistory bars) { lowerBollingerBand = new BBLower(bars.Close, 20, 2.0); upperBollingerBand = new BBUpper(bars.Close, 20, 2.0); PlotIndicator(lowerBollingerBand, Color.Navy, PlotStyles.Bands); atr = new ATR(bars, 14); Plot(atr); } public override void Execute(BarHistory bars, int idx) { if (idx < 20) return; if (HasOpenPosition(bars, PositionType.Long) == false) { if (bars.Low[idx] <= lowerBollingerBand[idx] && bars.Close[idx] > lowerBollingerBand[idx]) { atrStop = ATR.Series(bars, 14)[idx - 1] * 2; DrawBarAnnotation(Math.Round((bars.Close[idx] - atrStop), 1).ToString(), idx, true, Color.Black, 7); SetBackgroundColor(idx, Color.LightGreen, "Price"); PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, bars.Close[idx]); } } else { PlaceTrade(bars, TransactionType.Sell, OrderType.Stop, bars.Close[idx] - atrStop); SetBackgroundColor(idx, Color.LightPink, "Price"); PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, upperBollingerBand[idx]); } } } } [/CODE]

The reason the position is not stopped out is because you are calculating the stop level on each bar, and the price never reaches the stop level. Do you want to calculate the stop price once, at the time of entry? If so, then this code does just that. If instead you want to recalculate the stop price every bar, uncomment the specified line. I am also plotting the stop level with a red dot to make it easy to see on the chart.

using QuantaculaBacktest;
using System;
using QuantaculaCore;
using QuantaculaIndicators;
using System.Drawing;
using System.Collections.Generic;

namespace Quantacula
{
	public class MyModel24 : UserModelBase
	{
		private BBLower lowerBollingerBand;
		private BBUpper upperBollingerBand;
		private ATR atr;
		private double stopLevel;

		double atrStop;

		public override void Initialize(BarHistory bars)
		{
			lowerBollingerBand = new BBLower(bars.Close, 20, 2.0);
			upperBollingerBand = new BBUpper(bars.Close, 20, 2.0);
			PlotIndicator(lowerBollingerBand, Color.Navy, PlotStyles.Bands);
			atr = new ATR(bars, 14);
			PlotIndicator(atr);
		}

		public override void Execute(BarHistory bars, int idx)
		{
			if (idx < 20)
				return;

			if (HasOpenPosition(bars, PositionType.Long) == false)
			{				
				if (bars.Low[idx] <= lowerBollingerBand[idx] && bars.Close[idx] > lowerBollingerBand[idx])
				{
					SetBackgroundColor(idx, Color.LightGreen, "Price");
					PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, bars.Close[idx]);
					atrStop = bars.Close[idx] - atr[idx] * 2;
					DrawEllipse(idx, atrStop, idx, atrStop, Color.Red, LineStyles.Thick, "Price");
					PlaceTrade(bars, TransactionType.Sell, OrderType.Stop, atrStop);
				}
			}
			else
			{
				//uncomment this line to recalculate the stop level each bar
				//atrStop = bars.Close[idx] - atr[idx] * 2;
				DrawEllipse(idx, atrStop, idx, atrStop, Color.Red, LineStyles.Thick, "Price");
				PlaceTrade(bars, TransactionType.Sell, OrderType.Stop, atrStop);
				SetBackgroundColor(idx, Color.LightPink, "Price");
				PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, upperBollingerBand[idx]);
			}
		}
	}
}
The reason the position is not stopped out is because you are calculating the stop level on each bar, and the price never reaches the stop level. Do you want to calculate the stop price once, at the time of entry? If so, then this code does just that. If instead you want to recalculate the stop price every bar, uncomment the specified line. I am also plotting the stop level with a red dot to make it easy to see on the chart. [CODE] using QuantaculaBacktest; using System; using QuantaculaCore; using QuantaculaIndicators; using System.Drawing; using System.Collections.Generic; namespace Quantacula { public class MyModel24 : UserModelBase { private BBLower lowerBollingerBand; private BBUpper upperBollingerBand; private ATR atr; private double stopLevel; double atrStop; public override void Initialize(BarHistory bars) { lowerBollingerBand = new BBLower(bars.Close, 20, 2.0); upperBollingerBand = new BBUpper(bars.Close, 20, 2.0); PlotIndicator(lowerBollingerBand, Color.Navy, PlotStyles.Bands); atr = new ATR(bars, 14); PlotIndicator(atr); } public override void Execute(BarHistory bars, int idx) { if (idx < 20) return; if (HasOpenPosition(bars, PositionType.Long) == false) { if (bars.Low[idx] <= lowerBollingerBand[idx] && bars.Close[idx] > lowerBollingerBand[idx]) { SetBackgroundColor(idx, Color.LightGreen, "Price"); PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, bars.Close[idx]); atrStop = bars.Close[idx] - atr[idx] * 2; DrawEllipse(idx, atrStop, idx, atrStop, Color.Red, LineStyles.Thick, "Price"); PlaceTrade(bars, TransactionType.Sell, OrderType.Stop, atrStop); } } else { //uncomment this line to recalculate the stop level each bar //atrStop = bars.Close[idx] - atr[idx] * 2; DrawEllipse(idx, atrStop, idx, atrStop, Color.Red, LineStyles.Thick, "Price"); PlaceTrade(bars, TransactionType.Sell, OrderType.Stop, atrStop); SetBackgroundColor(idx, Color.LightPink, "Price"); PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, upperBollingerBand[idx]); } } } } [/CODE]

Hi Glitch

Thanks for your help. I will take a closer look tomorrow.

I would greatly appreciate it if there would be a simple risk-management-framework. I prefer to spend my focus on the trading logic and not risk management...

Hi Glitch Thanks for your help. I will take a closer look tomorrow. I would greatly appreciate it if there would be a simple risk-management-framework. I prefer to spend my focus on the trading logic and not risk management...

You mean, like some "set and forget" style methods, perhaps, SetStopLoss, SetProfitTarget, etc.?

You mean, like some "set and forget" style methods, perhaps, SetStopLoss, SetProfitTarget, etc.?

To keep things simple, I think it's good enough if you write a FAQ blog about how to handle bracket-orders (Limit Order together with Stop and Take Profit Order). Of course a code sample must be included, this helps the most. Another point which still is open is the handling with trailing stops. I would briefly explain this in the blog with a code sample (using market and limit orders). There's another order type named OCO but I haven't used this in any of my strategies so far.

I know that in most of the products on the market, the order-management can get somewhat complicated, especially if you deviate from market orders. Unfortunately, I stumble all too often over such "small things" in QS :-(

To keep things simple, I think it's good enough if you write a FAQ blog about how to handle bracket-orders (Limit Order together with Stop and Take Profit Order). Of course a code sample must be included, this helps the most. Another point which still is open is the handling with trailing stops. I would briefly explain this in the blog with a code sample (using market and limit orders). There's another order type named OCO but I haven't used this in any of my strategies so far. I know that in most of the products on the market, the order-management can get somewhat complicated, especially if you deviate from market orders. Unfortunately, I stumble all too often over such "small things" in QS :-(

Let me know if this helps … Crafting the Exit.

Let me know if this helps … [Crafting the Exit](https://www.quantacula.com/Help/OpenHelp/devblog/785).

I am finding it very helpful!

I am finding it very helpful!
Forum Tips

Please sign in if you want to participate in our forum.

Our forum uses Markdown syntax to format posts.

To embed code snippets, enclose them in [CODE][/CODE] tags.