Скрипт для расчета лотов треугольника

Программирование прибыли: от азов к секретам мастерства. Читайте, спрашивайте, делитесь опытом.
Бонус за сообщение 0.5$
Ответственный Модератор - Haos

Скрипт для расчета лотов треугольника

Сообщение Haos » 11 окт 2021, 09:52

Создадим скрипт для расчета лотов трех валютных пар, входящих в треугольник (прототипом тема является о треугольном арбитраже - Треугольный арбитраж). Почему, собственно, скрипт? Потому, что размер лотов вычисляется один раз перед входом в сделки и потому, что расчетные значения меняются незначительно со временем. Т.е. советник не подходит здесь, также как и индикатор, значит скрипт.

После создания заготовки скрипта нужно прописать свойство:
Код: выделить все
#property script_show_inputs

Чтобы скрипт мог при запуске получать от пользователя вводимые значения переменных (настройки).

01-Расчет лотов трина.png

Теперь нам надо организовать выбор пользователем тринов, т.е. заранее организовать список треугольников. Это необходимо потому, что, во-первых, пользователь может не помнить какие именно валютные пары входят в тот или иной трин, во-вторых, последовательность валютных пар в треугольнике жестко фиксирована, т.е. пары менять местами нельзя.

Нам не нужно вводить сразу же все возможные трины. Обычно торгуется один-два трина из мажорных пар. Но со временем, можно пополнять будет список тринов, скажем, в новых версиях. Сейчас главное создать прототип, вообще, для помощи рассчета лотов трейдерам, которые ни бум-бум в этой теме.

Организуем трины через перечисления:
Код: выделить все
enum enuTri
{
   EURUSD_GBPUSD_EURGBP = 1,     
   EURCHF_USDCHF_EURUSD = 2,
};

Заодно сразу же организуем выбор направления торговли для тринов и также через перечисления.
Направления торговли трина могут быть двух типов:
BUY-SELL-SELL и
SELL-BUY-BUY.
Это и организуем:

Код: выделить все
enum enuDir
{
   BUY_SELL_SELL = 1,     
   SELL_BUY_BUY  = 2,
};

Введем две перепенные для работы с упомянутыми перечислениями. На данном этапе код наш будет выглядеть так:
Код: выделить все
//+------------------------------------------------------------------+
//|                                                SC-TrinLot-v1.mq4 |
//|                                                             Haos |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Haos"
#property link      "https://investforum.ru/forum"
#property version   "1.00"
#property strict
#property script_show_inputs

//***********************************************************************************************

enum enuTri
{
   EURUSD_GBPUSD_EURGBP = 1,     
   EURCHF_USDCHF_EURUSD = 2,
};

enum enuDir
{
   BUY_SELL_SELL = 1,     
   SELL_BUY_BUY  = 2,
};

//***********************************************************************************************

input enuTri intTri  = 1;     // Трин для торговли
input enuDir intDir  = 1;     // Направление торговли трина

//***********************************************************************************************

void OnStart()
{

   
}


Из переменных, вводимых пользователем, надо предусмотреть размер лота первой пары трина. Для второй и третьей пары расчет будет производить скрипт:
Код: выделить все
input double dblQ1   = 0.1;   // Размер торгового лота 1-ой пары трина

Теперь к самой процедуре OnStart() перейдем.
Прежде всего сформируем переменные:
Код: выделить все
double dblQ2, dblQ3;
string strP1, strP2, strP3;
int    intLoDi = 2;

dblQ2, dblQ3 - размер лотов для 2-ой и 3-ей пары;
strP1, strP2, strP3 - имена торговых пар трина;
intLoDi - кол-во знаков после точки в размере лота. По умолчанию инициализируем значением 2, но в процедуре произведем перерасчет (хотя, еще не встретились нам ни разу, чтобы размер лота после точки не был равен двум).

Организуем перебор тринов, в переключателе switch. Будем в нем инициализировать нужные переменные, а также производить вычисление размеров лота трина по правилам, указанным в статье Справочник по треугольному арбитражу.

