VPM (Все сообщения пользователя)

Выбрать дату в календареВыбрать дату в календаре

Страницы: Пред. 1 2 3 4 5 6 7 8 9 10 11 ... 29 След.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
"Ни когда не было и вот опять". Прежде чем разбираться с подходами Р. Винса, они направлены на управление портфелями, а задача ставилась изначально возможность работы с одним инструментом и простейшими торговыми стратегиями. Хотя и пересобрал блок "капитал менеджер", текущая задача все таки  несколько другая, не меняя важная, скорее даже основополагающая, ведь от качества полученных данных зависят все остальные расчеты.

Организация получения данных удобным способом, есть одна из главных задач, собственно с нее то и нужно было начинать, но как понять что необходимо под конкретную задачу, мой подход "ввязаться в бой". Думаю нужен универсальный подключаемый модуль, чтоб не заниматься переделками каждый раз. Тогда задачу условно можно разделить на 2 этапа: получение данных и их управление. Нужно собрать дата менеджер.  
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Важное замечание!
В рамках системы Винса, когда мы говорим о управлении капиталом, важно учитывать не только отдельные тикеры или активы, но и влияние каждого из них на общий капитал.

Механизм «постоянного доминирования» — это не просто расчет доли капитала для каждого инструмента по отдельности, а полноценное управление всей портфельной позицией с учетом того, как изменения в одной части капитала влияют на общий баланс и в совокупности на капитал как таковой.
К моему велосипеду еще нужны педали?  :sad:  
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Что бы не изобретать очередной велосипед возьму свою систему управления капиталом. Вот структура  и согласование модулей между собой:

1. Иерархия модулей.

VinceCapitalManager (верхний уровень)
   v Наследует
AdaptiveCapitalManager (средний уровень)  
   v Наследует
CapitalManager (базовый уровень)

2. Поток данных между модулями.

CapitalManager > AdaptiveCapitalManager:

- `total_capital`, `active_capital`, `passive_capital`
- `money_positions`, `depo_positions`
- `allocated_capital` - распределение по инструментам
- Статистика использования капитала

AdaptiveCapitalManager > VinceCapitalManager:

- Рыночные режимы и волатильность
- Метрики риска (VaR, Expected Shortfall)
- Производительность инструментов (Sharpe, просадки)
- Система алертов

В этом случае все что от меня требуется, подключиться к разрабатываемой торговой системе, то есть  по сути согласовать 3 жизненных цикла, простая торговая стратегия все та же, выдает приказы (Что сделаешь Важная командует)
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
 Возможные подходы к организации жизненного цикла "Money". Есть несколько основных подходов для организации такого жизненного цикла, которые могут зависеть от сложности системы и наших бизнес-правил.

1. Цикл распределения средств (активно-торговые деньги).
Это подход, при котором для каждого тикера или позиции выделяется определенная сумма из активного капитала, которая будет использоваться для торговли на этом тикере.

Пример:
* У вас есть капитал ( C ), и вы хотите распределить его по 10 тикерам.
* Таким образом, каждый тикер получит сумму ( C / 10 ) для торговли.

Пример кода:
Код
function Trading:allocate_money_for_trading()
    local total_active_capital = self:get_active_capital()  -- Общее количество активных средств
    local total_tickers = self:get_number_of_tickers()      -- Количество активных тикеров
    
    local capital_per_ticker = total_active_capital / total_tickers
    
    -- Для каждого тикера выделяем средства
    for i = 1, total_tickers do
        self:allocate_ticker_capital(i, capital_per_ticker)
    end
end

function Trading:allocate_ticker_capital(ticker_id, amount)
    -- Логика выделения средств для конкретного тикера
    log_info(string.format("Для тикера %d выделено %.2f средств на торговлю.", ticker_id, amount))
    -- Дополнительная логика управления средствами для конкретного тикера...
end

2. Управление капиталом на основе рисков.
Этот подход более сложный и ориентирован на распределение капитала в зависимости от рисков, связанных с каждым активом. В этом случае капитал может быть выделен не равномерно между тикерами, а в зависимости от их волатильности, ликвидности или других факторов риска.

Пример:
* Для более рисковых тикеров (например, с большой волатильностью) вы выделяете меньшую сумму, чтобы уменьшить возможные потери.
* Для более стабильных тикеров (с низкой волатильностью) выделяется больше средств.

Пример кода:
Код
function Trading:allocate_money_based_on_risk()
    local total_active_capital = self:get_active_capital()
    local ticker_risks = self:get_ticker_risks()  -- Структура риска для каждого тикера
    
    -- Распределяем капитал пропорционально рискам
    for i, risk in ipairs(ticker_risks) do
        local capital_for_ticker = self:calculate_risk_adjusted_capital(total_active_capital, risk)
        self:allocate_ticker_capital(i, capital_for_ticker)
    end
end

function Trading:calculate_risk_adjusted_capital(total_capital, risk)
    -- Логика расчета капитала с учетом рисков
    return total_capital * (1 - risk)
end

3. Цикл обновления и перераспределения капитала (динамическое распределение).
Этот подход заключается в том, что мы регулярно обновляем капитал, выделенный на торговлю, в зависимости от текущего состояния рынка и состояния активных позиций.
Например:
* Когда прибыль с позиции увеличивается, мы можем перераспределить часть этого капитала на другие, более прибыльные тикеры.
* Если риски становятся слишком высокими, мы можем заморозить капитал на определенных тикерах, чтобы уменьшить возможные потери.

Пример кода:
Код
function Trading:reallocate_capital_based_on_market_conditions()
    local total_active_capital = self:get_active_capital()
    local market_conditions = self:get_market_conditions() -- Получаем текущие рыночные условия
    
    for i, condition in ipairs(market_conditions) do
        if condition == "high_volatility" then
            self:reduce_capital_for_ticker(i)
        elseif condition == "stable_market" then
            self:increase_capital_for_ticker(i)
        end
    end
end

function Trading:reduce_capital_for_ticker(ticker_id)
    -- Логика уменьшения капитала для данного тикера
    log_info(string.format("Уменьшаем капитал для тикера %d из-за высокой волатильности.", ticker_id))
end

function Trading:increase_capital_for_ticker(ticker_id)
    -- Логика увеличения капитала для данного тикера
    log_info(string.format("Увеличиваем капитал для тикера %d из-за стабильного рынка.", ticker_id))
end
Есть и другие подходы, но на этом этапе нам важно собрать полный жизненный цикл, а оптимизацией можно и потом заняться.

Что следует учесть:

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

2. Прочие элементы жизненного цикла:
  * Отслеживание маржи. Если используется маржа (например, при использовании заемных средств для торговли), нам нужно контролировать, сколько капитала выделяется на поддержание маржи для открытых позиций.
  * Риски и лимиты. Управление рисками — это ключевая часть жизни капитала. Нужно учитывать лимиты на потери для каждого тикера и корректно перераспределять капитал, если актив выходит за пределы допустимого.

Следовательно. Для эффективной работы жизненного цикла money стоит предусмотреть следующие моменты:

* Разделить управление пассивным и активным капиталом, чтобы избежать использования средств, зарезервированных для покрытия рисков.
* Разработать систему перераспределения средств на основе рисков, объемов торгов и других факторов.
* Учитывать механизмы блокировки и гарантии, которые могут возникать при торговле с маржой или при достижении предельных значений по позициям.
* Обеспечить регулярное обновление данных по активному капиталу и следить за состоянием ликвидности.

Таким образом, жизненный цикл money должен быть организован с учетом наших специфических требований по управлению средствами, рисками и капиталом, что обеспечит гибкость и точность в процессе торговли.
Как задачка?  :what:  
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 

В этом случае образуется еще одна пара depo / money, которая непосредственно влияет на capital, и требует добавления еще одного жизненного цикла, чтобы контролировать и управлять деньгами, используемыми в торговых операциях. Это важная составляющая системы для адекватного управления капиталом, рисками и средствами, доступными для торговли.

Принципиальное понимание, весь торговый капитал делится на 2 составляющие:

  1. Активный и Пассивный капитал:

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

    • Активный капитал — это средства, которые мы используем для торговых операций. Из этих средства нужно будет выделить на конкретные тикеры (позиции), которые будут торговаться. Чем больше активных позиций (тикеров), тем больше капитал должен быть задействован для каждой торговой операции.

  • Жизненный цикл Money:

    • Суть этого цикла — это управление средствами, которые будут выделяться для торговли. Этот процесс включает в себя:

      • Распределение средств по активным позициям и тикерам.

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

      • Актуализация данных по торговым операциям (открытым позициям, маржинальным требованиям и т.д.).

      • Перераспределение средств между различными торговыми активами (тикерами).

    Таким образом, жизненный цикл money будет связан с жизненным циклом ордеров и сделок, но будет ориентирован на правильное распределение и отслеживание средств для торговли.

  • Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    И так обсудили разные подходы по организации полного жизненного цикла заявки. Каким подходом пользоваться, каждый определяет для себя сам. Это архитектурная дилемма. Так как окончательного мнения у меня не сложилось, буду пользоваться наиболее понятными для меня подходами. Задача все еще прежняя, собрать модульную структуру в которой торговые стратегии работают независимо отдавая приказы на исполнение. И технологическая, все это дело обслуживает.

    Таким образом можно выделить 3 главных блока, которые взаимодействуют ка между собой так и с апи  квик.
    А. полный жизненный цикл заявки.
    Б. полный жизненный цикл  сделки.
    С. полный жизненный цикл money.

    Следовательно в блоках можно условно выделить основное взаимодействие, это пары:
    1. заявка / сделка = depo;
    2. актив depo / актив money, что в свою очередь можно представить как depo + money = capital.

    Следовательно, следующая задача,  нужно организовать жизненный цикл money.
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Но Вы же сами пишите, с чем можно сталкиваться, то не пришло, то не то пришло, а с таймерами вообще не понятно что делать, сколько ждать, ждать или не ждать? (Вечный философский вопрос: "Быть или не Быть?"  :smile:  ).  Да и вопрос встает как опрашивать, последовательно перебирая, но ведь приходят ни пойми как? Накидал асинхронный вариант с машиной состояний и процедурный (после всех выкрутасов конечно проще, но отвечает ли в полной мере на все вопросы?).
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Nikolay,  Ну я не знаю где у этой гидры хвост, а где голова. Я строю модульный вариант. 80% решается в другом модуле  DataManager + get API (Получился не большой универсальный Фреймворк)  окончательный этот вариант или нет, не могу сказать пока, ключевое здесь возможность многократного использования.  
    Того же добиваюсь от AOL -  для автоматической системы надежность и вообще как он сделан имеет ключевое значение, если он не рабочий, Все остальное ни имеет смысла как бы оно не работало! Мне нужен универсальный, надежный, для многократного использования (принцип - "написал и забыл") ,  для одного инструмента - это просто упрощение.

    Если я Вас правильно понимаю, фоновые решения предлагаете решать через "замыкания"? И решения поиска ордеров и сделок в таблицах квик?
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Ну и как Вам задачка для обычного пользователя QUIK!  :unamused:  
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Нет все таки упустил.

    Нужна машина состояний - FSM (finite state machine) — для описания переходов состояний ордера.
    Очередь событий или coroutine - менеджер обработки событий, следовательно  Асинхронную модель.
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Lua-модуль,  реализующий атомарный жизненный цикл заявок (Atomic Order Lifecycle) в QUIK.

    Должен быть построен по принципу:

    * независим от стратегии,
    * обрабатывает все три потока событий (OnTransReply, OnOrder, OnStopOrder),
    * поддерживает атомарность и согласованность данных,
    * частичное исполнение,
    * восстановления текущих ордеров и стоп-заявок при запуске  или сбоях из данных таблиц QUIK,
    * проводить периодическую сверку с данными таблиц  QUIK,
    * логирование,
    * таймауты.

    Для реализации стабильной работы AOL-контроллера необходимо чтоб его архитектура соответствовала асинхронной модели? Я ни чего не упустил?
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Вот и пришли с чего начали. Для создания стабильно работающего AOL-контроллера необходимо переработать его архитектуру в соответствии с асинхронной моделью?
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Обработка OnTransReply.  Ожидание ответа в цикле main приводит к проблеме - зависанию. Асинхронная модель: реагирует на события в колбэках, не ожидая их в основном потоке не блокирует его?
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    nikolz,  Хотя читать тяжеловато логика понятна, решение "класс". Не понял только как в Вашем варианте поступать с "частичное исполнение" и восстановления текущих ордеров и стоп-заявок при запуске  или сбоях из данных QUIK?

    Да вон еще Nikolay, страстей написал? Получается и периодическую сверку запускать нужно? Ну либо вообще от подхода отказаться?
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Тогда Архитектура:
    1.  Trading Strategy.
        (логика входа/выхода, анализ данных, решения)
    +-------------------------------------------------------------+

    2.  OrderManager (AOL).
       (отправка, принятие, исполнение, таймаут, отмена)
    +-------------------------------------------------------------+
    3.  RiskManager:
        (контроль капитала, просадки, стопы)
    +-------------------------------------------------------------+
    4.  QUIK API Layer:
       ( DataManager:  getParamEx, ...
       sendTransaction,
       OnTransReply, OnOrder, OnTrade)  
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    6. Таймаут и отмена ордера.
    Код
    function OrderManager:check_timeouts()
        local now = os.time()
        for trans_id, ctx in pairs(self.pool) do
            if ctx.status == "SENT" and now - ctx.timestamp > self.timeout_sec then
                ctx.status = "TIMEOUT"
                log(string.format("[%s] x Таймаут ордера, отправляем отмену", ctx.sec))
                self:cancel(ctx)
            end
        end
    end
    
    function OrderManager:cancel(ctx)
        if not ctx.order_num then return end
        local txn = {
            ACCOUNT     = ACCOUNT,
            CLIENT_CODE = CLIENT_CODE,
            CLASSCODE   = ctx.class,
            SECCODE     = ctx.sec,
            ACTION      = "KILL_ORDER",
            ORDER_KEY   = ctx.order_num,
            TRANS_ID    = tostring(math.random(1000000,9999999))
        }
        sendTransaction(txn)
        ctx.status = "CANCELLED"
    end
    
    7. Использование стратегии.   Теперь стратегия не работает напрямую с sendTransaction, она обращается к менеджеру:
    Код
    local ctx = {
        sec = "SBER",
        class = "TQBR",
        base_price = nil,
        drop_trigger = 0.002,
        qty = 1
    }
    
    local function strategy_tick()
        local price = tonumber(getParamEx(ctx.class, ctx.sec, "LAST").param_value)
        if not price or price <= 0 then return end
    
        if not ctx.base_price then
            ctx.base_price = price
            return
        end
    
        local change = (price - ctx.base_price)/ctx.base_price
        if change <= -ctx.drop_trigger then
            OrderManager:send({
                class = ctx.class,
                sec = ctx.sec,
                operation = "B",
                price = price,
                qty = ctx.qty
            })
        elseif change >= ctx.drop_trigger then
            OrderManager:send({
                class = ctx.class,
                sec = ctx.sec,
                operation = "S",
                price = price,
                qty = ctx.qty
            })
        end
    end
    
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    1. Структура данных ордера.
    Код
    OrderManager = {
        pool = {},       -- все активные ордера по TRANS_ID
        timeout_sec = 10 -- время ожидания исполнения
    }
    
    Каждый ордер хранит:
    Код
    -- контекст ордера
    local order_ctx = {
        trans_id   = "12345", -- уникальный локальный ID
        class      = "TQBR",
        sec        = "SBER",
        operation  = "B",     -- B / S
        price      = 123.45,
        qty        = 1,
        status     = "NEW",   -- NEW / SENT / PARTIALLY_FILLED / FILLED / CANCELLED / REJECTED / TIMEOUT
        order_num  = nil,     -- номер заявки на сервере QUIK
        trades     = {},      -- массив исполненных частей
        timestamp  = os.time()
    }
    
    2. Отправка ордера (атомарный цикл).
    Код
    function OrderManager:send(order)
        local trans_id = tostring(math.random(1000000,9999999))
        order.trans_id = trans_id
        order.status = "NEW"
        order.timestamp = os.time()
        self.pool[trans_id] = order
    
        local txn = {
            ACCOUNT     = ACCOUNT,
            CLIENT_CODE = CLIENT_CODE,
            CLASSCODE   = order.class,
            SECCODE     = order.sec,
            ACTION      = "NEW_ORDER",
            OPERATION   = order.operation,
            PRICE       = tostring(order.price),
            QUANTITY    = tostring(order.qty),
            TYPE        = "L",
            TRANS_ID    = trans_id
        }
    
        local result = sendTransaction(txn)
        if result ~= "" then
            log(string.format("[%s] x Ошибка отправки: %s", order.sec, result))
            order.status = "REJECTED"
            return false
        else
            order.status = "SENT"
            log(string.format("[%s] v Ордер отправлен (TRANS_ID=%s)", order.sec, trans_id))
            return true
        end
    end
    
    3. Колбэк OnTransReply.
    Код
    function OnTransReply(tr)
        local ctx = OrderManager.pool[tr.trans_id]
        if not ctx then return end
    
        if tr.status == 3 then
            ctx.order_num = tr.order_num
            log(string.format("[%s] v Ордер принят биржей (order_num=%s)", ctx.sec, ctx.order_num))
        elseif tr.status >= 4 then
            ctx.status = "REJECTED"
            log(string.format("[%s] x Ордер отклонен: %s", ctx.sec, tr.result_msg))
            OrderManager.pool[tr.trans_id] = nil
        end
    end
    
    4. Колбэк OnOrder (обновление состояния).
    Код
    function OnOrder(ord)
        for _, ctx in pairs(OrderManager.pool) do
            if ctx.order_num and ctx.order_num == ord.order_num then
                ctx.balance = ord.balance
                if ctx.balance == 0 then
                    ctx.status = "FILLED"
                    log(string.format("[%s] v Ордер полностью исполнен", ctx.sec))
                elseif ord.balance < ctx.qty then
                    ctx.status = "PARTIALLY_FILLED"
                    log(string.format("[%s] v Частичное исполнение: %d/%d", ctx.sec, ctx.qty-ord.balance, ctx.qty))
                end
            end
        end
    end
    
    5. Колбэк OnTrade (фиксируем сделки).
    Код
    function OnTrade(trade)
        for _, ctx in pairs(OrderManager.pool) do
            if ctx.order_num and ctx.order_num == trade.order_num then
                table.insert(ctx.trades, {
                    trade_num = trade.trade_num,
                    qty       = trade.qty,
                    price     = trade.price,
                    dir       = trade.flags % 2 == 0 and "B" or "S"
                })
            end
        end
    end
    
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Тогда, Atomic Order Lifecycle (AOL) контроллер для QUIK на Lua.

    Идея: мы создаём отдельный модуль OrderManager, который обеспечивает атомарный жизненный цикл ордера независимо от стратегии.
    Стратегия просто говорит: «открыть лонг / шорт», а менеджер следит за статусом, частичным исполнением, таймаутами и отменами. Следовательно:
    [*]

    Вся логика отправки, ожидания, исполнения, таймаута и отмены — централизована в OrderManager.

    [*]

    Стратегия просто делает запрос на ордер и получает атомарное событие исполнения.

    [*]

    Поддерживается частичное исполнение, таймаут, отмена.

    [*]

    Состояние ордера можно сохранить на диск и восстановить при перезапуске.

    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Как адаптировать AOL к реальности?

    ПроблемаТеория AOLРеальный подход
    Частичное исполнениеНарушает атомарностьСчитать атомарным весь ордерный контекст, а не сделку
    Разные IDОдин идентификаторВвести сопоставление ID между уровнями
    Перезапуск / сбойНе допускаетсяВосстанавливать состояние из текущих таблиц QUIK
    Ошибки транзакцийОткат всей операцииРеагировать по статусам OnTransReply и отменять при status > 3
    ТаймаутНет отдельного понятияРеализовать вручную (watchdog coroutine)
    ┌──────────────┐
    │ sendTransaction()
    └──────┬───────┘
                     │ TRANS_ID
                    ▼
    ┌──────────────┐
    │ OnTransReply(status=0..1)
    └──────┬───────┘
                     │ order_num
                    ▼
    ┌──────────────┐
    │ OnOrder(update)
    └──────┬───────┘
                     │ trade_num
                    ▼
    ┌──────────────┐
    │ OnTrade() — исполнение
    └──────┬───────┘
                     │
                    ▼
    ┌──────────────┐
    │ FILLED / CANCELLED
    └──────────────┘
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    А если жизненный цикл (путь), расписать по шагам в конкретные процедуры (как Nikolay, ) подход сам напрашивается. И что делать с ожиданиями? Нужен фоновый подход?
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Разбираюсь с жизненным циклом заявки, наткнулся на подход Atomic Order Lifecycle (AOL) представляет собой последовательность этапов, через которые проходит ордер в торговой системе. Все этапы жизненного цикла ордера — от его создания и отправки до исполнения, таймаута или отмены — должны быть атомарными, т.е. все операции должны завершаться

    * либо успехом (ордер выполнен),
    * либо ошибкой (ордер отменён или отказан).

    Это гарантирует, что трейдер или торговая система всегда знает, в каком состоянии находится ордер и что с ним происходит.

    Процесс жизненного цикла ордера в торговой системе можно разбить на следующие этапы:

    1. Создание ордера

    2. Отправка ордера

    3. Ожидание принятия

    4. Ожидание исполнения

    5. Исполнение

    6. Таймаут или отказ

    7. Отмена ордера

    Правильная обработка каждого из этих этапов и корректная реакция на возможные ошибки позволяют эффективно управлять ордерами и минимизировать риски на финансовых рынках. Но что делать в ситуации когда нет атомарной гарантии "all-or-nothing":   ПРОБЛЕМА: Частичное исполнение нарушает атомарность? Или другая,  ПРОБЛЕМА: Разные идентификаторы на разных этапах? Непонятно?

    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Nikolay,  Супер подход, на каждом шаге процедура, так? Я даже не задумывался о такой возможности.
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     

    Как поступлю. Адаптируем “умный” блок (SmartOrder, reply, sdelka, обработчики событий QUIK) в уже готовый скрипт, чтобы он:

    • поддерживал открытие / закрытие позиций (лонг и шорт),

    • корректно работал через callbacks,

    • не зависел от ожиданий (основной поток не блокируется),

    • сохранял информацию о транзакциях и сделках в reply / sdelka,

    • и синхронизировался с реальными ордерами QUIK.

    Что изменилось и почему это круто, судите сами;
    БылоСтало
    send_and_wait с циклом ожиданияsend_order_async — не блокирует поток, обрабатывает ответ асинхронно
    Прямые проверки result_msgПереход на централизованное хранилище SmartOrder.pool
    Потеря информации о транзакцииВся история сохраняется в reply и sdelka
    Не обновлялась позицияПозиции обновляются автоматически при OnTrade
    Возможна гонка данныхКаждая транзакция отслеживается независимо через свой trans_id
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Теперь относительно самого кода: Что можно сделать и что важно знать?
    1. Отрицательные / нулевые цены — проверяем и игнорируем, пока не придёт реальная сделка (OnTrade).

    2. Пакеты приходят неупорядоченно — полагаемся на локальную метку - os.time(), при несоответствии времени пакета можно игнорировать «старые» пачки.

    3. sendTransaction ≠ сделка — сделать транзакционный цикл: (отправка → ожидание принятия → ожидание исполнения → таймаут → отмена).

    4. OnOrder / OnTrade — обязательные точки контроля. Без них нельзя корректно знать цену исполнения и состояние позиции?

    5. Закрытие позиций на стоп — реализовать опционально флаг CLOSE_POS_STOP.

    6. Адаптация к брокеру / версии QUIK — нужно проверить реальные поля, которые приходят в OnOrder / OnTrade от брокера, и использовать парсер.

    7. Обратите внимание что в этом подходе есть скрытые зависимости и они могут вносить свою лепту.

    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Еще раз про код, для тех кто рискнет пробовать и разбираться, обратите внимание на замечания от Nikolay,  все они имеют место быть, всё что перечислено реально критично для боевого робота. Нужна конкретная переработка подхода + рабочая, практичная версия кода, да и сам подход желает лучшего. Я своей целью ставил демонстрацию возможного применения корутин, и максимально сложного написания аналога с помощью подхода использования замыканий на луа без сторонних библиотек. Это просто не кая демонстрация возможностей.
    Автозапуск скрипта LUA при старте QUIK
     
    Helen,  Вы просто выкладывайте с чем, Вам нужно разобраться, и сообщество откликнется. Если же Вы хотите заказать разработку, то это скорее на специализированные сайты, здесь кто продает свои разработки подписываются ссылкой на сайты. Удачи.
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    С утра "чесались руки" слепил из того что было, хочу такой же с замыканиями и таблицей состояний сделать. Ну и сравнить. За анализ благодарю! А Страх и Смелость просто не обходимы!  :smile:  
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Пример карутинного элегантного подхода.

    ВНИМАНИЕ! РЕАЛЬНО ТОРГУЕТ!  
    Для начала закомментируйте вывод заявок! Вместо отправке поставьте сообщение.
    Это просто демонстрация возможностей! "Иметь или не иметь" решаем самостоятельно.
    Код
    - Pобот - торговый менеджер с реальным риск-контролем QUIK
    --[[ Цель. Создать систему, которая:
    
    1. Определяет начальный капитал (из QUIK).
    2. Каждые несколько секунд получает текущую оценку капитала.
    3. Вычисляет просадку в процентах от начального капитала.
    4. Если просадка превышает лимит (например, 2%), —
           останавливает все стратегии, фиксирует в логах и выводит уведомление.
    --]]
    --[[Пример: простая стратегия на основе корутины!
    
    Идея: Следим за ценой инструмента (SBER), и 
       если она падает больше чем на "drop_trigger %" от последней зафиксированной — покупаем 1 лот.
       Если поднимается больше чем на "drop_trigger %"% — продаём 1 лот.
       
    Все операции выполняются пошагово, без блокировки основного потока. --]]
    
    local is_run = true
    
    ------------------------------------------------------------
    -- === Конфигурация ===
    ------------------------------------------------------------
    local ACCOUNT     = ""
    local CLIENT_CODE = ""
    local FIRM_ID     = "" 
    
    local    order_type = "LIMIT"
    local    time_in_force = "GTC"
    
    local LIMIT_KIND  = 0            -- 0 = по деньгам
    local RISK_LIMIT  = -0.02        -- максимум 2% просадки
    local LOT_SIZE    = 1
    
    local drop_trigger = 0.001
    local take_profit  = 0.002
    local stop_loss    = 0.001
    
    local instruments = {
        { class = "TQBR", sec = "SBER" },
        { class = "TQBR", sec = "GAZP" },
        { class = "TQBR", sec = "ROSN" },
    }
    
    ------------------------------------------------------------
    -- === Логирование ===
    ------------------------------------------------------------
    
    -- Объявляем log_file глобально для скрипта
    local log_file; --= getScriptPath() .. "\\robot_capital_risk_log.txt"
    
    -- Функция для логирования
    local function log(msg)
        local line = os.date("%Y-%m-%d %H:%M:%S") .. " | " .. msg
        message(line)
        local f = io.open(log_file, "a")
        if f then
            f:write(line .. "\n")
            f:close()
        else
            message("x Не удалось открыть файл для записи: " .. log_file)
        end
    end
    
    --[[ ЭЛЕГАНТНАЯ ФУНКЦИЯ ОКРУГЛЕНИЯ удалением ,0]]--
    local round = function (num, idp)
        
        -- Если num некорректное, вернуть как есть
        if not num or type(num) ~= "number" then return num end
        -- добавbv обработку очень малых чисел:
        if math.abs(num) < 1e-10 then return 0 end  -- ДОБАВИТЬ
        
        -- Если idp не указан, использовать 0 (округление до целого числа)
        idp = idp or 0 
        local mult = 10^idp
    
        -- Универсальное Округление для любого числа 
        local rounded = num >= 0 and math.floor(num * mult + 0.5) / mult or num < 0 and math.ceil(num * mult - 0.5) / mult or 0
        
        -- Если число целое, убрать .0
        if rounded == math.floor(rounded) then
            return math.floor(rounded)
        end
    
        return rounded
    end
    local function fit(price, step, scale)
    
      local result = math.floor(price / step) * step;
      log('result = '..tostring(round(result, scale)) );
      return round(result, scale)
    end
    
    ------------------------------------------------------------
    -- === Модуль Risk Manager (по реальному капиталу) ===
    ------------------------------------------------------------
    
    -- Получаем текущий капитал клиента (денежный баланс в рублях или общие активы)
    local function get_current_capital()
    
    --* Свободно - Доступно для вывода средств при сохранении обеспеченности позиций либо открытия позиций по немаржинальным активам, с учетом заблокированных средств под активные заявки, с точностью валюты цены инструмента. Соответствует значению параметра «НаПокупкуНеМаржин» в таблице «Клиентский портфель». Для срочного рынка соответствует значению параметра «План. чист. поз.» в таблице «Ограничения по клиентским счетам» 
    -- lim_non_margin STRING Оценка денежных средств, доступных для покупки немаржинальных инструментов 
    
      if instruments and instruments[1] and instruments[1].class == "TQBR" then
        -- Если не удалось — пробуем через портфель
        local pf = getPortfolioInfoEx(FIRM_ID, CLIENT_CODE, 2)
        if pf and pf.lim_non_margin then
            -- return tonumber(pf.assets)
          --message( "Свободные средства для покупки немаржинальных активов: ".. tostring(pf.lim_non_margin) )
          return tonumber(pf.lim_non_margin)
        end
      end
    
      if instruments and instruments[1] and instruments[1].class == "SPBFUT" then
       local limits = getLimitInfo(FIRM_ID, CLIENT_CODE)
        if limits and limits["План. чист. поз."] then
            local free_for_futures = tonumber(limits["План. чист. поз."])
            message(string.format("Свободные средства для открытия чистых позиций на срочном рынке: %.2f руб.", free_for_futures))
        else
            message("Не удалось получить информацию о свободных средствах для открытия позиций на срочном рынке.")
        end
      end
    
        -- Если ничего не удалось получить — вернём nil
        return nil
    end
    
    local Risk = {
        start_capital = nil,
        current_capital = nil,
        total_change = 0.0
    }
    -- Инициализация начального капитала
    function Risk:init()
        self.start_capital = get_current_capital()
        if not self.start_capital then
            log("x Не удалось определить начальный капитал!" .. tostring(self.start_capital) )
            self.start_capital = 1
        else
            log(string.format("v Стартовый капитал: %.2f руб.", self.start_capital))
        end
    end
    -- Проверка текущего состояния
    function Risk:check()
        local capital = get_current_capital()
        if not capital then
            coroutine.yield()
            return
        end
        self.current_capital = capital
        self.total_change = (capital - self.start_capital) / self.start_capital
        log(string.format("v Текущий капитал: %.2f руб. (%.2f%%)",
            capital, self.total_change * 100))
    
        if self.total_change <= RISK_LIMIT then
            log(string.format("v Просадка %.2f%% превысила лимит %.2f%% — остановка торгов!",
                self.total_change * 100, RISK_LIMIT * 100))
            is_run = false
        end
    end
    
    ------------------------------------------------------------
    -- === Отправка заявок ===
    ------------------------------------------------------------
    local function send_order(class, sec, direction, price)
        local order = {
            CLASSCODE   = class,
            SECCODE     = sec,
            ACCOUNT     = ACCOUNT,
            CLIENT_CODE = CLIENT_CODE,
            OPERATION   = direction,
            PRICE       = tostring(price),
            QUANTITY    = tostring(LOT_SIZE),
            TYPE        = "L",
          ACTION      = "NEW_ORDER",
            TRANS_ID    = tostring(math.random(1000000, 9999999))
        }
    
        local res = sendTransaction(order)
        if res ~= "" then
            log(string.format("[%s] x Ошибка при заявке: %s", sec, res))
        else
            log(string.format("[%s] v %s по %.2f (%d)", sec,
                direction == "B" and "Куплено" or "Продано", price, LOT_SIZE))
        end
    end
    
    ------------------------------------------------------------
    -- === Стратегия по одному инструменту ===
    ------------------------------------------------------------
    local function strategy(class, sec)
        local base_price = nil
        log(string.format("[%s] v Старт стратегии", sec))
        local step = tonumber(getParamEx(class, sec, "sec_price_step").param_value)
        local scale = tonumber(getParamEx(class, sec, "sec_scale").param_value)
        while is_run do
            local price = tonumber(getParamEx(class, sec, "LAST").param_value)
            if not price or price <= 0 then
                coroutine.yield()
            else
                if not base_price then
                    base_price = price
                    log(string.format("[%s] Базовая цена: %.2f", sec, price))
                end
    
                while is_run do
                    local cur = tonumber(getParamEx(class, sec, "LAST").param_value)
                    if cur and cur > 0 then
                        local drop = (cur - base_price) / base_price
                        if drop <= -drop_trigger then
                            log(string.format("[%s] v Падение %.2f%% > Покупка", sec, drop * 100))
                            cur = fit(cur, step, scale)
                            send_order(class, sec, "B", cur)
                            local entry = cur
    
                            -- Контроль позиции
                            while is_run do
                                local now = tonumber(getParamEx(class, sec, "LAST").param_value)
                                if now then
                                    local change = (now - entry) / entry
                                    if change >= take_profit then
                                        log(string.format("[%s]  Тейк +%.2f%% > Продажа", sec, change * 100))
                                        now = fit(now, step, scale)
                                        send_order(class, sec, "S", now)
                                        base_price = now
                                        break
                                    elseif change <= -stop_loss then
                                        log(string.format("[%s] Стоп %.2f%% > Продажа", sec, change * 100))
                                        now = fit(now, step, scale)
                                        send_order(class, sec, "S", now)
                                        base_price = now
                                        break
                                    end
                                end
                                coroutine.yield()
                            end
    
                            log(string.format("[%s] Позиция закрыта, возвращаемся к ожиданию.", sec))
                            break
                        end
                    end
                    coroutine.yield()
                end
            end
            coroutine.yield()
        end
        log(string.format("[%s] x Стратегия завершена", sec))
    end
    
    ------------------------------------------------------------
    -- === Менеджер стратегий ===
    ------------------------------------------------------------
    local Manager = {}
    Manager.tasks = {}
    function Manager:add_strategy(class, sec)
        local co = coroutine.create(function()
            strategy(class, sec)
        end)
        table.insert(self.tasks, {co = co, class = class, sec = sec})
        log(string.format("[%s] v Добавлена стратегия", sec))
    end
    function Manager:run_all()
        local check_interval = os.time()
    
        while is_run do
            -- Проверка капитала каждые 5 секунд
            if os.time() - check_interval >= 5 then
                Risk:check()
                check_interval = os.time()
            end
    
            for _, task in ipairs(self.tasks) do
                local co = task.co
                if coroutine.status(co) == "dead" then
                    log(string.format("[%s] Перезапуск стратегии", task.sec))
                    task.co = coroutine.create(function()
                        strategy(task.class, task.sec)
                    end)
                else
                    local ok, err = coroutine.resume(co)
                    if not ok then
                        log(string.format("[%s] Ошибка: %s", task.sec, tostring(err)))
                        task.co = coroutine.create(function()
                            strategy(task.class, task.sec)
                        end)
                    end
                end
            end
            sleep(300)
        end
        log("x Менеджер остановлен (риск или пользователь).")
    end
    
    ------------------------------------------------------------
    -- === Основной запуск ===
    ------------------------------------------------------------
    function main()
        math.randomseed(os.time())
        log("v Запуск торгового менеджера с реальным риск-контролем...")
        Risk:init()
    
        for _, inst in ipairs(instruments) do
            Manager:add_strategy(inst.class, inst.sec)
        end
    
        Manager:run_all()
    end
    
    ------------------------------------------------------------
    -- === Завершение ===
    ------------------------------------------------------------
    function OnStop()
        is_run = false
        log("v Скрипт остановлен пользователем.")
    end
    
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Да это ранее мы обсуждали, пока настройки передаю, не сильно затратно, но более надежно (ни куда не пропадают при "прыгании" по инструментам, тф.) .
    ООП  в индикаторах тоже попробовал, мнения определенного пока не сложилось, понравилось что таблицы луа органично ложатся на этот подход, классная расширяемость модульность, такое в впечатление что он под луа и создавался (шучу), что не понравилось накладные больше, значительно сложней в отладке. Сейчас пишу небольшие задачи в двух вариантах подходов. Пытаюсь как мы с Вами сейчас разобраться, что для меня приемлемей.
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Nikolay,  Я с Вами не спорю, и ни чего не утверждаю, просто пытаюсь разобраться в возможностях LUA, то что подразумевается под понятием оптимизация, когда какой метод применять? Для себя некую шпаргалку сделать, пределы использования подхода. Да, обсуждение полезно, не много по другому взглянул на подходы. Замыкание использовал в основном при написании индикаторов там это логично. Ну чтобы с Вами окончательно согласиться или нет, нужна собственная история, буду пробовать. Надеюсь что не только мне было интересно и познавательно, но и многим нашим коллегам. Вам большое спасибо за предметное обсуждение.
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Поле -* Свободно - Доступно для вывода средств при сохранении обеспеченности позиций либо открытия позиций по не маржинальным активам, с учетом заблокированных средств под активные заявки, с точностью валюты цены инструмента.

    Соответствует значению параметра «НаПокупкуНеМаржин» в таблице «Клиентский портфель».

    Для срочного рынка соответствует значению параметра «План. чист. поз.» в таблице «Ограничения по клиентским счетам».
    параметр - lim_non_margin STRING Оценка денежных средств, доступных для покупки не маржинальных инструментов .

    По факту - это средства доступные для торговли (для проведению торговых операций), А каково, как оформлено, судите сами?

    Ну что тут скажешь, не в нашу копилочку талантов! Ну не специально же люди так старались?
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Все о великом да о великом! А элементарные вещи: Как при торговле на маржинальном счете Акциями, получить "Свободный денежный остаток"? Во Задача!
    Сколько лет уже пользуюсь QUIK но ответ на этот вопрос взывает смятение.
    Элементарный вопрос для каждого, оценить капитал в распоряжении. Стоимость Активов в бумагах + Свободные денежные средства = Активы клиента.

    Как получить Эти "Свободные денежные средства", без выкрутасов? Во Задача?
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    TGB,  Вы в начале полностью прочитайте обсуждение, а затем рекомендации выдавайте. Если рассмотреть пример с фреймворком то он значительно легче, если подобную логику выстраивать подходом с замыканиями.

    По образованию инженер тяжело расстаюсь с техникой. А мой еще хоть куда, даст фору некоторым что сегодня в продаже. Это лучшая пока машинка из всех что были у меня (Sony, Toshiba, Asus ..) хотя и 3 поколение i7 прекрасно справляется с 3 квиками 5 браузерами и еще всякой всячиной. Проблема есть с WiFi старое поколение ограничена скорость приема данных 200, а нужно 1000.  
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    TGB,  А Вам это о чем то говорит: ОС > Терминал > Lua? У Вас мощная машина, а у меня старенький 13 летний? 2. Еще в позапрошлом ВЕКЕ придумали "семафор" и он прекрасно справляется с пропусками поездов до сих пор. А пользоваться или нет параллельным программированием - решает пользователь для себя сам. Что касается меня так я примитивный пользователь - СЕМАФОР!  :smile:  
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Да, но в подходе с замыканиями - приходится вручную хранить состояние "этапа" в цикле "подожди, пока". А код постепенно превращается в "машину состояний". При добавлении новых этапов он становится всё громоздче и сложней. Отсюда и вывод для простых задач!

    В подходе с корутинами. Никаких ручных "этапов" — код читается как обычный последовательный сценарий. Можно использовать yield где угодно, чтобы "заморозить" выполнение.  Можно легко добавить ожидание событий, сетевые операции и т.д. Логика выглядит линейно, но выполняется асинхронно.
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    TGB,  Ну так это без тестов пoнятно конечно, чуть больше, т.к. стек корутины сохраняется.

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

    Nikolay,  Ну как же нет? Смотрим на саму суть:

    * Что делает замыкание? Создало локальную переменную.

    * Что делает корутина? Создало независимы поток (без задержек и не блокирует QUIK с минимальной нагрузкой на CPU)

    Преимущества очевидны:
    1.  Не блокирует QUIK.
    2.  Можно запустить много задач одновременно (мониторинг, логирование, анализ, обновление лимитов).
    3.  Лёгкая расширяемая структура — можно добавлять задачи динамически.
    4. Поддерживает интервалы выполнения (как cron).

    Посмотрим, что такое «лёгкий поток» — coroutines (сопрограммы) в контексте Lua? Это кооперативные задачи, которые работают внутри одного потока исполнения, но могут приостанавливать и возобновлять выполнение, передавая управление вручную. yield() - приостанавливает только задачу, не весь скрипт.

    По каким признакам это "лёгкий поток"?
    1. Лёгкий по памяти. Корутине нужно всего несколько КБ стека.
    2. Быстрый. Не создаёт системных потоков/контекстов.
    3. Кооперативный. Поток сам решает, когда уступить управление.
    4. Полный контекст исполнения. Сохраняются локальные переменные, состояние и позиция.
    5. Пауза без блокировки QUIK. yield() приостанавливает только задачу, не весь скрипт.

    * Каротину можно считать лёгким потоком Lua — она живёт, имеет собственный контекст и исполняется пошагово.
    * А `main()` — просто планировщик, который возобновляет _ Ну так это без тестов пoнятно конечно, чуть больше, т.к. стек корутины сохраняется.

    Резюме. В Lua корутины — это лёгкие, управляемые потоки. Они позволяют выполнять фоновую работу, не блокируя основной цикл QUIK. В QUIK это единственный безопасный способ организовать "многозадачность" без внешних библиотек.
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Подсобрался с мыслями. Можно некий итог подвести.

    Различия двух подходов, про применение см. выше мнение не поменял.

    1. Подход с замыканием.
    Нет возможности приостанавливать сложные задачи на середине выполнения.
    Сложно реализовать ожидание событий или таймеров внутри задачи без внешнего кода.
    Каждая задача должна сама заботиться о том, как уступать управление (через sleep в основном цикле).

    2. Подход с корутиной.
    Внутри корутины можно писать сложные алгоритмы и «разбивать» их на шаги.
    Более гибкая модель.
    Можно приостанавливать задачу на любом шаге.
    Можно легко комбинировать несколько фоновых задач в одном цикле.
    Более похоже на настоящие асинхронные потоки.
    Сложнее.
    Немного больше накладных расходов на создание и управление корутиной (хотя для QUIK это не критично).
    coroutine.yield() - приостанавливает выполнение задачи, позволяя основному циклу выполняться и управлять её запуском.

    Пока разбирались на кидал лёгкий планировщик фоновых задач для QUIK. Всем пока хорошего кода.
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Ну хорошо вот другой пример:
    Код
    function main()
        threads[1] = coroutine.create(function() worker("A", 2) end)
        threads[2] = coroutine.create(function() worker("B", 5) end)
    
        while is_run do
            for _, co in ipairs(threads) do
                if coroutine.status(co) ~= "dead" then
                    coroutine.resume(co)
                end
            end
            sleep(200)
        end
    end
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Отличие прежнее.

    Вы в потоке маин вызываете псевдоним функции выше, после выполнения функции цикл  приостанавливается sleep(1000) .
    В то время как в примере с корутиной  имеется свой легки поток без задержек, а   coroutine.yield()  -- уступаем управление (подвешивает задачу) не создавая ни каких задержек, но цикл то маин выполняется. И таких задач можно вешать сколько угодно, в то время как в Вашем варианте после каждой задачи задержка.
    Код
    -- "Лёгкий поток" для фоновой задачиlocal function background_task()
        local count = 0
        while is_run do
            count = count + 1
            message("Фоновая задача: шаг " .. count)
            coroutine.yield()  -- уступаем управление
        end
    end
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Да но есть ключевое отличие. yield() - приостанавливает только задачу, не весь скрипт как sleep.

    --- Ключевая идея лёгкого потока (coroutine - кооперативная задача). Этот код создаёт лёгкий поток (фактически, сопрограмму), который выполняется по шагам, каждый шаг — между yield() и resume().
    Код
    local co = coroutine.create(function()
        for i = 1, 5 do
            print("Работаю...", i)
            coroutine.yield() -- приостанавливаем "поток"
        end
    end)
    
    while coroutine.status(co) ~= "dead" do
        coroutine.resume(co) -- возобновляем
        sleep(100)
    end
    Код
    --- Пример «лёгкого фонового потока» в QUIK
    
    local is_run = true
    
    -- "Лёгкий поток" для фоновой задачи
    local function background_task()
        local count = 0
        while is_run do
            count = count + 1
            message("Фоновая задача: шаг " .. count)
            coroutine.yield()  -- уступаем управление
        end
    end
    
    function main()
        local co = coroutine.create(background_task)
        message("Запуск фонового лёгкого потока...")
        while is_run do
            coroutine.resume(co)  -- возобновляем поток
            sleep(1000)           -- имитация "асинхронного" поведения
        end
    end
    
    function OnStop()
        is_run = false
        message("Остановка фонового потока.")
    end
    
    
    -- * Корутину можно считать лёгким потоком Lua — она живёт, имеет собственный контекст и исполняется пошагово.
     
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Нашел. Вот когда то я сохранял для себя понимание. Что то с родни Вашему подходу.

    Lua предоставляет "лёгкие потоки" — coroutines (сопрограммы). Это кооперативные задачи, которые работают внутри одного потока исполнения, но могут приостанавливать и возобновлять выполнение, передавая управление.

    --Ключевая идея лёгкого потока (coroutine)

    local is_run = true
    local threads = {}

    function worker(name, delay)
       local i = 0
       while is_run do
           i = i + 1
           message(string.format("[%s] шаг %d", name, i))
           for _ = 1, delay do coroutine.yield() end
       end
    end

    function main()
       threads[1] = coroutine.create(function() worker("A", 2) end)
       threads[2] = coroutine.create(function() worker("B", 5) end)

       while is_run do
           for _, co in ipairs(threads) do
               if coroutine.status(co) ~= "dead" then
                   coroutine.resume(co)
               end
           end
           sleep(200)
       end
    end

    function OnStop() is_run = false end

    Это — мини-диспетчер потоков на чистом Lua.
    Каждый "поток" (worker) — корутина с собственным ритмом, а цикл в main() играет роль планировщика.

    * Корутину можно считать лёгким потоком Lua — она живёт, имеет собственный контекст и исполняется пошагово.

    * А main() — просто планировщик, который возобновляет корутины.
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Цитата
    Nikolay написал:
    А значит выполняться она может тоже только через вызовы в основном цикле mian. И тоже частота вызовов регулируется задержкой цикла main.Собственно ваш любимый hacktrade так и работает.
    Вы не поняли сути hacktrade, как раз он как пример такой асинхронности.

    1. При первом в ходе создается корутина и мы работаем в ее цикле, а у нее никаких задержек, (кроме накладных), в ней идет вся торговая логика расчеты, вычисление и так далее. Останавливаем исполнение когда выполнены условия на отправку заявки и заявка отправлена (выполняется yield).

    2. Структурно код стал на выполнение в цикле маин (с задержками sleep)  функционально идет работа с заявкой. Где тут последовательность?
    Самая что не на есть асинхронность.

    Выполнение кода структурно разделено на две составляющие:
    А) Поиск условия выполнения заявки (в цикле корутины);
    Б) Не посредственное исполнение заявки (взаимодействие с апи квик - в цикле маин).  
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Смотрим внимательно, сравниваю эти два подхода.

    На первый взгляд оба примера — "фоновые циклы" в QUIK, но они не эквивалентны и не взаимозаменяемы?

    Это разные подходы к организации фоновой работы:

    Вариант 1 — "корутина как фоновый поток".

    local co = coroutine.create(worker)
    while is_run do
       coroutine.resume(co)
       sleep(1000)
    end

    Вариант 2 — "итераторы (замыкания) как фоновые задачи".

    local worker1 = counter(1, 5, print)
    local worker2 = counter(2, 20, print)

    while is_run do
       if worker1 and worker1() then worker1 = nil end
       --...
       sleep(100)
    end

    Различие по сути:

    | Критерий                     | Вариант 1 (корутина)                 | Вариант 2 (итераторы/счётчики) |
    | ----------------------------     | ------------------------------------       | ------------------------------     |
    | Тип выполнения               | Асинхронное (через yield)            | Синхронное (через цикл)        |
    | Внутреннее состояние         | Контекст сохраняется автоматически   | Состояние хранится в замыкании |
    | Можно делать yield внутри    | Да                                   | Нет                            |
    | Можно использовать sleep()   | Только в main, не в worker           | В основном цикле               |
    | Уровень сложности            | Средний                              | Простой                        |
    | Производительность           | Чуть выше накладных вызовов          | Зависит от частоты цикла       |
    | Где применять                | Долгие фоновые задачи                | Мелкие периодические проверки  |

    Вывод. Не взаимозаменяемы!

     Первый вариант — "реальный фон через корутину" (асинхронная модель).
     Второй — "упрощённый псевдопараллельный цикл" (итераторы).
     
    Мнение.

    1. Если нужно имитировать многопоточность (параллельно опрашивать QUIK, считывать данные и т.п.) -> вариант с корутиной.
    2. Если просто нужно выполнять несколько лёгких задач с разной частотой -> вариант с замыканиями/итераторами.
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Нет, дело тут не в счетчике, счетчик в моем примере это просто минимальная операция для примеров. Дело в этом:
    Цитата
    Nikolay написал:
    Вместо теста скорости можно вставить любую периодическую задачу: * запрос рыночных данных (getParamEx), * обновление таблиц QUIK, * контроль позиций и алертов, * логирование в файл.и так далее.
    Корутина  превращает прямолинейную реализацией - последовательное чтение функций, в не линейное исполнение разных задач. В едином потоке луа появляется, как бы несколько раздельно выполняемых задач. Мы же можем к сопрограммам написать и менеджер, по их управлению.  
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Цитата
    Nikolay написал:
    Корутина с остановкой удобна, если надо запомнить состояние и вернуться к выполнению
    Так это и есть ключ. Помнит место остановки и туда возвращается. Если подобное исполнение делать с помощью функции, нужно что то наверное в парадигме машины состояний. А затем придется еще этим состояниями управлять.

    По чему мне нравится подход с корутинами. Логику можно строить так, как мы с Вами это сейчас обсуждаем. Вот прямо могу написать код обсуждения, асинхронно получить время, послушать новости, и еще чего то там. Мне кажется круто!
    Кстати навело на мысль попробовать в отдельной корутине отслеживать состояние ордеров, сейчас двух факторная по транс ид и номеру ордера из колбеков, а что если не зависимо все подтвердили тогда только учитываем? Попробую.
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Дело тут не в терминологии, а в особенностях применения сопрограмм в Lua, и те возможностях которые возникают от их применения. Исполнять код по принципу "который нужно, когда нужно"!  
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Вот и Вы туда же? Цель:
    Цитата
    VPM написал:
    сделать фоновый поток в QUIK с использованием корутины, без блокировки терминала и с периодическим выводом результата.
    Скорость ограничена скорость получения данных, "Не прыгнешь выше своей головы":
    Цитата
    VPM написал:
    И главное здесь, сама система: "Биржа - Сервер - Терминал - Скрипт"
    Измерение скорости в наших условиях для наших задач, нужно только для оптимизации скрипта.  А у нашего "Профессора", идет путаница, он пытается обрабатывать большие массивы, где безусловно важным факторам является скорость операций, с нашими повседневными задачами, ограниченными по скорости передачи данных. Это и пытаюсь донести, обижается?  :sad:    Насчет смысла, смыл писать эффективный код, как же нет?
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Nikolay,  На примере код TGB,  выложил два простых скрипта, различных по своей архитектуре. Приведите пример Вашего подхода, возможно он действительно будет оптимальной и лучше? Я ведь на самом то деле, ни чего не отрицаю и ни утверждаю, а декларирую возможности Lua,  и возможности создавать шедевры. Давайте сравним, всем будет интересно! Возможно я заблуждаюсь?  
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Ну и последнее. Для Профессора и не только.

    Приведу пример, как сделать фоновый поток в QUIK с использованием корутины, без блокировки терминала и с периодическим выводом результата.

    --- Цель. Скрипт main() должен:

    * стартовать и создать фоновый поток,
    * этот поток будет каждые несколько секунд выполнять тест,
    * не блокировать работу QUIK,
    * безопасно завершаться при остановке скрипта.
    Код
    --- Рабочий пример фонового скрипта QUIK с корутиной
    local is_run = true  -- глобальный флаг для остановки фоновой задачи
    
    function main()
        message("Скрипт запущен. Идёт фоновое измерение производительности...")
    
        -- создаём корутину (фоновую задачу)
        local co = coroutine.create(worker)
    
        -- основной цикл QUIK
        while is_run do
            local ok, err = coroutine.resume(co)
            if not ok then
                message("Ошибка в корутине: " .. tostring(err))
                break
            end
            sleep(1000) -- основной поток спит 1 сек между циклами
        end
    
        message("Скрипт остановлен.")
    end
    
    -- фоновая функция
    function worker()
        local N = 100000
        local c, f = 0, 0
    
        local function foo()
            f = f + 1
        end
    
        local co_task = coroutine.create(function()
            while true do
                c = c + 1
                coroutine.yield()
            end
        end)
    
        while is_run do
            local TT1 = os.clock()
            for i = 1, N do
                local ok, err = coroutine.resume(co_task)
                if not ok then
                    message("Ошибка в co_task: " .. tostring(err))
                    break
                end
            end
            TT1 = (os.clock() - TT1) * 1000
    
            local TT2 = os.clock()
            for i = 1, N do
                foo()
            end
            TT2 = (os.clock() - TT2) * 1000
    
            local out = string.format(
                "Фон: N=%d, coroutine=%.3f мс (c=%d), func=%.3f мс (f=%d), T1/T2=%.2f",
                N, TT1, c, TT2, f, TT1 / TT2
            )
            message(out)
    
            sleep(3000) -- измерение каждые 3 секунды
        end
    end
    
    function OnStop()
        is_run = false
        message("Останавливаем фоновую задачу...")
    end
    
    Вывод:
       TYPE   DATE   TIME   MESSAGE
    1   1   29.10.2025   10:31:19   Скрипт запущен. Идёт фоновое измерение производительности...
    2   1   29.10.2025   10:31:20   Фон: N=100000, coroutine=584.000 мс (c=100000), func=6.000 мс (f=100000), T1/T2=97.33
    3   1   29.10.2025   10:31:23   Фон: N=100000, coroutine=604.000 мс (c=200000), func=7.000 мс (f=200000), T1/T2=86.29
    4   1   29.10.2025   10:31:27   Фон: N=100000, coroutine=586.000 мс (c=300000), func=7.000 мс (f=300000), T1/T2=83.71
    5   1   29.10.2025   10:31:30   Фон: N=100000, coroutine=553.000 мс (c=400000), func=7.000 мс (f=400000), T1/T2=79.00
    6   1   29.10.2025   10:31:34   Фон: N=100000, coroutine=614.000 мс (c=500000), func=7.000 мс (f=500000), T1/T2=87.71
    7   1   29.10.2025   10:31:38   Фон: N=100000, coroutine=550.000 мс (c=600000), func=7.000 мс (f=600000), T1/T2=78.57
    8   1   29.10.2025   10:31:41   Фон: N=100000, coroutine=555.000 мс (c=700000), func=7.000 мс (f=700000), T1/T2=79.29
    9   1   29.10.2025   10:31:45   Фон: N=100000, coroutine=544.000 мс (c=800000), func=6.000 мс (f=800000), T1/T2=90.67
    10   1   29.10.2025   10:31:45   Останавливаем фоновую задачу...
    11   1   29.10.2025   10:31:49   Скрипт остановлен.
    
    --- Как это работает:

    | Часть       | Что делает                                                                                                                                         |
    | ------------  | ---------------------------------------------------------------------------------------- ------------------------------------------|
    | main()      | Запускается QUIK ом. Создаёт и запускает корутину worker().                                                      |
    | worker()   | Основная логика теста — циклически выполняет измерения и выдаёт результат в message(). |
    | sleep()     | Позволяет не блокировать интерфейс и экономит CPU.                                                                 |
    | OnStop() | Срабатывает при остановке скрипта пользователем — корректно завершает цикл.                    |

    --- Что можно делать в фоне?

    Вместо теста скорости можно вставить любую периодическую задачу:
    * запрос рыночных данных (getParamEx),
    * обновление таблиц QUIK,
    * контроль позиций и алертов,
    * логирование в файл.
    и так далее.

    Корутины в QUIKэто идеальный инструмент для таких сценариев, они позволяют делать "псевдопотоки", не мешая интерфейсу и другим скриптам!!!

    Это и есть основной смысл и задача Корутин в QUIK! Надеюсь убедил? Всем хорошего кода.  :smile:  
    Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
     
    Помним!
    Время работы корутин в QUIK может зависеть от множества факторов:
    * нагрузки платформы,
    * количества исполняющихся скриптов,
    * задержек в сети
    и так далее.

    Код
    --[[Помним!
    Время работы корутин в QUIK может зависеть от множества факторов: 
    * нагрузки платформы, 
    * количества исполняющихся скриптов, 
    * задержек в сети
     и так далее.
    --]]
    
    -- Переписанный пример с использованием корутин и пустой функции в QUIK в формате кода Lua:
    
    --local message = message or print;
    local run = true
    
    function main()
    
       local N = 100000
       local c,f = 0,0
       -- создаем корутину, которая будет выполнять асинхронную задачу
       local co = coroutine.create(function()
          local yield = coroutine.yield
          while true do
             -- можно выполнять любую логику
           c = c + 1
             yield()  -- здесь задача будет приостанавливаться
          end
       end)
    
       -- простая функция, которая ничего не делает
       local function foo1() 
            f = f + 1
            return f
       end
    
       local out = 'Результат теста: количество вызовов = ' .. N .. ', время вызова сопрограммы '
       while true do
            
          -- замер времени для корутины
            local TT1 = os.clock()
            for i = 1, N do
                local ok, err = coroutine.resume(co)  -- возобновляем выполнение корутины
                if not ok then
                    message("Ошибка при выполнении корутины: " .. err)
                    break
                end
            end
            TT1 = (os.clock() - TT1) * 1000
          message("TT1: " .. TT1 .."; c = " .. c)
    
            -- замер времени для обычной функции
            local TT2 = os.clock()
            for i = 1, N do
                foo1()
            end
          TT2 = (os.clock() - TT2) * 1000
          message("TT2: " .. TT2 .."; f = " .. f)
    
            -- вывод результата
            out = out .. string.format("%.3f мс, функции %.3f мс, T1/T2 = %.2f", TT1, TT2, TT1 / TT2)
            message(out)
             return
       end
       run = false
    
    end
    
    Результаты у одного брокера:
       TYPE   DATE   TIME   MESSAGE
    1   1   29.10.2025   9:47:15   TT1: 555.00000000029; c = 100000
    2   1   29.10.2025   9:47:15   TT2: 7.9999999998108; f = 100000
    3   1   29.10.2025   9:47:15   Результат теста: количество вызовов = 100000, время вызова сопрограммы 555.000 мс, функции 8.000 мс, T1/T2 = 69.38
    

    Ну по крайней мере, что то похожее на обсуждение!  :smile:  
    Страницы: Пред. 1 2 3 4 5 6 7 8 9 10 11 ... 29 След.
    Наверх