Книга: Язык программирования MQL5: Продвинутое использование торговой платформы MetaTrader 5
Назад: Тестирование советников
Дальше: Создание эксперта с помощью мастера MQL5 Wizard

Управление капиталом и оценка эффективности эксперта

Управление капиталом – это способ принятия решения о том, какой частью счета следует рисковать в отдельной торговой возможности.

Типичные правила управлением капитала:

Соотношение возможных потерь и прибылей 1:3.

Закрывать убыточные позиции раньше, чем прибыльные.

Избегать очень кратковременных позиций.

Не принимать решений перед закрытием торгов.

Общая сумма средств, вкладываемых в одну сделку, не может превышать 10% – 15% от общего капитала.

Общий процент вложенных средств по всем открытым позициям не может превышать 30%.

Максимально допустимый риск на сделку 2—5% от размера депозита.

Общий максимально допустимый риск по всем открытым позициям 5—7% от размера депозита.

Стратегии управления капиталом:

Фиксированная сумма или фиксированный процент капитала, подвергаемый риску при следующей сделке.

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

Пересечение кривых цены – по этой системе производится анализ торговой системы с помощью длинной и короткой скользящих средних кривых (например, 3 и 8) прибылей и убытков сделок. Если короткая скользящая средняя кривая находится над более длинной скользящей кривой, тогда торговая система дает лучшие результаты, если же короткая скользящая средняя кривая находится под длинной скользящей кривой, тогда система работает хуже и сигналы торговой системы на открытие позиций нужно пропускать. При этом для расчета кривых используются все сигналы торговой системы, а не только те, которые были фактически реализованы. Наличие выигрышных и проигрышных периодов торговой системы также определяется с помощью статистического анализа.

Optimal f и Secure f это определение на основе тестирования торговой системы на прошлых сделках некоторой оптимальной доли начального капитала, инвестируемой в каждую сделку, с целью достижения максимальной доходности данной системы в качестве основного критерия (Optimal f) или достижения максимальной доходности с учётом ограничения предельно допустимых убытков (Secure f).

Зависимость между проигрышами и выигрышами анализируется с помощью Z-счета. Доверительный интервал при этом должен превышать 94%.

Z-счет рассчитывается путем сравнения количества серий в наборе сделок относительно количества серий, которое можно было бы ожидать при статистической независимости результатов текущей сделки от прошедших сделок.

