Расчет залога для будущего ордера: OrderCalcMargin

Прежде чем отправлять торговый приказ на сервер, MQL-программа может вычислить размер залога, который потребуется для планируемой торговой операции, с помощью функции OrderCalcMargin. Рекомендуется всегда это делать во избежание чрезмерной нагрузки на депозит.

bool OrderCalcMargin(ENUM_ORDER_TYPE action, const string symbol,
  double volume, double price, double &margin)

Функция вычисляет размер маржи, необходимой для указанного типа ордера action и финансового инструмента symbol в объеме volume лотов. При этом учитываются настройки текущего счета, но не учитываются имеющиеся отложенные ордера и открытые позиции. Перечисление ENUM_ORDER_TYPE было представлено в разделе Типы ордеров.

Значение маржи (в валюте счета) записывается в передаваемый по ссылке параметр margin.

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

Функция возвращает признак успеха (true) или ошибки (false). Код ошибки можно привычным образом получить из переменной _LastError.

Функцию OrderCalcMargin можно использовать только в экспертах и скриптах. Для расчета маржи в индикаторах нужно реализовать альтернативный способ, например, запускать вспомогательный эксперт в объекте-графике с передачей ему параметров и получением результата через механизм событий или самостоятельно описать в MQL5 вычисления по формулам согласно типам инструментов. В следующем разделе мы приведем пример такой реализации, вместе с оценкой потенциальной прибыли/убытка.

Мы могли бы написать простой скрипт, который вызывает OrderCalcMargin для символов из Обзора рынка, и сравнить значения маржи для них. Вместо этого слегка усложним задачу и рассмотрим заголовочный файл LotMarginExposure.mqh, который позволяет оценить загрузку депозита и уровень маржи после открытия позиции с предопределенным уровнем риска. Чуть позже мы познакомимся с функцией OrderCheck, способной предоставить аналогичную информацию. Однако наш алгоритм дополнительно сможет решать обратную задачу — выбирать размер лота по заданным уровням загрузки или риска.

Применение новых возможностей продемонстрировано в неторгующем эксперте LotMarginExposureTable.mq5.

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

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

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

Все функции файла объединены в пространство имен во избежание конфликтов и для наглядности (указание контекста перед вызовом внутренней функции дает знать о происхождении и размещении этой функции).

namespace LEMLR
{
   ...
};

Аббревиатура LEMLR означает "Lot, Exposure, Margin Level, Risk".

Основные расчеты выполняются в функции Estimate. Учитывая прототип встроенной функции OrderCalcMargin, легко предположить, что в параметрах функции Estimate потребуется передать название символа, тип ордера, объем и цену. Но это не все, что нам понадобится.

   bool Estimate(const ENUM_ORDER_TYPE typeconst string symbolconst double lot,
      const double price,...)

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

Ml = money / margin * 100
Ex = margin / money * 100

Здесь переменной margin обозначена сумма маржи, для получения которой достаточно вызвать OrderCalcMargin.

Однако трейдеры часто предпочитают исходить из предопределенного уровня загрузки или маржи, и рассчитывать для них объем. Более того, существует не менее популярный подход с расчетом лота на основе риска. Под риском понимается размер потенциального убытка от торговли при неблагоприятном движении цены, в результате чего будет уменьшаться содержимое другой переменной из вышеприведенных формул — money.

Для вычисления убытка важно знать волатильность финансового инструмента на торговом периоде (длительности стратегии) или дистанцию предполагаемого пользователем стоп-лосса.

Поэтому перечень параметров функции Estimate расширяется.

   bool Estimate(const ENUM_ORDER_TYPE typeconst string symbolconst double lot,
      const double price,
      const double exposureconst double riskLevelconst int riskPoints,
      const ENUM_TIMEFRAMES riskPerioddouble money,...)

В параметре exposure укажем желаемую загрузку депозита в процентах, а в параметре riskLevel — часть депозита (тоже в процентах), которой мы готовы рискнуть. Для расчетов на основе риска можно передать размер стоп-лосса в пунктах в параметре riskPoints. Когда он равен 0, в дело вступает параметр riskPeriod: в нем указывается период, для которого алгоритм автоматически рассчитает диапазон котировок символа в пунктах. Наконец, в параметре money можно задать произвольный размер свободных средств для оценки лота. Некоторые трейдеры условно делят депозит между несколькими роботами. Когда money равно 0, функция заполнит эту переменную свойством AccountInfoDouble(ACCOUNT_MARGIN_FREE).

Осталось решить, как возвращать результаты работы функции. Поскольку она способна оценить много торговых показателей и несколько вариантов объема, имеет смысл определить структуру SymbolLotExposureRisk.

   struct SymbolLotExposureRisk
   {
      double lot;                      // запрошенный объем (или минимальный)
      int atrPointsNormalized;         // диапазон цен нормализованный по размеру тика
      double atrValue;                 // диапазон как сумма прибыли/убытка для 1 лота
      double lotFromExposureRaw;       // ненормализованный (м.б. меньше минимального лота)
      double lotFromExposure;          // нормализованный лот из загрузки депозита
      double lotFromRiskOfStopLossRaw// ненормализованный (м.б. меньше минимального лота)
      double lotFromRiskOfStopLoss;    // нормализованный лот из риска
      double exposureFromLot;          // загрузка исходя из объема 'lot'
      double marginLevelFromLot;       // уровень маржи из объема 'lot'
      int lotDigits;                   // количество цифр в нормализованных лотах
   };

Поле lot в структуре содержит переданный в функцию Exposure лот, если он не равен 0. Если переданный лот — нулевой, вместо него подставляется свойство символа SYMBOL_VOLUME_MIN.

Под расчетные значения объемов исходя из загрузки депозита и риска выделено по два поля: с суффиксом Raw (lotFromExposureRaw, lotFromRiskOfStopLossRaw) и без него (lotFromExposure, lotFromRiskOfStopLoss). Raw-поля содержат "чистый арифметический" результат, который может не соответствовать спецификации символа. В полях без суффикса лоты нормализованы с учетом минимума, максимума и шага. Такое дублирование полезно, в частности, для тех случаев, когда расчет выдает значения меньше минимального лота (например, lotFromExposureRaw равно 0.023721 при минимуме 0.1, из-за чего lotFromExposure сводится к нулю): тогда по содержимому Raw-поля можно оценить, сколько средств добавить или насколько повысить риск, чтобы добраться до минимального лота.

