Это тестер или демо?
Местами у Вас вместо "else if" стоит просто "if". По какой-то причине используете вложенность if-ов, вместо того, чтобы использовать один if и &&.
Дорогим видится этот кусок
Посмотрите в сторону параметра reserve_size.
Сделал замер исполнения всей функции OnTick получил в среднем 100-200 мс, но почему в тестере без визуализации идет тестирование медленное мне непонятно. Такое ощуение, что где то идет утечка и в процессе тестирования когда ордеров становиться много (советеник с усреднениями в 2 стороны) и скорость падает до 400-500 мс !!!
Для желающих покопаться прикладываю файл советника с текущей библиотекой.
Это тестер или демо?
Местами у Вас вместо "else if" стоит просто "if". По какой-то причине используете вложенность if-ов, вместо того, чтобы использовать один if и &&.
Дорогим видится этот кусок
Посмотрите в сторону параметра reserve_size.
все замеры в тестере, мне важно ускорить тестирование советников.
Похоже нашел я источник повышающейся нагрузки, у меня автоматически работает поддержка виртуальных стпоов и тейков, при этом храню данные о них в глобальных переменных и каждый тик опрашивает советник , не изменилось ли чего и не пора ли закрыть ордер принудительно.
вот сама функция она и отжирает в зависимости от кол-ва ордеров до 400 мс
{
//id_tp, id_sl
if(!options_bVirtual) return; //если виртуализация выключена, то не проверяем
string del[],sglobal="",param[],n;
int icount=0,i,itype=-1,isizeParam;
ArrayResize(del,0);
int isize=GlobalVariablesTotal();
for(i=isize-1;i>=0;i--)
{
sglobal=GlobalVariableName(i);
if(sglobal=="" || sglobal==NULL) continue;
if(StringFind(sglobal,"_sl")<0 && StringFind(sglobal,"_tp")<0) continue; //пропустим не наши
if(GlobalVariableGet(sglobal)==0) //все нулевые удалим
{
ArrayResize(del,icount+1);
del[icount]=sglobal;
icount++;
continue;
}
string_explode("_",sglobal,param);
isizeParam=ArraySize(param);
if(isizeParam!=2 && isizeParam!=3) continue; //пропустим сразу неподходящие под наши правила
if((isizeParam==2 && StringToInteger(param[0])>0) || (isizeParam==3 && param[0]=="virtual" && StringToInteger(param[1])>0))
{
int iticket=(int)StringToInteger(param[0]);//тикет
if(isizeParam==3)
{
iticket=(int)StringToInteger(param[1]);//тикет
}
//check dtp,sl
if(!OrderSelect(iticket)) continue;
itype=OrderType();
if(itype>=2 || OrderCloseTime()>0) //отложки тоже подчистим и уже закрытых ордеров тоже
{
ArrayResize(del,icount+1);
del[icount]=sglobal;
icount++;
continue; //next order
}
n=OrderSymbol();
if(CheckMagic(OrderMagicNumber()) && CheckSymbol(n))
{
double pt=0;
double dtp=0;
double dsl=0;
//получим сразу 2 и стоп и тейк, на следующей итерации подчистим, если ордер был закрыт на этом шаге
dtp=virtual_get_tp(iticket);
dsl=virtual_get_sl(iticket);
if(dtp!=0 || dsl!=0)
{
RefreshRates();
}
if(dtp==0 && dsl==0) continue;
double _Ask=Ask(n);
double _Bid=Bid(n);
if(dtp!=0 && ((_Bid>=dtp && itype==OP_BUY) || (_Ask<=dtp && itype==OP_SELL)))
{
if(OrderClose(iticket,-1))
{
Print("Close ",iticket);
ArrayResize(del,icount+1);
del[icount]=sglobal;
icount++;
m_total--;
}
}//close by dtp
if(dsl!=0 && ((_Bid<=dsl && itype==OP_BUY) || (_Ask>=dsl && itype==OP_SELL)))
{
if(OrderClose(iticket,-1))
{
Print("Close ",iticket);
ArrayResize(del,icount+1);
del[icount]=sglobal;
icount++;
m_total--;
}
}//close by dstop
}
}
}
//delete global
if(icount>0)
{
for(i=0;i<icount;i++)
{
if(del[i]!="" && GlobalVariableCheck(del[i]))
GlobalVariableDel(del[i]);
}
}
}
COrderInfo должен быть структурой.
сделал. Но не думаю что структура сильно отличается от класса в работе. Хотя да логично бы ее использовать как структуру.
Начал копать функцию virtual_init . Все остальное исполняется очень быстро, а вот она дает ощутимые задержки, пока повторяющиеся проверки в ее функциях сделал запоминание на весь класс.
Но особой скорости это не дало. может 50 мс выиграл.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Уже много лет делаю советники для МТ4 с использованием библиотек,в которых все нужные функции в обертке классов.
Решил заняться серьезно оптимизацией и вот с чем столкнулся, скорость в тестере без визуализации на загрузку данных о ордерах занимает в среднем 20-25 мс, хочу снизить, но похоже предел.
Приведу только необходимые функции, может кто подскажет как можно ускорить, данные о ордерах хранит специальный класс, через переменные которого я могу обратиться к любому ордеру по его индексу или тикету.
Вот основная функция загрузки данных о ордерах, так как у меня реализована поддержка открытия ордеров если лот превышает максимальный, то надо грузить все ордера по инструменту без учета их меджика, и уже потом отбирать при необходимости нужные.
int Update()
{
CSpeed s1;
s1.Start(__DEBUG__);
__COPYRIGHT__
ArrayResize(m_orders,0);
m_all=0;
ArrayInitialize(orders,0);
int isize=0;
int itotal=OrdersTotal();
m_total=itotal; //запомним дял проверок кол-ва
for(int i=itotal-1;i>=0;i--)
{
if(!OrderSelect(i,SELECT_BY_POS)) continue; //если не выбирает, пропускаем
if(CheckSymbol(OrderSymbol()))
{
if((!options_bSupportMaxLot && CheckMagic(OrderMagicNumber())) || options_bSupportMaxLot) //поддержка лотв больше чем максимальный
{
isize=ArraySize(m_orders); //кол-во оредров в структуре
if(ArrayResize(m_orders,isize+1)!=-1)
{
if(m_orders[isize].getOrderByTicket(OrderTicket())) //загрузим информацию по ордеру (грузим все ордера)
{
if(CheckMagic(m_orders[isize].OrderMagicNumber))
{
orders[m_orders[isize].OrderType]++; //увеличим счетчик
m_all++;
}
}
}
}
}
}
s1.Stop(__FUNCTION__);
return m_all; //вернем кол-во ордеров в структуре
}
Как реализован m_orders?
Есть класс COrderInfo в классе ордеров объявлен массив этого класса.
{
public:
string OrderSymbol;
string OrderComment;
int OrderTicket;
int OrderType;
int OrderMagicNumber;
datetime OrderOpenTime;
datetime OrderCloseTime;
datetime OrderExpiration;
double OrderOpenPrice;
double OrderClosePrice;
double OrderLots;
double OrderProfit;
double Profit; //суммарный профит
double OrderProfitPoint; //профит убыток в пунктах, если оредер будет закрыт
double OrderStep; //считаем шаг между ордерами, без учета их закрытия (для бай между аск, для селл между бид)
double OrderSwap;
double OrderCommission;
double OrderStopLoss;
double OrderTakeProfit;
double ProfitPoint() //ордер должен быть выбран!
{
string n=OrderSymbol;
double dpp=point(n);
if(dpp==0.0) return 0;
if(OrderCloseTime==0.0 && OrderType<2)
{
if(OrderType==OP_BUY) return (NormalizeDouble((MarketInfo(n,MODE_BID)-OrderOpenPrice)/dpp,1));
if(OrderType==OP_SELL) return (NormalizeDouble((OrderOpenPrice-MarketInfo(n,MODE_ASK))/dpp,1));
}
if(OrderCloseTime!=0.0 && OrderType<2)
{
if(OrderType==OP_BUY) return (NormalizeDouble((OrderClosePrice-OrderOpenPrice)/dpp,1));
if(OrderType==OP_SELL) return (NormalizeDouble((OrderOpenPrice-OrderClosePrice)/dpp,1));
}
return 0;
}
double StepPoint() //ордер должен быть выбран!
{
string n=OrderSymbol;
double dpp=point(n);
if(dpp==0.0) return 0;
if(OrderCloseTime==0.0 && OrderType<2)
{
if(OrderType==OP_BUY) return (NormalizeDouble((MarketInfo(n,MODE_ASK)-OrderOpenPrice)/dpp,1));
if(OrderType==OP_SELL) return (NormalizeDouble((OrderOpenPrice-MarketInfo(n,MODE_BID))/dpp,1));
}
if(OrderCloseTime!=0.0 && OrderType<2)
{
if(OrderType==OP_BUY) return (NormalizeDouble((OrderClosePrice-OrderOpenPrice)/dpp,1));
if(OrderType==OP_SELL) return (NormalizeDouble((OrderOpenPrice-OrderClosePrice)/dpp,1));
}
return 0;
}
void operator=(const int nil=NULL) //нулевая инициализация переменных
{
OrderSymbol="";
OrderComment="";
OrderTicket=0;
OrderType=-1;
OrderMagicNumber=0;
OrderOpenTime=0;
OrderCloseTime=0;
OrderExpiration=0;
OrderOpenPrice=0;
OrderClosePrice=0;
OrderLots=0;
OrderProfit=0;
Profit=0;
OrderProfitPoint=0;
OrderStep=0;
OrderSwap=0;
OrderCommission=0;
OrderStopLoss=0;
OrderTakeProfit=0;
}
void operator=(const COrderInfo &a1)//простое копирование
{
OrderSymbol=a1.OrderSymbol;
OrderComment=a1.OrderComment;
OrderTicket=a1.OrderTicket;
OrderType=a1.OrderType;
OrderMagicNumber=a1.OrderMagicNumber;
OrderOpenTime=a1.OrderOpenTime;
OrderCloseTime=a1.OrderCloseTime;
OrderExpiration=a1.OrderExpiration;
OrderOpenPrice=a1.OrderOpenPrice;
OrderClosePrice=a1.OrderClosePrice;
OrderLots=a1.OrderLots;
OrderProfit=a1.OrderProfit;
Profit=a1.Profit;
OrderProfitPoint=a1.OrderProfitPoint;
OrderStep=a1.OrderStep;
OrderSwap=a1.OrderSwap;
OrderCommission=a1.OrderCommission;
OrderStopLoss=a1.OrderStopLoss;
OrderTakeProfit=a1.OrderTakeProfit;
}
bool getOrderByTicket(int iticket,int imode=MODE_TRADES)
{
if(OrderTicket()!=iticket)
{
if(!OrderSelect(iticket,SELECT_BY_TICKET,imode)) return false;
}
if(iticket<=0) return false;
OrderSymbol=OrderSymbol();
OrderComment=OrderComment();
OrderTicket=OrderTicket();
OrderType=OrderType();
OrderMagicNumber=OrderMagicNumber();
OrderOpenTime=OrderOpenTime();
OrderCloseTime=OrderCloseTime();
OrderExpiration=OrderExpiration();
OrderOpenPrice=OrderOpenPrice();
OrderClosePrice=OrderClosePrice();
OrderLots=OrderLots();
OrderProfit=OrderProfit();
OrderProfitPoint=ProfitPoint();
OrderStep=StepPoint();
OrderSwap=OrderSwap();
OrderCommission=OrderCommission();
OrderStopLoss=OrderStopLoss();
OrderTakeProfit=OrderTakeProfit();
Profit=OrderProfit+OrderSwap+OrderCommission;
return true;
}
};
Для определения скорости обработки испольузется класс CSpeed
class CSpeed
{
protected:
ulong utimer;
bool bPrint;
public:
void Start(bool bprint)
{
bPrint=bprint;
if(bPrint)utimer=GetMicrosecondCount();
}
void Stop(string sfunc,string sdop="")
{
if(!bPrint) return; //выключено
ulong msec=GetMicrosecondCount()-utimer;
PrintFormat("CSpeed(%s): %s - %d ms",sdop,sfunc,msec);
}
void ReStart(string sfunc,string sdop="")
{
if(!bPrint) return; //выключено
ulong msec=GetMicrosecondCount()-utimer;
PrintFormat("CSpeed(%s): %s - %d ms",sdop,sfunc,msec);
utimer=GetMicrosecondCount();
}
};
Как считаете, можно ли добиться скорости загрузки данных о ордерах больше? Может както иначе код функции опроса реализовать?
Вообще по таким тестам больше всего у меня отжирают функции открытия ордеров от 36 до 300 мс
Вот вспомогательная функция которая отвечает за открытие сделок
int Trade(const int cmd,const double dlot,double dopen,const double dsl=0,const double dtp=0,const string _comment="",const datetime dt=0,const color cl=clrNONE,const bool tpslIsPoint=true,const bool bAddCount=true)
{
__COPYRIGHT__
ierrors=0;
if(cmd<0) return -1;
if(!CheckOrderSend(cmd,dlot))
{
//Alert("Enough money to make the operation");
ierrors=134;
return (-134);
}
if(dlot==0.0) return 0; //лот не задан. не открываемся
double ddlot=NLot(dlot,m_name); //округлим лот по правилам
p=point(m_name);
bool is_open=true;
bool bret=false;
string sinfo="";
double dstop=0,dtake=0;
double dnewopen=dopen;
int digit=digits(m_name);
int iticket=-1,j=0;
ierrors=0; //обнулим данные о последней ошибке
bool bOpenEmpty=false;
if(stoplevel==0) stoplevel=GetStopLevel(m_name)*MarketInfo(m_name,MODE_POINT);
double _Ask=Ask(m_name);
double _Bid=Bid(m_name);
while(is_open && j<options_iRestartCount && !IsStopped())
{
j++;
if(dopen==-1)
{
RefreshRates();
_Ask=Ask(m_name);
_Bid=Bid(m_name);
bOpenEmpty=true;
if(cmd==OP_BUY) dnewopen=_Ask;
if(cmd==OP_SELL) dnewopen=_Bid;
}
//проверим корректность тейка и стопа для постановки, иначе отклоним неверный запрос
if(cmd==OP_BUYSTOP && _Ask>=dnewopen-stoplevel)
{
return 0;
}
if(cmd==OP_SELLSTOP && _Bid<=dnewopen+stoplevel)
{
return 0;
}
if(iticket==-1)
{
dtake=0;dstop=0;
if(!tpslIsPoint)
{
if(cmd==OP_BUYSTOP || cmd==OP_BUYLIMIT)
{
if(dtp>0) dtake=dtp;
if(dsl>0) dstop=dsl;
}
if(cmd==OP_SELLSTOP || cmd==OP_SELLLIMIT)
{
if(dtp>0) dtake=dtp;
if(dsl>0) dstop=dsl;
}
}else{
if(cmd==OP_BUYSTOP || cmd==OP_BUYLIMIT)
{
if(dtp>0) dtake=dnewopen+dtp*p;
if(dsl>0) dstop=dnewopen-dsl*p;
}
if(cmd==OP_SELLSTOP || cmd==OP_SELLLIMIT)
{
if(dtp>0) dtake=dnewopen-dtp*p;
if(dsl>0) dstop=dnewopen+dsl*p;
}
}
if(cmd>=2)
{
if(!options_bNoModify) iticket=::OrderSend(m_name,cmd,ddlot,NormalizeDouble(dnewopen,digit),m_islippage,dstop,dtake,_comment,(int)m_imagic,dt,cl);
else iticket=::OrderSend(m_name,cmd,ddlot,NormalizeDouble(dnewopen,digit),m_islippage,0,0,_comment,(int)m_imagic,dt,cl);
if(iticket>0 && options_bVirtual) virtual_set(iticket,NormalizeDouble(dstop,digit),NormalizeDouble(dtake,digit)); //дублируем виртуальным режимом , он будет по умолчанию
}
else
{
iticket=::OrderSend(m_name,cmd,ddlot,NormalizeDouble(dnewopen,digit),m_islippage,0,0,_comment,(int)m_imagic,dt,cl);
}
}
//модифицируем ордер после открытия для точной постановки стопа и тейка от цены открытия
if(iticket!=-1 && cmd<2)
{
if(OrderSelect(iticket))
{
dnewopen=OrderOpenPrice();
dtake=0;dstop=0;
if(!tpslIsPoint)
{
if(cmd==OP_BUY)
{
if(dtp>0) dtake=dtp;
if(dsl>0) dstop=dsl;
}
if(cmd==OP_SELL)
{
if(dtp>0) dtake=dtp;
if(dsl>0) dstop=dsl;
}
}
else
{
if(cmd==OP_BUY)
{
if(dtp>0) dtake=dnewopen+dtp*p;
if(dsl>0) dstop=dnewopen-dsl*p;
}
if(cmd==OP_SELL)
{
if(dtp>0) dtake=dnewopen-dtp*p;
if(dsl>0) dstop=dnewopen+dsl*p;
}
}
if((dtake>0.0 || dstop>0.0) && dnewopen>0)
{
//поддержка возможности полностью отключить выставление стопа и тейка, они ставятся только виртуально
if(!options_bNoModify) bret=::OrderModify(iticket,NormalizeDouble(dnewopen,digit),NormalizeDouble(dstop,digit),NormalizeDouble(dtake,digit),dt); else bret=true;
if(options_bVirtual) virtual_set(iticket,NormalizeDouble(dstop,digit),NormalizeDouble(dtake,digit));
}
}
}
ierrors=GetLastError();
if(iticket==-1 || ierrors>0)
{
sinfo=StringConcatenate(__FUNCTION__,"(",m_name,",",string_type(cmd),",",ddlot,",",dnewopen,",",dstop,",",dtake,",",_comment,") Ask=",_Ask," Bid=",_Bid);
is_open=ErrorMessage(ierrors,j,sinfo,options_iTimeout);
}
else
{
is_open=false;
break;
}
if(iticket>0)
{
if(OrderSelect(iticket))
{
int i=ArraySize(m_orders);
ArrayResize(m_orders,i+1);
if(m_orders[i].getOrderByTicket(iticket))
{
//обновим счетчики, обновлять не будем , если мы хотим дооткрыть ордера к текущему и это не учитывать в расчетах
if(bAddCount)
{
m_all++;
orders[m_orders[i].OrderType]++;
}
}
}
if(iticket>0) m_total++;
return iticket;
}
}
if(iticket>0) m_total++;
return iticket;
}
Замер скорости открытия
С чем связаны скачки скорости открытия в тестере? Это так задумано или нет?