README.md

О проекте

qupy-trader - комплекс программ выполняемый в среде Linux и прелназначенный для разработки и тестирования торговых алгоритмов взаимодействующих с терминалом QUIK. Взаимодействие осуществляется через библиотеку https://github.com/cia76/QuikPy. Для хранения информации используется СУБД Postgresql.

ВНИМАНИЕ: Программа не предназначена для неподготовленного пользователя. Нужны базовые знания по linux и питону, в противном случае всё закончится если не на этапе установки, так при написании конфига для ТС. ВНМАНИЕ: программа находится на ранем этапе разработки и может выкидывать фортеля. По мере наработки моторесурса баги будут уходить, фичи появляться, но как известно, разработку нельзя закончить, ее можно только пректатить.

Состав

Комплекс состоит из следующих компонентов:

  • hub.py - шлюз выполняющий функции концентратора данных от QUIK (свечи, результаты обработки заявок) и отправляющий на QUIK заявки сгенерированные торговыми алгоритмами. Хранение данных и взаимодействие с другими модулями осуществляется через СУБД.
  • ctl.py - утилита для прямой (минуя шлюз) работы с quiksharp для отладки, импорта исторических данных, создания и просмотра заявок. Во многом надёргана из Examples/ проекта QuikPY но сведеных в одной точке и выдающая информацию в более понятном (на взгляд автора) виде.
  • agent.ctl.py - утилита взаимодействующая с QUIK через хаб для отладки взаимодействия.
  • agent.py - программный модуль торгового агента реализующий функции тестирования алгоритма на исторических данных и его посильного выполнения в реальном времени
  • watchdog.py - утилита контроля работы хаба и агентов
  • config.yml.example - пример основного конфига используемого всеми утилитами
  • hub.actions.yml - конфиг действий для хаба (сделана отдельным файлов что бы не захламлять основной конфиг)
  • examples/*.yml - пример торговой стратегии которая передается agent.py и содержит описание стратегии.
  • samples/scheme.sql - схема sql
  • samples/sample.csv - заготовка для csv-кеша (по-моему уже нигде не используется)
  • local.d/ - каталог оверрайдов на экшены вотчдога и хаба

Терминология проекта

Может отличаться от общепринятой, ну как назвалось.

  • хаб, шлюз (hup.py) - прокладка между агентами и квиком (скриптом quiksharp если быть точным)
  • quiksharp - плагин для квика дающий доступ к его апи для внешних программ.
  • агент - экземпляр agent.py обрабатывающих заданную стратегию
  • конфиг агента - базовые настройк и алгоритм торговли записанный в yaml файл обрабатываемый агентом
  • бар - запись об итогах торгов с заданной дискретностью (интервалом)
  • CLASS_CODE - класс инструмента, например SPBFUT. Доступные классы можно увидеть через ctl.py
  • SECURITY_CODE - код конкретного рычного инструмента. Например GAZR
  • INTERVAL - временной интервал данные по торгам внутри которого суммируются
  • code - короткая кодировка инструмента в формате CLASS_CODE.SECURITY_CODE.INTERVAL
  • ticker - тоже что и код но без указания интервала
  • resample - передискретизация данных с одного интервала на другой (например с минуток на пятнашки)

Установка

подготовка linux

Отрабатывалась на debian-11. Можно наверное какой угодно, но если поедут версии библиотек, концов не найдете.

Установить базовый дебиан-11. Далее с правами администратора (или любого пользователя, приклад не требует особых прав при работе):

apt-get install -y sudo python3-yaml postgresql-13 python3-psycopg2 apg git python3-pip
git clone https://gitflic.ru/project/konsul/qupy-trader
git clone https://github.com/cia76/QuikPy
cd QuikPy && git checkout bb61f337170e5d3ee506339402703ec89aaaeed5  # в более поздней версии изменился API
cp ~/qupy-trader/samples/scheme.sql /tmp/;chmod 644 /tmp/scheme.sql # схема будет нужна при подготовки БД
cd;cp QuikPy/QuikPy.py qupy-trader/   # скрипт нужен в текущем каталоге для импорта

Создаем БД, пользователя и задаем права.

su - postgres
sudo -u postgres psql
postgres=# create database qupydb;
postgres=# create user qupyu with encrypted password 'mypass';
postgres=# grant all privileges on database qupydb to qupyu;

Заружаем схему:

sudo -u postgres psql -d qupydb </tmp/scheme.sql

Поставить пандас, нампи и библиотеки теханализа через pip3. Скопировать шаблон конфига и поправить пароль пользователя СУБД:

cd ~/qupy-trader
pip3 install "numpy<2.0" plotly
pip3 install pandas-ta pandas ta PrettyTable
cp config.yml.example config.yml

Проверяем доступност бд:

root@tra2:~/qupy-trader# ./hub.py 
...[notag]: Loadingi yaml: config.yml
...[connect_db]: База данных 'qupydb' подключена
...[prepare_debug_log]: Debug log: logs//hub.py.debug.20240831-122814

Подготовка QUIK

  • На рабочем месте с квиком запустить скрипт QuikSharp из комплекта https://github.com/cia76/QuikPy в соответствии с документацией
  • Тем или иным образом обеспечить доступ к портам квикшарпа из linux на котором будет запускаться qupy-trader. Например запустив openssh для windows и пробросив нужные порты:
ssh winuser@windows-ip -p22 -L34130:127.0.0.1:34130 -L34131:127.0.0.1:34131

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

Проверка связки

После проброса, убедиться что цепочка работает корректно:

Выводим список доступных счетов:

root@tra2:~/qupy-trader# ./ctl.py --list-accounts
...[notag]: Loadingi yaml: config.yml
...[lib_open_qp_connection]: quik provider created
trdaccid : SPBFUT39aba firmid : SPBFUT status : 0 description :  bank_acc_id :  
trdaccid : L01-00000A00 firmid : MC0003300000 status : 0 description :  bank_acc_id :  
trdaccid : MB0008323930 firmid : MC0003300000 status : 0 description :  bank_acc_id :  
trdaccid : VTBRM_CL firmid : MC0002600000 status : 0 description :  bank_acc_id :  
...[close_qp_connection]: Disconnected

Запрос истории по инструменту класса SPBFUT с кодом CNYRUBF и интервалом 1 минута:

root@tra2:~/qupy-trader# ./ctl.py --show-quik-bars SPBFUT.CNYRUBF.1
...[notag]: Loadingi yaml: config.yml
...[lib_open_qp_connection]: quik provider created
                       open    high     low   close  volume
datetime                                                   
2024-06-18 12:40:00  11.760  11.766  11.760  11.765     191
2024-06-18 12:41:00  11.765  11.775  11.764  11.775    2311
2024-06-18 12:42:00  11.775  11.781  11.767  11.781    2854
...

Прочие команды доступны в хелпе: ./ctl.py --help.

На этом этапе настройка завершена.

запуск шлюза и заполнение истории

Так как оригинальный плагин quiksharp не умеет работать с несколькими соединениями, связь с ним осуществляется через прокладку hub.py. Модуль осуществляет заполнение таблиц с историей инструментов, транслирует заявки в квик и обрабатывает калбеки (приход новых баров, срабатывание заявок итп). В общем случае он не требует отдельно настройки и взводится запуском с ключом --run:

root@tra2:~/qupy-trader# ./hub.py --run
...[notag]: Loadingi yaml: config.yml
...[connect_db]: База данных 'qupydb' подключена
...[prepare_debug_log]: Debug log: logs//hub.py.debug.20240831-151412
...[lib_open_qp_connection]: quik provider created
...[main_run]: Started up in 0:00:00.017015
[quik:True][ltime:10:12:55][stime:10:12:54][data_gap:  48s] {'new': 6, 'duplicated': 0, 'updated': 0}

С заданной в конфиге периодичностью он будет выводить:

  • quik: True/False - квик соединен с сервером или нет
  • ltime: локальное время (local time)
  • stime: время квик-сервера (server time)
  • data_gap: сколько секунд прошло с последнего пришедшего по подписке бара (для остлеживания что подписка не сломалась)
  • число приехавших баров.

Подписка на инструменты осуществляется путем добавления записей в таблицу subscribes напрямую, через конфиг (секция hub/subscriptions) или через утилиту:

root@tra2:~/qupy-trader# ./agent.ctl --subscribe SPBFUT.CNYRUBF.1
...[notag]: Loadingi yaml: config.yml
...[connect_db]: База данных 'qupydb' подключена
...[prepare_debug_log]: Debug log: logs//agent.ctl.debug.20240831-152612
...[notag]: Subscribtion to SPBFUT.CNYRUBF.1 success
...

Если подписка прошла успешно, хаб выдаст что-то вроде:

..[main_run]: New subscription found id: 1, code: SPBFUT.CNYRUBF.1
...[qp_subscribe]: Subscribed to SPBFUT.CNYRUBF.1
...
...[main_run]: Подписка на [SPBFUT.CNYRUBF.1] подтверждена
...

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

После загрузки данных в бд (далее - кэш), их можно увидеть в том виде, как они дсоступны для агентов:

root@tra2:~/qupy-trader# ./agent.ctl --print-cached SPBFUT.CNYRUBF.1|head
...[notag]: Loadingi yaml: config.yml
...[connect_db]: База данных 'qupydb' подключена
...[prepare_debug_log]: Debug log: logs//agent.ctl.debug.20240831-153005
...[main]: Data started from: 2024-08-29 09:59:00
                       open    high     low   close  volume
datetime                                                   
2024-08-29 09:59:00  11.869  11.869  11.869  11.869     114
2024-08-29 10:00:00  11.869  11.887  11.869  11.878    1745
2024-08-29 10:01:00  11.879  11.889  11.879  11.886    1808
2024-08-29 10:02:00  11.888  11.917  11.887  11.912    5641
...

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

+++[quik_connected_callback]: 31.08.2024 15:28:58 - {'data': '', 't': 1725107324796.2, 'cmd': 'OnConnected'}
+++[main_run]: Stream queue have 229066 record. Response may be slow
...[main_run]: Queue cleared, normal processing now
[server time: 15:28:39] {'new': 86417, 'duplicated': 1486143, 'updated': 0}

При первом старте может возникать сообщение вида:

+++[main_run]: Возможно разрыв в исторических данных для TQBR.GAZP.60: '2024-09-04 20:00:00' - '2024-09-04 21:00:00'
False (datetime.datetime(2024, 9, 4, 21, 0), 'TQBR.GAZP.60', 122.55, 122.6, 122.06, 122.11, 66983.0)

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

Зарузка исторических данных

Квик отдает достаточно ограниченную глубину истории по инструментам (в зависимости от интервала), и ее может быть не достаточно дя проверк стратегий. Шлюз выдает бесшовную историю (сливает свежие данные из квика с кэшом), поэтому при необходимости исторические данные на произвольную глубину можно предзагрузить из csv. Штатно поддерживается тот формат который выдает ресурс https://daytradingschool.ru/trejderu/skachat-kotirovki-istoricheskie-dannye-po-fyuchersam/ (там доступны минуты).

Для импорта данных нужно указать путь до файла, код инструмента и интервал:

root@tra2:~/qupy-trader# ./ctl.py --import-bars ../GOLD.txt.short:SPBFUT.GOLD.1
...[notag]: Loadingi yaml: config.yml
...[main]: Обработка файла '../GOLD.txt.short', код 'SPBFUT.GOLD.1'
...[connect_db]: База данных 'qupydb' подключена
...[csv_file_to_pd]: Первая запись файла: 0
...[csv_file_to_pd]: Последняя запись файла: 8
...[csv_file_to_pd]: Кол-во записей в файле: 9
             datetime       open       high        low      close  volume           code
0 2009-01-11 10:33:00  859.90002  859.90002  854.00000  854.00000      13  SPBFUT.GOLD.1
1 2009-01-11 10:37:00  854.40002  854.40002  854.40002  854.40002       2  SPBFUT.GOLD.1
2 2009-01-11 10:39:00  855.29999  855.29999  855.29999  855.29999       8  SPBFUT.GOLD.1
3 2009-01-11 10:47:00  856.29999  856.29999  856.29999  856.29999       1  SPBFUT.GOLD.1
4 2009-01-11 10:51:00  856.20001  856.20001  856.20001  856.20001       8  SPBFUT.GOLD.1
5 2009-01-11 10:54:00  856.50000  856.50000  856.50000  856.50000      10  SPBFUT.GOLD.1
6 2009-01-11 10:58:00  857.00000  857.00000  857.00000  857.00000       1  SPBFUT.GOLD.1
7 2009-01-11 11:00:00  857.00000  857.00000  857.00000  857.00000       1  SPBFUT.GOLD.1
8 2009-01-11 11:03:00  857.00000  857.00000  856.20001  856.20001       8  SPBFUT.GOLD.1

После этого кешированные данные должны стать доступными агенту:

oot@tra2:~/qupy-trader# ./agent.ctl --print-cached SPBFUT.GOLD.1
...[notag]: Loadingi yaml: config.yml
...[connect_db]: База данных 'qupydb' подключена
...[prepare_debug_log]: Debug log: logs//agent.ctl.debug.20240831-163424
...[main]: Data started from: 2009-01-11 10:33:00
                          open       high        low      close  volume
datetime                                                               
2009-01-11 10:33:00  859.90002  859.90002  854.00000  854.00000      13
2009-01-11 10:37:00  854.40002  854.40002  854.40002  854.40002       2
2009-01-11 10:39:00  855.29999  855.29999  855.29999  855.29999       8
2009-01-11 10:47:00  856.29999  856.29999  856.29999  856.29999       1
2009-01-11 10:51:00  856.20001  856.20001  856.20001  856.20001       8
2009-01-11 10:54:00  856.50000  856.50000  856.50000  856.50000      10
2009-01-11 10:58:00  857.00000  857.00000  857.00000  857.00000       1
2009-01-11 11:00:00  857.00000  857.00000  857.00000  857.00000       1

Примечание: на настоящий момент импорт возможен только при условии, что данных в базе по данному инструменту и интервалу еще нет. Если данные требуется догрузить, то необходимо либо удалить старую историю, либо осуществить импорт с ограничением по стартовой дате добавив опцию --start-dt '2024-06-29 23:49:00' что бы избежать коллизий.

Разработка торговых стратегий и бэктест

Терминология

  • runner - экземпляр агента выполняющий торговую стратегию
  • источники (sources) - символическое имя инструмента для адресации внутри стратегии
  • streams - потоковый индикатор. Принимает на вход фрейм пандас и выдает результат в тот-же датафрейм в отдельную колонку.
  • booleans - переменные типа true/false вычисляемые на основе потоковых индикаторов. Для упрощения доступа, перед прогоном булей данные клонируются в словарь списков с доступом по индексу
  • actions - действия, изменение размера позиции на основании комбинации переменных.

тестирование на истории

Пример торговой стратегии доступен в каталоге examples/ и после предзагрузки с дейтрейдинга истории для SPBFUT.CNYRUBF (напрмер) можно оценить ее работу:

root@tra2:~/qupy-trader# ./agent.py --agent-config examples/sar-cny-backtest.yml --backtest --clean-state 
...
..[backtest]: -----statistics
{'actions': {'close-all': 124,
             'close-long': 439,
             'close-short': 424,
             'open-long': 504,
             'open-short': 480},
 'max_equity_abs': 1479,
 'max_equity_dt': '2024-06-28 17:39:00',
 'min_equity_abs': -401,
 'min_equity_dt': '2024-03-11 17:42:00'}
{'datetime': None, 'value': 1307}
...[notag]: backtest finished in 0:00:30.043035

Предпоседняя строчка с value - расчетный результат стратегии. Все прочие строчки - понятны из названия.

Сам конфиг агента подробно откоментирован.

Если агента запускать с параметром -v, то будет выведена детализация по сделками, если -vv то показания индикаторов на каждом баре.

Для более оперативного прогона по разным инструментам, можно использовать два доп. параметра:

  • –remap str1=str2 осуществляет глобальную замену подстроки1 на подстроку2 в конфиге который грузит бэктестер. Можно указывать много раз. str1 это регуляр со всеми вытекающими.
  • –resample RATE заменяет параметр передескритизации в том-же конфиге (теоретически можно былоб обойтись ремапом, но там кавычки и все прочее - проще напрямую назначить)
  • –start-dt ‘2024-01-01 00:00:00’ - ограничение выборки с начала указанной датой

Кроме того есть ключ `–plot-equity’ который нарисует кривенький график изменения депо. Не очень красиво, но поведение оценить можно.

калибровка глубины пересчета

Многие потоковые индикаторы врут если число расчитываемых баров не достаточно. Как правило это решается тем, что пересчет по полной истории идет на каждой итерации (на каждый новый бар), но это не всегда подходит (пересчет может занимать много времени). Так как в системе пересчет происходит только по числу баров заданных параметром recalc_depth, необходимо убедиться, что значение достаточно для корректного расчета потоковых индикаторов. Для этого необходимо вызвать агента с параметром калибровки. Агент начнет увеличивать объем пересчитываемых данных до тех пор, пока значение индикаторов не перестает изменятся и полученную цифру выведет как результат

root@tra2:~/qupy-trader# ./agent.py --calibrate --agent-config examples/sar-cny-backtest.yml 
...[notag]: Loadingi yaml: config.yml
...[notag]: Loadingi yaml: examples/sar-cny-backtest.yml
...[connect_db]: База данных 'qupydb' подключена
...[calibrate_recalc_depth]: Калибровка глубины пересчета ...
...[calibrate_recalc_depth]: Загрузка тестового потока данных
...[sources_to_df]: Processing source: s0
...[sources_to_df]: Loading history for SPBFUT.CNYRUBF.1
...[sources_to_df]: Resampling data to False
...[sources_to_df]: Loaded history size:89566 of bars
...[sources_to_df]: Finished in 0:00:00.548880
...[process_streams]: Stream processing ... PSARl
...[process_streams]: Stream processing ... PSARs
...[process_streams]: Stream processing ... DoW
...[process_streams]: Finished in 0:00:28.076645
...[calibrate_recalc_depth]: Required preload: 106

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

Рекомендуется округлять значение индикаторов насколько возможно (round(X)), т.к. при сравнении учитываются все цифры после запятой (а их может быть дофига) и расчитанная глубина пересчета будет совершенно конской, притом что влияние на результат околонулевое.

recalc_depth никак не влияет на скорость бэктеста, т.к. там пересчет производится только один раз на всей истории.

Торговля

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

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

При выставлении параметра trade = False в секции раннера, создаваемые позиции будут виртуальными (иметь статус ‘virtual’ в бд) как следствие не будут выставляться на рынок. При этом их учет будет производится по цене закрытия предыдущего бара.

При выставлении параметра confirm = True там-же, перед выставлением заявки будет осуществляться запрос подтверждения и заявка будет выполнятся (вне зависимости от режима торговли) только при получения утвердительного ответа оператора

# s0_in_position s0_short s0_long sar_up sar_down TIME     trade_allowed weekend 
  False          False    False   True   False    17:29:00 True          False   
Выполнить изменение позиции по инструменту SPBFUT.CNYRUBF на 1 лотов по цене 12.100999999999999? [Y/n]: 

Состояние запущенных агентов можно увидеть командой ./agent.ctl --list-agents

Заявки

Работа с заявками коцептуально отличается от привычной схемы ‘сигнал-открыли-заявку-сигнал-закрыли заявку’. Реализованная схема про соответствие сигнал-заявка вообще ничего не знает, она отслеживает исключительно соответствие целевой позиции агента (которая может изменяться произвольно на основании сигналов) и текущей (вычисляемой на основании успешно выставленных заявок). Это тащит за собой ряд проблем, но взамен даёт возможность выстраивать позиции произвольного уровня вложенности. Имеет-ли такой подход ссмысл, покажет только время.

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

При проблемах на квик-сервере (такое бывает например при сильной волатильности) заявки могут проходить с задержкой. Например отправка заявки квиком отвалилась по таймауту, хаб проставил ей статус инвалид и забил. На следующем баре агент попытается снова привести в соответствие целевые и текущую позиции, опять будет выставлена заявка, а квик-сервер через некоторое время напрмер продышался и сообщил что заявка отработала и старая и новая. Шлюз поменяет статус, агент по идее сменит размер текущей позиции - но всё это чисто теоретически. Поэтому если шлюз рапортует о проблемах с выставлением заявок, лучше вообще притормозить автоматику. У автора в связи с этими нюансами поведения был крайне познавательный опыт при работе на одной из коммерческих систем, когда она не получая от квика ответа всаживала по несколько одинаковых заявок, а они потом отрабатывали пачкой и вместо позиции 10 лотов в лонг, ты получал их 50.

действия по сигналу

Действия предпринимаемые при срабатывании сигнала определаются в поле function/functions (см. пример examples/execs-example.yml). Так как по сути ожидается выражение передаваемое exec, это позволяет производить не только действия по изменению позиции, но и выполнять произвольные команды. В примере в частности на один из сигналов вызывается ls -l.

время реакции

Следует учитывать, что от момента прихода на хаб нового бара, до момента когда его увидит агент может пройти до двух секунд (при условии, что хаб не загружен прожёвыванием очереди). Она складывается из периодичности опроса очереди баров заполняемой калбэками квика (раз в секунду) и периодичности опроса бд агентом (тоже секунда). Если задержка больше, следует посмотреть отладочный лог хаба (logs/hub.py.debug) на предмет времен прихода пакета (параметр pqt_enqueued) и времени извлечения его из очереди (pqt_dequeued). Частоту опроса очередей хабом можно увеличить снизив время задержки между циклами hub/query_interval, но ставить его в 0 или False не желательно, т.к. процессорное время он и субд жрать будут, а выигрыш по времени на фоне общей логики будет мизерный. Во избежании каких нибудь странностей, периодичность опроса сервера quik на предмет времени (фактически пинг) будет производится не чаще раза в секунду при любом значении задержки.

Частоту опроса субд агентом так же можно увеличить параметром runner/query_interval по аналогии с хабом.

прочая фигня

переменные

  • SHOWDT=1 - проставляет время во всех информационных сообщениях
  • TSDEBUG=1 - отладочная информация

статус агента

Хранится в бд в таблице agents. Строчки расписаны ниже, а словарь внутреннего состояния выглядит следующим образом:

$ ./agent.py --agent-config ../agents/ref21-cny.yml --agent-state
...[notag]: Loadingi yaml: config.yml
...[notag]: Loadingi yaml: ../agents/ref21-cny.yml
...[connect_db]: База данных 'qupydb' подключена
...[notag]: Running agent name:  ref21
{'actions_value': {'close-long': False,    ### словарь переменных как они расчитаны на последней итерации
                   'close-short': False,
                   'open-long': False,
                   'open-short': False},
 'booleans_value': {'TIME': '17:15:00',
                    'macd_cross_down': False,
                    'macd_cross_up': False,
                    's0_in_position': False,
                    's0_long': False,
                    's0_short': False,
                    'sig_close_long': False,
                    'sig_close_short': False,
                    'sig_open_long': False,
                    'sig_open_short': False,
                    'trade_allowed': True},
 'equity': {'datetime': '2024-08-30 17:15:00', 'value': 0},
 'orders': {'active': {},   ### список активных заявок (хаб ее выставил на рынок)
            'canceled': {}, ### отмененные заявки
            'filled': {},   ### выполненные
            'invalid': {},  ### хабу что-то не понравилось
            'processed': {}, ### заявка считана но ответ от квика о том что принято - еще не получен
            'rejected': {},  ### квик заявку отбил по каким-то причинам (цена вне диапазона, сессия не идет итп)
            'req-cancel': {}, ### запрос отмены заявк 
            'req-new': {}},   ### новая заявка
            'virtual': {}},   ### виртуальные заявки. заполняется если trade == False
 'position': {'SPBFUT.CNYRUBF': {'current': 0, 'source': 's0', 'target': 0}}, ###
 'statistics': {'actions': {'close-long': 0, ###
                            'close-short': 0,
                            'open-long': 0,
                            'open-short': 0},
                'max_equity_abs': 0,         ###
                'max_equity_dt': None,         ###
                'min_equity_abs': 0,         ###
                'min_equity_dt': None}}         ###

БД

описание таблиц

  • bars: таблица исторических данных
    • datetime дата бара
    • code код инструмента
    • open/high/low/close/volume
  • subscriptions подписки на бары
    • id сквозной идентификатор
    • code
    • status - если invalid то подписка не удалась
  • orders таблица заявок. записи создаются агентами и обрабатываются хабом.
    • datetime дата создания
    • agent имя агента создавшего заявки
    • code
    • operation_type покупка или продажа
    • price цена заявки. если 0 то тип приказа по рынку. иначе лимитная.
    • quantity обьем
    • client_code код генеренный агентом (фактически таймстамп )
    • status статус заявки
      • active заявка выставлена но не исполнена. проставляется хабом
      • canceled заявка отменена
      • filled заявка выполнена
      • rejected заявка отклонена квиком
      • invalid заявка отклонена хабом
      • processed промежуточное состояние перед active
      • req-new - выставляется агентом для новой заявки
      • req-cancel - выставляется агентом для отмена заявки
    • order_num - идентификатор заявки который возвращает квик после ее принятия
    • data - список словарей полученных от квика на обработку заявки
  • trades сделки. заполняется хабом по мере прихода колбэков на выставленные заявки
    • datetime
    • code
    • operation_type
    • price цена по которой операция была выполнена
    • quantity
    • trade_num номер сделки в понимании квика
    • order_num соответствует аналогичному полю заявки по которой сделка состоялась
    • brokerref соответствует полю client_code связанной заявки
    • data список словарей пришедших от квика

работа на windows

Теоретически оно должно работать, но как минимум есть проблема с отслеживанием таймаута на опрации взаимодействия с квиком, так как оно сделано через signal.SIGALRM который насколько я поняд в винде не але. Никто не мешает сделать альтернативную реализацию, но мне оно не надо, у будет спрос, можно будет подумать.

watchdog

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

Примечание: на слейве необходимо установить параметр hot_standby_feedback = on т.к. в противном случае будут возникать конфликты при выборках из-за быстрого обновления данных в таблице состоянии агентов.

problems

oot@tra2:~/qupy-trader# ./agent.py 
Traceback (most recent call last):
  File "/root/qupy-trader/./agent.py", line 15, in <module>
    import pandas_ta as pta
  File "/usr/local/lib/python3.9/dist-packages/pandas_ta/__init__.py", line 116, in <module>
    from pandas_ta.core import *
  File "/usr/local/lib/python3.9/dist-packages/pandas_ta/core.py", line 18, in <module>
    from pandas_ta.momentum import *
  File "/usr/local/lib/python3.9/dist-packages/pandas_ta/momentum/__init__.py", line 34, in <module>
    from .squeeze_pro import squeeze_pro
  File "/usr/local/lib/python3.9/dist-packages/pandas_ta/momentum/squeeze_pro.py", line 2, in <module>
    from numpy import NaN as npNaN
ImportError: cannot import name 'NaN' from 'numpy' (/usr/local/lib/python3.9/dist-packages/numpy/__init__.py)

pip3 install “numpy<2.0”

Описание

платформа для разработки и тестирования торговых систем интегрированная с QUIK

Конвейеры
0 успешных
0 с ошибкой