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

Материал из ТХАБ.РФ
Перейти к: навигация, поиск
м (Код)
(Код)
 
(не показано 5 промежуточных версий 1 участника)
Строка 1: Строка 1:
 
== Описание ==
 
== Описание ==
  
Робот торгует по 2-м простым скользящим средним (MA), выставляет тэйк-профит и стоп-лимит.
+
Робот торгует по 2-м простым [[Скользящая средняя|скользящим средним]] (MA), выставляет тэйк-профит и стоп-лимит.
  
 
# Если нет открытых роботом позиций выполняется следующий алгоритм:
 
# Если нет открытых роботом позиций выполняется следующий алгоритм:
# Если быстрая скользящая пересекает медленную снизу вверх и на текущей свече еще не открывались позиции, совершается покупка.
+
* Если быстрая скользящая пересекает медленную снизу вверх и на текущей свече еще не открывались позиции, совершается покупка.
# Если быстрая скользящая пересекает медленную сверху вниз и на текущей свече еще не открывались позиции, совершается продажа.
+
* Если быстрая скользящая пересекает медленную сверху вниз и на текущей свече еще не открывались позиции, совершается продажа.
# Если открылась позиция, выставляется "Тэйк-профит и Стоп-лимит"
+
# Если открылась позиция, выставляется  
 +
* Цена продажи для фиксации прибыли при росте цены (Тэйк-профит) и
 +
* Цена продажи для ограничения убытка при падении цены (Стоп-лимит)
 
# Робот ждет, пока закроется позиция по Стоп-лоссу, либо Тэйк-профиту, после чего переходит к пункту №1
 
# Робот ждет, пока закроется позиция по Стоп-лоссу, либо Тэйк-профиту, после чего переходит к пункту №1
  
 
=== Особенности: ===
 
=== Особенности: ===
1. Робот выводит в текстовых сообщениях информацию о ключевых моментах алгоритма, у всех его сообщений префикс "Простой MA-робот:".
+
 
2. Робот всегда находится в 1-м из 2-х состояний(ROBOT_STATE):'В ПРОЦЕССЕ СДЕЛКИ', либо 'В ПОИСКЕ ТОЧКИ ВХОДА'.
+
1. Робот выводит в текстовых сообщениях информацию о ключевых моментах работы алгоритма, у всех его сообщений префикс "Простой MA-робот:".
 +
 
 +
2. Робот всегда находится в одном из 2-х состояний (ROBOT_STATE):
 +
* В ПРОЦЕССЕ СДЕЛКИ или
 +
* В ПОИСКЕ ТОЧКИ ВХОДА
 +
 
 
3. Если при выставлении заявки на продажу робот узнает, что операции шорт запрещены по данному инструменту, он больше не будет открывать шорт, только лонг.
 
3. Если при выставлении заявки на продажу робот узнает, что операции шорт запрещены по данному инструменту, он больше не будет открывать шорт, только лонг.
4. Когда робот получает сигнал на открытие сделки, он совершает 10 попыток с прмежутками в 100 мс открыть позицию, если этого не удается, останавливает скрипт.
+
 
5. После открытия позиции робот совершает 10 попыток с прмежутками в 100 мс выставить Тэйк-профит и Стоп-лимит, а затем дождаться закрытия позиции, если этого не удается, останавливает скрипт.
+
4. Когда робот получает сигнал на открытие сделки, он совершает 10 попыток с промежутками в 100 мс открыть позицию, если этого не удается, останавливает скрипт.
 +
 
 +
5. После открытия позиции робот совершает 10 попыток с промежутками в 100 мс выставить Тэйк-профит и Стоп-лимит, а затем дождаться закрытия позиции, если этого не удается, останавливает скрипт.
 +
 
 
6. Если стоп-заявка сработала, но позиция не закрылась в течении 10 секунд, пытается за 10 попыток принудительно закрыть позицию встречной сделкой.
 
6. Если стоп-заявка сработала, но позиция не закрылась в течении 10 секунд, пытается за 10 попыток принудительно закрыть позицию встречной сделкой.
  
 
Если позицию удалось закрыть (даже принудительно), продолжает работать, иначе скрипт останавливается.
 
Если позицию удалось закрыть (даже принудительно), продолжает работать, иначе скрипт останавливается.
 +
 +
== Недостатки робота (Надо сделать) ==
 +
* Расчет скользящих средних надо не самому считать а брать их QUIK https://quikluacsharp.ru/quik-qlua/poluchenie-v-qlua-lua-dannyh-iz-grafikov-i-indikatorov/
 +
* в роботе не релизован вариант переворота позиции
  
 
== Код ==
 
== Код ==
Строка 43: Строка 57:
 
  --/*НАСТРАИВАЕМЫЕ ПАРАМЕТРЫ*/
 
  --/*НАСТРАИВАЕМЫЕ ПАРАМЕТРЫ*/
 
  --ACCOUNT          = 'NL0011100043';      -- Идентификатор счета
 
  --ACCOUNT          = 'NL0011100043';      -- Идентификатор счета
  ACCOUNT           = 'SPBFUT00506';        -- Идентификатор счета
+
  ACCOUNT             = 'SPBFUT00506';        -- Идентификатор счета
 
  --CLASS_CODE        = 'QJSIM';              -- Код класса
 
  --CLASS_CODE        = 'QJSIM';              -- Код класса
  CLASS_CODE       = 'SPBFUT';            -- Код класса
