He estado trabajando en un código y llegue a un punto donde me estoy dando cuenta que el backtest se comporta de manera errática realizando acciones y modificando variables que no se ven en el "Journal" del backtest pero si puedo verlas reflejadas al imprimir estas variables. Principalmente algo que no llego a entender es como el test trabaja sobre el balance. En mi código cada vez que se envía una orden de compra uso la "funcionAccountInfoDouble(ACCOUNT_MARGIN_FREE)" para obtener el margin disponible en ese momento y calcular el volumen acorde y asi evitar problemas de falta de dinero o un volumen grande en la compra. Al mismo tiempo me aseguro de cerrar la orden antes de abrir otra de forma que no queden ordenes abiertas que pudieran estar tomando parte del free margin. A manera de controlar imprimo cada vez que se ejecuta una compra el valor actual del balance, free margin, precio, volumen, etc. Y lo que estoy viendo es que durante el backtest el valor de free margin, equity y balance se ven disminuidos drasticamente como si deliberadamente el test apalancara la orden por 10 en momento donde hay perdidas con porcentajes altos, aún cuando las opciones de tester no están habilitando el apalancamiento. Lo que es peor al correr el mismo EA con diferentes depositos iniciales veo que a partir de determinado valor el algoritmo pasa de realizar casi 60 trades como deberñia a realizar 1 solo. Por ejemplo si el balance es 4000 hace 60 trades pero si el balance es 3000 hace 1 solo, este limite se da en torno al valor en divisa del volumen maximo permitido por el broker pero no estoy seguro si esta relacionado con esto. Quisiera saber primero porque el backtest tiene este comportamiento y que cosas se pueden mejorar el código para evitarlo, es decir si existe variable ocultas que no se ven o mejores formas para trabajar con el balance disponible o si existe una cantidad de variables internas que no se ven pero que afectan el comportamiento y por tanto más allá de la información en la cuenta tenemos que trabajar con ellas por ejemplo he notado que si imprimo el resultado de "ACCOUNT_MARGIN_FREE" obtengo un valor distinto a funcionAccountInfoDouble(ACCOUNT_MARGIN_FREE), es como si hubiera un valor de free margin distinto al que vemos en la cuenta, esto me hace sospechar que mas alla del volumen que pongo en la compra puede haber variables ocultas de apalancamiento u otras que estan teniendo efectos inesperados en el test. Les dejo el código:
//+------------------------------------------------------------------+//| RobotSMA.mq5 |//| Copyright 2024, MetaQuotes Ltd. |//| https://www.mql5.com |//+------------------------------------------------------------------+#property copyright"Copyright 2024, MetaQuotes Ltd."#property link"https://www.mql5.com"#property version"1.00"//---#include <Trade\Trade.mqh>
//#include <Trade\AccountInfo.mqh>// Input Variablesinputint ma1_period = 8; // MA Slowinputint ma2_period = 50; // MA Fastinputint rsi_period = 14; // RSI periodinputint rsi_bz = 30; // RSI Buy Zoneinputint rsi_sz = 70; // RSI Sell Zoneinputint macd_fast = 12; // MACD fast EMA periodinputint macd_slow = 26; // MACD slow EMA periodinputint macd_signal = 12; // MACD signal line periodinputdouble pips_sl = 10; // Stop Lossinputdouble pips_tp = 0.0112; // Take Profit// Internal Variablesdatetime date = 0; // Declaración de la variable 'date'double entry = 0;
int handle_ma1 = 0;
int handle_ma2 = 0;
int handle_rsi = 0; // Declaration of 'handle_rsi'int handle_macd = 0;
double array_ma_fast[];
double array_ma_slow[];
double array_rsi[];
double array_macd_main[];
double array_macd_signal[];
double array_macd_histogram[];
double perc_sl = 1-pips_sl/100;
double perc_tp = 1+pips_tp/100;
double balance = 0;
double balance2 = 0;
double balance3 = 0;
double balance4 = 0;
double balance5 = 0;
double vol=0;
int ticket = 0;
bool result;
double last_buy =0;
bool buy_context = false;
int retryAttempts = 3;
int retryCount = 0;
CTrade trade;
//CAccountInfo account;//+------------------------------------------------------------------+//| Expert initialization function |//+------------------------------------------------------------------+intOnInit()
{
//---
handle_ma1 = iMA(Symbol(),Period(),ma1_period, 0, MODE_EMA,PRICE_CLOSE);
handle_ma2 = iMA(Symbol(),Period(),ma2_period, 0, MODE_EMA,PRICE_CLOSE);
handle_rsi = iRSI(Symbol(), Period(), rsi_period, PRICE_CLOSE);
handle_macd = iMACD(Symbol(), Period(), macd_fast, macd_slow, macd_signal,PRICE_CLOSE);
//---return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+//| Expert deinitialization function |//+------------------------------------------------------------------+voidOnDeinit(constint reason)
{
//---
}
//+------------------------------------------------------------------+//| Expert tick function |//+------------------------------------------------------------------+voidOnTick()
{
datetime LocalTime=TimeLocal();
string HoursAndMinutes=TimeToString(LocalTime,TIME_MINUTES);
string YearAndDate=TimeToString(LocalTime,TIME_DATE);
MqlDateTime DateTimeStructure;
TimeToStruct(LocalTime, DateTimeStructure);
int day = DateTimeStructure.day_of_week;
int hour = DateTimeStructure.hour;
if (iTime(Symbol(),Period(),0) != date){
entry = SymbolInfoDouble(Symbol(), SYMBOL_BID);
if (Buy_Signal() && buy_context == false){
entry = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
balance = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
balance2 = AccountInfoDouble(ACCOUNT_EQUITY);
balance3 = AccountInfoDouble(ACCOUNT_BALANCE);
balance4 = AccountInfoDouble(ACCOUNT_CREDIT);
balance5 = AccountInfoDouble(ACCOUNT_PROFIT);
vol = balance/entry;
vol = MathFloor(vol * MathPow(10, 1)) / MathPow(10, 1);
double min_volume = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN),_Digits);
double max_volume = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX),_Digits);
if(vol > max_volume){vol = max_volume;}
Print("Max Volume=",max_volume,"Min Volume=",min_volume);
Print("Volume=",NormalizeDouble(vol,_Digits), " Price=", NormalizeDouble(entry, _Digits), " Free Margin=", balance, " Equity=", balance2, " Balance=", balance3, " Credit=", balance4);
result = trade.Buy(NormalizeDouble(vol,_Digits), Symbol(), NormalizeDouble(entry, _Digits), "Buy Order");
if(result){
Print("Buy Order Executed correctly");
}else{
Print("Buy Order Failed");
ExpertRemove();
}
buy_context = true;
last_buy = entry;
}elseif (Sell_Signal() && buy_context == true){
//ticket = GetOpenPositionTicket();
ticket = PositionGetTicket(PositionsTotal()-1);
Print("Open position ticket: ", ticket);
result = trade.PositionClose(ticket);
// Check the result of the closing operationif (result){
Print("Order with ticket ", ticket, " closed successfully.");
buy_context = false;
}
else{
Print("Error closing order with ticket ", ticket, ". Error code: ", GetLastError());
}
}
elseif (buy_context == true && entry < last_buy * perc_sl){
//ticket = GetOpenPositionTicket();
ticket = PositionGetTicket(PositionsTotal()-1);
Print("Open position ticket: ", ticket);
result = trade.PositionClose(ticket);
// Check the result of the closing operationif (result){
Print("Order with ticket ", ticket, " closed successfully.");
buy_context = false;
}
else{
Print("Error closing order with ticket ", ticket, ". Error code: ", GetLastError());
}
}
elseif (buy_context == true && entry > last_buy * perc_tp){
//ticket = GetOpenPositionTicket();
ticket = PositionGetTicket(PositionsTotal()-1);
Print("Open position ticket: ", ticket);
result = trade.PositionClose(ticket);
// Check the result of the closing operationif (result){
Print("Order with ticket ", ticket, " closed successfully.");
buy_context = false;
}
else{
Print("Error closing order with ticket ", ticket, ". Error code: ", GetLastError());
}
}
elseif (buy_context == true && day == 5 && hour == 22){
Print("Closing open orders because is the end of Friday.");
//ticket = GetOpenPositionTicket();
ticket = PositionGetTicket(PositionsTotal()-1);
Print("Open position ticket: ", ticket);
result = trade.PositionClose(ticket);
// Check the result of the closing operationif (result){
Print("Order with ticket ", ticket, " closed successfully.");
buy_context = false;
}
else{
Print("Error closing order with ticket ", ticket, ". Error code: ", GetLastError());
}
}
date = iTime(Symbol(),Period(),0);
}
}
//+------------------------------------------------------------------+// Funciones bool Buy_Signal (){
// if (array_ma_fast[0] > array_ma_slow[0] && array_ma_fast[1] < array_ma_slow[1] &&ArraySetAsSeries(array_ma_fast,true);
ArraySetAsSeries(array_ma_slow,true);
CopyBuffer(handle_ma1,0,1,2,array_ma_fast);
CopyBuffer(handle_ma2,0,1,2,array_ma_slow);
ArraySetAsSeries(array_rsi, true);
CopyBuffer(handle_rsi, 0, 1, 2, array_rsi);
ArraySetAsSeries(array_macd_main, true);
ArraySetAsSeries(array_macd_signal, true);
ArraySetAsSeries(array_macd_histogram, true);
CopyBuffer(handle_macd, 0, 1, 2, array_macd_main);
CopyBuffer(handle_macd, 1, 1, 2, array_macd_signal);
CopyBuffer(handle_macd, 2, 1, 2, array_macd_histogram);
if( array_rsi[0] < rsi_bz && // RSI is below 30 (oversold condition)
array_macd_main[0] > array_macd_signal[0] && array_macd_main[1] < array_macd_signal[1]){
returntrue;
} else {
returnfalse;
}
}
bool Sell_Signal(){
ArraySetAsSeries(array_ma_fast,true);
ArraySetAsSeries(array_ma_slow,true);
CopyBuffer(handle_ma1,0,1,2,array_ma_fast);
CopyBuffer(handle_ma2,0,1,2,array_ma_slow);
ArraySetAsSeries(array_rsi, true);
CopyBuffer(handle_rsi, 0, 1, 2, array_rsi);
ArraySetAsSeries(array_macd_main, true);
ArraySetAsSeries(array_macd_signal, true);
ArraySetAsSeries(array_macd_histogram, true);
CopyBuffer(handle_macd, 0, 1, 2, array_macd_main);
CopyBuffer(handle_macd, 1, 1, 2, array_macd_signal);
CopyBuffer(handle_macd, 2, 1, 2, array_macd_histogram);
// if (array_ma_fast[0] < array_ma_slow[0] && array_ma_fast[1] > array_ma_slow[1] &&if (array_rsi[0] > rsi_sz){ // RSI is above 70 (overbought condition)// if (array_macd_main[0] < array_macd_signal[0] && array_macd_main[1] > array_macd_signal[1]){returntrue;
} else {
returnfalse;
}
}
ulong GetOpenPositionTicket()
{
for (int i = 0; i < PositionsTotal(); i++)
{
ulong ticket = PositionGetTicket(i);
// Check if the position is openif (PositionSelectByTicket(ticket) && PositionGetSymbol(i) == Symbol() && PositionGetInteger(POSITION_TYPE) <= ORDER_TYPE_BUY)
{
return ticket;
}
}
return0; // No open position found
}
Para iniciar sesión y usar el sitio web MQL5.com es necesario permitir el uso de Сookies.
Por favor, active este ajuste en su navegador, de lo contrario, no podrá iniciar sesión.
Ha iniciado sesión. La autenticación de dos factores se ha desactivado. Vuelva a configurarla y escanee un nuevo código QR, o continúe con la autenticación de dos factores desactivada.
He estado trabajando en un código y llegue a un punto donde me estoy dando cuenta que el backtest se comporta de manera errática realizando acciones y modificando variables que no se ven en el "Journal" del backtest pero si puedo verlas reflejadas al imprimir estas variables. Principalmente algo que no llego a entender es como el test trabaja sobre el balance. En mi código cada vez que se envía una orden de compra uso la "funcionAccountInfoDouble(ACCOUNT_MARGIN_FREE)" para obtener el margin disponible en ese momento y calcular el volumen acorde y asi evitar problemas de falta de dinero o un volumen grande en la compra. Al mismo tiempo me aseguro de cerrar la orden antes de abrir otra de forma que no queden ordenes abiertas que pudieran estar tomando parte del free margin. A manera de controlar imprimo cada vez que se ejecuta una compra el valor actual del balance, free margin, precio, volumen, etc. Y lo que estoy viendo es que durante el backtest el valor de free margin, equity y balance se ven disminuidos drasticamente como si deliberadamente el test apalancara la orden por 10 en momento donde hay perdidas con porcentajes altos, aún cuando las opciones de tester no están habilitando el apalancamiento. Lo que es peor al correr el mismo EA con diferentes depositos iniciales veo que a partir de determinado valor el algoritmo pasa de realizar casi 60 trades como deberñia a realizar 1 solo. Por ejemplo si el balance es 4000 hace 60 trades pero si el balance es 3000 hace 1 solo, este limite se da en torno al valor en divisa del volumen maximo permitido por el broker pero no estoy seguro si esta relacionado con esto.
Quisiera saber primero porque el backtest tiene este comportamiento y que cosas se pueden mejorar el código para evitarlo, es decir si existe variable ocultas que no se ven o mejores formas para trabajar con el balance disponible o si existe una cantidad de variables internas que no se ven pero que afectan el comportamiento y por tanto más allá de la información en la cuenta tenemos que trabajar con ellas por ejemplo he notado que si imprimo el resultado de "ACCOUNT_MARGIN_FREE" obtengo un valor distinto a funcionAccountInfoDouble(ACCOUNT_MARGIN_FREE), es como si hubiera un valor de free margin distinto al que vemos en la cuenta, esto me hace sospechar que mas alla del volumen que pongo en la compra puede haber variables ocultas de apalancamiento u otras que estan teniendo efectos inesperados en el test. Les dejo el código: