- ago
Question on ClosePosition versus CloseAtTrailingStop. I have the following snippet in one of my strategies:

CODE:
//=== Exit === Position p = LastOpenPosition; if(p != null) {    // Stop Loss    CloseAtTrailingStop(p, TrailingStopType.PercentC, 25, "Trailing Stop");    if (p.IsOpen)    {       // Trailing Stop       ClosePosition(p, OrderType.Stop, p.EntryPrice - 5.0 * atr20[p.EntryBar], "Stop Loss");    } }

When I run this code thru a backtest on "RETA" I am presented with the following chart and position information.


Position Info
Position Symbol Quantity "Entry Date" "Entry Price" "Exit Date" "Exit Price" Profit "Profit %" "Bars Held" "Entry Signal" "Exit Signal" "MFE %" "MAE %"
Long RETA 362 5/31/2017 27.69 8/10/2017 26.6025 -393.67500000000075 -3.9274106175514696 51 Long Entry Trailing Stop 47.634525099313834 -10.021668472372705
Long RETA 329 9/27/2017 30.59 11/13/2017 24.9525 -1854.7374999999997 -18.429225237005554 34 Long Entry Trailing Stop 10.891140895717548 -18.429225237005554
Long RETA 345 1/12/2018 28.31 3/2/2018 22.642500000000002 -1955.287499999999 -20.019427764040966 34 Long Entry Trailing Stop 9.46661956905687 -20.01942776404097
Long RETA 105 7/25/2019 90.28 8/26/2019 70.80915310445411 -2044.4389240323185 -21.567176446107545 23 Long Entry Stop Loss 3.2676118741692544 -21.567176446107545
Long RETA 105 9/27/2019 89.13 2/28/2020 181.72 9721.95 103.88197015595199 105 Long Entry Trailing Stop 189.4255581734545 -17.670817906428812
Long RETA 65 7/9/2020 158.2 8/10/2020 105.403 -3431.804999999999 -33.37357774968394 22 Long Entry Trailing Stop 4.165613147914035 -33.37357774968393
Long RETA 88 10/20/2020 113.86 12/2/2020 137.06 2041.6000000000004 20.37590022835061 30 Long Entry Trailing Stop 64.07518004567012 -2.0551554540664
Long RETA 80 3/8/2021 127.85 3/25/2021 99 -2307.9999999999995 -22.565506452874455 13 Long Entry Trailing Stop 4.075087993742674 -22.565506452874455
Long RETA 72 5/28/2021 137.65 8/10/2021 100.38 -2683.4400000000005 -27.075917181256813 50 Long Entry Stop Loss 11.449328005811834 -27.075917181256813
Long RETA 237 1/27/2023 40.81 2/27/2023 26.78701946948002 -3323.446385733236 -34.361628352168545 21 Long Entry Stop Loss 27.370742465082074 -34.361628352168545

My question is why is the "Stop Loss" exit being triggered rather than the "Trailing Stop Loss" on the last trade? Price opened above both, then traded down through both. The first CloseAtTrailingStop() failed to close the position, hence the execution of ClosePosition(). I would have thought that the call to CloseAtTrailingStop() would have closed for a much smaller loss. Hope this makes sense, I can clarify further if needed.
-sifty
1
582
Solved
21 Replies

Reply

Bookmark

Sort
Glitch8
 ( 7.81% )
- ago
#2
By default WL will randomize exits unless an exit is tagged to a specific position. This happens automatically in building block strategies, but you can enforce the same behavior in a coded strategy by assigning a position tag like this,

PlaceTrade(bars, TransactionType.Buy, OrderType.Market, 0, 1);

The last parameter is a tag number that WL uses to distinguish different groups of positions.
0
Cone8
 ( 24.56% )
- ago
#3
Glitch, wouldn't this scenario be a candidate for signal pruning?
0
Glitch8
 ( 7.81% )
- ago
#4
Signal pruning only works for positions that have a group code (tag) because otherwise it wreaks havoc with certain coded systems that manage multiple positions.
0
- ago
#5
Just to clarify:
If I am placing multiple stop orders in the market to close a position, WL will randomize between the multiple stop orders unless a positionTag is used when opening the position. If a positionTag is used when opening the position, then WL will
a) execute the stops in the order specified by code?
b) execute the "closest" stop?
c) something else?
-sifty
0
Glitch8
 ( 7.81% )
- ago
#6
If you specify a group code like I showed, WL will know that the multiple exit orders belong to the same group. It will prune them so the stop order with the higher price will be the one selected.
1
Best Answer
- ago
#7
Thank you for the explanation Glitch!
0
- ago
#8
By the way, in case others are looking. This code simplified the exit back to a single CloseAtTrailingStop() call.
CODE:
         //=== Exit ===          Position p = LastOpenPosition;          if(p != null)          {             // Stop Loss             if (Double.IsNaN(p.TrailingStopPrice))             {                p.TrailingStopPrice = p.EntryPrice - 5.0 * atr20[p.EntryBar];             }             CloseAtTrailingStop(p, TrailingStopType.PercentC, 25, "Trailing Stop");          }
3
- ago
#9
So can the code in Reply# 8 be simplified to the following code below if you want a Chandelier exit with ATR*5? Also, where is this operation documented better? I would like to lookup what the periods are on the Highest (long trade) recent value and the ATR for the Chandelier exit computation. I assume the periods for both are 22 bars, but I would like to be sure.

