Правильный расчет цены безубытка, для серии ордеров. Алгоритм. - страница 3

 
Ihor Herasko:

В таком случае нужно постоянно быть онлайн, а это на 100% невозможно. Чтобы обезопасить позиции, приходится ставить стопы/профиты.

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

Но если взять кросскурс, то там цена пункта  меняется постоянно. Из-за это в примере выше 4 доллара не всегда равны 4 пунктам. Иногда это 3,5, а иногда 5.7

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

Задачу можно решить, только если знать будущий кросс-курс. Но если его знать, ни каких других задач уже решать не нужно )

Из идей — учитывайте среднюю волатильность кросса в том самом "запасе", который закладываете в стопы.

 
Maksym Mudrakov:

Написал класс для расчета безубытка для mql5

пальцы не болят? // тем более не верно

там делов на 5 строчек от силы.......

 
Ihor Herasko:

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

Здесь же обсуждается другой подход - заранее рассчитать нужную цену и по ней установить стоп/профит для всех ордеров. В итоге при достижении рассчитанной цены закрытие произойдет на стороне сервера, без участия советника. Кроме того, вероятность закрытия всех ордеров по заданной цене существенно повышается по сравнению со способом закрытия по рынку.

Игорь, а если сетка разнонаправленная и плавающий спред. В таком случае возможна ситуация, что один стоп ордер зацепит, а до другого не дойдёт и развернётся. И останутся не закрыты сделки какого то направления.

Есть конечно вариант, чтобы проверять закрытие одного направления по стоп ордеру, и принудительно закрывать другое по рынку. Но опять же требуется он-лайн работа советника. Без него никак не обойтись.

У меня в сетке динамически (на каждом тике) рассчитывается требуемый уровень (по Bid или Ask, в зависимости от общего направления), и закрывает всё советник по рынку. На погрешность закрытия делаю допуск 2-3 пипса.
 
Andrey Kaunov:

Игорь, а если сетка разнонаправленная и плавающий спред. В таком случае возможна ситуация, что один стоп ордер зацепит, а до другого не дойдёт и развернётся. И останутся не закрыты сделки какого то направления.

Есть конечно вариант, чтобы проверять закрытие одного направления по стоп ордеру, и принудительно закрывать другое по рынку. Но опять же требуется он-лайн работа советника. Без него никак не обойтись.

У меня в сетке динамически (на каждом тике) рассчитывается требуемый уровень (по Bid или Ask, в зависимости от общего направления), и закрывает всё советник по рынку. На погрешность закрытия делаю допуск 2-3 пипса.

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

Если же ДЦ поддерживает встречное закрытие, то возможен вариант работы без стопов и профитов при условии постоянной работы советника. В момент закрытия открывается локирующий рыночный ордер, противоположный по направлению доминирующему объему, с объемом, равным разности объемов Buy и Sell ордеров. Как только он открыт, цена закрытия становится фиксированной. Далее в спокойном режиме производится встречное закрытие ордеров. Да, времени на закрытие уйдет немало, но в данном случае важно то, что изменения цены уже никак не влияют на результат.

 
Когда сделок открыто много (да и когда не очень), это отличный способ. Согласен полностью.
 
Maksym Mudrakov #:

Написал класс для расчета безубытка для mql5

Спасибо, взял из вашего класса расчеты, засунул в свои функции, ваш вариант работает лучше, чем у меня было.
 
pribludilsa #:
Неужели все единодушны во мнении, что нужно такое лютое усложнение на ровном месте?
Ничего лютого нет, единственное, что работу с позициями и ордерами лучше выполнять в отдельной функции, единой для всего советника, чтобы не перегружать советник лишними циклами перебора.
 
Ihor Herasko #:

Цена БУ для серии ордеров одного направления:

Здесь:

  • fTotalVolume - совокупный объем рыночных ордеров (позиций, если в терминологии МТ5) одной серии
  • fTotalOPVolume - сумма произведений цен открытия ордеров на их объем
  • fTotalSwapComm - сумма свопов и комиссий всех ордеров серии
  • fTickSize - размер тика для символа, нужно проверить на корректность до вызова функции
  • fTickValue - стоимость тика для символа , нужно проверить на корректность до вызова функции

Подскажите пожалуйста подробней, что значит: 

  • fTotalOPVolume - сумма произведений цен открытия ордеров на их объем

Можно пример? пожалуйста

 

Я для МТ4 сделал такую штуку. Работает, правда, только интрадей, то есть, тогда, когда нет переноса через сутки. Словом, тогда, когда нет комиссии и свопов.

