Проблема с тестером?

 
Подскажите, пожалуйста.

Ниже привожу три файла. Первый - эксперт. Второй - индикатор к нему. Третий - включаемый, размещается в директории экспертов. Можно обойтись без него, закомментировав вызов функции "Report".

Индикатор рисует сигналы на покупку и продажу, причем ранее он работал на демо счете, и сигналы экспертом обрабатывались в реальном времени. То есть, вроде бы, тандем "эксперт - индикатор" должны работать нормально.

Но когда запускается тестер, при высоких значениях передаваемого в индикатор параметра (например, больше 150), сделок становится ноль. При том, что сигналы есть, то есть, они есть при работе на демо счете, в реальном времени... График же оптимизации в какой-то момент (после 140) просто идет горизонтально, с 0 сделок.

Либо я что-то напутал в индикаторе - эксперте (подскажите?), либо тестер барахлит.

С уважением,
Кварк

Эксперт:

#include "mylib.mq4"

// ------

extern double dZigzagSize = 148;

// ------

datetime timePrev = 0;

double dInitAmount = 1000.0;
int nNumOfExperts = 5;		// To do: autocalculate number of active experts
int nSlip = 5;

double dProfit = 0;		// old: arrTotals
double dLotSize = 0.1;

double dStopLoss;
double dTrailingStop;
double dTakeProfit;

int nPending = -1;

int nSignal = 0;

bool bUseMm = false;
int nMagic = 0;
bool bReportDone = false;

//////////////////
int init ()
{ 
	if(Symbol() == "EURUSD" && Period() == 60)				// 1616 ($700)
	{
		if(!IsTesting())
			dZigzagSize = 148;
			
		nMagic = 1;
	}
	else if(Symbol() == "EURUSD" && Period() == 15)			// 1115 ($366)
	{
		if(!IsTesting())
			dZigzagSize = 87;
			
		nMagic = 2;
	}
	else if(Symbol() == "EURJPY" && Period() == 15)
	{
		if(!IsTesting())
			dZigzagSize = 50;
		nMagic = 3;
	}

	dStopLoss = dZigzagSize * Point;
	dTrailingStop = dZigzagSize * Point;
	dTakeProfit = 0;
	
	return(0);
}
//////////////////
int deinit()
{
	return(0);
}
////////////////////
int start()
{
	if(AccountFreeMargin() < 500)
		return(0);
		
	Report("Zigzag", nMagic, bReportDone);

	bool bIsBarEnd = false;
	if(timePrev != Time[0]) 
		bIsBarEnd = true;
	timePrev = Time[0];
	
	// ------

	if(bIsBarEnd)
	{
		nSignal = iCustom(NULL, 0, "_Zigzag_Ind", dZigzagSize, 0, 1);
	}
		
	dProfit = 0;
		
	for(int nCnt = 0; nCnt < OrdersTotal(); nCnt++)
	{
		OrderSelect(nCnt, SELECT_BY_POS, MODE_HISTORY);
		if(OrderMagicNumber() == nMagic && OrderType() <= OP_SELL && OrderSymbol() == Symbol())
		{
			dProfit += OrderProfit();
		}
	}   

	int nNumOfOpenedOrders = 0;
	
	for(nCnt = 0; nCnt < OrdersTotal(); nCnt++)
	{
		OrderSelect(nCnt, SELECT_BY_POS, MODE_TRADES);
		if(OrderMagicNumber() == nMagic && OrderSymbol() == Symbol())
		{
			if(OrderType() <= OP_SELL)
				nNumOfOpenedOrders++;
		}
	}

	if(!nNumOfOpenedOrders)
	{
		if((bIsBarEnd && nSignal == -1) || nPending == OP_BUY)
		{
			dLotSize = GetLotSize();

			OrderSend(Symbol(), OP_BUY, dLotSize, Ask, nSlip, 
				Ask - dStopLoss, 0, "Zigzag", nMagic, 0, Aqua);
			return(0);
		}
		else if((bIsBarEnd && nSignal == 1) || nPending == OP_SELL)
		{
			dLotSize = GetLotSize();

			OrderSend(Symbol(), OP_SELL, dLotSize, Bid, nSlip, 
				Bid + dStopLoss, 0, "Zigzag", nMagic, 0, OrangeRed);
			
			return(0);
		}
	}
	else
	{
		for(nCnt = OrdersTotal() - 1; nCnt >= 0; nCnt--)
		{
			OrderSelect(nCnt, SELECT_BY_POS, MODE_TRADES);
			if(OrderMagicNumber() == nMagic && OrderSymbol() == Symbol())
			{
				int nMode = OrderType(); 
	
				if(bIsBarEnd && nMode == OP_BUY && nSignal == 1)
				{
			 		OrderClose(OrderTicket(), OrderLots(), Bid, nSlip, Aqua);
			 		nPending = OP_SELL;
			 		return(0);
				}
					
				if(bIsBarEnd && nMode == OP_SELL && nSignal == -1)
				{		
					OrderClose(OrderTicket(), OrderLots(), Ask, nSlip, OrangeRed);
					nPending = OP_BUY;
					return(0);
		    	}
			}
		}
	}

	// ------
	
	if(bIsBarEnd)
	{
		for(nCnt = 0; nCnt < OrdersTotal(); nCnt++)
		{
			OrderSelect(nCnt, SELECT_BY_POS, MODE_TRADES);
			if(OrderMagicNumber() == nMagic && OrderSymbol() == Symbol())
			{
				if(OrderType() == OP_BUY && Bid - OrderOpenPrice() > dTrailingStop) 
				{
					if(OrderStopLoss() < Bid - dTrailingStop)
					{
						OrderModify(OrderTicket(), OrderOpenPrice(), 
							Bid - dTrailingStop, OrderTakeProfit(), 0, Aqua);
						return(0);
					}
				}
			
				if(OrderType() == OP_SELL && OrderOpenPrice() - Ask > dTrailingStop)
				{
					if(OrderStopLoss() > Ask + dTrailingStop || OrderStopLoss() == 0)
					{
						OrderModify(OrderTicket(), OrderOpenPrice(), 
							Ask + dTrailingStop, OrderTakeProfit(), 0, OrangeRed);
						return(0);
					}
				}
			}
		}
	}
		
	return(0);
}

// ------

double GetLotSize()
{
	double dLot = 0.1;
	
	if(bUseMm)
	{
		dLot = (0.1 * dInitAmount + 0.1 * dProfit) / 1000;
	
		// To do: what fraction of free margin should be used
		if(dLot * 2 * dInitAmount > AccountFreeMargin() / nNumOfExperts)
			dLot = AccountFreeMargin() / (2 * dInitAmount * nNumOfExperts);

		dLot = MathFloor(dLot * 10) / 10;
	
		if(dLot < 0.1)
			dLot = 0.1;
	}
	
	return(dLot);
}

// ------



Индикатор:

#property copyright "Quark"
#property link      ""

#property indicator_separate_window
#property indicator_buffers 1
#property indicator_color1 Red
#property indicator_minimum -1
#property indicator_maximum 1

// indicator parameters
extern int nMinMaxPoints = 75;

// indicator buffers
double arrExtMapBuffer[];

int nExtCountedBars = 0;

int nLastMinMaxBar;
int nLastMinMaxType;

double dLastMin, dLastMax;

////////////////////////
int init()
{
	string strIndicatorShortName;

	// drawing settings
	SetIndexStyle(0, DRAW_HISTOGRAM);
	SetIndexShift(0, 0);
	SetIndexEmptyValue(0,0.0);
		
	IndicatorDigits(4);
		
	strIndicatorShortName = "Zigzag(" + nMinMaxPoints + ")";  
	IndicatorShortName(strIndicatorShortName);

	// indicator buffers mapping
	SetIndexBuffer(0, arrExtMapBuffer);

	dLastMin = Low[Bars - 1];
	dLastMax = High[Bars - 1];

	nLastMinMaxBar = Bars - 1;
	nLastMinMaxType = 0;

	int nPos = Bars - 1;
	int nLastPos = nPos;

	while(nPos >= 0)	// Looking for a first min or max
	{
		if(dLastMax <= High[nPos])
		{
			dLastMax = High[nPos];
			nLastMinMaxBar = nPos;
		}

		if(dLastMin >= Low[nPos])
		{
			dLastMin = Low[nPos];
			nLastMinMaxBar = nPos;
		}

		if(Low[nPos] < dLastMax - nMinMaxPoints * Point)	// Maximum found
		{
			nLastMinMaxType = 1;
			dLastMin = Low[nPos];
			dLastMax = High[nPos];
			nLastMinMaxBar = nPos;
		
			break;
		}
		else if(High[nPos] > dLastMin + nMinMaxPoints * Point)	// Minimum found
		{
			nLastMinMaxType = -1;
			dLastMax = High[nPos];
			dLastMin = Low[nPos];
			nLastMinMaxBar = nPos;
			
			break;			
		}

		nPos--;
	}

	return(0);
}
///////////////////////////
int start()
{
	nExtCountedBars = IndicatorCounted();
	if(nExtCountedBars < 0) 
		return(-1);

	// last counted bar will be recounted
	if(nExtCountedBars > 0) 
		nExtCountedBars--;
		
	Zigzag();  

	return(0);
}
///////////////////
void Zigzag()
{
	int nPos = Bars - nExtCountedBars;
	nPos = MathMax(nLastMinMaxBar - 1, nPos);

	dLastMin = Low[nPos];
	dLastMax = High[nPos];

	while(nPos >= 0)
	{
		arrExtMapBuffer[nPos] = 0.0;
		
		if(nLastMinMaxType != 1)	// Expecting maximum
		{
			if(dLastMax <= High[nPos])
			{
				dLastMax = High[nPos];
				nLastMinMaxBar = nPos;
			}
			
			if(Low[nPos] < dLastMax - nMinMaxPoints * Point)	// Maximum found
			{
				arrExtMapBuffer[nPos] = 1;//High[nLastMinMaxBar];
				nLastMinMaxType = 1;
				dLastMin = Low[nPos];
				dLastMax = High[nPos];
				nLastMinMaxBar = nPos;
			}
		}
		else if(nLastMinMaxType != -1)	// Expecting minimum
		{
			if(dLastMin >= Low[nPos])
			{
				dLastMin = Low[nPos];
				nLastMinMaxBar = nPos;
			}
			
			if(High[nPos] > dLastMin + nMinMaxPoints * Point)	// Maximum found
			{
				arrExtMapBuffer[nPos] = -1;//Low[nLastMinMaxBar];
				nLastMinMaxType = -1;
				dLastMax = High[nPos];
				dLastMin = Low[nPos];
				nLastMinMaxBar = nPos;
			}
		}

		nPos--;
	}
}
///////////////////



