Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 15.06.2023
09.02.2026 21:13:42
TGB, Ну так это же просто двойная очередь, с которой я разбирался и только ленивый меня "по столу носом не возил". Более медленный вариант, чем скажем кольцевой буфер? Можете архитектуру привести или пример?
Пользователь
Сообщений: Регистрация: 12.05.2020
09.02.2026 22:41:39
Цитата
VPM написал: Более медленный вариант, чем скажем кольцевой буфер? Можете архитектуру привести или пример?
Не надо. Большинству пользователям достаточно использовать приведенный простой вариант, по эффективности мало отличающийся от вашего кольцевого буфера, который я видел. Но кто хочет может использовать и ваш.
Пользователь
Сообщений: Регистрация: 12.05.2020
09.02.2026 22:46:27
Цитата
VPM написал: Ну так это же просто двойная очередь, с которой я разбирался и только ленивый меня "по столу носом не возил".
Эта очередь разбиралась пользователем Старатель около 4-х лет назад. "Все украли до нас" .
Пользователь
Сообщений: Регистрация: 02.01.2026
12.02.2026 10:11:30
Цитата
VPM написал: это же просто двойная очередь, с которой я разбирался и только ленивый меня "по столу носом не возил". Более медленный вариант, чем скажем кольцевой буфер?
А что сравнивать, если они для разных задач предназначены? Кольцевой буфер - для хранения ограниченного количества элементов. Индексация всё время сдвигается вправо. Возможно удаление и изменение элементов с любым индексом. Очередь из сообщения аналог обычной таблицы с индексацией, начиная с 1. Используется, когда надо удалять элементы в начале таблицы, т.к. table.insert с этой задачей плохо справляется.
Йцукен написал: Кольцевой буфер - для хранения ограниченного количества элементов.
Так ведь и двойная очередь ограничена количеством элементов, для этого и храним первый и последний индекс. Лично меня всегда смущает в кольцевом буфере не явная индексации, "под ложечкой ноет" а тот ли элемент получен в расчетах. В то время как в двойной все линейно.
Пользователь
Сообщений: Регистрация: 27.01.2017
12.02.2026 11:55:12
Я только не очень понял, почему представленный пример называется двойная очередь, если это просто очередь как связанный список. И как она стала потокобезопасной в Lua, где этого нет.
Когда я говорил о двойной очереди, я говорил именно о двух очередях.
Пользователь
Сообщений: Регистрация: 12.05.2020
12.02.2026 13:20:29
Цитата
Nikolay написал: Когда я говорил о двойной очереди, я говорил именно о двух очередях.
Из написанного вами о двойной очереди (по сути реализующей одну) я не понял между какими функциональностями робота они могут использоваться и что при этом не обеспечивается в QLua, выложенным мной давно известным вариантом?
Пользователь
Сообщений: Регистрация: 02.01.2026
12.02.2026 14:15:24
Цитата
Nikolay написал: И как она стала потокобезопасной в Lua, где этого нет.
Добавление элемента в начало очереди в одном потоке не приводит к тому, что при попытке в другом потоке извлечь последний элемент возвращается nil.
Цитата
Nikolay написал: Когда я говорил о двойной очереди, я говорил именно о двух очередях.
А можно ссылку на это обсуждение?
Пользователь
Сообщений: Регистрация: 27.01.2017
12.02.2026 14:43:19
Цитата
Йцукен написал: Добавление элемента в начало очереди в одном потоке не приводит к тому, что при попытке в другом потоке извлечь последний элемент возвращается nil.
Извлечь нет, а вот размер уже можно получить кривой. Плюс надо как-то гарантировать, что в двух потоках не будут разбирать очередь. Можно надеяться, что так не напишут, но это не гарантия, т.к. блокировок нет. Хотя можно завернуть код в table.ssort
Потому что в момент получения размера, другой поток изменит last или first
-- Текущий размер очереди --- function QueueSize(self) return self.last - self.first + 1 end
Пользователь
Сообщений: Регистрация: 02.01.2026
12.02.2026 15:56:33
Цитата
Nikolay написал: Потому что в момент получения размера, другой поток изменит last или first
При обращении к элементу с индексом, равным размеру очереди вы не получите nil в любом случае. В этом смысле работа с очередью потокобезопасна, если запись только - в одном потоке, а извлечение - в другом. Но, если вы одновременно добавляете элементы в разных потоках или извлекаете в разных потоках, то да, нужно код оборачивать в ssort, например.
Пользователь
Сообщений: Регистрация: 02.01.2026
12.02.2026 16:15:32
Цитата
Nikolay написал: в момент получения размера, другой поток изменит last или first
Да, но потокобезопасность - это гарантия, а не вероятность.
Я говорю, что сомнительно, что то, о чём вы пишите, вообще может произойти в Lua 5.4. Потому что там байт-код, который по определению выполняется под блокировкой.
Пользователь
Сообщений: Регистрация: 27.01.2017
12.02.2026 16:59:55
Мы не знаем как реализовали доп. поток в Квик. Реализовали ли макросы lua_lock вместо заглушек в lua исходниках. Хотя в этой давней теме обсуждалось, что таки реализовали.
Пользователь
Сообщений: Регистрация: 02.01.2026
12.02.2026 17:34:20
Nikolay, а для чего вам знать размер очереди? Вы же не извлекаете из середины? Если организуется FIFO, то берётся первый элемент из очереди до тех пор, пока там что-то есть.
Пользователь
Сообщений: Регистрация: 27.01.2017
12.02.2026 17:37:09
Цитата
Йцукен написал: , а для чего вам знать размер очереди? Вы же не извлекаете из середины? Если организуется FIFO, то берётся первый элемент из очереди до тех пор, пока там что-то есть.
Может и не надо. Ремарка была, т.к. прозвучало, что это потокобезопасно. Не более.
Пользователь
Сообщений: Регистрация: 27.01.2017
12.02.2026 17:43:48
Цитата
TGB написал: Из написанного вами о двойной очереди (по сути реализующей одну) я не понял между какими функциональностями робота они могут использоваться и что при этом не обеспечивается в QLua, выложенным мной давно известным вариантом?
Думаю, что для qlua такой проблемы вообще нет. Не те скорости. Это просто был пример для более тяжелых систем, не более.
Пользователь
Сообщений: Регистрация: 15.06.2023
12.02.2026 21:21:27
Цитата
Nikolay написал: Думаю, что для qlua такой проблемы вообще нет. Не те скорости. Это просто был пример для более тяжелых систем, не более.
А вот это обидно! А Вы уберите sleep и по рассуждаем чья инженерия кода лучше?
Пользователь
Сообщений: Регистрация: 15.06.2023
12.02.2026 21:47:06
Или вот это, До обсуждались, про супер:
Цитата
Nikolay написал: Nikolay , а для чего вам знать размер очереди? Вы же не извлекаете из середины? Если организуется FIFO, то берётся первый элемент из очереди до тех пор, пока там что-то есть.
А Не смущает то что мы ее задаем и вынуждены контролировать? Постоянно так Quik Ни чего нигарантирует?
Пользователь
Сообщений: Регистрация: 15.06.2023
12.02.2026 21:56:16
Возможно термин не удачен "Двойная очередь", но это просто перевод из учебника по луа от автора, самого луа. Мне тоже кажется не правильно, поэтому применяю термин "Двухфакторная", более логина. Но раз закрепилась от автора "Двойная очередь", то слов из песни не выбросить.
Пользователь
Сообщений: Регистрация: 02.01.2026
12.02.2026 23:01:43
Цитата
VPM написал: А Не смущает то что мы ее задаем и вынуждены контролировать?
Для чего?
Пользователь
Сообщений: Регистрация: 27.01.2017
13.02.2026 08:18:54
Цитата
VPM написал: А вот это обидно! А Вы уберите sleep и по рассуждаем чья инженерия кода лучше?
Sleep здесь вообще не важен. Тем более, что если уберете его из main, то Квик банально встанет, что не говорит в его пользу и организацию работы с потоками.
Пользователь
Сообщений: Регистрация: 12.05.2020
13.02.2026 14:59:11
Цитата
Nikolay написал: Квик банально встанет, что не говорит в его пользу и организацию работы с потоками.
В ветке "Что стоило бы изменить в QUIK по-крупному" (от 15.03.2024) предложены изменения схемы встраивания Lua в QUIK (с сохранением существующего API пользователя) устраняющие некоторые существующие его дефекты, но реакция разработчик QUIK нулевая. Нет даже возражений.
Пользователь
Сообщений: Регистрация: 15.06.2023
15.02.2026 15:04:30
"Но если задача стоит построить систему, которая должна быть математически корректной, отказоустойчивой и масштабируемой, без событийной модели и формальных инвариантов не обойтись. Потому что её поведение можно доказать на реальной торговле на любых объёмах и с любыми стратегиями, а не просто протестировать!" В соседней веточке подняли на мой взгляд, очень важную тему, сколько угодно можно осуждать тему "система принятия торговых решений", но если не решена задача описанная выше, все становится просто бессмысленно. Перестраивая свою систему безопасности именно к такому выводу подошел. И хочу привести ряд подходов.
Пользователь
Сообщений: Регистрация: 15.06.2023
16.02.2026 10:56:38
Temporal Logic of Actions (TLA+) - метод, который взят за основу, — это не академическая теория, а активно используемая инженерная практика.
TLA+ применяется, критически важных системах, для верификации алгоритмов в распределённых системах, где ошибка может стоить очень дорого. Крупнейшие технологические компании, такие как Amazon Web Services (AWS) и Microsoft, применяют TLA+ как Промышленный стандарт. TLA+ помогает смоделировать взаимодействие нескольких торговых "ботов" на общем рынке и выявить сценарии, приводящие к "flash crashes" в Торговых алгоритмах. Например, его используют для проверки протоколов консенсуса в блокчейнах (таких как Solana и Tendermint ), платёжных каналов (Lightning Network ) и баз данных (Azure Cosmos DB ).
Суть. TLA+ позволяет описать систему не как код на Lua, а как набор состояний и переходов между ними. Это абстракция, в которой мы можем "забыть" о неважных деталях реализации и сосредоточиться на критической логике.
В моей текущей реализации системы это просто аналогия, Watchdog — это непрерывный контроль в реальном времени. Он утверждает: "Прямо сейчас состояние допустимо".
TLA+ даёт принципиально иной уровень гарантий: "При любом развитии событий, даже самом невероятном, система никогда не войдёт в недопустимое состояние". Эти два подхода не исключают, а идеально дополняют друг друга, обеспечивая максимальную надёжность.
Следовательно. Мы можем построить абстрактную модель системы на TLA+. Что позволит нам формально, а не только интуитивно, убедиться в том, что наши доказательства (proof-sketch) верны, а архитектура действительно гарантирует безопасность счёта при любых мыслимых сценариях работы биржи и сети.
И это совершено иная концепция безопасности. "Ни гадать а быть уверенным"!
Пользователь
Сообщений: Регистрация: 15.06.2023
22.02.2026 10:09:15
Нужно было просто поправить стратегию, и опять на те же "грабли!"
Разделение Ответственности.
Или взгляни на свою систему, как старший инженер, проектирующий инфраструктуру под множество стратегий, а не под одну конкретную. То есть, смещаемся на уровень инженера, который проектирует платформы, и отвечает за вопрос, как сделать правильное разделение ответственности.
Теперь задачу, можно свести к правильной архитектурной проблеме — не "как сделать ещё сложнее", а где проходят границы ответственности и кто от кого должен зависеть. Правильное разделение ответственности, сформулирую правильный вектор этого подхода.
Напрашивается, разделение по типу ответственности:
A. Технологическое ядро (универсальное) B. Адаптер к qLua (инфраструктура терминала) C. Стратегический уровень (разные стратегии)
Вопрос стоит так, как избежать "Главной архитектурной ошибки", которая почти всегда возникает - слои смешаны, то есть: * Стратегия знает про OrderManager; * Recovery знает про FSM; * FSM знает про детали брокера; * Executor знает про стратегический контекст. Это делает систему, сложно тестируемой, трудно расширяемой, опасной при добавлении еще одной или другой стратегии.
Сформулирую принципы, строго условно, выделив несколько слоев.
Layer 1 — Технологическое ядро (Core Engine). Это НЕ про торговлю.
должнен быть: * универсальным, * не знать, что такое стратегия, * не знать, что такое "акция" или "фьючерс".
Ему всё равно. Если завтра ты вставишь что то другое — ядро не меняется.
Layer 2 — qLua Adapter Layer. Это тонкий слой интеграции.
за что отвечает: * оборачивает getItem / getNumberOf * обрабатывает OnTrade * обрабатывает OnOrder * делает sendTransaction * нормализует данные
Это не стратегия. Это инфраструктурный адаптер. Ядро должно зависеть от интерфейса, а не от QUIK.
Layer 3 — Strategy Layer.
Это единственный слой, где есть: * SignalEngine; * FSM арбитража; * Логика входа/выхода; * Крайние параметры (edge); * Управление позицией
Стратегия НЕ должна, читать getItem, читать getNumberOf, знать class_code, знать brokerref. Она работает с абстракцией.
Сформулирую правильные зависимости (очень важно): * Strategy > Core; * Core > BrokerAdapter; * BrokerAdapter > qLua. И НИКОГДА наоборот.
"Стратегия не знает, что такое qLua. Core не знает, что такое конкретная стратегия".
Пользователь
Сообщений: Регистрация: 15.06.2023
26.02.2026 11:34:19
OnInit, или просто прозрение, которым хочется поделиться. Ни так давно (по меркам вечности), на страницах этого форума зашел спор, суть которого можно выразить фразой "На фига козе баян".
Высказывались опытные разработчики скриптов, мнение разделись, одни утверждали: > "OnInit не нужен, всё можно сделать в main", другие что нужен, но зачем, так ни кто и не объяснил. Я придерживался простого мнения: кому нужен пользуемся, кому не нужен не пользуемся. Пока не встал у меня вопрос, "Как отслеживать зависимости между модулями"? Пришлось детально разбираться. И вот что получилось.
Если смотреть на архитектуру QUIK Lua строго с точки зрения production-дизайна — OnInit не просто "не лишняя", а ключевая точка композиции системы. Посмотрим системно.
1. Что реально делает QUIK.
Архитектурно терминал работает так: 1. Загрузка скрипта 2. Вызов OnInit(script_path) 3. Создание отдельного потока > main() 4. Далее event callbacks (OnOrder, OnTrade, OnQuote, ...) вызываются только если main() жив
Ключевой момент. > Все callback’и не будут вызываться, если main завершился? Следовательно, main() — это lifecycle-контроллер. А OnInit() — это фаза конфигурации до старта runtime.
2. Почему в production OnInit крайне полезен. Ошибочное мнение. > "OnInit не нужен, всё можно сделать в main"? Можно. Но это плохая архитектура. А правильная архитектурная роль OnInit — это:
2.1. Корень композиции зависимостей (Composition Root ) Именно здесь правильно: * собирать зависимости * инстанцировать модули * связывать Core - Adapter - Strategy * валидировать конфигурацию * логировать старт
Это аналог: * Spring Boot bootstrap * ASP.NET Composition Root * Dependency Injection container setup Это три фундаментальных понятия современной backend-разработки, описывающих, как приложение запускается, собирается из компонентов и управляет своими зависимостями.
2.2. Fail-fast зона не менее важная. Если что-то не так: * нет доступа к счёту * отсутствует класс инструмента * нет прав * ошибка конфигурации Лучше упасть до старта main, чем в рабочем цикле.
2.3. Разделение фаз | Фаза | Что происходит | | ---------- | -------------------------- | | OnInit | Конфигурация | | main | Runtime | | Callbacks | Event-driven обработка | Это даёт чистую архитектуру.
3. OnInit особенно удобно.
3.1. Связывание зависимостей (Wiring). function OnInit(script_path)
4. Для чего НЕ стоит использовать OnInit. Не надо: запускать торговый цикл, делать в нем бесконечные sleep, ну и конечно обрабатывать рынок. Это не среда выполнения (runtime).
5. Production-подход к структуре QUIK-скрипта. И тогда просмативается правильный структурный подходС (skeleton):
-- ====================== -- Global references -- ====================== local App = {}
-- ====================== -- OnInit -- ====================== function OnInit(script_path) App = Bootstrap(script_path) end
-- ====================== -- main runtime loop -- ====================== function main() App:run() end
-- ====================== -- Event callbacks -- ====================== function OnOrder(order) App:onOrder(order) end
function OnTrade(trade) App:onTrade(trade) end
function OnStop() App:onStop() end Следовательно, так можем, централизовать систему, упрощаем тестирование, делает архитектуру масштабируемой.
6. Архитектурное мнение (строго production класса). Если мы хотим строить, multi-instrument, risk-aware, state machine driven, persistent trading system, то OnInit обязателен как "корень композиции" (composition root). Без него система будет выглядеть процедурно и хаотично.
7. Ну и моя идея. > "В ней удобно выстраивать зависимости модулей между собой для наглядности и контроля". Оказалась, абсолютно правильный инженерный подход, уровеня промышленной архитектуры. Разработчик, который фактически мыслит в терминах, Граф зависимостей (Dependency Graph), Управление жизненным циклом (Lifecycle management), Контролируемая начальная загрузка (Controlled bootstrap)
8. Что имеем, итоговое заключение. OnInit, не обязателен технически, и обязателен архитектурно в production подходе. Он делает систему, детерминированной, контролируемой, масштабируемой, безопасной, ну конечно удобной для отслеживания зависимостей самих модулей.
Пользователь
Сообщений: Регистрация: 27.01.2017
26.02.2026 12:16:50
А теперь представьте, что у вас запускаемый скрипт не содержит main. Т.е. тот файл, что выбирает в Квик - это скрbпт без main. Единственное, что этот скрипт делает - это подключает библиотеки и устанавливает параметры, имеет свой искатель библиотек (если нужно).
А как же это работает? Просто - подключая какую-то библиотеку через require, выполняется тело библиотеки. И в какой-то есть main. Т.о. этот запускаемый файл и выполняет роль инициализатора скрипта. Почему - это не в OnInit. Ответ прост - это намного гибче, т.к. пока дойдем до выполнения main уже выполнится много кода, инициализирующую среду исполнения. Причем в зависимости от скрипта, инициализация разная, может быть сложная, в может простая. Каждая подключаемая библиотека тоже что-то свое подключает.
Т.о. когда доходим до OnInit, то мы уже имеем инициализированную среду и нам остается только вывести в лог пути к подключенным библиотекам, их версии, параметры. Т.е. мы фиксируем в лог что подключено и какие параметры среды. Если всю работу выше загнать в OnInit, то он будет сложным, плохо контролируемым. Т.е. OnInit - это окончание инициализации, где просто выводим в лог состояние. а не начало инициализации.
Также достаточно просто посмотреть на схему выполнения Lua скрипта в документации, где видно, что сначала выполняется body, а потом OnInit скрипта. Т.е. с точки зрения main они равнозначны - выполняются до main. А вот у же с точки зрения OnInit отсутствие body - это недостаток, т.к. когда проект состоит из десятков файлов со своей инициализацией, то контроль за процессом запуска только в OnInit - это сложно.
И с таким подходом, OnInit - совсем не обязательная сущность.
Цитата
VPM написал: 3.2. Загрузка сохраненного состояния (persisted state). В OnInit удобно:* восстанавливать позиции;* читать журнал;* восстанавливать FSM состояние;* загружать последние известные заявки (last known orders)
Возможно я не понял это, но максимум, что можно сделать до main - это прочитать что-то с диска. Ни о каких восстановлении состояния речи не может быть, т.к. восстановление подразумевает и актуализацию после подключения к серверу. Если это просто чтение, то да. Но и это я тоже не делаю в OnInit, т.к. скрипт может не останавливаться и работать сутками без выключения терминала. Это значит, что OnInit вызвался вчера, а сегодня что? Надо же после восстановления связи с сервером и обновления таблиц тоже актуализировать состояние, проверить что случилось, что есть в данных и т.д. И делать это надо при старте каждого дня пока скрипт работает. А работать он может месяцами.
Поэтому OnInit - это технический колбек, просто сигнализирующий, что код исполненный выше в body скрипта при его запуске, не привел к ошибкам. Ничего более. Делается это один раз при запуске. Поэтому там и могут быть только сугубо технические действия именно при запуске скрипта и ничего более.
Пользователь
Сообщений: Регистрация: 12.05.2020
26.02.2026 14:01:52
Цитата
VPM написал: OnInit, или просто прозрение, которым хочется поделиться.
Я бы не стал это комментировать, но ведь кто то может это воспринять как открытие и руководство к действию. Попробую объяснить ваше заблуждение (очередное). Я в своих скриптах не использую ни OnInit ни кода в body по следующим причинам: 1) Код OnInit и код body будут исполняться в основном служебном единственном потоке, который обслуживает: - запуск всех Lua-скриптов пользователя; - запуск коллбеков всех Lua-скриптов пользователя; - обработка всех коллбеков таблиц QUIK (это не таблицы Lua); - обработка всех индикаторов пользователя. Это значит, что пока он будет "возиться" с кодами OnInit и body другого он ничего делать не будет. На всякий случай попробуйте в начале OnInit написать строку: sleep(30000) и посмотрите что получится . 2) Всю необходимую инициализацию (включая пакеты) можно разместить в начале main в виде функции, вызываемой сразу после ее определения и это будет выполняться в отдельном созданном потоке main, а основной служебный поток продолжит параллельно (если у ПК несколько ядер) выполнять свою перечисленную ранее работу. 3) в рассмотренном варианте при запуске нескольких скриптов сразу они начинают выполняться быстрее, чем с кодами в OnInit и body.
Пользователь
Сообщений: Регистрация: 12.05.2020
26.02.2026 14:27:05
Цитата
TGB написал: можно разместить в начале main в виде функции
Уточнение: если очень хотите, определение этой функции в переменной с именем отличным от OnInit можно разместить в области body, но вызвать только в начале main.
Пользователь
Сообщений: Регистрация: 15.06.2023
26.02.2026 20:28:52
TGB, Ну Вы опять, как школьник вызубривший урок наизусть, и ни грамма отступить от догмы, ну как тут не поверить, что перед нами отличник?
Ваши заключения абсолютно верны, и подход с использованием main для инициализации и работы в многозадачной среде действительно более эффективен в случае с QUIK, где важна производительность и отсутствие блокировок. Ну все этим пользуются, и я не исключение.
Но разве разговор про это? Ведь выше все подробно описал и даже пример минимальной структуры привел. Найдите там блокирующее body?
В кои веке, удалось найти что QUIK, что однозначно гарантирует. Ничего не произойдёт в скрипте пока OnInit не просигнализирует.
Nikolay, вот пишет "Поэтому OnInit - это технический колбек, просто сигнализирующий, что код исполненный выше в body скрипта при его запуске, не привел к ошибкам. Ничего более. Делается это один раз при запуске. Поэтому там и могут быть только сугубо технические действия именно при запуске скрипта и ничего более."
Это как фазовый гарантированный переход! Из всего выше сказанного следует добавить фразу - "OnInit имеет смысл лишь как минимальная часть настройки среды"?
Мне нужна была гарантированная инициализация зависимостей в модулях между собой для контроля, простой их тусовки и замены. Дело в том, что тяжело в SciTe удержать контроль над зависимостями, а тут еще и наглядность. Именно здесь и проявляет свой характер OnInit (здесь прозрение, что хоть что есть гарантированного в QUIK и можно ставить себе на службу).
Nikolay, Ваши замечания как всегда точны и логичны. Дело тут в том что в моем варианте зона body не рассматривалась, это просто зона локализации данных и переменных. Ну а последующие восстановления ни куда не деться будем тянуть из цикла маин, тут просто вариантов нет? Или я опять что то упускаю из вида?
Пользователь
Сообщений: Регистрация: 12.05.2020
26.02.2026 21:12:39
Цитата
VPM написал: Ваши заключения абсолютно верны, и подход с использованием main для инициализации и работы в многозадачной среде действительно более эффективен в случае с QUIK, где важна производительность и отсутствие блокировок. Ну все этим пользуются, и я не исключение.
и А тогда зачем вы хотите использовать OnInit, выполняемый в служебном потоке? Где вы видите разницу между функцией OnInit, выполняемой перед запуском main и точно такой же функцией, но с другим именем, выполняемой сразу вначале main (кроме того что в первом случае загружается служебный поток)?