Берём группу однонаправленных ордеров. Например Бай-группу. Если провести математические вычисления, то можно ввести понятие виртуального серединного ордера. Ну тоесть как будто у нас открыта не группа бай-ордеров, а один ордер по некой цене и с неким лотом. Вот на уровень этой цены я ставил зелёную горизонтальную линию. Если всю бай-группу закрыть по достижении ценой  Bid этой линии, то она закроется в ноль. То же и с Селл-группой (для неё я ставил красную горизонталь). В коде это реализовывал так:

// =================================================================================================
// ************************* MiddleOrder ***********************************************************
// =================================================================================================

//=========== PriceMiddleOrder()  ==========================
/* функция возвращает цену плавающего среднего ордера (условного) сразу для всей
        группы однонаправленных ордеров. Параметр Type может принимать значение "BUY" или "SELL"
        
        Как это работает?
        
        Если имеется позиция на покупку объемом 1 лот и совершается торговая операция на покупку еще 1 лота, в итоге будет получена одна позиция объемом 2 лота. При этом цена открытия пересчитывается — рассчитывается средневзвешенная цена открытия: (Цена первой сделки*Объем первой сделки + Цена второй сделки*Объем второй сделки)/(Объем первой сделки + Объем второй сделки).
        Аналогично происходит при совершении сделки в обратном направлении. Если имеется позиция на покупку объемом 1 лот и совершается торговая операция на продажу 0.5 лота, в итоге будет получена одна позиция на покупку объемом 0.5 лота.
*/
// -------------------------------------------
double PriceMiddleOrder(string Type,int Magic){
        bool            LNG=TerminalInfoString(TERMINAL_LANGUAGE)=="Russian";
        string SMB=Symbol();
        int DGS=(int)MarketInfo(SMB,MODE_DIGITS);
        if(Type!="BUY" && Type!="SELL"){
                Print(__FUNCTION__,Text(LNG,"(): Некорректное значение переменной Type = ","(): Incorrect variable value Type = "),Type);
                return(-1);
        }
        double OpenPrice1=0,OpenPrice2=0,MiddlePrice=0;
  double Lot1=0,Lot2=0,MiddleLot=0;
        int                     ChtchBuy=0,ChtchSell=0,SchBuy=SchBuy(Magic),SchSell=SchSell(Magic);
        for(int i=OrdersTotal()-1;i>=0;i--){
    if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)){
                        Print(__FUNCTION__,"(): ",Text(LNG,"Ошибка выбора ордера № ","Failed to select order No. "),i,Text(LNG," Код ошибки = "," Error code = "),GetLastError());
                        return(-1);
                }
    else{
      if(OrderSymbol()!=SMB || OrderMagicNumber()!=Magic){continue;} 
      if(Type=="BUY"){
                                if(OrderType()==OP_BUY){        
                                        if(SchBuy==1){
                                                return(NormalizeDouble(OrderOpenPrice(),DGS));
                                        }
                                        if(SchBuy>1){
                                                if(ChtchBuy==0){
                                                        OpenPrice1=OrderOpenPrice();
                                                        Lot1=OrderLots();
                                                        ChtchBuy++;
                                                        continue;
                                                }
                                                if(ChtchBuy==1){
                                                        OpenPrice2=OrderOpenPrice();
                                                        Lot2=OrderLots();
                                                        MiddleLot=Lot1+Lot2;
                                                        MiddlePrice=(OpenPrice1*Lot1+OpenPrice2*Lot2)/(Lot1+Lot2);
                                                        ChtchBuy++;
                                                        continue;
                                                }
                                                if(ChtchBuy>1){
                                                        OpenPrice1=MiddlePrice;
                                                        Lot1=MiddleLot;
                                                        OpenPrice2=OrderOpenPrice();
                                                        Lot2=OrderLots();
                                                        MiddleLot=Lot1+Lot2;
                                                        MiddlePrice=(OpenPrice1*Lot1+OpenPrice2*Lot2)/(Lot1+Lot2);
                                                        ChtchBuy++;
                                                        continue;
                                                }
                                        }
                                }
                        }
                        if(Type=="SELL"){
                                if(OrderType()==OP_SELL){       
                                        if(SchSell==1){
                                                return(NormalizeDouble(OrderOpenPrice(),DGS));
                                        }
                                        if(SchSell>1){
                                                if(ChtchSell==0){
                                                        OpenPrice1=OrderOpenPrice();
                                                        Lot1=OrderLots();
                                                        ChtchSell++;
                                                        continue;
                                                }
                                                if(ChtchSell==1){
                                                        OpenPrice2=OrderOpenPrice();
                                                        Lot2=OrderLots();
                                                        MiddleLot=Lot1+Lot2;
                                                        MiddlePrice=(OpenPrice1*Lot1+OpenPrice2*Lot2)/(Lot1+Lot2);
                                                        ChtchSell++;
                                                        continue;
                                                }
                                                if(ChtchSell>1){
                                                        OpenPrice1=MiddlePrice;
                                                        Lot1=MiddleLot;
                                                        OpenPrice2=OrderOpenPrice();
                                                        Lot2=OrderLots();
                                                        MiddleLot=Lot1+Lot2;
                                                        MiddlePrice=(OpenPrice1*Lot1+OpenPrice2*Lot2)/(Lot1+Lot2);
                                                        ChtchSell++;
                                                        continue;
                                                }
                                        }
                                }
                        }
    }
  }
  return(NormalizeDouble(MiddlePrice,DGS));
}