Опишем последний выходной параметр функции Estimate как ссылку на данную структуру. В теле функции постепенно заполним все её поля. Прежде всего, получим размер маржи для одного лота с помощью вызова OrderCalcMargin и сохраним в локальную переменную lot1margin.

   bool Estimate(const ENUM_ORDER_TYPE typeconst string symbolconst double lot,
      const double priceconst double exposure,
      const double riskLevelconst int riskPointsconst ENUM_TIMEFRAMES riskPeriod,
      double moneySymbolLotExposureRisk &r)
   {
      double lot1margin;
      if(!OrderCalcMargin(typesymbol1.0,
         price == 0 ? GetCurrentPrice(symboltype) : price,
         lot1margin))
      {
         Print("OrderCalcMargin "symbol" failed: "_LastError);
         return false;
      }
      if(lot1margin == 0)
      {
         Print("Margin "symbol" is zero, "_LastError);
         return false;
      }
      ...

Если входная цена не указана, т.е. price равно 0, вспомогательная функция GetCurrentPrice возвращает подходящую цену на основе типа ордера: для покупок будет взято свойство символа SYMBOL_ASK, и для продаж — SYMBOL_BID. Эта и другие вспомогательные функции здесь опущены, с их содержимым можно ознакомиться в прилагаемых исходных кодах.

Если расчет маржи завершился ошибкой или получено нулевое значение, функция Estimate вернет false.

Следует иметь в виду, что нулевая маржа может быть нормой или ошибкой, в зависимости от инструмента и типа ордера. Так для биржевых тикеров отложенные ордера облагаются залогом, а для внебиржевых — нет (то есть залог 0 корректен). Этот нюанс следует учитывать в вызывающем коде: он должен запрашивать маржу только по таким сочетаниям символов и типов операций, для которых она имеет смысл и предполагается ненулевой.

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

      double usedMargin = 0;
      if(money == 0)
      {
         money = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
         usedMargin = AccountInfoDouble(ACCOUNT_MARGIN);
      }
   
      r.lotFromExposureRaw = money * exposure / 100.0 / lot1margin;
      r.lotFromExposure = NormalizeLot(symbolr.lotFromExposureRaw);
      ...

Вспомогательная функция NormalizeLot приведена ниже.

Для того чтобы получить лот в зависимости от риска и волатильности потребуется чуть больше вычислений.

      const double tickValue = SymbolInfoDouble(symbolSYMBOL_TRADE_TICK_VALUE);
      const int pointsInTick = (int)(SymbolInfoDouble(symbolSYMBOL_TRADE_TICK_SIZE)
         / SymbolInfoDouble(symbolSYMBOL_POINT));
      const double pointValue = tickValue / pointsInTick;
      const int atrPoints = (riskPoints > 0) ? (int)riskPoints :
         (int)(((MathMax(iHigh(symbolriskPeriod1), iHigh(symbolriskPeriod0))
         -  MathMin(iLow(symbolriskPeriod1), iLow(symbolriskPeriod0)))
         / SymbolInfoDouble(symbolSYMBOL_POINT)));
      // округление по размеру тика
      r.atrPointsNormalized = atrPoints / pointsInTick * pointsInTick;
      r.atrValue = r.atrPointsNormalized * pointValue;
      
      r.lotFromRiskOfStopLossRaw = money * riskLevel / 100.0
         / (pointValue * r.atrPointsNormalized);
      r.lotFromRiskOfStopLoss = NormalizeLot(symbolr.lotFromRiskOfStopLossRaw);
      ...

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

Наконец, получим загрузку счета и уровень маржи для заданного лота.

      r.lot = lot <= 0 ? SymbolInfoDouble(symbolSYMBOL_VOLUME_MIN) : lot;
      double margin = r.lot * lot1margin;
     
      r.exposureFromLot = (margin + usedMargin) / money * 100.0;
      r.marginLevelFromLot = margin > 0 ? money / (margin + usedMargin) * 100.0 : 0;
      r.lotDigits = (int)MathLog10(1.0 / SymbolInfoDouble(symbolSYMBOL_VOLUME_MIN));
      
      return true;
   }

В случае успешного расчета функция вернет true.

