Блог им. hobbit

Ч9. Выборка, это тоже диверсификация, только еще более эффективная

Тенденция — это, попросту говоря, направление рыночного движения.… Рынки зачастую движутся между двумя параллельными линиями

Джон Мэрфи

Алгоритмические стоп-лоссы и тейк-профиты всегда можно визуализировать. То же можно проделать и с другими алгосигналами открытия и закрытия. Главное преимущество человека над торговым роботом — визуальное восприятие картинки торгов, как текущей, так и в прошлой истории. Так почему бы этим не воспользоваться?

Как-то упоминал о своем любимом индикаторе SavMeter, основанном на линиях двух SAR. Одна линия — трендовая. По ней открывается позиция. Другая, более быстрая, — замена трейлинг-стопа. При ее пробитии, позиция закрывается. Глядя на историю можно легко отрегулировать расстояние линий так, чтобы не было слишком много ложных сигналов. Это проще и быстрее, чем гонять тестера на истории.

Что особенно важно, индикатор SavMeter несет в себе эффект синергии. Объединяет сразу несколько инструментов. Объединенный график становится более сглаженными, это тоже уменьшает ложные сигналы. Корректировать параметры на одном графике проще, чем заниматься оптимизацией на графиках каждого отдельного инструмента.

Получается, синергия — более эффективная диверсификация. Среднегодовая доходность SavMeter на часовых фьючерсах на истории у меня всегда превышала 200%. На спотовом рынке получается в 5 раз меньше. Приблизительно 40%. Но есть еще более эффективный способ получения дохода.

Однажды меня заинтересовал блог AlexChi на Смартлабе. Он предлагает достаточно простую систему (BWS) выборки акций на недельном моменте. Система позволяет обогнать индекс. Нечто похожее, с сортировкой в таблице, я уже встречал. Не помню иностранного автора. Решил проверить. Действительно. На 4х-часовых фьючерсах доходность увеличилась в 1.5 раза больше, чем давал индикатор SavMeter, настроенный на одни и те же инструменты. Но прежде нужно было править LbotTest.

О новых свежих возможностях 2025. Они достаточно принципиальны, как и 3D. Речь идет об использовании внешних функций. Раньше, язык Lbot был замкнутым. Закрытым для сложных внешних алгоритмов и расчетов. Сейчас таких проблем нет. Фактически, можно расширять и развивать Lbot дальше по своему усмотрению, не вторгаясь во внутренности LbotTest_2025 и Lbot3D_2025. Для расширения нужно только знать язык Qlua (Lua для QUIK).

Пример использования внешней функции best34() на языке Lbot.

OpenLong = {best34(), 1} >= 29
CloseLong = {best34(), 1} < 29
OpenShort = {best34(), 1} <= -29
CloseShort = {best34(), 1} > -29

По правилам выше, позиция открывается в лонг у инструмента, если он входит в шестерку сильнейших. Имеет вес от 29 до 34. Более сильный получает больший вес (больший номер). Критерием выборки может быть индикатор RSI. Ниже пример такой функции best34(). Код требуется поместить в файл LbotExternal.lua (внутри нового дистрибутива LbotTest_2025). Робот выбирает лучшие на данный момент инструменты из заранее заданных 34х в массиве TGS.

------------------------------------------------------------------
local getCBar = getConcBar  -- Для чтения параллельной свечи сравниваемого инструмента

