Support Board
Date/Time: Fri, 31 Jan 2025 03:22:26 +0000
Post From: bar backtest: is there a way to adjust the line-delay/time-to-order-in-force delay?
[2019-03-30 18:12:43] |
uM8137 - Posts: 183 |
https://www.dropbox.com/s/aep4v0ufwedtbdu/Screenshot%202019-03-30%2018.48.12.png?dl=0 http://www.sierrachart.com/image.php?Image=1553968643225.png In this example, I'm trying to understand how this 1min bar simulation fill is not a bug in the backtest fill logic. In the above chart, the right side orange down-arrow is a SELL simulation order at 2019-March-25, 04:25:00am UTC time on F.US.EPM19. The trade log (visible on the dropbox screenshot) clearly shows a fill at 2796.50 USD. That price was not observed at all during the 2019-March-25 04:25:00 UTC bar! The *only* point when price was near that fill was one minute earlier at 4:24. I'm getting "prices from the past". These awarded fills over a minute stale, and very off. The simulation was done from 2019-March-25T04:00:00 UTC - 2019-March-25T23:55:00 UTC, and the trading system study to reproduce these trades here is a very simple saw tooth test pattern that buys and sells alternatively every 5 minutes. It is pasted below. The data feed for this /ES contract is from CQG, but SierraChart input 1min bars should be identical. Please let me know if there is any reasonable interpretation. Getting prices from the past is equivalent to predicting on future data. A huge bugaboo. #include "sierrachart.h" SCDLLName("sawtooth_buy_0_and_sell_5") #define vv(args...) \ do {SCString Msg; Msg.Format(args); sc.AddMessageToLog(Msg, 0);} while(0); void showArray(SCStudyInterfaceRef sc, const char* name, SCFloatArray arr, int beg, int end) { for (int i = beg; i < end; i++) { vv("%s[%i]=%g", name, i, arr); } } int getCurrentPos(SCStudyInterfaceRef sc) { s_SCPositionData pos; sc.GetTradePosition(pos); int curPos = pos.PositionQuantity; return curPos; } SCSFExport scsf_Saw_Buy_on_0_and_Sell_on_5(SCStudyInterfaceRef sc) { SCInputRef MaxContract = sc.Input[0]; // Subgraphs SCSubgraphRef Buy = sc.Subgraph[0]; SCSubgraphRef Sell = sc.Subgraph[1]; SCSubgraphRef Debug = sc.Subgraph[2]; // above are recommended, below are those we took based // on our state of already being maxed or not. SCSubgraphRef TakenBuy = sc.Subgraph[3]; SCSubgraphRef TakenSell = sc.Subgraph[4]; int BarStartTime = sc.BaseDateTimeIn.TimeAt(sc.Index); int minuteOfDay = BarStartTime / 60; int sec = BarStartTime % 60; // Set configuration variables if (sc.SetDefaults) { sc.SupportReversals = 1; // Unmanaged Automated Trading sc.AllowMultipleEntriesInSameDirection =1; sc.AllowOppositeEntryWithOpposingPositionOrOrders =1; // default is true. sc.CancelAllOrdersOnEntriesAndReversals =0; sc.AllowEntryWithWorkingOrders =1; sc.AllowOnlyOneTradePerBar =0; sc.MaximumPositionAllowed = 10; sc.GraphName = "sawtooth_buy_at_0_sell_at_5"; sc.StudyDescription = "every minute after minute, buy on the 0, sell on the 5 timestamps"; sc.GraphRegion = 0; MaxContract.Name = "MaxContract"; MaxContract.SetInt(1); MaxContract.SetIntLimits(1, 100000); MaxContract.SetDescription("The number of contracts transact"); Buy.Name = "Buy"; Buy.PrimaryColor = COLOR_GREEN; Buy.DrawStyle = DRAWSTYLE_ARROWUP; Buy.LineWidth = 2; Sell.Name = "Sell"; Sell.DrawStyle = DRAWSTYLE_ARROWDOWN; Sell.PrimaryColor = COLOR_RED; Sell.LineWidth = 2; Debug.Name = "Debug"; //Debug.DrawStyle = DRAWSTYLE_ARROWDOWN; Debug.PrimaryColor = COLOR_PURPLE; Debug.LineWidth = 1; TakenBuy.Name = "TakenBuy"; TakenBuy.PrimaryColor = COLOR_YELLOW; TakenBuy.DrawStyle = DRAWSTYLE_ARROWUP; TakenBuy.LineWidth = 3; TakenBuy.DrawZeros = false; TakenSell.Name = "TakenSell"; TakenSell.DrawStyle = DRAWSTYLE_ARROWDOWN; TakenSell.PrimaryColor = COLOR_ORANGE; TakenSell.LineWidth = 3; TakenSell.DrawZeros = false; // Auto Looping On sc.AutoLoop = 1; // During development only set to 1 sc.FreeDLL = 1; // want end times of bars too. sc.MaintainAdditionalChartDataArrays = 1; // Must return return; } // Get the date int BarDate = sc.BaseDateTimeIn.DateAt(sc.Index); // Get the End DateTime at the current index. SCDateTime BarEndDateTime = sc.BaseDataEndDateTime[sc.Index]; int BarEndTime = BarEndDateTime.GetTime(); // always returns 0. huh. why? // bounce saw-tooths between 0 and 5. we will buy at 0, and sell at 5. int saw = minuteOfDay % 10; if (saw >= 5) { saw = 10 - saw; // make a saw-tooth pattern. } double close = sc.Close[sc.Index]; double hi = sc.BaseData[SC_HIGH][sc.Index]; double lo = sc.BaseData[SC_LOW][sc.Index]; double op = sc.BaseData[SC_OPEN][sc.Index]; //vv("at sc.Index %i, close is %f", sc.Index, close); Debug[sc.Index] = 2800 + saw; // 2800 so we are visible on the /ES graph 2019 March 25 int maxcon = MaxContract.GetInt(); sc.MaximumPositionAllowed = 10; Buy[sc.Index] = 0; Sell[sc.Index] = 0; int curPos = getCurrentPos(sc); if (saw == 5) { // time to sell Sell[sc.Index] = sc.High[sc.Index] + sc.TickSize; if (curPos > -maxcon) { // room to sell int qty = maxcon; vv("at simtm %02i:%02i:%02i at sc.Index %i, saw is %i, time to sell, curPos=%i, orderQty=%i; close=%f, op=%f, hi=%f, lo=%f, sc.LastTradePrice=%f", minuteOfDay / 60, minuteOfDay % 60, sec, sc.Index, saw, curPos, qty, close,op,hi,lo,sc.LastTradePrice); // actual sell order entry. // Create an s_SCNewOrder object. // Automated Trading From an Advanced Custom Study: s_SCNewOrder Structure Members s_SCNewOrder NewOrder; // qty == 0 for BuyExit or SellExit means flatten the position to zero. NewOrder.OrderQuantity = qty; NewOrder.OrderType = SCT_ORDERTYPE_MARKET; NewOrder.TimeInForce = SCT_TIF_GTC; NewOrder.Price1 = 0; NewOrder.Price2 = 0; int Result = (int)sc.SellEntry(NewOrder); if (Result > 0) { //If there has been a successful order entry, then draw an arrow at the high of the bar. TakenSell[sc.Index] = sc.High[sc.Index] + 2*sc.TickSize; int updatedPos = getCurrentPos(sc); vv("at simtm %02i:%02i:%02i successful sell size=%i, at BarStartTime=%i, Result=%i, prevPos=%i, updatedPos=%i", minuteOfDay / 60, minuteOfDay % 60, sec, qty, BarStartTime, Result, curPos, updatedPos); } else { //order error int updatedPos = getCurrentPos(sc); vv("at simtm %02i:%02i:%02i failed to sell size=%i, at BarStartTime=%i, Result=%i, prevPos=%i, updatedPos=%i", minuteOfDay / 60, minuteOfDay % 60, sec, qty, BarStartTime, Result, curPos, updatedPos); sc.AddMessageToLog(sc.GetTradingErrorTextMessage(Result), 0); } } } if (saw == 0) { // time to buy Buy[sc.Index] = sc.Low[sc.Index] - sc.TickSize; if (curPos < maxcon) { // room to buy int qty = maxcon; vv("at simtm %02i:%02i:%02i at sc.Index %i, saw is %i, time to buy, curPos=%i, orderQty=%i; close=%f, op=%f, hi=%f, lo=%f, sc.LastTradePrice=%f", minuteOfDay / 60, minuteOfDay % 60, sec, sc.Index, saw, curPos, qty, close,op,hi,lo, sc.LastTradePrice); // Create an s_SCNewOrder object. s_SCNewOrder NewOrder; NewOrder.OrderQuantity = qty; NewOrder.OrderType = SCT_ORDERTYPE_MARKET; NewOrder.TimeInForce = SCT_TIF_GTC; NewOrder.Price1 = 0; NewOrder.Price2 = 0; int Result = (int)sc.BuyEntry(NewOrder); if (Result > 0) {//If there has been a successful order entry, then draw an arrow at the low of the bar. TakenBuy[sc.Index] = sc.Low[sc.Index] - 2*sc.TickSize; int updatedPos = getCurrentPos(sc); vv("at simtm %02i:%02i:%02i successful buy size=%i, at BarStartTime=%i, Result=%i, prevPos=%i, updatedPos=%i", minuteOfDay / 60, minuteOfDay % 60, sec, qty, BarStartTime, Result, curPos, updatedPos); } else { //order error int updatedPos = getCurrentPos(sc); vv("at simtm %02i:%02i:%02i failed to buy size=%i, at BarStartTime=%i, Result=%i, prevPos=%i, updatedPos=%i", minuteOfDay / 60, minuteOfDay % 60, sec, qty, BarStartTime, Result, curPos, updatedPos); sc.AddMessageToLog(sc.GetTradingErrorTextMessage(Result), 0); } } } } Date Time Of Last Edit: 2019-03-30 18:20:25
|