Anton Belonogov, Спасибо, за ссылку. Так я и утверждаю что "Тормоза в багажнике". Ну хорошо, не хотите вести перечень имен таблицы текущих параметров, хотя бы ссылку на инструкцию их получения, закрепите на форуме в явном виде (Часто задаваемые вопросы). Интересно сколько раз пользователи читают инструкцию по использованию телевизора?
Anton Belonogov, Все дело в том, что есть права и есть обязанности! Не должен конечный пользователь, добывать информацию "под пытками". В чем сложность публиковать параметры транслируемые Квиком?
Нет, точного соответствия между наименованиями параметров на торговых площадках и в QUIK нет.
Получается QUIK не проста транслирует данные с биржи, но изменяет имена параметров на собственные! При этом их не публикует, для конечного пользователя? О, Чудеса - чудесные! Это в финансовом мире принято что - то не договаривать, в этой ситуации разработчики выступают инженерами и технологами. Здесь аналогия такая: "посадили за руль автомобиля, с педалью тормоза в багажнике"?
--[[STATUS STRING Статус
TRADINGSTATUS STRING Состояние сессии
TRADINGPHASE STRING Биржевой статус торговой сессии
LOTSIZE NUMERIC Размер лота
BID NUMERIC Лучшая цена спроса
BIDDEPTH NUMERIC Спрос по лучшей цене
BIDDEPTHT NUMERIC Суммарный спрос
NUMBIDS NUMERIC Количество заявок на покупку
OFFER NUMERIC Лучшая цена предложения
OFFERDEPTH NUMERIC Предложение по лучшей цене
OFFERDEPTHT NUMERIC Суммарное предложение
NUMOFFERS NUMERIC Количество заявок на продажу
NUMTRADES NUMERIC Количество сделок за сегодня
NUMCONTRACTS NUMERIC Количество открытых позиций
OPEN NUMERIC Цена открытия
HIGH NUMERIC Максимальная цена сделки
LOW NUMERIC Минимальная цена сделки
LAST NUMERIC Цена последней сделки
CHANGE NUMERIC Разница цены последней к предыдущей сессии
QTY NUMERIC Количество бумаг в последней сделке
TIME STRING Время последней сделки
VOLTODAY NUMERIC Количество бумаг в обезличенных сделках
VALTODAY NUMERIC Оборот в деньгах
VALUE NUMERIC Оборот в деньгах последней сделки
WAPRICE NUMERIC Средневзвешенная цена
HIGHBID NUMERIC Лучшая цена спроса сегодня
LOWOFFER NUMERIC Лучшая цена предложения сегодня
PREVPRICE NUMERIC Цена закрытия
PREVWAPRICE NUMERIC Предыдущая оценка
CLOSEPRICE NUMERIC Цена периода закрытия
LASTCHANGE NUMERIC % изменения от закрытия
PRIMARYDIST STRING Размещение
ACCRUEDINT NUMERIC Накопленный купонный доход
YIELD NUMERIC Доходность последней сделки
COUPONVALUE NUMERIC Размер купона
YIELDATPREVWAPRICE NUMERIC Доходность по предыдущей оценке
YIELDATWAPRICE NUMERIC Доходность по оценке
PRICEMINUSPREVWAPRICE NUMERIC Разница цены последней к предыдущей оценке
CLOSEYIELD NUMERIC Доходность закрытия
CURRENTVALUE NUMERIC Текущее значение индексов Московской Биржи
LASTVALUE NUMERIC Значение индексов Московской Биржи на закрытие предыдущего дня
LASTTOPREVSTLPRC NUMERIC Разница цены последней к предыдущей сессии
PREVSETTLEPRICE NUMERIC Предыдущая расчетная цена
PRICEMVTLIMIT NUMERIC Лимит изменения цены
PRICEMVTLIMITT1 NUMERIC Лимит изменения цены T1
MAXOUTVOLUME NUMERIC Лимит объема активных заявок (в контрактах)
PRICEMAX NUMERIC Максимально возможная цена
PRICEMIN NUMERIC Минимально возможная цена
NEGVALTODAY NUMERIC Оборот внесистемных в деньгах
NEGNUMTRADES NUMERIC Количество внесистемных сделок за сегодня
CLOSETIME STRING Время закрытия предыдущих торгов (для индексов РТС)
OPENVAL NUMERIC Значение индекса РТС на момент открытия торгов
CHNGOPEN NUMERIC Изменение текущего индекса РТС по сравнению со значением открытия
CHNGCLOSE NUMERIC Изменение текущего индекса РТС по сравнению со значением закрытия
BUYDEPO NUMERIC Гарантийное обеспечение продавца
SELLDEPO NUMERIC Гарантийное обеспечение покупателя
CHANGETIME STRING Время последнего изменения
SELLPROFIT NUMERIC Доходность продажи
BUYPROFIT NUMERIC Доходность покупки
TRADECHANGE NUMERIC Разница цены последней к предыдущей сделки (FORTS, ФБ СПБ, СПВБ)
FACEVALUE NUMERIC Номинал (для бумаг СПВБ)
MARKETPRICE NUMERIC Рыночная цена вчера
MARKETPRICETODAY NUMERIC Рыночная цена
NEXTCOUPON NUMERIC Дата выплаты купона
BUYBACKPRICE NUMERIC Цена оферты
BUYBACKDATE NUMERIC Дата оферты
ISSUESIZE NUMERIC Объем обращения
PREVDATE NUMERIC Дата предыдущего торгового дня
DURATION NUMERIC Дюрация
LOPENPRICE NUMERIC Официальная цена открытия
LCURRENTPRICE NUMERIC Официальная текущая цена
LCLOSEPRICE NUMERIC Официальная цена закрытия
QUOTEBASIS STRING Тип цены
PREVADMITTEDQUOT NUMERIC Признаваемая котировка предыдущего дня
LASTBID NUMERIC Лучшая спрос на момент завершения периода торгов
LASTOFFER NUMERIC Лучшее предложение на момент завершения торгов
PREVLEGALCLOSEPR NUMERIC Цена закрытия предыдущего дня
COUPONPERIOD NUMERIC Длительность купона
MARKETPRICE2 NUMERIC Рыночная цена 2
ADMITTEDQUOTE NUMERIC Признаваемая котировка
BGOP NUMERIC БГО по покрытым позициям
BGONP NUMERIC БГО по непокрытым позициям
STRIKE NUMERIC Цена страйк
STEPPRICET NUMERIC Стоимость шага цены
STEPPRICE NUMERIC Стоимость шага цены (для новых контрактов FORTS и RTS Standard)
SETTLEPRICE NUMERIC Расчетная цена
OPTIONTYPE STRING Тип опциона
OPTIONBASE STRING Базовый актив
VOLATILITY NUMERIC Волатильность опциона
THEORPRICE NUMERIC Теоретическая цена
PERCENTRATE NUMERIC Агрегированная ставка
ISPERCENT STRING Тип цены фьючерса
CLSTATE STRING Статус клиринга
CLPRICE NUMERIC Котировка последнего клиринга
STARTTIME STRING Начало основной сессии
ENDTIME STRING Окончание основной сессии
EVNSTARTTIME STRING Начало вечерней сессии
EVNENDTIME STRING Окончание вечерней сессии
MONSTARTTIME STRING Начало утренней сессии
MONENDTIME STRING Окончание утренней сессии
CURSTEPPRICE STRING Валюта шага цены
REALVMPRICE NUMERIC Текущая рыночная котировка
MARG STRING Маржируемый
EXPDATE NUMERIC Дата исполнения инструмента
CROSSRATE NUMERIC Курс
BASEPRICE NUMERIC Базовый курс
HIGHVAL NUMERIC Максимальное значение (RTSIND)
LOWVAL NUMERIC Минимальное значение (RTSIND)
ICHANGE NUMERIC Изменение (RTSIND)
IOPEN NUMERIC Значение на момент открытия (RTSIND)
PCHANGE NUMERIC Процент изменения (RTSIND)
OPENPERIODPRICE NUMERIC Цена предторгового периода
MIN_CURR_LAST NUMERIC Минимальная текущая цена
SETTLECODE STRING Код расчетов по умолчанию
STEPPRICECL DOUBLE Стоимость шага цены для клиринга
STEPPRICEPRCL DOUBLE Стоимость шага цены для промклиринга
MIN_CURR_LAST_TI STRING Время изменения минимальной текущей цены
PREVLOTSIZE DOUBLE Предыдущее значение размера лота
LOTSIZECHANGEDAT DOUBLE Дата последнего изменения размера лота
CLOSING_AUCTION_PRICE NUMERIC Цена послеторгового аукциона
CLOSING_AUCTION_VOLUME NUMERIC Количество в сделках послеторгового аукциона
LONGNAME STRING Полное название бумаги
SHORTNAME STRING Краткое название бумаги
CODE STRING Код бумаги
CLASSNAME STRING Название класса
CLASS_CODE STRING Код класса
TRADE_DATE_CODE DOUBLE Дата торгов
MAT_DATE DOUBLE Дата погашения
DAYS_TO_MAT_DATE DOUBLE Число дней до погашения
SEC_FACE_VALUE DOUBLE Номинал бумаги
SEC_FACE_UNIT STRING Валюта номинала
SEC_SCALE DOUBLE Точность цены
SEC_PRICE_STEP DOUBLE Минимальный шаг цены
SECTYPE STRING Тип инструмента--]]
Из собственного опыта, скачал у брокера еще одну версию, поставил ее параллельно не работающей, моментально все загрузилось! Вывод что произошло с сохранением данных в предыдущей версии. Давненько не было таких проблем, все работало стабильно, уже и забыл, как с этим справляться. Всем пока, хорошей торговли.
Anton Belonogov, Да соединение проходит, пишет "чтение информации о расположении окон" и на этом зависает. Из диспетчера видно , загружает гигабайты, Вчера все норм было.
"Ни когда не было и вот опять!". Все утро пытаюсь загрузить квик версия 11.4.1.3. Вернее не так сам квик грузится, но при входе по логину, часами зависает на "чтении информации об окнах", качает себе гигабайты и крутит колесико? Увлекательное зрелище! Проверил скорость интернета, сбросил лог., сбросил квик несколько раз для загрузки предыдущих wnd, не помогло. Позвонил брокеру, "Сервера работают нормально, пере закажите данные". НЕ помогло?
На моей памяти так, параметры устанавливает биржа, постоянно изменяются, разработчики предлагали выгружать таблицу всех параметров. По логике документацию должна биржа публиковать?
Сергей ВАТ, Вам нужно подтянуть материальную часть. Это далеко не алгоритм, алгоритм - это прежде всего логика и последовательность действий, что совсем отсутствует в Вашем коде. Ниже выкладываю демонстрационный пример, как код должен выглядеть. Это не торговый, это демонстрация возможностей!
Код
--stopped = false -- Этот флаг здесь не нужен
function OnStop()
--stopped = true
return 5000
end
-- Установки
local Kod_klienta = "10800"; -- нужно поставить свое значение
local Torg_schet = "NL0011100043"; -- нужно поставить свое значение
local CLASS_CODE = "QJSIM" -- Код класса
local SEC_CODE = "SPBE" -- Код бумаги
local Kod_FirM = "NC0011100000" -- Код фирмы
local step_grid = 5
local run = true -- флаг работы скипта
function main()
-- один раз получает шаг цены (так как величина постоянная)
local step_price = getParamEx(CLASS_CODE, SEC_CODE, "SEC_PRICE_STEP").param_value
while run do
--в цикле каждые милисекунды (sleep(10000)) обновляем цену перед расчетом нового уровня
local Sena = getParamEx(CLASS_CODE, SEC_CODE, "LAST").param_value
-- Расчет 10 уровней с шагом сетки step_grid = 5
for i = 1, 10 do
-- расчет сдвига цены (сетка)
Sena = Sena + i * step_price * step_grid
local podgot = {
ACCOUNT = Torg_schet,
CLIENT_CODE = Kod_klienta,
TYPE = "L",
TRANS_ID = "1",
CLASSCODE = CLASS_CODE,
SECCODE = SEC_CODE,
ACTION = "NEW_ORDER",
OPERATION = "B",
PRICE = tostring(Sena),
QUANTITY = "1"
}
--Отпровляется транзакция
--if -- Условия для выхода из расчета
sendTransaction(podgot)
--then break; end;
end
sleep(10000)
end
end
__spb13__, С кодом у Вас, все в порядке. Перед основным циклом, Вы задаете инструмент по которому хотите работать, он загружается один раз перед выполнением цикла, а дальше вся работа идет в цикле. sec_code = 'SU26219RMFS4' -- infinite loop to compensate for problems with getting the price while true do
Цитата
__spb13__ написал: Но только если в окне "Текущая таблица параметров" выбрана вот та самая бумага - 'SU26219RMFS4'. Это ОФЗ.Выбираешь в окне другую и нужно менять код в скрипте.
У вас окна квика привязаны к таблице текущих параметров, для того чтобы отключить эту функцию, нужно сбросить якоря(в верхнем углу стакана горит цветная кнопка, встаете на нее л.кнопка мыши "отключить"). Чтоб работал Этот код должен быть открыт стакан по данному инструменту. quoteLevel = getQuoteLevel2('TQOB', sec_code). в вашем случае, sec_code = 'SU26219RMFS4'. Если хотите добавить работу с портфелем бумаг вместо sec_code = 'SU26219RMFS4', создайте таблицу sec_codes = {'SU26219RMFS4', ...,} и в цикле перебирайте эту таблицу.
Код
local sec_codes = {'SU26219RMFS4', ...}
-- infinite loop to compensate for problems with getting the price
while true do
for _, sec_code in ipairs(sec_codes) do
quoteLevel = getQuoteLevel2('TQOB', sec_code)
if quoteLevel.offer then
pc = tonumber(quoteLevel.offer[1].price)
end
if pc == 0 or pc < 0 or pc == nil then
co.toLog(sn, ' still nothing...')
sleep(math.random(1000, 2000))
else
break
end
end
end
__spb13__, С перового прочтения сформулированный Вами вопрос, сильно смахивает на каламбур, рассмешил, извините еще раз. Касаемо вопроса. 1) Квик действительно нужно настроить под себя, в открытом окне развернуть необходимые для работы таблицы. Зайти в настройки и установить умный заказ. Более подробно, на сайте есть от разработчиков, по моему первые посты. 2) цену в скрипте можно получать разными способами: с подпиской, с графика, из таблицы текущих параметров, стакана и так далее. 3) То что Вы описываете, поведение не совсем характерное, для понимания проблемы, лучше показать код, где получаете цену и как пытаетесь вывести ее в ордере.
Megafan, Спасибо за уточнение. Не проверял, микро добавил по аналогии для демонстрации возможности, свою метку ограничиваю мили, что вполне хватает для моей задачи оценки актуальности. nikolz, то что данные приходят с некоторым лагом в программу, это и так понятно, это другая задача, моя же задача проверки актуальности данных, сводится к уже полученным и на копленным данным. И причина в необходимости проверки заключается, в асинхронности обработки двух разных быстрых таблиц в различных корутинах. Ведь дальше следует вывод и принятие решения. То есть, проверить данные полученные из стакана на соответствие данным полученным из т. всех сделок!
Про актуальность данных. nikolz, поднял важную тему. Пришлось призадуматься, ведь если при последовательной обработке таблиц не очень и нужны временные метки. В варианте с обработкой таблиц в разных сопрограмма уже не обойтись, без них. Есть вариант стакан и таблицу всех сделок обрабатывать последовательно в одной сопрограмме, но таблицы очень быстрые, да и легких путей не ищу.
Пересмотрев внимательней статью выложенную Nikolay, обратил внимание, на запись данных книги ордеров, кроме стандартного времени, есть запись временной метки!
В среде QUIK доступна функция sysdate(), которая возвращает системную дату и время с точностью до микросекунд, это отличная возможность использовать её для работы с временными метками! Эта функция предоставляет всю необходимую информацию для точной индексации событий и записи меток времени с высокой точностью, что важно для высокоскоростных операций, коими и являются эти две таблицы.
Код
-- Получаем дату и время
local sysdate = os.sysdate()
-- Создаем строку временной метки с точностью до микросекунд
local timestamp = string.format("%04d-%02d-%02d %02d:%02d:%02d.%03d.%03d", sysdate.year, sysdate.month, sysdate.day, sysdate.hour, sysdate.min, sysdate.sec, sysdate.ms, sysdate.mcs)
-- Выводим временную метку
print("Timestamp with microsecond precision:", timestamp)
Пример временной метки: 2025-02-11 14:12:52.123.456
Здесь:
2025-02-11 — это дата (год, месяц, день).
14:12:52 — это время (часы, минуты, секунды).
.123 — это миллисекунды.
.456 — это микросекунды.
Для чего все это? Как вызываются сопрограммы, пока им одним ведома? В моем варианте данные собираю в очереди, пришлось и их переделать, в одну универсальную, чтоб не привязываться конкретно к таблицам, добавил метаданные, ну конечно и временной штамп.
1. Это позволяет точно отслеживать события и их порядок, что критично для приложений с высокими требованиями к скорости и точности.
2. Все данные о времени в одном формате, что удобно для сортировки, фильтрации и анализа.
3. Функция использует системные вызовы, что гарантирует реальное время, а не время процессора.
Если бы мне сказали, когда я начинал собирать свою программу, что она достигнет такой сложности, я бы просто не поверил. А мне за букварь надо со всем этим разбираться!
Да, спасибо, в переводном варианте есть ссылка на оригинал, перевел для себя, но все равно до конца все не понял. Кумулятивный спрос и предложения вывожу на график, а в алгоритме тоже нормирую, для понимания настроений. Мой же подход, краткосрочно забрать 10 пунктов, для этого, на какой стороне заходить и предлагаю алгоритм, оценки этой стороны. А логика простая, чтоб набрать крупную позицию, сегодня вынуждены двигать цены в стакане в сторону набора, в погоне за ликвидностью. Вернутся деньги в рынок, наверняка все будет по другому. Ну и конечно одного этого недостаточно.
Nikolay, За информацию спасибо! Согласен с Вами, меня собственно подтолкнул, отчет моего брокера, который оценил участников на рынке, 70% частники, то есть крупному игроку негде разгуляться, отсюда следует что должны приспосабливаться и приводит свои алгоритмы под текущую ситуацию. Обратил внимание на стакан, MM в обеспечение своих обязательств, приходится чаще набирать позиции на слабой стороне, таковы реалии сегодня. Второе, опробовал технологию асинхронности обработки таблиц (затолкал данные от колбэков в сопрограммы, с небольшими очередями), что позволяет выполнять описанный выше алгоритмы. Ну и последнее, задача стоит для общей торговой системы, сделать разные модули торговых стратегий, с авто переключением между ними, такие амбиции, пока не понятно что из этого выйдет? Но одновременная работа трендовой и реального времени вполне приемлема, по крайней мере вижу куда двигаться. Но и самое главное надеюсь что подход избавит от постоянной переделки системы, с использованием стратегий в модулях.
nikolz, Собственно этот алгоритм продолжение этой темы https://forum.quik.ru/messages/forum10/message77319/topic8859/#message77319, корректирую архитектуру торговой системы и привожу структуру данных. Этот алгоритм один из модулей торговой стратегии стратегия "скальперской", задачей которого является оценка ликвидности в стакане, ответ на вопрос на чьей стороне. Формализация алгоритма необходима для применения в алгоритмической торговле. Что делает алгоритм, еще раз, нормируем, переводит в диапазон -1/+1, Фишер нужен для выделения хвостов (в диапазоне -0.5/+0.5 передается практически линейно, значения выше с лог. зависимостью, не создает задержек). Статистика в водится, если можно так сказать, для оценки результатов и их стабилизации. Периоды накопления данных для такой оценки регулирую в зависимости от инструмента и необходимости. Ни какого прогноза, оцениваем что происходит, конечно с неким лагом. Метрики применяю обычные, тенденция, импульс ... Для чего для поддержки принятия решений в краткосрочных стратегиях. На счет актуальности, ну конечно, если даже данные пришли за вчера! На счет применения каждый решает сам, подход я озвучил, привел пример с нечеткой логикой, для сглаживания переходных процессов и задания зон принадлежности. Любой может взять и попробовать.
nikolz, Да Вы совершено правы, я лишь хотел подчеркнуть особенность подключения
Цитата
VPM написал: Функция dofile выполнит Lua-скрипт, и все переменные и функции, определённые в нём, будут доступны в текущем окружении. И смысл здесь в подключении новой задачи!
Ведь есть еще способ local Utility = require("Utility").
nikolz, Не совсем понял Ваш вопрос? Применяя Вашу аллегорию, можно пояснить смысл. Если выпитый стакан вчера был с алкоголем, сев сегодня за руль, и подышав в трубочку представителю закона, результат выявлен. Так и в нашем случае, данные какие получили такие и есть. А задача подхода оценить кто заправляет в стакане, деля резултат алгоритма на 3 категории покупатель, продавец или нейтрально. Могу добавить что этот модуль, только часть большей задачи. Стакан это настроения, выставляя лимитки мы подменяем ММ и говорим о намерениях, рынок двигают рыночными ордерами это и оцениваем. В большой задаче, примерно также оцениваю Спрос/Предложение, и Настроение на рынке. Сейчас задача объединить анализ настроений с анализом сделок, в сделках уже есть время сделки.
В стратегии реального времени, для автоматической торговли, в системы принятия торговых решений внес изменения, добавил использование данных о ликвидности в стакане. Давно "руки чесались", алгоритм не сложный, но мест где наделать ошибки достаточно. Вкратце следующий, получаем данные о ликвидности из стакана, проводится нормировка для преобразования к нормальному распределению, выделяем пиковые значения с помощью преобразования Фишера, создаем правила на основе нечеткой логики . Думаю еще прикрутить правило 3 сигм для стабилизации результатов, но пока без сигм покручу нужно добиться стабильности. Пока хорошей торговли!
nikolz написал: И еще...Нет смысла запускать скрипты с помощью dofile (особенно как в приведенном VPM примере)Так как это лишь замедляет исполнение.---------------dofile имеет смысл применять для разделения большого скрипта на блоки, чтобы упростить чтение и отладку скрипта.
nikolz, Пример выше это просто демонстрация возможностей, ни на что не претендующая. К примеру у себя использую следующий вариант (кусочек из рабочего код):
Код
-- Пытаемся загрузить библиотеку
local fuzzy;
local success, err = pcall(dofile, path..'\\luafuzzy.lua')
if not success then
Log:error("Ошибка при загрузке файла luafuzzy: " .. err)
else
-- Если библиотека успешно загружена, используем её
local fuzzy = luafuzzy()
Log:info("Библиотека luafuzzy успешно загружена!")
end
while WORKING_FLAG do
Перед основным циклом while WORKING_FLAG do 1 раз вызываем "Пан или пропал!" , ни чего не замедляем, просто Функция dofile выполнит Lua-скрипт, и все переменные и функции, определённые в нём, будут доступны в текущем окружении. И смысл здесь в подключении новой задачи!
Подскажите по каким признакам, можно фильтровать время проведения торговых сессий? Ни как не возьму в толк, с новыми изменениями, где начало где конец сессии? Какие активы торгуются? На какой бирже?
Nikolay, Согласен, вопрос составлен не четко, двусмысленно. Но ведь он звучит все таки так:
Цитата
Saturn написал: То есть один Kua скрипт запущенный в цикле проверял бы если другой Lua скрипт не запущен - то запустить его.
Ответ: Да, это возможно. В Lua можно запустить другой Lua-скрипт с помощью функции dofile или loadfile. Вы можете создать основной скрипт, который будет проверять, запущен ли другой скрипт, и если нет, то запускать его. Вот такого подхода пример:
Код
-- основной скрипт (main.lua)
local is_other_script_running = false
-- Функция для проверки, запущен ли другой скрипт
function check_if_other_script_running()
-- Здесь можно реализовать проверку, например, через файл или глобальную переменную
-- В данном примере просто используем переменную для демонстрации
return is_other_script_running
end
-- Функция для запуска другого скрипта
function run_other_script()
is_other_script_running = true
dofile("other_script.lua") -- Запуск другого скрипта
is_other_script_running = false
end
-- Основной цикл
while true do
if not check_if_other_script_running() then
print("Другой скрипт не запущен, запускаю...")
run_other_script()
else
print("Другой скрипт уже запущен.")
end
-- Пауза перед следующей проверкой
os.execute("sleep 1") -- Для Linux/MacOS
-- os.execute("timeout /t 1") -- Для Windows
end
Никаких принципиальных сложностей. В этом примере: Основной скрипт (main.lua) работает в бесконечном цикле. Он проверяет, запущен ли другой скрипт (other_script.lua), с помощью функции check_if_other_script_running. Если другой скрипт не запущен, он запускает его с помощью dofile("other_script.lua"). После завершения работы другого скрипта, флаг is_other_script_running сбрасывается.
Вы поднимаете более сложный вопрос, о взаимодействии между скриптами в окружении терминала, в контексте Lua. И отвечаете, прямое управление скриптами в терминале невозможно, но есть обходные пути.
Разбираясь в работе Quik и функциональных возможностях Lua, иногда можно запутаться, и тогда трезвый взгляд со стороны помогает всё расставить по местам. Для меня таким взглядом всегда был Nikolay. И сейчас я бы не стал вмешиваться в эту дискуссию, если бы не ответы!
Да, это возможно! В Lua действительно можно запустить другой Lua-скрипт с помощью функций dofile или loadfile. Вы можете создать основной скрипт, который будет проверять, запущен ли другой скрипт, и если нет — запускать его. Все это и так явно.
Но почему бы не пойти дальше? Хочу озвучить другой подход.
Давайте забудем про все языки (Си, C#, да и про Fortran с Basic я забыл ещё со студенческих времён). Что у нас есть?
API Quik: разработчики любезно предоставили нам возможность через Lua получать данные от брокера и отправлять приказы (заявки) брокеру.
Функционал Lua: вот здесь начинается самое интересное!
Представим, что поток main — это основной поток (другого ничего нет), в котором всё крутится (в этом легко убедиться, запустив его без задержек). Функционал Lua предоставляет лёгкие потоки, которые работают внутри основного потока — они называются корутины.
Что они делают? Любые программы, блоки, модули, функции — всё это можно запускать в корутинах! Причём не только последовательно, но и асинхронно.
Таким образом, функция main(), будучи запущенной, превращается в сложнейшую программу, способную не только явно но и в фоновом режиме выполнять что угодно: получать данные, проводить расчёты, подключать и останавливать модули и многое другое.
К чему я так подробно рассказываю? Напишите в своей программе модуль на Lua (это та же таблица) который хотите запустить, и используйте его где угодно.
Это открывает огромные возможности для создания гибких и мощных решений.
Коллбек как источник данных, для асинхронной обработки данных. Довел вариант до рабочего, в качестве источника данных OnAllTrade, получаем таблицу и переносим расчеты, не в маин, в сопрограмму. Такой подход не блокирует выполнение других операций программы, если количество сделок большое, или сделка обрабатывается долго, которые запущены в процессе. Производительность и нагрузки просто супер. Задание паузы между итерациями обработки позволяет контролировать частоту обработки данных, что полезно для предотвращения перегрузки системы, а также задает временные промежутки для накопления данных в очередь. Этот подход позволяет не только улучшить структуру кода, но и легко расширять систему с новыми метриками и функциональностью. Каждый компонент изолирован и отвечает только за свою часть. Легко добавлять или менять логику внутри каждого модуля, не влияя на другие части системы. А также, можно добавлять новые метрики или алгоритмы, расширяя существующие возможности самой программы. Собраны метрики и алгоритмы в отдельные модули. Каждый модуль может экспортировать только необходимые функции и данные, позволяя другим частям системы использовать их через интерфейсы, что должно помочь избежать излишней зависимости между компонентами. Метрики можно групировать по стратегиям. Система принятия решений будет интегрировать метрики, анализировать их с использованием различных стратегий и на основе результатов принимать решение о торговых действиях. Основными компонентами такой системы принятия решений будут: 1. Метрики – используем различные метрики для анализа данных. 2. Стратегии – каждая стратегия (скальпинг, трендовая торговля, арбитраж) будет использовать различные метрики для принятия решений. 3. Алгоритм принятия решений – будет выбирать, какая стратегия должна быть использована в текущих условиях рынка и какое действие (купить/продать) должно быть выполнено. 4. Система обработки данных – будет собирать данные (например, цены и объемы), передавать их в соответствующие стратегии и использовать метрики для анализа. В моем случае придется, переосмыслить архитектуру основной программы - добавить универсальности, но и наконец то, выхожу на однотипную структуру данных для такой программы, под гордым названием с большой буквы Робот. Пока, Всем хорошей торговли!
Не знаю почему не получается сворачивать окно у меня. Подскажите последовательность нажатия конок? Хотел предложить на обсуждение еще один вариант, обработки сделок уже т. всех сделок с использованием очереди. По факту даже два варианта. 1) обрабатываем в маин. 2) асинхронный. Как раз перерабатываю, чтобы модуль добавить в программу.
Касаемо обработки собственных сделок, была а возможно и есть программа написана на qpl, называется Калькулятор уровней позиции: (q_calc) автор Николай Морошкин. На ней учился переписывая на луа логику. Выкладываю как возможный пример обработки сделок, просто в нем есть четкая логика, на ошибки нужно проверять.
Код
function detect_position()
--[[ Проверки статуса заявок (1-й и 2-й биты flags):
"Активна": bit.band(order.flags, 1) > 0
"Неактивна" (исполнена либо снята): bit.band(order.flags, 1) == 0
"Исполнена": bit.band(order.flags, 1) == 0 и bit.band(order.flags, 2) == 0
"Снята": bit.band(order.flags, 1) == 0 и bit.band(order.flags, 2) > 0
Нас интересуют два флага:
OF_ACTIVE = 0x01
OF_KILL = 0x02
--]]
local ord = "orders"
local max_numorder = getNumberOf("orders")
-- Отслеживаем количество ордеров
Log:trace('detect_position: отслеживаю размер массива '
.. ' getNumberOf =' .. tostring(max_numorder)
.. ' index =' .. tostring(max_numorder-1)
.. ' / o_index = ' .. tostring(o_index)
.. ' save_needed =' .. tostring(save_needed))
-- Функция для поиска ордеров
local function myFind(A, C, S, F)
return (A == ACCOUNT and C == CLASSCODE and S == SECCODE
and bit.band(F, 0x2) == 0 and bit.band(F, 0x1) == 0)
end
-- Поиск ордеров с нужными фильтрами
local iItems = SearchItems("orders", 0, max_numorder - 1, myFind, "account,class_code,sec_code,flags")
-- Проверка на пустой результат
if not iItems or #iItems == 0 then
Log:trace('ERROR: SearchItems вернул пустой результат')
return false
end
Log:trace('detect_position: всего элементов ' .. tostring(#iItems) .. ' последний ордер: ' .. tostring(iItems[#iItems]))
-- Если еще не инициализировано
if initialized == 0 then
initialized = -1
return
end
-- Обработка ордеров
for i = o_index, #iItems do
local order = getItem("orders", iItems[i])
if not order or type(order) ~= "table" then return false end
-- Определение флагов состояния ордера
local OF_ACTIVE = bit.band(order.flags, 1) > 0
local NOT_ACTIVE = bit.band(order.flags, 1) == 0
local OF_FILL = bit.band(order.flags, 1) == 0 and bit.band(order.flags, 2) == 0
local OF_KILL = bit.band(order.flags, 1) == 0 and bit.band(order.flags, 2) > 0
if order.account == ACCOUNT and order.class_code == CLASSCODE and order.sec_code == SECCODE
and (OF_FILL or OF_ACTIVE) and not Orders[order.order_num] then
Orders[order.order_num] = os.time() -- Запоминаем время
-- Получаем информацию о заказе
local order_n = order.order_num
local order_dt = order.datetime
local order_d = get_date(order_dt) -- Дата
local order_t = get_time(order_dt) -- Время
last_tradedate = order_d
local order_b = order.balance
local order_q = order.qty - order_b
local order_p = order.price
local order_op = bit.band(order.flags, 0x4) == 0 and 'BUY' or 'SELL'
-- Логирование активных ордеров
if OF_ACTIVE then
Log:trace(NAME_OF_STRATEGY .. ' ' .. i .. ' ACTIVE лимитная заявка по ' .. order.sec_code
.. ' number: ' .. tostring(order_n)
.. ' ' .. tostring(order_d)
.. ' ' .. tostring(order_t)
.. ' ' .. tostring(order_op)
.. ' ' .. tostring(order_q) .. ' (' .. tostring(order_b) .. ')'
.. ' ' .. tostring(order_p))
end
-- Обработка исполненных ордеров
if OF_FILL then
Log:trace(NAME_OF_STRATEGY .. ' ' .. i .. ' Исполнена лимитная заявка по ' .. order.sec_code
.. ' ' .. tostring(order_d)
.. ' ' .. tostring(order_t)
.. ' ' .. tostring(order_op)
.. ' ' .. tostring(order_q) .. ' (' .. tostring(order_b) .. ')'
.. ' ' .. tostring(order_p)
.. ' number: ' .. tostring(order_n))
local trade_q = 0
local trade_p = 0
local exchange_comission = 0
-- Поиск сделок, соответствующих ордеру
for trades_i = 0, getNumberOf("trades") - 1 do
local trade = getItem("trades", trades_i)
if trade.order_num == order_n then
last_order_id = order_n
local trade_d = get_date(trade.datetime)
local trade_t = get_time(trade.datetime)
local qq = trade.qty
local pp = trade.price
trade_q = trade_q + qq
trade_p = trade_p + (qq * pp)
exchange_comission = exchange_comission + trade.exchange_comission
end
end
if trade_q ~= 0 then
trade_p = trade_p / trade_q
exchange_comission = exchange_comission / trade_q
end
-- Проверка на рассинхронизацию заявок и сделок
if order_q ~= trade_q then
Log:trace("Рассинхронизация заявок и сделок: " .. tostring(order_n)
.. ", " .. tostring(order_q) .. "/" .. tostring(trade_q))
message("Рассинхронизация заявок и сделок: " .. tostring(order_n)
.. ", " .. tostring(order_q) .. "/" .. tostring(trade_q))
end
-- Запись в журнал
Log:trace('Получаю тек. позицию: '
.. " " .. tostring(pos.dir)
.. ' ' .. tostring(pos.qty)
.. ' ' .. tostring(pos.op)
.. ' ' .. tostring(pos.cap)
.. ' ' .. tostring(pos.be))
Log:trace('Получаю изменения в позиции: '
.. ' ' .. tostring(qcap)
.. ' order_op=' .. tostring(order_op)
.. ' order_q=' .. tostring(order_q)
.. ' order_b=' .. tostring(order_b)
.. ' order_p=' .. tostring(order_p)
.. ' / trade_q=' .. tostring(trade_q)
.. ' trade_p=' .. tostring(trade_p))
-- Управление позицией
local log_action = ""
if pos.dir == "n" then
pos.dir, pos.qty, pos.op, pos.cap, pos.be, pos.open = set_position(order_op, trade_q, trade_p)
write_journal(order_op, qcap, trade_q, trade_p, trade_datetime)
log_action = "Открытие позиции " .. tostring(pos.dir) .. ' ' .. tostring(pos.qty) .. ' ' .. tostring(pos.op)
Log:trace(i .. ' ' .. log_action)
save_needed = 1
elseif (pos.dir == "L" and order_op == "BUY") or (pos.dir == "S" and order_op == "SELL") then
pos.op = pos.op * pos.qty + trade_p * trade_q
pos.qty = pos.qty + trade_q
pos.op = pos.op / pos.qty
write_journal(order_op, -1, trade_q, trade_p, trade_datetime)
log_action = "Наращивание позиции " .. tostring(pos.dir) .. tostring(pos.qty)
Log:trace(i .. ' ' .. log_action)
save_needed = 1
else
if trade_q == pos.qty then
pos.dir, pos.qty, pos.op, pos.cap, pos.be, pos.open = "n", 0, 0, 0, 0, bar.open
write_journal(order_op, 0, trade_q, trade_p, trade_datetime)
log_action = "Закрытие позиции " .. tostring(pos.dir) .. ' ' .. tostring(pos.qty)
Log:trace(i .. ' ' .. log_action)
end
save_needed = 1
end
end
end
end
end
nikolz, Вы опять правы, мои предположения и понимание не есть "истина в последней инстанции", и я не отношу себя к "гуру", бывает ошибаюсь. Мне просто не понятно, что на специализированном форуме проходится рассуждать про объективные вещи. Да по мере присутствия на бирже учусь и набираюсь опыта, им делюсь и обсуждаю.
Цитата
nikolz написал: Вы полагаете что БКС гоняется за тиками и поэтому Вы его называете ММ?
Вы зачем ставите все "с ног на голову"? Ведь это Ваше утверждение.
Цитата
nikolz написал: Я утверждаю, что понятие (кличка) "MM" связано лишь с оказанием платных услуг биржи. Это никак не связано ни со следами, ни с размером ни с чем другим .
ММ это прежде всего коммерческий проект, целью которого является извлечение прибыли, как и любой другой коммерческой организации. Обладая значительными средствами, набирают большие позиции которые, оставляя следы на графике, а значить могут повести рынок за собой или держать его в диапазоне. Это интересует трейдера, отвечая на вопрос в какую сторону держать позицию, и уж совсем не интересно сколько заработал ММ. Именно это поведение как крупного игра их отличает. Вы можете даже конкретные алгоритмы найти в сети. Обладая дополнительной информацией они строят свои торговые стратегии, свой бизнес.
"Что написано пиром, то не вырубить топором". Касаемо моего подхода, читайте выше, там есть и формализация понятия о чем я рассуждаю, а рассуждаю я о контрагентах в сделках, как их можно формализовать в алгоритмических стратегиях.
nikolz, Все я прекрасно понял. Смею предположить, Ваши торговые стратегии, видимо не выходят за пределы технологий "скальпинга", отсюда и кругозор. Выше Вы опубликовали участников заключивших договора, Вам наименования организаций не о чем не говорят? Именно они гоняются за тиками? Вы просто посмотрите на ликвидность в стаканах, и на финансовые возможности компаний обеспечивающих эту ликвидность. Что делать с остальными финансами? Могу предположить, что под скапинг выделяется не большая доля активного капитала на котором и крутятся HFT-алгоритмы, выдели 1% а что делать с 99% средств? Кстати, на эту же проблему эффективности, натолкнулся у себя в своих среднесрочных стратегиях, суть в следующем: есть 100% торговый капитал, по правилам риск менеджмента активный не более < 25%, что означает > 75% т. капитала лежит просто в обеспечении сделки, не плохо бы часть покрутить в скальперских стратегиях. Касаемо придумок все за долго до меня, стратегии опубликованы, на классику жанра указал выше. И никаких кличек, терминология применяется разная но это для упрощения в пониманиях, сегодня модная есть "смарт мани", там своя, у классиков своя, Суть одна. Касаемо следов не только на ценовых графиках отслеживаются, но часто в стакане, такой объем можно видеть, так как рассчитываемся по определенным правилам для данного эмитента то есть алгоритмический. Такие объемы определяются, и от них строятся разные стратегии. Платная услуга биржи, это не признак ММ, это один из источников дохода ММ. Даже мне раньше прилетало от биржи.
nikolz, Все это верно, это не предмет обсуждения. ММ это прежде всего коммерческий проект, целью которого является извлечение прибыли, как и любой другой коммерческой организации. Обладая значительными средствами, набирают большие позиции которые, оставляя следы на графике, а значить могут повести рынок за собой или держать его в диапазоне. Это интересует трейдера, отвечая на вопрос в какую сторону держать позицию, и уж совсем не интересно сколько заработал ММ. Именно это поведение как крупного игра их отличает. Вы можете даже конкретные алгоритмы найти в сети. Обладая дополнительной информацией они строят свои торговые стратегии, свой бизнес.
nikolz, На форуме существует некая путаница в понятиях, и Вы не первый кто рассуждая о Маркет - мейкерах, упрощает их деятельность или сравнивает их с некими злодеями Крупными игроками занимающихся манипуляциями рынков. На фондовых биржах это крупные инвестиционные банки (например, Goldman Sachs, Morgan Stanley) как Вы думаете их интересуют сделки в один тик? На разных рынках по разному, задачи и техники принципиально одни. Отслеживая сделки на рынке в целях выявления крупных сделок для лучшего понимания ситуации и реализации в системах принятия решений, делю игроков условно на 3 категории: 1) Крупный игрок 2) Маркет-мейкер 3) Ретейл. Различие в поведении в торгах! имелось в виду именно это, поэтому подробно подчернил технический аспект. Касаемо ММ, хотя это специализированная участник, компания или и.банк, обязанные соблюдать строгие регуляторные требования, они тоже совершают ошибки. Давайте представим ситуацию набрана огромная позиция, а рынок пошел против, что на весах, штраф если заметят, или манипуляция во спасение добра. И таких ситуаций множество. И это только моя точка зрения в основе которой, методологии Ричарда Вайкоффа (Wyckoff) с его композитным игроком, решения как считать каждый принимает сам.
Да, использую в обработке собственных сделок комбинированный подход. Ну про это говорили. Вот коллбек которым пользуюсь, на форуме он широко обсуждался, сто лет пользуюсь не заглядывая.
Код
--[[ Trade CALLBACK ]]-- -- Вызов колбэка OnTrade, который использует self для контроля и записи данных
--function OnTrade(trade) tradeManager:OnTrade(trade) end
function OnTrade(trade)
local key = trade.trans_id
local i=tonumber(sdelka[0]);
if working
and key and key>0
and (i==0 and key~=sdelka.id) or (i>0 and sdelka[i][1]~=key)
--and trade.sec_code==symbol --and trade.class_code==class
then
i=i+1;
sdelka[0]=i;
sdelka[i]={};
sdelka[i][0]=sdelka[0];
sdelka[i][1]=trade.trans_id;
sdelka[i][2]=get_date(trade.datetime)
sdelka[i][3]=get_time(trade.datetime)
sdelka[i][4]="B"; if bit.band(trade.flags,4)~=0 then sdelka[i][4]="S"; end;
local dir = sdelka[i][4]=="B" and 1 or sdelka[i][4]=="S" and -1 or 0;
sdelka[i][5]=trade.qty*dir;
sdelka[i][6]=trade.price;
----- comission
sdelka[i][7]=trade.clearing_comission+trade.exchange_comission+trade.tech_center_comission;
sdelka[i][8]=trade.order_num;
sdelka[i][9]=trade.trade_num;
sdelka[i][10]=trade.sec_code;
sdelka.id=trade.trans_id;
Log:info("OnTrade! sdelka "..i .."; "
.."; trans_id="..sdelka[i][1]
.."; "..sdelka[i][2]
.."; "..sdelka[i][3]
.."; "..sdelka[i][4]
.."; "..sdelka[i][5]
.."; "..sdelka[i][6]
.."; comission="..sdelka[i][7]
.."; onum="..sdelka[i][8]
.."; tnum="..sdelka[i][9]
.."; "..sdelka[i][10]
.."\n"
);
--local a,v; for a,v in pairs(sdelka[i) do Log:trace( 'OnTrade! sdelka['..i..']['..tostring(a)..'] = '.. tostring(v) ) end;
end
end;
Всем добрый день, хочу просто добавить фундамент, в о сознание цели. Знание сила!
Коллбеки (или обратные вызовы) — это механизм, при котором одна функция передается как параметр в другую функцию, и она вызывается (или "вызвана") внутри этой функции, когда наступает определенное событие или условие. В контексте торгового алгоритма и работы с терминалом QUIK, коллбеки используются для получения данных (например, сделок) и их обработки в реальном времени. В очередь ставятся данные полученные от них, для уменьшения нагрузки.
Кто выступает контрагентом в сделке, и что нужно учитывать в скальпинге и подобных стратегиях.
1) Рынки МосБиржи, если рассматриваете ее, управляются маркет-мейкерами. Основные алгоритмы для обеспечения максимальной эффективности которые, можно даже сказать, обязан использовать маркет-мейкер: a) Алгоритмы выставления двустороних котировок; b) Алгоритмы управления позицией; c) Арбитражные алгоритмы; d) Алгоритмы управления рисками; e) Алгоритмы HFT (High-Frequency Trading). Работают не со стаканом а с книгой ордеров. Принцип работы HFT-алгоритмов (High-Frequency Trading): Осуществляют тысячи сделок в секунду. Мониторят микро изменения цен и мгновенно реагируют на рыночные дисбалансы. Технически: Используются FPGA (платы программируемой логики) для минимизации задержек. Торговля осуществляется максимально близко к серверам биржи (co-location), используются прямые подключения к биржевым серверам через высокоскоростные сети. Современные алгоритмы маркет-мейкеров работают с задержкой менее 1 миллисекунды. А в кризисных ситуациях может выйти из рынка.
2) На российском рынке сейчас преобладают частные инвесторы (МосБиржа), и этому есть простые объяснения. Вот из оценки брокера, доля частных инвесторов в объеме торгов акциями на ноябрь 2024г. - 76% за 2023г. -40%.
Разрабатывая архитектуру своей алгоритмической торговой программы, на мой взгляд, это нужно учитывать, особенно если стратегии скальперские. Луа в рамках QUIK, прекрасно справляется с торговыми задачами. Корутинный подход (асинхронность) прекрасно решает задачу Event и Wait озвученные nikolz, для этого, один из подходов создается своя таблица событий (Event).
-- Функция для вычисления кумулятивной функции распределения нормального распределения (N(x))
function norm_cdf(x)
local t = 1 / (1 + 0.2316419 * math.abs(x))
local d = 0.3989422804014337 * math.exp(-0.5 * x * x)
local prob = d * t * (0.319381530 + t * (-0.356563782 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429)))))
if x >= 0 then
return 1 - prob
else
return prob
end
end
-- Основная функция для расчета цены опциона по модели Блэка-Шоулза
function black_scholes_call_price(S, K, T, r, sigma)
local d1 = (math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * math.sqrt(T))
local d2 = d1 - sigma * math.sqrt(T)
return S * norm_cdf(d1) - K * math.exp(-r * T) * norm_cdf(d2)
end
-- Функция для нахождения подразумеваемой волатильности для одного опциона
function implied_volatility(S, K, T, r, market_price)
local low = 0.0001
local high = 2.0
local tolerance = 0.0001
local sigma = (low + high) / 2
local price = black_scholes_call_price(S, K, T, r, sigma)
-- Итерационный метод поиска
while math.abs(price - market_price) > tolerance do
if price < market_price then
low = sigma
else
high = sigma
end
sigma = (low + high) / 2
price = black_scholes_call_price(S, K, T, r, sigma)
end
return sigma
end
-- Функция для нахождения подразумеваемых волатильностей для серии опционов
function implied_volatilities_for_series(S, r, options)
local ivs = {}
for i, option in ipairs(options) do
local iv = implied_volatility(S, option.K, option.T, r, option.market_price)
table.insert(ivs, {K = option.K, T = option.T, implied_volatility = iv})
end
return ivs
end
-- Пример использования
local S = 100 -- Текущая цена актива
local r = 0.05 -- Безрисковая процентная ставка (5%)
-- Список опционов с параметрами: Цена исполнения (K), Время до экспирации (T в годах), Рыночная цена опциона (market_price)
local options = {
{K = 95, T = 30 / 365, market_price = 6},
{K = 100, T = 30 / 365, market_price = 5},
{K = 105, T = 30 / 365, market_price = 3.5},
{K = 110, T = 60 / 365, market_price = 4.5}
}
-- Вычисляем подразумеваемую волатильность для каждого опциона
local ivs = implied_volatilities_for_series(S, r, options)
-- Выводим результаты
for _, iv in ipairs(ivs) do
print(string.format("Цена исполнения: %.2f, Время до экспирации: %.2f лет, Подразумеваемая волатильность: %.4f%%",
iv.K, iv.T, iv.implied_volatility * 100))
end
Код
Что получилось:
Подразумеваемая волатильность: 42.0108
Цена исполнения: 95.00, Время до экспирации: 0.08 лет, Подразумеваемая волатильность: 21.5971%
Цена исполнения: 100.00, Время до экспирации: 0.08 лет, Подразумеваемая волатильность: 42.0108%
Цена исполнения: 105.00, Время до экспирации: 0.08 лет, Подразумеваемая волатильность: 46.9254%
Цена исполнения: 110.00, Время до экспирации: 0.16 лет, Подразумеваемая волатильность: 48.9867%
-- Функция для вычисления кумулятивной функции распределения нормального распределения (N(x))
function norm_cdf(x)
local t = 1 / (1 + 0.2316419 * math.abs(x))
local d = 0.3989422804014337 * math.exp(-0.5 * x * x)
local prob = d * t * (0.319381530 + t * (-0.356563782 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429) )))
if x >= 0 then
return 1 - prob
else
return prob
end
end
-- Основная функция для расчета цены опциона по модели Блэка-Шоулза
function black_scholes_call_price(S, K, T, r, sigma)
local d1 = (math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * math.sqrt(T))
local d2 = d1 - sigma * math.sqrt(T)
return S * norm_cdf(d1) - K * math.exp(-r * T) * norm_cdf(d2)
end
-- Функция для нахождения подразумеваемой волатильности
function implied_volatility(S, K, T, r, market_price)
local low = 0.0001
local high = 2.0
local tolerance = 0.0001
local sigma = (low + high) / 2
local price = black_scholes_call_price(S, K, T, r, sigma)
-- Итерационный метод поиска
while math.abs(price - market_price) > tolerance do
if price < market_price then
low = sigma
else
high = sigma
end
sigma = (low + high) / 2
price = black_scholes_call_price(S, K, T, r, sigma)
end
return sigma
end
-- Для примера использования
local S = 100 -- Текущая цена актива
local K = 100 -- Цена исполнения
local T = 30 / 365 -- Время до экспирации (в годах)
local r = 0.05 -- Безрисковая процентная ставка (5%)
local market_price = 5 -- Рыночная цена опциона call
local iv = implied_volatility(S, K, T, r, market_price)
print(string.format("Подразумеваемая волатильность: %.4f", iv * 100))
Да действительно, есть особенности в самой функции math.floor, углубляться не стал, вернулся к варианту изложенному Kolossi, с двумя правилами, сделал просто более читаемым для себя. Спасибо за обсуждение. Казалось бы простая задача округления числа, а вариантов собралось с десяток.
Acaw написал: В блоке MAIN у меня создается источник данных (ДС):ds["NLMK"] = CreateDataSource("TQBR", "NLMK", INTERVAL_D1)Далее мне нужно в функции обращаться к нему, но не напрямую, а присвоив его локальному дс:local dsf = ds["NLMK"], т.е. чтобы можно обращаться через dsf:C(i).
В Lua, поведение относительно памяти зависит от того, является ли это совершенно новой таблицей или ссылкой на существующую таблицу. 1) Под новую таблицу, Lua выделит новую память. Предыдущая таблица, на которую ссылается пользователь, станет доступной для сборки мусора. Утечки памяти нет. 2) Если это ссылка (или псевдоним) на таблицу, которая уже существует в программе, то она просто укажет на эту существующую таблицу. Для самой таблицы не выделяется новая память. Тем не менее, предыдущая таблица, на которую ссылается пользователь, все равно будет удалена сборщиком мусора, если на нее больше не ссылаются нигде. Короче говоря, в целом безопасен и эффективен. Сборка мусора Lua отвечает за управление памятью, предотвращая утечки памяти в большинстве случаев.
Добрый день! Решил разбавить свой портфель разными активами, остановил выбор на ОФЗ, дописал под них свой класс. В общем не чего особенного, ОФЗ котируют трех типов поэтому в основу легло три стратегии, которые при одной и той же ситуации ведут себя по разному. Но столкнулся с проблемой автоматического обновления показателей инфляции и ключевой ставки, то есть с необходимость подключить внешние данные? Возможно уже решали подобную задачу или есть идеи. В основу создания универсального класса положена простая идея, класс который способен давать однозначные рекомендации по облигациям с указанием, на каких показателях основывается рекомендация. Сейчас это выглядит примерно так
Код
-- Вывод рекомендаций
function Bond:print_recommendations(inflation_rate, expected_rate_change)
local strategies = self:recommend_strategy(inflation_rate, expected_rate_change)
print("Рекомендации для облигации:", self.name)
for _, strategy in ipairs(strategies) do
print(string.format("- Стратегия: %s", strategy.strategy))
print(string.format(" Применима: %s", strategy.applicable and "Да" or "Нет"))
print(string.format(" Рекомендация: %s", strategy.message))
end
end
-- Пример использования
local myBond = Bond:new("ОФЗ 26207", "ОФЗ-ПД", 1000, 0.07, 950, 5)
local inflation_rate = 0.06 -- Текущая инфляция 6%
local expected_rate_change = -0.01 -- Ожидаемое снижение ставки на 1%
myBond:print_recommendations(inflation_rate, expected_rate_change)
Это ответ
Код
modules\Bond.lua" 1
Рекомендации для облигации: ОФЗ 26207
- Стратегия: Сохранение капитала
Применима: Да
Рекомендация: Облигация сохраняет капитал при текущей инфляции.
- Стратегия: Стабильный доход
Применима: Да
Рекомендация: ОФЗ-ПД подходят для стабильного дохода благодаря фиксированным купонам.
- Стратегия: Заработок на ставках
Применима: Нет
Рекомендация: Для заработка на ставках подходят долгосрочные ОФЗ-ПД.
>Exit code: 0 Time: 0.8326
function round(num, idp)
-- Если num некорректное, вернуть как есть
if not num or type(num) ~= "number" then return num end
-- Если idp не указан, использовать 0 (округление до целого числа)
idp = idp or 0
local mult = 10^idp
-- Округление для любого числа
local rounded = math.floor(num * mult + 0.5) / mult
-- Если число целое, убрать .0
if rounded == math.floor(rounded) then
return math.floor(rounded)
end
return rounded
end