PDA

View Full Version : SetStopLoss() with dynamic SL-offset doesn't trigger


poseidon_sthlm
06-21-2010, 07:52 AM
Hi,
My strategies use the SetStopLoss() method in OnExcecution() with a dynamic SL-offset in Ticks that is calculated in OnBarUpdate(). I have noticed that SetStopLoss() doesn't trigger an exit order when price "jumps" above/ below the SL-level. When I say that price "jumps" I mean that the dynamic SL-level passes 0 ticks and gets negative without touching the zero-level . To avoid this problem I have added an extra "JumpStopLoss" exit in the OnBarUpdate() that catches this situations. But this extra "Jump Stop" executes one bar later and some ticks below/ above the true StopLoss compared to if SetStopLoss() would trigger also when price jumps above/below 0. How can I make the SetStopLoss() trigger also when price jumps above/ below 0?

I'v attached two screenshots and a simple as possible example code that uses your MACrossOver strategy and the OnExceution sample code. The first screen shot shows the strategy with the extra "Jumpstop" and the seccond screen shot shows the same image without the "JumpStop".

namespace NinjaTrader.Strategy
{
/// <summary>
/// Simple moving average cross over strategy.
/// </summary>
[Description("Simple moving average cross over strategy.")]
public class SL_Example_MACrossOver : Strategy
{
#region Variables
private int fast = 10;
private int slow = 25;

private double stopLoss; // StoppLoss offset in SetStopLoss()

private IOrder long1 = null; // This variable holds an object representing our entry order
private IOrder short1 = null; // This variable holds an object representing our entry order
#endregion

/// <summary>
/// This method is used to configure the strategy and is called once before any strategy method is called.
/// </summary>
protected override void Initialize()
{
SMA(Fast).Plots[0].Pen.Color = Color.Orange;
SMA(Slow).Plots[0].Pen.Color = Color.Green;

Add(SMA(Fast));
Add(SMA(Slow));

// Entry handling
EntriesPerDirection = 1;
EntryHandling = EntryHandling.UniqueEntries;
CalculateOnBarClose = true;
}

/// <summary>
/// Called on each bar update event (incoming tick).
/// </summary>
protected override void OnBarUpdate()
{ if(Position.MarketPosition == MarketPosition.Flat)
{
if (CrossAbove(SMA(Fast), SMA(Slow), 1))
{
long1 = EnterLong("Long");
}
else if (CrossBelow(SMA(Fast), SMA(Slow), 1))
{
short1 = EnterShort("Short");
}
}

#region 1 Tick SL-strategy
// Calculate stopLoss in ticks.

// Recalculate StopLoss before position
if (Position.MarketPosition == MarketPosition.Flat)
{
stopLoss = (High[0]-Low[0])/TickSize +1;
}


// Set SL 1 tick below highest low since entry
if (Position.MarketPosition == MarketPosition.Long)
{
stopLoss = (Position.AvgPrice - MAX(Low, BarsSinceEntry() +1 )[0])/ TickSize + 1;
}
// Set SL 1 tick above lowest high since entry
else if (Position.MarketPosition == MarketPosition.Short)
{
stopLoss = (MIN(High, BarsSinceEntry() +1 )[0] - Position.AvgPrice)/ TickSize + 1;
}

/*
Print("");
Print(Time[0]);
Print("Position.AvgPrice: " + Position.AvgPrice);
Print("MIN(High, BarsSinceEntry() +1 )[0]: " + MIN(High, BarsSinceEntry() +1 )[0]);
Print("MAX(Low, BarsSinceEntry() +1 )[0]: " + MAX(Low, BarsSinceEntry() +1 )[0]);
Print("stopLoss: " + stopLoss);
Print("");
*/

/*
//EXTRA JUMP STOP - Check if the StopLoss level is hit. If price jumps above the StopLoss level , exit position since StopLoss is hit
if (Position.MarketPosition == MarketPosition.Long && Low[0] <= Position.AvgPrice - stopLoss*TickSize )
{
ExitLong("JumpStopLoss", "Long" );

}
//If price jumps below the StopLoss level , exit position since StopLoss is hit
else if (Position.MarketPosition == MarketPosition.Short && High[0] >= Position.AvgPrice + stopLoss*TickSize )
{
ExitShort("JumpStopLoss", "Short" );

}
*/

#endregion

}



#region OnExecution
protected override void OnExecution(IExecution execution)
{
if (long1 != null && long1.Token == execution.Order.Token)
{
if (execution.Order.OrderState == OrderState.Filled || execution.Order.OrderState == OrderState.PartFilled || (execution.Order.OrderState == OrderState.Cancelled && execution.Order.Filled > 0))
{
SetStopLoss("Long", CalculationMode.Ticks, stopLoss, false);

// Resets the entryOrder object to null after the order has been partially filled
if (execution.Order.OrderState != OrderState.PartFilled)
{
long1 = null;
}
}
}
if (short1 != null && short1.Token == execution.Order.Token)
{
if (execution.Order.OrderState == OrderState.Filled || execution.Order.OrderState == OrderState.PartFilled || (execution.Order.OrderState == OrderState.Cancelled && execution.Order.Filled > 0))
{
SetStopLoss("Short", CalculationMode.Ticks, stopLoss, false);

// Resets the entryOrder object to null after the order has been partially filled
if (execution.Order.OrderState != OrderState.PartFilled)
{
short1 = null;
}
}
}
}
#endregion


#region Properties
/// <summary>
/// </summary>
[Description("Period for fast MA")]
[GridCategory("Parameters")]
public int Fast
{
get { return fast; }
set { fast = Math.Max(1, value); }
}

/// <summary>
/// </summary>
[Description("Period for slow MA")]
[GridCategory("Parameters")]
public int Slow
{
get { return slow; }
set { slow = Math.Max(1, value); }
}
#endregion
}
}