-- Выборка лучшего инструмента по RSI (*_h4_r)
local TGS = { 'Si_h4_r', 'CR_h4_r', 'Eu_h4_r', 'GL_h4_r', 'BR_h4_r', 'NA_h4_r', 'RI_h4_r', 'MM_h4_r', 'NG_h4_r', 'GD_h4_r'
, 'SV_h4_r', 'SF_h4_r', 'ED_h4_r', 'SR_h4_r', 'GZ_h4_r', 'RN_h4_r', 'SN_h4_r', 'LK_h4_r', 'VB_h4_r', 'YD_h4_r'
, 'TI_h4_r', 'MN_h4_r', 'ME_h4_r', 'GK_h4_r', 'TT_h4_r', 'AL_h4_r', 'PZ_h4_r', 'AF_h4_r', 'NK_h4_r', 'AK_h4_r'
, 'MT_h4_r', 'PI_h4_r', 'CH_h4_r', 'VK_h4_r' }
local MEDIUM = 17
function best34(id, tag, line, index)
    local tg = tag .. '_r'
    local n = getNumCandles(tg)
    if n == 0 then
        message('best34() ОШИБКА Отсутствует график ' .. tg, 3)
        return 0
    end
    local b, n = getCandlesByIndex(tg, line, index - 1, 1)
    if n ~= 1 then
        message('best34() ОШИБКА Отсутствует значение №' .. index .. ' графика ' .. tg, 3)
        return 0
    end
    b = b[0]
    local c = b.close
    local rate = 1
    local exists
    local k = 0  -- Счетчик падающих инструментов (нужен для отрицательного rate)
    if c < 50 then
        k = k + 1
    end
    index = index - 1
    for _, tg1 in pairs(TGS) do
        if tg1 and tg1 ~= tg then
            local b1 = getCBar(tag, index, tg1, line)
            if b1 then
                local c1 = b1.close
                if c1 < 50 then
                    k = k + 1
                end
                exists = true
                if c > c1 then
                    rate = rate + 1
                end
            end
        end
    end
    assert(exists, 'best34() ОШИБКА Отсутствуют сравниваемые значения для графика ' .. tg)
    if k > MEDIUM then
        rate = rate - #TGS - 1
    end
    return rate
end

Пожалуйста, ставьте лайки, чтобы поддержать проект.

Начало здесь

Тестер для конструктора роботов Lbot3D. Ч1. Нужна обратная связь

Ч2. Как все начиналось (из истории трейдера и программиста)

Ч3. В начале все стратегии были приведены к одной общей формуле

Ч4. Расшифровка торговой формулы E=#X%VD

Ч5. Пример расчета потерь при торговле на нескольких таймфреймах

Ч6. Оптимальное распределение активов при торговле фьючерсами

Ч7. Первое время, сама торговля для меня была тестированием

Ч8. Роботам недостаточно одного теста, тем более без добавок и низкого качества



★2
10 комментариев
Только что слегка подправил код best34(). Убрал лишние переменные и операции
avatar
---------------------------------------------------------------------------------------local getCBar = getConcBar-- Выборка лучшего из 32 инструментов по RSI
после этого кода это добавить
------------------------------------------------------------------
local getCBar = getConcBar  -- Для чтения параллельной свечи сравниваемого инструмента

-- Выборка лучшего инструмента по RSI (*_h4_r)

?

Как индюка добавить на график еквити и транс? ошибка LuaScripts\LbotTest_2025\log\BRT1.csv.
avatar
cern, Привет. Очень размытый вопрос. Какая конкретная ошибка? Не появляются индикаторы еквити и транс? Сейчас проверю у себя и потом объясню. Я сам давно уже ими не пользуюсь
avatar
Хоббит, a чем пользуешься? вквике эта выскакивает oшибкa

— Расширение для внешних функций, массивов и переменных, задаваемых в правилах инструкции *.ini
— Например, для {tag.line.func(arg,...), n} или {VAR[ind1,...]} или {VAR} реально происходит
— обращение к функции func(id, tag, line, index-n, org, ...) или к элементу массива VAR[id][ind1]… или VAR[id]
— id — имя секции (CR_TD в примере), tag — идентификатор графика (CR_m15), line — номер линии графика (по умолчанию 0),
— index — номер свечи (с 1). Возможен простой вариант вызова функции {func()}