А вот и функция NormalizeLot с сокращениями (все проверки на 0 для простоты опущены). Подробности про соответствующие свойства можно найти в разделе Разрешенные объемы торговых операций.

   double NormalizeLot(const string symbolconst double lot)
   {
      const double stepLot = SymbolInfoDouble(symbolSYMBOL_VOLUME_STEP);
      const double newLotsRounded = MathFloor(lot / stepLot) * stepLot;
      const double minLot = SymbolInfoDouble(symbolSYMBOL_VOLUME_MIN);
      if(newLotsRounded < minLotreturn 0;
      const double maxLot = SymbolInfoDouble(symbolSYMBOL_VOLUME_MAX);
      if(newLotsRounded > maxLotreturn maxLot;
      return newLotsRounded;
   }

Приведенная выше реализация Estimate не учитывает поправки на перекрывающиеся позиции — как правило, они приводят к уменьшению залога, поэтому текущая оценка загрузки счета и уровня маржи может быть более пессимистичной, чем получится в реальности, но это обеспечивает дополнительную защиту. Желающие могут добавить код для анализа состава уже замороженных средств счета (их общая сумма содержится в свойстве счета ACCOUNT_MARGIN) в разбивке по позициям и ордерам: тогда станет возможным учесть потенциальный взаимозачет маржи с новым ордером (например, учтётся только наибольшая позиция из встречных или применится пониженная ставка хеджированной маржи, см. подробности в разделе Маржинальные требования).

Пришло время применить оценку маржи и лотов на практике в эксперте LotMarginExposureTable.mq5. С учетом того, что Raw-поля мы будем показывать только в тех случаях, когда нормализация лотов привела к их обнулению, общее количество колонок в результирующей таблице показателей равно 8.

#include <MQL5Book/LotMarginExposure.mqh>
#define TBL_COLUMNS 8

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

input ENUM_ORDER_TYPE Action = ORDER_TYPE_BUY;
input string WorkList = "";                   // Symbols (comma,separated,list)
input double Money = 0;                       // Money (0 = free margin)
input double Lot = 0;                         // Lot (0 = min lot)
input double Exposure = 5.0;                  // Exposure (%)
input double RiskLevel = 5.0;                 // RiskLevel (%)
input int RiskPoints = 0;                     // RiskPoints/SL (0 = auto-range of RiskPeriod)
input ENUM_TIMEFRAMES RiskPeriod = PERIOD_W1;

Для отложенных типов ордеров необходимо обязательно выбирать биржевые символы, поскольку для иных символов будет получена нулевая маржа, что спровоцирует ошибку в функции Estimate. Если список символов оставлен пустым, эксперт обработает только символ текущего графика. Нулевые по умолчанию значения в параметрах Money и Lot означают, соответственно, текущий размер свободных средств на счете и минимальный лот по каждому символу.

Значение 0 в параметре RiskPoints предписывает получение диапазона цен за RiskPeriod (по умолчанию неделя).

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

input int UpdateFrequency = 0// UpdateFrequency (sec, 0 - once per bar)

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

string symbols[];
datetime lastTime;

При запуске включаем секундный таймер.

void OnInit()
{
   Comment("Starting...");
   lastTime = 0;
   EventSetTimer(1);
}

В обработчике таймера обеспечиваем первый вызов основного расчета в OnTick, если OnTick еще не был вызван по приходу тика — такое может быть, например, на выходных или на спокойном рынке. Также OnTimer является точкой входа для повторных расчетов с заданной частотой.

void OnTimer()
{
   if(lastTime == 0// расчет первый раз (если не успел сработать OnTick)
   {
      OnTick();
      Comment("Started");
   }
   else if(lastTime != -1)
   {
      if(UpdateFrequency <= 0// если частоты нет, работаем по новым барам в OnTick
      {
         EventKillTimer();     // и таймер больше не нужен
      }
      else if(TimeCurrent() - lastTime >= UpdateFrequency)
      {
         lastTime = LONG_MAX// предотвращаем повторный вход в эту ветвь if
         OnTick();
         if(lastTime != -1)   // отработали без ошибки
         {
            lastTime = TimeCurrent(); // обновляем метку времени
         }
      }
      Comment("");
   }
}

В обработчике OnTick прежде всего проверяем входные параметры и преобразуем список символов в массив строк. В случае обнаружения проблем в lastTime записывается признак ошибки: значение -1, и обработка последующих тиков прерывается в самом начале.

void OnTick()
{
   if(lastTime == -1return// уже была ошибка, выходим
  
   if(UpdateFrequency <= 0)   // если частота обновления не задана
   {
      if(lastTime == iTime(NULL00)) return// ждем нового бара
   }
   else if(TimeCurrent() - lastTime < UpdateFrequency)
   {
      return;
   }
      
   const int ns = StringSplit((WorkList == "" ? _Symbol : WorkList), ',', symbols);
   if(ns <= 0)
   {
      Print("Empty symbols");
      lastTime = -1;
      return;
   }
   
   if(Exposure > 100 || Exposure <= 0)
   {
      Print("Percent of Exposure is incorrect: "Exposure);
      lastTime = -1;
      return;
   }
   
   if(RiskLevel > 100 || RiskLevel <= 0)
   {
      Print("Percent of RiskLevel is incorrect: "RiskLevel);
      lastTime = -1;
      return;
   }
   ...

В частности, ошибкой считается, если входные значения Exposure и RiskLevel не лежат в диапазоне от 0 до 100, присущем процентам. В случае нормальных входных данных обновляем временную метку, описываем структуру LEMLR::SymbolLotExposureRisk для приема расчетных показателей из функции LEMLR::Estimate (по одному символу), а также двумерный массив LME (от "Lot Margin Exposure") для сбора показателей по всем символам.

   lastTime = UpdateFrequency > 0 ? TimeCurrent() : iTime(NULL00);
   
   LEMLR::SymbolLotExposureRisk r = {};
   
   double LME[][13];
   ArrayResize(LMEns);
   ArrayInitialize(LME0);
   ...

В цикле по символам вызываем функцию LEMLR::Estimate и заполняем массив LME.

   for(int i = 0i < nsi++)
   {
      if(!LEMLR::Estimate(Actionsymbols[i], Lot0,
         ExposureRiskLevelRiskPointsRiskPeriodMoneyr))
      {
        Print("Calc failed (will try on the next bar, or refresh manually)");
        return;
      }
      
      LME[i][eLot] = r.lot;
      LME[i][eAtrPointsNormalized] = r.atrPointsNormalized;
      LME[i][eAtrValue] = r.atrValue;
      LME[i][eLotFromExposureRaw] = r.lotFromExposureRaw;
      LME[i][eLotFromExposure] = r.lotFromExposure;
      LME[i][eLotFromRiskOfStopLossRaw] = r.lotFromRiskOfStopLossRaw;
      LME[i][eLotFromRiskOfStopLoss] = r.lotFromRiskOfStopLoss;
      LME[i][eExposureFromLot] = r.exposureFromLot;
      LME[i][eMarginLevelFromLot] = r.marginLevelFromLot;
      LME[i][eLotDig] = r.lotDigits;
      LME[i][eMinLot] = SymbolInfoDouble(symbols[i], SYMBOL_VOLUME_MIN);
      LME[i][eContract] = SymbolInfoDouble(symbols[i], SYMBOL_TRADE_CONTRACT_SIZE);
      LME[i][eSymbol] = pack2double(symbols[i]);
   }
   ...

В качестве индексов массива использованы элементы специального перечисления LME_FIELDS, которое предоставляет одновременно названия и номера для показателей из структуры.

enum LME_FIELDS // 10 полей + 3 дополнительных свойства символа
{
   eLot,
   eAtrPointsNormalized,
   eAtrValue,
   eLotFromExposureRaw,
   eLotFromExposure,
   eLotFromRiskOfStopLossRaw,
   eLotFromRiskOfStopLoss,
   eExposureFromLot,
   eMarginLevelFromLot,
   eLotDig,
   eMinLot,
   eContract,
   eSymbol
};

Свойства SYMBOL_VOLUME_MIN и SYMBOL_TRADE_CONTRACT_SIZE добавляются для справки. Название символа "упаковывается" в приблизительное значение типа double с помощью функции pack2double, чтобы впоследствии реализовать унифицированную сортировку по любому из полей, включая и названия.

double pack2double(const string s)
{
   double r = 0;
   for(int i = 0i < StringLen(s); i++)
   {
      r = (r * 255) + (StringGetCharacter(si) % 255);
   }
   return r;
}

На этом этапе мы могли бы уже запустить эксперт и распечатать результаты в журнале, примерно так.

ArrayPrint(LME);

Но смотреть все время в журнал неудобно. Кроме того, единое форматирование величин из разных колонок, и тем более представление "упакованных" строк в double никак нельзя назвать дружественным. Поэтому был разработан класс табло (Tableau.mqh) для отображения произвольной таблицы на графике. Помимо того что при подготовке таблицы мы можем сами управлять форматом каждого поля (в перспективе — подсвечивать разным цветом), этот класс позволяет интерактивно сортировать таблицу по любой колонке: первый щелчок мышью сортирует в одном направлении, второй — в обратном, третий — отменяет сортировку.

Здесь мы не будем описывать класс подробно. При необходимости можно ознакомиться с его исходным кодом. Важно лишь отметить, что интерфейс строится на базе графических объектов. Фактически ячейки таблицы формируются объектами типа OBJ_LABEL, и все их свойства уже знакомы читателю. Однако кое-какие технические приемы, использованные в исходном коде табло — в частности, работа с графическими ресурсами и измерение отображаемого текста, будут представлены позднее, в седьмой части.

Конструктор класса Tableau принимает несколько параметров:

  • prefix — префикс для имен создаваемых графических объектов;
  • rows — количество строк;
  • cols — количество колонок;
  • height — высота строки в пикселях (-1 означает удвоенный размер шрифта);
  • width — ширина ячейки в пикселях;
  • c — угол графика для привязки объектов;
  • g — зазор в пикселях между ячейками;
  • f — размер шрифта;
  • font — название шрифта для обычных ячеек;
  • bold — название жирного шрифта для заголовков;
  • bgc — цвет фона;
  • bgt — прозрачность фона.

class Tableau
{
public:
   Tableau(const string prefixconst int rowsconst int cols,
      const int height = 16const int width = 100,
      const ENUM_BASE_CORNER c = CORNER_RIGHT_LOWERconst int g = 8,
      const int f = 8const string font = "Consolas"const string bold = "Arial Black",
      const int mask = TBL_FLAG_COL_0_HEADER,
      const color bgc = 0x808080const uchar bgt = 0xC0)
      ...
};

Большинство этих параметров пользователь может задать во входных переменных эксперта LotMarginExposureTable.mq5.

input ENUM_BASE_CORNER Corner = CORNER_RIGHT_LOWER;
input int Gap = 16;
input int FontSize = 8;
input string DefaultFontName = "Consolas";
input string TitleFontName = "Arial Black";
input string MotoTypeFontsHint = "Consolas/Courier/Courier New/Lucida Console/Lucida Sans Typewriter";
input color BackgroundColor = 0x808080;
input uchar BackgroundTransparency = 0xC0// BackgroundTransparency (255 - opaque, 0 - glassy)

Количество колонок таблицы у нас определено заранее, количество строк равно количеству символов, плюс верхняя строка с заголовками.

Важно отметить, что шрифты для таблицы следует выбирать без пропорционального начертания букв, поэтому в переменной MotoTypeFontsHint приводится подсказка с набором стандартных моноширинных шрифтов Windows.

Заполнение созданных графических объектов данными производит метод fill класса Tableau.

   bool fill(const string &data[], const string &hint[]) const;

В него наш эксперт передает массив строк data, которые получены из массива LME путем серии преобразований через StringFormat, а также массив hint со всплывающими подсказками для заголовков.

На следующем изображении представлен фрагмент графика с работающим экспертом при настройках по умолчанию, но со списком символов "EURUSD,USDRUB,USDCNH,XAUUSD,XPDUSD".

Уровни загрузки депозита и маржи при минимальном лоте по каждому символу
Уровни загрузки депозита и маржи при минимальном лоте по каждому символу

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

В последующих колонках:

  • L(E) — лот, вычисленный для уровня загрузки E депозита 5% после сделки;
  • L(R) — лот, вычисленный при риске R на 5% депозита после неудачной торговли (диапазон в пунктах и сумма риска — в последней колонке);
  • E% — загрузка депозита после входа минимальным лотом;
  • M% — уровень маржи после входа минимальным лотом;
  • MinL — минимальный лот для каждого символа;
  • Contract — размер контракта (1 лот) для каждого символа;
  • Risk — прибыль/убыток в деньгах при торговле 1 лотом и этот же диапазон в пунктах.

В колонках E% и M% в данном случае использованы минимальные лоты, поскольку входной параметре Lot равен 0 (по умолчанию).

Видно, что при загрузке депозита 5% торговля возможна для всех выбранных символов, кроме "XPDUSD". Для последнего получился объем 0.03272, что меньше минимального лота 0.1, в связи с чем результат заключен в скобки. Если разрешим загрузку 20% (введем 20 в параметр Exposure), получим для "XPDUSD" минимальный лот 0.1.

Если введем в параметр Lot значение 1, увидим в таблице обновленные значения в колонках E% и M% (загрузка увеличится, уровень маржи упадет).

Уровни загрузки депозита и маржи при единичном лоте по каждому символу
Уровни загрузки депозита и маржи при единичном лоте по каждому символу

На последнем скриншоте, иллюстрирующем работу эксперта, приведен большой набор "голубых фишек" российской биржи MOEX с сортировкой по объему, рассчитанному для 5%-загрузки депозита (2-я колонка). Среди нестандартных настроек можно отметить, что Lot=10, а период для вычисления ценового диапазона и риска равен MN1. Фон сделан полупрозрачным белым, привязка — к левому верхнему углу графика.

Лоты, загрузка депозита и уровень маржи для инструментов MOEX
Лоты, загрузка депозита и уровень маржи для инструментов MOEX