Login Page - Create Account

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