poseidon_sthlm
06-21-2010, 07:58 AM
Strategy attached. (The extra JumpStop exit condition is commented out in the attached strategy.)

NinjaTrader_RyanM
06-21-2010, 08:09 AM
Hello Poseidon_shtml,

That behavior is expected when using ExitLong when CalculateOnBarClose == true. Orders are submitted on the next bar following the condition.

What you may consider doing is adding a check for > 0 for the stoploss price.

SetStopLoss("Long", CalculationMode.Ticks, Math.Max(stopLoss, 1), false);

poseidon_sthlm
06-21-2010, 09:09 AM
Thanks for the prompt reply.

Your suggestion looks very attractive, but I can't make it work. I'v tried:
SetStopLoss("Long", CalculationMode.Ticks, Math.Max(stopLoss, 1), false);
as well as
SetStopLoss("Long", CalculationMode.Ticks, Math.Max(stopLoss, 0), false);

No exitorder is triggerd when Math.Max(stopLoss, 0) = 0.
Any suggestions?

/Regards



2008-06-11 21:20:00
Position.AvgPrice: 1329,5
MIN(High, BarsSinceEntry() +1 )[0]: 1330
MAX(Low, BarsSinceEntry() +1 )[0]: 1326,5
stopLoss: 3
Math.Max(stopLoss, 0): 3


2008-06-11 21:25:00
Position.AvgPrice: 1329,5
MIN(High, BarsSinceEntry() +1 )[0]: 1328
MAX(Low, BarsSinceEntry() +1 )[0]: 1326,5
stopLoss: -5
Math.Max(stopLoss, 0): 0


2008-06-11 21:30:00
Position.AvgPrice: 1329,5
MIN(High, BarsSinceEntry() +1 )[0]: 1328
MAX(Low, BarsSinceEntry() +1 )[0]: 1326,5
stopLoss: -5
Math.Max(stopLoss, 0): 0


2008-06-11 21:35:00
Position.AvgPrice: 1329,5
MIN(High, BarsSinceEntry() +1 )[0]: 1328
MAX(Low, BarsSinceEntry() +1 )[0]: 1327,25
stopLoss: -5
Math.Max(stopLoss, 0): 0

NinjaTrader_RyanM
06-21-2010, 09:39 AM
poseidon_sthlm,

The idea was to submit the higher of your stoploss value or 1. If you are submitting stop loss values at zero or lower, then you will likely get exited at the same price you entered or your order will be rejected.

Your ExitLong() and ExitShort() statements will likely function as you expect in a real-time environment with CalculateOnBarClose == false. Orders are then submitted as soon as conditions evaluate to true.

poseidon_sthlm
06-21-2010, 10:15 AM
Yes, I understand. I tried Math.Max(stopLoss, 1) as well, but I get the same results.
I did also adjust the code so that stopLoss offset is calculated immediately in OnBarUpdate(). Any other suggestions?


#region 1 Tick SL-strategy
// Calculate stopLoss in ticks.

// Calculate StopLoss before position
if (Position.MarketPosition == MarketPosition.Flat)
{
stopLoss = (High[0]-Low[0])/TickSize +1;
}


