1000 раз уже на форуме говорил "Не программист, и знания мои так себе". Это раз! Форум - это площадка для обсуждений, а не выставления амбиций. Это два!
Теперь что касается последней темы:
1. Не надо "Кусочничить". Я тему веду последовательно, и рассуждения читать нужно также. 2. А Вы то понимаете сами? Пока не создан подход, то и оптимизировать НЕ ЧЕГО, от слова совсем? 3. Ответ на Ваш вопрос. Оптимизация - это КОМПРОМИС. Поменяйте понимание и все встанет по местам. 4. Повторюсь мне не сложно: Подход - Инвестиционный (означает тф. месячный и больше, в квик их нет). Задача - возможность торговать и управлять 1 инструментом так и портфелем (универсальность). Структура - модульная, много разового использования. Интерфейс - наипростейший, понятный бабушке!
Про КПД, чего Вы рассуждаете? Все КПД моего старенького направленно на эффективность торговли = увеличению баланса счета. Тем более что подход описан, и можно воспользоваться функциями без корутин.
И последнее, Вы не сомневайтесь, сомнения нужны на стадии анализа, а когда решение принято, нужно действовать. Попробуйте. Ну или обосновано возражайте.
Пользователь
Сообщений: Регистрация: 30.01.2015
06.11.2025 10:25:03
Цитата
VPM написал: Попурри на тему "Три тополя на Плющихи" в исполнении ... . ::
Зацените на сколько удобен интерфейс конечного пользователя в этом подходе.
Код
Преимущества MARKETDATA_PRO v1.0:
Вы не учитываете тот факт, что только Вы знаете это .
Мне как пользователю непонятно зачем мне это. -------------------------- Слишком.сложно, много букв. Трудно выявлять ошибки. -------------------- Попробуйте не дублировать уже описанное. -------------------- Все общее разместите в начале. Например так:
Код
-- Примеры использования универсальной системы:
market = "TQBR"
ticker = "SBER"
-- Глобальный экземпляр
MDP = MarketData_Pro.Manager:new()
-- 1. УНИВЕРСАЛЬНЫЙ ТЕХНИЧЕСКИЙ АНАЛИЗ ДЛЯ ЛЮБЫХ ДАННЫХ
-- Анализ цены последней сделки
local price_analysis = MDP:getOrCreate{ interval = INTERVAL_H1, param = "last" }
print ( "Цена SMA20:" , price_analysis:sma( 20 ))
print ( "Цена RSI14:" , price_analysis:rsi( 14 ))
-- Анализ стакана (биды)
local bid_analysis = MDP:getOrCreate{ interval = INTERVAL_M5, param = "bid" }
print ( "Биды EMA10:" , bid_analysis:ema( 10 ))
print ( "Биды STDDEV20:" , bid_analysis:getIndicator( "STDDEV" , 20 ))
-- Анализ объема
local volume_analysis = MDP:getOrCreate{ interval = INTERVAL_M15, param = "volume" }
print ( "Объем SMA30:" , volume_analysis:sma( 30 ))
-- 2. КАСТОМНЫЕ ИНДИКАТОРЫ ДЛЯ ЛЮБЫХ ПАРАМЕТРОВ
-- Регистрация индикатора для спреда
MDP:registerIndicator( "SPREAD_EMA" , function (buffer, period)
if buffer.size < period then return nil end
local sum = 0
for i = 1 , period do sum = sum + buffer:last(i).value end
return round(sum / period, 4 )
end , "EMA for Spread" , {"numeric"})
-- Анализ спреда bid/offer
local spread_data = MDP:getOrCreate{ description = "Bid-Offer Spread" }
-- Здесь можно обновлять буфер вручную с вычисленным спредом
spread_data:updateBuffer( "value" , 0.15 ) -- пример спреда
print ( "Спред EMA10:" , spread_data:getIndicator( "SPREAD_EMA" , 10 ))
-- 3. УНИВЕРСАЛЬНЫЙ АНАЛИЗ РАЗНЫХ ТИПОВ ДАННЫХ
local analyses = {
{param = "last" , desc = "Price Analysis" },
{param = "bid" , desc = "Bid Analysis" },
{param = "volume" , desc = "Volume Analysis" },
{param = "value" , desc = "Value Analysis" }
}
for _, analysis in ipairs(analyses) do
local data = MDP:getOrCreate{ ticker = "GAZP" , interval = INTERVAL_D1, param = analysis.param }
local sma = dat a:sma( 20 )
local rsi = dat a:rsi( 14 )
print ( string.format ( "%s - SMA20: %.2f, RSI14: %.1f" , analysis.desc, sma or 0 , rsi or 0 ))
end
-- 4. COMPLEX MULTI-PARAMETER ANALYSIS
interval = INTERVAL_H1
-- Анализ нескольких параметров одновременно
local multi_analysis = function ()
local price = MDP:getOrCreate{ param = "last" }
local volume = MDP:getOrCreate{ param = "volume" }
local bids = MDP:getOrCreate{ param = "bid" }
local price_trend = price:ema( 10 ) > price:ema( 20 )
local volume_spike = volume:sma( 5 ) > volume:sma( 20 ) * 1.5
local bid_strength = bids:ema( 5 ) > bids:ema( 10 )
return price_trend and volume_spike and bid_strength
end
-- 5. DYNAMIC INDICATOR CREATION
-- Создание индикатора на лету
local dynamic_indicator = function ()
MDP:registerIndicator( "VOLUME_PRICE_RATIO" , function (buffer, priceBuffer, period)
if not priceBuffer or buffer.size < period then return nil end
local volume_avg = 0
local price_avg = 0
for i = 1 , period do
local vol_item = buffer:last(i)
local price_item = priceBuffer:last(i)
if vol_item and price_item then
volume_avg = volume_avg + vol_item.value
price_avg = price_avg + price_item.value
end
end
volume_avg = volume_avg / period
price_avg = price_avg / period
return volume_avg / math.max (price_avg, 0.001 )
end , "Volume-Price Ratio" , {"volume"})
end
Универсальная система - это такая система, разработчик которой не имеет представления где и как ее будут применять. ----------------------- Пользователь такой системы не в состоянии понять все, что включил в нее разработчик. ------------------------- В результате реально либо используется 10 процентов возможности такой системы, либо пользователь начинает делать свою - не универсальную.
Пользователь
Сообщений: Регистрация: 15.06.2023
06.11.2025 10:45:08
nikolz, На счет сложности, для меня очень сложно, здесь с Вами не поспоришь. Отсюда бывает и путаница.
Не могу согласиться: "Универсальная система - это такая система, разработчик которой не имеет представления где и как ее будут применять.". На все известно и источники и методы, а использование их на прямую зависит от сложности самой торговой системы. Изначально думал для удобства сделать единый интерфейс рыночных данных, как то само собой добавились индикаторы и вся остальная инфраструктура и все вылилось в целый фреймворк. Конечно нужно тестирование и оптимизация.
"Пользователь такой системы не в состоянии понять все, что включил в нее разработчик." Так ведь и задача стоит, не нужно пользователю начинка, подключил блок и пользуйся понятным простым интерфейсом. Меня "ушатала" постоянная переделка. Задача все таки, удобство получать в торговых подходах, а не упражнения в программировании. Удобно и то что есть алгоритмы которые использует большинство, ATR, StDev, средниеи.
Пользователь
Сообщений: Регистрация: 15.06.2023
06.11.2025 11:09:36
"Едем дальше, видим больше"! На мой взгляд данный подход, нужно распространить на получение "Серверных данных". Для понимания проблематики, архитектуру предлагается использовать общею для фондового и сросного рынков? Задача все та же.
Задача - возможность торговать и управлять 1 инструментом так и портфелем (универсальность), с учетом рисков (как квиковских, так и собственно ручных), ну и конечно одной из основных задач управление позицией. Структура - модульная, много разового использования. Интерфейс - наипростейший, понятный бабушке!
Обобщенно свести квиковские в функции в удобную оболочку:
Функции взаимодействия скрипта Lua и Рабочего места QUIK
getDepoEx - функция для получения позиций по инструментам указанного типа
getMoneyEx - функция для получения информации по денежным позициям указанного типа
getFuturesLimit - функция для получения информации по фьючерсным лимитам
getFuturesHolding - функция для получения информации по фьючерсным позициям
getSecurityInfo - функция для получения информации по инструменту?
getTradeDate - функция для получения даты торговой сессии
CalcBuySell - функция для расчета максимально возможного количества лотов в заявке? (скорее для контроля, уж больно тяжелая?)
getPortfolioInfoEx - функция для получения значений параметров таблицы «Клиентский портфель» с учетом срока расчётов
getBuySellInfoEx - функция для получения параметров (включая срок расчётов) таблицы «Купить/Продать» (Важная для маржинальной торговли!)
Пользователь
Сообщений: Регистрация: 15.06.2023
06.11.2025 14:16:24
Вариант реализации подхода, что скажите?
Код
QuickDataManager = {}
QuickDataManager.__index = QuickDataManager
-- Конструктор класса
function QuickDataManager:new(firmid, client_code)
local obj = setmetatable({}, self)
obj.firmid = firmid
obj.client_code = client_code
obj.data = {} -- Для хранения кэшированных данных
obj.loaded = {} -- Для отслеживания загруженных параметров
return obj
end
-- Функция для ленивой загрузки данных с учетом firmid и client_code
function QuickDataManager:_loadData(param, optional_params)
-- Формируем уникальный ключ для кэширования (с учетом параметров)
local cache_key = param .. "_" .. (optional_params or "")
if self.loaded[cache_key] then
return self.data[cache_key] -- Возвращаем из кэша
end
-- Если данных нет в кэше, загружаем их
local data
if param == "money" then
-- Вызов getMoneyEx для получения информации по деньгам
data = getMoneyEx(self.firmid, self.client_code, optional_params.tag or "", optional_params.currcode or "", optional_params.limit_kind or 0)
elseif param == "depo" then
-- Вызов getDepoEx для получения позиций по инструментам
data = getDepoEx(self.firmid, self.client_code, optional_params.sec_code or "", optional_params.trdaccid or "", optional_params.limit_kind or 0)
elseif param == "portfolio" then
-- Вызов getPortfolioInfoEx для получения информации по клиентскому портфелю
data = getPortfolioInfoEx(self.firmid, self.client_code, optional_params.limit_kind or 0, optional_params.board_tag or "", optional_params.currency or "")
elseif param == "buy_sell" then
-- Вызов getBuySellInfoEx для получения параметров по заявкам
data = getBuySellInfoEx(self.firmid, self.client_code, optional_params.class_code or "", optional_params.sec_code or "", optional_params.price or 0)
elseif param == "futures_limit" then
-- Вызов getFuturesLimit для получения фьючерсных лимитов
data = getFuturesLimit(self.firmid, optional_params.trdaccid or "", optional_params.limit_type or 0, optional_params.currcode or "")
elseif param == "futures_holding" then
-- Вызов getFuturesHolding для получения позиций по фьючерсным счетам
data = getFuturesHolding(self.firmid, optional_params.trdaccid or "", optional_params.sec_code or "", optional_params.type or 0)
end
-- Кэшируем загруженные данные
self.data[cache_key] = data
self.loaded[cache_key] = true
return data or {}
end
-- Доступ к данным с ленивой загрузкой
function QuickDataManager:__index(key)
if key == "money" then
return self:_loadData("money", {tag = "default", currcode = "USD", limit_kind = 0})
elseif key == "depo" then
return self:_loadData("depo", {sec_code = "SBER", trdaccid = "1234", limit_kind = 0})
elseif key == "portfolio" then
return self:_loadData("portfolio", {limit_kind = 0, board_tag = "TQBR", currency = "RUB"})
elseif key == "buy_sell" then
return self:_loadData("buy_sell", {class_code = "TQBR", sec_code = "SBER", price = 1000})
elseif key == "futures_limit" then
return self:_loadData("futures_limit", {trdaccid = "TQBR", limit_type = 0, currcode = "USD"})
elseif key == "futures_holding" then
return self:_loadData("futures_holding", {trdaccid = "1234", sec_code = "SBER", type = 0})
else
-- Загружать другие параметры по необходимости
return self:_loadData(key)
end
end
-- Пример использования
local manager = QuickDataManager:new("Firm1", "Client1")
-- Ленивое обращение к данным
local money = manager.money -- Получение информации по деньгам
local depo = manager.depo -- Получение позиций по инструментам
local portfolio = manager.portfolio -- Получение информации по портфелю
local buy_sell = manager.buy_sell -- Получение информации по заявкам на покупку/продажу
Пользователь
Сообщений: Регистрация: 15.06.2023
06.11.2025 14:26:30
Что делает:
QuickDataManager:new(firmid, client_code). Конструктор создаёт экземпляр менеджера с обязательными параметрами firmid и client_code. Это основные параметры для работы с системой QUIK. Все остальные параметры передаются как опциональные в виде таблицы в функции.
QuickDataManager:_loadData(param, optional_params). Это основная функция для ленивой загрузки данных. В зависимости от параметра (например, "money", "depo", "portfolio", и т.д.), она делает запросы к соответствующим функциям QUIK. Функция также проверяет, есть ли уже кэшированные данные для этого параметра, и если они есть — возвращает их. Если данных нет, выполняется запрос и кэшируются результаты? Здесь основной вопрос как правильно организовать работу?
QuickDataManager:__index(key). Метатаблица __index отвечает за доступ к данным через ключи, такие как money, depo, portfolio и другие. Для каждого ключа вызывается соответствующая функция ленивой загрузки, которая получает необходимые данные и кэширует их.
Параметры (например, tag, currcode, limit_kind, sec_code и другие) передаются через опциональные параметры, что позволяет адаптировать запросы под различные нужды и сохранить гибкость подхода.
Пример использования. При первом обращении к данным (manager.money, manager.depo, manager.portfolio, и т.д.) данные будут загружены, затем они будут кэшироваться для последующего использования без лишних запросов к системе. Что позволяет уменьшить нагрузку на систему и ускорить последующие операции. Как альтернатива. Если нужно избежать повторного получения одинаковых данных, можно добавить механизм для "очистки" кэша по истечении времени или по запросу.
QuickDataManager:new(firmid, client_code). Конструктор создаёт экземпляр менеджера с обязательными параметрами firmid и client_code. Это основные параметры для работы с системой QUIK. Все остальные параметры передаются как опциональные в виде таблицы в функции.
QuickDataManager:_loadData(param, optional_params). Это основная функция для ленивой загрузки данных. В зависимости от параметра (например, "money", "depo", "portfolio", и т.д.), она делает запросы к соответствующим функциям QUIK. Функция также проверяет, есть ли уже кэширов анные данные для этого параметра, и если они есть — возвращает их. Если данных нет, выполняется запрос и кэшируются результаты? Здесь основной вопрос как правильно организовать работу?
QuickDataManager:__index(key). Метатаблица __index отвечает за доступ к данным через ключи, такие как money, depo, portfolio и другие. Для каждого ключа вызывается соответствующая функция ленивой загрузки, которая получает необходимые данные и кэширует их.
Параметры (например, tag, currcode, limit_kind, sec_code и другие) передаются через опциональные параметры, что позволяет адаптировать запросы под различные нужды и сохранить гибкость подхода.
Пример использования. При первом обращении к данным (manager.money, manager.depo, manager.portfolio, и т.д.) данные будут загружены, затем они будут кэшироваться для последующего использования без лишних запросов к системе. Что позволяет уменьшить нагрузку на систему и ускорить последующие операции. Как альтернатива. Если нужно избежать повторного получения одинаковых данных, можно добавить механизм для "очистки" кэша по истечении времени или по запросу.
Лучше не заставлять пользователя изучать Lua и не вводить без острой надобности специальные термены. Например, вместо:
Код
QuickDataManager:new(firmid, client_code).
написал бы
Код
fimid=XXXX
client_code=EEEE
а в скрипте под капотом написал бы
Код
QuickDataManager:new(firmid, client_code).
про термины: "ленивая" загрузка - пользователю обязательно это знать? Тогда что такое ленивая и какие еще есть ? ---------------------------- Т е сначала решите, для кого Вы пишите эту инструкцию. Т е какой у него уровень знаний должен быть, чтобы понять, что вы написали.
Пользователь
Сообщений: Регистрация: 30.01.2015
06.11.2025 15:12:23
и еще В своем скрипте все параметры типа firmid, client получаю из QUIK. Т е их не надо вводить пользователю.
Пользователь
Сообщений: Регистрация: 15.06.2023
06.11.2025 15:27:47
nikolz, Это не инструкция, это описные одного из вариантов подхода, если хотите мое виденье, выложено на обсуждение сообществу.
"ленивая" загрузка - описание есть выше, такую возможность предоставляет ООП, если не очень понятно можно посмотреть фреймворк на который я ссылаюсь.
Подход уже тоже устарел так как нашлись более удобные решения, например кэширует результаты и имеет авто обновление через корутину (с ограничением нагрузки),
Пользователь
Сообщений: Регистрация: 15.06.2023
06.11.2025 15:31:19
Цитата
nikolz написал: В своем скрипте все параметры типа firmid, client получаю из QUIK. Т е их не надо вводить пользователю.
Не очень понимаю этот подход, ну если с firmid все еще понятно, зависит в чей терминал скипт загружен. То с client не все очевидно, все равно приходится в водить,
написал: В своем скрипте все параметры типа firmid, client получаю из QUIK. Т е их не надо вводить пользователю.
Не очень понимаю этот подход, ну если с firmid все еще понятно, зависит в чей терминал скипт загружен. То с client не все очевидно, все равно приходится в водить,
можете вводить это не принципиально. Для информации. Вот что надо ввести пользователю в мой скрипт
Код
clas_list="QJSIM:0,SPBFUT:1,CETS:2"; --список классов счетов и тип инструментов 0 -акции 1-фьючерсы 2- валюта 3- опционы
sec_list="ROSN,GAZP,SBER,PLZL,GMKN,CHMF,HYDR,LKOH,MOEX,SNGS" --список инструментов для портфеля, если нет, то все инструменты по заданным классам
task_list="task1,task2,task3,task4" -- список существующих задач
sec_user={ --параметры устанавливаемые пользователем для инструментов по умолчанию
1, --интервал свечей
0, --подписка на стакан
0, --флаг разрешения коротких позиций
0,--флаг разрешения установки стопа 1 -обычный, 2-скользящий, 3 -take 4-stop и take
"task1",
"task2" --список задач по умолчанию для каждого инструмента
}
т е clas_list - список торгуемых классов sec_list -список торгуемых инструментов task_list -- список существующих задач, которые ему дает разработчик или он пишет сам sec_user -- параметры для все инструментов ---------------------- Для каждого инструмента,задачи можно через ':' указать индивидуальные параметры. Пользователю не надо изучать луа, если он не хочет писать задачи. =================== У меня тоже универсальная система.
Пользователь
Сообщений: Регистрация: 27.01.2017
06.11.2025 16:33:20
Код
if self.loaded[cache_key] then
return self.data[cache_key] -- Возвращаем из кэша
end
Т.к. я вижу в данных таблицы, которые постоянно изменяются, то не очень понятно о каком кеше идет речь. Кеш нужен для условно постоянно информации, а не для той, что изменяется каждую секунду. Эти данные нужны для совершения торговых операций, например, проверить, что там с доступными средствами. Обращаться постоянно к ним особой нужды нет. Также есть данные изменяемые при изменении статуса сессии, завершении клиринга - вот их можно помещать в кеш, и обновлять оный по этим событиям.
И да, делать для этого объекты - так себе затея.
Входящие параметры для скрипта - это код инструмента, торговый счет, субсчет (код клиента) если их несколько. Все. Остальное получается из терминала. Впрочем даже счет и субсчет можно подставить из данных для класса инструмента. Зачастую они и нужны.
Если Вы пишите скрипты для себя, то, конечно, делайте как хотите. Но если уже для других, то всегда отталкивайтесь, что максимум что может сделать пользователь - это изменить файл настроек, где все максимально просто и понятно. Не надо искать информацию в служебных окнах терминала. Либо делать интерфейс, через который тоже просто задаются параметры.
Пользователь
Сообщений: Регистрация: 30.01.2015
06.11.2025 18:27:36
Цитата
VPM написал: "Едем дальше, видим больше"! На мой взгляд данный подход, нужно распространить на получение "Серверных данных". Для понимания проблематики, архитектуру предлагается использовать общею для фондового и сросного рынков? Задача все та же.
Задача - возможность торговать и управлять 1 инструментом так и портфелем (универсальность), с учетом рисков (как квиковских, так и собственно ручных), ну и конечно одной из основных задач управление позицией. Структура - модульная, много разового использования. Интерфейс - наипростейший, понятный бабушке!
Обобщенно свести квиковские в функции в удобную оболочку:
Функции взаимодействия скрипта Lua и Рабочего места QUIK getDepoEx - функция для получения позиций по инструментам указанного типа getMoneyEx - функция для получения информации по денежным позициям указанного типа getFuturesLimit - функция для получения информации по фьючерсным лимитам getFuturesHolding - функция для получения информации по фьючерсным позициям getSecurityInfo - функция для получения информации по инструменту? getTradeDate - функция для получения даты торговой сессии CalcBuySell - функция для расчета максимально возможного количества лотов в заявке? (скорее для контроля, уж больно тяжелая?) getPortfolioInfoEx - функция для получения значений параметров таблицы «Клиентский портфель» с учетом срока расчётов getBuySellInfoEx - функция для получения параметров (включая срок расчётов) таблицы «Купить/Продать» (Важная для маржинальной торговли!)
У меня эти функции спрятаны. Пользователю доступ к ним не нужен и он про них ничего не знает и знать не хочет. В задаче он просто читает нужную переменную скрипта.
Пользователь
Сообщений: Регистрация: 30.01.2015
06.11.2025 18:29:11
У меня данные обновляются лишь по колбекам. Поэтому нет смысла их кэшировать.
Пользователь
Сообщений: Регистрация: 30.01.2015
06.11.2025 18:36:10
, Возможно Вы не обратили внимание, но у меня ВСЯ УНИВЕРСАЛЬНАЯ СИСТЕМА это 380 операторов из них 120 - это конечный автомат обработки колбеков и 60 - конечный автомат обработки задач. ----------------------------- + скрипты задач. которые могут создать событие на выставление удаление перестановку заявок. ---------------------- Интерфейс к ней показал ранее.
Пользователь
Сообщений: Регистрация: 30.01.2015
06.11.2025 18:48:51
сделал новый интерфейс еще проще:
Код
list={
--список инструментов для портфеля, если нет, то все инструменты по заданным классам
sec="ROSN,GAZP,SBER,PLZL,GMKN,CHMF,HYDR,LKOH,MOEX,SNGS"
task="task1,task2,task3,task4" -- список существующих задач
}
user_sec=" interval:1,quote:0,short:0,stop:0,takeprofit:0,task:task1:task2,quota:0"
--user_sec--параметры пользователя для sec по умолчанию--interval --интервал свечей --quote --подписка на стакан --short -- разрешениe коротких позиций --stop--флаг разрешения установки стопа 1 -обычный, 2-скользящий, --takeprofit, -- флаг разрешения TakePrifit --task---список задач по умолчанию для каждого инструмента --quota--для средств для покупки в одной заявке --можно задавать индивидуальные параметры для задач и инструментов
Пользователь
Сообщений: Регистрация: 30.01.2015
06.11.2025 18:49:50
поправил комментарии
Код
list={
--список инструментов для портфеля
sec="ROSN,GAZP,SBER,PLZL,GMKN,CHMF,HYDR,LKOH,MOEX,SNGS"
task="task1,task2,task3,task4" -- список существующих задач
}
user_sec=" interval:1,quote:0,short:0,stop:0,takeprofit:0,task:task1:task2,quota:0"