Доп. файл mylib.mq4:


void Report(string strFileName, int nMagic, bool& bReportDone)
{
	if(Hour() == 0 && Minute() >= nMagic / 2 && IsTesting() == false)
	{
		if(bReportDone == false)
		{
			int hFile = FileOpen(strFileName + "_" + Symbol() + "_" + Period() + ".rpt", 
				FILE_BIN | FILE_WRITE, ',');
			
			string str = "CloseDateTime,Type,Profit\r\n";
			FileWriteString(hFile, str, StringLen(str)); 

			for(int nCnt = 0; nCnt < HistoryTotal(); nCnt++)
			{
				OrderSelect(nCnt, SELECT_BY_POS, MODE_HISTORY);	
				if(OrderMagicNumber() == nMagic && OrderType() <= OP_SELL && OrderSymbol() == Symbol())
				{
					str = "";
					str = str + TimeToStr(OrderCloseTime(), TIME_DATE|TIME_MINUTES);
					
					if(OrderType() == OP_BUY)
						str = str + ", buy";
					else
						str = str + ", sell";
					
					str = str + "," + OrderProfit();
					str = str + "\r\n";
					
					FileWriteString(hFile, str, StringLen(str));
				}
			}			

			FileFlush(hFile); 
			FileClose(hFile); 
			
			bReportDone = true;
		}
	}
	else if(Hour() != 0)
		bReportDone = false;
}

// ------




 
Забыл сказать: тестируем на EURJPY, M15 с 8 ноября 2004 по сегодня, параметр от 10 до 200, шаг 1.
 
Я , конечно , шаг сделал побольше, но после 100, действительно, сделки не идут. Но по Евре на часовках неплохо выглядит. Сходу ничего не скажу, индикатор после 100 сигналы дает. :(
 
Знаю, что неплохо выглядит :) Вопрос, можно ли этому верить :( Сделки не идут, а сигналы отрисовываются (см. окошко индикатора). А кроме сигналов на принятие решения о сделке ничего не влияет...

Я пока не могу понять главного - моя это бага, или тестера. То есть, можно ли верить тестеру ЭТОГО эксперта, надо ли менять код, чтобы тест был корректным, и как.

Еще до кучи - индикатор - простой зигзаг. По идее, меняя таймфрейм, да с учетом моделирования баров внутри периода (я специально загрузил с Альпари все минутки, и тестировал с 16.06.2004, чтобы все минутки были доступны), так вот, смена таймфрейма должна лишь чуть-чуть менять профиль оптимизации. Она же (смена) меняет этот профиль полностью, не узнать. Что не так? Я даже подтягивание стопов к концу бара привязал, для единообразия. Все равно. Надеюсь, кто-нибудь да поможет разобраться... Разработчики, например, должны знать :)
 