+
  CLASS_CODE         = 'SPBFUT';            -- Код класса
 
  --SEC_CODE          = 'SBER';              -- Код бумаги
 
  --SEC_CODE          = 'SBER';              -- Код бумаги
  SEC_CODE         = 'RIZ5';              -- Код бумаги
+
  SEC_CODE           = 'RIZ5';              -- Код бумаги
  INTERVAL         = INTERVAL_M1;          -- Таймфрейм графика (для построения скользящих)
+
  INTERVAL           = INTERVAL_M1;          -- Таймфрейм графика (для построения скользящих)
 
  SLOW_MA_PERIOD    = 12;                  -- ПЕРИОД МЕДЛЕННОЙ скользящей
 
  SLOW_MA_PERIOD    = 12;                  -- ПЕРИОД МЕДЛЕННОЙ скользящей
 
  SLOW_MA_SOURCE    = 'C';                  -- ИСТОЧНИК МЕДЛЕННОЙ скользящей [O - open, C - close, H - hi, L - low]
 
  SLOW_MA_SOURCE    = 'C';                  -- ИСТОЧНИК МЕДЛЕННОЙ скользящей [O - open, C - close, H - hi, L - low]
Строка 65: Строка 79:
 
  trans_result_msg  = '';                  -- Сообщение по текущей транзакции из функции OnTransPeply
 
  trans_result_msg  = '';                  -- Сообщение по текущей транзакции из функции OnTransPeply
 
  CurrentDirect    = 'BUY';                -- Текущее НАПРАВЛЕНИЕ ['BUY', или 'SELL']
 
  CurrentDirect    = 'BUY';                -- Текущее НАПРАВЛЕНИЕ ['BUY', или 'SELL']
  LastOpenBarIndex  =  0;                  -- Индекс свечи, на которой была открыта последняя позиция (нужен для того, чтобы после закрытия по стопу тут же не открыть еще  
+
  LastOpenBarIndex  =  0;                  -- Индекс свечи, на которой была открыта последняя позиция (нужен для того, чтобы после закрытия по стопу тут же не открыть еще одну позицию)
одну позицию)
 
 
   
 
   
 
  Run              = true;                -- Флаг поддержания работы бесконечного цикла в main
 
  Run              = true;                -- Флаг поддержания работы бесконечного цикла в main

Текущая версия на 21:53, 28 мая 2020

Описание

Робот торгует по 2-м простым скользящим средним (MA), выставляет тэйк-профит и стоп-лимит.

  1. Если нет открытых роботом позиций выполняется следующий алгоритм:
  • Если быстрая скользящая пересекает медленную снизу вверх и на текущей свече еще не открывались позиции, совершается покупка.
  • Если быстрая скользящая пересекает медленную сверху вниз и на текущей свече еще не открывались позиции, совершается продажа.
  1. Если открылась позиция, выставляется
  • Цена продажи для фиксации прибыли при росте цены (Тэйк-профит) и
  • Цена продажи для ограничения убытка при падении цены (Стоп-лимит)
  1. Робот ждет, пока закроется позиция по Стоп-лоссу, либо Тэйк-профиту, после чего переходит к пункту №1

Особенности:

1. Робот выводит в текстовых сообщениях информацию о ключевых моментах работы алгоритма, у всех его сообщений префикс "Простой MA-робот:".

2. Робот всегда находится в одном из 2-х состояний (ROBOT_STATE):

  • В ПРОЦЕССЕ СДЕЛКИ или
  • В ПОИСКЕ ТОЧКИ ВХОДА

3. Если при выставлении заявки на продажу робот узнает, что операции шорт запрещены по данному инструменту, он больше не будет открывать шорт, только лонг.

4. Когда робот получает сигнал на открытие сделки, он совершает 10 попыток с промежутками в 100 мс открыть позицию, если этого не удается, останавливает скрипт.

5. После открытия позиции робот совершает 10 попыток с промежутками в 100 мс выставить Тэйк-профит и Стоп-лимит, а затем дождаться закрытия позиции, если этого не удается, останавливает скрипт.

6. Если стоп-заявка сработала, но позиция не закрылась в течении 10 секунд, пытается за 10 попыток принудительно закрыть позицию встречной сделкой.

Если позицию удалось закрыть (даже принудительно), продолжает работать, иначе скрипт останавливается.

Недостатки робота (Надо сделать)

Код

--[[ Простой MA-робот (c)QuikLuaCSharp.ru
!!! ДЛЯ ИСПОЛЬЗОВАНИЯ ТОЛЬКО В ОБРАЗОВАТЕЛЬНЫХ ЦЕЛЯХ НА ДЕМО-СЧЕТЕ !!!

Робот торгует по 2-м простым(simple) скользящим средним (MA).
1.Если нет открытых роботом позиций выполняется следующий алгоритм:
  1.Если быстрая скользящая пересекает медленную снизу вверх и на текущей свече еще не открывались позиции, совершается покупка.
  2.Если быстрая скользящая пересекает медленную сверху вниз и на текущей свече еще не открывались позиции, совершается продажа.
2.Если открылась позиция, выставляется "Тэйк-профит и Стоп-лимит"
3.Робот ждет, пока закроется позичия по Стоп-лоссу, либо Тэйк-профиту, после чего переходит к пункту №1

Особенности:
1.Робот выводит в текстовых сообщениях информацию о ключевых моментах алгоритма, у всех его сообщений префикс "Простой MA-робот:".
2.Робот всегда находится в 1-м из 2-х состояний(ROBOT_STATE):'В ПРОЦЕССЕ СДЕЛКИ', либо 'В ПОИСКЕ ТОЧКИ ВХОДА'.
3.Если при выставлении заявки на продажу робот узнает, что операции шорт запрещены по данному инструменту, он больше не будет открывать шорт, только лонг.
4.Когда робот получает сигнал на открытие сделки, он совершает 10 попыток с прмежутками в 100 мс открыть позицию, если этого не удается, останавливает скрипт.
5.После открытия позиции робот совершает 10 попыток с прмежутками в 100 мс выставить Тэйк-профит и Стоп-лимит, а затем дождаться закрытия позиции,
 если этого не удается, останавливает скрипт.
6.Если стоп-заявка сработала, но позиция не закрылась в течении 10 секунд, пытается за 10 попыток принудительно закрыть позицию встречной сделкой.
 Если позицию удалось закрыть (даже принудительно), продолжает работать, иначе скрипт останавливается.
]]