— LbotTest.ini
--[CR_TD]
--; Используется внешняя функция sma() и переменная SMA_LENGTH
--Security = CRH5, SPBFUT, CR_m15, TD
--OpenLong = {CR_m15.0.sma(20), 1} > 0 and {SMA_LENGTH} < 3
--CloseLong = {sma(20), 1} < 0 or {SMA_LENGTH} > 5
— LbotExternal.lua
SMA_LENGTH = {}  — счетчик свечей между изменениями направленности SMA
local SMA = {}
— Определяет направленность скользящей среднней
function sma(id, tag, line, index, period)
    if not SMA[id] then
        SMA[id] = {}
        SMA_LENGTH[id] = {}
    end
    if not SMA[id][period] then
        SMA[id][period] = {}
    end
    if SMA[id][period].LastIndex == index then
        return SMA[id][period].LastValue
    end
    SMA[id][period].LastIndex = index
    local c, n = getCandlesByIndex(tag, 0, index — 1, 1)
    if n ~= 1 then
        message('ОШИБКА при вызове sma3() для '… tag, 3)
    end
    local b = c[0]
    SMA[id][period][index] = b.close
    local value
    if index > 2 * period then
        local sma1 = 0
        for i = index, index — period + 1, -1 do
            if SMA[id][period][i] then
                sma1 = sma1 + SMA[id][period][i]
            end
        end
        sma1 = sma1 / period
        local sma2 = 0
        for i = index — period, index — 2 * period + 1, -1 do
            if SMA[id][period][i] then
                sma2 = sma2 + SMA[id][period][i]
            end
        end
        sma2 = sma2 / period
        local value0 = SMA[id][period].LastValue
        value = sma1 — sma2
        SMA[id][period].LastValue = value
        — Проверка на смену направленности
        if not value0 or value * value0 < 0 then
            SMA_LENGTH[id] = 1
        else
            SMA_LENGTH[id] = SMA_LENGTH[id] + 1
        end
        local dt = b.datetime
        --message(dt.day… '.'… dt.month… '.'… dt.year… ' '… dt.hour… ':'… dt.min)
        --message('LastValue='… SMA[id][period].LastValue)
    end
    return value
end
------------------------------------------------------------------

---------------------------------------------------------------------------------------
local getCBar = getConcBar

— Выборка лучшего из 32 инструментов по RSI
local TGS32 = { 'SBER_d_r', 'GAZP_d_r', 'LKOH_d_r', 'YDEX_d_r', 'MGNT_d_r', 'GMKN_d_r', 'ALRS_d_r',
                'NVTK_d_r', 'ROSN_d_r', 'CHMF_d_r', 'NLMK_d_r', 'AFKS_d_r', 'PLZL_d_r', 'SNGSP_d_r',
                'PHOR_d_r', 'MOEX_d_r', 'TRNFP_d_r', 'TATNP_d_r', 'MTSS_d_r', 'IRAO_d_r', 'FEES_d_r',
                'RTKMP_d_r', 'VTBR_d_r', 'SPBE_d_r', 'HYDR_d_r', 'RASP_d_r', 'SIBN_d_r', 'OGKB_d_r',
                'TGKA_d_r', 'MRKP_d_r', 'MSNG_d_r', 'NMTP_d_r' }
function r32(id, tag, line, index)
    local MEDIUM = 16
    local tg = tag… '_r'
    local n = getNumCandles(tg)
    if n == 0 then
        message('r32() ОШИБКА Отсутствует график '… tg, 3)
        return 0
    end
    local b, n = getCandlesByIndex(tg, line, index — 1, 1)
    if n ~= 1 then
        message('r32() ОШИБКА Отсутствует значение №'… index… ' графика '… tg, 3)
        return 0
    end
    b = b[0]
    local c = b.close
    local rate = 1
    local exists
    local p, k = 0, 0
    if c > 50 then
        p = p + 1
    elseif c < 50 then
        k = k + 1
    end
    for _, tg1 in pairs(TGS32) do
        if tg1 and tg1 ~= tg then
            local b1 = getCBar(tag, index — 1, tg1, line)
            --local b1 = getConcBar(tag, index, tg1, line)
            if b1 then
                local c1 = b1.close
                if c1 > 50 then
                    p = p + 1
                elseif c < 50 then
                    k = k + 1
                end
                exists = true
                if c > c1 then
                    rate = rate + 1
                end
            end
        end
    end
    assert(exists, 'r32() ОШИБКА Отсутствуют сравниваемые значения для графика '… tg)
    local dt = b.datetime
    if dt.year == 2025 and dt.month == 2 and dt.day == 9 and dt.hour >= 9 then
        message(dt.day… '.' ..  dt.month… '.'… dt.year… ' '… dt.hour… ':'… dt.min)
        message('r='… tg… '  c='… c… '  rate='… rate)
    end
    local rm
    if p >= MEDIUM then
        rm = p
    elseif k <= MEDIUM then
        rm = -k
    else
        rm = 0
    end
    if rm < 0 then
        rate = rate — #TGS32 — 1
    end
    return rate