//=========== LotMiddleOrder()  ==========================
// функция возвращает лот Миддл-ордера. Описание смотри в функции PriceMiddleOrder()
// ---------------------------------------------------------------------------------
double LotMiddleOrder(string Type,int Magic){
        bool            LNG=TerminalInfoString(TERMINAL_LANGUAGE)=="Russian";
        if(Type!="BUY" && Type!="SELL"){
                Print(__FUNCTION__,Text(LNG,"(): Некорректное значение переменной Type = ","(): Incorrect variable value Type = "),Type);
                return(-1);
        }
        double Lot1=0,Lot2=0,MiddleLot=0;
        string SMB=Symbol(); 
  int                   chtch=0,SchBuy=SchBuy(Magic),SchSell=SchSell(Magic);
        for(int i=OrdersTotal()-1;i>=0;i--){
    if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)){
                        Print(__FUNCTION__,"(): ",Text(LNG,"Ошибка выбора ордера № ","Failed to select order No. "),i,Text(LNG," Код ошибки = "," Error code = "),GetLastError());
                        return(-1);
                }
    else{
      if(OrderSymbol()!=SMB || OrderMagicNumber()!=Magic){continue;} 
      if(Type=="BUY"){
                                if(OrderType()==OP_BUY){        
                                        if(SchBuy==1){
                                                return(ProverkaLota(OrderLots()));
                                        }
                                        if(SchBuy>1){
                                                if(chtch==0){
                                                        Lot1=OrderLots();
                                                        chtch++;
                                                        continue;
                                                }
                                                if(chtch==1){
                                                        Lot2=OrderLots();
                                                        MiddleLot=Lot1+Lot2;
                                                        chtch++;
                                                        continue;
                                                }
                                                if(chtch>1){
                                                        Lot1=MiddleLot;
                                                        Lot2=OrderLots();
                                                        MiddleLot=Lot1+Lot2;
                                                        chtch++;
                                                        continue;
                                                }
                                        }
                                }
                        }
                        if(Type=="SELL"){
                                if(OrderType()==OP_SELL){       
                                        if(SchSell==1){
                                                return(ProverkaLota(OrderLots()));
                                        }
                                        if(SchSell>1){
                                                if(chtch==0){
                                                        Lot1=OrderLots();
                                                        chtch++;
                                                        continue;
                                                }
                                                if(chtch==1){
                                                        Lot2=OrderLots();
                                                        MiddleLot=Lot1+Lot2;
                                                        chtch++;
                                                        continue;
                                                }
                                                if(chtch>1){
                                                        Lot1=MiddleLot;
                                                        Lot2=OrderLots();
                                                        MiddleLot=Lot1+Lot2;
                                                        chtch++;
                                                        continue;
                                                }
                                        }
                                }
                        }
    }
  }
  return(ProverkaLota(MiddleLot));
}
// ============ ProverkaLota() =====================================================================
// функция принимает нормализует лот ордера
//-----------------------------------------------------
double ProverkaLota(double LotOrdera){
  string SMB=Symbol(),Temp;
  double MinLots=(MarketInfo(SMB,MODE_MINLOT));
  double MaxLots=MarketInfo(SMB,MODE_MAXLOT);
  double CorrectLot=0;
  int    diglots=0;
  if(MinLots==1 || MinLots==0.1 || MinLots==0.01){
                if(MinLots==1){
                        diglots=0;
                }
                if(MinLots==0.1){
                        diglots=1;
                }
                if(MinLots==0.01){
                        diglots=2;
                }
        }
        Temp=DoubleToStr(LotOrdera,diglots);// отбрасываем лишние числа
        CorrectLot=StrToDouble(Temp);
  if(CorrectLot<MinLots){
    CorrectLot=MinLots;
  }
  if(CorrectLot>MaxLots){
    CorrectLot=MaxLots;
  }
  return(CorrectLot);
}
// =================================================================================================