Закомментировав в эксперте все, что только можно, сравниваем даты сделок с сигналами, которые рисует индикатор. Приходим к выводу, что часть (большая) сигналов игнорируется. Таймфрейм и валюта те же.

#include "mylib.mq4"

// ------

extern double dZigzagSize = 148;

// ------

datetime timePrev = 0;

double dInitAmount = 1000.0;
int nNumOfExperts = 5;		// To do: autocalculate number of active experts
int nSlip = 5;

double dProfit = 0;		// old: arrTotals
double dLotSize = 0.1;

double dStopLoss;
double dTrailingStop;
double dTakeProfit;

int nPending = -1;

int nSignal = 0;

bool bUseMm = false;
int nMagic = 0;
bool bReportDone = false;

//////////////////
int init ()
{ 
	if(Symbol() == "EURUSD" && Period() == 60)				// 1616 ($700)
	{
		if(!IsTesting())
			dZigzagSize = 148;
			
		nMagic = 1;
	}
	else if(Symbol() == "EURUSD" && Period() == 15)			// 1115 ($366)
	{
		if(!IsTesting())
			dZigzagSize = 87;
			
		nMagic = 2;
	}
	else if(Symbol() == "EURJPY" && Period() == 15)
	{
		if(!IsTesting())
			dZigzagSize = 50;
		nMagic = 3;
	}

	dStopLoss = dZigzagSize * Point;
	dTrailingStop = dZigzagSize * Point;
	dTakeProfit = 0;

	nSlip = 5 * Point;
	
	return(0);
}
//////////////////
int deinit()
{
	return(0);
}
////////////////////
int start()
{
	if(AccountFreeMargin() < 500)
		return(0);
		
	Report("Zigzag", nMagic, bReportDone);

	bool bIsBarEnd = false;
	if(timePrev != Time[0]) 
		bIsBarEnd = true;
	timePrev = Time[0];
	
	// ------

	if(bIsBarEnd)
	{
		nSignal = iCustom(NULL, 0, "_Zigzag_Ind", dZigzagSize, 0, 1);
	}
		
	dProfit = 0;
		
	for(int nCnt = 0; nCnt < OrdersTotal(); nCnt++)
	{
		OrderSelect(nCnt, SELECT_BY_POS, MODE_HISTORY);
		if(OrderMagicNumber() == nMagic && OrderType() <= OP_SELL && OrderSymbol() == Symbol())
		{
			dProfit += OrderProfit();
		}
	}   

	int nNumOfOpenedOrders = 0;
	
	for(nCnt = 0; nCnt < OrdersTotal(); nCnt++)
	{
		OrderSelect(nCnt, SELECT_BY_POS, MODE_TRADES);
		if(OrderMagicNumber() == nMagic && OrderSymbol() == Symbol())
		{
			if(OrderType() <= OP_SELL)
				nNumOfOpenedOrders++;
		}
	}

	if(!nNumOfOpenedOrders)
	{
		if((bIsBarEnd && nSignal == -1) || nPending == OP_BUY)
		{
			dLotSize = GetLotSize();

			OrderSend(Symbol(), OP_BUY, dLotSize, Ask, nSlip, 
				Ask - dStopLoss, 0, "Zigzag", nMagic, 0, Aqua);
				
			nPending = -1;
			
			return(0);
		}
		else if((bIsBarEnd && nSignal == 1) || nPending == OP_SELL)
		{
			dLotSize = GetLotSize();

			OrderSend(Symbol(), OP_SELL, dLotSize, Bid, nSlip, 
				Bid + dStopLoss, 0, "Zigzag", nMagic, 0, OrangeRed);
				
			nPending = -1;
			
			return(0);
		}
	}
	else
	{
		for(nCnt = 0; nCnt < OrdersTotal(); nCnt++)
		{
			OrderSelect(nCnt, SELECT_BY_POS, MODE_TRADES);
			if(OrderMagicNumber() == nMagic && OrderSymbol() == Symbol())
			{
				int nMode = OrderType(); 
				if(bIsBarEnd && nMode == OP_BUY )//&& nSignal == 1)
				{
			 		OrderClose(OrderTicket(), OrderLots(), Bid, nSlip, Aqua);
//			 		nPending = OP_SELL;
			 		return(0);
				}
					
				if(bIsBarEnd && nMode == OP_SELL )//&& nSignal == -1)
				{		
					OrderClose(OrderTicket(), OrderLots(), Ask, nSlip, OrangeRed);
//					nPending = OP_BUY;
					return(0);
		    	}
			}
		}
	}

	// ------
	
/*	if(bIsBarEnd)
	{
		for(nCnt = 0; nCnt < OrdersTotal(); nCnt++)
		{
			OrderSelect(nCnt, SELECT_BY_POS, MODE_TRADES);
			if(OrderMagicNumber() == nMagic && OrderSymbol() == Symbol())
			{
				if(OrderType() == OP_BUY && Bid - OrderOpenPrice() > dTrailingStop) 
				{
					if(OrderStopLoss() < Bid - dTrailingStop)
					{
						OrderModify(OrderTicket(), OrderOpenPrice(), 
							Bid - dTrailingStop, OrderTakeProfit(), 0, Aqua);
						return(0);
					}
				}
			
				if(OrderType() == OP_SELL && OrderOpenPrice() - Ask > dTrailingStop)
				{
					if(OrderStopLoss() > Ask + dTrailingStop || OrderStopLoss() == 0)
					{
						OrderModify(OrderTicket(), OrderOpenPrice(), 
							Ask + dTrailingStop, OrderTakeProfit(), 0, OrangeRed);
						return(0);
					}
				}
			}
		}
	}
*/		
	return(0);
}

