I have this trailing stop code I've used that when spot checking seems to work as expected. However, today in live trading I noticed my actual sell point was higher than the backtest. The backtest sold at the initial stop instead of having that trailing stop level rise up to the low of the previous day as expected.
Ticker: GFS
Entry Date: 12/9/22
Exit Date: 12/16/22
Initial Stop Level: $60.43
Trailing Stop was at $61.80 on 12/15/22
Gap over my stop, so expected to sell the open: $61.32
Instead it sold lower at my initial stop: $60.43
I'm not clear why the trailing stop level of 12/15/22 was not used in this instance.
/*
SIMPLE DC CHECK IF IN POSITION
This strategy looks for pullbacks in uptrends (i.e. Flags) anticipating that they will break bullish and continue the trend.
Simplified and added explanation to the code to post to Amibroker Forum for assistance in adding a Filter so that stocks
that already have a buy signal won't show up.
*/
#include_once "Formulas\Norgate Data\Norgate Data Functions.afl"
// ==========================================================================================
// === TRADING SYSTEM PARAMETERS ===
// ==========================================================================================
SetOption("InitialEquity", 100000);
SetTradeDelays( 0, 0, 0, 0 ); // Trades intra-day with stop orders that are placed the night before
SetOption("AllowPositionShrinking", True);
RoundLotSize = 1; // Disallow fractional share purchasing
SetOption("MaxOpenPositions", 12); // Maximum number of open positions: 30
SetOption("AccountMargin", 50); // 100 = Cash account, 75 = Using half available margin, 50 = 2X margin
// ==========================================================================================
// === RISK MANAGEMENT & POSITION SIZING ===
// ==========================================================================================
// === INITIAL (MAX) STOP LOSS ====
SetOption("ActivateStopsImmediately", False); // Stop behavior defined by trailing stop below - IS THIS THE CORRECT SETTING?
stopLoss = 4;
ApplyStop(stopTypeLoss, stopModePercent, stopLoss, ExitAtStop = 1, True); // Allows for intraday stops even with TradeDelays
// === POSITION SIZING ===
PositionRisk = 1.0; // Percentage of account at risk if wrong
PosSize = 100 * PositionRisk / stopLoss; // Position size as a percent of equity for a given stop level
SetPositionSize(PosSize, spsPercentOfEquity);
// ==========================================================================================
// === PLOT CHART & INDICATORS ===
// ==========================================================================================
// Chart
SetChartOptions(0,chartShowArrows|chartShowDates);
_N(Title = StrFormat("{{NAME}} - " + FullName() + " | {{INTERVAL}} {{DATE}}: Open %g, Hi %g, Lo %g, Close %g (%.1f%%) {{VALUES}}", O, H, L, C, SelectedValue( ROC( C, 1 ) ) ));
Plot( C, "Close", ParamColor("Color", colorDefault ), styleNoTitle | ParamStyle("Style") | GetPriceStyle() );
// Donchain Channels
pdsU=4; // High channel lookback
pdsL=5; // Low channel lookback
DonchianUpper =HHV(H,pdsU);
DonchianLower = LLV(L,pdsL);
Plot(DonchianUpper,"DU",/*color*/ 2,styleDashed);
Plot(DonchianLower,"DL",/*color*/ 2, styleDashed);
// Moving Averages (use Nz to avoid lack of history screening strong stocks out)
sma50 = Nz(ma(Close, 50));
sma150 = Nz(ma(Close, 150));
Plot(sma50,"50MA",/*color*/ 3, styleNoTitle);
Plot(sma150,"150MA",colorWhite, styleNoTitle);
// ==========================================================================================
// === FLAG DETECTION === https://forum.amibroker.com/t/high-tight-flag-power-play-detection/21895
// ==========================================================================================
flagLookback = 13; // Number of bars to look back for top of flag
PoleHigh = HHV(H, flagLookback); // Highest high within the lookback period
PHBar = HHVBars(H, flagLookback); // How many bars back the pivot high bar occurred
FlagLow = LLV(L, PHBar); // Flag Low is the lowest price since PoleHigh
FLBar = LLVBars(L, PHBar); // How many bars back is the low point of the flag
flagPoleLookback = 21; // Look ~20 days prior to the pole for shorter term flags
PoleLow = Ref(LLV(L, flagPoleLookback), -PHBar-1);
PLBar = Ref(LLVBars(L, flagPoleLookback), -PHBar-1);
// Flag characteristics
PoleLength = Nz((PoleHigh - PoleLow)/PoleLow); // Percentage move that forms the flag pole
FlagDepth = Nz((PoleHigh - FlagLow)/PoleHigh); // Percentage retracement off the highs
FlagRatio = Nz(FlagDepth/PoleLength); // Flag-to-Pole ratio
// Flag Requirements
Flag =
PoleLength > 0.10 AND // Must have had at least a 10% rise
FlagDepth < 0.25 AND // Flag must not have come down more than 25% off the flag pole top
FlagRatio < 0.5 AND // Flag cannot have retraced more than 50% of the initial move
PHBar > 1; // Flag must be more than two candles (will trigger at third)
PlotShapes(Flag*shapeCircle, colorBlue, 0, Low);// Plot dots indicating consolidation meets requirements
// ==========================================================================================
// === FILTERING AND SCREENING CRITERIA ===
// ==========================================================================================
// Set other non-chart visible variables
adr = MA((High - Low) / abs(Low) * 100, 21);
vol = MA(Volume, 20);
inTrade = 0;
tradeFilter =
DonchianUpper > sma50 AND // when the buy is triggered stock is above the 50 SMA
Close > sma150 AND // Only look at stocks in Stage 2 uptrends as defined by the 30 week/150 day MA (Stain Weinstein)
Flag == True AND // The screen the night before should show the stock being in a proper consolidation
adr >= 4.0 AND // Look for stocks with higher average daily ranges
vol >= 300000 AND // Filter out illiquid stocks
Close >= 1.5; // Filter out penny stocks
// Filter Ribbon - add for better visibility when the strategy thinks a security is viable/not viable
Color = IIf(tradeFilter,0,1);
Plot ( 1, "", Color, styleArea |styleNoLabel | styleOwnScale , 0, 100);
// ==========================================================================================
// === BUY & SELL LOGIC ===
// ==========================================================================================
// === NORGATE VARIABLES TO AVOID DESLISTED STOCKS ===
dt = DateTime();
bi = BarIndex();
delistedSecurity = Nz(DateTimeDiff(dt, GetFnData("DelistingDate")) >= 0);
barsBeforeDelisting = LastValue(ValueWhen(dt == GetFnData("DelistingDate"), bi, 1)) -bi;
OnSecondLastBarOfDelistedSecurity = barsBeforeDelisting == 1;
OnLastTwoBarsOfDelistedSecurity = barsBeforeDelisting == 2 OR barsBeforeDelisting == 1 OR delistedSecurity;
// === BUY AND SELL LOGIC
Buy = (High > Ref(DonchianUpper, -1)) AND Ref(tradeFilter, -1)
AND NOT OnLastTwoBarsOfDelistedSecurity;
//AND (NorgateIndexConstituentTimeSeries("$SPX") OR NorgateIndexConstituentTimeSeries("$NDX")); // Unhide to for historical testing
BuyPrice=Max(Ref(DonchianUpper, -1), Open);
// == TRAILING STOP ===
// Initialize variables
Sell=0;
trailArray = Null;
trailStop = 0;
inTrade = 0;
//Trailing stop logic
for( i = 0; i < BarCount; i++ )
{
if( trailstop == 0 AND Buy[ i ] )
{
trailstop = BuyPrice[i] * (1- stopLoss[i]/100); // Use stoploss level
inTrade = 1;
}
else Buy[i] = 0; // remove excess buy signals
if( trailstop > 0 AND Low[ i ] < trailstop AND (NOT Buy[i] OR (Buy[i] AND Close[i] < Open[i]))) // adding Buy check ensures you don't stop out on the breakout candle when the stop is within it unless it's a bearish candle
{
Sell[ i ] = 1;
SellPrice[ i ] = Min( Open[ i ], trailstop );
trailstop = 0;
inTrade = 0;
}
if( trailstop > 0 )
{
trailStop = Max(DonchianLower[i], trailStop);
trailARRAY[ i ] = trailstop;
}
}
Plot(trailArray, "Trailing Stop Level", colorRed, styleThick);
// Overwrite sells to ensure not holding deslisted securities
Sell = Sell OR OnSecondLastBarOfDelistedSecurity;
// Plot arrows indicating trades
PlotShapes(IIf(Buy, shapeUpTriangle, shapeNone),colorLime, 0, L, Offset=-50);
PlotShapes(IIf(Sell, shapeDownTriangle, shapeNone),colorRed, 0, H, Offset=-50);
// ==========================================================================================
// === POSITION SCORE FOR RANKING STOCKS ===
// ==========================================================================================
PositionScore = Ref(vol, -1); // sort by most liquid stocks
// ==========================================================================================
// === EXPLORATION / SCANNING COLUMNS ===
// ==========================================================================================
Filter = tradeFilter AND NOT inTrade; // Filter out stocks that don't have current buy signals
AddTextColumn(FullName(), "Name");
AddTextColumn( SectorID(1), "Sector");
AddTextColumn(IndustryID(1), "Industry Name");
AddColumn(adr, "ADR", 1.1);
AddColumn(vol, "Avg. Volume", 1.0);
AddColumn(inTrade, "In Trade?", 1.0);
// Add other things to title bar I want to see
Title += StrFormat(" | ADR: %.1f", ADR); // Add ADR to the title of charts