В результате код для OnStart() будет иметь вид:
Код: выделить все
void OnStart()
{
   double dblQ2, dblQ3;
   string strP1, strP2, strP3;
   int    intLoDi = 2;
     
   switch(intTri)
   {
      case 1:
         // EURUSD_GBPUSD_EURGBP
         strP1 = "EURUSD"; strP2 = "GBPUSD"; strP3 = "EURGBP";
         intLoDi = f_GetLotDigits(strP1);
         dblQ3 = dblQ1;
         if(intDir == 1) // BUY_SELL_SELL
         {
            dblQ2 = NormalizeDouble(dblQ1 * MarketInfo(strP3, MODE_BID), intLoDi);
         }
         else if(intDir == 2) // SELL_BUY_BUY
         {
            dblQ2 = NormalizeDouble(dblQ1 * MarketInfo(strP3, MODE_ASK), intLoDi);
         }
         break;       
      case 2:
         //EURCHF_USDCHF_EURUSD
         strP1 = "EURCHF"; strP2 = "USDCHF"; strP3 = "EURUSD";
         intLoDi = f_GetLotDigits(strP1);
         dblQ3 = dblQ1;
         if(intDir == 1) // BUY_SELL_SELL
         {
            dblQ2 = NormalizeDouble(dblQ1 * MarketInfo(strP3, MODE_BID), intLoDi);
         }
         else if(intDir == 2) // SELL_BUY_BUY
         {
            dblQ2 = NormalizeDouble(dblQ1 * MarketInfo(strP3, MODE_ASK), intLoDi);
         }
         break;         
      break;
   }
   
   Comment( "\n", "Размер лотов треугольника: ",
            "\n", strP1," = ", DoubleToStr(dblQ1, intLoDi),
            "\n", strP2," = ", DoubleToStr(dblQ2, intLoDi),
            "\n", strP3," = ", DoubleToStr(dblQ3, intLoDi));
}

При этом, вывод полученных значений организуем в виде комментария, выводимого в левый верхний угол окна с графиком валютной пары. Запускать скрипт можно в любом окне, от окна расчет не зависит.

Итак, окончательно код скрипта будет выглядеть так:
Код: выделить все
//+------------------------------------------------------------------+
//|                                                SC-TrinLot-v1.mq4 |
//|                                                             Haos |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Haos"
#property link      "https://investforum.ru/forum"
#property version   "1.00"
#property strict
#property script_show_inputs

//***********************************************************************************************

enum enuTri
{
   EURUSD_GBPUSD_EURGBP = 1,     
   EURCHF_USDCHF_EURUSD = 2,
};

enum enuDir
{
   BUY_SELL_SELL = 1,     
   SELL_BUY_BUY  = 2,
};

//***********************************************************************************************

input enuTri intTri  = 1;     // Трин для торговли
input enuDir intDir  = 1;     // Направление торговли трина
input double dblQ1   = 0.1;   // Размер торгового лота 1-ой пары трина

//***********************************************************************************************

void OnStart()
{
   double dblQ2, dblQ3;
   string strP1, strP2, strP3;
   int    intLoDi = 2;
     
   switch(intTri)
   {
      case 1:
         // EURUSD_GBPUSD_EURGBP
         strP1 = "EURUSD"; strP2 = "GBPUSD"; strP3 = "EURGBP";
         intLoDi = f_GetLotDigits(strP1);
         dblQ3 = dblQ1;
         if(intDir == 1) // BUY_SELL_SELL
         {
            dblQ2 = NormalizeDouble(dblQ1 * MarketInfo(strP3, MODE_BID), intLoDi);
         }
         else if(intDir == 2) // SELL_BUY_BUY
         {
            dblQ2 = NormalizeDouble(dblQ1 * MarketInfo(strP3, MODE_ASK), intLoDi);
         }
         break;       
      case 2:
         //EURCHF_USDCHF_EURUSD
         strP1 = "EURCHF"; strP2 = "USDCHF"; strP3 = "EURUSD";
         intLoDi = f_GetLotDigits(strP1);
         dblQ3 = dblQ1;
         if(intDir == 1) // BUY_SELL_SELL
         {
            dblQ2 = NormalizeDouble(dblQ1 * MarketInfo(strP3, MODE_BID), intLoDi);
         }
         else if(intDir == 2) // SELL_BUY_BUY
         {
            dblQ2 = NormalizeDouble(dblQ1 * MarketInfo(strP3, MODE_ASK), intLoDi);
         }
         break;         
      break;
   }
   
   Comment( "\n", "Размер лотов треугольника: ",
            "\n", strP1," = ", DoubleToStr(dblQ1, intLoDi),
            "\n", strP2," = ", DoubleToStr(dblQ2, intLoDi),
            "\n", strP3," = ", DoubleToStr(dblQ3, intLoDi));
}

//***********************************************************************************************

int f_GetLotDigits(string sy)
{
/*
   Описание : Количество цифр после запятой в величине лота
   Параметры:                                                               
   sy - наименование инструмента   ("0" - текущий символ) 
*/
   if(sy == "0") sy = _Symbol;

return((int)MathLog10(1 / MarketInfo(sy, MODE_LOTSTEP)));
}