// Set SL 1 tick below highest low since entry
if (Position.MarketPosition == MarketPosition.Long)
{
stopLoss = Math.Max((Position.AvgPrice - MAX(Low, BarsSinceEntry() +1 )[0])/ TickSize + 1, 1);
}
// Set SL 1 tick above lowest high since entry
else if (Position.MarketPosition == MarketPosition.Short)
{
stopLoss = Math.Max((MIN(High, BarsSinceEntry() +1 )[0] - Position.AvgPrice)/ TickSize + 1, 1);
}


Print("");
Print(Time[0]);
Print("Position.AvgPrice: " + Position.AvgPrice);
Print("MIN(High, BarsSinceEntry() +1 )[0]: " + MIN(High, BarsSinceEntry() +1 )[0]);
Print("MAX(Low, BarsSinceEntry() +1 )[0]: " + MAX(Low, BarsSinceEntry() +1 )[0]);
Print("stopLoss: " + stopLoss);
Print("");

#endregion


Same result as before:
008-06-11 21:20:00
Position.AvgPrice: 1329,5
MIN(High, BarsSinceEntry() +1 )[0]: 1330
MAX(Low, BarsSinceEntry() +1 )[0]: 1326,5
stopLoss: 3
Math.Max(stopLoss, 1): 3


2008-06-11 21:25:00
Position.AvgPrice: 1329,5
MIN(High, BarsSinceEntry() +1 )[0]: 1328
MAX(Low, BarsSinceEntry() +1 )[0]: 1326,5
stopLoss: -5
Math.Max(stopLoss, 1): 1


2008-06-11 21:30:00
Position.AvgPrice: 1329,5
MIN(High, BarsSinceEntry() +1 )[0]: 1328
MAX(Low, BarsSinceEntry() +1 )[0]: 1326,5
stopLoss: -5
Math.Max(stopLoss, 1): 1


2008-06-11 21:35:00
Position.AvgPrice: 1329,5
MIN(High, BarsSinceEntry() +1 )[0]: 1328
MAX(Low, BarsSinceEntry() +1 )[0]: 1327,25
stopLoss: -5
Math.Max(stopLoss, 1): 1


2008-06-11 21:40:00
Position.AvgPrice: 1329,5
MIN(High, BarsSinceEntry() +1 )[0]: 1328
MAX(Low, BarsSinceEntry() +1 )[0]: 1327,25
stopLoss: -5
Math.Max(stopLoss, 1): 1


2008-06-11 21:45:00
Position.AvgPrice: 1329,5
MIN(High, BarsSinceEntry() +1 )[0]: 1327
MAX(Low, BarsSinceEntry() +1 )[0]: 1327,25
stopLoss: -9
Math.Max(stopLoss, 1): 1


2008-06-11 21:50:00
Position.AvgPrice: 1329,5
MIN(High, BarsSinceEntry() +1 )[0]: 1325,25
MAX(Low, BarsSinceEntry() +1 )[0]: 1327,25
stopLoss: -16
Math.Max(stopLoss, 1): 1


2008-06-11 21:55:00
Position.AvgPrice: 1329,5
MIN(High, BarsSinceEntry() +1 )[0]: 1324,25
MAX(Low, BarsSinceEntry() +1 )[0]: 1327,25
stopLoss: -20
Math.Max(stopLoss, 1): 1

NinjaTrader_RyanM
06-21-2010, 10:20 AM
What results are you seeing with this? It should submit a stop loss at 1 tick anytime the calculated value is below 1.

poseidon_sthlm
06-22-2010, 03:34 AM
Hi again,

I have adjusted the code as highlited in blue in the code example below according to your suggestion. But I don't get the expected result. The SetStopLoss() still doesn't trigger any exitorder when stopLoss = 1 as you can see from the attached image and the print output below. I have also attched the strategy.I would rellay appreciate if this idea would work so that I can get rid of my extra "JumpStop".

/Regards


