A couple of concepts that need explaining.
The procedure is based on the concept of a "Trading Day" as defined by the trader or strategy. The user will set a Reset Time (hh:mm) which acts as the beginning of the trading day. The procedure keeps track of the beginning and ending of the current trading day based on the reset time. For example, if it is 2011-12-07 14:00 (server time) and the reset time is 13:00, the procedure will consider the trading day to have just begun an hour earlier and last for another 23 hours. Mondays and Fridays are a special case in that the trading day can possibly span the weekend. The calculation looks for the previous/next
trading day.
At the start of a Trading Day, the procedure captures the current account balance for use in percent changes to the account. In the case when the EA isn't actually watching at the time of the Trading Day start, account balance will be calculated by taking the then current account balance and subtracting the OrderProfit() value for trades that closed after the daily reset. This subtraction is done without concern for MagicNumber or Symbol settings and ignores any open trades.
The DFTDUseEquity setting only impacts the calculation of balance gain/loss, pip gain/loss and percent gain/loss. Winning/Losing/Max trades must be closed trades.
The action DFTDUseTightStop currently just turns on TrailingStops. You will need to set the appropriate stop in the original setting for TrailingStops.
All the *Loss settings expect you to enter the values as positive numbers. You "lose 80 pips", you don't "lose -80 pips".
For the ADR test, the daily range that is being compared to the calculated ADR is the range of the actual D1 bar that we are on, not the Trading Day defined by the Reset Time. I did it this way because the market is looking at the daily bar, and doesn't care about your trading day.
Instead of using bool variables to determine if we want to use one of the specific criterion, it just checks for non-zero values. So if you set it to something, it will use it. There is an overall setting to use DoneForTheDay(), bool UseDoneForTheDay, for when it is called in IsTradingAllowed()
It's also important to note that much of the logic in the procedure depends on the trade history covering the trading day. If the trades don't appear in the trading history, the procedure can't know about them and won't act on them. This is of particular importance at the change of a day/week/month since those are common history filter settings.
I have added a blank procedure StrategyDoneForTheDay() that is called at the end of DoneForTheDay() to allow for a non-generic test. It currently just returns false.
For the coders out there, I've added a new procedure called SaveOrder() which saves the current selected order as well as the TicketNo. It looks like this.
Code: Select all
#define push 1
#define pop 2
#define save 1
#define restore 2
void SaveOrder(int cmd) {
static int ordernumber=0;
static int ticketno=0;
if (cmd == push) {ordernumber = OrderTicket(); ticketno = TicketNo; }
if (cmd == pop) {OrderSelect(ordernumber,SELECT_BY_TICKET); TicketNo = ticketno;}
GetLastError(); //ignore errors
}
I make a call to SaveOrder(push) before making any OrderSelect() calls and when I am done I call SaveOrder(pop). This way I can be sure I haven't unexpectedly changed the state for some other code down the road. You can also use SaveOrder(save) / SaveOrder(restore) if you don't want to think in terms of stacks. I don't know if this is necessary, but I feel better using it.
I also added code to calculate the ADR. It is:
Code: Select all
double CalculateADR(int i, int Days) {
double sum=0;
double cnt=0;
int offset=0;
int day=0;
while (cnt<Days) {
offset = iBarShift(NULL,PERIOD_D1,Time[i]);
//ignore Sundays
if(TimeDayOfWeek(iTime(NULL,PERIOD_D1,day+1+offset)) != 0) {
sum += iHigh(NULL,PERIOD_D1,day+1+offset)-iLow(NULL,PERIOD_D1,day+1+offset);
cnt++;
}//if(TimeDayOfWeek(iTime(NULL,PERIOD_D1,day+1+offset)) != 0)
day++;
}
return(sum/cnt);
}
Finally, I made a change so that multiplier is a global variable -- currently it is defined in init(). I use it to turn points into pips in my code and didn't want to recalculate it. If Steve doesn't want to do that, I'll probably add a function int GetMultiplier().
OK, the actual code to calculate DoneForTheDay() is attached. I have not yet written the notification code, so the only options when you are done for the day is to close all orders, close all pending orders, and/or turn on trailing stops.
I tried to make the code as insulated as possible so that it would be easier to add to existing bots. That being said, I'm not happy with the information we can get from it in it's current state. For example, I would like to be able to report in DisplayUserFeedback() why we have stopped and when we will be restarting.
Anyhow, I was able to test various scenarios last week while the market was still open, but I am sure there are some bugs left in there. No doubt some very obvious ones that I can't see because I typed them in. I will continue to test this week and will update any fixes I make.
Please take a look at the code and let me know if anything jumps out. If you'd like to try things out in DEMO or Strategy Tester, feel free. I would not use it in live, however.
George
You do not have the required permissions to view the files attached to this post.