Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 30.01.2015
17.11.2025 16:39:15
Повторю еще раз. Подписка делается однократно при запуске робота. Т е она не влияет на его работу никак.
Пользователь
Сообщений: Регистрация: 15.06.2023
17.11.2025 16:57:39
nikolz, И в примере выше подписка сделана один раз, перед созданием потока луа, более того подписка то прошла, ответ true? А индекс = 0? Речь идет о тиковых данных. А количество инструментов тут не причем, разве только все усугубить нужно, но проблематика прекрасна видна на одном инструменте. Я ведь ни чего не придумал, нет тут от "себятины":
"param – необязательный параметр. Если параметр не задан, то заказываются данные на основании таблицы обезличенных сделок, если задан – данные по этому параметру.
Функция возвращает таблицу data_source в случае успешного завершения. Если указан неверный код класса, код инструмента, интервал или параметр, то возвращается «nil». При этом error_desc содержит описание ошибки.
Функцию CreateDataSource можно использовать только внутри функций main() и callback."
local WORKING_FLAG = true local class_code, sec_code = "TQBR", "SBER"--"SPBFUT", "RIZ5"
-- Простые переменные для данных local last_bid = "N/A" local last_offer = "N/A" local last_candle = nil local ds = nil
-- Прямые callback функции без сложных систем function OnParam(class_code, sec_code) if class_code == "SPBFUT" and sec_code == "RIZ5" then last_bid = getParamEx(class_code, sec_code, "bid").param_value or "N/A" last_offer = getParamEx(class_code, sec_code, "offer").param_value or "N/A" end end
function OnQuote(class_code, sec_code) -- Просто получаем стакан, но не обрабатываем чтобы не нагружать end
-- Callback для свечей local function onCandleUpdate(index) if ds and index = 30 then message(string.format("Статус: %d итераций, Bid=%s, Offer=%s", iteration, last_bid, last_offer))
local result = ds:SetEmptyCallback() -- SetUpdateCallback(onCandleUpdate) -- message("получать данные с сервера, размер: " .. ds:Size() ..' '..tostring(result) ) if ds:Size() and ds:Size()>0 then message(string.format("Свеча: O=%.1f H=%.1f L=%.1f C=%.1f", ds:O(ds:Size()), ds:H(ds:Size()), ds:L(ds:Size()), ds:C(ds:Size()) )) end
last_print_time = current_time end
-- ВАЖНО: Даем время QUIK обработать события sleep(1000) -- 1 СЕКУНДА - достаточно для избежания "Превышения времени" end
message("Робот остановлен") end
-- Простые обработчики остановки function OnStop() WORKING_FLAG = false if ds then ds:Close() end message("Робот остановлен по команде") end function OnClose() WORKING_FLAG = false if ds then ds:Close() end end function OnInit() message("Скрипт инициализирован") end
У Вас в скрипте ошибка Надо подписываться не так CreateDataSource(class_code, sec_code, 1,"last") а так CreateDataSource(class_code, sec_code, 1)
Пользователь
Сообщений: Регистрация: 30.01.2015
17.11.2025 17:12:16
, Поясняю. "Все смешалось, кони, люди" -------------------- Вы подписываетесь на параметр из ТТП а ждете свечи.Вы уж определитесь. ------------------------------------------------------------------------ "А, ты за большевиков, али за коммунистов?"
VPM написал: "param – необязательный параметр. Если параметр не задан, то заказываются данные на основании таблицы обезличенных сделок, если задан – данные по этому параметру.
Пользователь
Сообщений: Регистрация: 15.06.2023
17.11.2025 17:24:18
VPM, А чему Вы удивляетесь? Это не я придумал? Таланты в стране, ни чего не поделаешь.
Цитата
nikolz написал: "А, ты за большевиков, али за коммунистов?"
Я за индивидуальных ТРЕЙДЕРОВ, которым нужны простые рабочие варианты. По сути если обобщить получение данных из квик, есть 2 основных варианта: 1. с помощью параметров (class_code, sec_code) 2. firmid, client_code.
А есть особые случаи!!!
Пользователь
Сообщений: Регистрация: 15.06.2023
17.11.2025 17:31:32
Цитата
Nikolay написал: Т.е. кидаем запросы атомарно, создаем задачу для проверки ответа
Поясните?
Подпиской создали таблицу интерфейса для получения данных, а дальше в потоке луа получаем ds:O(I).
Подпиской создали таблицу интерфейса для получения данных, а дальше в потоке луа получаем ds:O(I).
Вы так восторгались корутинами, что подход запрос -> чтение ответа через вызов-проверка должен быть очевиден. Самое главное - у нас нет понятия сколько времени займет приход ответа. Может 100 млс, а может 5 минут. Поэтому нельзя использовать любые подходы с циклами ожидания с какой-то заданной величиной. Да, необходима какая-то отсечка по времени, чтобы отсекать проблемные запросы.
Вот наивная реализация такого подхода. Специально добавил счетчики как задачи из прошлого примера, чтобы было видно, что можно переключаться между разного рода задачами. Наивная реализация потому что в реальности необходимо учитывать, что задачи могут быть вытесняющие, с приоритетами, последовательными, с многими внутренними шагами и т.д.
Также стоит учитывать, что простая проверка размера ds:Size() - это слишколм просто. Я проверяю время последнего бара и время последней сделки, чтобы избежать порционного получения данных, когда размер уже не 0, но он еще не последний.
Код
local is_run = true
if _G.message then
print = function(...) _G.message(table.concat({...}, ", ")) end
end
local task_queue = {}
local function AddActionResult(task, is_done, is_error, mes)
if not task then return end
if is_done then
task.done = true
task.error = false
task.response_mes = tostring(mes or '')
elseif is_error then
task.done = false
task.error = true
task.response_mes = tostring(mes or '')
end
end
local function Process(task)
if not task then return end
if not task.process then return end
if not task.in_progress then task.in_progress = true end
AddActionResult(task, task.process())
end
local function Dispatch()
if not is_run then return end
local count = 1
while is_run and #task_queue > 0 do
local task = task_queue[count]
if task and not (task.done or task.error or task.stop) then
Process(task)
end
if task and (task.done or task.error or task.stop) then
task.in_progress = false
local response_mes = ''
if not task.silent or not task.done then
if task.done then
response_mes = 'Задача выполнена успешно: '..tostring(task.description)
elseif task.error then
response_mes = 'Задача не выполнена: '..tostring(task.description)..', по причине: '
elseif task.stop then
response_mes = 'Задача остановлена: '..tostring(task.description)
end
print(response_mes)
if (task.response_mes or '')~= '' then print('\t'..(task.response_mes or ''))
end
if type(task.done_callback) == 'function' then task.done_callback(task) end
if type(task.err_callback) == 'function' then task.err_callback(task) end
end
table.sremove(task_queue, count)
count = count - 1
end
if count + 1 >= #task_queue then
if #task_queue == 0 then print('Очередь задач пуста') end
return
end
count = count >= #task_queue and 1 or count + 1
end
end
local function create_ds(ctx, done_callback, err_callback, wait_time)
if not ctx then return end
local ds, err = _G.CreateDataSource(ctx.class_code, ctx.sec_code, ctx.interval)
if not ds then
print(err, 3)
return
end
ds.interval = ctx.interval
ds.class_code = ctx.class_code
ds.sec_code = ctx.sec_code
ds.description = ctx.class_code..'|'..ctx.sec_code..', интервал '..tostring(ds.interval)
ds.done_request = false
ctx.ds = ds
print('Заказ данных '..ds.description)
wait_time = wait_time or 60
local lt = os.time()
local task = {description = 'Заказ данных '..ds.description, ctx = ctx}
task.process = function()
if os.time() - lt >= wait_time then return false, true, 'Не удалось инициализировать инструменты за время '..tostring(wait_time)..'сек.' end
local size = ds:Size()
if size == 0 then return end
ds.done_request = true
return true, false, 'Получены бары '..ds.description..', размер: '..tostring(size)
end
task.done_callback = done_callback
task.err_callback = err_callback
task_queue[#task_queue+1] = task
end
local function counter(i, limit)
local x = 0
local task = {description = 'Счетчик до '..tostring(limit)}
task.process = function()
x = x + 1
if x >= limit then
return true, false, string.format('%i: Счетчик до %i завершен', i, limit)
end
end
task_queue[#task_queue+1] = task
end
local sec_list = {}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'SBER', interval = 1}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'SBER', interval = 2}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'SBER', interval = 5}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'SBER', interval = 10}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'SBER', interval = 20}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'GAZP', interval = 10}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'GAZP', interval = 2}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'GAZP', interval = 5}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'LKOH', interval = 5}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'FEES', interval = 5}
sec_list[#sec_list+1] = {class_code = 'SPBFUT', sec_code = 'SiZ5', interval = 5}
sec_list[#sec_list+1] = {class_code = 'SPBFUT', sec_code = 'RIZ5', interval = 5}
sec_list[#sec_list+1] = {class_code = 'SPBFUT', sec_code = 'RIZ5', interval = 15}
sec_list[#sec_list+1] = {class_code = 'SPBFUT', sec_code = 'SRZ5', interval = 30}
function main()
for i = 1, #sec_list do
counter(i, i*2)
create_ds(sec_list[i])
end
while is_run do
Dispatch()
sleep(100)
end
end
function OnStop()
is_run = false
end
В итоге можно заказать много потоков, но пока не принято решение, что данные получены, есть задача проверки. И не важно сколько времени это займет. Также и с транзакциями. Например, сегодя с утра один из брокеров обрабатывал транзакции 2 минуты, хотя обычно менее секунды. Но т.к. нельзя сказать сколько времени займет процедура запрос-ответ, то и просто ждать 30 секунд нельзя. Да и 2 минуты нельзя, т.к. завтра может придется ждать 5 минут.
Пользователь
Сообщений: Регистрация: 15.06.2023
18.11.2025 15:40:43
Запустил у себя, лог:
Код
Тип Дата Время Сообщение
1 18.11.2025 15:26:30 Заказ данных TQBR|SBER, интервал 1
2 18.11.2025 15:26:30 Заказ данных TQBR|SBER, интервал 2
3 18.11.2025 15:26:30 Заказ данных TQBR|SBER, интервал 5
4 18.11.2025 15:26:30 Заказ данных TQBR|SBER, интервал 10
5 18.11.2025 15:26:30 Заказ данных TQBR|SBER, интервал 20
6 18.11.2025 15:26:30 Заказ данных TQBR|GAZP, интервал 10
7 18.11.2025 15:26:30 Заказ данных TQBR|GAZP, интервал 2
8 18.11.2025 15:26:30 Заказ данных TQBR|GAZP, интервал 5
9 18.11.2025 15:26:30 Заказ данных TQBR|LKOH, интервал 5
10 18.11.2025 15:26:30 Заказ данных TQBR|FEES, интервал 5
11 18.11.2025 15:26:30 Заказ данных SPBFUT|SiZ5, интервал 5
12 18.11.2025 15:26:30 Заказ данных SPBFUT|RIZ5, интервал 5
13 18.11.2025 15:26:30 Заказ данных SPBFUT|RIZ5, интервал 15
14 18.11.2025 15:26:30 Заказ данных SPBFUT|SRZ5, интервал 30
15 18.11.2025 15:26:30 Задача выполнена успешно: Заказ данных SPBFUT|SiZ5, интервал 5
16 18.11.2025 15:26:30 Получены бары SPBFUT|SiZ5, интервал 5, размер: 23300
17 18.11.2025 15:26:30 Задача выполнена успешно: Заказ данных SPBFUT|RIZ5, интервал 5
18 18.11.2025 15:26:30 Получены бары SPBFUT|RIZ5, интервал 5, размер: 33461
19 18.11.2025 15:26:30 Задача выполнена успешно: Заказ данных SPBFUT|RIZ5, интервал 15
20 18.11.2025 15:26:30 Получены бары SPBFUT|RIZ5, интервал 15, размер: 12794
21 18.11.2025 15:26:30 Задача выполнена успешно: Счетчик до 2
22 18.11.2025 15:26:30 1: Счетчик до 2 завершен
23 18.11.2025 15:26:30 Задача выполнена успешно: Заказ данных TQBR|SBER, интервал 1
24 18.11.2025 15:26:30 Получены бары TQBR|SBER, интервал 1, размер: 10499
25 18.11.2025 15:26:30 Задача выполнена успешно: Заказ данных TQBR|SBER, интервал 2
26 18.11.2025 15:26:30 Получены бары TQBR|SBER, интервал 2, размер: 3255
27 18.11.2025 15:26:30 Задача выполнена успешно: Заказ данных TQBR|SBER, интервал 5
28 18.11.2025 15:26:30 Получены бары TQBR|SBER, интервал 5, размер: 6302
29 18.11.2025 15:26:30 Задача выполнена успешно: Заказ данных TQBR|SBER, интервал 10
30 18.11.2025 15:26:30 Получены бары TQBR|SBER, интервал 10, размер: 3052
31 18.11.2025 15:26:30 Задача выполнена успешно: Заказ данных TQBR|SBER, интервал 20
32 18.11.2025 15:26:30 Получены бары TQBR|SBER, интервал 20, размер: 3027
33 18.11.2025 15:26:30 Задача выполнена успешно: Заказ данных TQBR|GAZP, интервал 10
34 18.11.2025 15:26:30 Получены бары TQBR|GAZP, интервал 10, размер: 3052
35 18.11.2025 15:26:30 Задача выполнена успешно: Заказ данных TQBR|GAZP, интервал 2
36 18.11.2025 15:26:30 Получены бары TQBR|GAZP, интервал 2, размер: 3255
37 18.11.2025 15:26:30 Задача выполнена успешно: Заказ данных TQBR|GAZP, интервал 5
38 18.11.2025 15:26:30 Получены бары TQBR|GAZP, интервал 5, размер: 6302
39 18.11.2025 15:26:30 Задача выполнена успешно: Заказ данных TQBR|LKOH, интервал 5
40 18.11.2025 15:26:30 Получены бары TQBR|LKOH, интервал 5, размер: 3103
41 18.11.2025 15:26:30 Задача выполнена успешно: Заказ данных TQBR|FEES, интервал 5
42 18.11.2025 15:26:30 Получены бары TQBR|FEES, интервал 5, размер: 3101
43 18.11.2025 15:26:30 Задача выполнена успешно: Счетчик до 4
44 18.11.2025 15:26:30 2: Счетчик до 4 завершен
45 18.11.2025 15:26:30 Задача выполнена успешно: Счетчик до 6
46 18.11.2025 15:26:30 3: Счетчик до 6 завершен
47 18.11.2025 15:26:30 Задача выполнена успешно: Счетчик до 8
48 18.11.2025 15:26:30 4: Счетчик до 8 завершен
49 18.11.2025 15:26:31 Задача выполнена успешно: Счетчик до 10
50 18.11.2025 15:26:31 5: Счетчик до 10 завершен
51 18.11.2025 15:26:31 Задача выполнена успешно: Счетчик до 12
52 18.11.2025 15:26:31 6: Счетчик до 12 завершен
53 18.11.2025 15:26:31 Задача выполнена успешно: Счетчик до 14
54 18.11.2025 15:26:31 7: Счетчик до 14 завершен
55 18.11.2025 15:26:31 Задача выполнена успешно: Счетчик до 16
56 18.11.2025 15:26:31 8: Счетчик до 16 завершен
57 18.11.2025 15:26:31 Задача выполнена успешно: Счетчик до 18
58 18.11.2025 15:26:31 9: Счетчик до 18 завершен
59 18.11.2025 15:26:32 Задача выполнена успешно: Счетчик до 20
60 18.11.2025 15:26:32 10: Счетчик до 20 завершен
61 18.11.2025 15:26:32 Задача выполнена успешно: Счетчик до 22
62 18.11.2025 15:26:32 11: Счетчик до 22 завершен
63 18.11.2025 15:26:32 Задача выполнена успешно: Счетчик до 24
64 18.11.2025 15:26:32 12: Счетчик до 24 завершен
65 18.11.2025 15:26:32 Задача выполнена успешно: Счетчик до 26
66 18.11.2025 15:26:32 13: Счетчик до 26 завершен
67 18.11.2025 15:26:32 Задача выполнена успешно: Счетчик до 28
68 18.11.2025 15:26:32 14: Счетчик до 28 завершен
69 18.11.2025 15:26:33 Задача выполнена успешно: Заказ данных SPBFUT|SRZ5, интервал 30
70 18.11.2025 15:26:33 Получены бары SPBFUT|SRZ5, интервал 30, размер: 3013
71 18.11.2025 15:26:33 Очередь задач пуста
Все задачи Выполнены. Но ведь это тот же самый вариант? Отличие Ваш скрипт ждет подписки (скорее подписка чем получение), а наш блокирует исполнение, привлекая внимание пользователя, передавая ему управление. (Смысл - Допустим, заблокируй не рабочий тикер, и запусти по новой). В Вашем подходе будет все висеть пока все тикеры не подпишутся. Допустим сменился тикер у фьючерса, пользователь в надежде что все работает в автомате? (Это просто частный случай, Вы сами утверждаете, что ситуация мало прогнозируема). А основной поток луа (цикл) остается не рабочем? Тогда уж, все исполнять нужно в основном цикле, с фиксацией состояния подписки, чтоб не повторять запросы?
Пользователь
Сообщений: Регистрация: 27.01.2017
18.11.2025 16:11:38
Я не понял комментарий. Есть контекст. В нем открываем ds и запускаем задачу. У ds есть признак done_request. В очередь задач кидаем функцию проверки размера. Эта задача ничего не блокирует, т.е. можно в рамках контекста выполнять другие задачи, например, обработать сделки, совершенные пользователем, команды интерфейса и т.д. А процедура, которая зависит от ds, просто ждет когда признак done_request изменится.
Код
В Вашем подходе будет все висеть пока все тикеры не подпишутся
Что будет висеть? Вызов Dispatch() - это один из методов main, коих может быть бесчисленно число. Т.е. обработали задачи, какие-то завершились, а какие-то нет. Но это не значит, что висит, просто переходим к дальнейшему выполнению main, после возврата из Dispatch(), т.к. он не блокирующий. В нем просто перебираем накопившиеся задачи и выходим после прохода по очереди.
Пользователь
Сообщений: Регистрация: 15.06.2023
18.11.2025 16:20:09
Теперь я ничего не понял? Вот это место:
Код
for i = 1, #sec_list do
counter(i, i*2)
create_ds(sec_list[i])
end
Пока цикл for i = 1, #sec_list do не завершен, подвешен не исполняется основной цикл while is_run do Так?
while is_run do
Dispatch()
sleep(100)
end
Пользователь
Сообщений: Регистрация: 27.01.2017
18.11.2025 16:26:56
Цикл for i = 1, #sec_list do - это просто пример организации заказа данных. Он выполняется однократно и ничего не блокирует. С тем же успехом можно было бы вставить где-то в другом месте вызов заказа данных.
Единственно, что можно изменить - это более ранний выход из процедуры обработки задач, чтобы проход по очереди задач был однократный за вызов.
count=count+1 ifcount>#task_queuethen if#task_queue==0thenprint('Очередь задач пуста') end return end
Пользователь
Сообщений: Регистрация: 15.06.2023
18.11.2025 16:41:43
Так и в нашем упрощённом варианте предусмотрен выход из цикла подписки. Либо передача управления в основной цикл (поток луа), либо вообще остановка скрипта, так как данных нет, обрабатывать дальше не чего. Но это уже на усмотрение архитектуры решаемых задач.
Переписал с помощью корутин, здесь их применение наиболее показательно.
Код
local is_run = true
if _G.message then
print = function(...) _G.message(table.concat({...}, ", ")) end
end
-- Таблица для управления корутинами
local coroutines = {}
local coroutine_queue = {}
-- Функция для добавления корутины в очередь
local function create_task(process_func, description, done_callback, err_callback, ctx)
local task = {
coroutine = coroutine.create(process_func),
description = description,
done_callback = done_callback,
err_callback = err_callback,
ctx = ctx,
done = false,
error = false
}
table.insert(coroutine_queue, task)
return task
end
-- Функция для выполнения корутин (аналог Dispatch)
local function ProcessCoroutines()
if #coroutine_queue == 0 then
return
end
local i = 1
while i <= #coroutine_queue do
local task = coroutine_queue[i]
if task.done or task.error then
-- Задача завершена, удаляем из очереди
if not task.silent then
if task.done then
print('Задача выполнена успешно: '..tostring(task.description))
elseif task.error then
print('Задача не выполнена: '..tostring(task.description))
end
if task.response_mes and task.response_mes ~= '' then
print('\t'..task.response_mes)
end
end
if task.done and task.done_callback then
task.done_callback(task.ctx, task.response_mes)
elseif task.error and task.err_callback then
task.err_callback(task.ctx, task.response_mes)
end
table.remove(coroutine_queue, i)
else
-- Выполняем корутину
local status = coroutine.status(task.coroutine)
if status == 'dead' then
task.done = true
elseif status == 'suspended' then
local success, result = coroutine.resume(task.coroutine)
if not success then
task.error = true
task.response_mes = tostring(result)
elseif coroutine.status(task.coroutine) == 'dead' then
task.done = true
task.response_mes = tostring(result)
end
end
i = i + 1
end
end
if #coroutine_queue == 0 then
print('Очередь корутин пуста')
end
end
-- Асинхронная версия создания источника данных
local function create_ds(ctx, done_callback, err_callback, wait_time)
if not ctx then return end
wait_time = wait_time or 60
local start_time = os.time()
local process = function()
local ds, err = _G.CreateDataSource(ctx.class_code, ctx.sec_code, ctx.interval)
if not ds then
error(err)
end
ds.interval = ctx.interval
ds.class_code = ctx.class_code
ds.sec_code = ctx.sec_code
ds.description = ctx.class_code..'|'..ctx.sec_code..', интервал '..tostring(ds.interval)
ds.done_request = false
ctx.ds = ds
print('Заказ данных '..ds.description)
while os.time() - start_time < wait_time do
local size = ds:Size()
if size > 0 then
ds.done_request = true
coroutine.yield(true, false, 'Получены бары '..ds.description..', размер: '..tostring(size))
return
end
coroutine.yield(false, false, 'Ожидание данных...')
end
error('Не удалось инициализировать инструменты за время '..tostring(wait_time)..' сек.')
end
create_task(
process,
'Заказ данных '..ctx.class_code..'|'..ctx.sec_code,
done_callback,
err_callback,
ctx
)
end
-- Асинхронная версия счетчика
local function counter(i, limit)
local x = 0
local process = function()
while x < limit do
x = x + 1
if x >= limit then
coroutine.yield(true, false, string.format('%i: Счетчик до %i завершен', i, limit))
return
else
coroutine.yield(false, false, string.format('%i: Счетчик на %i из %i', i, x, limit))
end
end
end
create_task(process, 'Счетчик до '..tostring(limit))
end
local sec_list = {
{class_code = 'TQBR', sec_code = 'SBER', interval = 1},
{class_code = 'TQBR', sec_code = 'SBER', interval = 2},
{class_code = 'TQBR', sec_code = 'SBER', interval = 5},
{class_code = 'TQBR', sec_code = 'SBER', interval = 10},
{class_code = 'TQBR', sec_code = 'SBER', interval = 20},
{class_code = 'TQBR', sec_code = 'GAZP', interval = 10},
{class_code = 'TQBR', sec_code = 'GAZP', interval = 2},
{class_code = 'TQBR', sec_code = 'GAZP', interval = 5},
{class_code = 'TQBR', sec_code = 'LKOH', interval = 5},
{class_code = 'TQBR', sec_code = 'FEES', interval = 5},
{class_code = 'SPBFUT', sec_code = 'SiZ5', interval = 5},
{class_code = 'SPBFUT', sec_code = 'RIZ5', interval = 5},
{class_code = 'SPBFUT', sec_code = 'RIZ5', interval = 15},
{class_code = 'SPBFUT', sec_code = 'SRZ5', interval = 30},
}
function main()
for i = 1, #sec_list do
counter(i, i*2)
create_ds(sec_list[i])
end
-- Основной цикл выполнения
while is_run do
ProcessCoroutines() -- Обрабатываем корутины вместо задач
sleep(100)
end
end
function OnStop()
is_run = false
end
Пользователь
Сообщений: Регистрация: 15.06.2023
18.11.2025 16:56:14
Кажется я уловил идею, цикл до основного потока это отдельная не блокирующая основной цикл задача! Супер.
Пользователь
Сообщений: Регистрация: 27.01.2017
18.11.2025 16:59:33
Цитата
VPM написал: Так и в нашем упрощённом варианте предусмотрен выход из цикла подписки.
Может я, конечно, невнимательно смотрел примеры, но все одни предполагали ожидание через цикл. Т.е. заказали - и тут же ждем. В итоге заказ 100 источников растягивается во времени, т.к. последний будет заказан только через 100*время ожидания. Что долго. Подход заказали все сразу в цикле и потом ждем все в цикле лучше, но тоже заблокирует скрипт пока ждем циклом. Здесь же заказ всех сразу. И периодически просто атомарно "тыкаем палочкой" на предмет данных. Пришли - можем делать что-то с данными. Нет - переходим к другим задачам, а эта пока путь остается. Т.е. ожидание не мешает другим задачам не связанным с данными, коих может быть много в скрипте.
Пользователь
Сообщений: Регистрация: 15.06.2023
18.11.2025 17:03:21
Не, не Вы правы это я невнимателен, не сразу рассмотрел идею. "Доходит до утки, на третьи сутки" из этой области.
Пользователь
Сообщений: Регистрация: 15.06.2023
18.11.2025 17:18:08
С этим разобрались, идея классная и правильная! Но мой эксперимент сводился к получению данных с использованием 4 необязательного параметра, задача получить ("LAST", "NUMCONTRACTS", "NUMBIDS", "NUMOFFERS", "BIDDEPTHT", "OFFERDEPTHT") в виде свечей и применить к ним технический анализ?
написал: Так и в нашем упрощённом варианте предусмотрен выход из цикла подписки.
Может я, конечно, невнимательно смотрел примеры, но все одни предполагали ожидание через цикл. Т.е. заказали - и тут же ждем. В итоге заказ 100 источников растягивается во времени, т.к. последний будет заказан только через 100*время ожидания. Что долго. Подход заказали все сразу в цикле и потом ждем все в цикле лучше, но тоже заблокирует скрипт пока ждем циклом. Здесь же заказ всех сразу. И периодически просто атомарно "тыкаем палочкой" на предмет данных. Пришли - можем делать что-то с данными. Нет - переходим к другим задачам, а эта пока путь остается. Т.е. ожидание не мешает другим задачам не связанным с данными, коих может быть много в скрипте.
А если поставить колбек то и тыкать не надо. Тратим время лишь когда приди данные. Что не так?
Пользователь
Сообщений: Регистрация: 30.01.2015
18.11.2025 17:36:15
Круто нагородили.
Пользователь
Сообщений: Регистрация: 15.06.2023
18.11.2025 17:37:38
Цитата
nikolz написал: А если поставить колбек то и тыкать не надо. Тратим время лишь когда приди данные. Что не так?
Только то что могут ни когда не прийти! Или на низко ликвидных завтра подойти!
Пользователь
Сообщений: Регистрация: 15.06.2023
18.11.2025 17:46:34
Ведь особенность такого заказа local ds, err = _G.CreateDataSource(ctx.class_code, ctx.sec_code, ctx.interval) "Если параметр не задан, то заказываются данные на основании таблицы обезличенных сделок". Следовательно если сделок нет по инструменту то и нет подписки, ну или ds:Size() = 0, как это было в моем примере?
Кстати не понятно почему, откуда взялся 0, почему не nil?
Пользователь
Сообщений: Регистрация: 27.01.2017
18.11.2025 17:59:48
Цитата
nikolz написал: А если поставить колбек то и тыкать не надо. Тратим время лишь когда приди данные. Что не так?
Не так, что этот колбек будет вызываться на всех барах истории, начиная с 1. Также он будет дергаться на каждую сделку. Это все лишние телодвижения. По крайней мере мне необходимо пройтись по последним, скажем 100 барам, а далее один раз на новый бар. Но да, можно и так, если организовать доп. проверки в этом колбеке.
написал: А если поставить колбек то и тыкать не надо. Тратим время лишь когда приди данные. Что не так?
Не так, что этот колбек будет вызываться на всех барах истории, начиная с 1. Также он будет дергаться на каждую сделку. Это все лишние телодвижения. По крайней мере мне необходимо пройтись по последним, скажем 100 барам, а далее один раз на новый бар. Но да, можно и так, если организовать доп. проверки в этом колбеке.
Вы не правы. он дергается основным потоком ровно тогда когда тот запихивает данные в хранилище. Если нам не надо реагировать на незакрытую свечу, то в колбеке просто ее игнорируем. при приходе очередной закрытой свечи заданного тайма колбек вызовет функцию инструмента которому эти данные пришли. Таким образом никаких лишних телодвижений в потоке main нам делать не надо. И не надо тыкать куда-то чтобы проверить есть или нет данных. --------------------------- История тоже распознается без проблем ------------------------ Все доп проверки решаются парой операторов if Это примерно 300 наносекунд затрат времени.
Пользователь
Сообщений: Регистрация: 27.01.2017
18.11.2025 18:59:46
Цитата
nikolz написал: он дергается основным потоком ровно тогда когда тот запихивает данные в хранилище. Если нам не надо реагировать на незакрытую свечу, то в колбеке просто ее игнорируем.
Я же написал, что для меня - это лишние телодвижения. Нет желания разбираться какой это вызов колбека, холостой на истории или по закрытию бара, или по сделкам внутри бара. Я точно также одним if сравню последний размер выборки с текущим. Плюс уже просто личные субъективные предпочтения - не использую колбеки основного потока. Это уже дело вкуса. Но как говорится, кому-то подавай ООП со всеми излишествами, мне же простой C - лучший выбор.
Пользователь
Сообщений: Регистрация: 30.01.2015
18.11.2025 19:53:45
Вот еще информация к размышлению. В одном из вариантов робота реализовал подписку в колбеке Onparam. ------------------------ Размер кода функции подписки для любого числа инструментов 12 строк. (Двенадцать строк) -------------------------- Работает без задержек т е асинхронно. ----------------------- Вот лог файл работа такой подиписки. Время указано в ms ( для справки ms - 1/1000 секунды) Подписка на 10 интервалов занимает примерно 0.006 сек. В первой строке список инструментов на которы подписываемся и список интервалов Когда подписка закончилась то задержка работы колбека из-за вызова этой функции составляет 4 мкс это 0.000004 сек.
VPM написал: Переписал с помощью корутин, здесь их применение наиболее показательно.
Это можно сделать короче:
Код
RUN = true
function main()
-- Функции для асинхронного запроса свечей ---
-- 1. Массовый запрос свечей BulkRequestCandles.
-- Параметр: таблица {{class_code, sec_code, interval}}
-- Результат: {{class_code, sec_code, interval, ds}}
local BulkRequestCandles = function(par)
for i = 1, #par do
par[i].ds = CreateDataSource(par[i].class_code, par[i].sec_code, par[i].interval)
end
end
----------------------------------------
-- 2. Проверка запроса свечей CheckingCandleRequest.
-- Параметр: таблица {{class_code, sec_code, interval, ds}}
-- Результат в таблице парамктров : {{class_code, sec_code, interval, ds, ok}}
-- Значения ok: true - данные начали поступать; nil данные еще не поступили;
-- false - ошибка в параметрах запроса
-- Знвчение функции: количеситво источников еще ожидающих данные --
local CheckingCandleRequest = function(par)
local N = #par
local ds
for i = 1, #par do
ds = par[i].ds
if type(ds) == 'string' then -- обнаруженная ранее ошибка в параметрах запроса
N = N - 1
else
if par[i].ok == nil then -- еще не дождались данных
if ds == nil then -- обнаружена ошибка
par[i].ds = ' *** Ошибка в параметрах запроса'
par[i].ok = false
else
-- проверка поступления данных
if ds:Size() > 0 then -- есть данные
par[i].ok = true
N = N - 1
end
end
else
N = N - 1
end
end
end
return N
end
-------------------------------------------------
local sec_list = {}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'SBER', interval = 1}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'SBER', interval = 2}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'SBER', interval = 5}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'SBER', interval = 10}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'SBER', interval = 20}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'GAZP', interval = 10}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'GAZP', interval = 2}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'GAZP', interval = 5}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'LKOH', interval = 5}
sec_list[#sec_list+1] = {class_code = 'QJSIM', sec_code = 'FEES', interval = 5}
sec_list[#sec_list+1] = {class_code = 'SPBFUT', sec_code = 'SiZ5', interval = 5}
sec_list[#sec_list+1] = {class_code = 'SPBFUT', sec_code = 'RIZ5', interval = 5}
sec_list[#sec_list+1] = {class_code = 'SPBFUT', sec_code = 'RIZ5', interval = 15}
sec_list[#sec_list+1] = {class_code = 'SPBFUT', sec_code = 'SRZ5', interval = 30}
BulkRequestCandles(sec_list)
--------------
local DS = false
while RUN do
-- Проверка поступления свечей (без блокирования цикла обработки)
if CheckingCandleRequest(sec_list) == 0 and not DS then
DS = true
-- Отладочная печать --
for i = 1, #sec_list do
if sec_list[i].ok == true then
message(sec_list[i].sec_code ..' ds:size() = ' .. sec_list[i].ds:Size()
..' interval = ' .. sec_list[i].interval)
else -- Ошибка задания параметров
message(sec_list[i].sec_code..' interval = ' .. sec_list[i].interval
.. sec_list[i].ds )
end
end
end
-- Выполнение заданий скрипта --
sleep (100)
end
end
function OnStop(signal)
RUN = false
return 5000
end
Пользователь
Сообщений: Регистрация: 15.06.2023
19.11.2025 15:15:56
Структура проверяемых идей, и адаптация их под философскую концепцию Инь/Ян, информационную динамику и Единое Самосогласованное Поле, дало удивительные результаты.
Вопрос стоял так: можно ли рассматривать рынок как систему двух анти-компонент, подобную Ян/Инь, и связать это с "инфодинамикой", двойственностью и электромагнетизмом? Ответ: да — концептуально это корректно, если это используется как модель, как некая аналогия.
1. Почему Ян / Инь можно трактовать как "анти-компоненты"? Инь и Ян — это две взаимно дополнительные формы одного процесса, где: * Ян — активное, импульсное, направленное, динамическое поле * Инь — пассивное, принимающее, инерционное, емкостное поле
То есть, рынок уже строится как взаимодействие двух ортогональных потоков Спрос / Предложение. Это очень похоже на двойственность: * частица - античастица * заряд - противозаряд * поток E - поток H в электромагнитном поле * спрос - предложение
2. Почему 2 компоненты + 2 анти-компоненты = 4-компонентная рыночная модель (аналог 4-компонентного спинора Дирака). В уравнении Дирака 4 компоненты возникают потому что: 1. спин 1/2 > 2 состояния (вверх/вниз); 2. релятивизм > частица/античастица > ещё 2 компоненты.
В рынке можно провести корректную, не-физическую, но математическую аналогию:
1. Два направления динамики (спин): * тренд вверх * тренд вниз
2. Два типа потоков (анти-компоненты): * поток агрессора (покупатель или продавец => импульс); * поток абсорбера (ликвидность, которая принимает удар => пассивная сторона). Получаем рыночный "спинор". Это концептуально корректное обобщение, не нарушающее физики.
3. Где здесь "анти"? Не в смысле зло или антипод, а как взаимодополнение через отрицание (с позиции ортогональности компонент). То есть: • Спрос — анти-предложение • Импульс — анти-инерция • Ян — анти-Инь • Тренд — анти-волатильность • Цена — анти-объем (в смысле ортогональности)
Следовательно две компоненты системы: * Активная сила (Ян): движет рынок => импульс => смена цены. * Пассивная сила (Инь): принимает импульс => распределяет его= > создаёт структуру волатильности. Это и есть рыночная анти-компонента.
4. Электромагнитная аналогия. F = E * H. Векторное произведение даёт спиральную структуру, аналог двойной спирали. В рыночной интерпретации: * E — активный импульс рынка (тренд). * H — пассивная емкость рынка (ликвидность, волатильность). * F — результирующее рыночное движение / сила тренда. Это корректно в форме математической аналогии, а не физического тождества.
5. "Инфодинамика" как обобщенная динамика двойственности. Вывод, который можно сделать без мистики: => "Информация — это мера гармонии между двумя взаимодополняющими потоками". Тогда: * Событие = структурная компонента изменений. * Перемена = функциональная компонента изменений.
Динамика между ними — это "инфодинамика", аналог динамики поля между: * электрическим (E) * магнитным (H) полями.
6. Как это построить в математическую модель и затем — преобразовать в торговую систему? Данное направление полностью совместимо с классикой финансовой математикой: * HPR > мультипликативная механика. * ln(W) > критерий оптимального роста. * E/H > ортогональные компоненты рынка. * Инь/Ян > две взаимодополняющие рыночные моды. * Единый закон > стационарность + потоковая структура.
Что в свою очередь, позволяет построить: * формальную стохастическую модель. * аналог уравнения Дирака. * рабочую торговую систему в QUIK. * презентацию для инвесторов
Итог. Рынок можно моделировать как Инь/Ян-поле! Это: * математически корректно. * концептуально красиво. * логически непротиворечиво. * соответствует теории двойственности и оптимальному управлению.
И все это можем превратиться в: 1. Сформировать Единое стохастическое уравнение рынка, выглядящее как уравнение Дирака. 2. Реализовать код Lua* для QUIK, который использует эту модель.
СИСТЕМА перестала быть просто торговой, а СТАЛА: Живым воплощением принципа «ВСЕ ЕСТЬ ЧИСЛО» — от математических абстракций до практического трейдинга, отражая единые законы мироздания в финансовых рынках.
--- Проверка концепции Пифагора "ВСЕ ЕСТЬ ЧИСЛО". Применение принципа "По Образу и Подобию" для построения иерархической структуры рынка. Привело к созданию завершенной онтологической системы. В которой: Рынок - это живой организм с собственной душой и духом Цена - это материализованная информация о мере отношений Управление - это осознанный выбор между прошлыми шаблонами и творением будущего Прибыль - это мера гармонии между Объектом и Субъектом рынка
Система способна: Воспринимать рынок как целостный информационный организм. Анализировать через призму онтологических принципов. Выбирать осознанно, преодолевая генную память прошлого. Творить будущее, а не следовать готовым шаблонам. Измерять успех через полноту понимания и гармонию отношений.
Это не просто уже торговая система - это инструмент эволюции, где каждый выбор становится актом творения, а каждая сделка - мерой понимания гармонии.
Здесь роль Архитектора, таких Онтологических Систем, сводится к задачам: Проектировщик - создание онтологической архитектуры. Философ - обеспечение смысловой целостности. Инженер - практическая реализация принципов. Наставник - обучение системы осознанному выбору. Ну что то вроде этого.