--/*НАСТРАИВАЕМЫЕ ПАРАМЕТРЫ*/
--ACCOUNT           = 'NL0011100043';       -- Идентификатор счета
ACCOUNT             = 'SPBFUT00506';        -- Идентификатор счета
--CLASS_CODE        = 'QJSIM';              -- Код класса
CLASS_CODE          = 'SPBFUT';             -- Код класса
--SEC_CODE          = 'SBER';               -- Код бумаги
SEC_CODE            = 'RIZ5';               -- Код бумаги
INTERVAL            = INTERVAL_M1;          -- Таймфрейм графика (для построения скользящих)
SLOW_MA_PERIOD    = 12;                   -- ПЕРИОД МЕДЛЕННОЙ скользящей
SLOW_MA_SOURCE    = 'C';                  -- ИСТОЧНИК МЕДЛЕННОЙ скользящей [O - open, C - close, H - hi, L - low]
FAST_MA_PERIOD    = 4;                    -- ПЕРИОД БЫСТРОЙ скользящей
FAST_MA_SOURCE    = 'C';                  -- ИСТОЧНИК БЫСТРОЙ скользящей [O - open, C - close, H - hi, L - low]
STOP_LOSS         = 10;                   -- Размер СТОП-ЛОССА (в шагах цены)
TAKE_PROFIT       = 30;                   -- Размер ТЭЙК-ПРОФИТА (в шагах цены)

--/*РАБОЧИЕ ПЕРЕМЕННЫЕ РОБОТА (менять не нужно)*/
SEC_PRICE_STEP    = 0;                    -- ШАГ ЦЕНЫ ИНСТРУМЕНТА
SEC_NO_SHORT      = false;                -- Флаг, что по данному инструменту запрещены операции шорт
DS                = nil;                  -- Источник данных графика (DataSource)
ROBOT_STATE       ='В ПОИСКЕ ТОЧКИ ВХОДА';-- СОСТОЯНИЕ робота ['В ПРОЦЕССЕ СДЕЛКИ', либо 'В ПОИСКЕ ТОЧКИ ВХОДА']
trans_id          = os.time();            -- Задает начальный номер ID транзакций
trans_Status      = nil;                  -- Статус текущей транзакции из функции OnTransPeply
trans_result_msg  = ;                   -- Сообщение по текущей транзакции из функции OnTransPeply
CurrentDirect     = 'BUY';                -- Текущее НАПРАВЛЕНИЕ ['BUY', или 'SELL']
LastOpenBarIndex  =  0;                   -- Индекс свечи, на которой была открыта последняя позиция (нужен для того, чтобы после закрытия по стопу тут же не открыть еще одну позицию)

Run               = true;                 -- Флаг поддержания работы бесконечного цикла в main

-- Функция первичной инициализации скрипта (ВЫЗЫВАЕТСЯ ТЕРМИНАЛОМ QUIK в самом начале)
function OnInit()
  -- Получает доступ к свечам графика
  local Error = ;
  DS,Error = CreateDataSource(CLASS_CODE, SEC_CODE, INTERVAL);
  -- Если график, к которому нужно подключиться не открыт в терминале, то данные заказываются с сервера, на их получение нужно время,
  -- по этому, рекомендуется добавлять вот такое ожидание, прежде, чем обращаться к DS:
  -- Ждет, пока данные будут получены с сервера (на случай, если такой график не открыт)
  while (Error == "" or Error == nil) and DS:Size() == 0 do sleep(1) end
  if Error ~= "" and Error ~= nil then message("Ошибка подключения к графику: "..Error) return end

  -- Подписывается на обновления графика
  DS:SetEmptyCallback()

  -- Получает ШАГ ЦЕНЫ ИНСТРУМЕНТА
  SEC_PRICE_STEP = getParamEx(CLASS_CODE, SEC_CODE, "SEC_PRICE_STEP").param_value;
end;

function main()
  -- Выводит сообщение
  message('Простой MA-робот: '..ROBOT_STATE);
  -- "Бесконечный" цикл
  while Run do 
     --Если СОСТОЯНИЕ робота "В ПРОЦЕССЕ СДЕЛКИ"
     if ROBOT_STATE == 'В ПРОЦЕССЕ СДЕЛКИ' then
        -- Выводит сообщение
        message('Простой MA-робот: В ПРОЦЕССЕ СДЕЛКИ');
        -- Делает 10 попыток открыть сделку
        local Price = false; -- Переменная для получения результата открытия позиции (цена, либо ошибка(false))
        for i=1,10 do
           if not Run then return; end; -- Если скрипт останавливается, не затягивает процесс
           -- Если первый раз пытается открыть SELL, а операции шорт по данному инструменту запрещены
           if CurrentDirect == "SELL" and SEC_NO_SHORT then
              -- Прерывает цикл FOR
              break; 
           end;
           -- Совершает СДЕЛКУ указанного типа ["BUY", или "SELL"] по рыночной(текущей) цене размером в 1 лот,
           --- возвращает цену открытой сделки, либо FALSE, если невозможно открыть сделку
           Price = Trade(CurrentDirect);
           -- Если сделка открылась
           if Price ~= false then
              -- Прерывает цикл FOR
              break;
           end;
           sleep(100); -- Пауза в 100 мс между попытками открыть сделку
        end;
        if not Run then return; end; -- Если скрипт останавливается, не затягивает процесс
        -- Если сделка открыта
        if Price ~= false then
           -- Запоминает индекс свечи, на которой была открыта последняя позиция (нужен для того, чтобы после закрытия по стопу тут же не открыть еще одну позицию)
           LastOpenBarIndex = DS:Size();
           -- Выводит сообщение
           message('Простой MA-робот: Открыта сделка '..CurrentDirect..' по цене '..Price);
           -- Делает 10 попыток выставить СТОП-ЛОСС и ТЭЙК-ПРОФИТ, и дождаться закрытия сделки
           message('Простой MA-робот: Делает 10 попыток выставить СТОП-ЛОСС и ТЭЙК-ПРОФИТ, и дождаться закрытия сделки');
           local Result = nil; -- Переменная для получения результата выставления и срабатывания СТОП-ЛОСС и ТЭЙК-ПРОФИТ
           for i=1,10 do
              if not Run then return; end; -- Если скрипт останавливается, не затягивает процесс
              -- Выставляет СТОП-ЛОСС и ТЭЙК-ПРОФИТ, ЖДЕТ пока он сработает, принимает ЦЕНУ и ТИП ["BUY", или "SELL"] открытой сделки,
              --- возвращает FALSE, если не удалось выставить СТОП-ЛОСС и ТЭЙК-ПРОФИТ, или TRUE, если сделка закрылась,
              --- либо NIL, если при ошибке за 10 попыток не удалось принудительно закрыть позицию
              Result = SL_TP(Price, CurrentDirect);
              -- Если сделка закрылась
              if Result == true then
                 -- Прерывает цикл FOR
                 break;
              end;
           end;
           -- Если за 10 попыток не удалось закрыть позицию
           if Result == nil or Result == false then
              -- Выводит сообщение
              message('Простой MA-робот: После 10-и попыток не удалось закрыть сделку!!! Завершение скрипта!!!');
              -- Завершает выполнение скрипта
              Run = false;
              -- Прерывает основной цикл WHILE
              break;
           else -- УДАЛОСЬ ЗАКРЫТЬ СДЕЛКУ               
              -- Выводит сообщение
              message('Простой MA-робот: Сделка закрыта по СТОП-ЛОССУ, либо ТЭЙК-ПРОФИТУ');
              --Меняет СОСТОЯНИЕ робота на "В ПОИСКЕ ТОЧКИ ВХОДА"
              ROBOT_STATE = 'В ПОИСКЕ ТОЧКИ ВХОДА';
              -- Выводит сообщение
              message('Простой MA-робот: В ПОИСКЕ ТОЧКИ ВХОДА');
           end;
        else -- Сделку не удалось открыть
           -- Если первый раз пытался открыть SELL, а операции шорт по данному инструменту запрещены
           if CurrentDirect == "SELL" and SEC_NO_SHORT then
              -- Выводит сообщение
              message('Простой MA-робот: Была первая попытка совершить запрещенную операцию шорт! Больше этого не повторится:)');
              --Меняет СОСТОЯНИЕ робота на "В ПОИСКЕ ТОЧКИ ВХОДА"
              ROBOT_STATE = 'В ПОИСКЕ ТОЧКИ ВХОДА';
              -- Выводит сообщение
              message('Простой MA-робот: В ПОИСКЕ ТОЧКИ ВХОДА');
           else
              -- Выводит сообщение
              message('Простой MA-робот: После 10-и попыток не удалось открыть сделку!!! Завершение скрипта!!!');
              -- Завершает выполнение скрипта
              Run = false;
           end;
        end;
     else         
        -- СОСТОЯНИЕ робота 'В ПОИСКЕ ТОЧКИ ВХОДА'
        -- Если на этой свече еще не было открыто позиций
        if DS:Size() > LastOpenBarIndex then 
           -- Если быстрая пересекла медленную СНИЗУ ВВЕРХ
           if FastMA(DS:Size()-1) <= SlowMA(DS:Size()-1) and FastMA() > SlowMA() then
              -- Задает направление НА ПОКУПКУ
              CurrentDirect = 'BUY';
              message('CurrentDirect = "BUY"');
              -- Меняет СОСТОЯНИЕ робота на "В ПРОЦЕССЕ СДЕЛКИ"
              ROBOT_STATE = 'В ПРОЦЕССЕ СДЕЛКИ';
           -- Если быстрая пересекла медленную СВЕРХУ ВНИЗ
           elseif FastMA(DS:Size()-1) >= SlowMA(DS:Size()-1) and FastMA() < SlowMA() then
              -- Если по данному инструменту не запрещены операции шорт
              if not SEC_NO_SHORT then
                 -- Задает направление НА ПРОДАЖУ
                 CurrentDirect = 'SELL';
                 message('CurrentDirect = "SELL"');
                 -- Меняет СОСТОЯНИЕ робота на "В ПРОЦЕССЕ СДЕЛКИ"
                 ROBOT_STATE = 'В ПРОЦЕССЕ СДЕЛКИ';
              end;
           end;
        end;
     end;
     sleep(10);--Пауза 10 мс, для того, чтобы не перегружать процессор компьютера
  end;
end;

-- Функция вызывается терминалом QUIK при получении ответа на транзакцию пользователя
function OnTransReply(trans_reply)
  -- Если поступила информация по текущей транзакции
  if trans_reply.trans_id == trans_id then
     -- Передает статус в глобальную переменную
     trans_Status = trans_reply.status;
     -- Передает сообщение в глобальную переменную
     trans_result_msg  = trans_reply.result_msg;
  end;
end;

-- Функция ВЫЗЫВАЕТСЯ ТЕРМИНАЛОМ QUIK при остановке скрипта
function OnStop()
  Run = false;
end;

-----------------------------
-- ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ --
-----------------------------

-- Совершает СДЕЛКУ указанного типа (Type) ["BUY", или "SELL"] по рыночной(текущей) цене размером в 1 лот,
--- возвращает цену открытой сделки, либо FALSE, если невозможно открыть сделку
function Trade(Type)
  --Получает ID транзакции
  trans_id = trans_id + 1;

  local Price = 0;
  local Operation = ;
  --Устанавливает цену и операцию, в зависимости от типа сделки и от класса инструмента
  if Type == 'BUY' then
     if CLASS_CODE ~= 'QJSIM' and CLASS_CODE ~= 'TQBR' then Price = getParamEx(CLASS_CODE, SEC_CODE, 'offer').param_value + 10*SEC_PRICE_STEP;end; -- по цене, завышенной на 
10 мин. шагов цены
     Operation = 'B';
  else
     if CLASS_CODE ~= 'QJSIM' and CLASS_CODE ~= 'TQBR' then Price = getParamEx(CLASS_CODE, SEC_CODE, 'bid').param_value - 10*SEC_PRICE_STEP;end; -- по цене, заниженной на 10 
мин. шагов цены
     Operation = 'S';
  end;
  -- Заполняет структуру для отправки транзакции
  local Transaction={
     ['TRANS_ID']   = tostring(trans_id),
     ['ACTION']     = 'NEW_ORDER',
     ['CLASSCODE']  = CLASS_CODE,
     ['SECCODE']    = SEC_CODE,
     ['OPERATION']  = Operation, -- операция ("B" - buy, или "S" - sell)
     ['TYPE']       = 'M', -- по рынку (MARKET)
     ['QUANTITY']   = '1', -- количество
     ['ACCOUNT']    = ACCOUNT,
     ['PRICE']      = tostring(Price),
     ['CLIENT_CODE']= 'Простой MA-робот'
  }
  -- Отправляет транзакцию
  sendTransaction(Transaction);
  -- Ждет, пока получит статус текущей транзакции (переменные "trans_Status" и "trans_result_msg" заполняются в функции OnTransReply())
  while Run and trans_Status == nil do sleep(1); end;
  -- Запоминает значение
  local Status = trans_Status;
  -- Очищает глобальную переменную
  trans_Status = nil;
  -- Если транзакция не выполнена по какой-то причине
  if Status ~= 3 then
     -- Если данный инструмент запрещен для операции шорт
     if Status == 6 then
        -- Выводит сообщение
        message('Простой MA-робот: Данный инструмент запрещен для операции шорт!');
        SEC_NO_SHORT = true;
     else
        -- Выводит сообщение с ошибкой
        message('Простой MA-робот: Транзакция не прошла!\nОШИБКА: '..trans_result_msg);
     end;
     -- Возвращает FALSE
     return false;
  else --Транзакция отправлена
     local OrderNum = nil;
     --ЖДЕТ пока ЗАЯВКА на ОТКРЫТИЕ сделки будет ИСПОЛНЕНА полностью
     --Запоминает время начала в секундах
     local BeginTime = os.time();
     while Run and OrderNum == nil do
        --Перебирает ТАБЛИЦУ ЗАЯВОК
        for i=0,getNumberOf('orders')-1 do
           local order = getItem('orders', i);
           --Если заявка по отправленной транзакции ИСПОЛНЕНА ПОЛНОСТЬЮ
           if order.trans_id == trans_id and order.balance == 0 then
              --Запоминает номер заявки
              OrderNum  = order.order_num;
              --Прерывает цикл FOR
              break;
           end;
        end;
        --Если прошло 10 секунд, а заявка не исполнена, значит произошла ошибка
        if os.time() - BeginTime > 9 then
           -- Выводит сообщение с ошибкой
           message('Простой MA-робот: Прошло 10 секунд, а заявка не исполнена, значит произошла ошибка');
           -- Возвращает FALSE
           return false;
        end;
        sleep(10); -- Пауза 10 мс, чтобы не перегружать процессор компьютера
     end;

     --ЖДЕТ пока СДЕЛКА ОТКРЫТИЯ позиции будет СОВЕРШЕНА
     --Запоминает время начала в секундах
     BeginTime = os.time();
     while Run do
        --Перебирает ТАБЛИЦУ СДЕЛОК
        for i=0,getNumberOf('trades')-1 do
           local trade = getItem('trades', i);
           --Если сделка по текущей заявке
           if trade.order_num == OrderNum then
              --Возвращает фАКТИЧЕСКУЮ ЦЕНУ открытой сделки
              return trade.price;
           end;
        end;
        --Если прошло 10 секунд, а сделка не совершена, значит на демо-счете произошла ошибка
        if os.time() - BeginTime > 9 then
           -- Выводит сообщение с ошибкой
           message('Простой MA-робот: Прошло 10 секунд, а сделка не совершена, значит на демо-счете произошла ошибка');
           -- Возвращает FALSE
           return false;
        end;
        sleep(10); -- Пауза 10 мс, чтобы не перегружать процессор компьютера
     end;
  end;
end;

-- ПРИНУДИТЕЛЬНО ЗАКРЫВАЕТ ОТКРЫТУЮ ПОЗИЦИЮ переданного типа (Type) ["BUY", или "SELL"]
function KillPos(Type)
  -- Дается 10 попыток
  local Count = 0; -- Счетчик попыток
  if Type == 'BUY' then
     -- Пока скрипт не остановлен и позиция не закрыта
     while Run and not Trade('SELL') do -- Открывает SELL, тем самым закрывая BUY, если Trade('SELL') вернет TRUE, цикл прекратится
        Count = Count + 1; -- Увеличивает счетчик
        -- Если за 10 попыток не удалось закрыть позицию
        if Count == 10 then
           -- Возвращает NIL
           return nil;
        end;
        sleep(100); -- Пауза 100 мс, чтобы изменилась ситуация на сервере
     end;
  else
     -- Пока скрипт не остановлен и позиция не закрыта
     while Run and not Trade('BUY') do -- Открывает BUY, тем самым закрывая SELL, если Trade('BUY') вернет TRUE, цикл прекратится
        Count = Count + 1; -- Увеличивает счетчик
        -- Если за 10 попыток не удалось закрыть позицию
        if Count == 10 then
           -- Возвращает NIL
           return nil;
        end;
        sleep(100); -- Пауза 100 мс, чтобы изменилась ситуация на сервере
     end;
  end;
  -- Возвращает TRUE, если удалось принудительно закрыть позицию
  return true;
end;

-- Выставляет СТОП-ЛОСС и ТЭЙК-ПРОФИТ, ЖДЕТ пока он сработает, принимает ЦЕНУ (Price) и ТИП (Type) ["BUY", или "SELL"] открытой сделки,
--- возвращает FALSE, если не удалось выставить СТОП-ЛОСС и ТЭЙК-ПРОФИТ, либо TRUE, если сделка закрылась,
--- либо NIL, если при ошибке за 10 попыток не удалось принудительно закрыть позицию
function SL_TP(Price, Type)
  -- ID транзакции
  trans_id = trans_id + 1;

-- Находит направление для заявки local operation = ""; local price = "0"; -- Цена, по которой выставится заявка при срабатывании Стоп-Лосса (для рыночной заявки по акциям должна быть 0) local stopprice = ""; -- Цена Тэйк-Профита local stopprice2 = ""; -- Цена Стоп-Лосса

  local market = "YES"; -- После срабатывания Тэйка, или Стопа, заявка сработает по рыночной цене

-- Если открыт BUY, то направление стоп-лосса и тэйк-профита SELL, иначе направление стоп-лосса и тэйк-профита BUY if Type == 'BUY' then operation = "S"; -- Тэйк-профит и Стоп-лосс на продажу(чтобы закрыть BUY, нужно открыть SELL)

     -- Если не акции
     if CLASS_CODE ~= 'QJSIM' and CLASS_CODE ~= 'TQBR' then
        price = tostring(math.floor(getParamEx(CLASS_CODE, SEC_CODE, 'PRICEMIN').param_value)); -- Цена выставляемой заявки после страбатывания Стопа минимально возможная, 
чтобы не проскользнуло
        market = "NO";  -- После срабатывания Тэйка, или Стопа, заявка сработает НЕ по рыночной цене
     end;

stopprice = tostring(Price + TAKE_PROFIT*SEC_PRICE_STEP); -- Уровень цены, когда активируется Тэйк-профит stopprice2 = tostring(Price - STOP_LOSS*SEC_PRICE_STEP); -- Уровень цены, когда активируется Стоп-лосс else -- открыт SELL operation = "B"; -- Тэйк-профит и Стоп-лосс на покупку(чтобы закрыть SELL, нужно открыть BUY)

     -- Если не акции

if CLASS_CODE ~= 'QJSIM' and CLASS_CODE ~= 'TQBR' then

        price = tostring(math.floor(getParamEx(CLASS_CODE, SEC_CODE, 'PRICEMAX').param_value)); -- Цена выставляемой заявки после страбатывания Стопа максимально возможная, 
чтобы не проскользнуло
        market = "NO";  -- После срабатывания Тэйка, или Стопа, заявка сработает НЕ по рыночной цене
     end;

stopprice = tostring(Price - TAKE_PROFIT*SEC_PRICE_STEP); -- Уровень цены, когда активируется Тэйк-профит stopprice2 = tostring(Price + STOP_LOSS*SEC_PRICE_STEP); -- Уровень цены, когда активируется Стоп-лосс end; -- Заполняет структуру для отправки транзакции на Стоп-лосс и Тэйк-профит local Transaction = { ["ACTION"] = "NEW_STOP_ORDER", -- Тип заявки ["TRANS_ID"] = tostring(trans_id), ["CLASSCODE"] = CLASS_CODE, ["SECCODE"] = SEC_CODE, ["ACCOUNT"] = ACCOUNT, ["OPERATION"] = operation, -- Операция ("B" - покупка(BUY), "S" - продажа(SELL)) ["QUANTITY"] = "1", -- Количество в лотах ["PRICE"] = price, -- Цена, по которой выставится заявка при срабатывании Стоп-Лосса (для рыночной заявки по акциям должна быть 0) ["STOPPRICE"] = stopprice, -- Цена Тэйк-Профита ["STOP_ORDER_KIND"] = "TAKE_PROFIT_AND_STOP_LIMIT_ORDER", -- Тип стоп-заявки ["EXPIRY_DATE"] = "TODAY", -- Срок действия стоп-заявки ("GTC" – до отмены,"TODAY" - до окончания текущей торговой сессии, Дата в формате "ГГММДД")

     -- "OFFSET" - (ОТСТУП)Если цена достигла Тэйк-профита и идет дальше в прибыль,
     -- то Тэйк-профит сработает только когда цена вернется минимум на 2 шага цены назад,
     -- это может потенциально увеличить прибыль

["OFFSET"] = tostring(2*SEC_PRICE_STEP), ["OFFSET_UNITS"] = "PRICE_UNITS", -- Единицы измерения отступа ("PRICE_UNITS" - шаг цены, или "PERCENTS" - проценты)

     -- "SPREAD" - Когда сработает Тэйк-профит, выставится заявка по цене хуже текущей на 100 шагов цены,
     -- которая АВТОМАТИЧЕСКИ УДОВЛЕТВОРИТСЯ ПО ТЕКУЩЕЙ ЛУЧШЕЙ ЦЕНЕ,
     -- но то, что цена значительно хуже, спасет от проскальзывания,
     -- иначе, сделка может просто не закрыться (заявка на закрытие будет выставлена, но цена к тому времени ее уже проскочит)

["SPREAD"] = tostring(100*SEC_PRICE_STEP), ["SPREAD_UNITS"] = "PRICE_UNITS", -- Единицы измерения защитного спрэда ("PRICE_UNITS" - шаг цены, или "PERCENTS" - проценты)

     -- "MARKET_TAKE_PROFIT" = ("YES", или "NO") должна ли выставится заявка по рыночной цене при срабатывании Тэйк-Профита.
     -- Для рынка FORTS рыночные заявки, как правило, запрещены,
     -- для лимитированной заявки на FORTS нужно указывать заведомо худшую цену, чтобы она сработала сразу же, как рыночная

["MARKET_TAKE_PROFIT"] = market, ["STOPPRICE2"] = stopprice2, -- Цена Стоп-Лосса ["IS_ACTIVE_IN_TIME"] = "NO",

     -- "MARKET_TAKE_PROFIT" = ("YES", или "NO") должна ли выставится заявка по рыночной цене при срабатывании Стоп-Лосса.
     -- Для рынка FORTS рыночные заявки, как правило, запрещены,
     -- для лимитированной заявки на FORTS нужно указывать заведомо худшую цену, чтобы она сработала сразу же, как рыночная

["MARKET_STOP_LIMIT"] = market, ["CLIENT_CODE"] = "Простой MA-робот ТЭЙК-ПРОФИТ и СТОП-ЛОСС" }

  -- Отправляет транзакцию на установку ТЭЙК-ПРОФИТ и СТОП-ЛОСС

sendTransaction(Transaction);

  -- Ждет, пока не получит статус текущей транзакции (переменные "trans_Status" и "trans_result_msg" заполняются в функции OnTransReply())
  while Run and trans_Status == nil do sleep(10); end;
  -- Запоминает значение
  local Status = trans_Status;
  -- Очищает глобальную переменную
  trans_Status = nil;
  -- Если транзакция не выполнена по какой-то причине
  if Status ~= 3 then
     -- Выводит сообщение с ошибкой
     message('Простой MA-робот: Установка ТЭЙК-ПРОФИТ и СТОП-ЛОСС не удалась!\nОШИБКА: '..trans_result_msg);
     -- Возвращает FALSE
     return false;
  else
     -- Выводит сообщение
     message('Простой MA-робот: ВЫСТАВЛЕНА заявка ТЭЙК-ПРОФИТ и СТОП-ЛОСС: '..trans_id);
     local OrderNum_CLOSE = nil;
     -- ЖДЕТ пока СТОП-ЗАЯВКА на СТОП-ЛОСС и ТЭЙК-ПРОФИТ будет ИСПОЛНЕНА полностью
     while Run and OrderNum_CLOSE == nil do
        -- Перебирает ТАБЛИЦУ СТОП-ЗАЯВОК
        for i=0,getNumberOf("stop_orders")-1 do
           local stop_order = getItem("stop_orders", i);
           -- Если заявка по текущей транзакции СТОП-ЛОСС и ТЭЙК-ПРОФИТ
           if stop_order.trans_id == trans_id then
              -- Если заявка по отправленной СТОП-ЛОСС и ТЭЙК-ПРОФИТ транзакции ИСПОЛНЕНА ПОЛНОСТЬЮ
              if stop_order.balance == 0 then
                 -- Если по наступлению стоп-цены выставлена заявка
                 if stop_order.linkedorder > 0 then
                    -- Запоминает номер заявки, которая была создана при срабатывании СТОП-ЛОСС, или ТЭЙК-ПРОФИТ
                    OrderNum_CLOSE  = stop_order.linkedorder;
                    -- Прерывает цикл FOR
                    break;
                 -- Стоп-заявка сработала, но была отвергнута торговой системой
                 elseif CheckBit(stop_order.flags, 10) == 1 then
                    -- ПРИНУДИТЕЛЬНО ЗАКРЫВАЕТ ОТКРЫТУЮ ПОЗИЦИЮ и выходит из функции
                    return KillPos(Type);
                 end;
              end;
           end;
        end;
        sleep(10); -- Пауза 10 мс, чтобы не перегружать процессор компьютера
     end;
     --ЖДЕТ пока СДЕЛКА ЗАКРЫТИЯ позиции будет СОВЕРШЕНА
     --Запоминает время начала в секундах
     BeginTime = os.time();
     while Run do
        --Перебирает ТАБЛИЦУ СДЕЛОК
        for i=0,getNumberOf("trades")-1 do
           local trade = getItem("trades", i);
           --Если сделка по текущей заявке на СТОП-ЛОСС и ТЭЙК-ПРОФИТ
           if trade.order_num == OrderNum_CLOSE then
              -- Возвращает TRUE
              return true;
           end;
        end;
        --Если прошло 10 секунд, а сделка не совершена, значит на демо-счете произошла ошибка сервера "Обработка кросс-заявок блокирована"
        if os.time() - BeginTime > 9 then
           -- ПРИНУДИТЕЛЬНО ЗАКРЫВАЕТ ОТКРЫТУЮ ПОЗИЦИЮ и выходит из функции
           return KillPos(Type);
        end;
        sleep(1);
     end;
  end;
end;

-- Возвращает ЗНАЧЕНИЕ МЕДЛЕННОЙ скользящей по индексу свечи (по умолчанию: последняя)
function SlowMA(Index)
  -- Если индекс свечи не указан, то устанавливает индекс последней свечи
  if Index == nil then Index = DS:Size(); end;
  -- Сумма значений SLOW_MA_SOURCE на SLOW_MA_PERIOD свечах
  local Sum = 0;
  -- Перебирает последние SLOW_MA_PERIOD свечей
  for i=Index, Index - (SLOW_MA_PERIOD - 1), -1 do      
     -- Считает сумму, исходя из выбранного источника для медленной скользящей
     if SLOW_MA_SOURCE == 'O' then
        Sum = Sum + DS:O(i);
     elseif SLOW_MA_SOURCE == 'C' then
        Sum = Sum + DS:C(i);
     elseif SLOW_MA_SOURCE == 'H' then
        Sum = Sum + DS:H(i);
     elseif SLOW_MA_SOURCE == 'L' then
        Sum = Sum + DS:L(i);
     else
        message('Простой MA-робот:ОШИБКА! Не верно указан источник для медленной скользящей!');
        -- Останавливает скрипт
        OnStop();
     end;
  end;
  -- Возвращает значение
  return Sum/SLOW_MA_PERIOD;
end;
-- Возвращает ЗНАЧЕНИЕ БЫСТРОЙ скользящей по индексу свечи (по умолчанию: последняя)
function FastMA(Index)
  -- Если индекс свечи не указан, то устанавливает индекс последней свечи
  if Index == nil then Index = DS:Size(); end;
  -- Сумма значений FAST_MA_SOURCE на FAST_MA_PERIOD свечах
  local Sum = 0;
  -- Перебирает последние FAST_MA_PERIOD свечей
  for i=Index, Index - (FAST_MA_PERIOD - 1), -1 do
     -- Считает сумму, исходя из выбранного источника для быстрой скользящей
     if FAST_MA_SOURCE == 'O' then
        Sum = Sum + DS:O(i);
     elseif FAST_MA_SOURCE == 'C' then
        Sum = Sum + DS:C(i);
     elseif FAST_MA_SOURCE == 'H' then
        Sum = Sum + DS:H(i);
     elseif FAST_MA_SOURCE == 'L' then
        Sum = Sum + DS:L(i);
     else
        message('Простой MA-робот:ОШИБКА! Не верно указан источник для быстрой скользящей!');
        -- Останавливает скрипт
        OnStop();
     end;
  end;
  -- Возвращает значение
  return Sum/FAST_MA_PERIOD;
end;

-- Функция возвращает значение бита (число 0, или 1) под номером bit (начинаются с 0) в числе flags, если такого бита нет, возвращает nil
function CheckBit(flags, bit)
  -- Проверяет, что переданные аргументы являются числами
  if type(flags) ~= "number" then error("Предупреждение!!! Checkbit: 1-й аргумент не число!"); end;
  if type(bit) ~= "number" then error("Предупреждение!!! Checkbit: 2-й аргумент не число!"); end;
  local RevBitsStr  = ""; -- Перевернутое (задом наперед) строковое представление двоичного представления переданного десятичного числа (flags)
  local Fmod = 0; -- Остаток от деления
  local Go = true; -- Флаг работы цикла
  while Go do
     Fmod = math.fmod(flags, 2); -- Остаток от деления
     flags = math.floor(flags/2); -- Оставляет для следующей итерации цикла только целую часть от деления
     RevBitsStr = RevBitsStr ..tostring(Fmod); -- Добавляет справа остаток от деления
     if flags == 0 then Go = false; end; -- Если был последний бит, завершает цикл
  end;
  -- Возвращает значение бита
  local Result = RevBitsStr :sub(bit+1,bit+1);
  if Result == "0" then return 0;
  elseif Result == "1" then return 1;
  else return nil;
  end;
end;

См. также

Ссылки