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

Время с момента генерации сигнала о покупки/продажи до фактической покупки/продажи... Какое у Вас ?
Евгений Reply:
мая 4, 2009 at 11:18
От одной секунды — зависит от:
1. Качество интернета
2. Присутсвия/отсутствия тормозов на сервере торговой системы.
Здраствуйте, Евгений.
Нарисал функцию : «Стоп-лимит по исполнению заявки».
При исполнении функции на демо-счете, стоп-заявка по исполнению базовой заявки не выставляется.Подскажите пожалуйста в чем проблема.Спасибо.
Привожу код:
FUNC STOPORDERLIMIT (BASEORDERKEY,STOPPRICE,PRICE)
new_global («trans», "") ' заводим две глобальные переменные'
new_global («transR», "«)
trans = „«
trans = set_value (trans, „TRANS_ID“, UID) ' тот самый ИН транзакции'
trans = set_value (trans, „ACTION“,»NEW_STOP_ORDER“) ' тип заявки'
trans = set_value (trans, «STOP_ORDER_KIND»,»ACTIVATED_BY_ORDER_SIMPLE_STOP_ORDER")'тип стоп-ордера: стоп-лимит по исполнению заявки'
trans = set_value (trans, «BASE_ORDER_KEY»,BASEORDERKEY)'Регистрационный номер заявки-условия'
trans = set_value (trans, «ACTIVATE_IF_BASE_ORDER_PARTLY_FILLED»,"yes")'активизировать если базовый ордер исполнится'
trans = set_value (trans, «USE_BASE_ORDER_BALANCE»,"yes")'использовать значение «остатка» базового ордера'
trans = set_value (trans, «STOPPRICE», STOPPRICE)
trans = set_value (trans, «PRICE»,PRICE)
trans = set_value (trans, «CLASSCODE»,TagID) ' код класса'
trans = set_value (trans, «SECCODE», InstrumentID) ' инструмент'
trans = set_value (trans, «ACCOUNT», AccountID) ' здесь прописываем свой аккаунт'
trans = set_value (trans, «OPERATION»,"S") ' направление'
trans = set_value (trans, «CLIENT_CODE»,ClientID) ' здесь свой код клиента'
transR = SEND_TRANSACTION (30, trans) ' отправляем заявку в систему'
message («transR =»&transR,1)
END FUNC
Евгений Reply:
мая 4, 2009 at 12:17
Может быть много причин. Например как у вас генерируется номер транзакции? Если он не уникальный — транзакция не пройдет. Затем, как передается номер базовой заявки? Более точнее скажу вечером — дома посмотрю.
После того, как я выставляю заяку, я хочу узнать ее номер:
ORDER (PRICE+10, LOTS, «B», LIMIT, TrID ) ' ВЫСТАВЛЯЕМ ЗАЯВКУ НА ПОКУПКУ
KOLZ=GET_NUMBER_OF («ORDERS»)+0 смотрю кол-во заявок
NUMBER=GET_VALUE (GET_ITEM («ORDERS», KOLZ),"NUMBER") — получаем номер последней заявки
так вот проблема в том, что в таблице заявок заявка есть, а KOLZ — на единицу меньше. Пробовал сделать KOLZ+1, но тогда NUMBER="". (из отладчика). сделать небольшую задержку между выставлением заявки и запросом номера тоже не помогает. Не встречалась ли вам такая проблема, может быть подскажете как ее решить?
Dr. Livsey Reply:
мая 18, 2009 at 21:39
забыл сказать — цена выставляется заведомо исполнимая
Евгений Reply:
мая 18, 2009 at 22:22
Дело в том, что первая строка в кивке всегда «нулевая». т.е. количество строк GET_NUMBER_OF («ORDERS») может быть десять, но на самом деле последняя запись девятая. Так что надо отнимать единицу, а не прибавлять.
Но правильней всего, получать номер выставленной заявки в процессе ее выставления, почитайте руководство по языку QPILE (есть в разделе скачать) о функции SEND_TRANSACTION, при исполнении которой образуется массив с нужным вам ORDER_NUMBER — это как раз и есть номер заявки. И не надо ломать голову.
Dr. Livsey Reply:
мая 19, 2009 at 23:51
Проблема была в том, перед началом расчета Quik делает слепок таблиц. И все что в них попадает по ходу будет доступно только со следующей итерации.
З.Ы. вдруг кому пригодится))))
Евгений Reply:
мая 20, 2009 at 9:19
Правильно. Я однажды долго помучился, почему текущая чистая позиция не меняется внутри цикла
а ларчик, получается, просто открывался))))) Спасибо
Евгений Reply:
мая 18, 2009 at 22:56
Не за что!
Добрый день. Название данной темы навеивает мысли о необходимости форума...
Добрый день, Евгений!
Не пойму в чем дело: переменная ISREALTIME регулярно меняет своё значение: 0 или 1.
Естественно, когда 0 робот не может выставлять заявки.
Собственно вопрос: когда параметр SSTATUS, используемый при определении значения переменной ISREALTIME, принимает значение 0 и статус чего он определяет?
SSTATUS после введения нескольких клиринговых сеансов некорректно работает. Я не стал с ним заморачиваться и ограничился контролем по функции IS_CONNECTED () опсмотрите в руководстве.
Значит удалю вовсе этот SSTATUS.
Кстати всем совет, который дался мне очень нелегко:
если у вас торговый счет (или то же счет-депо) имеет хоть одну строчную (маленькую) букву (а не все БОЛЬШИЕ), например: SPBFUT12a34, то заявки скорее всего выставляться не будут, так как QUIK автоматом исправит ее на большую и не сможет впоследствии прочитать правильно ваш аккаунт.
Лечением этой проблемы является добавление строчки USE_CASE_SENSITIVE_CONSTANTS; в шапку программы.
Пример:
PORTFOLIO_EX FORTS_ROBOT;
DESCRIPTION FORTS_ROBOT;
CLIENTS_LIST ALL_CLIENTS;
FIRMS_LIST ALL_FIRMS;
USE_CASE_SENSITIVE_CONSTANTS;
PROGRAM
Удачи!
Евгений, добрый день!
Помогите плз.
Я пытаюсь в начать с изучения каждой функции в отдельности. Для мне необходимо вывести результат в виде таблице, для наглядности, затем поиграть параметрам и посмотреть как измениться результат. Начал с массивов, но затем мой код деградировал до:
PORTFOLIO_EX VERSION;
DESCRIPTION Изучение синтаксиса;
CLIENTS_LIST ALL_CLIENTS;
FIRMS_LIST ALL_FIRMS;
program
a=1
end_program
PARAMETER a;
PARAMETER_TITLE Объем всех сделок;
PARAMETER_DESCRIPTION объем;
PARAMETER_TYPE NUMERIC (10,0);
END
END_PORTFOLIO_EX
При этом, как я понимаю, на экране должна появляться таблица со столбцом «объем всех сделок» и единственной ячейкой со значением 1.
Однако QUICK выдает следующую ошибку:
---------------------------
Ошибка
---------------------------
Неверный формат файла описания портфелей — Ошибка синтаксиса. Файл «E:\DOCUMENTS\Мои проекты\Биржа\Торговые роботы\1.0\version.qpl», строка 4.
---------------------------
Т.е. я не могу дойти даже до выполнения программы, хотя заголовок сделан по аналогии с Вашим.
Что делать?
Спасибо, за то что уделили время моему смешному), для всех user-ов сайта, вопросу!
Евгений Reply:
ноября 11, 2009 at 19:33
То что сразу бросается в глаза — у вас отсутствует блок вывода значения вашей переменной в таблицу. Вчера разбирали похожий простенький пример — посмотрите в конце ленты комментариев:
www.hirobot.ru/2009/03/zn...oy/#comment-1263
Евгений, подскажите пожалуйста, мой работ в режиме отладки ежесекундно получает последнюю цену по бумаге Роснефть. Однако, эта цена не отображается в моей таблице, т.е с каждым тиком в таблице появляется новая строка с порядковым номером строки и пустой ячейкой в графе цена.
Как сделать так, чтобы цена отображалась?
Код:
PORTFOLIO_EX STAKAN;
DESCRIPTION STAKAN;
CLIENTS_LIST ALL_CLIENTS;
FIRMS_LIST ALL_FIRMS;
USE_CASE_SENSITIVE_CONSTANTS;
PROGRAM
new_global («a»,0)
price=get_param («EQNL»,"ROSN","last")
a=a+1
add_item (a,price)
END_PROGRAM
PARAMETER price;
PARAMETER_TITLE price;
PARAMETER_DESCRIPTION price;
PARAMETER_TYPE numeric (10,20);
END
END_PORTFOLIO_EX
Результат:
price
1,000000 0,000000
2,000000 0,000000
3,000000 0,000000
4,000000 0,000000
5,000000 0,000000
6,000000 0,000000
7,000000 0,000000
Евгений Reply:
ноября 14, 2009 at 18:57
У вас ошибка в блоке создания таблицы. нужно так:
price=get_param («EQNL»,"ROSN","last")
создать коллекцию «МАП» и добавить в нее значение
OUTPUT=CREATE_MAP ()
OUTPUT=SET_VALUE (OUTPUT,"price",price)
a=a+1
Выводим строку
ADD_ITEM (a,OUTPUT)
Евгений помогите найти ошибку в коде.
я сделал стратегию на подобии пробойной но когда подключил к ней блок выставления заявок,Квик при получении сигнала стратегии ругается. Пишет — указаная транзакция по указаному адресу не найдена.
Вот код блока заявок:
DATETIME=GET_VALUE (GET_DATETIME (), «DATETIME»)
DATE=SUBSTR (DATETIME,6,4)&SUBSTR (DATETIME,3,2)&SUBSTR (DATETIME,0,2)
SHORTDATE=SUBSTR (DATETIME,4,1)&SUBSTR (DATETIME,0,2)
TIME=SUBSTR (DATETIME,11,2)&SUBSTR (DATETIME,14,2)&SUBSTR (DATETIME,17,2)+0
TRID=SHORTDATE&TIME ' ГЕНЕРИРУЕМ ИДЕНТИФИКАТОР ТРАНЗАКЦИЙ
OP="« ' ЗАДАЕМ ТЕКСТОВУЮ ПЕРЕМЕННУЮ ОПЕРАЦИИ
IF ISREALTIME=1 ' ЕСЛИ МОЖНО ТОРГОВАТЬ
IF TP=0 ' ЕСЛИ У НАС НЕТ ОТКРЫТЫХ ПОЗИЦИЙ
IF (MAXMAX=CURCLOSE) AND (ORDERCOUNT=0) AND (FLAGBUY=1) ' И Т.Д. ПО АЛГОРИТМУ УСЛОВИЙ
OP=»B" ' ОПЕРАЦИЯ ПОКУПКА
MESSAGE («MUST BUY FIRST»,1) ' QUIK ПИШЕТ СООБЩЕНИЕ В ОКНЕ
ORDER (FPRICE,LOTS,OP,"L",TRID)
FLAGBUY=0 ' ФЛАГ ЗАПРЕЩЕНИЯ ПОКУПКИ
FLAGSELL=1 ' ФЛАГ РАЗРЕШЕНИЯ НА ПРОДАЖУ
END IF
IF (MINMIN=CURCLOSE) AND (ORDERCOUNT=0) AND (FLAGSELL=1) ' АНАЛОГИЧНО ПРЕДЫДУЩЕМУ
OP="S"
MESSAGE («MUST SELL FIRST»,1)
ORDER (FPRICE,LOTS,OP,"L",TRID)
FLAGSELL=0
FLAGBUY=1
END IF
END IF
' REVERS
IF (TP0) AND (MINMIN=CURCLOSE) AND (ORDERCOUNT=0) AND (FLAGSELL=1) ' АНАЛОГИЧНО ПРЕДЫДУЩЕМУ
MESSAGE («MUST SELL REVERS»,1)
ORDER (FPRICE,LOTS,OP,"L",TRID)
LOTS=LOTS*2
OP="S"
FLAGSELL=0
FLAGBUY=1
END IF
END IF
FUNC ORDER (FPRICE,FLOTS,FDIRECTION,FTYPE,FTRID) ' ФУНКЦИЯ ОТПРАВКИ ТРАНЗАКЦИЙ В ТОРГОВУЮ СИСТЕМУ
NEW_GLOBAL («TRANS_PARAMS», "")
NEW_GLOBAL («TRANS_RESULT», "")
TRANS_PARAMS = "«
TRANS_PARAMS = SET_VALUE (TRANS_PARAMS, „TRANS_ID“, FTRID&»")
TRANS_PARAMS = SET_VALUE (TRANS_PARAMS, «ACTION», «NEW_STOP_ORDER»)
TRANS_PARAMS = SET_VALUE (TRANS_PARAMS, «CLASSCODE», «SPBFUT»)
TRANS_PARAMS = SET_VALUE (TRANS_PARAMS, «SECCODE», INSTRUMENT)
TRANS_PARAMS = SET_VALUE (TRANS_PARAMS, «ACCOUNT», «SPBFUTXXXXX») ' ВАШ АККАУНТ НА ФОРТС
TRANS_PARAMS = SET_VALUE (TRANS_PARAMS, «CLIENT_CODE», «SPBFUTXXXXX») ' ВАШ КОД
TRANS_PARAMS = SET_VALUE (TRANS_PARAMS, «OPERATION», FDIRECTION&"«)
TRANS_PARAMS = SET_VALUE (TRANS_PARAMS, „PRICE“, (FPRICE+1000)&»") ' ДЕЛАЕМ ПСЕВДОМАРКЕТ
TRANS_PARAMS = SET_VALUE (TRANS_PARAMS, «QUANTITY», FLOTS&"")
TRANS_RESULT = SEND_TRANSACTION (30, TRANS_PARAMS)
RESULT=GET_VALUE (TRANS_RESULT, «DESCRIPTION»)
MESSAGE (RESULT,1)
END FUNC
Здравствуйте, Евгений!
Столкнулся недавно с одной интересной особенностью Quik'а:
После успешного проведения транзакции средствами API, в результате которой естественно изменяется количество позиций по инструменту SecCode, проверка количества позиций:
...
N = 0+GET_NUMBER_OF («DEPO_LIMITS»)
FOR i FROM 0 TO N
g = GET_ITEM («DEPO_LIMITS», i) 'Лимиты по бумагам
IF GET_VALUE (g, «SECCODE») = SecCode
nP = 0+GET_VALUE (g, «CURRENT_BALANCE») 'всего позиций по SecCode
BREAK
END IF
END FOR
...
не дает ожидаемого результата. Вместо этого получаем данные, как если бы транзакции не было, т.е. GET_VALUE (g, «CURRENT_BALANCE») выдает данные по состоянию до транзакции.
При этом, эта же проверка, проведенная на следующем заходе в портфель до проведения транзакции выдает уже обновленный результат.
У меня закралось подозрение, что «свои» таблицы Quik обновляет после обработки, т.е. выхода из моего портфеля.
У Вас такого не случалось? А если случалось, как вышли из этого?
Евгений Reply:
ноября 19, 2009 at 19:13
Привет.
купайл получает данные таблиц квика только один раз за один период расчета портфеля.
п.с.
Купайлу все равно каким образом осуществилась транзакция.
EugN Reply:
ноября 20, 2009 at 16:42
Спасибо, Евгений.
Эмпирически тоже пришел к этому, т.е. Вы подтвердили мои подозрения. Пришлось перестроить бота, перенеся модуль транзакций в конец портфеля.
Евгений подскажите, если я запускаю двух роботов одновременно и у меня в каждом функции аналогичные с вашими KILLSTOP. Как роботы будут работать, видят ли они только свои заявки или они будут удалять все стопы в том числе и выставленные другим роботом?
Как лучше запускать двух и более роботов, на одном квике или на разных?
Совмещать ли их в одном Портфеле или делать отдельных роботов?
Евгений Reply:
ноября 27, 2009 at 19:36
Лучше всего совмещать в одном портфеле все нужные функции, или если это не возможно — запускать два разных квика каждого со своим роботом и со своим счетом.
Евгений доскажите как лучше решить данную задачу- в роботе есть ряд переменных которые рассчитываются с периодичностью заданной в настройках портфеля, скажем 1 секунду для оперативной работы с заявками. Как сделать так чтобы одна переменная рассчитывалась с периодичностью не 1 секунду, а 20 минут?
Данный вопрос возник после теста многих стратегий большинство стратегий плохо работают на таймфреймах меньше 20 минут, так как слишком много шума.
Евгений Reply:
декабря 6, 2009 at 18:55
ПРивет.
Если вам надо чтоб только ОДНА переменная из множества в портфеле пересчитывалась раз в 20 мин, ее надо назначить глобальной, и сделать цикл запусков. Например весь портфель у вас рассчитывается раз в секунду. Значит:
NEW_GLOBAL («VALUE»,0)
NEW_GLOBAL («COUNT»,0)
COUNT=COUNT+1
IF COUNT = 1200 ' 20 МИНУТ ЭТО 20*60=1200 СЕКУНД
VALUE=2*2 ' РАСЧЕТ ВАШЕЙ ПЕРЕМЕННОЙ
COUNT=0 ' ОБНУЛЕНИЕ
END IF
Таким образом ваша переменная будет рассчитываться раз в 20 минут.
pocemon Reply:
декабря 6, 2009 at 19:56
Большое спасибо Евгений. Я сам бы не догадался так элегантно решить проблему у меня была идея сделать для переменой отдельный портфель и прикрутить его к роботу
Ваш вариант намного проще.
Здравствуйте, Евгений!
Мы с Вами уже переписывались по поводу постановки заявки в заданное
время и небольшого полуавтомата, который будет по мере исполнения
первой заявки выставлять противоположные.
Фактически алгоритм таков:
1. Выставляем заявку в заданное время. Алгоритм готов и работает.
Номер заявки нам известен.
ZAY_ORDER_NUMBER = GET_VALUE (TRANS_RESULT, «ORDER_NUMBER»)
2. Обращаемся к остатку бумаг в заявке.
Возникла проблема — транзакция отправляется через функцию которая
вызывается только в определенное время — время выставления заявки, с
начала расчета портфеля заново номер транзакции уже недоступен.
TRIGGER=0
IF TIME==START
ORDER ((PRICE-OTSTUP),LOTS,"B",TIME,CLASSCODE,INSTRUMENT)
TRIGGER=1
END IF
IF TRIGGER=1
FOR I FROM 0 TO GET_NUMBER_OF («ORDERS») 'Перебираем строки таблицы заявок
MESSAGE (ZAY_ORDER_NUMBER,1)
trade = GET_ITEM («ORDERS», I)
NUMBER = GET_VALUE (trade, «NUMBER») + 0
IF GET_VALUE (trade, «STATUS») == «ACTIVE» 'AND ZAY_ORDER_NUMBER == NUMBER
MESSAGE ("Номер заяки «&NUMBER&». Остаток: "&GET_VALUE (trade, «BALANCE»),1)
END IF
END FOR
END IF
Сразу после выставления заявки по времени данный код вывод список
активных заявок, но без той которая была только что выставлена.
3. Пока остаток в первоначальной заявке не стал равен 0 выставляем
противоположные заявки при изменении Остатка лотов в первоначальной заявке,
но сумма лотов уже выставленных противоположных заявок не должна
превышать суммы лотов в первоначальной заявке.
Не могу сообразить как это должно структурно выглядеть. Был бы
благодарен за подсказку.
Евгений Reply:
декабря 10, 2009 at 20:04
Ну во первых переменную с номером заявки сделайте глобальной:
NEW_GLOBAL («ZAY_ORDER_NUMBER»,0)
тогда ее крайнее значение будет сохраняться при каждом запуске портфеля. Далее код не смотрю. т.к. возможно введение этого момента его кардинально изменит.
empenoso Reply:
декабря 15, 2009 at 14:24
Евгений, добрый день!!!
При введении глобальной переменной номер стал сохраняться — и стало возможно обращаться к остаткам этой заявки. Спасибо!!!
Сам механизм заработал, но у него похоже я что-то напутал с программной логикой — не знаю как точно описать предыдущее значение для переменной, как Ref (BALANCE, -1); в программах ТА.
То есть теперь при работе когда лоты из поставленной заявки начинают выкупаться, выставляются противоположные — но правильно только для первого цикла расчетов — например два лота было в заявке — два выкупили, он ставит два противоположных а потом еще два, еще один и похоже так собирается продолжать пока хватит денег
Посмотрите, пожалуйста, код, в нем я написал текстовые комментарии для того чтобы была понятна логика действий — в чем же состоит ошибка в логике?
NEW_GLOBAL («ZAY_ORDER_NUMBER»,1)
NEW_GLOBAL («BALANCE»,LOTS)
NEW_GLOBAL («KOMPENSIR_LOT»,0)
...
'Перебираем строки таблицы заявок
FOR I FROM 0 TO GET_NUMBER_OF («ORDERS»)
trade = GET_ITEM («ORDERS», I)
NUMBER = GET_VALUE (trade, «NUMBER») + 0
'Если это та заявка которая нум нужна — ее номер уже есть — ZAY_ORDER_NUMBER, то далее
IF NUMBER==ZAY_ORDER_NUMBER 'AND GET_VALUE (trade, «STATUS») == «ACTIVE»
'предыдущее значение остатка, как Ref (BALANCE, -1); в программах ТА. здесь у меня есть сомнения правильно ли записано
OLD_BALANCE=BALANCE
'текущее значение остатка для заявки с известным номером
BALANCE = GET_VALUE (trade, «BALANCE») + 0
'если значение предудущего расчета остатка не совпадает с текущим, то есть надо подать компенсирующие лоты, то далее:
IF OLD_BALANCE!=BALANCE
'для визульного вывода в таблицу. Лоты в первонач.заявке минус текущее значение остатка
KOMPENSIR_LOT=LOTS-BALANCE
'сколько лотов подавать на текщуем цикле расчета в заявку. предыдущее значение остатка — текущее значение остатка
LORDER=OLD_BALANCE-BALANCE
'если это значение больше 0:
IF LORDER>0 'AND KOMPENSIR_LOT!=LOTS
'выставляем ордер
ORDER ((PRICE+OTSTUP),LORDER,"S",TIME,CLASSCODE,INSTRUMENT)
MESSAGE ("Первоначальная заявка: «&LOTS&». Подано комп. лотов: «&KOMPENSIR_LOT&» ",1)
END IF
END IF
END IF
END FOR
Спасибо!
Евгений Reply:
декабря 15, 2009 at 19:23
А вы на какой площадке работаете?
На ФОРТС есть очень элегантный способ решения вашей задачи: в таблице позиции по клиентским счетам для каждого инструмента есть значения активной покупки и активной продажи. Таким образом вы всегда знаете сколько у вас активный остаток в заявке/ах. И зная значение текущпозы вы просто сравниваете его с активным кол-вос противоположной направленности.
empenoso Reply:
декабря 15, 2009 at 19:37
Это для ММВБ
Евгений Reply:
декабря 15, 2009 at 19:55
В таком случае вам нужно сравнивать значение текпоза и его знака с направленностью и кол-вом активных остатков. По приведенному куску кода сложно сказать где ошибка.
TP=0
FOR I FROM 0 TO GET_NUMBER_OF («DEPO_LIMITS»)
IF GET_VALUE (GET_ITEM («DEPO_LIMITS», I), «CURRENT_BALANCE»)+0<>0 AND GET_VALUE (GET_ITEM («DEPO_LIMITS»,I), «SECCODE»)=INSTRUMENT
TP=GET_VALUE (GET_ITEM («DEPO_LIMITS»,I), «CURRENT_BALANCE»)+0
END IF
END FOR
ORDERCOUNTBUY=0
ORDERCOUNTSELL=0
FOR I FROM 0 TO GET_NUMBER_OF («ORDERS»)
IF (GET_VALUE (GET_ITEM («ORDERS», I), «STATUS»)="ACTIVE") AND (GET_VALUE (GET_ITEM («ORDERS», I), «OPERATION»)="BUY")
ORDERCOUNTBUY=GET_VALUE (GET_ITEM («ORDERS», I), «BALANCE»)
END IF
IF (GET_VALUE (GET_ITEM («ORDERS», I), «STATUS»)="ACTIVE") AND (GET_VALUE (GET_ITEM («ORDERS», I), «OPERATION»)="SELL")
ORDERCOUNTSELL=GET_VALUE (GET_ITEM («ORDERS», I), «BALANCE»)
END IF
END FOR
и затем сравниваете текпоз с полученными значениями. Что-то в этом духе короче.