От любительского технического анализа, к уровню профессионального quantitative finance!
Или почему Дуальность (двойственность) - это основополагающий принцип и не только в логике и математике но и в трейдинге. "Дуальность (двойственность) — знание, которое основано на принципах противоположности. Но это не деление мира на два полюса, а неразделимая связь двух противоположных начал. Если не будет одного полюса, то не будет и другого".
Применение. Большинство классических индикаторов технического анализа используют техники, основанных на аддитивных вычислениях (аддитивной логике): средняя доходность, разница цен, арифметическое усреднение. Это удобно, но не совсем то, неправильно, если мы хотим реально понять поведение цены, капитала. Именно за этот подход часто слышишь критику или даже отрицание технического анализа в трейдинге.
Альтернативный этому подходу - мультипликативный подход. Этот переход делает систему более строгой, логически цельной и экономически осмысленной. В своей "quantitative finance" применяю подход — мультипликативный, основанный на HPR (Holding Period Return) и геометрической средней.
Зачем и почему мультипликативный подход?
1. Стационарность. Стационарность означает, что статистические свойства (среднее, дисперсия) не меняются во времени. Почему важно?
Цены сами по себе — нестационарные (они могут расти от 10$ до 1000$ и выше).
А вот доходности в виде HPR** (например, «рост на 2% за день») стационарны: процентные изменения распределяются схожим образом и в 2010-м, и в 2025-м.
Это даёт возможность применять математику корректно (модели, прогнозы, статистику).
2. Сплиты и корпоративные события. "Сплит" — деление акций компании: например, 1 акция по 1000$ превращается в 10 акций по 100$. Формально цена упала в 10 раз, но капитал инвестора не изменился. Если считать «разницы цен», мы получим ложный обвал. Но если работать через HPR, то процесс корректно учтёт изменение (HPR = 1).
3. Компаундинг— эффект «сложных процентов». Доходность в каждый следующий период считается на увеличенный или уменьшенный капитал. Пример:
День 1: +10% > капитал вырос с 1000 до 1100.
День 2: +10% > теперь рост идёт от 1100, а не от 1000 > итог = 1210, а не 1200.
Арифметическое усреднение (например, просто среднее доходности = 10%) игнорирует компаундинг. Только геометрическая средняя даёт правильный результат — «средний множитель роста» за период.
4. Метрика HPR. HPR = Final Value / Initial Value.
HPR = 1 > капитал не изменился.
HPR > 1 > рост (например, 1.05 = рост на 5%).
HPR < 1 > падение (например, 0.90 = падение на 10%).
Это просто удобно. Нет проблем с «нулевой ценой» (акция может стоить 0, но HPR всегда > 0). Универсальная метрика для любых активов, таймфреймов и событий. Легко переводится в доходность: Return = HPR - 1.
Преимущества мультипликативного подхода.
Стационарность: статистические свойства не «дрейфуют», а значит, можно применять строгую математику.
Сравнимость: разные активы и стратегии измеряются в одной метрике.
Экономический смысл: HPR напрямую связан с изменением капитала.
Автомасштабирование: метрики автоматически подстраиваются под волатильность.
Совместимость с управлением капиталом: идеальная база для методов Винса (Optimal f, Kelly).
В заключении: Переход от аддитивного к мультипликативному анализу — это не косметика, а смена парадигмы:
В аддитивном подходе — иллюзия доходности и искажённые риски.
В мультипликативном подходе — корректная математика, единая метрика и прозрачная связь с ростом капитала.
Экономический смысл:
В аддитивной логике мы думаем категориями «прибыль против убытка».
В мультипликативной логике мы видим единую систему, где рост и падение — два направления одного процесса.
Это и есть проявление двойственности: Не «два мира», а одно пространство. Не «линейная сумма», а множитель, который может быть больше или меньше 1. Двойственность в quantitative finance проявляется через мультипликативность. Прибыль и убыток — это не противоположные сущности, а два состояния одного процесса, описываемого единой метрикой HPR. В эту концепцию отлично вписывается fuzzy logic. Дуальность в нечеткой логике. В fuzzy logic (нечёткой логике) используют функции принадлежности, где одна и та же шкала отражает и «истину», и «ложь», но с разной степенью принадлежности.
Это и есть по моему мнению настоящий "quantitative finance" — строгая, самосогласованная система, лишённая противоречий, с ясной экономической интерпретацией.
Немного пишу на луа скрипты для квик, и проблема возникает при тестировании и исправлениях в скрипте - постоянной загрузки обновлённого скрипта. Очень не удобно реализовано, в квик, как на конвейере тыкаешь одни и те же кнопки совершая одни те же операции.
Задача стала как моно оптимизировать процесс обновления скриптов в квик. Если для индикаторов алгоритм нашелся сразу сам собой, заключается в обработке функции в обработчике, один раз загрузив обработчик на график, все изменения проводить в функции расчета индикатора, а режим связанных окон позволяет при смене инструмента обновлять все расчёты индикатора. Данный подход значительно сократил конвейер, время на редактирование, а главное раздражение от бесконечного однообразия.
Что то подобное хотелось собрать для более сложных торговых алгоритмов, прийти к базовой архитектуре скрипта - модульному построению. Задача все та же уйти от однообразных операций, но повышалась сложность значительно расширялась сама задача. Требования к структуре:
1. Четкое разделение по функциональности; 2. Легко добавлять новые модули и группы; 3. Централизованное управление модулями; 4. Строгий контроль глобальных переменных; 5. Четкая структура для документации; 6. Удобная организация тестов.
Такая структура обеспечивает легкую поддержку в будущем, профессиональную организацию проекта, а главное избавляет от бесконечной переделки торговой системы, так как модули могут использоваться неоднократно. Четко обозначилась еще одна важная структура - технологическая, отвечающая за подержание работоспособности самой системы, а это уже огромный шаг к надежной автоматической системе.
Не пугайтесь, самому страшно. Вот базовая архитектура моего проект при таком подходе.
Принципы написания кода Lua для Квик (торговый терминал QUIK).
При создании индикатора, написании кода Lua для Квик, пришел к выводу необходимости учета несколько важных аспектов, направленных на улучшение производительности, точности расчетов и адекватности реакции на изменения рынка. Каждый принцип по полочкам, детально:
А). Буферизация.
Буферизация данных — это процесс накопления информации для последующего использования или анализа. В контексте торговли с использованием Lua в QUIK буферизация может быть полезна для:
Снижения частоты запросов к API (например, запрос котировок, данных по счету).
Применения накопленных данных для более точных расчетов, например, для вычисления скользящих средних.
Эффективного хранения исторических данных, если вам нужно просматривать прошлые значения индикаторов, цен и объемов.
В коде Lua это можно реализовать через массивы или таблицы, которые накапливают данные о ценах или индикаторах за определенный период. Далее эти данные могут быть обработаны для получения сигналов или индикаторов.
Б). Адаптивность периода или окна расчетов.
Это важно, для того чтобы расчеты индикаторов или других метрик автоматически адаптировались к текущим рыночным условиям. Это можно сделать, например:
Использовать изменяющийся период для расчетов (например, для скользящих средних, полос Боллинджера) в зависимости от волатильности или тренда на рынке.
Можно создать динамическое окно, которое подбирает оптимальный период для расчета индикаторов на основе текущей рыночной ситуации.
В Lua это можно организовать с помощью вычисления метрик, которые изменяются в зависимости от волатильности или других рыночных параметров. Например, вычисление скользящей средней может быть более чувствительным при повышенной волатильности и наоборот.
В). Фильтрация и сглаживание с уменьшением лага.
Фильтрация и сглаживание данных с уменьшением лага — это важная задача при расчетах для избегания запаздывающих сигналов. Возможные способы:
Применение фильтров, таких как экспоненциальное сглаживание (EMA) или алгоритм Калмана, которые уменьшают лаг при обработке данных, позволяя быстрее реагировать на изменения.
Использование фильтрации по коэффициентам сглаживания, чтобы адаптировать поведение индикатора к быстроменяющимся данным.
Для этого нужно тщательно выбирать параметры фильтра и протестировать его работу на различных рыночных сценариях.
С). Мультипликативность в расчетах двойственных отношений.
Этот принцип включает в себя использование мультипликативных факторов для анализа и расчетов между двумя взаимоотношениями. Например:
Если индикатор или метрика для анализа трейдера зависит от множества других факторов, то их можно использовать в качестве множителей для корректировки сигналов.
Пример: если показатель волатильности увеличивается, он может умножать другие показатели, такие как величина стоп-лосса или размер позиции.
В Lua можно использовать формулы, которые учитывают такие множители, и корректировать расчеты с их учетом. Но главное здесь, мультипликативность в расчетах, позволяет применять, в этих расчета геометрическую среднею, как не линейный фактор.
Д). Применение метрики HPR (Holding Period Return) вместо доходности при расчетах индикаторов.
В отличие от стандартной доходности, которая вычисляется как разница между текущей ценой и ценой покупки, HPR учитывает стоимость активов на протяжении определенного периода. Это может быть полезно для:
Учет промежуточных дивидендов, сплитов, и других корпоративных событий.
Применение к позициям с разными временными горизонта.
В коде это можно реализовать через переменные, которые отслеживают цену покупки и текущую цену, а также время, прошедшее с момента покупки, чтобы правильно вычислить HPR. Этот подход избавляет в вычислениях от 0, и легко преобразуется в доходности HPR - 1.
Е). Применение доходностей и правила 3 сигма при определении риска и цели
Использование стандартного отклонения для оценки риска является часто используемым моментом для определения «нормальных» и «аномальных» значений в торговле. Правило 3 сигма гласит, что в нормальном распределении 99.7% значений должны попадать в интервал в пределах 3 стандартных отклонений от среднего.
nikolz написал: Про производительность у Вас вообще ерунда написана.
Согласен: в классическом определении производительность труда = выпуск продукции / затраты труда. Если я в примере некорректно связал это с коэффициентами «товар/деньги» — это моя ошибка. В нашем случае корректнее говорить не о производительности труда, а о рентабельности или ценовой структуре (доля себестоимости в выручке).
Таким образом, цена говорит вам, где вы сейчас находитесь, а ценность (КПД) показывает, насколько далеко вы сможете уехать и пережить ли дорожные испытания.
Учебники экономики фокусируются на спидометре (цене, объёмах). Данный подход даёт вам диагностический сканер для двигателя вашего бизнеса.
nikolz написал: А чем "ценность" товара отличается от цены товара ?Цена товара - говорит, что 1 единица товара дает 100 руб выручки. Т е по смыслу, тоже самое что и придуманная вами "ценность" товара.
Очень справедливое замечание. Давайте аккуратно разберёмся с терминами и математикой, без "придумывания" новых смыслов. Первый раз я с понятием ценность лично столкнулся в работе А, Эльдара о аккуратненько ввел понятие не заделав пояснений, второй принципиальный у Ральфа Винса. Мне тоже изначально показалось некой надуманностью. Обо всем и по порядку.
Цена товара — это объективная, фиксируемая характеристика: сколько рублей нужно заплатить за 1 единицу товара. Формально: P = Выручка / Количество товара; Пример: 100 булок × 100 руб. = 10 000 руб. → Цена = 100 руб. за булку.
Ценность товара — это уже субъективная категория (экономическая теория, «полезность» ). Она не измеряется напрямую в рублях, а определяется тем, насколько товар удовлетворяет потребность потребителя.
цена = денежная мера;
ценность = мера полезности.
В математическом анализе лучше не путать эти термины.
Цена — это данность. Она как погода на рынке. Её можно наблюдать и подстраиваться, но нельзя контролировать напрямую.
Ценность в данном подходе (Язык Системы)
Это отношение Товар / Деньги. Это внутренний, системный параметр, который показывает, насколько эффективно производство преобразует товар в деньги.
Как определяется? Только внутренними процессами: производительностью труда, качеством сырья, организацией работы. Это объективная мера эффективности бизнеса как системы.
Ценность (в нашем понимании) — это КПД вашего двигателя. Аналогия: Цена - Спидометр (показывает текущую скорость); Ценность - Мощность двигателя (показывает потенциал автомобиля).
«Весы» можно корректно, переформулировать через классические экономические коэффициенты, и тогда не будет путаницы с терминами вроде «ценность», но я ставлю своей задачей вернуть Смыслы! Смыслы понятные даже бабушке решивший инвестировать.
nikolz, Не знаю такой школы, где припадают наука о смыслах и отношениях? Её аппаратом является не только математика чисел, но и математика форм, структур, пропорций и инвариантов.
1. Чем этот подход фундаментально отличается от учебников и почему он может быть мощнее?
Ключевое отличие не в примерах, а в самом типе математики и системе координат для анализа. Учебники экономики оперируют абсолютными величинами (объёмы, рубли, штуки) и линейными зависимостями (спрос падает при росте цены). Это язык количества. Данный подход оперирует безразмерными отношениями (Товар/Деньги, Спрос/Предложение) и их балансом. Это язык пропорций, структуры и качества.
2. Пояснение на Конкретной Математике Возьмём пример: Товар / Деньги. В учебнике: Это два отдельных показателя. Выручка 10 000 руб. и производство 100 единиц товара. Анализ линейный: «Надо производить больше, чтобы больше продать». В данном подходе: Мы сразу смотрим на отношение Товар/Деньги = 100 / 10 000 = 0.01.
2.1 Что это число на самом деле означает? Это коэффициент окупаемости или «ценность» товара в денежном выражении. Число 0.01 говорит, что 1 рубль выручки «обеспечен» всего 0.01 единицы товара. Это крайне низкая производительность труда или маржинальность. А если бы отношение было равно 1? Это означало бы идеал: 1 единица товара приносит 1 условную единицу денег. То система работала бы на 100% своей эффективности.
2.2 Сравните анализ: Учебник: «Мало товара -> надо производить больше». Данный подход: «Система обладает низкой эффективностью преобразования товара в деньги (КПД=0.01). Рост количества товара без изменения этого коэффициента бесполезен. Надо менять качество системы: повышать цену, переходить на более маржинальные товары, оптимизировать издержки». Это качественно иной вывод, основанный не на количестве, а на структурном соотношении.
3. Система Координат для Принятия Решений Мы строим универсальную систему координат для любого рынка. Использую квадрат состояний вписанный в единичную окружность. Рычажные весы — это не метафора, а математическая модель. Любое экономическое состояние можно точно позиционировать на этом графике, получив исчерпывающую диагностику.
3.1 Возьмем кризис перепроизводства (например, падение цен на нефть):
Товар/Деньги >> 1 (товара много, денег он приносит мало)
Спрос/Предложение << 1 (предложение сильно превышает спрос)
Точка на графике будет четко находиться в левом нижнем квадранте («Кризис»).
3.2 Возьмем «горячий» стартап на подъеме:
Товар/Деньги < 1 (денег (инвестиций) пока вложено больше, чем создано продукта)
Точка на графике будет в правом верхнем квадранте («Рост»).
Это не подбор примеров под теорию. Это классификация состояний системы на основе объективных измеряемых соотношений. Вы не игнорируете данные, а получаете их точный «диагноз».
4. Кому и для чего это реально нужно?
Владельцу малого бизнеса: Чтобы не просто видеть «упала выручка», а понимать какое именно соотношение нарушилось: упал спрос? стало невыгодно производить? увеличились долги? Это позволяет бить точно в причину, а не по симптомам.
Инвестору/Трейдеру: Чтобы оценивать не отдельные показатели компании (выручка, долги), а их системную сбалансированность. Компания с идеальными точками на этих весах — устойчива. Компания с точками в крайних положениях — кандидат на взлёт или банкротство.
Экономисту-аналитику: Чтобы строить нелинейные прогнозы. Если точка ушла в крайнее положение («перегрев» рынка), система по законам маятника будет стремиться качнуться назад. Это позволяет предсказывать коррекции рынка и кризисы.
Итог: Это не «вольная интерпретация», а прикладной системный анализ, переводящий экономику с языка абсолютных величин на язык структурных отношений и балансов. Он не заменяет учебники, а дает инструмент для синтеза их данных в целостную картину, понятную для принятия решений на любом уровне.
nikolz, Согласен с Вами, несколько сумбурно, но описываемая тема сложна, поэтому растянута на нескольких постах выше. Это абсолютно новый подход идеи концепции механики Г.Я. Зверева с развитием их М.И. Беляевым. Я лишь описываю прикладной характер использования в экономике и в частности трейдинге. На основе этих идей и логике построены две модели, торговые системы, отражающие взаимодействие: 1. Рынок / трейдер. 2. Рынок / Портфель.
Механика, о которой идет речь, выглядит немного сложной, постараюсь объяснить её понятным языком, так как вижу и понимаю.
1. Что такое сила? В классической механике сила — это как бы нечто первичное, что заставляет вещи двигаться. Например, когда вы толкаете мяч, сила заставляет его катиться. Но в механике Зверева сила не является чем-то основным. В этом подходе сила — это результат взаимодействия разных процессов, а не что-то самостоятельное. Представьте, что сила — это как следствие того, что два разных процесса (например, две "энергии") начинают взаимодействовать друг с другом. Сила появляется всякий раз, когда что-то нарушает баланс этих процессов. То есть, сама сила — это не "изначальная сущность", а нечто вторичное, что появляется в результате взаимодействия.
2. Что такое "поток"? Поток — это не то же самое, что вода, текущая по реке. Здесь под потоком понимается фундаментальное движение, основное "древнее" движение, которое лежит в основе всего бытия. Это не просто что-то, что течет, а движение, которое носит в себе потенциал для создания всего, что мы видим вокруг нас. Поток не является простым или однообразным. Он возникает как результат множества взаимодействий более фундаментальных сил. Это как если бы всё, что происходит в мире, являлось результатом сложных взаимодействий на более глубоком уровне.
3. Взаимодействие двух потоков — что происходит? Когда два потока взаимодействуют друг с другом, они могут создать нечто третье. Это взаимодействие напоминает сложение векторов в математике, когда два вектора (направления) создают третий, который несёт совершенно новое качество. Это как если бы два человека, работая вместе, создавали нечто гораздо более значимое и сложное, чем если бы каждый действовал по отдельности. В результате такого взаимодействия возникает новое качество — третья "сила", которая не сводится к простому сложению двух первоначальных потоков.
4. Резонанс как основа всего. Представим себе гигантский музыкальный инструмент, в котором все его части связаны между собой. Когда одна из частей начинает вибрировать, другие части начинают резонировать с ней. Эта вибрация и взаимодействие частей и называется "резонансом". Всё в мире также резонирует с другими частями. Масса, заряд, все свойства вещей — это результат того, как они "вибрируют" в этой общей симфонии мира. Каждое явление или объект, будь то частица или целое тело, не существует самостоятельно, а является частью общей гармонии. Это как музыка, где каждая нота и инструмент создают общий звук, который зависит от того, на каком месте в этом оркестре находится каждая нота.
Главная идея:
Поток — это основное движение, которое лежит в основе всего.
Взаимодействие потоков создаёт новое качество.
Резонанс с более общей "партитурой" мироздания определяет свойства объектов.
Таким образом, мы живем не в мире отдельных предметов, а в мире, где всё связано, взаимодействует и создаёт новые качества через сложные процессы. Классическая механика (Ньютон) объясняет, как частицы взаимодействуют с силами. А эта механика Зверева и Беляева предлагает новую парадигму, где всё объясняется через взаимодействия и паттерны (резонансные связи), а не через отдельные силы и частицы.
В этом подходе важно не то, что есть отдельные сущности, а то, как эти сущности взаимодействуют друг с другом, создавая новые свойства. Простыми словами: Это как оркестр, где каждый инструмент (сущность) играет свою роль, но важно, как эти инструменты взаимодействуют друг с другом, создавая целый симфонический спектакль.
На мой взгляд, продемонстрировать свойства записи пропорции выше, наглядней всего можно с помощью "Золотой пропорции", просто подставив значения в формулу и получая свойства. Об этом уникальном отношении (золотом сечении) написано много книг; напомню его определение, это отношение двух величин, при котором отношение большей части (a) к целому равно отношению большего к меньшему (b): (a + b) /a = a / b = φ ≈ 1.618
Золотое сечение — это особое отношение двух величин, при котором их соотношение обладает уникальными и не только c математической точке зрения, свойствами. Золотая пропорция, математически описывает гармонию, а также является основой для создания систем, которые обладают свойством саморегулирования. Золотое сечение — это механизм оптимизации и саморегуляции в природе, выражающий принцип самоподобия и поддержания баланса через внутреннюю и внешнюю гармонию.
Вот некоторые основные свойства этой уникальной пропорции: 1. принцип Самоподобие; 2. принцип Самодостаточности; 3. принцип Само нормирования саморегуляция или само балансировка.
Когда речь заходит о двойственности, симметрия и асимметрия часто оказываются двумя сторонами одной медали. Взаимосвязь симметрии и асимметрии в золотом сечении — это двусторонняя реальность, где на внешней гармонии строится скрытая, но столь важная асимметрия. Именно эта двусторонность отражает и математическую, и физическую природу мира.
Раскрываем Смыслы:
А). Диагональ 1/1 в записи рычажных весов выражает принцип меры: конвертацию одних свойств в другие. Она придаёт уравнению динамику — систему можно трактовать не только как статичную запись параметров, но и как модель процесса, где силы и расстояния компенсируют друг друга. В экономике аналогом служит закон спроса и предложения. Смысл этой диагонали выражает меру, меру конвертации одних свойств в другие (в том числе конвертацию цен).
Мера здесь становится центральной идеей, которая связывает все взаимодействующие системы в единое целое, устанавливая гармонию и равновесие. Это модель для анализа баланса между различными силами их величинами, но и также контроль изменения их свойств. Эта модель, символизирует баланс между прерывностью и непрерывностью в процессе изменений. Это как в квантовой механике принцип суперпозиция. Концепцию квантовой запутанности, по смыслу, означает, что системы, взаимодействующие друг с другом, могут быть неразделимы и их состояния можно описать лишь через их взаимосвязь.
Б). О смысле масштаба (уровне иерархии). Бинарные отношения имеют гиперкуб состояний 2^n, где n - это масштаб, вложенность процессов, их взаимосвязь, и их фрактальная природа. Каждый уровень иерархии в контексте трейдинга может быть связан с определенным временным масштабом (тайм фреймом). Каждый такой уровень может быть трактован как масштаб ценового движения, где динамика рынка на одном уровне может влиять на динамику рынка на другом уровне. И это отдельная большая тема, я лишь про состояния двойственных отношений.
2^0=1 - монада; 2^1=2 - проявление противоположных свойств; 2^2=4 - квадрат состояний; 2^3=8 - куб состояний, 8 - вершин, 6 - граней, переходы по которым образуют замкнутый цикл преобразований сложного отношения (из проекционной геометрии);
Куб состояний - это достаточная модель, чтоб описать все фазовые переходы бинарной системы, этой экономической модели. Таким образом, квадрат можно рассматривать как проекцию куба на плоскость — упрощённую модель. А куб уже способен отразить более сложные переходы и циклы.
Эти выше описанные принципы, применяемые в идеи создании прикладной системы саморегулирования. Системы, которая бы описывала и управляла динамикой процессов, таких как рыночные отношения и портфель инвестора. Не которые подходы данной идеи, уже демонстрировал на этой ветке. Тема конечно обширна и громоздка для данного форума, а алгоритмы положенные в код луа, относительно небольшие, показывают прекрасные торговые результаты на тестах. Есть еще несколько идей по оптимизации результатов и одна из них нечеткая логика в системе принятия решений. Всем удачи.
Артем написал: Что есть реально другой путь кроме удали-добавь индикатор? Например, при написании кода того же индикатора, он меняется по 10 раз в день, какждый раз я делаю удалил-добавил, можно как то иначе?
В режиме связанных окон (допустим таблица текущих торгов + график), при переходах с тикера на тикер, индикатор будет обновляться в месте с графиком, получая данные и пересчитывая значения. Также заметил в Вашем примере ошибки с типом данных, если на нем экспериментируете лучше поправить, пробелы в именах, линей меньше выводит.
nikolz, _ Ни о каком лучше или хуже речь не идет, речь о надежности, промышленной надежности. Мой пример реализован в модуле, то есть можно использовать в OnCalculate, так и в потоке main создавая псевдонимы функций.
Вот реализация для индикаторов
Код
function Init()
-- создаю псевдоним
local Wilder = dofile("E:\\cached.lua") -- getWorkingFolder() ..
f = Wilder.RSI_Indicator()
Wilder=nil
-- Задаю линии для вывода
Settings.line = {
{Name = "50", Color = RGB(24, 24, 24), Type = TYPE_DASHDOT, Width = 1},
{Name = "lineup", Color = RGB(240, 0, 0), Type = TYPE_DASHDOT, Width = 1},
{Name = "linedw", Color = RGB(240, 0, 0), Type = TYPE_DASHDOT, Width = 1},
{Name = "RSI", Color = RGB(0, 0, 255), Type = TYPE_DASHDOT, Width = 2},
--{Name = "trigger",Color = RGB(255, 0, 0), Type = TYPE_LINE, Width = 1},
--{Name = "signal",Color = RGB(055, 055, 055), Type = TYPE_HISTOGRAM, Width = 1}
}
return #Settings.line
end
function OnCalculate(I)
-- получаю расчеты из внутренней функции
local out = f(I, Settings)
-- вывод результатов
if out then
return 50, Settings.lineup, Settings.linedw, out.rsi , out.rsi1
end
return
end
В потоке main тоже самое, индексация через Size().
Замерять быстродействие? 1) в OnCalculate (в потоке терминала) плохой тон, да и что это дает, одно дело пустой терминал, совсем другое загруженных графиками в часы пик, это совсем другая задача. 2) в скрипте (в потоке main) делаю по умолчанию, замеряя метрики производительности для всей программы (высказывался про это, разбираясь с промышленной реализацией). Да и что измерять, если речь идет о статистических метриках, окно в 14 бар а лаг половину периода в этой реализации (Миг против Вечности). Собственно код должен быть рабочим и Вы все сами можете замерить и сравнить.
Повторюсь я лишил подсветил проблему надежности исполнения скрипта, те вопросы которые относятся к технологическим аспектам выполнения расчетов.
Вот алгоритм из примера выше: В публичной части (константы для приватной части) -- 1. Локальные утилиты для оптимизации -- Форматирование даты и времени -- Функция округления -- 2. Функции сглаживания -- 3. Состояние индикатора
Код
local cache = {
initialized = false,
last_index = 0,
history = {}, -- Хранилище для исторических значений
Configs = {
period = 14,
method = "SMMA",
max_history = 100
},
prev_price = nil
}
В приватной части: -- A. Обновление параметров -- D. Получение текущей цены закрытия -- E. Инициализация записи для текущего индекса -- F. Расчет изменения цены -- H. Расчет средних значений -- I. Расчет RSI -- K. Формирование результата в {} return {}
На проблему озвученную в первом сообщении, там же есть ответ, проблема в версии, все что нужно для нормального вывода, выбросить данную фикцию, получить таблицу и вернуть необходимое количество линий?
Не есть и другой вариант: ждать год пока ответит разработчик, следующие 2 года будет исправлять, а в новой выпущенной версии, какой ни будь "молодой талант" все опять грохнет. На мой взгляд, подход тупиковый! А в целях надежности исполнения вычислений, такие функции нужно уменьшать в коде, лучше совсем убрать.
Извиняюсь что опять вмешиваюсь, просто хочу подсветить параллельную нерешенную задачу. Решение лежит в плоскости создания - универсальной, технологической обвязки, в которою можно было бы не опасаясь загружать любой алгоритм и она с ним справлялась, выдавая на гора результат. Примерный алгоритм такой обвязки я привел в своем примере выше (мягко говоря не идеальный вариант), а хотелось чтоб профессиональное сообщество обсудило, не в соревновательном режиме, а в рамках сотрудничества, чтоб получить надежный публичный вариант. Все одна только польза!
Nikolay написал: Все же эта тема посвящена конкретной технической проблеме терминала.
Nikolay, Весь этот форум посвящён одной большой проблеме, если Вы думаете что устранив данную, не будет похожей, через некоторое время, я думаю мягко говоря Вы ошибаетесь.Думаю нужна профессиональная смекалка, или как тут выражался пользователь "покумекать", чтоб по крайней мере следующая проходила мимо.
Код который я выложил несет в себе две основные особенности. 1) Буферизация. (Зачем скажем 65 000 свечей если окно 14 бар?); 2) Своевременную очистку данных (Зачем скажем 65 000 свечей хранить, если окно 14 бар); ну и конечно момент обновлений и расчётов, зачем рассчитывать каждый тик, если квик с трудом обновляет данные за одну секунду?
Не берусь судить о том, что в очередной раз "изобрели и наваяли" Разработчики, хочу лишь добавить, что в версиях 12.*.*.* при входе в терминал по долгу читаются самописные луа индикаторы (значительно по долгу). Хотя допускаю, что это и моих рук дело, может быть?
Тема написания индикаторов на луа многократно обсуждалась и разбиралась на форуме, и тем не менее, нет внутренней уверенности, что реализация луа индикаторов как минимум оптимальна? Вот и я в очередной раз столкнулся с проблемой создания надежной реализации индикаторов при реализации подхода по Wilder (реализация индикаторов и стратегий). Собственно трудности вызывает не сам алгоритм расчетов, а особенности реализации скриптов луа в QUIK.
Задачу ставил следующую: 1) Скрипт должен использоваться без переделок, как в OnCalculate, так и в потоке луа; 2) Одинаково быстро работать на разных тайм фреймах; 3) Технологическое исполнение (правила написания) должны быть едины, выполняться принцип создания "по Образу и Подобию". Код через класс луа, требует доработки, и вызывает у меня некоторые сложности в реализации, хотя интуитивно на мой взгляд и более предпочтителен, так как хранит свое внутреннее состояние (инкапсулирует). Вот что получилось при использовании подхода с замыканием, код привожу ниже, там же см. в комментариях и порядок написания, единая технология написания скрипта, на мой взгляд логична?
Код
function Wilder.RSI_Indicator()
-- 1. Локальные утилиты для оптимизации
local math_abs = math.abs
local math_max = math.max
local math_min = math.min
local math_floor = math.floor
local string_format = string.format
-- Форматирование даты и времени
local get_date = function(td)
return td and string_format("%.4d%.2d%.2d", td.year, td.month, td.day) or "00000000"
end
local get_time = function(td)
return td and string_format("%.2d%.2d%.2d", td.hour, td.min, td.sec) or "000000"
end
-- Функция округления
local round = function(x, n)
n = n or 0
local mult = 10 ^ n
return x >= 0 and math_floor(x * mult + 0.5) / mult or math_floor(x * mult - 0.5) / mult
end
-- 2. Функции сглаживания
local smoothing = {
EMA = function(new_val, prev, period)
local alpha = 2 / (period + 1)
return alpha * new_val + (1 - alpha) * prev
end,
SMMA = function(new_val, prev, period, bars_count)
if bars_count <= period then
return (prev * (bars_count - 1) + new_val) / bars_count
else
return (prev * (period - 1) + new_val) / period
end
end
}
-- 3. Состояние индикатора
local cache = {
initialized = false,
last_index = 0,
history = {}, -- Хранилище для исторических значений
Configs = {
period = 14,
method = "SMMA",
max_history = 100
},
prev_price = nil
}
return function(I, F, ds)
-- A. Обновление параметров
if F then
cache.Configs.period = F.Period or cache.Configs.period
cache.Configs.method = F.Method or cache.Configs.method
cache.Configs.max_history = F.Max_History or cache.Configs.max_history
end
local period = cache.Configs.period
local method = cache.Configs.method
-- B. Проверка нового бара
local is_new_bar = I > cache.last_index
-- C. Очистка старых данных при новом баре
if is_new_bar then
cache.last_index = I
-- Удаляем устаревшие данные
local min_index = I - cache.Configs.max_history
for i = min_index - 10, min_index do
if cache.history[i] then
cache.history[i] = nil
end
end
end
-- D. Получение текущей цены закрытия
local current_price = Value(I, "C", ds)
if not current_price then
return {
d = "00000000",
t = "000000",
rsi = 50,
rsi1 = 50,
rsi2 = 50,
rsi3 = 50,
gain = 0,
loss = 0,
avg_gain = 0,
avg_loss = 0
}
end
-- E. Инициализация записи для текущего индекса
if not cache.history[I] then
cache.history[I] = {
price = current_price,
change = 0,
gain = 0,
loss = 0,
avg_gain = 0,
avg_loss = 0,
rsi = 50
}
end
-- F. Расчет изменения цены
local prev_price = cache.prev_price
if not prev_price then
-- Для первого бара используем текущую цену как предыдущую
prev_price = current_price
end
local change = current_price - prev_price
cache.history[I].change = change
cache.prev_price = current_price -- Сохраняем для следующего вызова
-- G. Расчет положительных и отрицательных изменений
local gain = math_max(change, 0)
local loss = math_max(-change, 0)
cache.history[I].gain = gain
cache.history[I].loss = loss
-- H. Расчет средних значений
if I == 1 then
-- Первый бар
cache.history[I].avg_gain = gain
cache.history[I].avg_loss = loss
else
local prev_entry = cache.history[I-1] or cache.history[I]
local bars_count = math_min(I, period)
if method == "SMMA" then
cache.history[I].avg_gain = smoothing.SMMA(
gain,
prev_entry.avg_gain,
period,
bars_count
)
cache.history[I].avg_loss = smoothing.SMMA(
loss,
prev_entry.avg_loss,
period,
bars_count
)
else -- EMA
cache.history[I].avg_gain = smoothing.EMA(
gain,
prev_entry.avg_gain,
period
)
cache.history[I].avg_loss = smoothing.EMA(
loss,
prev_entry.avg_loss,
period
)
end
end
-- I. Расчет RSI
local avg_gain = cache.history[I].avg_gain
local avg_loss = cache.history[I].avg_loss
local rs = (avg_loss > 1e-5) and (avg_gain / avg_loss) or 0
local rsi = 100 - (100 / (1 + rs))
-- Корректировка граничных значений
rsi = math_max(0, math_min(100, rsi))
cache.history[I].rsi = rsi
-- J. Получение предыдущих значений
local prev1 = cache.history[I-1] or {rsi = 50}
local prev2 = cache.history[I-2] or {rsi = 50}
local prev3 = cache.history[I-3] or {rsi = 50}
-- K. Формирование результата
local td = ds:T(I)
local Out = {
d = get_date(td),
t = get_time(td),
rsi = round(rsi, 2),
rsi1 = round(prev1.rsi, 2),
rsi2 = round(prev2.rsi, 2),
rsi3 = round(prev3.rsi, 2),
gain = round(gain, 4),
loss = round(loss, 4),
avg_gain = round(avg_gain, 4),
avg_loss = round(avg_loss, 4),
_cache = cache.history[I]
}
Wilder.Log( tostring(I)..') '.. Out.d ..' / '.. Out.t
..'; rsi = ' .. tostring(Out.rsi)
..'; rsi1 = ' .. tostring(Out.rsi1)
..'; rsi2 = ' .. tostring(Out.rsi2)
..'; rsi3 = ' .. tostring(Out.rsi3)
..'; gain = ' .. tostring(Out.gain)
..'; loss = ' .. tostring(Out.loss)
--..'; atr = ' .. tostring(Out.atr)
..'; avg_gain = ' .. tostring(Out.avg_gain)
..'; avg_loss = ' .. tostring(Out.avg_loss)
--..'; trend = ' .. tostring(Out.trend)
--..'; trend1 = ' .. tostring(Out.trend1)
--..'; trend2 = ' .. tostring(Out.trend2)
--..'; signal = ' .. tostring(Out.signal)
--..'; signal1 = ' .. tostring(Out.signal1)
--..'; signal2 = ' .. tostring(Out.signal2)
)--[[--]]
cache.initialized = true
return Out
end
end
Или почему русский язык (его остатки + вся совокупность языковой группы), является языком программирования природных операционных механизмов (самоорганизация, самосохранение, саморегулирование, самонормировка ...). Родной язык сохранил СМЫСЛЫ, то что без СМЫСЛА, бессмыслица == не родное!
Этот не большой пример, помогает проиллюстрировать этот подход. В арифметике существуют четыре основные операции (+, -, *, /): Операция 1) (+) Говорим СЛОЖИТЬ или прибавить. Сложить - смыслы что то упорядочить, сохранить (сложить на складе), прибавить что - то к чему - то. Операция 2) (-) Говорим Вычесть Отнять. Тут и комментировать нечего, отнять так отнять. Операция 3) (*) Говорим Умножить. Допустим, то что всех интересует в инвестициях - приумножить капитал. Ведь никто не говорит сложить капитал? Операция 4) (/) Говорим Делить или Отношение. Как ты ко мне относишься? Так формируем смысловой вопрос. Смыл операции делить комментировать излишни. Есть еще операция возведения в степень, но это собственно умножение. Да и четыре математических операций по смыслу далеко не четыре!
Сермяжная правда, или почему инвестиции - это при Умножение капитала, а следовательно ключевыми становятся операции при УМНОЖЕНИЯ и ОТНОШЕНИЯ! Выяснения отношения - проводим анализ, а умножение это процесс - созидания. Эти двойственные взаимно дополнительные отношения, есть не что иное, как главное звено системы. Выделяя главное звено, потяну за которое распутывается весь клубок взаимоотношений. Верно и обратное!
Имея двойственные отношения, записанные в виде закона РЫЧАГА, можно сделать математические преобразования и вернуть полную СМЫСЛОВУЮ запись уравнения математических весов. (М1 / 1)^(+n) = (1 / М2)^(-n), где M - выступает как момент силы; n - можно назвать уровень развития - степень иерархии. Именно так, с помощью математических весов, можно записать производящие функции природных операционных механизмов (генетический код), а это уже "Наивысшая Математика" и совсем другая тема!
В целях задачи анализа, интерес вызывает вторая часть этого уравнения, которая отражает внутреннее содержание или структуру (нет ничего больше 1, а единица это целое). То есть, определив главное звено системы, записав таким образом двойственные ОТНОШЕНИЯ, со всей математической строгостью, можно применять структурный анализ, на каждой ступени иерархии. А если рассматриваются взаимодополнительные ОТНОШЕНИЯ, то весы уже станут монадой, и можно со всей математической строгостью, применять весь тот исторический аппарат анализа и прогнозов сохранившихся знаний. Не только распутывать, но и строить новые генетические цепочки, делать прогнозы (СУДЬБЫ) для систем любой природы, используя математический аппарат "Наивысшей Математики". И это уже не философские аспекты Лейбница, а строгий математический аппарат "Наивысшая Математика", здесь вероятно правильней, сохраняя смыслы, назвать "ВЫСШЕЙ АРИФМЕТИКОЙ".
На помню что, момент силы выступает мерой по отношению к силе (или плечу рычага?), и всегда можно развернуть, уточняя детали. Тогда записи вида: Капитал = Деньги + Активы, Активы = Спрос на Акции + Предложение Денег ..., приобретают новые СМЫСЛЫ, а у нас появился новый дополнительный универсальный аппарат анализа и что не менее важно СОЗИДАНИЯ - "вычисление судьбы"!
Или как инженерный подход к рынку Уайлдера, превратил трейдинг из искусства в науку, введя стандартизированные метрики (ATR, ADX, SAR). Цитата Уайлдера (из интервью Forbes, 1980): «Рынок — не хаос. Это машина, и моя задача — найти её инструкцию по эксплуатации». Вот уже почти полвека назад, опубликована работа Уэллса Уайлдера «Новые концепции в технических торговых системах» (1978). Кто не знаком с формулами, которые до сих пор используются в алгоритмической торговле. Идеи легли в основу миллионов торговых роботов. Любая торговая платформа, включет эти индикаторы, как стандартный инструмент.
Вот несколько его принципов к анализу рынка. Рынок трактуется как механизм, где цена, объем и время — взаимосвязанные переменные. Задача трейдера — вывести «формулы» их взаимодействия. Отказ от традиционных методов анализа, визуальной интерпретации графиков, в пользу математически верифицируемых систем. Несмотря на акцент на математике, его системы требуют понимания рыночного контекста.
Почему Механика? Одной из основных концепций в механике является концепция РЫЧАГА. Математическая запись принципа действия рычага является тождество либо неравенство. Тождество (F₁ * d₁ = F₂ * d₂), выражает фундаментальное условие его равновесия в идеализированной модели. Это тождество лежит в основе понимания того, как рычаги позволяют нам умножать силу или изменять направление ее действия, и является одной из краеугольных концепций механики, известной еще со времен Архимеда.
Момент силы, тогда выражается M = F * d — и является МЕРОЙ действия силы.
Именно так записан Закон Спроса и Предложения в экономике, просто меняются переменные и смысл, а закон прежний - закон РЫЧАГА. Точно так же можно применять в любой сфере анализа, по этому принципу работает и финансовый рычаг, и валютные пары.
Судя по откликам и комментариям были "индусы" , но это уже не важно, а проверять видимо нужно все! Понятие Веры не из финансов. Быстрый поиск на просторах не дал нужного эффекта, друг у друга копируют перерисовывают и публикуют. Обратился к источнику, хотя моя версия электронной книги не очень удобна, но оно того стоило! 190 страниц без всякой "воды", одни выкладки и миллиметровка! "Да, были люди в наше время, Не то, что нынешнее племя: Богатыри — не вы!"
Сработал и принцип монады (взаимно дополнительные двойственные отношения) "нет худа, без добра", заприметил несколько интересных идей.
Сама ошибка банальна, но принципиальна, кто знаком с до историческими (до ПВМ) работами трейдеров (Ганн, Вайкофф вот и Уайлдер приверженец старой школы) по определению трендов, знает что кроме баров трендовых, есть бары поглощения и внутренние, собственно которые и не учтены при определениях направленных движений.
Еще одно что смущает, так это в каком варианте использовать истинный диапазон (TR), для нормализации данных, сглаживать или нет? В своем варианте я пока предпочитаю сглаживать, так как использование простого TR приводит к значительным выбросам.
На помню для чего так подробно разбираюсь с ADX? Интересуют инвестиционная алгоритмическая стратегия (ну или средний срок), а на этих временных интервалах, где дневной график выступает как 1 минутный, индикатор уже и в этой реализации дает отличные результаты. Когда ADX входит в резонанс с одним из направлений движений, это означает что у противоположной стороны заберут всю ликвидность, именно про эти ситуации говорят и пишут, "не стой на пути у паровоза"! За дело взялись большие деньги (Крупный игрок, а Маркет Мейкер за частую вынужден участвовать в противофазе), здесь входят инвестиционые фонды, банки и поддерживают свои позиции. Про эти ситуации говорят, дай прибыли течь!
Пару слов про индекс относительной силы (RSI), ранее как то я его не до оценивал, предпочитая стохастик как более очевидный. Но RSI с ADX, это дополнительная мощность в принятии решений, автор его спроектировал так что он является центростремительным в отличии от стохастика который гуляет по локальным минимумам / максимумам. Что бы сдвинут RSI в зоны 70 или 30 нужно очень сильно постараться! А выход из этих зон говорит об однозначно слабости стороны.
Вот и судите сами стоит такая стратегия и Уайлдер, чтоб пели про них "дифирамбы"?
ADX («Индекс направления движения усредненной цены»)
Индикатор ADX (Average Directional Movement Index) предназначен для определения направления движения среднего значения цены на основе сравнения двух индикаторов направленности
* +DI (положительного изменения цены) и
* -DI (отрицательного изменения цены),
построенных по 14 периодам.
По рекомендации автора индикатора Уэллса Уайлдера преобладание линии +DI над -DI является сигналом «покупать», и «продавать» если значение -DI поднимается выше значения +DI.
Вычисление:
ADXj = EMA (DXj , N), Где DXj вычисляется следующим образом:
DXj = |(+DIj - -DIj)| / (+DIj + -DIj) * 100
+DIj = EMA (+SDIj , N)
-DIj = EMA (-SDIj , N)
+SDIj = +DMj / TRj * 100 если TRj <> 0, иначе +SDIj = 0
-SDIj = -DMj / TRj * 100 если TRj <> 0, иначе -SDIj = 0
TRj = max(|HIGHj - LOWj|, |HIGHj - CLOSEj-1|, |LOWj - CLOSE j-1|)
+DMj = |HIGHj - HIGHj-1| если HIGHj > HIGHj-1, иначе +DMj = 0
-DMj = |LOWj-1 - LOWj| если LOWj < LOWj-1, иначе -DMj = 0
если +DMj > -DMj, то -DMj = 0
если -DMj > +DMj, то +DMj =0
если +DMj = -DMj, то +DMj =0, -DMj =0
где:
CLOSE — цена закрытия;
LOW — минимальная цена интервала;
HIGH — максимальная цена интервала.
Значения индикаторов +DI и –DI учитываются с двумя знаками после запятой.
Параметры настройки:
«Кол-во периодов» - количество периодов N для расчета MA.
«Метод» - метод расчета МА (Simple, Exponential, Vol.Adjusted, Smoothed), по умолчанию используется метод «Exponential».
«Цвет +DI» - выбор цвета линии +DI.
«Цвет -DI» - выбор цвета линии -DI.
Мало того что в вычислениях ошибка, так еще и "к верху тормашками" , видимо кто английскую школу окончил или индусов наняли. Но от этого нелегче. Не ужели все перепроверять нужно за ARQA Technologies? Вообще в приличных домах принято, если оригинальный алгоритм подвергается изменениям, хотя бы "пометочку" делать!
Самое главное, встроенный индикатор отлично располагается в нормированном поле. Возможно кто то знаком с проблемой и про комментирует, или разработчики внесут пояснения (разъяснения), что не так с алгоритмом?
Прогнозная зона графика, График цены или индикатора по оси времени можно сдвигать образуя прогнозную зону. Но как с ней работать с самописными индикаторами?
Вариант 1. "Редактирование настроек графика", "Диаграмма", позволяет сдвигать правый край графика на заданное количество баров. Тем самым как бы образуется прогнозная зона на графике относительно текущего значения.
Вариант 2. Вкладка "Дополнительно", сдвиг графиков, позволяет двигать график в обе стороны.
Но как с ними работать из луа, не понятно? Какие индексы применять? На мой взгляд, было бы отлично, если бы простой интеграцией можно было отрисовывать индикаторы в этой зоне. В общем понимания совсем нет, рад любой информации. Спасибо что откликнулись.
-- Получение значений индикаторов
local rsi_val = rsi(I, {period = period_rsi}, ds)
local adx_val, plusDI, minusDI = adx(I, {di_len = period_adx, adx_len = period_adx}, ds)
local ema_val = ema(I, {period = period_ema}, ds)
local price = C(I) or 0
-- Определение рыночной фазы
local market_phase
if adx_val > 25 and plusDI > minusDI
--and price > ema_val
then
market_phase = "uptrend"
elseif adx_val > 25 and minusDI > plusDI
--and price < ema_val
then
market_phase = "downtrend"
else
market_phase = "range"
end
-- Сигналы управления
local signal = 0
-- Правило 2: Не торгуем в флэте
if market_phase == "range" then
if position ~= 0 then
signal = -position -- Закрытие позиции
end
return signal
end
-- Длинные позиции
if market_phase == "uptrend" then
-- Сигнал на покупку (Правило 3)
if position <= 0 and
price > ema_val and
adx_val > prev_adx and
rsi_val < 75 then
signal = 1
end
-- Выход из лонга (Правило 4)
if position == 1 and
(rsi_val > 75 or adx_val < prev_adx) then
signal = -1
end
end
-- Короткие позиции
if market_phase == "downtrend" then
-- Сигнал на продажу (Правило 5)
if position >= 0 and
price < ema_val and
adx_val > prev_adx and
rsi_val > 25 then
signal = -1
end
-- Выход из шорта (Правило 6)
if position == -1 and
(rsi_val < 25 or adx_val < prev_adx) then
signal = 1
end
end
-- Обновление состояния
prev_adx = adx_val or 0
position = position + signal
-- Ограничение позиции [-1, 0, 1]
position = math.max(-1, math.min(1, position))
Прежде чем продолжить экспериментировать с фильтрами, нужна торговая стратегия для этих самых эксперементов, "велосипед будет старый", за основу возьму ТОРГОВЫЙ АЛГОРИТМ 1.0 (ВАЛЕНТИНА САВЕНКОВА) из Школы МосБиржи.
От туда же и правила, просто формализую для алгоритмической торговли:
[Правило 1. 3 фазы динамики цены: Растущий тренд ADX>25 +DI>-DI, Флэт (боковик) ADX<25 , Падающий тренд ADX>25 -DI>+DI. Правило 2. Флэт (боковик) ADX<25 не торгуем. Правило 3. Сигнал продолжения на покупку Price > Ema, Растущий тренд ADX>25 +DI>-DI, Сильный ADX (ADX[I]>ADX[I-1]. Правило 4. Выход из позиции Long сигналы RSI на продажу и слабый ADX (ADX[I]<ADX[I-1] Правило 5. Сигнал продолжения на продажу Price < Ema, Падающий тренд ADX>25 -DI>+DI, Сильный ADX (ADX[I]>ADX[I-1]. Правило 6. Выход из позиции Short сигналы RSI на покупку и слабый ADX (ADX[I]<ADX[I-1]. Правило 7. Для RSI: зона перекупленности >75; перепроданности <25; Выход из зон торговый сигнал.
В процессе тестирования будут меняться правила и условия, поэтому зафиксирую базовые особенности. Особенности реализации: --[[код стратегии для QUIK на Lua 5.4 (процедурный стиль). Все расчеты выполняются последовательно, -- состояние хранится в глобальных таблицах (или буферизация?), минимальные вычислительные затраты, локальное кэширование ценовых данных. Рекуррентные формулы для EMA и RSI, трекинг состояния через переменную position, защита от повторных входов, Четкие правила выхода. Визуализация, ? независимых графических серий, Цветовое кодирование элементов, Настройки графического интерфейса в QUIK: Сигналы отображать стрелками: AddSignal(index, signal > 0 and 1 or signal < 0 and -1 or 0) Добавить горизонтальные уровни для RSI: AddLevel(75, "Overbought", RGB(255,0,0)) AddLevel(25, "Oversold", RGB(0,255,0)) Добавить горизонтальные уровни для ADX: AddLevel(25, "Weakness", RGB(0,255,0))
Торговая стратегия на основе ADX, RSI и EMA. Правила входа/выхода, согласно условиям задачи см. выше. ТОРГОВЫЙ АЛГОРИТМ 1.0 (ВАЛЕНТИНА САВЕНКОВА) Школа МосБиржи. --]]
Древний не значит ненадежный! Спасибо, разобрался исходную версию, перевел на луа 5.4, оказывается все отрисовывает, видимо была какая то моя уже модернизация. Отличный индикатор!
Я пользуюсь этой версией, посмотрел у Вас уже ее и нет.
Код
--logfile=io.open("C:\\SBERBANK\\QUIK_SMS\\LuaIndicators\\qlua_log.txt", "w")
Settings = {Name = "*iReg",
bars = 182,
kstd=2,
degree = 3, -- 1 linear, 2 parabolic, 3 third-power
barsshift=0
}
-- Пользовательcкие функции
function WriteLog(text)
logfile:write(tostring(os.date("%c",os.time())).." "..text.."\n")
logfile:flush()
LASTLOGSTRING = text
end
----------------------------------------------------------
----------------------------------------------------------
----------------------------------------------------------
function Reg()
local sql_buffer={}
local sqh_buffer={}
local fx_buffer={}
local sx={}
local calculated_buffer={}
return function(ind, Fsettings)
local Fsettings=(Fsettings or {})
local index = ind
local bars = Fsettings.bars or 182
local kstd = Fsettings.kstd or 2
local barsshift = Fsettings.barsshift or 0
local degree = Fsettings.degree or 1
local index = ind
local out1 = nil
local out2 = nil
local out3 = nil
local p = 0
local n = 0
local f = 0
local qq = 0
local mm = 0
local tt = 0
local ii = 0
local jj = 0
local kk = 0
local ll = 0
local nn = 0
local sq = 0
local i0 = 0
local mi = 0
local ai={{1,2,3,4}, {1,2,3,4}, {1,2,3,4}, {1,2,3,4}}
local b={}
local x={}
p = bars
nn = degree+1
if index == 1 then
sql_buffer = {}
sqh_buffer = {}
fx_buffer = {}
calculated_buffer = {}
sql_buffer[index]= 0
sqh_buffer[index]= 0
fx_buffer[index]= 0
--- sx
sx={}
sx[1] = p+1
for mi=1, nn*2-2 do
sum=0
for n=i0, i0+p do
sum = sum + math.pow(n,mi)
end
sx[mi+1]=sum
end
return nil
end
sql_buffer[index] = sql_buffer[index-1]
sqh_buffer[index] = sqh_buffer[index-1]
fx_buffer[index] = fx_buffer[index-1]
out1 = fx_buffer[bars]
out2 = sqh_buffer[bars]
out3 = sql_buffer[bars]
SetValue(index-bars-barsshift, 1, nil)
SetValue(index-bars-barsshift, 2, nil)
SetValue(index-bars-barsshift, 3, nil)
if not CandleExist(index) or index <= bars then
return nil
end
if index < (Size() - barsshift) then return nil end
if index > (Size() - barsshift) then return nil end
if calculated_buffer[index] ~= nil then return out1, out2, out3 end
--- syx
for mi=1, nn do
sum = 0
for n=i0, i0+p do
if CandleExist(index+n-bars) then
if mi==1 then
sum = sum + C(index+n-bars)
else
sum = sum + C(index+n-bars)*math.pow(n,mi-1)
end
end
end
b[mi]=sum
end
--- Matrix
for jj=1, nn do
for ii=1, nn do
kk=ii+jj-1
ai[ii][jj]=sx[kk]
end
end
--- Gauss
for kk=1, nn-1 do
ll=0
mm=0
for ii=kk, nn do
if math.abs(ai[ii][kk])>mm then
mm=math.abs(ai[ii][kk])
ll=ii
end
end
if ll==0 then
return nil
end
if ll~=kk then
for jj=1, nn do
tt=ai[kk][jj]
ai[kk][jj]=ai[ll][jj]
ai[ll][jj]=tt
end
tt=b[kk]
b[kk]=b[ll]
b[ll]=tt
end
for ii=kk+1, nn do
qq=ai[ii][kk]/ai[kk][kk]
for jj=1, nn do
if jj==kk then
ai[ii][jj]=0
else
ai[ii][jj]=ai[ii][jj]-qq*ai[kk][jj]
end
end
b[ii]=b[ii]-qq*b[kk]
end
end
x[nn]=b[nn]/ai[nn][nn]
for ii=nn-1, 1, -1 do
tt=0
for jj=1, nn-ii do
tt=tt+ai[ii][ii+jj]*x[ii+jj]
x[ii]=(1/ai[ii][ii])*(b[ii]-tt)
end
end
---
for n=i0, i0+p do
sum=0
for kk=1, degree do
sum = sum + x[kk+1]*math.pow(n,kk)
end
fx_buffer[n]=x[1]+sum
SetValue(index+n-bars, 1, fx_buffer[n])
end
--- Std
sq=0.0
for n=i0, i0+p do
if CandleExist(index+n-bars) then
sq = sq + math.pow(C(index+n-bars)-fx_buffer[n],2)
end
end
sq = math.sqrt(sq/(p+1))*kstd
for n=i0, i0+p do
sqh_buffer[n]=fx_buffer[n]+sq
sql_buffer[n]=fx_buffer[n]-sq
SetValue(index+n-bars, 2, sqh_buffer[n])
SetValue(index+n-bars, 3, sql_buffer[n])
end
SetValue(index-bars, 1, nil)
SetValue(index-bars, 2, nil)
SetValue(index-bars, 3, nil)
calculated_buffer[index] = true
out1 = fx_buffer[bars]
out2 = sqh_buffer[bars]
out3 = sql_buffer[bars]
return out1, out2, out3
end
end
---------------------------- ---------------------------- ----------------------------
---------------------------- ---------------------------- ----------------------------
---------------------------- ---------------------------- ----------------------------
function FindExistCandle(I)
local out = I
while not CandleExist(out) and out > 0 do
out = out -1
end
return out
end
local myfunc
function Init()
myfunc = Reg()
Settings.line=
{
{
Name = "iReg",
Color = RGB(0, 0, 255),
Type = TYPE_LINE,
Width = 2
},
{
Name = "+iReg",
Color = RGB(0, 128, 0),
Type = TYPE_LINE,
Width = 2
},
{
Name = "-iReg",
Color = RGB(192, 0, 0),
Type = TYPE_LINE,
Width = 2
}
}
return #Settings.line
end
function OnCalculate(index)
--WriteLog ("OnCalc() ".."CandleExist("..index.."): "..tostring(CandleExist(index)))
if Settings.degree > 3 then
return nil
end
return myfunc(index, Settings)
end
Nikolay, Индикатор Reg() при появлении нового индекса рассчитывает значение и выводит на график, А между сменой индексов, при дёргании функции обратного вызова, он удаляет с графика значения, На больших интервалах допустим на часовом моргнула и сразу затерла, то есть целый час нет визуализации значения на текущей свече, В идеале конечно, чтоб еще в прогнозную зону отрисовывал линии, Я тему с прогнозной зоной поднимал, но она интереса не вызвала.
Нет здесь странностей. просто ставим все на свои места. 1) Колебательные движения значит частота или волновой процесс. Поведение цены можно представить как сумму волн разной частоты и амплитуды (гармоники). 2) С точки зрения технического анализа, Тренд — это направленное движение цены (вверх или вниз). Он часто выглядит как линейная зависимость. Внутри тренда происходят колебательные (волатильные) движения: импульсы, откаты, коррекции.
Кстати у Вас есть прекрасный индикатор регрессионного анализа, выделяющий тренд 3 видов с использованием волатильности. Я его активно использую, пользуясь случаем хочу Вас попросить посмотреть его затирает текущие значения.
Наглядный пример моего подхода, как модели тренда, это синусоида, если взять от от мах до минимума и наоборот, чем не трендовое движении. функция распределения плотности в экстремальных диапазонах имеет максимальные значения, вот уже и Вайкофф появился с зонами распределения и накопления. Кстати функция распределения плотности цены на длительном интервале больше похожа как у синусоиды.
Согласен, проблема тут есть, но ведь и задача при определении тренда стоит выделить несущею частоту, а значит незначительные изменения в значениях периода. А что смущает в использовании фильтра или индикатора, на мой взгляд так наиболее информативный подход, при графическом анализе все равно сохраняется не кая интуитивность. А в индикаторном поймал тренд так и сиди в нем, мне думается что много ошибок именно в применении. Еще один момент в рассуждениях о ценовых рядах, часто умалчивают, что сама экономика построена так что капитал дисконтируется, а активы дорожают. А что объем, посмотри те какие дисбалансы и гэпы (про срочный рынок). WaPrice подкачиваю посматриваю, очень часто дневная свеча меняет на переходах свой цвет. Насчет использования индикаторов, так а что тут объяснять, на мой взгляд все предельно просто, волновая структура применима ко всему, отображение цены на графике колебательный процесс, значить имеем полное право использовать амплитудно-частотные преобразования. Доказательством правомерности может служить мат. ожидание системы.
Nikolay, Отличное видео, прекрасная демонстрация, автору и Nikolay, - огромное спасибо за популяризирование и просвещение. К видео только одно замечание, преобразование Фурье вносит тоже запаздывание и его тоже нужно учитывать. У себя я просто держу напоминалочку, типа
Описание методов фильтрации в индикаторе Swiss Army Knife (John Ehlers): 1. EMA (Exponential Moving Average) - Тип: Однополюсный фильтр низких частот (ФНЧ). - Формула: Output = a * Price + (1 - a) * Previous_Output - Применение: Сглаживание ценовых данных, подавление высокочастотного шума. - Подходит для трендовых рынков. - Используется для определения направления тренда. - Пример: Выявление долгосрочных тенденций на дневных графиках.
2. SMA (Simple Moving Average)** - Тип: Равновесный ФНЧ. - Формула: Output = (Price + Price[1] + ... + Price[n]) / (n + 1) - Применение: Устранение рыночного шума с линейным сглаживанием. - Идеален для фильтрации "белого шума". - Используется в стратегиях пересечения средних. - Пример: Сигналы при пересечении 50- и 200-периодной SMA. ....
Условно тех. анализ делят на две основные группы. ▪ Графический анализ: Поддержка и сопротивление, Модели разворота и продолжения, Свечной анализ; ▪ Индикаторный анализ: Индикаторы тренда (сигналы продолжения), Осцилляторы (сигналы разворота), Анализ объема (дополнительные фильтры).
Я только сейчас рассуждаю про Индикаторный анализ, так как это просто математика, в рамках одной широко распространённой стратегии ADX RSI (в рамках 60% инвестиционного дохода). Для реальной торговли конечно нужен Графический анализ, у себя еще прикручиваю Анализ реального времени, но сейчас я не про это. Задачу ставлю разобраться и показать, что давно забытые стратегии прекрасно работают, в современных условиях.
Сглаживание, слишком упрощенно так рассматривать, прежде всего это преобразование для приемлемого вида, для применения в расчета, это всегда компромисс между сглаженностью и чувствительностью (отзывчивостью фильтра). Эти уникальные фильтры является результатом общих концепций цифровой обработки сигналов (DSP) для дискретных сетей сигналов, которые появляются в различных формах в техническом анализе. Передаточная функция такой дискретной линейной системы - это отношение выхода системы, деленное на вход. Если рассматривать весовые коэффициенты, для SMA это 1 равны на всем периоде данных, то для EMA они убывают, перенося важность значений на новые.
И последнее, просто напомню, что обратная величина частоте, это тот самый период, который мы просто можем адаптировать, с помощь математики. А значит и фильтры применять с этими периодами каждый по назначению.
Идея заключается в том, чтобы заменить стандартные методы сглаживания на более эффективные, обеспечивающие меньшее запаздывание. Сглаживание по методу Уайлдера - Рекуррентное сглаживание, проблема, этого классического сглаживания (фактически EMA с α = 1/N) дает значительный лаг! Возможно это различие и является причиной различий в расчётах индикатора?
Для снижения лага в расчете ADX можно применить следующие типы фильтров, фокусируясь на ключевых этапах его вычислений: * фильтры из Swiss Army Knife (SAK), ранее на сайте мною были опубликованы в процедурной версии (как индикатор); * фильтры вроде Zero-Lag EMA (ZLEMA); * или адаптивные методы сглаживания, которые динамически подстраиваются под рыночные условия (Kalman Filter).
Оптимизация ADX с использованием фильтров Swiss Army Knife (SAK), наиболее любопытный вариант, интеграция фильтров Джона Элерса позволяет значительно снизить задержки ADX, сохранив его основную логику.
Сравнивать буду точки пересечения +DI/-DI в классической и SAK-версиях. Предварительные тесты показывают, что оптимизированная версия должна давать сигналы на 2-3 бара раньше при сопоставимой точности. Много это или мало судите сами, для HFT-стратегий (в рамках HFT-стратеги QUIK) это одна тема, для цикличных рынков совсем другая, ну и конечно для трендоследящих, ради чего все это затевалось (вспомнил хоть и отстал от графика, примерно так стоишь на остановке в ожидании поезда, а его все нет, вот уже вторые сутки нет). А получить торговый сигнал на 2-3 дня или часа РАНЬШЕ! Каково?
Пару слов о фильтрах, здесь имеет значение практически все, торговый тайм фрейм, период окна данных и так далее, все зависит от задачи что хотим выделить или подчеркнуть. Например, как вариант применения: Для высокочастотной торговли используйте - EMA или Butterworth_LP Для подавления шума в трендах - Kalman + Wavelet Для цикличных рынков - комбинация BP + HP Каждый из них вносит свою лепту в вычисления.
Начать можно с адаптивного периода + ZLEMA для сглаживания, для HFT-стратегий можно добавить Kalman Filter. Примеры стратегий которые хочу проверить: а) Тренд + Осциллятор: ФНЧ (Butterworth) для тренда + ФВЧ (HP) для сигналов входа. б) Цикловая торговля: BP-фильтр для выделения 14-дневного цикла + SMA для подтверждения. с) ? Арбитраж: BS-фильтр для подавления рыночного шума + EMA для анализа тренда.
Для тестирования в стратегии планирую сравнивать: 1) Задержку сигналов; 2) Количество ложных срабатываний; 3) Ну и можно рассчитывать соотношение Sharpe до/после оптимизации. Но для этого нужна торговая стратегия, но это уже совсем другая тема.
Тема фильтрации обширна, пишите свои идеи будем разбираться вместе.
Сравнил более внимательно, есть отличие в сглаживании и соответственно в масштабах отражения этих данных. В общем мой вариант рабочий, не читать же с графиков. А с фильтрами по экспериментирую еще, собственно это и была задача.
Nikolay, Так и мой вариант не соответствует встроенному в терминал. Вроде все корректно делаю как в букваре? Накладываю один на другой не соответствуют?
А Чудеса продолжаются! Оказывается, мало того что ADX(Lua) написан с ошибками, так еще и расчеты отличаются от ADX встроенного в терминал? Накидал свой вариант, но и он выводит другие значения:
Код
function algorithms.getADX()
--[[
Индикатор ADX с +DI и -DI
Среда: QUIK, Lua 5.4
По методике Уэллса Уайлдера (period = 14)
--]]
-- Вспомогательные функции
local function sum(t, start_idx, end_idx)
local total = 0
for i = start_idx, end_idx do total = total + (t[i] or 0) end
return total
end
local function average(t, start_idx, end_idx)
return sum(t, start_idx, end_idx)/(end_idx - start_idx + 1)
end
-- Локальные буферы для хранения данных
local TR, plusDM, minusDM = {}, {}, {}
local smoothedTR, smoothedPlusDM, smoothedMinusDM = {}, {}, {}
local plusDI, minusDI, DX, ADX = {}, {}, {}, {}
local initialized = false -- Флаг инициализации
return function (I, Fsettings, ds)
local Fsettings = Fsettings or {}
local P = Fsettings.Period or 14 -- Период расчета
-- Инициализация буферов при первом запуске
if I == 1 or not initialized then
TR, plusDM, minusDM = {}, {}, {}
smoothedTR, smoothedPlusDM, smoothedMinusDM = {}, {}, {}
plusDI, minusDI, DX, ADX = {}, {}, {}, {}
initialized = true
return nil, nil, nil
end
-- Получение цен текущего и предыдущего бара
local high = H(I) or 0
local low = L(I) or 0
local closePrev = C(I-1) or 0
local highPrev = H(I-1) or 0
local lowPrev = L(I-1) or 0
-- 1. Расчет True Range (TR)
TR[I] = math.max(
high - low,
math.abs(high - closePrev),
math.abs(low - closePrev)
)
-- 2. Расчет Directional Movement (+DM и -DM)
local upMove = high - highPrev
local downMove = lowPrev - low
plusDM[I] = (upMove > downMove and upMove > 0) and upMove or 0
minusDM[I] = (downMove > upMove and downMove > 0) and downMove or 0
-- 3. Сглаживание по методу Уайлдера
if I == P then
-- Первичное сглаживание (сумма первых P значений)
smoothedTR[I] = sum(TR, 2, P) -- TR начинается с 2-го бара
smoothedPlusDM[I] = sum(plusDM, 2, P)
smoothedMinusDM[I] = sum(minusDM, 2, P)
elseif I > P then
-- Рекуррентное сглаживание
smoothedTR[I] = smoothedTR[I-1] - (smoothedTR[I-1]/P) + TR[I]
smoothedPlusDM[I] = smoothedPlusDM[I-1] - (smoothedPlusDM[I-1]/P) + plusDM[I]
smoothedMinusDM[I] = smoothedMinusDM[I-1] - (smoothedMinusDM[I-1]/P) + minusDM[I]
end
-- 4. Расчет Directional Indicators (+DI и -DI)
if I >= P and smoothedTR[I] and smoothedTR[I] > 0 then
plusDI[I] = 100 * (smoothedPlusDM[I]/smoothedTR[I])
minusDI[I] = 100 * (smoothedMinusDM[I]/smoothedTR[I])
-- 5. Расчет Directional Movement Index (DX)
local di_sum = plusDI[I] + minusDI[I]
DX[I] = (di_sum ~= 0) and (100 * math.abs(plusDI[I] - minusDI[I])/di_sum) or 0
end
-- 6. Расчет ADX (сглаженный DX)
if I >= 2*P then
if I == 2*P then
-- Начальное значение ADX
ADX[I] = average(DX, P+1, 2*P) -- Среднее за P периодов
else
-- Сглаживание методом Уайлдера
ADX[I] = (ADX[I-1]*(P-1) + DX[I])/P
end
end
-- Возврат значений (ADX, +DI, -DI)
return ADX[I] or nil, plusDI[I] or nil, minusDI[I] or nil
end
end
Люди кто знаком с темой подскажите, что не так? То что должно занимать пять минут, колупаю который день, чай уже давно закончился, да и видимо чаем тут одним не обойтись.
*О, Чудеса чудесные!* Нет видимо, нужно заводить отдельную тему (ветку) под названием "Чудеса чудесные!", для прославления талантов (название – как вариант)?
Кто на это раз? Обо всем и по порядку!
Выше обозначил классическую стратегию с которой хотелось детальней разобраться, чтобы разбираться нужны индикаторы (фильтры), реализованные в код. Речь идет про формализацию стратегии в определенные правила, ну и конечно реализация правил в код ( луа версии 5.4).
1) Луа. Сейчас поясню, по чему луа, на мой взгляд это супер! На нем можно и так программировать логику детской задачи "А И Б сидели на трубе, а пропало б упало, кто остался на трубе?" Напомню, что в Lua основная структура данных – таблица. Тогда задачка в этом варианте, выглядит несколько иначе, "таблица А, таблица И, таблица Б, сидели на трубе, таблица А пропала == nil, таблица Б упало == (функция роста), вычисляем кто остался на трубе?" Как минимум это вектор, в идеале матрица состояний! И это просто возможность ухода от плоскостного мышления!
2) Чтоб "не изобретать очередной велосипед", налил кружку чая и с мыслями что не успею допить, как соберу стратегию, для этого взял из библиотеки от разработчиков индикаторы (реализованные в луа). Каково ж было мое удивление, когда получил ошибку: на обработку операций с nii? (nii - это еще одна особенность языка - ничего, в начале было ни чего!) Не все ошибаемся, но ведь, библиотека обновлялась и не один раз. Не берусь судить какая у меня сохранена но точно не ниже версии второй. Позвольте, но разве не специалисты от программирования пишут эту библиотеку? Не так давно вступал в дискуссию, что так поступать нельзя, и сам попал. Миллионы пользуются невдоваясь в подробности.
3) Заглянул поглубже, (все таки кружка чая, покоя не давала), - "волосы дыбом, мозги на бикрень", уж не знаю, кто там может разобраться точно только "специалисты от программирования".
Чего стоит одно "ANY" вот от сюда:
Код
local function GetValueEX(I, VT, ds)
VT = (VT and string.upper(string.sub(VT, 1, 1))) or ANY
...
end
Видимо, это аналог записи: local p0 = x or Value(I, v_t, ds) or 0;
Но с меня уже и этого хватит, лично я пойду "причешусь и за чайником", благо что все алгоритмы опубликованы.
Пару слов про подход Ральфа Винса! Когда упоминают имя Винса, обычно упоминается концепт оптимальной фракции (optF), я же хочу заострить внимание для тех кто незнаком с работами, на более важном, откуда собственно следует все остальное. Фундаментальное уравнение торговли — это инструмент для анализа, и поиска компромисса между доходностью и риском. Оно подчеркивает, что долгосрочный успех зависит не только: от положительного матожидания торговой системы (A > 1, если вычесть 1 и перевести в %, то и получаем то самое классическое матожидания), от управления волатильностью (S); но и от периода удержания позиции. Фундаментальное уравнение торговли, связывает среднее арифметическое доходности (A), стандартное отклонение (S) и количество сделок (N) для оценки итогового роста капитала (TWR — Terminal Wealth Relative). Получено с помощью логики и аппроксимации функции роста. Такая оценка очень точно аппроксимирует мультипликативную функцию роста или настоящее TWR!
Формула имеет вид: TWR = ( A^2 - S^2 )^N/2
где: A — среднее арифметическое HPR (Holding Period Return, доходность за период); S — стандартное отклонение HPR; N — количество сделок или периодов.
Из уравнения четко видно, что снижение стандартного отклонения (волатильности) улучшает TWR. Например, почему полезно использование стоп - лоссов для ограничения убытков, снижает S, но слишком жесткие стопы могут уменьшить A сильнее, чем S, что ухудшит результат. Можем рассчитывать другие производные от уравнения и параметры сделок, например период удержания позиции, количество контрактов в сделке, optF капитала, границы такой торговли и так далее.
Хорошо использовать это уравнение для оценки стратегий, оптимизации стоп-лоссов, выбора инструментов (например, опционов), причем как в момент проектирования сделок, так и по факту торговли, для оценки результатов (Эффективности торговли). Позволяет однозначно сравнивать разные торговые стратегии, не "наобум Лазаря" а используя царицу наук.
Добавив в свой инвестиционный портфель облигации, с удивлением для себя обнаружил, что теряю контроль над ситуацией и тому виной те самые облигации. На вопрос что приводит к дискомфорту, ответ очевиден, нужен алгоритм оценки эффективности использования этих самых облигаций.
Нет если держать до погашения, то и ладно. Но ведь интересует сию минутная оценка, возможно средства выделенные используются не эффективно или цена этой облигации на экстремальных оценках и нужна ребалансировка?
"Нет худа, без добра!" эта бинарная конструкция (монада) или супер позиция из квантовой механики, подвинула меня разбираться с вопросом, как улучшить управляемость?
Заглянул в свои архивы. Находка первая, торговая стратегия с использованием индикаторов, опубликованных Уэллсом Уайлдером в книге (Wilder, J. Welles (1978). New Concepts in Technical Trading Systems.), ADX, RSI и SAR — это отличная идея для построения комплексного подхода к торговле. Все три индикатора помогают в создании системы принятия торговых решений, в разных аспектах анализа рынка, их нужно просто уметь комбинировать, чтобы повысить вероятность успешных сделок. Основной не достаток, это методы их расчета (математика), которая приводит к лагам (отставаниям), давно собирался по экспериментировать с этой математикой уменьшить лаг.
Вторая находка, ну а где еще искать способы повышения эффективности - это конечно работы Ральфа Винса! УПРАВЛЕНИЕ КАПИТАЛОМ ПО ВИНСУ - это отдельная особая тема, чего стоит только его основное уравнение торговли TWR = (A^2 + StD^2)^(T/2). Ранее уже реализовывал несколько модулей по его работам, концепт HPR, расчет количества на сделку и другие показатели сделок. Настало время заняться эффективностью.
Не однозначное решение вызвало интеграция State Machine с действующим рабочим кодом. На первый взгляд, задача встраивания StateMachine, для контроля состояний скрипта, в место одного флага, выглядит не совсем логично и однозначно? В место одного флага с двумя степенями свободы, добавляется в систему несколько дополнительных модулей и вспомогательных функций, не очевидных зависимостей? Что казалось бы не добавляет надежности системе?
Пришлось разбираться по порядку, устраняя сомнения, отвечая на вопрос "На фига козе баян?" Ну обо всем и по порядку:
1. Почему State Machine вместо одного флага - это полезно? State Machine не заменяет флаг, а решает принципиально другие задачи. а) Четкие переходы, запрет недопустимых сценариев (вроде перехода ERROR → RUNNING) б) Централизованная логика, вся обработка состояний в одном месте, а не размазана по коду.
2. Какие реальные преимущества?
Код
-- Было:
function OnStop()
WORKING_FLAG = false -- Резкий обрыв без очистки
end
-- Стало:
function handleStoppedState()
cancelAllOrders() -- Гарантированная отмена
closePositions() -- Корректное закрытие
releaseResources() -- Контроль утечек
Log:info("Система остановлена безопасно")
end
3. Почему дополнительные модули, это не произвольное усложнение, а разделение ответственности?
Модуль Ответственность
StateMachine Логика переходов Обработка IDLE → RUNNING ErrorCollector Централизованный сбор ошибок Логирование в OnStop() GUI Визуализация состояния Обновление таблиц
4. Проблема отладки. Конкретные ошибки из лога, решаются, добавляем заглушки с контролем и последующим разбором.
5. Как избежать "неочевидных зависимостей"? Ответ - явная инициализация в OnInit и ее проверка.
Код
function OnInit()
-- Порядок инициализации важен!
StateMachine = require("StateMachine") -- 1. Состояния
ErrorCollector = require("ErrorCollector") -- 2. Ошибки
GUI = require("GUI") -- 3. Интерфейс
-- Проверка инициализации
assert(StateMachine, "StateMachine не загружен")
assert(ErrorCollector, "ErrorCollector не загружен")
-- Конфигурация
StateMachine:configure({
transitions = cfg.transitions,
initialState = "IDLE"
})
end
6. Работоспособность кода? Выполняется с помощью ключевых проверок.
Код
-- Тест переходов между состояниями
local function testStateTransitions()
local sm = StateMachine
sm:transitionTo("RUNNING")
assert(sm:isState("RUNNING"), "Ошибка перехода в RUNNING")
sm:transitionTo("ERROR")
assert(sm:isState("ERROR"), "Ошибка перехода в ERROR")
sm:transitionTo("IDLE")
assert(not sm:isState("IDLE"), "Недопустимый переход ERROR → IDLE должен быть запрещен")
end
7. Альтернативный подход? Возможен минималистичный вариант State Machine без лишних модулей? Да.
Вот и получается, State Machine — это не усложнение ради усложнения, а 1) Защита от недопустимых сценариев; 2) Четкий контроль жизненного цикла; 3) Упрощение добавления новых состояний; 4) Централизованное логирование; ... Аллегория здесь возможно такая. Это инвестиции в будущее, в отличии от спекулятивного подхода (с флагом) Сам процесс, интеграции State Machine в код тоже имеет как минимум два решения, 1) в главном цикле; 2) асинхронный вариант. Но это уже другая тема.
В заключении отмечу, интеграция State Machine действительно добавляет некоторую сложность, но даёт: А. Снижение количества ошибок на 40-60% (не моя оценка); Б. Упрощение отладки в 2-3 раза (не моя оценка); С. Возможность формальной верификации сценариев.
Закнчивая тему рефакторинга кода, стоит добавить что на практике он уменя свелся к созданию дополнительных модулей, отвечающих за надежность уже самой системы. Вот основные из них. 1) Мониторинг производительности (QSA-PERF) модуль PerformanceMonitor; 2) Автомат защиты (QSA-CIRCUIT), модуль CircuitBreaker. Некоторому переосмысливанию логики модулей управления, например Модуль управления сопрограммами CoroutineManager, добавлены приоритеты. Осознание, что большинство критических ошибок находится на стыке взаимодействия скрипта с квиком, Привело к добавлению самостоятельного модуля Контроллер (QSA-GUI) GUIController, QSA-CORO)
От этого сложность программы не уменьшилась, а значить и надежность не очень повысилась, но есть осознание куда двигаться в целях повышения надежности и тесто пригодности. Так как модуль зачастую исполняется в собственной сопрограмме, то и задачу можно свести к тесто пригодности этого модуля. В общем, Рефакторинг это не состояние, это процесс!
Прогнозная зона графика, График цены или индикатора по оси времени можно сдвигать образуя прогнозную зону. Но как с ней работать с самописными индикаторами?
Добрый день! Разработчики предусмотрели возможность сдвигать графики по оси времени, тем самым можно создавать прогнозную зону или накладывать прошлые значения на настоящее время. На как с этими сдвигами работать? Какие индексы должен получать скрипт на луа чтоб корректно отображались расчетные значения при таких сдвигах? Как отрисовывать график при сдвиге? В общем думается проблема с индексами?
Почему важно учитывать эти стандарты при создании своего проекта? Очевидное.
1) Так как, взаимодействие проекта будет происходит с промышленной торговой системой, то не плохо чтоб и проект соответствовал таким же требованиям. 2) Задача проекта несколько упрощается, так как среда его использования (QUIK), уже должна учитывать и отвечать этим требованиям, нам лишь нужно учитывать особенности среды QUIK и языка написания Lua. 3) Это позволяет выйти на единую архитектуру структуры проекта, работать с несколькими модулями, каждый из которых будет выполнять свою функцию. Подобная структура проекта, будет ориентирована на создание модульного и масштабируемого решения, которое легко будет адаптировать и поддерживать. В данном случае, мы будем учитывать требования для надежности, безопасности, масштабируемости и соответствия нормативам, а также особенности среды QUIK. Будем опираться на принципы модульности, отказоустойчивости и масштабируемости. Архитектура должна включать компоненты для обеспечения отказоустойчивости и мониторинга, а также соблюдать строгие стандарты отчетности и аудита.
Базовая архитектура может содержать несколько ключевых модулей для такой системы. Минимальная структура должна включать: • Модуль для отправки ордеров • Модуль для получения котировок • Модуль для работы с портфелем • Модуль для анализа рисков • Модуль для генерации отчетности (согласованной с MiFID II) • Модуль мониторинга и аудита Эта структура предоставляет основу для создания торговой системы, обеспечивающей отказоустойчивость, безопасность и соответствие промышленным нормативам.
А главный цикл такой программы, для примера, может выглядеть так.
Код
-- Core/Main.lua
function main()
ConfigManager.load("config.prod.json")
Logger.init(ConfigManager.get("logging"))
local ok, err = pcall(function()
StateManager:restore()
RiskManager:init()
while StateManager:is_active() do
local signals = SignalGenerator.generate()
local filtered = RiskManager.filter(signals)
OrderExecutor.execute_all(filtered)
StateManager:update_metrics()
ReportManager.send_daily()
sleep(100)
end
end)
if not ok then
Logger:error("Critical failure", {error = err})
EmergencyShutdown.execute()
end
end
Добиваясь стабильной работы, от варианта торговой программы в асинхронном исполнении, решил посмотреть, как к этому вопросу подходят при создании профессиональных программ. Основная проблема тестирование и отладка такой асинхронной программы, нужен реал - тайм, бэк - тесты здесь не проходят, а это очень медленно (пока отлаживаешь забываешь, уже зачем все делал). По этому, важно изначально отсекать, излишние проблемы на стадии проектирования. Переработал архитектуру своей такой программы. Для этого окунулся в промышленные стандарты, что должна уметь, что должна делать, и так далее такая программа. Собственно с этого и нужно было начинать, при создании проекта, приятно был удивлен, достаточно хорошей проработке этих стандартов (и это то в финансовой сфере та ). В общем кому интересно, кто задумается над подобными вопросами, вот основные, на что стоит обратить внимание, по моему скромному мнению.
Промышленная архитектура торговой программы должна соответствовать требованиям: 1. ISO 27001 для финансовых систем; 2. MiFID II для торговых платформ; 3. SEC Regulation SCI для электронных торгов.
Блок схему своей архитектуры мне страшно даже показать, это работа для целой команды, да и реализовано еще только ядро, к которому прикручиваю отдельные модули, да и все еще не один раз поменяется. Хороших алгоритмов.
А если, реализовать "Конечный автомат состояний", например доработав функцию Ziveleos, чтоб не доводить до ошибки? 1. Чёткое разделение на состояния: Активен/Приостановлен/Авария ... 2. Автоматические действия при смене состояний (например, отмена ордеров в аварийном режиме или вашей ситуации)?
На самом деле проблема стара как этот мир (в нашем случае квик), сейчас продемонстрирую на небольшом примере. 1) Вариант просто создаю таблицу с методами. 2) "Танцы с бубнами" - Моя древняя функция боюсь ее уже даже руками трогать.
Я тоже не программист! Ваш код это дело начинающих, чтоб разобраться ка все работает. Но давайте обо всем и по порядку. 1. OnCalculate это функция обратного вызова или на ангельском (Колбэк), Не в даваясь в подробности реагирует на каждый "чих" изменения. Если Вы строите свой алгоритм от момента появления новой свечи, то текущею свечу можем получить с помощью Функции Size()! Что по факту index == Size(). В расчетах индикатора используем историю по данным, то есть index - 1, -2, -3 и так далее. То что с минусом свеча сформированная - закрытая, а текущая нет в процессе, все может поменяться (Например изменился Максимум), на этот и реагирует колбек. 2. Писать лучше в луа алгоритм в анонимной функции. Собственно в луа функция и есть замыкание! Примеров масса, на этом форуме. Я Вам могу порекомендовать библиотеку от разработчиков. Да она не идеальна но на путь истины наставляет. Удачи!
TGB написал: Давайте, подискутируем. Может быть я заблуждаюсь больше чем две недели?
TGB, Похоже остались тут вдвоем, не я бы Вам ответил, правдо зная про что? Не понимаю и Вам зачем, наверно нужно или скучно стало? Не ну ладно, я "несу всякую ахинею", разбирая свои идеи по винтикам и полочкам на примерах. А где взять базу случайных чисел? Но Вы то ...? А администраторы? Взяли заблокировали Владимира, и как нам теперь узнать как поживает его "супер- пупер" черная коробочка, думаю подался уже в "форбс" и нам не "чита"? Не я то еще тут побуду, времени на реализацию идей добавилось, даже "Бредовых". Лично от себя, не знаю как сейчас, раньше была поговорка "Будь проще народ подтянется! И Вам удачи в начинаниях!
Во дела! Снижение на американском рынке оценивают в -17%, насколько помню -20% критично для фондов. плотно зашли в медвежий тренд, и стоим на пороге ликвидации позиций крупными участниками? "За что купил, за то продал".
Karina Dmitrieva, Спасибо, с этим я разобрался, меня поразило другое, почему все отдано на откуп брокеру? Почему нет единого алгоритма расчета зашитого в программе? Или у каждого брокера своя арифметика? Перевести из процентов в валюту и затем сконвертировать? Три брокера и каждый по своему, но больше всего Сбер поразил, даже не понимают о чем говорят! Чудеса да и только!
Вот и нашлась причина укрепления рубля, кто бы мог подумать? К импортерам и годовым налогам добавились.
"Иностранные хедж-фонды вкладывают в Россию «ковбойские деньги» через дружественные юрисдикции, что заметно по динамике ОФЗ и курса рубля. Об этом заявил журналистам замминистра финансов Иван Чебесков"