//+------------------------------------------------------------------+ //| ProjectName | //| Copyright 2020, CompanyName | //| http://www.companyname.net | //+------------------------------------------------------------------+ #include #include #include #include #include #include #include CTrade a_trade; CPositionInfo a_position; CSymbolInfo a_symbol; CAccountInfo a_accauntInfo; CDealInfo a_deal; COrderInfo a_order; //--- Объявляем структуры, которые будут использоваться для торговли MqlTick latest_price; // Будет использоваться для текущих котировок MqlTradeRequest mrequest; // Будет использоваться для отсылки торговых запросов MqlTradeResult mresult; // Будет использоваться для получения результатов выполнения торговых запросов MqlRates mrate[]; // Будет содержать цены, объемы и спред для каждого бара sinput int Magic = 0; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CGeneral_class { private: public: CGeneral_class(); ~CGeneral_class(); double LotSize(int _SL, double MR); bool OpenBuyPosition(double volume,double price, double StopLoss_, double TP_); bool OpenSellPosition(double volume,double price, double StopLoss_, double TP_); int CheckOpen(int &ob, int &os, datetime & t_ob, datetime & t_os); void CheckVolume(double &Volume_current_sell,double &Volume_current_buy); void CloseBuy(); void CloseSell(); void CloseBuyPartial(string Symbol_, int x); void CloseSellPartial(string Symbol_, int x); bool isNewBar(ENUM_TIMEFRAMES per); string st5(double x); bool CheckTimePeriod(string Start_Time, string End_Time); bool SpreadFilter(int bars, int pauseTime, int x); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CGeneral_class::CGeneral_class() { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CGeneral_class::~CGeneral_class() { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ //+------------------------------------------------------------------------+ //+- Расчет лота в зависимости от расстояния до StopLoss +// //+------------------------------------------------------------------------+ double CGeneral_class::LotSize(int _SL, double MR)// SL-StopLoss MR-MaxRisk в % от баланса { if(_SL == 0) return(0); // исключение деления на ноль double Free = AccountInfoDouble(ACCOUNT_BALANCE); double LotVal = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); //стоимость 1 пункта 1 лота double Min_Lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double Max_Lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double Step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); double Lot = MathFloor((Free * MR / 100) / (_SL * LotVal) / Step) * Step; if(Lot < Min_Lot) Lot = Min_Lot; if(Lot > Max_Lot) Lot = Max_Lot; return(Lot); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CGeneral_class :: OpenSellPosition(double volume,double price, double StopLoss_, double TP_) { ZeroMemory(mrequest); ZeroMemory(mresult); mrequest.action = TRADE_ACTION_DEAL; // немедленное исполнение mrequest.price = NormalizeDouble(price, _Digits); // последняя цена Bid mrequest.sl = StopLoss_; // Stop Loss mrequest.tp = TP_ ;// NormalizeDouble(latest_price.bid - TP * _Point, _Digits); // Take Profit mrequest.symbol = _Symbol; // символ mrequest.volume = volume; // количество лотов для торговли mrequest.magic = Magic; // Magic Number mrequest.type = ORDER_TYPE_SELL; // ордер на продажу mrequest.type_filling = ORDER_FILLING_FOK; // тип исполнения ордера - все или ничего mrequest.deviation = 100; // проскальзывание от текущей цены //--- отсылаем ордер OrderSend(mrequest, mresult); // анализируем код возврата торгового сервера if(mresult.retcode == 10009 || mresult.retcode == 10008) //Request is completed or order placed { Print("Ордер Sell успешно помещен, тикет ордера #:", mresult.order, "!!"); return true; } else { SendMail(_Symbol + " Запрос на установку ордера Buy не выполнен. код ошибки: " + GetLastError(), _Symbol + " result retcode: " + mresult.retcode + "result.deal: " + mresult.deal + "result.order " + mresult.order + "Период: " + GetNameTF()); Print("Sell -> false. Result Retcode: ",+ mresult.retcode + " result.deal: " + mresult.deal + "result.order " + mresult.order + "Период: " + GetNameTF()); return false; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CGeneral_class :: OpenBuyPosition(double volume, double price, double StopLoss_, double TP_) { ZeroMemory(mrequest); ZeroMemory(mresult); mrequest.action = TRADE_ACTION_DEAL; // немедленное исполнение mrequest.price = NormalizeDouble(price, _Digits); // последняя цена Bid mrequest.sl = StopLoss_; // Stop Loss mrequest.tp = TP_;//NormalizeDouble(latest_price.ask + TP * _Point, _Digits); // Take Profit mrequest.symbol = _Symbol; // символ mrequest.volume = volume; // количество лотов для торговли mrequest.magic = Magic; // Magic Number mrequest.type = ORDER_TYPE_BUY; // ордер на продажу mrequest.type_filling = ORDER_FILLING_FOK; // тип исполнения ордера - все или ничего mrequest.deviation = 100; // проскальзывание от текущей цены //--- отсылаем ордер OrderSend(mrequest, mresult); // анализируем код возврата торгового сервера if(mresult.retcode == 10009 || mresult.retcode == 10008) //Request is completed or order placed { return true; Print("Ордер Sell успешно помещен, тикет ордера #:", mresult.order, "!!"); } else { SendMail(_Symbol + " Запрос на установку ордера Buy не выполнен. код ошибки: " + GetLastError(), _Symbol + " result retcode: " + mresult.retcode + "result.deal: " + mresult.deal + "result.order " + mresult.order + "Период: " + GetNameTF()); Print("Buy -> false. Result Retcode: ",+ mresult.retcode + " result.deal: " + mresult.deal + "result.order " + mresult.order + "Период: " + GetNameTF()); return false; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CGeneral_class :: CheckOpen(int &ob, int &os, datetime & t_ob, datetime & t_os) { ob = 0; os = 0; int res = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { a_position.SelectByIndex(i); if(a_position.Magic() == Magic && a_position.Symbol() == _Symbol) { if(a_position.PositionType() == POSITION_TYPE_BUY) { t_ob = a_position.Time(); ob++; } if(a_position.PositionType() == POSITION_TYPE_SELL) { t_os = a_position.Time(); os++; } } } res = ob + os; return res; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ //double Volume_current_sell; //double Volume_current_buy; void CGeneral_class :: CheckVolume(double &Volume_current_sell,double &Volume_current_buy) { Volume_current_sell = 0; Volume_current_buy = 0; for(int i = PositionsTotal()-1; i >= 0; i--) { a_position.SelectByIndex(i); if(a_position.Magic() == Magic && a_position.Symbol() == _Symbol) { if(a_position.PositionType() == POSITION_TYPE_BUY) { Volume_current_buy += PositionGetDouble(POSITION_VOLUME); } if(a_position.PositionType() == POSITION_TYPE_SELL) { Volume_current_sell += PositionGetDouble(POSITION_VOLUME); } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CGeneral_class :: CloseSell() { for(int i = PositionsTotal() - 1; i >= 0; i--) { a_position.SelectByIndex(i); if(a_position.Magic() == Magic && a_position.Symbol() == _Symbol) { if(a_position.PositionType() == POSITION_TYPE_SELL) a_trade.PositionClose(a_position.Ticket()); } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CGeneral_class :: CloseBuy() { for(int i = PositionsTotal() - 1; i >= 0; i--) { a_position.SelectByIndex(i); if(a_position.Magic() == Magic && a_position.Symbol() == _Symbol) { if(a_position.PositionType() == POSITION_TYPE_BUY) { int o = a_trade.PositionClose(a_position.Ticket()); if(o <= 0) Print("<--- CloseBuy ", _LastError, " ", __LINE__); } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CGeneral_class :: CloseBuyPartial(string Symbol_, int x) { double Volume_current_buy = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { a_position.SelectByIndex(i); if(a_position.Magic() == Magic && a_position.Symbol() == Symbol_) { if(a_position.PositionType() == POSITION_TYPE_BUY) { Volume_current_buy = PositionGetDouble(POSITION_VOLUME); double Lot = NormalizeDouble(Volume_current_buy/x, 2); bool o = a_trade.PositionClosePartial(a_position.Ticket(),Lot,-1); if(o == false) Print("<--- CloseBuy ", _LastError, " ", __LINE__); } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CGeneral_class ::CloseSellPartial(string Symbol_, int x) { double Volume_current_sell = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { a_position.SelectByIndex(i); if(a_position.Magic() == Magic && a_position.Symbol() == Symbol_) { if(a_position.PositionType() == POSITION_TYPE_SELL) { Volume_current_sell = PositionGetDouble(POSITION_VOLUME); double Lot = NormalizeDouble(Volume_current_sell/x, 2); bool o = a_trade.PositionClosePartial(a_position.Ticket(),Lot,-1); if(o == false) Print("<--- CloseBuy ", _LastError, " ", __LINE__); } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CGeneral_class :: isNewBar(ENUM_TIMEFRAMES per) { //--- в статической переменной будем помнить время открытия последнего бара static datetime last_time = 0; //--- текущее время datetime lastbar_time = SeriesInfoInteger(Symbol(), per, SERIES_LASTBAR_DATE); //--- если это первый вызов функции if(last_time == 0) { //--- установим время и выйдем last_time = lastbar_time; return(false); } //--- если время отличается if(last_time != lastbar_time) { //--- запомним время и вернем true last_time = lastbar_time; return(true); } //--- дошли до этого места - значит бар не новый, вернем false return(false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ string CGeneral_class :: st5(double x) { return DoubleToString(x, _Digits); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CGeneral_class :: CheckTimePeriod(string Start_Time, string End_Time) { datetime currentTime = TimeCurrent(); datetime start_time = StringToTime(Start_Time); datetime end_time = End_Time > Start_Time ? StringToTime(End_Time) : StringToTime(StringToTime(End_Time) + 3600 * 24); //+------------------------------------------------------------------+ // Такой вид конструкции называется тернарной операцией. Это более красивая форма записи кода ниже: // //if (End_Time > Start_Time) //{ // end_time = StringToTime(End_Time) //} //else //{ // end_time = StringToTime(StringToTime(End_Time) + 3600 * 24) //} // Структура тернарной операции такая: if() ? valueIfTrue : valueIfFalse; // // Если требуется выполнить несколько операций в результате исхода, то используется if. // Т.е так не получится // end_time = End_Time > Start_Time ? StringToTime(End_Time) *тут еще какая то операция* : StringToTime(StringToTime(End_Time) + 3600 * 24) *или тут*; //+------------------------------------------------------------------+ //Print("Current time: ", currentTime); //Print("Start time: ", start_time); //Print("End time: ", end_time); return currentTime > start_time && currentTime < end_time; // Аналогично этому: // if(currentTime > start_time && currentTime < end_time) // { // return true; // } // return false; } //+------------------------------------------------------------------+ //|Проверяем размах спреда | //+------------------------------------------------------------------+ bool CGeneral_class :: SpreadFilter(int bars, int pauseTime, int x) { double spread = 0; int values[]; int count = CopySpread(_Symbol, PERIOD_H1, 0, bars, values); for(int i = 0; i < count; i++) { spread += values[i]; } double middleSpread = spread / bars * SymbolInfoDouble(_Symbol, SYMBOL_POINT); double currentSpread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); datetime lastAllowedSpreadTime = 0; // Если спред большой, то запоминаем текущее время и выжидаем некоторую паузу, после которой он может уменьшится. if(currentSpread > (middleSpread+x))// x - это погрешность, которую мы учитываем { lastAllowedSpreadTime = TimeCurrent(); return false; } else { if(TimeCurrent() < lastAllowedSpreadTime + pauseTime) return false; lastAllowedSpreadTime = 0; return true; } } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ string GetNameTF(int TimeFrame = 0) { if(TimeFrame == 0) TimeFrame = Period(); switch(TimeFrame) { case PERIOD_M1: return("M1"); case PERIOD_M5: return("M5"); case PERIOD_M10: return("M10"); case PERIOD_M15: return("M15"); case PERIOD_M20: return("M20"); case PERIOD_M30: return("M30"); case PERIOD_H1: return("H1"); case PERIOD_H2: return("H2"); case PERIOD_H4: return("H4"); case PERIOD_D1: return("Daily"); case PERIOD_W1: return("Weekly"); case PERIOD_MN1: return("Monthly"); default: return("Unknown Period"); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+