Очевидно, что скрипт есть куда улучшать. Написан он, прямо скажем, "в лоб". Можно ввести массив для наименований пар, нужно учесть ситуацию, когда наименования отличаются от классических и имеют постфиксы в виде буквы. Можно сократить код, введением функций в которых однотипные операции вынести и т.д.
Предлагаю это всё осуществить заинтересованному читателю. Заготовочка имеется, задача решена по расчету размеров лота для треугольника. Дальше, как говорится "доработать напильником" остается. :-):

Скрин запуска скрипта по параметрам, установленным по умолчанию:
Вложения
02-Расчет лотов трина.png
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.

Скрипт для расчета лотов треугольника

Сообщение Haos » 14 окт 2021, 20:28

Произведем некоторые улучшения кода данного скрипта.

1. Прежде всего организуем трехмерный массив для внесения в него символов треугольников.
Дадим массиму имя:

Код: выделить все
string strMSym[N][3]; // массив символов в треугольнике (N строк, 3 столбца)

Это будет строковый массив.
Количество треугольников в нем будем задавать директивой #define:

Код: выделить все
#define N 19  // Количество треугольников для анализа (всего)

По мере заполнения тринами массива, можно будет корректировать это значение. Мудрёж в виде заполнения тринов из текстового файла или путем составления перебором всех комбинаций символов, которое так любят показывать некоторые программеры на мкуэль5 сайтах и т.п., делать не будем.
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.

Скрипт для расчета лотов треугольника

Сообщение Haos » 14 окт 2021, 20:31

Процедуру инициализации массива strMSym лучше делать в отдельной функции - процедуре, под названием, к примеру, f_SetTriData():

Код: выделить все
void f_SetTriData()
{
   // Заполнение массива символов треугольников
   
   strMSym[0][0]  = "AUDUSD"; strMSym[0][1]  = "NZDUSD"; strMSym[0][2]  = "AUDNZD";
   strMSym[1][0]  = "EURUSD"; strMSym[1][1]  = "AUDUSD"; strMSym[1][2]  = "EURAUD";
   strMSym[2][0]  = "GBPUSD"; strMSym[2][1]  = "AUDUSD"; strMSym[2][2]  = "GBPAUD";
   strMSym[3][0]  = "AUDCAD"; strMSym[3][1]  = "USDCAD"; strMSym[3][2]  = "AUDUSD";
   strMSym[4][0]  = "AUDCHF"; strMSym[4][1]  = "USDCHF"; strMSym[4][2]  = "AUDUSD";
   strMSym[5][0]  = "AUDJPY"; strMSym[5][1]  = "USDJPY"; strMSym[5][2]  = "AUDUSD";
   strMSym[6][0]  = "EURUSD"; strMSym[6][1]  = "GBPUSD"; strMSym[6][2]  = "EURGBP";
   strMSym[7][0]  = "EURUSD"; strMSym[7][1]  = "NZDUSD"; strMSym[7][2]  = "EURNZD";
   strMSym[8][0]  = "EURCAD"; strMSym[8][1]  = "USDCAD"; strMSym[8][2]  = "EURUSD";
   strMSym[9][0]  = "EURCHF"; strMSym[9][1]  = "USDCHF"; strMSym[9][2]  = "EURUSD";
   strMSym[10][0] = "EURDKK"; strMSym[10][1] = "USDDKK"; strMSym[10][2] = "EURUSD";
   strMSym[11][0] = "EURJPY"; strMSym[11][1] = "USDJPY"; strMSym[11][2] = "EURUSD";
   strMSym[12][0] = "EURNOK"; strMSym[11][1] = "USDNOK"; strMSym[12][2] = "EURUSD";
   strMSym[13][0] = "EURSEK"; strMSym[13][1] = "USDSEK"; strMSym[13][2] = "EURUSD";
   strMSym[14][0] = "GBPUSD"; strMSym[14][1] = "NZDUSD"; strMSym[14][2] = "GBPNZD";
   strMSym[15][0] = "GBPCAD"; strMSym[15][1] = "USDCAD"; strMSym[15][2] = "GBPUSD";
   strMSym[16][0] = "GBPCHF"; strMSym[16][1] = "USDCHF"; strMSym[16][2] = "GBPUSD";
   strMSym[17][0] = "GBPJPY"; strMSym[17][1] = "USDJPY"; strMSym[17][2] = "GBPUSD";
   strMSym[18][0] = "NZDJPY"; strMSym[18][1] = "USDJPY"; strMSym[18][2] = "NZDUSD";

}

Эту процедуру надо будет вызвать в самом начале при запуске пользователем скрипта в блоке void OnStart().
На данный момент я заполнил 19 треугольников. Это достаточно для большинства целей, но это еще не все треугольники согласно таблице. Постепенно можно будет все добрать.
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.