namespace NinjaTrader.Strategy
{
/// <summary>
/// Simple moving average cross over strategy.
/// </summary>
[Description("Simple moving average cross over strategy.")]
public class SL_Example_MACrossOver : Strategy
{
#region Variables
private int fast = 10;
private int slow = 25;

private double stopLoss; // StoppLoss offset in SetStopLoss()

private IOrder long1 = null; // This variable holds an object representing our entry order
private IOrder short1 = null; // This variable holds an object representing our entry order
#endregion

/// <summary>
/// This method is used to configure the strategy and is called once before any strategy method is called.
/// </summary>
protected override void Initialize()
{
SMA(Fast).Plots[0].Pen.Color = Color.Orange;
SMA(Slow).Plots[0].Pen.Color = Color.Green;

Add(SMA(Fast));
Add(SMA(Slow));

// Entry handling
EntriesPerDirection = 1;
EntryHandling = EntryHandling.UniqueEntries;
CalculateOnBarClose = true;
}

/// <summary>
/// Called on each bar update event (incoming tick).
/// </summary>
protected override void OnBarUpdate()
{ if(Position.MarketPosition == MarketPosition.Flat)
{
if (CrossAbove(SMA(Fast), SMA(Slow), 1))
{
long1 = EnterLong("Long");
}
else if (CrossBelow(SMA(Fast), SMA(Slow), 1))
{
short1 = EnterShort("Short");
}
}

#region 1 Tick SL-strategy
// Calculate stopLoss in ticks.

// Calculate StopLoss before position
if (Position.MarketPosition == MarketPosition.Flat)
{
stopLoss = (High[0]-Low[0])/TickSize +1;
}


// Set SL 1 tick below highest low since entry
if (Position.MarketPosition == MarketPosition.Long)
{
stopLoss = (Position.AvgPrice - MAX(Low, BarsSinceEntry() +1 )[0])/ TickSize + 1;
}
// Set SL 1 tick above lowest high since entry
else if (Position.MarketPosition == MarketPosition.Short)
{
stopLoss = (MIN(High, BarsSinceEntry() +1 )[0] - Position.AvgPrice)/ TickSize + 1;
}

// Ensure that stopLoss always is equal or greater than 1
stopLoss = Math.Max(stopLoss, 1);


//Trouble Shooting
Print("");
Print(Time[0]);
Print("stopLoss: " + stopLoss);
Print("Position.MarketPosition: " + Position.MarketPosition);
Print("");

#endregion

}



#region OnExecution
protected override void OnExecution(IExecution execution)
{
if (long1 != null && long1.Token == execution.Order.Token)
{
if (execution.Order.OrderState == OrderState.Filled || execution.Order.OrderState == OrderState.PartFilled || (execution.Order.OrderState == OrderState.Cancelled && execution.Order.Filled > 0))
{
SetStopLoss("Long", CalculationMode.Ticks, stopLoss, false);

// Resets the entryOrder object to null after the order has been partially filled
if (execution.Order.OrderState != OrderState.PartFilled)
{
long1 = null;
}
}
}
if (short1 != null && short1.Token == execution.Order.Token)
{
if (execution.Order.OrderState == OrderState.Filled || execution.Order.OrderState == OrderState.PartFilled || (execution.Order.OrderState == OrderState.Cancelled && execution.Order.Filled > 0))
{
SetStopLoss("Short", CalculationMode.Ticks, stopLoss, false);

// Resets the entryOrder object to null after the order has been partially filled
if (execution.Order.OrderState != OrderState.PartFilled)
{
short1 = null;
}
}
}
}
#endregion


#region Properties
/// <summary>
/// </summary>
[Description("Period for fast MA")]
[GridCategory("Parameters")]
public int Fast
{
get { return fast; }
set { fast = Math.Max(1, value); }
}

/// <summary>
/// </summary>
[Description("Period for slow MA")]
[GridCategory("Parameters")]
public int Slow
{
get { return slow; }
set { slow = Math.Max(1, value); }
}
#endregion
}
}
Output from the print statment in the code above:
2009-06-22 16:30:00
stopLoss: 3
Position.MarketPosition: Short


2009-06-22 16:31:00
stopLoss: 2
Position.MarketPosition: Short


2009-06-22 16:32:00
stopLoss: 1
Position.MarketPosition: Short


2009-06-22 16:33:00
stopLoss: 1
Position.MarketPosition: Short


2009-06-22 16:34:00
stopLoss: 1
Position.MarketPosition: Short

NinjaTrader_RyanM
06-22-2010, 08:06 AM
poseidon_sthlm,

The SetStopLoss() still doesn't trigger any exitorder when stopLoss = 1

Let's clarify what it is you expect this to do. What do you mean by any exitorder? The statement will submit a stop loss order at 1 tick whenever your calculated value stoploss is below 1. Is it doing this?

poseidon_sthlm
06-23-2010, 08:51 AM
Sorry for the confusion. The obvious solution is to modify the Stop Loss with a seccond SetStopLoss() statment in OnBarUpdate() (besides the one in OnExecution() ). Everything now works as expected.

//Regards

NinjaTrader_RyanM
06-23-2010, 08:55 AM
Thanks for sharing your solution, poseidon. Glad you got it sorted.