Заметка

Qlua для чайников. Часть 4. Анализ информации из стакана и работа с заявками

  5  

Продолжаем тему прошлого урока. Мы начали писать робота.

Предыдущие статьи:

Qlua для чайников. Часть 1

Qlua для чайников. Часть 2. Циклы

Qlua для чайников. Часть 3. Работа со стаканом

Так что, теперь, если вы принимаетесь за написание программы, у вас уже не должно возникать вопроса: «С чего начать?», ибо на прошлом уроке мы этот вопрос прекрасно разобрали. Но может возникнуть следующий вопрос: «А как продолжить?». Вот научились мы работать со стаканом, написали запись стакана в файл (чисто ради тренировки), а дальше-то что? Как реального робота создать?

Вообще, чтобы подобные вопросы не возникали («Как начать?», «Как продолжить?», «Как закончить?») полезно иметь определенный план действий. Вот сейчас мы с вами и составим такой план. Для начала разобьем процесс написания робота по шагам (начиная с текущего состояния):

  1. Разработать механизм определения границ лучших цен, с учетом уже выставленных заявок. Для этой цели нам придется написать механизм поиска своих заявок.
  2. Разработать механизм выставления заявок, с учетом того факта, что заявки могут быть уже выставлены и могут быть исполнены.
  3. Разработать механизм перевыставления заявок при изменении цен.
  4. Разработать механизм удаления выставленных заявок и закрытия всех открытых позиций по рынку в заданное время.

Теперь приступим к реализации этого плана. Согласно первому пункту, нам нужна какая-то подпрограмма, которая бы искала выставленные заявки. Давайте для начала определимся, а так ли уж нам нужен поиск заявок. Дело в том, что в qlua есть предопределенная функция OnOrder, которая вызывается каждый раз, когда вводиться новая заявка или когда меняются параметры существующей заявки.  Давайте испытаем эту функцию. Добавим в нашего робота вот такой код:

function OnOrder(order)

      message(tostring(order["order_num"]),1)

end

Код внутри функции OnQuote можете временно убрать, чтобы не создавать на диске кучу файлов. Кстати, сразу расскажу, как код можно убрать временно. Один из способов – поставить перед каждой строкой кода значок “--”, который обозначает комментарий:

function OnQuote(class_code, sec_code)

      if class_code==p_classcode and sec_code==p_seccode then

            --l_file=io.open("D:\\1\\1\\"..tostring(count)..".txt", "w")

            --tb=getQuoteLevel2(class_code, sec_code)

           

            --l_file:write("BID:\n")

            --for i=1,tb.bid_count,1 do

            --    l_file:write(tostring(tb.bid[i].price)..";  "..

            --          tostring(tb.bid[i].quantity).."\n")

            --end

           

            --l_file:write("OFFER:\n")

            --for i=1,tb.offer_count,1 do

            --    l_file:write(tostring(tb.offer[i].price)..";  "..

            --          tostring(tb.offer[i].quantity).."\n")

            --end      

           

            --count=count+1

            --l_file:close()

      end

end

А можно просто поставить return 0:

function OnQuote(class_code, sec_code)

      if class_code==p_classcode and sec_code==p_seccode then

            return 0

Тогда программа дойдет до этой команды и вернется из функции, не выполняя код, лежащий после return 0.

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

 

Если мы эту заявку удалим, то получим сообщение еще раз. Все верно, OnOrder вызывается каждый раз, когда изменяется статус заявки. Удаление заявки меняет ее статус.

Но как узнать, что же мы делаем с заявкой? Вводим новую, удаляем или, может быть, событие OnOrder вызвано исполнением заявки?

На этот вопрос нам даст ответ свойство flags таблицы параметра, передаваемого в OnOrder. Измените функцию OnOrder следующим образом:

function OnOrder(order)

      message(tostring(order["order_num"].." flags="..order["flags"]),1)

end

Теперь вы будете видеть, что когда мы вводим заявку, у нас flags=25, а когда удаляем 26. Если у нас заявка исполняется, то flags=24.

Но на самом деле не нужно сравнивать flags с числом. Дело в том, что это битовые флаги. В компьютере все числа представлены в двоичном виде. В отличие от десятичной системы счисления, в двоичной предусмотрено только две цифры 0 или 1. Соответственно, каждый разряд отличается от соседнего не в 10 раз, а в 2 раза. Таким образом, число 10 в двоичной системе это 2 в десятичной, а 100 это 4. 101 в двоичной это будет 5 в десятичной. Что бы перевести число из десятичной системы в двоичную, надо делить его на 2 и записывать остаток от деления на каждой итерации. Начинаем с младшего разряда. Например, переведем число 25 в двоичную систему. Делим на 2, получаем 12, остаток 1. Делим еще на 2, получаем 6, остаток 0. Затем 3 и остаток 0. Потом 1 и остаток 1 и плюс еще остаток 1. Получим 11001. Каждый бит этого числа имеет определенное свое значение. Естественно, если 1 значит данный признак включен, 0 – выключен. Рассмотрим подробнее эти флаги:

  • бит 0 (0x1) Заявка активна, иначе – не активна
  • бит 1 (0x2) Заявка снята. Если флаг не установлен и значение бита «0» равно «0», то заявка исполнена
  • бит 2 (0x4) Заявка на продажу, иначе – на покупку. Данный флаг для сделок и сделок для исполнения определяет направление сделки (BUY/SELL)
  • бит 3 (0x8) Заявка лимитированная, иначе – рыночная
  • бит 4 (0x10) Возможно исполнение заявки несколькими сделками
  • бит 5 (0x20) Исполнить заявку немедленно или снять (FILL OR KILL)
  • бит 6 (0x40) Заявка маркет-мейкера. Для адресных заявок – заявка отправлена контрагенту
  • бит 7 (0x80) Для адресных заявок – заявка получена от контрагента
  • бит 8 (0x100) Снять остаток
  • бит 9 (0x200) Айсберг-заявка

Число 11001 означает, что включены следующие флаги:

  • Заявка активна.
  • Заявка лимитированная.
  • Возможно исполнение заявки несколькими сделками.

При снятии заявки у нас flags=26, что соответствует двоичному числу 11010. В этом случае у нас флаг «Заявка активна» равен нулю, а вот флаг «заявка снята» на этот раз равен единице.  

Но, согласитесь, так работать с битовыми флагами неудобно. Поэтому в qlua предусмотрены специальные средства для работы с битовыми флагами. А именно, логические функции. С помощью таких функций, например, можно проверить, установлен или брошен отдельный конкретный флаг. Например, проверить флаг «заявка снята» можно следующим образом:

      if bit.band(order["flags"],2)>0 then

            message("Заявка удалена",1)

      end

Этот код может показаться странным и непонятным, поэтому, придется изучить эти самые логические функции. Всего их четыре:

  • Логическое сложение – функция ИЛИ (OR).
  • Логическое умножение – функция И (AND).
  • Логическое отрицание – функция НЕ (NOT)
  • Исключающее ИЛИ (XOR).

Разберем эти функции подробнее. Функция «ИЛИ» дает 1, если хотя бы один из ее входных битов равен 1, и дает 0, если все нули. Функция «И» дает единицу, если на всех ее входах единица, если хотя бы на одном 0 — то дает 0. Функция «НЕ» превращает 0 в 1 и 1 в 0. Исключающее или дает 0 если все входы одинаковы – только одни единицы или только одни нули. Если есть различия, то на выходе единица.

Таким образом, если мы применим операцию «И» к двоичным числам 1010 и 1100 то на выходе получим  1000, так как у нас только в старшем разряде на обоих входах единицы.

Теперь поговорим о том, как же проверить конкретный бит при помощи этих логических функций. Очень просто. Надо совершить операцию «И» между набором битовых флагов и числом, которое содержит единицу только в проверяемом разряде. В нашем примере bit.band – это как раз функция «И», первый ее аргумент – битовые флаги, второй число 2, которое соответствует в двоичном коде числу 10, содержащему единицу только во втором разряде, который соответствует биту флага «Заявка снята». Понятно, что если этот бит включен – то результат функции будет какое то число, больше чем 0, в противном случае результат будет равен нулю, ибо тогда обнулятся все разряды.

Теперь вроде бы мы пришли к выводу, что поиск заявок нам и не нужен. Но как быть в том случае, если мы робота остановили, потом снова запустили? Например, у нас завис компьютер, мы были вынуждены его перезагрузить и заново запустить квик. Даже если получив номера заявок через OnOrder мы сохраним их в памяти, эти данные при закрытии и повторном запуске робота потеряются.  Как быть? Можно сохранять их в текстовом файле. Даже если файлик потеряется при аварийном завершении работы робота, мы всегда можем набрать его «ручками». Есть и другое решение. Например, можно прочитать таблицу заявок. Но тут возникают еще и другие вопросы. А что, если мы еще и вручную торгуем? Или у нас торгует несколько роботов, и каждый создает заявки. Как найти свои заявки?

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

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

В начале программы объявим переменные, в которых у нас будут запоминаться сведения о введенных заявках. Разумеется, не в самом начале, не там где у нас параметры (переменные на букву p), а ниже, под ними:

buy_order="";

buy_price=0;

buy_count=0;

sell_order=""

sell_price=0;

sell_count=0;

Тут мы будем запоминать номер заявки, цену, и остаток заявки (баланс). Под остатком понимается количество инструмента, по которому еще не совершена сделка. То есть, если вы выставляете лимитированную заявку, которая исполняется не сразу, то начальное значение остатка – это количество из заявки. По мере исполнения (когда заявка исполняется частично), остаток будет уменьшатся, когда исполниться полностью станет 0. Для того чтобы получить такой остаток, нужно обратиться к атрибуту заявки balance. Не путать с атрибутом qty!

Вот каким образом мы будем получать эти данные:

function OnOrder(order)

      p_file:write(os.date().." OnOrder\n");

      --сначала проверим, по нашему ли инструменту эта заявка

      if order["sec_code"]==p_seccode and order["class_code"]==p_classcode then

            p_file:write(os.date().."  заявка "..order["order_num"].."\n")

     

            --если заявка активна, то запоминаем ее

            if bit.band(order["flags"],1)>0 then

                  if bit.band(order["flags"],4)>0 then

                        sell_order=order["order_num"]

                        sell_price=tonumber(order["price"])

                        sell_count=tonumber(order["balance"])

                  else

                        buy_order=order["order_num"]

                        buy_price=tonumber(order["price"])

                        buy_count=tonumber(order["balance"])

                  end

            else

                  --если заявка не активна то сбрасываем информацию о заявке

                  if bit.band(order["flags"],1)>0 then

                        if bit.band(order["flags"],8)>0 then

                             sell_order=""

                             sell_price=0

                             sell_count=0

                        else

                             buy_order=""

                             buy_price=0

                             buy_count=0

                        end

                  end

            end

      end

end

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

У вас может возникнуть вопрос: а для чего в программе используется tonumber? Дело в том, что в переменной order, куда функция OnOrder  передает  структуру с параметрами заявки, поля имеют строковый тип. А строка – это не число. Но нам нужно число, потому что далее мы с этими данными будем проводить различные числовые операции, в частности, сравнение. Почему строка? Так решили разработчики, нам остается с этим смириться и просто преобразовывать к нужному нам типу.

Идем дальше. Получив данные о заявке, нам нужно учесть их при расчете крайних цен в стакане. Этой подпрограммы у нас пока нет, мы реализуем ее в функции OnQuote вместо того кода, который выводил у нас стакан, генерируя кучу файлов:

function OnQuote(class_code, sec_code)

      if class_code==p_classcode and sec_code==p_seccode then

            tb=getQuoteLevel2(class_code, sec_code)

           

            if buy_count==0 then

                  v_bid=tb.bid[math.ceil(tb.bid_count)].price+p_delta

            else

                  if tonumber(tb.bid[math.ceil(tb.bid_count)].quantity)==buy_count and tonumber(tb.bid[math.ceil(tb.bid_count)].price)==buy_price then

                        v_bid=tb.bid[math.ceil(tb.bid_count)-1].price+p_delta

                  else

                        v_bid=tb.bid[math.ceil(tb.bid_count)].price+p_delta

                  end

            end

           

            if sell_count==0 then

                  v_offer=tb.offer[1].price-p_delta

            else

                  if tonumber(tb.offer[1].quantity)==sell_count and tonumber(tb.offer[1].price)==sell_price then

                        v_offer=tb.offer[2].price-p_delta

                  else

                        v_offer=tb.offer[1].price-p_delta

                  end

            end

           

            p_file:write(os.date().."    "..v_bid..","..v_offer.."\n")

      end

end

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

Так же у вас может возникнуть вопрос по math.ceil. Дело в том, что tb.bid_count представляет собой не целое число, а дробное, хотя и округленного до целого. В общем, просто пока запомните, что в программировании 10 и 10.0 не одно и то же, хотя они и равны. В поле tb.bid_count как раз содержится число, подобное второму, и при попытке обратится вот так tb.bid[(tb.bid_count] вылазит сообщение об ошибке. Функция math.ceil как раз преобразует число к первому виду.

В нашем примере OnQuote так же выводит лог, что позволит нам проверить, как работает программа. По этому логу мы можем посмотреть, какие были расчетные цены до ввода заявки, после ввода и после ее отмены. И, кстати, сразу же обнаруживаем недочет:

09/16/14 17:02:59    315.3,316.08

09/16/14 17:03:01    315.3,316.07

09/16/14 17:03:02    316.01(!!!!),316.07

09/16/14 17:03:02 OnOrder

09/16/14 17:03:02  заявка 5507321

09/16/14 17:03:02 OnOrder

09/16/14 17:03:02  заявка 5507321

09/16/14 17:03:02    315.3,316.07

09/16/14 17:03:02    315.46,316.07

             09/16/14 17:03:03    315.46,316.07

Как видим, перед вызовом функции OnOrder у нас крайнее значение неверное (рассчитанное от нашей заявки), но потом оно восстанавливается. Это связано с тем, что сначала приходит событие OnQuote, а уже потом OnOrder. При приходе OnQuote у нас уже есть заявка в стакане, но программа еще не знает, что это наша заявка, она узнает чуть позже, когда придет OnOrder. Другой недочет – при включении программа информация о рассчитанных ценах появляется не сразу, а лишь тогда, когда в стакане что-то измениться. Если меняется редко, то программа может долго не работать при включении, и заработать только спустя некоторое время (когда придет первое изменение стакана). Как исправить эти недочеты я расскажу в следующих уроках. А пока, думаю, информации достаточно.

Полный текст последнего примера приведен в приложении.

Прикрепленные файлы

·   Приложение к уроку 4.rar

Комментарии

asas55555 — 4 октября 2014 г.

Где приложение , не подскажите?

1 +

orekton — 6 октября 2014 г.

asas55555, приложение добавил.

0 +

thejobber — 18 октября 2014 г.

orekton, а если нет номера телефона который начинается на +7 )) как пример скачать?

0 +

orekton — 19 октября 2014 г.

thejobber, открою вам доступ в понедельник

0 +

Nemo_2000 — 7 октября 2014 г.

Есть ли какой-нибудь простенький примерчик с анализом волатильности и торговлей на пробой? На форексе это иногда с mql удавалось ) и можно играть в пробой или откат без соревнований с hft, чего не избежать при использовании вашего примера..

0 +

megabax — 7 октября 2014 г.

Nemo_2000, Не, к сожалению, такого примерчика нет. Если будет - напишу.

0 +

finstrateg — 12 октября 2014 г.

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

0 +

megabax — 13 октября 2014 г.

finstrateg, Вскоре тема с роботом спредером будет закончена. Тогда начнется рассмотрение других тем. Ваше пожелание учту.

0 +

asas55555 — 20 октября 2014 г.

почему к бидам мы применяем math.ceil, а к офферам нет, или это издержки письма?

0 +

megabax — 21 октября 2014 г.

asas55555, math.ceil - это округление, так как иногда без этого округления глючит. В случае с офферами мы обращаемся к первому элементу массива, а в случае с бидами к элементу, которое в поле count, а там тип дробный (типа 10.00000), из за этого и глюк: не хочет quik воспринимать такие числа как целые, приходиться округлять.

0 +

asas55555 — 21 октября 2014 г.

megabax,а если так
v_bid = tonumber(tb.bid[tonumber(tb.bid_count)].price)

0 +

megabax — 22 октября 2014 г.

asas55555, Кажется, я так пробовал. НЕ прокатило. Если у вас прокатит - пожалуйста, делайте так.

0 +

robotqlua — 28 октября 2014 г.

Добрый день!
Просьба открыть приложения к урокам с телефонами без +7.
Спасибо.

0 +

finstrateg — 9 ноября 2014 г.

Почему в каких-то случаях точка с запятой ставится в конце строки а в каких-то нет?
Например в файле:
is_run=true
count=1
buy_order="";
buy_price=0;
buy_count=0;
sell_order=""
sell_price=0;
sell_count=0;
Ну и в других местах...

0 +

megabax — 10 ноября 2014 г.

finstrateg, Точка с запятой в qlua не обязательна. Но если она есть - это тоже НЕ считается ошибкой. Я часто программировал на тех языках, где точка с запятой обязательна, и иногда ставлю ее машинально, по привычке.

1 +

Rich74 — 17 ноября 2014 г.

Добрый день, не понятен вот этот момент :
--если заявка активна, то запоминаем ее
if bit.band(order["flags"],1)>0 then
............................................................................
--если заявка не активна то сбрасываем информацию о заявке
if bit.band(order["flags"],1)>0 then
Почему условие в if одинаковое??
да и второе условие при сбросе не понятное:
if bit.band(order["flags"],8)>0 then
8 это же проверка на лимит/маркет?

0 +

Rich74 — 17 ноября 2014 г.

И еще я как то не совсем понимаю метод OnQuote, то есть мы смотрим есть ли у нас активная заявка на покупку, ели нет, то берем лучшую цену и добавляем дельту,
Если у нас есть заявка, и мы видим что лучшая цена в стакане по биду это наша, то есть объем и цена совпадают с теми что запомнил скрипт - что в таком случае делается то?
Зачем нам брать бид 2ой после лучшего и добавлять дельту? что вообще делает переменная v_bid?
Ну и конечно понятно что по аску будет все зеркально.

0 +

megabax — 19 ноября 2014 г.

Rich74, Допустим, у нас в стакане лучшие 1000 на 1100. Мы выставляем на дельту от этих цен. Допустим, у нас дельта 10, тогда наши цены 1010 и 1090. А что бы в следующий раз у нас не стало 1020 и 1080 нам приходиться учитывать свои выставленные заявки.

0 +

megabax — 19 ноября 2014 г.

Rich74, возможно, опечатка. Вместо if bit.band(order["flags"],8)>0 then надо поставить if bit.band(order["flags"],4)>0 then. Вообще, опечатки в remember_order исправлены в следующих уроках.

0 +

Rich74 — 20 ноября 2014 г.

megabax, Так разве не должно быть так?
-если заявка не активна то сбрасываем информацию о заявке
if bit.band(order["flags"],1)<0 then

у нас же если не активная заявка, то первый бит 0 равен

0 +

megabax — 21 ноября 2014 г.

Rich74, Нет, меньше нуля не будет. Никак не будет. Либо нуль либо больше нуля. Вообще, во второй ветке после else условие "if bit.band(order["flags"],1)>0 then" как раз лишнее. В следующих уроках я это исправил. Тоесть, ветвь после else как раз выполняется в том случае, когда заявка не активна. А вот этим условием "if bit.band(order["flags"],4)>0 then" мы проверяем, на покупку это заявка или на продажу. И исходя из этого сбрасываем соответствующие переменные.
Вот так выглядит функция в последнем варианте:
function remember_order(order)

p_file:write(os.date().." начало remember_order\n")

--если заявка активна, то запоминаем ее
if bit.band(order["flags"],1)>0 then
p_file:write(os.date().." remember_order: заявка активна\n")
p_file:write(os.date().." флаг:"..bit.band(order["flags"],4).."\n",1)
if bit.band(order["flags"],4)>0 then
p_file:write(os.date().." remember_order: заявка на продажу\n")
sell_order=order["order_num"]
sell_price=tonumber(order["price"])
sell_count=tonumber(order["balance"])
else
p_file:write(os.date().." remember_order: заявка на покупку\n")
buy_order=order["order_num"]
buy_price=tonumber(order["price"])
buy_count=tonumber(order["balance"])
p_file:write(os.date().." remember_order: buy_price="..buy_price.."\n")
end
else
--если заявка не активна то сбрасываем информацию о заявке
p_file:write(os.date().." remember_order: условие else\n")
p_file:write(os.date().." remember_order: проверка флага 1\n")
if bit.band(order["flags"],4)>0 then
p_file:write(os.date().." remember_order: прроверка флага 4\n")
sell_order=""
sell_price=0
sell_count=0
else
p_file:write(os.date().." remember_order: прроверка флага 4 else\n")
buy_order=""
buy_price=0
buy_count=0
end
end
end

1 +

crn05 — 21 февраля 2015 г.

megabax, понятно, что второй блок после else лишний, он сработает если первое условие if bit.band(order["flags"],1)>0 then - не сработает.
Только не понятно, почему в блоке после else при проверке активности/неактивности заявки опять идет то же самое условие if bit.band(order["flags"],1)>0 then?
Разве не должно быть if bit.band(order["flags"],1)=0 then (первый бит РАВЕН нулю)
т.к. бит 0 (0x1) Заявка активна, иначе – не активна.

Либо там ошибка, либо я не до конца понял работу с битовыми флагами.

0 +

crn05 — 21 февраля 2015 г.

crn05, все верно, сейчас все проверил, нужно писать if bit.band(order["flags"],1)==0
(и именно два знака равенства, т.к. это С подобный язык)

0 +

megabax — 16 апреля 2015 г.

crn05, Там ошибка, в приложениях к следующим урокам она исправлена.

0 +

crn05 — 21 февраля 2015 г.

Rich74, по идее должно быть if bit.band(order["flags"],1)=0
т.к. в булевских операциях только два состояния либо 1 (истина) либо 0 (ложь) .

0 +

edch — 28 ноября 2014 г.

Добрый день!
Пытался запустить в квике - выдает ошибку

Syntax error while compiling C:\Users\EDWARD\Desktop\цены стакана.lua: C:\Users\EDWARD\Desktop\цены стакана.lua:6: unexpected symbol near '–

0 +

megabax — 13 марта 2015 г.

edch, должно быть, вы как то некорректно текст робота скопировали. Вот этот вот текст "Qlua для чайников. Часть 4. Анализ информации из стакана и работа с заявками. Приложение." вы убрали?

0 +

drghestykmb — 15 апреля 2015 г.

почему не работает.
function robot()
local N1=getNumCandles("RIM5-1")
--local N2=getNumCandles("RSI-15-BRJ5")
local N=getNumCandles("RIM5-1")
t1,n1,i1=getCandlesByIndex("RIM5-1", 1, N1-3, 2)--("RSI-1", 0, N1-3, 2)
t2,n2,i2=getCandlesByIndex("RIM5-1", 0, N2-3, 2)--("RSI-2", 0, N2-3, 2)
t,n,i=getCandlesByIndex("RIM5-1", 0, N-1, 1)

--сигнал на продажу (первый мувинг пересекает втрой RSI-15-BRJ5 сверху вниз
if t1[1].close>t1[2].close and t1[2].close>t2[].close then--фильтр уровня and t1[2].close --if t1[0].close>t2[0].close and t1[1].close --if t1[0].close>t2[0].close and t1[1].close --if t1[1].close>p_sell_level_RSI --фильтр уровня
Trade("S",count+p_count,t[0].close-p_spread)
--end
end

--сигнал на покупку (первый мувинг RSI-5-BRJ5 пересекает второй снизу вверх
if t1[1].closet1[3].close
--if t1[0].close>t2[0].close and t1[1].close --if t1[1].close Trade("B",p_count-count,t[0].close+p_spread)
--end
end
end
смысл песни такой.реальн. 3и свечи и последняя какая танцует работает. а если 3 бывшие.сформировавшиеся не работает.цикл в цикле сосем не работает.удивительно.нужно отфильтровать.а отфильтровать могу ограниченно.

0 +

megabax — 16 апреля 2015 г.

drghestykmb, А что вообще то говорит? Приведенный вами код вообще какой то сумбурный

0 +

drghestykmb — 15 апреля 2015 г.

у меня не получается фильтровать более 3х and .почему

0 +

drghestykmb — 15 апреля 2015 г.

if (t1[0].close>t2[0].close and t2[1].close>t1[1].close and t1[1].close>p_sell_level_RSI) then
скобки и круглые.квадратные.и объединение.ничто кроме круглых.

0 +

megabax — 16 апреля 2015 г.

drghestykmb, Скобки круглые не надо, это не C

0 +

drghestykmb — 17 апреля 2015 г.

у меня не получается фильтровать более 3х and .почему.так и не ответили.
if low_level~=last_low_level then
if sell_stop_loss_num~="" then
delete_stop_loss(sell_stop_loss_num)
end
end
допустим для чайника.
if low_level~=last_low_level and sell_stop_loss_num~="" then
delete_stop_loss(sell_stop_loss_num)
end
какая разница?
в том случае не работает.к примеру нет данных .входа в цикл.или что?

0 +

ardeo — 26 октября 2015 г.

... Дело в том, что tb.bid_count представляет собой не целое число, а дробное, хотя и округленного до целого ...

Параметр Тип Описание
bid_count STRING Количество котировок покупки

0 +

mad_hound — 6 июля 2017 г.

4-й урок подряд... вот... мозг погиб))) робот живёт.))))

0 +

Написать комментарий

Чтобы написать комментарий, необходимо авторизоваться.

Написать администратору