Скрипт для расчета лотов треугольника

Сообщение Haos » 14 окт 2021, 20:35

Поскольку расчетным является всегда только лот второй пары в трине (так составлена таблица), то для его расчета можно сделать специальную функцию, вынеся в неё все необходимые процедуры расчета теперь уже для любого трина из массива:

Код: выделить все
double f_GetQ2(int tri, int dir, double q1)
{
   int int_LoDi = f_GetLotDigits(strMSym[tri][0]);
   double dbl_Q2 = 0;
   
   if(dir == 0) // BUY_SELL_SELL
   {
      dbl_Q2 = NormalizeDouble(q1 * MarketInfo(strMSym[tri][2], MODE_BID), int_LoDi);
   }
   else if(dir == 1) // SELL_BUY_BUY
   {
      dbl_Q2 = NormalizeDouble(q1 * MarketInfo(strMSym[tri][2], MODE_ASK), int_LoDi);
   }

return(dbl_Q2);   
}


Детально эту функцию можно будет рассмотреть отдельно.
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.

Скрипт для расчета лотов треугольника

Сообщение Haos » 14 окт 2021, 20:39

Теперь наш скрипт сильно уменьшился в коде, стал куда более оптимальным. Вся процедура OnStart() теперь выглядит так:

Код: выделить все
void OnStart()
{
   double dblQ2, dblQ3;
   int    intLoDi = 2;
   f_SetTriData();
     
   dblQ3 = dblQ1;
   dblQ2 = f_GetQ2(intTri, intDir, dblQ1);

   Comment( "\n", "Размер лотов треугольника: ",
            "\n", strMSym[intTri][0]," = ", DoubleToStr(dblQ1, intLoDi),
            "\n", strMSym[intTri][1]," = ", DoubleToStr(dblQ2, intLoDi),
            "\n", strMSym[intTri][2]," = ", DoubleToStr(dblQ3, intLoDi));
}

Присваеваем лоту 3-ей пары размер 1-ой пары трина, а размер лота 2-ой пары рассчитываем, вызвав функцию f_GetQ2().
В результате также выводим найденные значения объемов лотов на экран посредством комментария.

Тест скрипта проходит успешно.
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.

Скрипт для расчета лотов треугольника

Сообщение Haos » 15 окт 2021, 13:33

Неплохо бы уже подумать о введении возможности учета постфикса в наименовании валютных пар, т.к. зачастую у брокера валютные пары имеют названия, типа, EURUSDm и т.п.
Постфикс будет добавляться к исходному наименованию валютной пары справа, сразу после, собственно, наименования как на примере выше.
Надо предусмотреть возможность проверки, что если его нет, то и выполнять процедуру не нужно по добавлению.
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.

Скрипт для расчета лотов треугольника

Сообщение Haos » 15 окт 2021, 13:33

Введем в параметрах для пользователя строковую переменную для работы с постфиксом:

Код: выделить все
input string strPoS  = "";    // Постфикс в наименованиях валютных пар

Вначале, нам так и так нужно сформировать массив strMSym исходными значениями валютных пар, а при втором прогоне добавить постфикс, если нужно.
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.

Скрипт для расчета лотов треугольника

Сообщение Haos » 15 окт 2021, 13:48

Также, лучше сразу эту операцию вынести в отдельную функцию, скажем с названием f_AddPostfix(). Данная фунция будет иметь тип void.
Параметр символов постфикса будем передавать в неё. Функция использует глобальную переменную N (детали см. здесь).
Код таков:
Код: выделить все
void f_AddPostfix(string pos)
{
   if(pos != "")   
   {
      for(int i = 0; i < N; i++)
      {
         for(int j = 0; j < 3; j++) { strMSym[i][j] += pos; }
      }
   }
}
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.

Скрипт для расчета лотов треугольника

Сообщение Haos » 15 окт 2021, 13:50

Сама функция будет вызываться в OnStart() строкой:
Код: выделить все
f_AddPostfix(strPoS);

Сразу после первоначальной инициализаци массива strMSym.
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.

Скрипт для расчета лотов треугольника

Сообщение Haos » 15 окт 2021, 13:54

Улучшения можно добавить уже и опубликовать в скрипт, но сначала я добавлю еще тринов из указанной выше таблицы, чтобы приблизиться к их полному набору с избытком нужным для работы. Таким образом, сам скрипт выложу позже в теме Скрпит расчета лотов треугольного арбитража под именем третьей версии.
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.


Вернуться в MQL – теория и практика

Кто сейчас на форуме?

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 67

Права доступа к форуму

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения