Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 15.06.2023
03.01.2026 16:31:58
Согласен, картину страшную описали для авто торговли. Думал заменить подход, теперь верну.
Код
----- Получаем цену последней сделки с графика цены
local I = getNumCandles(tag.price)
while I==nil or I==0 do
sleep (100)
I = getNumCandles(tag.price)
end
if I<=4 then toLog(log, 'Недостаточно бар для продолжения работы ' .. tostring(I) ) return end
Вывод. Получение данных сводится к отдельной задаче. Корректность данных можно свести к общим подходам так как данные сводим в таблицы. Так? В реализации подхода рассмотренного мною, без машины состояний вообще не вариант. State Machine - система состояний (FSM), будет отслеживать состояние робота — в состоянии ли он торгов, или ждёт обновлений данных или ждёт сигналов и т.д.
Итог. правильная модель (минимальный фундамент): -> рынок — это функция времени (ввести понятие дискретности "квант времени"), -> стратегия — функция состояния (снимок, срез в заданный момент).
Принципы. 1. Источник истины — время 2. Минимальный ТФ > двигатель времени 3. Остальные ТФ > состояния мира 4. Стратегия никогда не входит в источники данных 5. Индикаторы — просто ещё один Source.
Каркас - итоговый поток:
DataSource v TimeEngine (tick - "квант времени") v WorldState (snapshot - снимок) v Strategy (signal) v SignalFilter v PositionManager v FSM v TradeAdapter v Equity
Каждый слой делает только своё. Попробую реализовать. Не могу сказать что упростил, но "руки чешутся" по пробовать.
Пользователь
Сообщений: Регистрация: 27.01.2017
03.01.2026 16:37:39
Радуйтесь, что в Lua нет понятия интерфейса, как концепции языка. Вот где начинается веселье. Но все же возникает ощущение, что Вы всегда уходите в процесс, вместо результата. Углубится и написать фреймворк иногда полезно. Но все же, обычно, это процесс долгий. А для получения результата сейчас лучше пойти намного более простым путем.
Пользователь
Сообщений: Регистрация: 15.06.2023
03.01.2026 16:44:20
Цитата
Nikolay написал: А для получения результата сейчас лучше пойти намного более простым путем.
А чем же он проще? Все модули нужны везде?
Пользователь
Сообщений: Регистрация: 15.06.2023
09.01.2026 12:25:30
Разделение ответственности, на примере создания индикатора. Единый принцип архитектуры торговой системы в QUIK.
Что общего в получении исторической информации в QUIK разными способами. Ответ очевиден - хранение информации в массивах данных и извлечения ее с помощью параметра "index".
1. OnCalculate. Задается на прямую QUIK. 2. getCandlesByIndex. Получаем количество свечей "==" local index = getNumCandles(CFG.tags.price), индекс текущей свечи. 3. CreateDataSource. По факту тоже самое, получаем уже размер массива Size() "== index". Зачем введены разные смыслы, остается загадкой?
Понимание этого, приводит к пониманию принципа построения единой архитектуры для разных источников данных. Принципиальная ИТОГОВАЯ АРХИТЕКТУРА: [ QUIK Chart ] -> [ Indicator/ CandlesByIndex/DataSource] -> [ SignalEngine ] -> [ Regime Filter ] -> [ Trading Core ].
Коротко почему это наиболее правильный подход: * безопасно для QUIK; * расширяемо; * легко диагностировать; * нет переобучения внутри источника (индикатора); * можно менять стратегию без переписывания индикаторов. Пример, индикатор источник данных, перенос ответственности.
Код
local path = getWorkingFolder()
local f;
local save = {
position = 0,
d = 0,
t = 0
}
Settings={}
Settings.Name = "*BreakoutIndicator"
Settings.period = 5
Settings.k = 1
Settings.wid = 0
function Init()
local Cached = dofile(path .. "\\cached.lua");
f = BreakoutIndicator()
Cached=nil
Settings.line = {
{Name = "BUY", Type = TYPE_TRIANGLE_UP, Width = 1, Color = RGB(0, 0, 255) },
{Name = "SELL", Type = TYPE_TRIANGLE_DOWN, Width = 1, Color = RGB(255, 0, 128) },
{Name = "save.upper", Type = TYPE_BAR , Width = 1, Color = RGB(0, 128, 255) },
{Name = "save.lower", Type = TYPE_BAR, Width = 1, Color = RGB(255, 0, 128) },
{Name = "SL", Type =TYPE_DASH, Width = 1, Color = RGB(255, 0, 0) }
}
return #Settings.line
end
function OnCalculate(index)
-- ====== ТЕКУЩИИ ЗНАЧЕНИЯ ======
local position1 = save.position
local d1 = save.d
local t1 = save.t
-- ====== Расчет ЗНАЧЕНИЙ ======
local breakout = f( index, Settings )
-- ====== Обработка ======
local newbar = (breakout and d1 ~= breakout.d or breakout.t ~= t1)
save.position = breakout and breakout.position
save.d = breakout and breakout.d
save.t = breakout and breakout.t
--====== RETURN LINES ======
if Settings.wid == 2 then
return nil,nil,nil,nil, breakout and breakout.volatilityRatio > 1 and breakout.volatilityRatio or nil
end
return breakout and (d1 ~= breakout.d or breakout.t ~= t1) and breakout.position == 1 and breakout.entryLevel or nil, -- BUY
breakout and (d1 ~= breakout.d or breakout.t ~= t1) and breakout.position == -1 and breakout.entryLevel or nil, -- SELL
breakout and breakout.position == 1 and breakout.upperPivot or nil,
breakout and breakout.position == -1 and breakout.lowerPivot or nil,
breakout and breakout.position ~= 0 and breakout.slLevel or nil
end
Пользователь
Сообщений: Регистрация: 27.01.2017
09.01.2026 12:56:05
Вы забыли данные из других источников. Индикаторы - это настолько малая часть и почти бесполезная часть, что её проще реализовать отдельно. Основное же использование - это скрипты, зачастую без интерфейса, а просто выполняющие расчеты, сохраняющие результаты в файлы. Так банально проще, т.к. анализ данных проще делать в других системах, в том же Python, а не в индикаторах Квика. Даже если это торгующий скрипт, то не ясно зачем ему график. График нужен человеку, для самоуспокоения, что всё правильно.
Еще важно, что иногда набор данных включает очень далекую историю, которую Квик просто не может предоставить. Например, данные баров выгружались за 5 лет. На основе этих данных строится свой источник данных с ТФ, например, равный 12 минут, 3 часа, 5 часов и т.д. Квик просто не дает такие данные. И уже этот источник поступает на вход алгоритма. Этот источник будет состоять из двух частей - кэшированные, верифицированные данные (что очень важно) и текущие данные реального времени.
Также не редко у брокера бывают проблемы с данными. Например у меня один брокер три дня подряд пропускал данные за прошлый день. День прошел - данные были. Наступает следующий день - данных за прошлый день нет. Переподключения, сбросы - ничего не решает вопрос, т.к. это ошибка на сервере брокера. И так каждый день, возникает скользящая дыра в данных. Можно сказать - зачем такой брокер - ответ прост: я такое наблюдал у многих брокеров.
Т.е. здесь вопрос консистентности данных - и это самый важный вопрос, намного важнее архитектуры. Точнее это требование очень сильно влияет на реализацию архитектуры. Т.к. если об этом не думать, то всегда возникнет ситуация когда алгоритм будет оперировать неактуальными данными и все его решения будут из-за этого под большим вопросом.
Пользователь
Сообщений: Регистрация: 15.06.2023
09.01.2026 14:50:56
Nikolay, Вы подняли очень важную проблему "вопрос консистентности данных", все мы сталкиваемся, так как повлиять на ситуацию целиком мы не можем, но должны учитывать эти особенности, должны применять какие то инженерные подходы в обеспечении безопасности. На мой взгляд проблема слишком большая, и требует отдельного обсуждения, может даже выноса в отдельную ветку (если еще не вынесена).
Но давайте по порядку.
Цитата
Nikolay написал: Вы забыли данные из других источников.
Я их умышленно обошел, и задачу свел к получению исторических данных из QUIK, предполагая что они уже есть, и показу единообразия в подходах. Запись в файл и чтения из него, здесь сводится к единой структуре данных. По сути заданию некоего индекса.
Расчет local breakout = f( index, Settings ) , если не нежны графики? выводим в скрипте f( index, Settings, ds ). По сути сменили источник с сохранением архитектуры, ровно то что Вы описали выше. Давно уже задался задачей сохранения архитектуры при изменяемых модулях.
Пользователь
Сообщений: Регистрация: 15.06.2023
09.01.2026 19:12:45
Концептуально, такую архитектуру можно свести к последовательным шагам взросления системы, в стиле боевого QUIK-safe. Важных, самостоятельных 4 - логических слоя, как в профессиональных торговых платформах:
ШАГ 1. Цель QUIK-safe. Приведение индикатора к QUIK-safe стилю, для QUIK это будет означать: 1. никаких глобальных переменных; 2. никаких "висящих" состояний; 3. чёткий lifecycle: "init -> bar -> return"; 4. "индикатор не знает про торговлю, только сигналы".
1.1. Новая контрактация индикатора (правильный подход). "Индикатор = детектор структуры рынка, не стратегии". Он должен: * принимать index, settings, datasourсe; * хранить локальный state; * возвращать расчетные значения (для индикатора только линии).
ШАГ 2. Встраивание в SignalEngine. Теперь "SignalEngine — мозг, индикатор — сенсор".
2.2. Использование данных от индикатора (и это уже по взрослому).
Код
function SignalEngine:updateFromBreakout(buy, sell, support, resistance, volRatio)
local factor = {}
-- Фактор направления
factor.breakout =
buy and 1 or
sell and -1 or
0
-- Фактор структуры
factor.structure =
support and 0.5 or
resistance and -0.5 or
0
-- Фактор волатильности
factor.volatility = clamp((volRatio or 0) - 1, -1, 1)
self.factors.breakout = factor
end
ШАГ 3. Indicator / TradingCore (разделение ответственности). Правило №1. "Индикатор не принимает решений".
local Trader = {}
function Trader:process(signalScore, regime)
if regime == "flat" then return end
if signalScore > 0.7 then
self:buy()
elseif signalScore < -0.7 then
self:sell()
end
end
ШАГ 4. Auto-Regime + фильтры.
4.1. Определение режима.
Код
function SignalEngine:detectRegime(vol, adx)
if vol < 0.7 and adx < 15 then
return "flat"
elseif adx > 25 then
return "trend"
else
return "transition"
end
end
function SignalEngine:filterFactorsByRegime(regime)
if regime == "flat" then
self.factors.breakout.breakout = 0
elseif regime == "transition" then
self.factors.breakout.breakout = 0.5
end
end
-- 4.3. Финальный Score
Код
function SignalEngine:calcScore()
local score = 0
for _, f in pairs(self.factors) do
score = score + (f.breakout or 0) + (f.structure or 0)
end
self.score = clamp(score, -1, 1)
return self.score
end
И ни какой магии, типа машинного обучения или нейросетей! А главное безопасное изменение и замена модулей. Хочется думать так, а жизнь покажет, что опять не учитываю.
Пользователь
Сообщений: Регистрация: 15.06.2023
10.01.2026 15:12:17
"А ларчик просто открывался!"
При описанным выше подходе "разделения ответственности", элегантно решаемся еще одна наедающая покоя задача. А именно передача параметра sec_coda из индикатора в луа скрипт. Конечно невозможно поучить во встроенных индикаторах, но всегда можно повторить его в луа, получить параметр и передать, как показано выше в моем примере.
Пользователь
Сообщений: Регистрация: 15.06.2023
20.01.2026 16:17:17
Цитата
VPM написал: Конечно невозможно поучить во встроенных индикаторах
Упёрся ровно в ключевое ограничение QUIK, и если решить его правильно — дальше всё встанет на свои места, очень чисто. Продлема: -> QUIK НЕ даёт напрямую получить параметры class_code и sec_code из легенды графика. -> Легенда — это "UI-строка", не идентификатор инструмента. Но не обходное решение нашлось, и оно рабочее. Задача. Нужна универсальная функция (алгоритм) утверждающая "это мой инструмент", должна быть безопасна при переключении графиков.
Это как раз типичный "quik-овский" момент, отвечающий принципу их "Ни чего не знаю, ни за что не отвечаю". Но все же он решается, довольно аккуратно и надёжно, "без костылей".
Важно. Нельзя здесь конечно ни чего утверждать однозначно. Так как строится данный алгоритм на гипотезе утверждающей: "Легенда графика всегда содержит short_name, но с хвостом вида: " [Price]" / " [Volume]" / " [ATR]" и т.п.". Есть просто надежда, что еще какое то время, она просуществует, но ведь существовала же до сих пор. Возможно это очередной "велосипед", и кто то решал уже подобную задачу, но я ни чего не нашел, поэтому будет "Трехколесный".
Ниже — по порядку.
Что у нас есть? SecurityInfo.short_name = "Сургнфгз-п" legenda = "Сургнфгз-п [Price]"
Правильная логика сравнения. Нужно убрать всё после '[' и сравнить "чистые" строки.
if short_name == legend_clean then -- это нужный график end
Это единственный надёжный путь. Стратегия - обратное сопоставление: legend -> short_name -> sec_code + class_code То есть: 1. Берём legend 2. Извлекаем short_name 3. Ищем инструмент, у которого SecurityInfo.short_name совпадает 4. Запоминаем class_code + sec_code
ВАЖНО: коллизии short_name. Это реальная проблема: * GAZP (акции) * GAZP-12.25 (фьючерс) Как защититься (думаю), хранить список, а не один элемент.
Это значит, что мы можем построить полностью автоматическую библиотеку инструментов:
getClassesList() v getClassInfo(class) v getClassSecurities(class) v getSecurityInfo(class, sec)
И уже по легенде графика восстанавливать: legend > short_name > sec_code + class_code.
Пользователь
Сообщений: Регистрация: 15.06.2023
24.01.2026 17:24:13
"Разделяй и Властвуй (лат. divide et impera)".
Сам себе удивляюсь. Разработка концепции "Разделение ответственности", пример выше, привела к осознанию, того что можно строить "Операционную Систему". Да, да и я не описался. Скриптовый язык луа, для торговой системы QUIK, можно строить торговую "ОС" ни прибегая к стороним программам. НУ Хорошо пусть будет платформа. Так я проверил отвечает принципам ОС. Просто в шоке! А где специалисты? Хоть кто то бы на это намекнул?
Пользователь
Сообщений: Регистрация: 30.01.2015
24.01.2026 17:37:03
, Вы еще больше удивитесь, когда узнаете, что из кирпичей можно построить стены дома и, даже сложить печку в доме том. -------------- А самое прикольное, Вы никогда бы не догадались, земля -круглая, а не плоская. -------------- В мире еще много удивительного и не познанного.
Пользователь
Сообщений: Регистрация: 30.01.2015
24.01.2026 17:39:26
А если серьезно, то попробуйте написать на луа какой-нибудь драйвер устройства, что является обязательным элементом OC.
Пользователь
Сообщений: Регистрация: 15.06.2023
24.01.2026 18:02:41
Цитата
nikolz написал: В мире еще много удивительного и не познанного.
Соглашусь с Вами!
Цитата
nikolz написал: А самое прикольное, Вы никогда бы не догадались, земля -круглая, а не плоская.
А Вы спросите у ГЕОЛОГОВ - специалистов? Это просто стереотип!
Цитата
nikolz написал: Вы еще больше удивитесь, когда узнаете, что из кирпичей можно построить стены дома и, даже сложить печку в доме том.
Не не не удивляйтесь, в степях строили "мазанки", в Сибири из бруса, кому удавалось, в основном кругляк, боюсь сказать какие материалы применяются в современном мире.
Пользователь
Сообщений: Регистрация: 15.06.2023
24.01.2026 18:09:10
Цитата
nikolz написал: А если серьезно, то попробуйте написать на луа какой-нибудь драйвер устройства, что является обязательным элементом OC.
Не я тоже возмущен почему, у "Условного Маркет Мейкера" скорость 10 мк.сек. а унас 200 мл.сек., а кому повезет, ну хорошо кто следит, 100 мл.сек. И это ни сколько не отменяет тот факт что код может отвечать принципам и требованиям ОС?
написал: А если серьезно, то попробуйте написать на луа какой-нибудь драйвер устройства, что является обязательным элементом OC.
Не я тоже возмущен почему, у "Условного Маркет Мейкера" скорость 10 мк.сек. а унас 200 мл.сек., а кому повезет, ну хорошо кто следит, 100 мл.сек. И это ни сколько не отменяет тот факт что код может отвечать принципам и требованиям ОС?
Вы заблуждаетесь относительно "у нас" На бирже торгует брокер, а не его клиенты. QUIK - это программа подачи заявок брокеру, а не для прямой торговли на бирже. -------------------- относительно мксек ММ и mkсек QUIK. млсек - это время прохождение данных от Вашего ПК до брокера по интернету. Разместите свой ПК в дата центре на M9 и у вас тоже будут мкс.
Пользователь
Сообщений: Регистрация: 15.06.2023
25.01.2026 10:07:55
nikolz, "У нас" - это те кто пользуется инфраструктурой Квика, и я рассуждаю о разворачивании на ее основе торговой платформы на луа, а не вообще. Не нужны, тут ни какие драйвера, кроме того что предоставляет квик. Разворачивая торговую систему, приводя в порядок архитектуру и дописывая модули, мне это напомнило порядок ОС (как пример DOS). Зная это, можно изначально закладывать архитектуру как промышленную. Сложно но можно!
Пользователь
Сообщений: Регистрация: 30.01.2015
25.01.2026 10:20:47
Цитата
VPM написал: , "У нас" - это те кто пользуется инфраструктурой Квика, и я рассуждаю о разворачивании на ее основе торговой платформы на луа, а не вообще. Не нужны, тут ни какие драйвера, кроме того что предоставляет квик. Разворачивая торговую систему, приводя в порядок архитектуру и дописывая модули, мне это напомнило порядок ОС (как пример DOS). Зная это, можно изначально закладывать архитектуру как промышленную. Сложно но можно!
а что такое "промышленная архитектура"? Откуда вы взяли этот термин? если сами придумали, то дайте определение.
Пользователь
Сообщений: Регистрация: 30.01.2015
25.01.2026 10:22:36
Цитата
VPM написал: Концептуально, такую архитектуру можно свести к последовательным шагам взросления системы, в стиле боевого QUIK-safe. Важных, самостоятельных 4 - логических слоя, как в профессиональных торговых платформах:
2.2. Использование данных от индикатора (и это уже по взрослому).
Код
И ни какой магии, типа машинного обучения или нейросетей! А главное безопасное изменение и замена модулей. Хочется думать так, а жизнь покажет, что опять не учитываю. ::
Прикольно, что вы написали. И это не по взрослому, а по дилетанскому. -----------------------
Пользователь
Сообщений: Регистрация: 30.01.2015
25.01.2026 10:28:28
объясняю популярно, для тех, кто изобретает велосипед. ------------------- Если Вы пользуете встроенные графики , то проще и быстрее не читать их в скрипте и потом выковыривать название индикатора из легенды, а записать из значения либо параметры, включая код индикатора , интервал в файл. В скрипте прочитать эти параметры при их смене на графике (если это график с якорем). либо писать индикаторы в файлы с именем инструмента и читать в скрипте соответствующий файл.
Пользователь
Сообщений: Регистрация: 15.06.2023
25.01.2026 10:55:49
Видите в чем дело, велосипед изобретен за долго до меня, я лишь кручу педали .
Изначально задача, сводилась быстро собрать стратегию для проверки идеи, которая должна была выносить на график множество линий, добавьте сюда мульти тайм фрейм. Только по этому и был выбран этот подход через getCandlesByIndex. Попробуйте сделайте со своим алгоритмом, его прочитать сложно, не то что решать подобную задачу.
Вторая логично вытекающая задача сделать его универсальным, в формате "Советника", с возможностью расширения архитектуры до боевой. Вот что обернулось быстро я частично описал.
Пользователь
Сообщений: Регистрация: 15.06.2023
25.01.2026 11:24:49
Цитата
nikolz написал: Прикольно, что вы написали. И это не по взрослому, а по дилетанскому.
Ну наконец то "ПрофЭсор" разбушевался.
Цитата
nikolz написал: а что такое "промышленная архитектура"? Откуда вы взяли этот термин? если сами придумали, то дайте определение.
Это видимо то что Вы пропустили в детском саду?
Пользователь
Сообщений: Регистрация: 15.06.2023
25.01.2026 13:56:45
nikolz, да Вы не обижайтесь. Ведь подобный стиль общения задаете именно Вы, я же предпочитаю "инженерный", если не прав или есть ошибки, так и приводите доводы. А лепет ребенка из песочницы, можно использовать когда все вокруг шутят.
Ответ на Ваш вопрос очевиден, "Промышленный" - это то чем пользуются хэдж - фонды, или закрытые отделы в банках под управлением у которых находятся собственные средства, а не клиентов. И такие торговые платформы отвечают не которым стандартом, где то выше уже какие то стандарты приводил.
Для своих целей собрал из них 10 бальную шкалу и проверяю на соответствие архитектуре свои скрипты. Уровень описанный выше соответствует в моей шкале началу 5 уровня, до которого зачастую не дорастают большинство любительских скриптов, заканчиваясь принятием решения при пересечениях индикаторов или что то в этом роде. Модульность позволяет разворачивать программу до нужного уровня. Разделение ответственности по кругу задач, еще один важный момент в подобном подходе, который у меня страдает.