CODE:
      //=== Exit ===       Position p = LastOpenPosition;       if (p != null)          CloseAtTrailingStop(p, TrailingStopType.ATR, 5, "Chandelier Xit");
0
- ago
#10
QUOTE:
Also, where is this operation documented better?

In the QuickRef it's mentioned in "TrailingStopType" and "UserStrategyBase" > CloseAtTrailingStop (btw, a change coming up for ATR exits: https://www.wealth-lab.com/Discussion/User-selectable-ATR-Period-in-TrailingStopType-ATR-9745)
1
pmbf8
- ago
#11
Hi Everyone,

Back to the topic of WL randomizing the multiple exit signals on the same bar, i find that the following two examples below produce similar random end results.

From a logic perspective, how is WL able to randomize multiple exit signals on the same bar for the below examples, when the stop loss is executed before Trailing Stop since the code for the former is above the latter?

Example 1
CODE:
Position p = LastOpenPosition; if(p != null) {    // Stop Loss    ClosePosition(p, OrderType.Stop, 0.8*p.EntryPrice, "Stop Loss");    if (p.IsOpen)    {    // Trailing Stop       CloseAtTrailingStop(p, TrailingStopType.PercentC, 25, "Trailing Stop");    } }

Example 2
CODE:
Position p = LastOpenPosition; if(p != null) {    // Stop Loss and Trailing Stop    ClosePosition(p, OrderType.Stop, 0.8*p.EntryPrice, "Stop Loss");    CloseAtTrailingStop(p, TrailingStopType.PercentC, 25, "Trailing Stop"); }
0
Glitch8
 ( 7.81% )
- ago
#12
Because the PlaceTrade statements are not immediately “filled” by the Backtester. Each bar, all trades (Transaction objects) are placed in a list, and finally randomized and acted upon.
0
Cone8
 ( 24.56% )
- ago
#13
In any case, these "Close" orders operate with the same position - there is nothing random here. The position will exit at the most restrictive stop price, always. If it's not doing that, I want to know about it.
0
pmbf8
- ago
#14
Hi Cone,

Please refer to the two screenshots below that show the random exit prices on 18th Mar 2020 for the same ticker, based on the codes in Example 1 and Example 2.

Trailing Stop Exit at 138.26


Stop Loss Exit at 145.41


pmbf
0
pmbf8
- ago
#15
Hi Glitch,

With regards to the following quote, can I ask when will the trades be filled? Would they be filled be after all the codes in Execute method have been executed for one bar?

QUOTE:
Because the PlaceTrade statements are not immediately “filled” by the Backtester. Each bar, all trades (Transaction objects) are placed in a list, and finally randomized and acted upon.


pmbf
0
Glitch8
 ( 7.81% )
- ago
#16
Yes that’s right.

Technically they that “filled” on the following bar, just like real trading.
0
pmbf8
- ago
#17
Hi Glitch,

I think certain trades such as stop losses or trailing stops are filled at current bar instead of next bar.

Using the below code on a single stock as a simple example,

1) PlaceTrade will cause the backtester to fill the trade at start of following bar, right?

2) However, ClosePosition and CloseAtTrailingStop will attempt to fill the exit at this bar based on the trailing stops and stop loss prices, right?

CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Indicators; namespace WealthScript9 {    public class MyStrategy : UserStrategyBase {       public override void Initialize(BarHistory bars) {          //--- Graphics ---          PlotStopsAndLimits(dotSize: 3); } public override void Execute(BarHistory bars, int idx) {          //=== Exit ===          Position p = LastOpenPosition;          if(p != null)          {             // Stop Loss             ClosePosition(p, OrderType.Stop, 0.8*p.EntryPrice, "Stop Loss");             if (p.IsOpen)             {             // Trailing Stop                CloseAtTrailingStop(p, TrailingStopType.PercentC, 25, "Trailing Stop");             }          }                    //===Filter===     // One position per symbol     if (HasOpenPosition(bars, PositionType.Long)) return;         //===Entry===     if(bars.Close[idx] < 100) return;         //===Transaction===          Transaction t = PlaceTrade(bars, TransactionType.Buy, OrderType.Market, 0.0, "Long Entry"); } } }
0
Glitch8
 ( 7.81% )
- ago
#18
All signals are “filled” on the following bar.
0
pmbf8
- ago
#19
Post deleted.
0
Cone8
 ( 24.56% )
- ago
#20
Sure, but what happens if you use a Position Tag, in orther words, a group code?

QUOTE:
// try this for the entry
Transaction t = PlaceTrade(bars, TransactionType.Buy, OrderType.Market, 0.0, 123, "Long Entry");

That said, and in my opinion, the default -1 "group code" is still the same for the same Position. IMHO, It should not be required to use a group code to get this pruning behavior for the same position. In what world would you place 2 stop orders to exit the same position and get the least restrictive. That's not realistic.
0
Glitch8
 ( 7.81% )
- ago
#21
Using the pruning logic for coded strategies with unassigned group coded (-1) unfortunately breaks several preexisting coded strategies that maintain multiple positions per symbol.
0

Reply

Bookmark

Sort