Z = (N* (R – 0.5) -X) / ((X* (X – N)) / ((N -1)) ^ (1/2)

Где:

N = общее количество сделок.

X = 2* (общее число прибыльных сделок) * (общее число убыточных сделок).

R = количество серий в наблюдении. Каждый раз, когда после прибыльной сделки следует убыточная сделка или наоборот считается за серию.

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

Отрицательный Z-счет означает, что в наблюдаемой истории сделок количество серий больше, чем следовало бы ожидать при статистически независимом распределении исходов сделок. Следовательно, за выигрышной сделкой обычно следует другая выигрышная сделка, а за убыточной сделкой следует другая убыточная сделка.

Наличие отрицательного Z-счета означает, что можно использовать пересечение кривых доходности, при том торговая система будет работать во время прибыльной фазы и выключаться после наступления убыточной фазы.

Значение Z-счета эксперта можно посмотреть во вкладке Бэктест тестера клиентского терминала после тестирования эксперта.

В рассмотренном примере эксперта на основе торговой системы Сидуса, при значениях numberBarOpenPosition=5 и numberBarStopPosition=5, Z-счет эксперта равен 2.89 (99.61%). Однако на этом основании скорректировать работу эксперта достаточно проблематично, так как после выигрыша может следовать не единичный проигрыш, а серия убытков и наоборот.

Вывести последовательность выигрышей и проигрышей эксперта можно в его функции OnDeinit.

void OnDeinit (const int reason)

{

if (HistorySelect (0,TimeCurrent ()))

{

int n=HistoryDealsTotal ();

for (int i=0;i <n;i++) {

if (HistoryDealGetDouble (HistoryDealGetTicket (i), DEAL_PROFIT)!=0.0) {

Print («Profit», HistoryDealGetDouble (HistoryDealGetTicket (i), DEAL_PROFIT));

}

}

}

}

Общая эффективность советника определяется как разница между реализованной в ходе трейда прибылью и потенциально возможной прибылью за время этого же трейда.

Общая эффективность =

(Реализованная разница цен) / (Потенциальная прибыль)

Для длинных позиций:

Общая эффективность = (Цена закрытия – Цена открытия) /

(Максимальная цена – Минимальная цена)

Для коротких позиций:

Общая эффективность = (Цена открытия – Цена закрытия) /

(Максимальная цена – Минимальная цена)

Эффективность входа в рынок определяется как максимальная разница в ценах относительно цены входа как часть общего потенциала доходности в ходе трейда. Эффективность входа в рынок показывает, насколько хорошо торговая система открывает трейды – в случае длинных позиций это то, насколько сигнал входа близок к наименьшей точке в ходе трейда, в случае коротких позиций – насколько сигнал входа близок к максимальной точке в ходе трейда.

Эффективность входа = (максимальная разница в ценах относительно цены входа) / (Потенциальная прибыль)

Максимальная разница в ценах относительно цены входа – это разница между максимальной ценой и ценой входа (для коротких позиций – минимальной ценой).

Для длинных позиций:

Эффективность входа = (Максимальная цена – Цена открытия трейда) / (Максимальная цена – Минимальная цена)

Для коротких позиций:

Эффективность входа = (Цена открытия трейда – Минимальная цена) / (Максимальная цена – Минимальная цена)

Эффективность выхода из рынка определяется как максимальная разница в ценах относительно цены выхода как часть общего потенциала доходности в ходе трейда. Эффективность выхода из рынка показывает, насколько хорошо система закрывает трейды – в случае длинных позиций это то, насколько сигнал выхода близок к максимальной точке в ходе трейда, в случае коротких позиций – насколько сигнал выхода близок к минимальной точке в ходе трейда.

Эффективность выхода = (максимальная разница в ценах относительно цены выхода) / (Потенциальная прибыль)

Максимальная разница в ценах относительно цены выхода – это разница между ценой выхода и минимальной ценой (для коротких позиций – максимальной ценой).

Для длинных позиций:

Эффективность выхода = (Цена выхода – Минимальная цена) / (Максимальная цена – Минимальная цена)

Для коротких позиций:

Эффективность выхода = (Максимальная цена – Цена выхода) / (Максимальная цена – Минимальная цена)

Общая эффективность является суммой эффективности входа и эффективности выхода минус 1.

Эффективность входа и эффективность выхода могут быть позитивными величинами, однако общая эффективность может быть отрицательной величиной. Если сумма эффективности входа и эффективности выхода меньше 100%, то сделка убыточна.

Для анализа работы эксперта вычисляются средняя общая эффективность, средняя эффективность входов и средняя эффективность выходов.

Для вычисления средних величин лучше пользоваться статистическим анализом, чтобы отбрасывать выскакивающие сделки.

Торговая система является статистически прибыльной, если:

(Средняя выигрышная сделка) * (% выигрышей) – накладные расходы>

(Средняя проигрышная сделка) * (% проигрышей)

Накладные расходы это проскальзывания, комиссионные и др.

Такие показатели как Средняя выигрышная сделка (Средний прибыльный трейд), % выигрышей (Прибыльные трейды (% от всех)), Средняя проигрышная сделка (Средний убыточный трейд), % проигрышей (Убыточные трейды (% от всех)) можно посмотреть во вкладке Бэктест тестера клиентского терминала после тестирования эксперта.

Показатель статистической прибыльности эксперта без учета накладных расходов или матожидание выигрыша также можно посмотреть во вкладке Бэктест тестера клиентского терминала после тестирования эксперта.

Матожидание выигрыша рассчитывается по следующей формуле.

Expected Payoff = (ProfitTrades / TotalTrades) * (GrossProfit / ProfitTrades) —

(LossTrades / TotalTrades) * (GrossLoss / LossTrades)

где:

TotalTrades – общее количество сделок;

ProfitTrades – количество прибыльных сделок;

LossTrades – количество убыточных сделок;

GrossProfit – общая прибыль;

GrossLoss – общий убыток.

Здесь ProfitTrades / TotalTrades это % выигрышей, GrossProfit / ProfitTrades – Средняя выигрышная сделка, LossTrades / TotalTrades – % проигрышей, GrossLoss / LossTrades – Средняя проигрышная сделка.

В рассмотренном примере эксперта на основе торговой системы Сидуса, при значении numberBarOpenPosition=3, матожидание выигрыша составляет 232.62 – 147.55=85. Это меньше 10 пунктов, поэтому можно сказать, что прибыльность эксперта является неустойчивой.

В рассмотренном примере эксперта на основе торговой системы Сидуса заменим сигналы закрытия позиции на использование Trailing Stop, т.е. при достижении цены уровня безубыточности будет передвигать StopLoss.

Для этого изменим класс Trade.

class Trade

{

private:

double StopLoss;

double Profit;

double Lot;

double TrailingStop;

double priceBuy;

double priceSell;

double slBuy;

double slSell;



public:

Trade (double stopLoss, double profit, double lot, double trailingStop);

~Trade ();

void Order (bool Buy, bool StopBuy, bool Sell, bool StopSell);

void Trailing ();

};

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

//| |

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

Trade::Trade (double stopLoss, double profit, double lot, double trailingStop)

{

StopLoss=stopLoss;

Profit=profit;

Lot=lot;

TrailingStop=trailingStop;

priceBuy=0.0;

priceSell=0.0;

slBuy=0.0;

slSell=0.0;

}

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

//| |

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

Trade::~Trade ()

{

}

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

void Trade::Trailing () {

//Проверка наличия открытой позиции, чтобы не пытаться открыть ее заново

bool BuyOpened=false;

bool SellOpened=false;



if (PositionSelect (_Symbol) ==true)

{

if (PositionGetInteger (POSITION_TYPE) ==POSITION_TYPE_BUY)

{

BuyOpened=true;

}

else if (PositionGetInteger (POSITION_TYPE) ==POSITION_TYPE_SELL)

{

SellOpened=true;

}

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —

MqlTradeRequest mrequest;

MqlTradeCheckResult check_result;

MqlTradeResult mresult;



MqlTick latest_price;

if (!SymbolInfoTick (_Symbol, latest_price))

{

Alert («Ошибка получения последних котировок – ошибка:», GetLastError (),»!!»);

return;

}

if (BuyOpened==true) {

double TBS=0;

if (TrailingStop <SymbolInfoInteger (Symbol (),SYMBOL_TRADE_STOPS_LEVEL) *_Point) {

TBS=SymbolInfoInteger (Symbol (),SYMBOL_TRADE_STOPS_LEVEL) *_Point;

} else {

TBS=TrailingStop;

}

if (

latest_price.bid-priceBuy> =TBS

) {

mrequest.action = TRADE_ACTION_SLTP;

mrequest.price = NormalizeDouble(latest_price.ask,_Digits);

mrequest.sl = NormalizeDouble (priceBuy,_Digits); // Stop Loss

mrequest.tp = NormalizeDouble(latest_price.ask + Profit,_Digits);

mrequest.symbol = _Symbol; // символ

mrequest. volume = Lot; // количество лотов для торговли

mrequest. type_filling = ORDER_FILLING_FOK;

mrequest. type = ORDER_TYPE_BUY; // ордер на покупку

// – - отсылаем ордер

if (!OrderCheck (mrequest, check_result))

{

return;

} else {

if (OrderSend (mrequest, mresult)) {}

}



// анализируем код возврата торгового сервера

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

slBuy= mrequest.sl;

priceBuy=slBuy+TrailingStop;

}

else

{

return;

}

}

}



if (SellOpened==true) {

double TSS=0;

if (TrailingStop <SymbolInfoInteger (Symbol (),SYMBOL_TRADE_STOPS_LEVEL) *_Point) {

TSS=SymbolInfoInteger (Symbol (),SYMBOL_TRADE_STOPS_LEVEL) *_Point;

} else {

TSS=TrailingStop;

}

if (

priceSell-latest_price.ask> =TSS

) {

mrequest.action = TRADE_ACTION_SLTP;

mrequest.price = NormalizeDouble(latest_price.bid,_Digits);

mrequest.sl = NormalizeDouble (priceSell,_Digits); // Stop Loss

mrequest.tp = NormalizeDouble(latest_price.bid – Profit,_Digits);

mrequest.symbol = _Symbol; // символ

mrequest. volume = Lot; // количество лотов для торговли

mrequest. type_filling = ORDER_FILLING_FOK;

mrequest. type = ORDER_TYPE_SELL; // ордер на покупку

// – - отсылаем ордер

if (!OrderCheck (mrequest, check_result))

{

return;

} else {

if (OrderSend (mrequest, mresult)) {}

}



// анализируем код возврата торгового сервера

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

slSell= mrequest.sl;

priceSell=slSell-TrailingStop;

}

else

{

return;

}

}

}



}

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

