Filtragem de acordo com o histórico
Introdução
Há vários filtros diferentes: valores de indicadores, volatilidade do mercado, tempo, dia da semana. Todos eles podem ser usados para fazer a seleção de negociações desvantajosas. É bastante fácil adicionar um filtro do tipo a um expert advisor: basta adicionar mais uma condição antes do bloco de abertura.
Mas o que deve ser feito caso você queira usar o histórico do EA como um filtro? Se você desligar o seu sistema de negociação após várias negociações mal sucedidas, você não terá um histórico mais tarde, e portanto não haverá nada a ser analisado. Para resolver este problema, nós precisamos ensinar o expert advisor a realizar negociações virtualmente, ou seja, simular a abertura, modificação e fechamento de negociações sem realizar negociações reais.
É disso que trata este artigo.
Estratégia experimental
Para a implementação do nosso sistema nós faremos algumas modificações no arquivo CrossMACD_DeLuxe.mq4 do expert advisor:
- durante a abertura/modificação/fechamento de cada posição, mudanças serão escritas em uma matriz de posições virtuais;
- adição de monitoramento de atuação do StopLoss e TakeProfit de posições virtuais;
- adição de um critério de filtragem: uma condição na qual negociações reais não serão abertas.
Contabilização de posições virtuais
Então, um sinal para abrir uma posição surgiu. Os parâmetros StopLoss e TakeProfit são calculados, e tudo está pronto para a chamada da função OrderSend(). Precisamente neste momento nós abrimos a negociação virtual - basta salvar todos os parâmetros necessários nas variáveis apropriadas:
void OpenBuy() { int _GetLastError = 0; double _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel; _OpenPriceLevel = NormalizeDouble(Ask, Digits); if(StopLoss > 0) _StopLossLevel = NormalizeDouble(_OpenPriceLevel - StopLoss*Point, Digits); else _StopLossLevel = 0.0; if(TakeProfit > 0) _TakeProfitLevel = NormalizeDouble(_OpenPriceLevel + TakeProfit*Point, Digits); else _TakeProfitLevel = 0.0; //---- open the virtual position virtOrderSend(OP_BUY, _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel); if(OrderSend(Symbol(), OP_BUY, 0.1, _OpenPriceLevel, 3, _StopLossLevel, _TakeProfitLevel, "CrossMACD", _MagicNumber, 0, Green) < 0) { _GetLastError = GetLastError(); Alert("Error OrderSend № ", _GetLastError); return(-1); } } //---- Save parameters of the opened position in main variables void virtualOrderSend(int type, double openprice, double stoploss, double takeprofit) { virtTicket = 1; virtType = type; virtOpenPrice = openprice; virtStopLoss = stoploss; virtTakeProfit = takeprofit; }
Nós podemos usar apenas cinco variáveis:
int virtTicket = 0; // determines, if there is an open virtual position int virtType = 0; // position type double virtOpenPrice = 0.0; // position opening price double virtStopLoss = 0.0; // position StopLoss double virtTakeProfit = 0.0; // position TakeProfit
Nós não precisamos de outras características para obter a salvação da nossa tarefa. Se você quiser ampliar a funcionalidade deste exemplo, basta adicionar a quantidade necessária de variáveis.
Para rastrear o fechamento e a modificação de posições, nós precisamos tomar mais medidas. Copie o bloco de controle de posições abertas, que está no expert advisor, e transforme as características do pedido em características virtuais:
int start() { // skipped... //+------------------------------------------------------------------+ //| Control block of "virtual" positions | //+------------------------------------------------------------------+ if(virtTicket > 0) { //---- if BUY-position is open, if(virtType == OP_BUY) { //---- if MACD crossed 0-line downwards, if(NormalizeDouble(MACD_1 + CloseLuft*Point*0.1, Digits + 1) <= 0.0) { //---- close position virtOrderClose(Bid); } //---- if the signal did not change, accompany the position by // TrailingStop else { if(TrailingStop > 0) { if(NormalizeDouble(Bid - virtOpenPrice, Digits ) > 0.0) { if(NormalizeDouble( Bid - TrailingStop*Point - virtStopLoss, Digits) > 0.0 || virtStopLoss < Point) { virtStopLoss = Bid - TrailingStop*Point; } } } } } //---- if SELL position is open if(virtType == OP_SELL) { //---- if MACD crossed 0-line upwards, if(NormalizeDouble(MACD_1 - CloseLuft*Point*0.1, Digits + 1 ) >= 0.0) { //---- close the position virtOrderClose(Ask); } //---- if the signal did not change, accompany the position by // TrailingStop else { if ( TrailingStop > 0 ) { if(NormalizeDouble( virtOpenPrice - Ask, Digits ) > 0.0 ) { if(NormalizeDouble( virtStopLoss - ( Ask + TrailingStop*Point ), Digits ) > 0.0 || virtStopLoss <= Point ) { virtStopLoss = Ask + TrailingStop*Point; } } } } } } // skipped... return(0); } //---- virtual position closing function void virtOrderClose(double closeprice) { //---- Save the parameters of the closed position in the array ArrayResize(virtClosedOrders, virtClosedOrdersCount + 1); virtClosedOrders[virtClosedOrdersCount][0] = virtType; virtClosedOrders[virtClosedOrdersCount][1] = virtOpenPrice; virtClosedOrders[virtClosedOrdersCount][2] = virtStopLoss; virtClosedOrders[virtClosedOrdersCount][3] = virtTakeProfit; virtClosedOrders[virtClosedOrdersCount][4] = closeprice; virtClosedOrdersCount ++; //---- clear variables virtTicket = 0; virtType = 0; virtOpenPrice = 0.0; virtStopLoss = 0.0; virtTakeProfit = 0.0; }
A modificação se transformou na simples atribuição de um novo valor à variável virtStopLoss. E o fechamento é bastante difícil: todas as características do pedido fechado são salvas em uma matriz. Mais tarde, todo o histórico virtual será salvo nela. A partir dela, nós iremos obter as informações sobre as posições fechadas para tomar uma decisão a respeito da abertura de uma nova posição.
Agora nós precisamos processar o fechamento da posição no StopLoss e no TakeProfit. Para isso, adicione várias strings ao bloco de controle criado.
if(virtType == OP_BUY) { //---- check, whether SL was not activated if(virtStopLoss > 0.0 && NormalizeDouble(virtStopLoss - Bid, Digits ) >= 0.0) { virtOrderClose(virtStopLoss); } //---- check, whether TPL was not activated if(virtTakeProfit > 0.0 && NormalizeDouble( Bid - virtTakeProfit, Digits ) >= 0.0) { virtOrderClose(virtTakeProfit); } }
Agora o nosso histórico virtual está pronto e nós podemos adicionar um critério de filtragem.
O que é "bom" e o que é "ruim"?
Nós precisamos proibir a abertura de posições após uma determinada condição ser implementada. Mas que condição devemos escolher? Várias negociações desvantajosas em sequência, a ativação do StopLoss ou a redução do lucro médio de várias negociações recentes. Certamente é difícil responder a essa pergunta. Cada opção possui as suas vantagens e desvantagens.
Para verificar a eficiência de cada condição, vamos tentar escrever o código de todas as três e testá-las no histórico.
extern int TradeFiltrVariant = 0; //---- Function of checking the necessity of the real trading bool virtCheckCondition() { int pos, check_pos = 2; double last_profit = 0.0, pre_last_profit = 0.0; //---- depending on the value of TradeFiltrVariant: switch(TradeFiltrVariant) { //---- 1: prohibit real trading, if 2 last deals are losing case 1: { //---- if the virtual history contains enough orders, if(virtClosedOrdersCount >= check_pos) { for(pos = 1; pos check_pos; pos ++) { //---- if the deal is profitable, return true if((virtClosedOrders[virtClosedOrdersCount-pos][0] == 0 && virtClosedOrders[virtClosedOrdersCount-pos][4] - virtClosedOrders[virtClosedOrdersCount-pos][1] >= 0.0) || (virtClosedOrders[virtClosedOrdersCount-pos][0] == 1 && virtClosedOrders[virtClosedOrdersCount-pos][1] - virtClosedOrders[virtClosedOrdersCount-pos][4] >= 0.0)) { return(true); } } } return(false); } //---- 2: prohibit real trading if the last position was closed // by StopLoss case 2: { //---- if the virtual history contains enough orders, if(virtClosedOrdersCount > 0) { //---- if the closing price of the last order is equal to StopLoss, if(virtClosedOrders[virtClosedOrdersCount-1][2] - virtClosedOrders[virtClosedOrdersCount-1][4] < Point && virtClosedOrders[virtClosedOrdersCount-1][4] - virtClosedOrders[virtClosedOrdersCount-1][2] < Point) { return(false); } } return(true); } //---- 3: prohibit real trading, if the profit of the last position //---- is lower than that of the last but one position (or loss is higher) case 3: { if(virtClosedOrdersCount >= 2) { if(virtClosedOrders[virtClosedOrdersCount-1][0] == 0) { last_profit = virtClosedOrders[virtClosedOrdersCount-1][4] - virtClosedOrders[virtClosedOrdersCount-1][1]; } else { last_profit = virtClosedOrders[virtClosedOrdersCount-1][1] - virtClosedOrders[virtClosedOrdersCount-1][4]; } if(virtClosedOrders[virtClosedOrdersCount-2][0] == 0) { pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][4] - virtClosedOrders[virtClosedOrdersCount-2][1]; } else { pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][1] - virtClosedOrders[virtClosedOrdersCount-2][4]; } if(pre_last_profit - last_profit > 0.0) { return(false); } } return(true); } //---- by default the filter is off, i.e. positions will be always opened in reality default: return(true); } return(true); } void OpenBuy() { int _GetLastError = 0; double _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel; _OpenPriceLevel = NormalizeDouble(Ask, Digits); if(StopLoss > 0) { _StopLossLevel = NormalizeDouble(_OpenPriceLevel - StopLoss*Point, Digits); } else { _StopLossLevel = 0.0; } if(TakeProfit > 0) { _TakeProfitLevel = NormalizeDouble(_OpenPriceLevel + TakeProfit*Point, Digits); } else { _TakeProfitLevel = 0.0; } //---- open a virtual position virtOrderSend(OP_BUY, _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel); //---- if virtual positions filter prohibits trading, exit if(virtCheckCondition() == false) { return(0); } if(OrderSend( Symbol(), OP_BUY, 0.1, _OpenPriceLevel, 3, _StopLossLevel, _TakeProfitLevel, "CrossMACD", _MagicNumber, 0, Green) < 0 ) { _GetLastError = GetLastError(); Alert("Error OrderSend № ", _GetLastError); return(-1); } }
Agora nós temos uma variável externa, TradeFiltrVariant. Ela é responsável pela escolha do critério de filtragem:
extern int TradeFiltrVariant = 0; //---- 0: filter is off, i.e. position is always opened in reality //---- 1: prohibit real trading, if 2 last positions are losing //---- 2: prohibit real trading, if the last position closed by StopLoss //---- 3: prohibit real trading, if the profit of the last position is lower, //---- than that of the last but one psition (or loss is higher)
Agora teste o expert advisors com filtros diferentes e compare os resultados.
O jogo não vale a candle?
Eu escolhi os seguintes parâmetros para o teste:
Símbolo: Período GBPUSD
: H4, 01.01.2005 - 01.01.2006
Modo de modelagem: todos os ticks (qualidade de modelagem 90%, cotas do HistoryCenter)
Parâmetros do EA:
StopLoss: 50
TakeProfit: 0 (desativado)
TrailingStop: 0 (desativado)
FastEMAPeriod: 12
SlowEMAPeriod: 26
OpenLuft: 10
CloseLuft: 0
A seguinte tabela mostra a dependência dos resultados em relação ao filtro usado:
| TradeFiltrVariant | Lucro/perda total | Total de negociações | Negociações lucrativas | Negociações desvantajosas |
|---|---|---|---|---|
| 0 | 1678,75 |
41 | 9 (22%) |
32 (78%) |
| 1 | 105,65 | 20 |
2 (10%) |
18 (90%) |
| 2 | -550,20 | 11 | 0 (0%) |
11 (100%) |
| 3 | 1225,13 | 28 | 7 (25%) |
21 (75%) |
Como você pode ver, a teoria a respeito da utilidade do filtro não foi comprovada. Além disso, os resultados das negociações com o filtro foram mais baixos do que aqueles das negociações sem o filtro. A única exceção foi a terceira opção - a taxa de negociações lucrativas foi mais alta (25 % contra 22%), mas o lucro total foi menor com todas as opções.
O que deu errado? Provavelmente, o critério de filtragem estava errado. Vamos tentar transformar esses três filtros nos filtros opostos, ou seja, fechar negociações reais se:
- as duas últimas negociações tiverem sido lucrativas;
- a última posição for lucrativa (nós não temos um análogo ao StopLoss, pois o TakeProfit está desativado);
- O lucro da última posição for maior do que o da penúltima.
//---- 4: prohibit real trading, if two last trades are profitable //---- 5: prohibit real trading, if the last position is profitable //---- 6: prohibit real trading, if the profit of the last position is higher, //---- than that of the last but one position (or loss is lower) //---- 4: prohibit real trading, if two last trades are profitable case 4: { if(virtClosedOrdersCount >= check_pos) { for(pos = 1; pos check_pos; pos ++) { //---- if the trade is losing, return true if((virtClosedOrders[virtClosedOrdersCount-pos][0] == 0 && virtClosedOrders[virtClosedOrdersCount-pos][1] - virtClosedOrders[virtClosedOrdersCount-pos][4] > 0.0) || (virtClosedOrders[virtClosedOrdersCount-pos][0] == 1 && virtClosedOrders[virtClosedOrdersCount-pos][4] - virtClosedOrders[virtClosedOrdersCount-pos][1] > 0.0)) { return(true); } } } return(false); } //---- 5: prohibit real trading, if the last position is profitable case 5: { if(virtClosedOrdersCount >= 1) { if(virtClosedOrders[virtClosedOrdersCount-1][0] == 0) { last_profit = virtClosedOrders[virtClosedOrdersCount-1][4] - virtClosedOrders[virtClosedOrdersCount-1][1]; } else { last_profit = virtClosedOrders[virtClosedOrdersCount-1][1] - virtClosedOrders[virtClosedOrdersCount-1][4]; } if(last_profit > 0.0) { return(false); } } return(true); } //---- 6: prohibit real trading, if the profit of the last position is higher, //---- than that of the last but one position (or loss is lower) case 6: { if(virtClosedOrdersCount >= 2) { if(virtClosedOrders[virtClosedOrdersCount-1][0] == 0) { last_profit = virtClosedOrders[virtClosedOrdersCount-1][4] - virtClosedOrders[virtClosedOrdersCount-1][1]; } else { last_profit = virtClosedOrders[virtClosedOrdersCount-1][1] - virtClosedOrders[virtClosedOrdersCount-1][4]; } if(virtClosedOrders[virtClosedOrdersCount-2][0] == 0) { pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][4] - virtClosedOrders[virtClosedOrdersCount-2][1]; } else { pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][1] - virtClosedOrders[virtClosedOrdersCount-2][4]; } if(last_profit - pre_last_profit > 0.0) { return(false); } } return(true); }
Agora vamos testar essas três novas opções e adicioná-las à nossa tabela:
| AdaptVariant | Lucro/perda total | Total de negociações | Transações lucrativas | Negociações desvantajosas |
|---|---|---|---|---|
| 0 | 1678,75 |
41 | 9 (22%) |
32 (78%) |
| 1 | 105,65 | 20 | 2 (10%) |
18 (90%) |
| 2 | -550,20 | 11 | 0 (0%) |
11 (100%) |
| 3 | 1225,13 | 28 | 7 (25%) |
21 (75%) |
| 4 |
1779,24 |
39 | 9 (23%) | 30 (77%) |
| 5 |
2178,95 |
31 |
9 (29%) |
22 (71%) |
| 6 |
602,32 |
24 |
5 (21%) |
19 (79%) |
A sexta opção filtrou metade das negociações, tanto as lucrativas quanto as desvantajosas. A quarta excluiu duas negociações desvantajosas, aumentado o lucro total em $100,49.
E a melhor opção foi aquela que proíbe negociações após toda negociação lucrativa: ela filtrou 10 negociações desvantajosas e nenhuma lucrativa.
Portanto, há esperança. Até mesmo uma estratégia tão simples e popular pode ser melhorada!
Conclusão
Eu acho que este tipo de filtro não é suficiente para uma melhoria real do sistema. Pesquisas muito mais profundas devem ser realizadas, e novas conclusões devem ser feitas.
Os filtros descritos podem ser tornados mais complexos e absolutamente diferentes para cada estratégia. A sua eficiência depende diretamente da troca entre as posições lucrativas e desvantajosas.
Este artigo se limita à questão da filtragem. Mas eu espero que ele lhe inspire a ir além.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/1441
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.