MIH8
- ago
Hello everyone.

Today i have a little puzzle to solve which is not transparent for me. Looking at the sample code the idea is to place an order on the first bar of a day.

CODE:
      public override void Execute(BarHistory bars, int idx)       {          if (bars.IsLastBarOfDay(idx))          {             limit = getLimit(ref bars,idx);             tradecount = 0;             return;          }          if (!HasOpenPosition(bars, PositionType.Long))          {             if (tradecount == 0)             {                Transaction t = PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, limit, -1, "Buy");             }          }          else          {             if (bars.IsLastBarOfDay(idx + 1))             {                tradecount++; // This is only relevant if there is an early exit with a different condition, e.g. TP,SL                PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose, 0, "SELL");             }          }       }


When I look at the position result and the charts, there are trades with 79 bars for a 5-minute scale. Both pictures show that the trade is placed on the signal bar, although I immediately return from it. If i adapt the code for IsFirstBarOfDay(), I still get this picture. I could not find any problems with the data. The first bar is always there as far as I have checked.





I guess i overlook something basic. Do you have a hint?
0
817
16 Replies

Reply

Bookmark

Sort
MIH8
- ago
#1
I changed the snippet a little bit because at least one thing was a mistake. Returning from the signal bar was not intended as i need to place the signal for the next bar :-).

CODE:
      public override void Execute(BarHistory bars, int idx)       {          if (bars.IsLastBarOfDay(idx))          {             limit = getLimit(ref bars,idx);             tradecount = 0;          }          // Place trade if there is no open position OR if the next candle is the opening candle          // Because there even can be a signal with an open position the selling leg needs to be          // checked in any case (removed if..else)          if (!HasOpenPosition(bars, PositionType.Long) || bars.IsLastBarOfDay(idx))          {             if (tradecount == 0)             {                Transaction t = PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, limit, -1, "Buy");             }          }          // ALWAYS check for open positions to sell          if(HasOpenPosition(bars, PositionType.Long))          {             if (bars.IsLastBarOfDay(idx + 1))             {                tradecount++; // relevant for different exit conditions before market close                PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose, 0, "SELL");             }          }       }


I am now getting entries for the 0935 entry bars, but not for all entries. There are still trades whose entries are around 1600 although a 0935 bar follows. The new code raises a new question. There are trades that have exactly 156 bars, but these trades should also stop after 78 bars because there are 1600 bars for these trades before that.
0
Cone8
 ( 7.27% )
- ago
#2
CODE:
IS: if (!HasOpenPosition(bars, PositionType.Long) || bars.IsLastBarOfDay(idx)) SHOULD BE: if (!HasOpenPosition(bars, PositionType.Long) && bars.IsLastBarOfDay(idx))


Also:
CODE:
IS: if (bars.IsLastBarOfDay(idx + 1)) SHOULD BE: if (idx < bars.Count - 1 && bars.IsLastBarOfDay(idx + 1))
0
MIH8
- ago
#3
CODE:
CODE: IS: if (!HasOpenPosition(bars, PositionType.Long) || bars.IsLastBarOfDay(idx)) SHOULD BE: if (!HasOpenPosition(bars, PositionType.Long) && bars.IsLastBarOfDay(idx))


Your suggestion is not what i want in this case. I need to place the signal at any 5-minute bar because otherwise no signal is generated for all bars within the day. I additionaly want to place a signal even if there is an open position for the last bar of the day. That is why OR seems to be ok for me.

CODE:
CODE: IS: if (bars.IsLastBarOfDay(idx + 1)) SHOULD BE: if (idx < bars.Count - 1 && bars.IsLastBarOfDay(idx + 1))


This is correct, but i check the index when entering the execution function. I did not do it for the code snippet.

This topic is not solved. Thanks for your help.
0
Cone8
 ( 7.27% )
- ago
#4
okay, I thought you only wanted to get a trade on the opening bar.

The code is correct for what you said but since you're not showing all of it, I'll retire from the conversation.
0
MIH8
- ago
#5
I now replaced the methods with DateTime conditions being aware that it can have a different behaviour than the methods. I still find trades where the exit didn't work.

CODE:
      public override void Execute(BarHistory bars, int idx)       {          if(idx + 1 >= bars.Count) return; // data limitation                    if (bars.DateTimes[idx].GetTime() == 1600)          {             limit = getLimit(ref bars,idx);             tradecount = 0;          }          // Place trade if there is no open position OR if the next candle is the opening candle          // Because there even can be a signal with an open position the selling leg needs to be          // checked in any case (removed if..else)          if (!HasOpenPosition(bars, PositionType.Long) || bars.DateTimes[idx].GetTime() == 1600)          {             if (tradecount == 0)             {                Transaction t = PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, limit, -1, "Buy");             }          }          // ALWAYS check for open positions to sell          if(HasOpenPosition(bars, PositionType.Long))          {             if (bars.DateTimes[idx].GetTime() == 1555)             {                tradecount++; // relevant for different exit conditions before market close                PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose, 0, "SELL");             }          }       }




What do you mean when you say i do not show everything? (Seriously?, here is no more information. Why don't you simply run it and see what happens?)

CODE:
namespace WealthScript2 {     public class KJI : UserStrategyBase {       private double limit = -1;       private int tradecount;       public double getLimit(ref BarHistory bars,int idx)       {          return bars.Close[idx] * 0.98;       }              public override void Execute(BarHistory bars, int idx)       {          if(idx + 1 >= bars.Count) return; // data limitation                    if (bars.DateTimes[idx].GetTime() == 1600)          {             limit = getLimit(ref bars,idx);             tradecount = 0;          }          // Place trade if there is no open position OR if the next candle is the opening candle          // Because there even can be a signal with an open position the selling leg needs to be          // checked in any case (removed if..else)          if (!HasOpenPosition(bars, PositionType.Long) || bars.DateTimes[idx].GetTime() == 1600)          {             if (tradecount == 0)             {                Transaction t = PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, limit, -1, "Buy");             }          }          // ALWAYS check for open positions to sell          if(HasOpenPosition(bars, PositionType.Long))          {             if (bars.DateTimes[idx].GetTime() == 1555)             {                tradecount++; // relevant for different exit conditions before market close                PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose, 0, "SELL");             }          }       }    } }
0
- ago
#6
QUOTE:
What do you mean when you say i do not show everything?

Hasn't getLimit() appeared in Post #5 only?🤷‍♂️
0
MIH8
- ago
#7
Come on, this is nitpicking. Please, i really don't want a heated debate for nothing. Can we simply agree to look into the issue. So far i simply provided information which could be used to do some analysis. If you need more information i will try to provide some. If it is a user issue, let me know what i am doing wrong. Thanks.
0
MIH8
- ago
#8
I did some more research and added some debug code to the previous snippet.

CODE:
public class KJI : UserStrategyBase {       private double limit = -1;       private int tradecount;       private String exitDate;       private DateTime unknownDate = new DateTime(1900,01,01);              public double getLimit(ref BarHistory bars,int idx)       {          return bars.Close[idx] * 0.98;       }              public override void Execute(BarHistory bars, int idx)       {          if(idx + 1 >= bars.Count) return; // data limitation                    if (bars.DateTimes[idx].GetTime() == 1600)          {             limit = bars.Close[idx] * 0.98; //getLimit(ref bars,idx);             tradecount = 0;             exitDate = unknownDate.ToString();             // Debug functionality: forecast if exit bar is available.             // Check from the next bar which should be a new day             // Because the day has maximum 78 bars for the 5-min scale,             // limit the loop on 78 bars and the general number of bars which are available             for (int i = idx + 1; i < idx + 79 && i < bars.Count - 1; i++)             {                // "i" belongs to new day as "idx+1". If date is after the trading day                // just stop to search the relevant exit date. It is not available.                if(bars.DateTimes[i].Date > bars.DateTimes[idx+1].Date)                   break;                                // If we find an exit bar (1600) for the trading day store the date                // to provide it in the position metrics                if (bars.DateTimes[i].GetTime() == 1600)                {                   // found exit candle for current day                   exitDate = bars.DateTimes[i].ToString();                   //WriteToDebugLog(bars.DateTimes[idx] + " " + bars.DateTimes[idx+1] + " " + exitDate);                   break;                }             }          }          // Place trade if there is no open position OR if the next candle is the opening candle          // Because there even can be a signal with an open position the selling leg needs to be          // checked in any case (removed if..else)          if (!HasOpenPosition(bars, PositionType.Long) || bars.DateTimes[idx].GetTime() == 1600)          {             if (tradecount == 0)             {                Transaction t = PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, limit, -1, "Buy");             }          }          // ALWAYS check for open positions to sell          if(HasOpenPosition(bars, PositionType.Long))          {             if (bars.DateTimes[idx].GetTime() == 1555)             {                tradecount++; // relevant for different exit conditions before market close                PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose, 0, exitDate);             }          }       }


Here are the results. Do you see the patterns ....





Result 1
One reason is the shortened trading days (which basically also have fewer bars),
e.g. christmas and i guess Black Friday which can be seen here.

Result 2
Another issue is, that the relevant exit bar is not available for a normal day. In such a case i suggest to use the last bar that is availabe of the day (instead of the closing bar).

Maybe there are more things to find but i hope you will look into it. I think i provided a lot information that you can work with.

Michael
0
Glitch8
 ( 13.30% )
- ago
#9
In your chart of BX with the red X on it, the bar with the red X is indeed 1600, but what is the timestamp of the bar immediately prior to that? I'd like to get a copy of that data file if possible, email to support@wealth-lab.com, so I can reproduce.
0
MIH8
- ago
#10
The timestemp is 1555 before that. If you have five minute data available on your side you should be able to reproduce it with many symbols and the given code.

There is a difference between the snippets. The last one does not use the IsLastBarOfDay() method. In the course I have replaced it with DateTimes. I am aware that there are differences. However, neither leads to clean solutions.

The lastest code snippet shows where to find trades that failed to hit the exit. You can simply copy and paste it, compile and run it.

CODE:
2014-04-11 15:45:00,18.2969,18.3121,18.2755,18.3121,305182 2014-04-11 15:50:00,18.3091,18.3304,18.2999,18.3121,357974 2014-04-11 15:55:00,18.3121,18.4037,18.306,18.3976,982618 2014-04-11 16:00:00,18.3976,18.3976,18.3671,18.3671,438042 2014-04-11 16:10:00,18.3854,18.3854,18.3854,18.3854,164 2014-04-11 16:15:00,18.4342,18.4342,18.3732,18.3732,2785 2014-04-11 16:20:00,18.3671,18.3671,18.1351,18.1351,9502 2014-04-11 16:25:00,18.2877,18.2999,18.2816,18.2816,17169


I will send you the file anyway, ... , done.
0
Glitch8
 ( 13.30% )
- ago
#11
Thanks, I'll dive in tomorrow!
1
Glitch8
 ( 13.30% )
- ago
#12
When I run this strategy on the data file you sent I do get the exit in the correct place.

CODE:
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Data; using WealthLab.Indicators; using System.Collections.Generic; namespace WealthScript2 { public class MyStrategy : UserStrategyBase {       private double limit;       private int tradecount;       public double getLimit(ref BarHistory bars, int idx)       {          return bars.Close[idx] * 0.98;       }              public override void Execute(BarHistory bars, int idx)       {          if (bars.IsLastBarOfDay(idx))          {             limit = getLimit(ref bars, idx);             tradecount = 0;             return;          }          if (!HasOpenPosition(bars, PositionType.Long))          {             if (tradecount == 0)             {                Transaction t = PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, limit, -1, "Buy");             }          }          else          {             if (bars.IsLastBarOfDay(idx + 1))             {                tradecount++; // This is only relevant if there is an early exit with a different condition, e.g. TP,SL                PlaceTrade(bars, TransactionType.Sell, OrderType.MarketClose, 0, "SELL");             }          }       } } }


0
MIH8
- ago
#13
Do you have the possibility to load a dataset with, let's say, 200 (100) symbols of 5 minute data for a data range of 10 (5) years. When you sort the position results for the exit signals it will point you to many entries of this kind. (using the latest version of the strategy sample code)

1. I know you want to simplify the frame you examine. It is ment to find some issues which can be produced and reproduced on your side then and has the same symtoms.

2. Then you can dive into a single problem (if it is related to a single item). Maybe it has something to do some general processing of the data, the data itself or any other stuff we do not have on the radar (yet).

Thank you.

Edit: I just see you did not use the latest strategy. Maybe you give it a try. (#8)
0
Glitch8
 ( 13.30% )
- ago
#14
I ran it on the updated strategy with the same result.

Are you getting the correct exit on a single symbol run? And the problem is only occurring when running against multiple symbols?
0
MIH8
- ago
#15
Hello Glitch, Hello Team. I'll have to come back to this issue later. Thanks so far.
At the moment I have some other questions that should be easy for you to answer.

1. If I have signals from the close of the previous day (intraday), I assume that the signals would have been transmitted at the end of the previous day if the strategy had been active. (I read about the "FinalOrder" in the help).

As the strategy has not been active so far, I can set the signals manually and they are transmitted to the Order Manager with the status "FinalOrder". Are they now transferred before the opening? (What has to be done so that the orders are available at the broker when the market opens (0930) and not only when the first bar is build)?

2. Another question is about ExecuteSessionOpen(). I could not find any restriction that it only works for EOD. Does it also work for intraday strategies?

3. Can you confirm that in both scenarios the use of Pre/Post Market data has to be disabled?

4. Can you tell me a way to run a strategy at 0930 without having pre-market enabled (user specific market)?

These questions aim to place orders before the market opens (previously known price, e.g. closing price of the previous day),
or to place the signal within the first bar of the day (sessionOpen), i.e. before the first candle has formed.

A few other questions will come up, the main aim of which is to avoid the Quotes window. The quotes window is able to handle these things,but in combination with intraday strategies it cannot track anything else.

To transform my EOD dip strategy into an intraday strategy I have to get around two hurdles. Firstly, the strategy must already have signals at the opening or generate signals directly at the start of the market at 0930 (such as the Q.T. tool). The second hurdle will be not to send all limit orders to the broker at the same time. Here, too, the Q.T. Tool has an advantage over the Strategy Monitor because only triggered orders are placed. I have ideas, but one step at a time.
0
- ago
#16
QUOTE:
2. Another question is about ExecuteSessionOpen(). I could not find any restriction that it only works for EOD. Does it also work for intraday strategies?

I think so.
0

Reply

Bookmark

Sort