void Trade::Order (bool Buy, bool BuyStop, bool Sell, bool SellStop) {

//Проверка наличия открытой позиции, чтобы не пытаться открыть ее заново

bool BuyOpened=false;

bool SellOpened=false;



if (PositionSelect (_Symbol) ==true)

{

if (PositionGetInteger (POSITION_TYPE) ==POSITION_TYPE_BUY)

{

BuyOpened=true;

}

else if (PositionGetInteger (POSITION_TYPE) ==POSITION_TYPE_SELL)

{

SellOpened=true;

}

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —

MqlTradeRequest mrequest;

MqlTradeCheckResult check_result;

MqlTradeResult mresult;



MqlTick latest_price;

if (!SymbolInfoTick (_Symbol, latest_price))

{

Alert («Ошибка получения последних котировок – ошибка:», GetLastError (),»!!»);

return;

}

if (Buy==true&&BuyOpened==false) {

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – —

if (((ENUM_SYMBOL_TRADE_EXECUTION) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_EXEMODE)) ==SYMBOL_TRADE_EXECUTION_INSTANT) {

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_DEAL;

mrequest.symbol = _Symbol;

mrequest. volume = Lot;

mrequest.price = NormalizeDouble(latest_price.ask,_Digits);

mrequest.sl = NormalizeDouble(latest_price.bid – StopLoss,_Digits);

mrequest.tp = NormalizeDouble(latest_price.ask + Profit,_Digits);

mrequest.deviation=10;

mrequest. type = ORDER_TYPE_BUY;

mrequest. type_filling = ORDER_FILLING_FOK;



ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10014) Alert («Неправильный объем в запросе»);

if(check_result.retcode==10015) Alert («Неправильная цена в запросе»);

if(check_result.retcode==10016) Alert («Неправильные стопы в запросе»);

if(check_result.retcode==10019) Alert («Нет достаточных денежных средств для выполнения запроса»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

priceBuy=mresult.price;

slBuy= mrequest.sl;

}

else

{

if(mresult.retcode==10004) //Реквота

{

Print («Requote bid ",mresult.bid);

Print («Requote ask ",mresult.ask);

} else {

Print («Retcode ",mresult.retcode);

}

}

} else {

Print («Retcode ",mresult.retcode);

}

}

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —

if (((ENUM_SYMBOL_TRADE_EXECUTION) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_EXEMODE)) ==SYMBOL_TRADE_EXECUTION_EXCHANGE) {

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_DEAL;

mrequest.symbol = _Symbol;

mrequest. volume = Lot;

mrequest. type = ORDER_TYPE_BUY;

mrequest. type_filling = ORDER_FILLING_FOK;

ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10014) Alert («Неправильный объем в запросе»);

if(check_result.retcode==10019) Alert («Нет достаточных денежных средств для выполнения запроса»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

priceBuy=mresult.price;



// – — – — – — – — – — – —

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_SLTP;

mrequest.symbol = _Symbol;

mrequest.sl = NormalizeDouble(mresult.price – StopLoss,_Digits);

mrequest.tp = NormalizeDouble(mresult.price + Profit,_Digits);

ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10015) Alert («Неправильная цена в запросе»);

if(check_result.retcode==10016) Alert («Неправильные стопы в запросе»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

slBuy= mrequest.sl;

}

else

{

Print («Retcode ",mresult.retcode);

}

} else {

Print («Retcode ",mresult.retcode);

}

}

// – — – — – — – — – — – — – — – — – — – — —

}

else

{

Print («Retcode ",mresult.retcode);

}

} else {

Print («Retcode ",mresult.retcode);

}

}

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —

if (SellStop==true&&SellOpened==true) {

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – —

if (((ENUM_SYMBOL_TRADE_EXECUTION) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_EXEMODE)) ==SYMBOL_TRADE_EXECUTION_INSTANT) {

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_DEAL;

mrequest.symbol = _Symbol;

mrequest. volume = Lot;

mrequest.price = NormalizeDouble(latest_price.ask,_Digits);

mrequest.sl = NormalizeDouble (0.0,_Digits);

mrequest.tp = NormalizeDouble (0.0,_Digits);

mrequest.deviation=10;

mrequest. type = ORDER_TYPE_BUY;

mrequest. type_filling = ORDER_FILLING_FOK;



ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10014) Alert («Неправильный объем в запросе»);

if(check_result.retcode==10015) Alert («Неправильная цена в запросе»);

if(check_result.retcode==10016) Alert («Неправильные стопы в запросе»);

if(check_result.retcode==10019) Alert («Нет достаточных денежных средств для выполнения запроса»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

Print («Price», mresult.price);

}

else

{

if(mresult.retcode==10004) //Реквота

{

Print («Requote bid ",mresult.bid);

Print («Requote ask ",mresult.ask);

} else {

Print («Retcode ",mresult.retcode);

}

}

} else {

Print («Retcode ",mresult.retcode);

}

}

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —

if (((ENUM_SYMBOL_TRADE_EXECUTION) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_EXEMODE)) ==SYMBOL_TRADE_EXECUTION_EXCHANGE) {

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_DEAL;

mrequest.symbol = _Symbol;

mrequest. volume = Lot;

mrequest. type = ORDER_TYPE_BUY;

mrequest. type_filling = ORDER_FILLING_FOK;



ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10014) Alert («Неправильный объем в запросе»);

if(check_result.retcode==10019) Alert («Нет достаточных денежных средств для выполнения запроса»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

Print («Price», mresult.price);

}

else

{

Print («Retcode ",mresult.retcode);

}

} else {

Print («Retcode ",mresult.retcode);

}

}

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —

if (Sell==true&&SellOpened==false) {

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —

if (((ENUM_SYMBOL_TRADE_EXECUTION) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_EXEMODE)) ==SYMBOL_TRADE_EXECUTION_INSTANT) {

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_DEAL;

mrequest.symbol = _Symbol;

mrequest. volume = Lot;

mrequest.price = NormalizeDouble(latest_price.bid,_Digits);

mrequest.sl = NormalizeDouble(latest_price.ask + StopLoss,_Digits);

mrequest.tp = NormalizeDouble(latest_price.bid – Profit,_Digits);

mrequest.deviation=10;

mrequest. type = ORDER_TYPE_SELL;

mrequest. type_filling = ORDER_FILLING_FOK;



ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10014) Alert («Неправильный объем в запросе»);

if(check_result.retcode==10015) Alert («Неправильная цена в запросе»);

if(check_result.retcode==10016) Alert («Неправильные стопы в запросе»);

if(check_result.retcode==10019) Alert («Нет достаточных денежных средств для выполнения запроса»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

priceSell=mresult.price;

slSell= mrequest.sl;

}

else

{

if(mresult.retcode==10004) //Реквота

{

Print («Requote bid ",mresult.bid);

Print («Requote ask ",mresult.ask);

} else {

Print («Retcode ",mresult.retcode);

}

}

} else {

Print («Retcode ",mresult.retcode);

}

}

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —

if (((ENUM_SYMBOL_TRADE_EXECUTION) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_EXEMODE)) ==SYMBOL_TRADE_EXECUTION_EXCHANGE) {

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_DEAL;

mrequest.symbol = _Symbol;

mrequest. volume = Lot;

mrequest. type = ORDER_TYPE_SELL;

mrequest. type_filling = ORDER_FILLING_FOK;



ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10014) Alert («Неправильный объем в запросе»);

if(check_result.retcode==10019) Alert («Нет достаточных денежных средств для выполнения запроса»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

priceSell=mresult.price;



// – — – — – — – — – — – —

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_SLTP;

mrequest.symbol = _Symbol;

mrequest.tp = NormalizeDouble(mresult.price – Profit,_Digits);

mrequest.sl = NormalizeDouble(mresult.price + StopLoss,_Digits);

ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10015) Alert («Неправильная цена в запросе»);

if(check_result.retcode==10016) Alert («Неправильные стопы в запросе»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

slSell= mrequest.sl;

}

else

{

Print («Retcode ",mresult.retcode);

}

} else {

Print («Retcode ",mresult.retcode);

}

}

// – — – — – — – — – — – — – — – — – — – — —

}

else

{

Print («Retcode ",mresult.retcode);

}

} else {

Print («Retcode ",mresult.retcode);

}

}

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —



}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – —

if (BuyStop==true&&BuyOpened==true) {

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —

if (((ENUM_SYMBOL_TRADE_EXECUTION) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_EXEMODE)) ==SYMBOL_TRADE_EXECUTION_INSTANT) {

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_DEAL;

mrequest.symbol = _Symbol;

mrequest. volume = Lot;

mrequest.price = NormalizeDouble(latest_price.bid,_Digits);

mrequest.sl = NormalizeDouble (0.0,_Digits);

mrequest.tp = NormalizeDouble (0.0,_Digits);

mrequest.deviation=10;

mrequest. type = ORDER_TYPE_SELL;

mrequest. type_filling = ORDER_FILLING_FOK;



ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10014) Alert («Неправильный объем в запросе»);

if(check_result.retcode==10015) Alert («Неправильная цена в запросе»);

if(check_result.retcode==10016) Alert («Неправильные стопы в запросе»);

if(check_result.retcode==10019) Alert («Нет достаточных денежных средств для выполнения запроса»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

Print («Price», mresult.price);

}

else

{

if(mresult.retcode==10004) //Реквота

{

Print («Requote bid ",mresult.bid);

Print («Requote ask ",mresult.ask);

} else {

Print («Retcode ",mresult.retcode);

}

}

} else {

Print («Retcode ",mresult.retcode);

}

}

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —

if (((ENUM_SYMBOL_TRADE_EXECUTION) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_EXEMODE)) ==SYMBOL_TRADE_EXECUTION_EXCHANGE) {

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_DEAL;

mrequest.symbol = _Symbol;

mrequest. volume = Lot;

mrequest. type = ORDER_TYPE_SELL;

mrequest. type_filling = ORDER_FILLING_FOK;



ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10014) Alert («Неправильный объем в запросе»);

if(check_result.retcode==10019) Alert («Нет достаточных денежных средств для выполнения запроса»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

Print («Price», mresult.price);

}

else

{

Print («Retcode ",mresult.retcode);

}

} else {

Print («Retcode ",mresult.retcode);

}

}

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —



}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – —

}

В функции OnInit советника вызовем функцию Trailing класса Trade.

#include "CheckTrade.mqh»

#include "Trade.mqh»

#include "Sidus.mqh»



input double Lot=1;

input double spreadLevel=5.0;

input double StopLoss=0.01;

input double Profit=0.1;

input int numberBarOpenPosition=4;

input int numberBarStopPosition=5;



input double TrailingStop=0.002;



CheckTrade checkTrade;

Trade trade (StopLoss, Profit, Lot, TrailingStop);

Sidus sidus (numberBarOpenPosition, numberBarStopPosition);



bool flagStopLoss=false;



//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

//| Expert initialization function |

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

int OnInit ()

{

return (checkTrade. OnCheckTradeInit (Lot));

}



//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

//| Expert tick function |

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

void OnTick ()

{



if (!checkTrade. OnCheckTradeTick (Lot, spreadLevel)) {

return;

}



//Ограничить вычисления советника по появлению нового бара на графике

static datetime last_time;

datetime last_bar_time= (datetime) SeriesInfoInteger (Symbol (),Period (),SERIES_LASTBAR_DATE);

if (last_time!=last_bar_time)

{

last_time=last_bar_time;

} else {

return;

}



/*

//Ограничить вычисления советника по flagStopLoss

static datetime last_time_daily;

datetime last_bar_time_daily= (datetime) SeriesInfoInteger (Symbol (),PERIOD_D1,SERIES_LASTBAR_DATE);

if (last_time_daily!=last_bar_time_daily)

{

last_time_daily=last_bar_time_daily;

flagStopLoss=false;

}



if (flagStopLoss==true) return;

*/



//Для вычисления сигналов торговой системы требуются исторические данные символа



MqlRates mrate [];

ResetLastError ();

if (CopyRates (Symbol (),Period (),0,numberBarStopPosition, mrate) <0)

{

Print (GetLastError ());

return;

}



ArraySetAsSeries (mrate, true);



// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —



bool TradeSignalBuy=false;

bool TradeSignalSell=false;



TradeSignalBuy=sidus. OnTradeSignalBuy ();

TradeSignalSell=sidus. OnTradeSignalSell ();



bool TradeSignalBuyStop=false;

bool TradeSignalSellStop=false;



//TradeSignalBuyStop=sidus. OnTradeSignalBuyStop ();

//TradeSignalSellStop=sidus. OnTradeSignalSellStop ();



trade. Order (TradeSignalBuy, TradeSignalBuyStop, TradeSignalSell, TradeSignalSellStop);

trade.Trailing ();



}

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

//| Trade function |

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

void OnTrade ()

{

static int _deals;

ulong _ticket=0;



if (HistorySelect (0,TimeCurrent ()))

{

int i=HistoryDealsTotal () -1;



if (_deals!=i) {

_deals=i;

} else {return;}



if ((_ticket=HistoryDealGetTicket (i))> 0)

{

string _comment=HistoryDealGetString (_ticket, DEAL_COMMENT);

if (StringFind (_comment,«sl», 0)!=-1) {

flagStopLoss=true;

}



}

}

}



// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – —

//| Expert deinitialization function |

//+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – +

void OnDeinit (const int reason)

{



}

Матожидание выигрыша теперь 230.89 – 179.78 = 51.

Хотя чистая прибыль при трейлинге увеличилась, статистическая прибыльность эксперта ухудшилась.

Другой показатель вкладки Бэктест тестера стратегий, это Коэффициент Шарпа, характеризующий эффективность и стабильность эксперта. Чем выше значение показателя, тем больше доходность на единицу риска. Значение коэффициента Шарпа для устойчивых экспертов более 0.25.

В нашем случае коэффициент Шарпа равен 0.09.

Фактор роста эксперта или GHPR (среднее геометрическое сделки) будет равен (Конечный депозит/Начальный депозит) в степени 1/ (Всего трейдов).

В данном случае GHPR (фактор роста) равен 1,0036 – больше единицы, что означает возможность торговли с использованием реинвестирования.

Другие показатели эксперта, которые можно увидеть во вкладке Бэктест тестера стратегий, это:

Прибыльность (Profit Factor) – Общая прибыль/общий убыток. Рекомендуемое значение не меньше 2.

Фактор восстановления – Чистая прибыль / Максимальная просадка по средствам. Чем больше показатель, тем менее рискованным является советник. Многие эксперты считают, что у эффективной торговой системы фактор восстановления должен быть не менее 3.

AHPR – среднеарифметическое сделки. Положительное значение говорит о том, что торговая система прибыльна.

Уровень маржи – минимальный уровень маржи в процентах, который был зафиксирован за период тестирования.

LR Correlation – позволяет оценить отклонения точек графика баланса счета от линейной регрессии. Чем LR Correlation ближе к нулю, тем более случайный характер имеет торговля.

LR Standard Error – отклонение графика баланса счета от линейной регрессии в денежном выражении.

Средний прибыльный трейд / Средний убыточный трейд – желательно, чтобы отношение этих двух показателей было больше единицы.

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

Исходя из глубины максимальной посадки по средствам, можно вычислить минимальный депозит, необходимый, чтобы начать торговать с данным экспертом.

Мин. депозит = Максимальная просадка по средствам * 2

Correlation (Profits, MFE) – связь между результатами позиций и MFE (Maximum Favorable Excursion – максимальный размер потенциальной прибыли, наблюдаемый за время удержания позиции). MFE показывает максимальное движение цены в благоприятном направлении. Чем ближе показатель Correlation (Profits, MFE) к единице, тем лучше эксперт реализует потенциальную прибыль.

Correlation (Profits, MAE) – связь между результатами позиций и MAE (Maximum Adverse Excursion – максимальный потенциальной убыток, наблюдаемый за время удержания позиции). MAE показывает максимально неблагоприятное движение цены. Чем ближе показатель Correlation (Profits, MAE) к единице, тем лучше эксперт использует защитный Stop Loss.

Correlation (MFE, MAE) – связь между MFE и MAE. Чем ближе показатель Correlation (MFE, MAE) к единице, тем лучше эксперт реализует максимальную прибыль и максимально защищает позицию на всем протяжении ее жизни.

Среднее время удержания позиции – показатель рассчитывается как общее время удержания, деленное на количество сделок. Время удержания позиции увеличивает риск, потому что просадка, очевидно, может быть большей при позициях, удерживаемых в течение большего периода времени.

Назад: Тестирование советников
Дальше: Создание эксперта с помощью мастера MQL5 Wizard