end
------------------------------------------------------------------
local getCBar = getConcBar  — Для чтения параллельной свечи сравниваемого инструмента

— Выборка лучшего инструмента по RSI (*_h4_r)
local TGS = { 'Si_h4_r', 'CR_h4_r', 'Eu_h4_r', 'GL_h4_r', 'BR_h4_r', 'NA_h4_r', 'RI_h4_r', 'MM_h4_r', 'NG_h4_r', 'GD_h4_r'
, 'SV_h4_r', 'SF_h4_r', 'ED_h4_r', 'SR_h4_r', 'GZ_h4_r', 'RN_h4_r', 'SN_h4_r', 'LK_h4_r', 'VB_h4_r', 'YD_h4_r'
, 'TI_h4_r', 'MN_h4_r', 'ME_h4_r', 'GK_h4_r', 'TT_h4_r', 'AL_h4_r', 'PZ_h4_r', 'AF_h4_r', 'NK_h4_r', 'AK_h4_r'
, 'MT_h4_r', 'PI_h4_r', 'CH_h4_r', 'VK_h4_r' }
local MEDIUM = 17
function best34(id, tag, line, index)
    local tg = tag… '_r'
    local n = getNumCandles(tg)
    if n == 0 then
        message('best34() ОШИБКА Отсутствует график '… tg, 3)
        return 0
    end
    local b, n = getCandlesByIndex(tg, line, index — 1, 1)
    if n ~= 1 then
        message('best34() ОШИБКА Отсутствует значение №'… index… ' графика '… tg, 3)
        return 0
    end
    b = b[0]
    local c = b.close
    local rate = 1
    local exists
    local k = 0  — Счетчик падающих инструментов (нужен для отрицательного rate)
    if c < 50 then
        k = k + 1
    end
    index = index — 1
    for _, tg1 in pairs(TGS) do
        if tg1 and tg1 ~= tg then
            local b1 = getCBar(tag, index, tg1, line)
            if b1 then
                local c1 = b1.close
                if c1 < 50 then
                    k = k + 1
                end
                exists = true
                if c > c1 then
                    rate = rate + 1
                end
            end
        end
    end
    assert(exists, 'best34() ОШИБКА Отсутствуют сравниваемые значения для графика '… tg)
    if k > MEDIUM then
        rate = rate — #TGS — 1
    end
    return rate
end
avatar
cern, Я кажется понял. Вы к уже существующему примеру в дистрибутиве best32 добавили здешний пример с best34 ). Поэтому дублирование. 

Сейчас есть новая версия LbotTest 10.05.04  Убрал лишние примеры ). Но чтобы заработал код выше с best34 надеюсь понимаете, что в QUIK нужно создать графики для всех 34х инструментов. Задать им идентификаторы, как в примере типа Si_h4_r,… Придется поработать )

Сделал скриншот, как задаются идентификаторы в QUIK. 
avatar
Хоббит, 
Сейчас есть новая версия LbotTest 10.05.04  Убрал лишние примеры ). Но чтобы заработал код выше с best34 надеюсь понимаете, что в QUIK нужно создать графики для всех 34х инструментов. Задать им идентификаторы, как в примере типа Si_h4_r,… Придется поработать )
непонял как в моем посте выше Выборка лучшего инструмента по RSI и каждый инструмент в код написать после RSI?
avatar

cern, Имя файла должно совпадать с идентификатором стратегии. Например:
Если стратегия BRA1 то в настройках TradesFile должен быть BRA1.csv

Если TradesFile не задан, по умолчанию предполагается что стратегия BRT1
[BRT1]
Security = 
WorkSize = 

avatar
avatar

теги блога Хоббит

....все тэги



UPDONW
Новый дизайн