// ------

double GetLotSize()
{
	double dLot = 0.1;
	
	if(bUseMm)
	{
		dLot = (0.1 * dInitAmount + 0.1 * dProfit) / 1000;
	
		// To do: what fraction of free margin should be used
		if(dLot * 2 * dInitAmount > AccountFreeMargin() / nNumOfExperts)
			dLot = AccountFreeMargin() / (2 * dInitAmount * nNumOfExperts);

		dLot = MathFloor(dLot * 10) / 10;
	
		if(dLot < 0.1)
			dLot = 0.1;
	}
	
	return(dLot);
}

// ------

 
Тестеру нельзя верить.
Даже на простейших примерах из дистрибутива он врет.
Я уже видел грааль на MACD Sample из дистрибутива.
А если случай чуть более сложный?

"Однажды солгавши, кто тебе поверит ..."

Моделирование "фракталами" - Имхо - полный бред (повторение 12 последних ...).
Да и к фракталам ни малейшего отношения не имеет.

Можно было бы моделировать случайным блужданием, тут смысла намного больше, НО, все это просто чьито недоказанные модели и предположения. Включать их в свой продукт не будучи уверенным что это вообще чемуто соответствует - очень непродуманный шаг.

Можно попытаться тестировать без всякого моделирования данных.
Шансов на правильный результат тут больше, но веры в это никакой,
нужно очень детально все проверять руками (а нафига тогда тестер?)

Неужели вам хочется разбираться в очевидно неправильных вещах?
(мне на это времени жалко ...)
 
Буду ждать ответа от разработчиков. Может быть, все-таки, они укажут на мою ошибку, или исправят свою...
 
Уважаемые разработчики!
Подтвердите, хотя бы, что ответ будет. Это сэкономит мне массу усилий.
 
Вы думаете, это быстро, разбираться с чужими кодами? Ваша проблема стоит в очереди.
 
Вы думаете, это быстро, разбираться с чужими кодами? Ваша проблема стоит в очереди.


ОК, спасибо. Буду ждать ответа.
 
Вы думаете, это быстро, разбираться с чужими кодами? Ваша проблема стоит в очереди.


ОК, спасибо. Буду ждать ответа.


Я протестировал (билд 176) - все работает в оптимизаторе и в обычном тестировании:





К сожалению, я не могу ничего сказать про пропуски сигналов.
Это может сделать только сам разработчик эксперта - то есть, Вы.
Причина обращения: