Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 15.06.2023
09.10.2025 11:39:30
Пару слов про показатели метода.
Забегая вперед отмечу, что прекрасно вписываются в данный подход и метрики трейдинга: * Профит фактор, * Процент выигрышных сделок, * Мат. ожидания системы, без расчета которого не должна существовать ни одна система, в том числе и данная. Управление капиталом вообще отдельная тема, но я сейчас про управление системой как бизнес. Вот система метрик ни чего нового, то как понимаю подход, структурированная по трем основным отчетам и ключевым бизнес-процессам.
1. Метрики Операционной деятельности (Income Statement & Cash Flow). Цель: Оценить эффективность генерации свободных денежных средств — «топлива» для инвестиций.
* Коэффициент сбережений (Savings Rate): * Формула: (Доходы - Расходы) / Доходы * 100% * Смысл: Это*рентабельность по чистой прибыли (Net Profit Margin) нашего личного «бизнеса». Показывает, какая доля дохода остается после всех жизненных «операционных расходов». Цель — постоянное увеличение. * Свободный денежный поток (Free Cash Flow - FCF): * Формула: Доходы - Все расходы (включая налоги и обязательные платежи) * Смысл: Абсолютная сумма в рублях/долларах, которую вы можете направить в инвестиционную деятельность (CFI) без привлечения заемных средств. Это главный источник роста.
2. Метрики Финансовой деятельности (Balance Sheet & Leverage). Цель: Оценить финансовое здоровье, структуру капитала и риски, связанные с долгом.
* Коэффициент долговой нагрузки (Debt-to-Income Ratio - DTI): * Формула: Сумма ежемесячных платежей по долгам / Ежемесячный доход * 100% * Смысл: Показывает, какая часть вашего операционного cash flow съедается обслуживанием долга. >40-50% — сигнал опасности. * Коэффициент финансового рычага (Leverage Ratio): * Формула: Обязательства / Активы или Обязательства / Собственный капитал (Equity) * Смысл: Показывает, насколько вы зависите от заемных средств. Для частного инвестора цель — низкое значение (например, <0.3 или 30%). * Стоимость заемного капитала (Cost of Debt): * Формула: Средневзвешенная процентная ставка по всем кредитам * Смысл: Критически важная метрика. Доходность от инвестиций (CFI) должна быть заведомо выше этой цифры.
3. Метрики Инвестиционной деятельности (Performance & Efficiency) Цель: Оценить эффективность управления портфелем-проектом.
* Общая доходность портфеля (Total Portfolio Return): * Формула: (Текущая стоимость портфеля - Внесенные средства) / Внесенные средства * 100%` * Смысл: ROI (Return on Investment) для всего вашего «бизнеса». Показывает общую эффективность. * Доходность с учетом дивидендов (Total Return): * Смысл: Важно учитывать не только рост цены, но и реинвестируемый cash flow (дивиденды, купоны). Это более точная метрика. * Скорректированная на риск доходность (Risk-Adjusted Return): * Метрика: Коэффициент Шарпа. * Смысл: Показывает, сколько дохода вы получаете за единицу риска (волатильности). Доходность 20% при волатильности 5% гораздо лучше, чем те же 20% при волатильности 25%. * Сравнение с бенчмарком (Benchmark Tracking): * Метрика: Отставание/опережение индекса (например, IMOEX или MSCI World). * Смысл: Ответ на вопрос: «А не мог бы я просто купить индекс и спать спокойно?». Если вы отстаете от бенчмарка, ваш «активный» управленческий труд не приносит добавленной стоимости.
4. Метрики Баланса (Balance Sheet Health) Цель: Оценить структуру и качество активов.
* Аварийный фонд (Emergency Fund Ratio): * Формула: Сумма на "черный день" / Среднемесячные расходы * Смысл: Показывает, на сколько месяцев вы можете прожить без дохода. Цель — 3-6 месяцев. Это коэффициент ликвидности. * Аллокация активов (Asset Allocation): * Метрика: % акций, % облигаций, % кэша, % альтернативных активов * Смысл: Главный драйвер риска и доходности. Отслеживание отклонений от целевой аллокации — основа управления портфелем. * Диверсификация (Diversification Metrics): * Метрики: * Количество эмитентов/стран/секторов. * Максимальная доля одного актива в портфеле.* (Например, правило: не более 5% на одну акцию). * Смысл: Показывает уровень несистематического риска. Снижает риск банкротства одного «проекта» для всей «компании».
5. Сводная таблица KPI для CEO «Я Holdings»**
| Сфера | Ключевая метрика | Целевое значение | ----------------- | ----------------------------------------- | ------------------------------------------------- | Операции | Коэффициент сбережений (>20%) | Постоянный рост | | Свободный денежный поток (FCF) | Положительный и растущий | Финансы | Коэффициент долговой нагрузки (DTI) (<30%)| Менее 30% | | Стоимость заемного капитала | Минимизировать; < ожидаемой доходности портфеля | Инвестиции | Общая доходность (Total Return) | Опережать инфляцию + целевой бенчмарк | | Коэффициент Шарпа | >1 (чем выше, тем лучше) | Баланс/Риски | Аварийный фонд | 3-6 месяцев расходов | | Отклонение от целевой аллокации | Не более ±5% по каждому классу активов | | Макс. доля одного актива | Не более 5%
Управляя этими метриками, мы превращаемся из пассивного владельца активов в активного CEO, который на основе данных принимает решения: * о распределении капитала, * управлении рисками * и стратегическом развитии своего главного бизнеса «Я Holdings» — собственного финансового будущего. ----------------- А не кто и не обещал, что будет легко.
Пользователь
Сообщений: Регистрация: 15.06.2023
11.10.2025 11:07:26
"... С голубого ручейка начинается река ..."
Внесу уточнение в модель, когда мы рассматриваем инвестиционную компанию как самостоятельный бизнес, фокус несколько смещается, и на мой взгляд упрощается, ведь управление отдельной компанией проще управления холдингом, сохраним рабочее название прежним «Я Holdings». Уточним модель для этого случая.
В этой модели компания «Я Holdings» — это самостоятельный бизнес, который живет по своим внутренним законам. Её цель — самоокупаемость и рост за счет реинвестирования прибыли.
Ключевые допущения: * Источник средств: Первоначальные взносы учредителей (наши личные накопления). * Доходы: Только доходы от инвестиционной деятельности (дивиденды, купоны, реализованная прибыль от продажи активов). * Расходы: Все изъятия средств учредителями на личные цели, а также комиссии и налоги. * Реинвестирование: Вся нераспределенная прибыль остается в компании и реинвестируется.
Три главных отчета остаются прежними, внесу лишь уточнение, в Отчет о движении денежных средств (ДДС) — «Хватает ли компании денег?»*. Это по-прежнему самый важный отчет, но его структура меняется.
* Операционная деятельность (ОСНОВНАЯ): Доходы от инвестиций. * Приток: Дивиденды, купоны, проценты. * Отток: Комиссии брокеров, оплата услуг управляющего. * Инвестиционная деятельность (ЦЕЛЕВАЯ): Покупка и продажа активов. * Отток: Покупка новых акций, облигаций. * Приток: Продажа активов (возврат капитала). * Финансовая деятельность (СВЯЗЬ С ВЛАДЕЛЬЦЕМ): Взаимодействие с учредителем. * Приток: Дополнительные взносы учредителя. * *Отток: Изъятия учредителя на личные расходы.
Главная формула успеха для самоокупаемой компании: Операционный Денежный Поток >= Изъятия (Финансовая деятельность) * Цель достигнута: Если дивидендов и купонов хватает на все ваши изъятия, значит, компания вышла на самоокупаемость. Вы живете на доход от ее деятельности, не трогая основной капитал. * Цель не достигнута: Если изъятия больше операционного потока, вам приходится продавать активы (Инвестиционная деятельность) или докладывать свои деньги (Финансовая деятельность), что тормозит рост.
2. Отчет о прибылях и убытках (ОПУ) — «Прибыльна ли компания?»
* Выручка: Совокупный инвестиционный доход (дивиденды + купоны + реализованная прибыль от продаж). * Расходы: Комиссии, налоги. * Чистая прибыль / убыток: Выручка - Расходы. * Важный нюанс: Нереализованная прибыль/убыток (изменение стоимости портфеля) здесь не отражается. Этот отчет показывает реальный денежный результат.
3. Баланс — «Сколько стоит компания?»
* АКТИВЫ: Только инвестиционные активы. Деньги на брокерском счете, акции, облигации, ETF. (Личные активы типа машины и квартиры теперь не учитываются — они принадлежат нам лично, а не компании ). * ОБЯЗАТЕЛЬСТВА: Кредитное плечо, маржинальные займы. * КАПИТАЛ: Активы - Обязательства. Это чистая стоимость вашего инвестиционного бизнеса. Рост капитала — главная KPI. ---
Пользователь
Сообщений: Регистрация: 15.06.2023
11.10.2025 11:18:53
Новая роль учредителя. В этой модели мы — не просто CEO, а учредитель и основной бенефициар (отдельной компании). Наша задача — управлять компанией «Я Holdings» так, чтобы:
1. Ее операционная деятельность (доходы от инвестиций) полностью покрывала ваши финансовые изъятия (вашу "зарплату"). 2. Ее инвестиционная деятельность (реинвестирование прибыли) обеспечивала постоянный рост капитала для защиты от инфляции и увеличения будущих доходов.
Когда эти условия выполняются, наш инвестиционный бизнес становится по-настоящему пассивным источником дохода и финансовой независимости.
Переход от бухгалтерской констатации фактов и финансового планирования к настоящему финансовому анализу. Да, мы должны это оценить. «И тогда наверняка вдруг запляшут облака» — метафора для этого эффекта, который дают эти рычаги: они могут вознести к облакам, но могут и породить грозу.
Оценим операционный и финансовый рычаг для нашей инвестиционной компании «Я Holdings».
1. Операционный рычаг (Леверидж) в инвестиционной компании. Что это такое? В классическом бизнесе — это отношение постоянных затрат к переменным. В нашем случае — это структура доходов компании. * Компания с ВЫСОКИМ операционным рычагом: Доход формируется в основном за счет роста капитала (продажа подорожавших активов). Это акции роста, венчурные проекты. Доля дивидендов мала. * Компания с НИЗКИМ операционным рычагом: Доход формируется в основном за счет текущих денежных потоков (дивиденды, купоны). Это дивидендные аристократы, облигации.
Как оценить? Коэффициент операционного рычага = (Доход от прироста капитала) / (Денежный доход (дивиденды + купоны)). * > 1 (Высокий рычаг):** Ваша прибыль сильно зависит от рыночной конъюнктуры. На бычьем рынке «облака запляшут» — доходность взлетит. На медвежьем — у вас не будет стабильного денежного потока для изъятий, придется продавать активы в убыток. * = 1 (Сбалансированный):** Золотая середина. Есть и рост, и стабильный cash flow. * < 1 (Низкий рычаг):** Ваша компания устойчива. Она генерирует стабильный денежный поток, почти не зависящий от курсовых колебаний. Но и взлететь к «облакам» ей сложнее.
Пример: а) Портфель А (Высокий рычаг): За год получили 50 тыс. руб. дивидендов и 500 тыс. руб. прибыли от продажи акций. Рычаг = 500 / 50 = 10. Высокий. Доходность «пляшет» вместе с рынком. б) Портфель Б (Низкий рычаг): За год получили 400 тыс. руб. дивидендов и 100 тыс. руб. прибыли от продаж. Рычаг = 100 / 400 = 0.25. Низкий. Компания стабильна, как скала.
2. Финансовый рычаг (Леверидж) в инвестиционной компании. Что это такое? Это использование заемных средств для увеличения размера инвестируемого капитала. То самое «плечо» (leverage). Как оценить? Есть несколько ключевых метрик: а) Коэффициент финансового левериджа = Обязательства / Капитал. * 0.5 (1:2): На 1 рубль заемных средств приходится 2 рубля собственных. Консервативно. * 1.0 (1:1): Соотношение 50/50. Агрессивно. * > 1.0: Заемных средств больше, чем собственных. Крайне рискованно.
б) Ставка покрытия долга = (Операционный Денежный Поток) / (Проценты по долгу). * > 3: Отлично. Доходы более чем в 3 раза покрывают процентные платежи. * 1 - 2: Опасно. Почти все денежные потоки съедают проценты. * < 1: Критично. Вы не можете обслуживать долг из текущих доходов.
Совместный эффект двух рычагов. Вот где начинается настоящая магия и настоящий риск. Эффекты рычагов умножаются, а не складываются!
Сценарий 1: «Облака запляшут» (Золотой сценарий). * Высокий операционный рычаг - (портфель из растущих акций) + Умеренный финансовый рычаг (дешевое плечо 1:0.5). * Результат: На бычьем рынке мы получаем доходность не только на свои деньги, но и на заемные. Наш капитал растет опережающими темпами.
Сценарий 2: «Гроза и шторм» (Катастрофический сценарий). * Высокий операционный рычаг** (волатильные активы) + Высокий финансовый рычаг (плечо 1:1 или выше). * Результат: На медвежьем рынке падение стоимости залога приводит к маржин-коллу. Брокер принудительно продает наши активы по низким ценам, фиксируя убытки и уничтожая наш капитал. Это тот случай, когда «облака» не пляшут, а обрушиваются ливнем.
Сценарий 3: «Уверенный рост» (Стратегия богатого папы). * Низкий операционный рычаг - (портфель генерирует стабильный cash flow) + Умеренный финансовый рычаг (дешевый долг, например, ипотека на доходную недвижимость). * Результат: Наш арендный денежный поток стабильно покрывает платежи по кредиту и наши изъятия. Компания растет надежно и предсказуемо.
Резюме для CEO «Я Holdings». 1. Операционный рычаг — это наш выбор стратегии (рост vs. доход). Определяет волатильность нашей прибыли. 2. Финансовый рычаг — это наш источник финансирования (свои vs. заемные). Определяет уровень риска ликвидации. 3. Вместе они создают коктейль с непредсказуемым эффектом. Высокие значения обоих рычагов — удел профессионалов, готовых к полной потере капитала.
Теперь наша задача как управляющего. Найти такое сочетание рычагов, который соответствует нашему темпераменту, опыту и горизонту инвестирования. Для большинства «облака запляшут» при умеренном операционном и минимальном финансовом рычаге. Помним: рычаг режет в обе стороны. ------------------------
Мы переходим от качественного анализа к количественному управлению, что является сутью профессионального подхода. И нашей конечной целью, создать такой алгоритм и написать код скрипта, который бы однозначно превращал пассивный доход в основной доход инвестора!
Формализуем эту задачу. Эту проблему идеально моделировать как задачу оптимального управления с граничными условиями, где мы управляем двумя видами рычагов для максимизации функции роста капитала при ограничении на максимальный убыток. Но это уже другая тема.
Пользователь
Сообщений: Регистрация: 15.06.2023
22.10.2025 10:51:03
Сказки на ночь, или как вылечить "чихание" колбеков в квик.
Начну с начала.
Сказка1. Допустил на инвестиционном счете (прошу не путать со спекулятивным) приличные просадки по отдельным бумагам, не то чтобы катастрофа, но "глаз мозолит". Решение было моментальное - усреднить. Счет маржинальный со средствами нет проблем. Огляделся вокруг, оказывается нет у меня подходящего инструмента для решения задачи в атематическом (алгоритмическом) режиме, все больше про бизнес и портфели. Опять решение моментальное - есть торговая стратегия сетка + HackTrade Nano-framework (как исполнитель, гарантировано набирает позицию), нужно дописать несколько модулей, вот основные из которых RiskManager, MoneyManager, GridManager. Задача решаема и быстрая. Слепил из того что было модульный асинхронный вариант.
И вот вам нате! Полезли ошибки в управлении ордерами (не квик то, безусловно виноват ведь "чихает" :smile: ) Но квик такой какой есть и с этим приходиться жить. Руками торговать не вариант, решение моментальное - "лечиться". Пробежался на просторах, проблема как и предполагал оказалось в получении колбеками информации с сервера.
В моем скрипте три торговых колбека связаны через свой уникальный идентификатор trans_id. Все три колбека — 'OnTransReply', 'OnOrder' и 'OnTrade' — можно связать через 'trans_id', чтобы отслеживать состояние каждой заявки в процессе её жизненного цикла. Не туто было!
Оказывается состояние ордера в колбеке 'OnOrder' можно отследить единственным способом: "'order_num'" — уникальный идентификатор самой заявки в рамках торговой системы. * Он уникален для каждого ордера и нужен для точной идентификации и управления заявками (например, отмена или модификация заявки). Также 'order_num' используется при возврате данных о состоянии ордера после его выполнения. * "'trans_id'" — уникальный идентификатор транзакции, который используется для идентификации заявки на более высоком уровне (в рамках системы торговли или брокера). Этот идентификатор часто используется для отслеживания всей жизненной циклы заявки, например, когда она была создана, изменена или отменена.
И тут возникает основной вопрос повествования, нет ни кто придумал это, а для Кого? Какими профессиональными знаниями нужно обладать, чтоб со всем этим справляться и не сдаться? Вопрос прежний для Кого?
Пользователь
Сообщений: Регистрация: 15.06.2023
22.10.2025 11:10:04
Сказка2. Решение моментальное - использовать оба идентификатора. Почему важно использовать оба идентификатора:
1. "'trans_id'" — уникальный идентификатор транзакции, который используется для идентификации заявки на более высоком уровне (в рамках системы торговли или брокера). Этот идентификатор часто используется для отслеживания всей жизненной циклы заявки, например, когда она была создана, изменена или отменена.
2. "'order_num'" — уникальный идентификатор самой заявки в рамках торговой системы. Он уникален для каждого ордера и нужен для точной идентификации и управления заявками (например, отмена или модификация заявки). Также 'order_num' используется при возврате данных о состоянии ордера после его выполнения.
Почему нельзя использовать только один из идентификаторов (например, 'order_num'): Статус и состояние. Состояние заявки (например, активна/отменена) нужно отслеживать в контексте как 'trans_id', так и 'order_num', чтобы правильно обработать ситуацию, если заявка была отменена, частично исполнена или изменена?
Тогда правильная структура и использование двух идентификаторов: 1. Для каждой заявки должен быть свой уникальный 'trans_id'" (для управления жизненным циклом). 2. Для каждой заявки также должен быть свой уникальный 'order_num'", который будет использоваться для отправки ордеров на рынок и их модификации. 3. Важно отслеживать состояние заявки с использованием обоих идентификаторов", чтобы правильно учитывать возможные изменения, отмены или частичные исполнения.
Обновленный подход к обработке заявок: Если хотим точно отслеживать и обрабатывать заявки в пуле, то следует использовать "и 'trans_id', и 'order_num'" для каждой заявки.
Итог: а) 'trans_id'" и "'order_num'" — это два взаимосвязанных идентификатора для каждой заявки. Каждый из них выполняет свою роль: * 'trans_id' связан с транзакцией (управление на уровне системы), * 'order_num' — с ордером на рынке (конкретная заявка). б) Оба идентификатора должны быть сохранены и использованы" для правильной обработки заявок и их состояния, чтобы избежать путаницы и обеспечить точное управление заявками и транзакциями в пуле.
Пользователь
Сообщений: Регистрация: 27.01.2017
22.10.2025 11:54:13
Это не так.
trans_id - это просто некий ключ, задаваемый при подаче транзакции. При этом, что важно, он не обязательно уникальный. Т.е. необходимо обеспечить уникальность trans_id для нескольких скриптов, если они есть. Иначе очень вероятна ситуация когда trans_id будет одинаковый у разных скриптов. Более того, если отправите транзакцию через команды интерфейса терминала, то trans_id будет пустой. Т.о. trans_id - это ваш ключ. Его необходимо использовать только для понимания, что транзакция принята сервером брокера, ядром биржи. Читая ответ транзакции по нему можете увидеть причину отказа.
Если же транзакция прошла, то наступает следующий этап - получить ключ в ядре биржи, и это order_num. Он будет уникальным. Какой там был ключ транзакции бирже не важно, она получает запрос, отправляет ответ. Сервер брокера перенаправляет ответ биржи клиенту.
Если ошибок при отправки транзакции нет, то после регистрации ордера в ядре бирже, в таблице ордеров появится новая запись, в которой будет order_num и trans_id. При этом trans_id может появится не с первого колбека, а, скажем, со второго. Задача - отслеживать записи в таблице ордеров, и по trans_id найти номер ордера - order_num. После его получения про trans_id уже не столь важен. Почему - все просто, ордер может жить дольше чем одна торговая сессия.
Запоминая номер ордера уже можно отслеживать его состояние. Кто-то предпочитает колбеки, т.к. именно так показано в большинстве примеров на просторах ...., я же предпочитаю читать состояние прямо из таблицы ордеров, в реальном времени. Это делать не так и сложно, т.к. всегда можно запомнить номер индекса записи в таблице ордеров. Т.о. order_num будет ключом для скрипта, по которому он всегда может понять что случилось с его ордером. И скрипт при этом должен уметь отличать свои ордера от чужих.
Поэтому нельзя использовать один ключ при клиент-серверном взаимодействии. Есть ключ запроса, есть ключ ответа. Запрос - это просто действие "сейчас". Ответ же может содержать информацию, которая имеет смысл более длительный промежуток времени.
Пользователь
Сообщений: Регистрация: 15.06.2023
22.10.2025 13:09:39
Nikolay, Отличное замечание! Спасибо за уточнение! Вы абсолютно правы - в реальной торговле важно правильно работать с идентификаторами. Это очень важное замечание, которое имеет большое значение для правильного построения системы и логики работы с ордерами в рамках торговых систем!
Пользователь
Сообщений: Регистрация: 30.01.2015
22.10.2025 14:18:06
До кучи... 'trans_id'" и "'order_num'" - это идентификаторы для разных серверов (биржи и брокера) и разных источников их генерации (биржа и клиент брокера) Поэтому их два и они не зависимые друг от друга.
Пользователь
Сообщений: Регистрация: 15.06.2023
22.10.2025 18:45:05
nikolz, Спасибо за уточнение. Да, Вы абсолютно правильно отметили. В контексте торговых систем, такие идентификаторы как trans_id и order_num часто используются для уникальной идентификации заявок и операций, но их назначение и контекст отличаются.
trans_id: Это идентификатор транзакции, который обычно используется для отслеживания транзакций внутри самой торговой системы или между различными её частями. Этот ID генерируется на стороне клиента (например, на уровне торгового робота) и может быть использован для уникальной идентификации всех операций, связанных с данной заявкой. Это полезно для внутреннего учёта робота или системы и может быть использован для отмены или модификации заявок.
order_num: Это идентификатор, который обычно генерируется на стороне биржи или брокера и используется для уникальной идентификации заявки в их системе. Когда заявка отправляется на биржу, она получает свой номер — order_num. Этот номер важен для взаимодействия с внешней торговой системой (биржей или брокером), например, для отмены заявки или запроса статуса заявки.
Мое понимание. Пример: Когда робот создаёт заявку, он может использовать trans_id, чтобы отслеживать все операции с этой заявкой. Если заявка требует изменения или отмены, этот идентификатор будет использоваться для корректного обращения к ней. Пример: После того как заявка была отправлена на биржу, она получает свой order_num, который будет использоваться для её отслеживания на бирже, а также для отмены или изменения заявки через API биржи.
Пользователь
Сообщений: Регистрация: 15.06.2023
22.10.2025 19:01:27
Сказка3. "ни словами сказать ни пером описать".
Напомню свое решение торговой системы: HackTrade + RiskManager + MoneyManager + GridManager (торговая стратегия сетка) + GUI.
HackTrade = Умные заявки (SmartOrder). "Представлена реализация stateful-заявок, которые помнят своё внутреннее состояние и могут динамически принимать изменения параметров. Этот подход сильно отличается от традиционных лимитных заявок. Заявка в фреймворке HackTrade представляет собой динамическую лимитную заявку с изменяемой ценой. При этом также может меняться количество и даже направление. Главное, то нужно запомнить, "умная заявка" будет пытаться набирать указанное количество лотов по заданной цене. Даже если вы снимите заявку торговой систем, SmartOrder породит новую и продолжить добирать заданное ранее количество (представьте, что SmartOrder - это заявка, которая гарантированно набирает зaданный объём)".
`SmartOrder` структурно, представляет собой объект (ОПП), который управляет динамическими лимитными заявками. Он следит за состоянием заявки, может корректировать цену и объём заявки и принимать решения на основе того, как рынок ведёт себя (например, снимать заявки, когда цена или объём изменяются)
Работа с ордерами в сводится к 2 моментам: * Если заявка активна, но не соответствует текущим данным (например, цена изменилась), происходит её отмена и пересоздание с новыми параметрами. * Если заявка ещё не была отправлена (поле `self.order` пусто), то создаётся новая лимитная заявка.
Пример работы алгоритма:
1. Инициализация заявки. При запуске робота создаётся объект `SmartOrder`, который получает уникальный `trans_id`. В дальнейшем этот объект будет управлять состоянием заявки.
2. Обновление заявки. Когда происходит обновление с новыми параметрами (например, новая цена или объём), метод `update` обновляет эти данные, и заявка готова к выполнению или корректировке.
3. Обработка заявки. В методе `process()` происходит анализ текущей заявки. Если она не соответствует нужным условиям (например, её нужно отменить), то вызывается соответствующая операция для отмены старой заявки и создания новой с актуальными параметрами.
4. Отправка заявки на рынок. Когда необходимо выставить заявку, это делается через метод `sendTransaction`, который отправляет команду на сервер для создания или отмены заявки.
Моя идея заключалась: SmartOrder = LevelGrid, то есть присваиваются все свойства SmartOrder, следовательно LevelGrid становится объектом (ОПП) и управляется алгоритмом SmartOrder.
Все не объяснимое начинается здесь.
Цитата
Nikolay написал: При этом trans_id может появится не с первого колбека, а, скажем, со второго.
От себя добавлю или неизвестно когда? В своих реализациях, насмотрелся такого "ни словами сказать ни пером описать".
Пользователь
Сообщений: Регистрация: 15.06.2023
23.10.2025 11:11:32
Пару слов про подход. ------------------------------- Технологическая философия оригинального HackTrade с умными stateful-заявками, его инженерия, это не что иное как реализация варианта специализированной машины состояний для трейдинга. Да. "SmartOrder" — это state machine, воплощенная в виде умной заявки, а не в виде абстрактного графа состояний. Это практический подход, который сохраняет все преимущества state machines — предсказуемость, надежность и отсутствие невозможных состояний — в контексте, реальной торговле на бирже. Эта "stateful-заявка (SmartOrder)", которая сама управляет своим жизненным циклом, что является классическим применением паттерна state machine. Ну согласитесь ведь гениально! Конечным автоматом в этой философии выступает, алгоритм торговой стратегии (мы создаем функцию "Robot"). С простым и очень понятным пользовательским интерфейсом.
По своей сложности. Очень сложно! В реализации подхода задействовано все чем силен скриптовый язык Lua: * таблицы реализованные как объекты подход (ОПП) с мета методами; * основной цикл программы разбит сопрограммами, что превращает выполнение в асинхронное; * модульность добавляет универсальность. Тяжело не только разобраться, но еще сложнее отладить.
SmartOrder спроектирован, так что обслуживает одну умную заявку, но не что не мешает создавать пул из умных заявок. Для большинства роботов нe хватит одной умной заявки, но мы можем создавать их больше, например для сетки маркет - мейкера, когда в рынке стоят сразу несколько заявок.
Пользователь
Сообщений: Регистрация: 15.06.2023
23.10.2025 13:05:12
Сказка4. «Через тернии к звёздам». "Тернии" опустим, все таки личное, а многое, с чем столкнёшься используя этот подход, уже ясно из описания самого подхода. А отказаться от него вообще не вариант, сейчас поясню!
Сразу «к звёздам»! Во избежание получения эффекта опасности "лапши из булевых флагов" (где то попалась терминология, хорошо описывает проблематику), когда система управляется разрозненными переменными, были собраны принципиально разные варианты философии с умными stateful-заявками.
HackTrade Pro version 2.0 - Много заявочная версия. Эта переработанная версия сохраняет обратную совместимость с оригинальным HackTrade 1.4, но добавляет: 1. Современную OOP архитектуру с классами Order, OrderManager; 2. Безопасную машину состояний для ордеров; 3. Систему событий для реактивного программирования; 4. Расширенную статистику и аналитику; 5. Улучшенное управление сетками ордеров. Доводил "сломано много копий", рабочий получилась 2.3 но не обоснована сложа, хотя и рабочая. Данную версию отнесу в "Тернии".
Наиболее интересным получился HackTrade Pro version 3.0. Хорошо сохраняет оригинальную концепцию. Реализованы ключевые принципы, заточенные под задачи алгоритмического трейдинга: - Конечное число состояний. Система может находиться в конкретном состоянии (есть активный ордер, нет ордера, ордер на покупку/продажу); - Четкие переходы: Метод process() проверяет текущее состояние и решает, нужно ли выставлять, отменять или перевыставлять заявку; - Предсказуемость: В каждый момент времени известно, что пытается сделать заявка и каково ее целевое состояние.
Сравнение. Вот как подход соотносится с классической машиной состояний:
| Аспект | "HackTrade Pro 3.0 (SmartOrder)" | "Классическая State Machine" | | --- | --- | --- | | "Суть подхода" | Специализированная машина состояний для трейдинга | Универсальная модель (например, XState) | | "Состояния" | `position`, `planned`, `price` (данные + цель) | Четкие состояния: `pending`, `executed`, `cancelled` | | "Переходы" | Автоматические через `process()` | Через явные события (event-driven) | | "Гибкость" | Высокая для торговых задач | Общая, для любых систем | | "Сложность" | Низкая, минимальный boilerplate | Выше, требует настройки |
Что сохранено из оригинального HackTrade: 1. Stateful умные заявки - помнят свое состояние (position, planned, `price); 2. Автоматическое управление - сами выставляют/снимают/перевыставляют заявки; 3. Динамическая адаптация - при изменении update() автоматически корректируют активные заявки; 4. Гарантированное исполнение - стремятся достичь целевой позиции несмотря на рыночные изменения; 5. Обратная совместимость - тот же API ( order:update(price, quantity) ) 6. Простота использования - создание заявки в 3 строки кода. Что улучшил: 1. Добавил обработку ошибок и "edge cases" 2. Улучшил безопасность с проверками nil значений
HackTrade Pro 3.0 - Много заявочная версия, поддерживает торговые стратегии:
- Арбитражные стратегии - несколько инструментов; - Маркет-мейкинг - сетки заявок в разных вариантах; - Мульти-инструментальные стратегии - портфельный подход; - Парный трейдинг - связанные инструменты; - Хеджирование - противоположные позиции.
Надеюсь не сильно утомил своими сказочками, это рассказ про то, как один небольшой но принципиальный недочет, отнимает массу времени и помогает "Кумекать" Всем хорошего кода.
Пользователь
Сообщений: Регистрация: 30.01.2015
23.10.2025 18:59:24
, Вы правильно назвали свой восторг по поводу скрипта HackTrade сказкой. Ваш восторг сильно преувеличен. ----------------------- Все возможности, которые вы описали можно реализовать проще, так как не вычисляются параллельно, а исполняются в бесконечном цикле main, то в более простом решении можно выиграть и в быстродействии и в затратах памяти. ==================== Поясняю упрощенно: ------------------------ Если какая-то функция вызывается в скрипте робота лишь в одном операторе , то ее реализация является избыточной. Эту функцию можно заменить на условный оператор. Нет никаких передаваемых параметров вообще, значит и время на вызов равно нулю. При такой реализации не только быстрее исполняется скрипт, но и существенно меньше затраты памяти.
Пользователь
Сообщений: Регистрация: 30.01.2015
23.10.2025 19:01:25
пардон, ссылка попала ошибочно, но удалить ее невозможно.
Пользователь
Сообщений: Регистрация: 15.06.2023
23.10.2025 21:58:24
nikolz, Да Вы все правильно говорите. Наверняка мой код не оптимален можно большего быстродействия добиться, все это так. Я еще тот писатель.
Речь здесь идет совершенно о другом: про надежность, про универсальность подхода и быстрое простое создание стратегии. Задача частного трейдера мало чем отличается от профессионального трейдинга. Только в профессиональном работают отделами, каждый из которых отвечает за свою область. А частный за все сразу. Вопрос у кого больше шансов наделать ошибок? Использование подходов с фреймворк помогает упростить, частнику ряд задач и заниматься непосредственно трейдингом.
Моя задача сводилась к элементарной задаче в трейдинге, снижению цены позиции в убыточной сделке на одном инструменте, (правильно так, уменьшения диапазона между текущей ценой и ценной набранной позиции). Для этого случая идеально подходит "торговая стратегия сетка + HackTrade - (frameworkупрощение при создании, как исполнитель, гарантировано набирает позицию), нужно дописать несколько модулей RiskManager, MoneyManager, GridManager. И дело в шляпе.
RiskManager, MoneyManager, GridManager это тоже модули подключаемые. То есть мы имеем в своем распоряжении некий конструктор. Под который создается торговая стратегия. В моем распоряжении сейчас: - Арбитражные стратегии - несколько инструментов; - Маркет-мейкинг - сетки заявок в разных вариантах; - Мульти-инструментальные стратегии - портфельный подход; - Парный трейдинг - связанные инструменты; - Хеджирование - противоположные позиции. Создание стратегии за 3 минуты. И мы уже "на боевом коне" и нас голыми руками не взять. Так ка время остается и на расчеты рисков, целей, и можно занимаемся деятельность трейдера, управлением Капитала.
Вот пример интерфейса конечного пользователя (конечный автомат) и это уже рабочий код (по сути трендовая), ну что еще проще может быть:
Код
dofile("hacktrade.lua")
function Robot()
feed = MarketData{
market = "MCODE",
ticker = "TCKR",
}
order = SmartOrder{
market = "MCODE",
ticker = "TCKR",
account = "ACNT1234567890",
client = "775533",
}
ind = Indicator{tag="MYIND"}
while true do
if feed.last > ind[-2] then
order:update(feed.last, 1)
elseif feed.last < ind[-2] then
order:update(feed.last, -1)
end
Trade()
end
end
2. Про быстродействие даже говорить не хочется, это территория профессиональных участников с другими технологическими возможностями. Там где я считаю свою миллисекунду у меня купят совершат еще несколько сделок за счет меня и мне же продадут, ну или примерно так. А насчет производительности и времени исполнения скрипта оно достаточно приличное под мои задачи. Но Вы всегда можете показать свои эффективные решения я только за обсуждение.
3. Про "так как coroutine не вычисляются параллельно, а исполняются в бесконечном цикле main", Вы ни как не поймете, что в этом то заключается вся ПРЕЛЕСТЬ. Именно это позволяет строить логику кусочно и выполнять код тогда, когда это нужно. Это и есть асинхронность исполнения кода, что позволяет добиваться уникальности в логике и алгоритмах.
4. Мой восторг по поводу скрипта HackTrade выражается не слом "сказка" а словом "Гениально"!
Пользователь
Сообщений: Регистрация: 30.01.2015
24.10.2025 09:45:44
, Но это просто написание функций которые обращаются к вложенным функциям и т д. можно сделать еще более обобщенную функцию для вашего примера, так: пишем файл robot.lua:
Код
dofile("hacktrade.lua")
function Robot()
ind = Indicator{tag}
while true do
if feed.last > ind[-2] then
order:update(feed.last, 1)
elseif feed.last < ind[-2] then
order:update(feed.last, -1)
end
Trade()
end
end
framework это просто упрощение при создании торговой логике. Избавляет от взаимодействия с квик, (принцип один раз отладил и забыл, только подключаем).
Что делает framework, решает две основные задачи: 1. Получение данных из квик из разных источников; 2. управление заявкой. Под задачи управления: отправка, снятие, перестановку заявок, обработку их статусов ну и так далее. (Именно эту проблему я и поймал - обработку статусов, что и ломало весь код.)
Вариант от Nikolay, как то я с ним не очень подружился, хотя необходим для поднятия и восстановления истории.
Пользователь
Сообщений: Регистрация: 27.01.2017
24.10.2025 11:50:38
Не важно какой подход используется - важно как он работает и работает ли он надежно. Если Вы делаете решение для себя и готовы постоянно заниматься отладкой пограничных ситуаций, то делайте как угодно. Хотя уже здесь корутины начинают привносить излишнюю сложность. Те же замыкания прекрасно справляются с запоминанием окружения и позволяют решать ту же задачу. Далее, что самое важное - это воспроизводимость результатов, обработка ошибок. И здесь колбеки - это не лучшее решение, т.к. они не гарантированы, приходят в случайном порядке. Для задач реального времени - это приговор.
Представьте, что датчик выдает данные. Вы решаете использовать "модную" библиотеку с колбеками. Но начиная использовать её, получаете данные с пропусками, данные могут приходить из прошлого. Во многих отраслях - это просто недопустимое поведение.
Так что нет, я уж как нибудь сам организую чтение данных, как эти делали последние лет 50.
Я понимаю, что есть соблазн использовать подход со слугой - сказал ему, что делать, он сообщит когда будет результат. Но такой подход всегда требует надсмотрщика, проверки. Так что самому подойти к кастрюле и проверить как там каша - надежней.
Пользователь
Сообщений: Регистрация: 15.06.2023
24.10.2025 12:27:50
Nikolay, Вы знаете, тестил до 100 корутин одновременно (правда не долго), у меня сложилось хорошее впечатление, если вылетает то нет блокировки всего кода. Показывает не исправность одного датчика, ошибка конкретизируется.
Вот пример Вы установили на свой скрипт пароль, корутина постоянно проверяет, а как с замыканием? Или проверяем подключение к серверу через isConnected, колбекам не доверяем? И таких задач набирается прилично. Но основное это то что можно торговую логику так закрутить, что сам диву даешься.
Насчет колбеков у меня тоже много вопросов. И основной для КОГО сделано? Мы пришли все таки торговать, а не решать задачи программирования. По мне так интерфейс пользователя, вон как в Метасток или того проще, с возможностью разворачивания для спецов. А в такой реализации, как сейчас, все должны быть спецами, а не торгашами.
Кстати проблема с отслеживанием статусов HackTrade, возникала давно, но при работе с портфелем и редких сделках, все откладывал, а тут прямо реально достала. Заодно делаю рефакторинг framework у, чтоб получал данные с подпиской на свечи с удобным интерфейсом пользователя. Не очень удобно реализована работа если нужно получать с разных тайм фреймов. Может выложу позже на обсуждение? Пока нравится по крайней мере наиболее удобная у меня версия.
Пользователь
Сообщений: Регистрация: 30.01.2015
24.10.2025 18:01:32
Цитата
Nikolay написал: Не важно какой подход используется - важно как он работает и работает ли он надежно. Если Вы делаете решение для себя и готовы постоянно заниматься отладкой пограничных ситуаций, то делайте как угодно. Хотя уже здесь корутины начинают привносить излишнюю сложность. Те же замыкания прекрасно справляются с запоминанием окружения и позволяют решать ту же задачу. Далее, что самое важное - это воспроизводимость результатов, обработка ошибок. И здесь колбеки - это не лучшее решение, т.к. они не гарантированы, приходят в случайном порядке. Для задач реального времени - это приговор.
Представьте, что датчик выдает данные. Вы решаете использовать "модную" библиотеку с колбеками. Но начиная использовать её, получаете данные с пропусками, данные могут приходить из прошлого. Во многих отраслях - это просто недопустимое поведение.
Так что нет, я уж как нибудь сам организую чтение данных, как эти делали последние лет 50.
Я понимаю, что есть соблазн использовать подход со слугой - сказал ему, что делать, он сообщит когда будет результат. Но такой подход всегда требует надсмотрщика, проверки. Так что самому подойти к кастрюле и проверить как там каша - надежней.
Еще надежнее вообще без функций на луа. И замыкания оказываются ненужными. Какой смысл запоминать окружение? Например , портфель из 100 бумаг. Что будете запоминать для каждой бумаги в замыкании?
сделал прикольный тест на оценку быстродействия корутин.
Код
function foo (a) return coroutine.yield(2*a) end
function foo1 (a) return 2*a end
co = coroutine.create(foo)
tstart() coroutine.resume (co ,10 ) print(tstop())
tstart() foo1 (10) print(tstop())
tstart() local a=10 a=2*a print(tstop())
результат:
17.2 пустая корутина 0.6 пустая функция 0.4 пустой if ------------------------ Корутина в 25 раз медленнее функции и в 40 раз медленнее условного оператора.
Пользователь
Сообщений: Регистрация: 15.06.2023
28.10.2025 08:35:14
Цитата
nikolz написал: Еще надежнее вообще без функций на луа.
Большинство так и пишет.
Цитата
nikolz написал: 17.2 пустая корутина 0.6 пустая функция 0.4 пустой if
Скорость исполнения полезно знать, но не нужно путать задачи?
* If - встроенный оператор сравнения, и так понятно что самая быстрая операция (фильтрация, больше не для чего ни годится); * функция - разделение кода по функциональности, (полную задачу делим, хотя бы чтоб можно было читать); * корутина - это сервис для исполнения минимального кода, что бы была возможность "за ходить в гости в точку С, а не просто шагать из А до Б".
Вот и смысл: "А И Б сидели на трубе, А - упало, Б - пропало, кто остался на требе?"
написал: 17.2 пустая корутина 0.6 пустая функция 0.4 пустой if
Скорость исполнения полезно знать, но не нужно путать задачи?
* If - встроенный оператор сравнения, и так понятно что самая быстрая операция (фильтрация, больше не для чего ни годится); * функция - разделение кода по функциональности, (полную задачу делим, хотя бы чтоб можно было читать); * корутина - это сервис для исполнения минимального кода, что бы была возможность "за ходить в гости в точку С, а не просто шагать из А до Б".
Вот и смысл: "А И Б сидели на трубе, А - упало, Б - пропало, кто остался на требе?" ::
Если учесть особенности построения роботов, как программ обработки данных в реальном времени, то чтобы можно было читать и все работало максимально быстро на Lua , используем технологию структурного программирования: -------------------- 1) Разбиваем алгоритм на блоки (функции) 2) Блоки (функции)пишем в отдельные файлы, которые включаются в основную прогу операторами dofile и loadfile. 3) Основная программа (конечный автомат состоит из бесконечного цикла и обработчика событий. 4) обработчик событий делается в виде оператора if... then ... else ... ------------------------
Пользователь
Сообщений: Регистрация: 30.01.2015
28.10.2025 13:20:47
Цитата
VPM написал: , Так оно так и есть, создаем конфигурацию к робот, строим необходимую торговою логику или используем заготовки торговых стратегий и в бой.
Можно и дальше пойти написать менеджер для торговых стратегий, который от рыночной ситуации будет менять стратегии.
Например вот файл конфигурации к стратегии описанной выше:
client="XXXX" --код клиента
clas_list="QJSIM:0,SBFUT:1,CETS:2"; --список классов счетов и тип интсрументов 0 -акции 1-фьючерсы 2- валюта 3- опционы
--clas_user={} --параметры устанавливаемые пользователем для класса по умолчанию
sec_list="ROSN,GAZP,SBER,PLZL,GMKN,CHMF,HYDR,LKOH,MOEX,SNGS" --список инструментов для портфеля, если нет, то все инструменты по заданным классам
--"s,c,min_price_step,scale,lot_size" --параметры инструментов
sec_user={ --параметры устанавливаемые пользователем для инструментов по умолчанию
1, --интервал свечей
0, --подписка на стакан
0, --флаг разрешения коротких позиций
0,--флаг разрешения установки стопа 1 -обычный, 2-скользящий, 3 -take 4-stop и take
}
pTH="D:/QUIK_SCRIPT/bot25/" --путь к каталогу функций
Пользователь
Сообщений: Регистрация: 30.01.2015
28.10.2025 13:29:38
Сейчас реализовал дополнительные функции на СИ, который позволяют: 1) создать любое число функций main, каждая в своем потоке Позволяет подключить Tensorflow и Torch. 2) создавать векторы целых,вещественных чисел ф формате С (позволяет примерно на порядок уменьшить требуемый объем памяти и скорость обработки) 3) потокобезопасную обработку таблиц и векторов. --------------------------------- Алгоритмы торговли пишутся в отдельные файлы и грузятся в основную программу или запускаются как самостоятельные задачи .
Пользователь
Сообщений: Регистрация: 15.06.2023
28.10.2025 14:29:28
Уважаемый nikolz, Вы поймите одну простую истину, здесь пользователи собрались, для ТОРГОВЛИ, а не для изучения языков программирования, тем более таких как Си.
Что касается меня так, я уже не помню как писать на Бейсик и Фортран, уж не приходится говорить о Си. C Lua разбираюсь по тому что он встроен в терминал, с таким же успехом разбирался бы с любым другим. На нем можно проверять свои торговые идеи, и не только, атематические торговые программы, с гордым названием Робот. Я пишу про это. Для этого не нужен ни Си ни какой другой язык, Lua с избытком. Даже он в некоторых случаях сложен, если учитывать как реализован сам торговый терминал. Язык встроенный необходим нам для автоматизации торговых решений. Если уж писать то грамотно, приходится придерживаться нескольких основополагающих принципов.
А для того чтоб торговать вообще не нужны ни какие языки, да и образования достаточно царской - приходской школы, арифметики. И причем тут Ваши изыски?
nikolz написал: сделал прикольный тест на оценку быстродействия корутин.
Вы обнаружили существенную деградацию эффективности реализации корутин в Lua 5.4.1. Причем при выполнении модифицированного мною вашего теста она больше, чем вы пишите: накладные расходы на вызов корутин больше по сравнению с вызовом функции ~150 раз. В Lua 5.1 эта разница была ~4 раза. Кому интересно, можно проверить в QUIK 8.4.1. Код теста:
Код
function main()
local N =100000
local function foo ()
local yield = coroutine.yield
while 1 do
yield()
end
end
local function foo1 () return end
-----
local out = 'Результат теста: количество вызовов = ' .. N .. ', время вызова сопрограммы '
local resume = coroutine.resume
local co = coroutine.create(foo)
local TT1 = os.clock()
for i =1, N do resume(co) end
TT1 = (os.clock() - TT1) * 1000
out = out .. TT1 .. ' млс.'
-- message('coroutine = ' .. (os.clock() - TT) * 1000)
local TT2 = os.clock()
for i =1, N do foo1(10) end
TT2 = (os.clock() - TT2) * 1000
-- message('function = '.. (os.clock() - TT) * 1000)
out = out .. ', функции ' .. TT2 .. ' млс. T1/T2 = ' .. TT1/TT2
message(out)
end
Пользователь
Сообщений: Регистрация: 30.01.2015
29.10.2025 05:58:16
Цитата
VPM написал: Уважаемый , Вы поймите одну простую истину, здесь пользователи собрались, для ТОРГОВЛИ , а не для изучения языков программирования, тем более таких как Си. ::
Что касается меня так, я уже не помню как писать на Бейсик и Фортран, уж не приходится говорить о Си. C Lua разбираюсь по тому что он встроен в терминал, с таким же успехом разбирался бы с любым другим. На нем можно проверять свои торговые идеи, и не только, атематические торговые программы, с гордым названием Робот. Я пишу про это. Для этого не нужен ни Си ни какой другой язык, Lua с избытком. Даже он в некоторых случаях сложен, если учитывать как реализован сам торговый терминал. Язык встроенный необходим нам для автоматизации торговых решений. Если уж писать то грамотно, приходится придерживаться нескольких основополагающих принципов.
А для того чтоб торговать вообще не нужны ни какие языки, да и образования достаточно царской - приходской школы, арифметики. И причем тут Ваши изыски?
Согласен, что знания языков программирования нужны для программирования, как и специальные знания для любой области человеческой деятельности. ---------------------- Если на форуме собрались пользователи для ТОРГОВЛИ, то они ошиблись местом. Место для торговли - это биржа, а не сайт разработчика торговой системы и не тема "Программирование на Lua" ------------------------- Вы противоречите сами себе. В теме программирование на луа, вы пишите про нечеткую логику. При этом Ваши знания в этом разделе науки еще меньше, чем знания в луа. ==================== Я не могу понять , в чем смысл пересказываний дилетанта того, что гораздо лучше уже описано в учебниках ====================== Говоря о том, что Вы пришли на форум для ТОРГОВЛИ, Вы пока не написали ничего конкретного для ТОРГОВЛИ. --------------------------- Все ваши рассуждения голословны и субъективны. Не обладая проф знаниями ни в одной теме, которые Вы затрагиваете, Вы с большим пафосом поверхностно излагаете демагогию на тему про луа, программирование, про нечеткое множество, --------------------------- Согласитесь, что нести знания в массы может лишь профессионал в этих знаниях, иначе это просто болтовня.
Пользователь
Сообщений: Регистрация: 30.01.2015
29.10.2025 06:25:26
и еще... То , что я разрабатываю, необходимо именно для торговли. --------------------- Все эти задачи возникли в процессе разработки торговой системы на основе нейросетей и ИИ на платформе QUIK, для тестирования торговых алгоритмов и реальной алгоритмической торговли. ---------------------- Например, возможность создать любое число функций main позволяет делать систему торговли на одном скрипте (не надо писать кучу колбеков), запускаемом в КВИКЕ. При этом, если у Вас комп с многими ядрами (например 16), то можно запустить 16 main и ускорить работу в 16 раз. Добавьте к этому запуск в потоках Luajit, что еще ускорит вычисления от 10 до 100 раз по сравнению с луа. ------------------------ Создание векторов Int и float вместо таблиц Lua позволяет увеличить объем для данных примерно в 10-20 раз, а использование mapping file позволяет работать практически с любым объемом данных совместно кучи потоков. ------------------------------- Вы постоянно мне возражаете, что вам не надо быстродействие. (Напоминает басню "Лиса и виноград") ------------------------------------------- Но разве Вы не хотите иметь робота, который сам обучается торговли и самостоятельно может формировать портфель из любых инструментов? --------------------------------- Я хочу не просто его иметь, но и пишу такого робота, на основе моего опыта и знаний. Мне это интересно. -------------------------------------- И об этом пишу именно на форуме разработчиков торговой системы QUIK. --------------------------------- Было бы неплохо, если бы можно было бы обсуждать на форуме конкретные алгоритмы для торговых роботов на луа для QUIK, как это можно делать на форуме торговой системы MT. Но на этом форуме большинство - это начинающие буратино, которые прочитали книжку "Как стать миллионером" и закапывают свои пять золотых на поле чудес.
Пользователь
Сообщений: Регистрация: 15.06.2023
29.10.2025 08:01:24
Профессионалу от дилетанта:
1. Начну с начала (хотя у Вас почему то суть в конце?). Суть: "Было бы неплохо, если бы можно было бы обсуждать на форуме конкретные алгоритмы для торговых роботов на луа для QUIK," Читаем название Вкладки "Программирование на языке Lua". Написать три буквы "L, U, A" с точки зрения "профессора по написанию торговых роботов на луа для QUIK" - это высший пилотаж в обсуждении?
2. "Видящий да увидит, слышащий да услышит!". Темы которые я иногда затрагиваю напрямую относятся к торговым алгоритмам, а подходы описанные за чистую Вы не найдете ни в одном учебнике. Судить о них позволяет мое базовое образование (времен когда еще в медицине давали клятву Гипократа, а поколения не делились по признакам знания высказывания "Сообразить на троих"). Идеи выложенные на форуме не каждому дано понять, и здесь нужно просто понимать, что у Все разные способности, кто то хороший доктор, кто то адвокат, а торговать могут все?
3. Не большой секрет. Торговать на бирже в рамках данного форума, подключить э. устройство к серверу QUIK, и в путь (не нужно ни куда ходить?).
4. Про любимую тему профессора, замеры скоростей. В первую очередь нужно научиться отличать "Образ/Подобие". Ни слова про оптимизацию скриптов, а про быстродействие и скорости в ущерб функциональности, удобству, универсальности. И главное здесь, сама система: "Биржа - Сервер - Терминал - Скрипт", поймете это само собой все отпадет.
Ну и конечно в Ваших разработках всяческой удачи. Читаем пункт 1.
Пользователь
Сообщений: Регистрация: 15.06.2023
29.10.2025 08:10:23
Совсем упустил. Просто на помню Профессору: "Нечеткое множество — это объект, а нечеткая логика — это система правил для работы с такими объектами." Так я про систему правил, ни ни про множество! И тут Профессор все передернул, а зачем? Помним "Образ/Подобие", "Объект/Система".
Пользователь
Сообщений: Регистрация: 15.06.2023
29.10.2025 08:38:10
TGB, В QUIK задача function main() создать поток в котором исполняется скрипт (с корутинами, либо без них) , именно так не иначе, Но Вы то зачем так, как "Профессор" передергиваете?
Пользователь
Сообщений: Регистрация: 15.06.2023
29.10.2025 09:54:54
Помним! Время работы корутин в 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
Ну по крайней мере, что то похожее на обсуждение!
Пользователь
Сообщений: Регистрация: 15.06.2023
29.10.2025 10:42:37
Ну и последнее. Для Профессора и не только.
Приведу пример, как сделать фоновый поток в 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! Надеюсь убедил? Всем хорошего кода.
Пользователь
Сообщений: Регистрация: 27.01.2017
29.10.2025 10:56:00
К сожалению, это не так. Корутины в Lua - это просто один из инструментов, далеко не идеальный. Я уже говорил, что это же можно сделать через замыкание и через очередь вызовов их. Т.к. они не обеспечивают ничего нового, уникального, то это просто дело вкуса. Если бы это был другой язык, например Go, то это был бы другой разговор.
Так что если нравится использовать корутины - на здоровье. Но сказать, что без них никак, точно нет. Я бы сказал, что к ним стоит прибегать в очень редких случаях - например, создание своего итератора (хотя и здесь можно обойтись без них). Или, например, завернуть некий вызов в корутину, отправить в очередь и потом вызвать.
Пользователь
Сообщений: Регистрация: 15.06.2023
29.10.2025 11:10:38
Nikolay, На примере код TGB, выложил два простых скрипта, различных по своей архитектуре. Приведите пример Вашего подхода, возможно он действительно будет оптимальной и лучше? Я ведь на самом то деле, ни чего не отрицаю и ни утверждаю, а декларирую возможности Lua, и возможности создавать шедевры. Давайте сравним, всем будет интересно! Возможно я заблуждаюсь?
Пользователь
Сообщений: Регистрация: 27.01.2017
29.10.2025 11:15:51
Не вижу смысла. Тем более, что приведенный код выглядит нагроможденным. Если цель проверить какое время необходимо для вычисления c = c + 1 N раз с остановкой на каждую итерацию, то зачем такое количество кода.
Пользователь
Сообщений: Регистрация: 15.06.2023
29.10.2025 11:30:59
Вот и Вы туда же? Цель:
Цитата
VPM написал: сделать фоновый поток в QUIK с использованием корутины, без блокировки терминала и с периодическим выводом результата.
Скорость ограничена скорость получения данных, "Не прыгнешь выше своей головы":
Цитата
VPM написал: И главное здесь, сама система: "Биржа - Сервер - Терминал - Скрипт"
Измерение скорости в наших условиях для наших задач, нужно только для оптимизации скрипта. А у нашего "Профессора", идет путаница, он пытается обрабатывать большие массивы, где безусловно важным факторам является скорость операций, с нашими повседневными задачами, ограниченными по скорости передачи данных. Это и пытаюсь донести, обижается? Насчет смысла, смыл писать эффективный код, как же нет?
Пользователь
Сообщений: Регистрация: 27.01.2017
29.10.2025 11:46:40
Код
сделать фоновый поток в QUIK с использованием корутины, без блокировки терминала и с периодическим выводом результата.
main в скриптах - это уже дополнительный поток, выполняющийся отдельно от потока терминала. Создавать внутри него псевдо-потоки можно, но это уже не имеет никакого отношения к фоновому исполнению, т.к. все внутри main выполняется фоново к терминалу, если использовать эту терминологию.
Пользователь
Сообщений: Регистрация: 15.06.2023
29.10.2025 12:12:00
Дело тут не в терминологии, а в особенностях применения сопрограмм в Lua, и те возможностях которые возникают от их применения. Исполнять код по принципу "который нужно, когда нужно"!
Пользователь
Сообщений: Регистрация: 27.01.2017
29.10.2025 12:18:34
Ну так и функция - это код, который выполняется тогда, когда вызываешь, а не иначе. Т.е. если надо периодически вызывать какой-то расчет, то почему просто не функция. Корутина с остановкой удобна, если надо запомнить состояние и вернуться к выполнению, не передавая заново весь набор параметров. Например, итераторы. Но это далеко не единственный способ так делать. Сделали набор функций и вызывает их в цикле main, пока он крутится.
Пользователь
Сообщений: Регистрация: 15.06.2023
29.10.2025 12:39:49
Цитата
Nikolay написал: Корутина с остановкой удобна, если надо запомнить состояние и вернуться к выполнению
Так это и есть ключ. Помнит место остановки и туда возвращается. Если подобное исполнение делать с помощью функции, нужно что то наверное в парадигме машины состояний. А затем придется еще этим состояниями управлять.
По чему мне нравится подход с корутинами. Логику можно строить так, как мы с Вами это сейчас обсуждаем. Вот прямо могу написать код обсуждения, асинхронно получить время, послушать новости, и еще чего то там. Мне кажется круто! Кстати навело на мысль попробовать в отдельной корутине отслеживать состояние ордеров, сейчас двух факторная по транс ид и номеру ордера из колбеков, а что если не зависимо все подтвердили тогда только учитываем? Попробую.
Пользователь
Сообщений: Регистрация: 27.01.2017
29.10.2025 13:39:34
Если цель помнить состояние, то это прекрасно делают замыкания. Я, например, так пишу таймеры. Можно и счетчик.
Вот ваш счетчик с колбеком по достижению через замыкание
Код
local function counter(i, limit, call_back)
local x = 0
return function(...)
x = x + 1
if x >= limit then
if call_back then call_back(i, ': reach', x, ...) end
return true
end
return false
end
end
if _G.message then
print = function(...) _G.message(table.concat({...}, ", ")) end
end
local is_run = true
function main()
local worker1 = counter(1, 5, print)
local worker2 = counter(2, 20, print)
local worker3 = counter(3, 10, print)
while is_run do
if worker1 and worker1() then worker1 = nil end
if worker2 and worker2() then worker2 = nil end
if worker3 and worker3() then worker3 = nil end
sleep(100)
end
end
function OnStop()
is_run = false
end
Это просто пример с прямолинейной реализацией.
Пользователь
Сообщений: Регистрация: 15.06.2023
29.10.2025 13:57:35
Нет, дело тут не в счетчике, счетчик в моем примере это просто минимальная операция для примеров. Дело в этом:
Цитата
Nikolay написал: Вместо теста скорости можно вставить любую периодическую задачу: * запрос рыночных данных (getParamEx), * обновление таблиц QUIK, * контроль позиций и алертов, * логирование в файл.и так далее.
Корутина превращает прямолинейную реализацией - последовательное чтение функций, в не линейное исполнение разных задач. В едином потоке луа появляется, как бы несколько раздельно выполняемых задач. Мы же можем к сопрограммам написать и менеджер, по их управлению.
Пользователь
Сообщений: Регистрация: 27.01.2017
29.10.2025 14:14:20
Ну так и в примере, что я показал, все можно сделать не прямолинейно. А колбек сам вызовется по исполнению. Это мало чем отличается от использования тех же корутин. Пишите менеджер вызова исполняемых процедур, а что он вызывает - не важно. Важно чтобы процедура исполнялась столько сколько надо. Если бы корутина работала сама, без менеджера, то это было бы совсем другая среда. Но такого в lua нет.
Пользователь
Сообщений: Регистрация: 15.06.2023
29.10.2025 14:53:51
Смотрим внимательно, сравниваю эти два подхода.
На первый взгляд оба примера — "фоновые циклы" в 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. Если просто нужно выполнять несколько лёгких задач с разной частотой -> вариант с замыканиями/итераторами.
Пользователь
Сообщений: Регистрация: 27.01.2017
29.10.2025 15:17:22
Вам, наверно, стоит все же внимательней изучить работу корутин. По крайней мере создается такое ощущение. Нет ничего асинхронного в операторе yield. Это просто выход из процедуры в этой точке. При повторном вызове вернетесь в нее же. Сама корутина ничего не делает после выхода по yield. А значит выполняться она может тоже только через вызовы в основном цикле mian. И тоже частота вызовов регулируется задержкой цикла main.
Собственно ваш любимый hacktrade так и работает.
--[[ MAIN LOOP ]]-- working = true function main() --create_table() log:trace("Robot started") if Start ~= nil then Start() end if Robot ~= nil then
--Создаем корутину, заворяаивая в неё процедуру Robot local routine = coroutine.create(Robot) while working do
--Выполняем корутину в бесконечном основном цикле скрипта local res, errmsg = coroutine.resume(routine) if res == false then log:fatal("Broken coroutine: " .. errmsg) end if coroutine.status(routine) == "dead" then log:trace("Robot routine finished") break end -- Orders processing calls after every coroutine iteration for trans_id, smartorder in pairs(SmartOrder.pool) do smartorder:process() end end end log:trace("Robot stopped") if Stop ~= nil then Stop() end io.close(log.logfile) end
function Robot()
-- Вынуждены создавать еще один бесконечный цикл, чтобы корутина жила. Иначе при втором вызове она умрет. while true do if feed.last > ind[-1] then order:update(feed.last, size) else order:update(feed.last, -size) end Trade() - это просто замаскированный yield end end
Логика проста - создали корутину и дергаём ее в цикле постоянно пока она жива. При этом процедура Robot тоже предельно проста - в ней создается бесконечный цикл (еще один), внутри которого есть yield. Т.е. приходится сооружать корутину, внутри которой городить цикл, чтобы она не умерла. Все это накладывается на работу с глобальными переменными....
Чем это отличается от того же вызова, в том же цикле main созданных исполняющих процедур, помнящих свое локальное состояние (что очень важно). Точно также вызывайте когда это необходимо, создавайте очереди вызовов, чтобы создавать иллюзию ассинхронности.