Перейти к содержимому
  • 0
DESHLI

QUIK: Как обьединить двух торговых роботов?

Вопрос

У меня есть два торговых робота: один "Судак", а другой "Xsar"

Вот их код:

Судак:

Скрытый текст

-- ************************ СЕКЦИЯ ОБЩИХ ПАРАМЕТРОВ ****************************
CLIENT_CODE = "D62154" -- общий код для акций и фьючерсов.
LogFileName = "c:\\SudakTudak\\short\\sudaktudak_log.txt" -- Технический лог.
ParamPath = "c:\\SudakTudak\\short\\" -- здесь хранятся файлики с параметрами. Три файла на каждый инструкмент.
SdelkaLog = "c:\\SudakTudak\\short\\sudaktudak_sdelki.txt" -- Лог сделок. Сюда пишутся ТОЛЬКО сделки.
SleepDuration = 20; -- отдыхаем 10 секунд. Слишком часто не надо молотить.
DemoMode = false -- Включите, чтобы начать "боевые" сделки. Если = false, сделок не будет, просто запишет в лог.

-- ************************ СЕКЦИЯ МАССИВОВ ДЛЯ ИНСТРУМЕНТОВ ************************
aTickerList = {"MMM0"}; -- сюда массив инструментов. Не забывайте перекладывать фьючерсы!!!
-- А при перекладывании фьючерсов не забывайте менять код как здесь, так и в следующих массивах.

-- Следующие массивы должны иметь значения для каждого инструмента из aTickerList
aClassCode = {MMM0="SPBFUT"} -- TQBR для акций, SPBFUT для фьючерсов.
aAccountCode = {MMM0="SPBFUT00Q00"} -- может отличаться для акций и фьючерсов.
aLotSize = {MMM0=1}; -- Массив лотов для покупки.
aProskalzivanie = {MMM0=0.05}; -- Проскальзывание при сделке.
maxVolume = -8; -- Сколько всего можем набрать
aCurrentPrice = 0;
aHour_1 = 10; -- Час начала работы
aHour_2 = 23; -- Час конца работы
aMinutes_1 = 00; -- Минуты начала работы
fibo = 0.1; -- Множитель увеличения каждого уровня усреднения
fiboStepSize = {MMM0=20}; -- Размер уровня для усреднения по каждому инструменту
full_flat_size = false; -- false - торговля между средней и крайней линией ББ, true - между крайними линиями

is_run=true

function main()
    while is_run do
        curr_date=os.date("*t")
        if (curr_date["hour"]>=aHour_1 and curr_date["min"]>aMinutes_1) and curr_date["hour"]<aHour_2 then
            for k,v in pairs(aTickerList) do
                Obrabotka(v,k);
            end
        end;
        sleep(SleepDuration*1000) -- Отдыхаем SleepDuration секунд.
    end
end

function GetLastPrice(TickerName, CandleType)
    -- Берем цену из графика. CreateDataSource пока не используем, т.к. при необходимости модификации
    -- алгоритма, хотим легко добавлять индикаторы.
    -- Плюс меньше зависим от коннекта - графики всегда с нами.
    local NL=getNumCandles(TickerName.."_Price_Sudak")
    
    tL, nL, lL = getCandlesByIndex (TickerName.."_Price_Sudak", 0, NL-1, 1) -- last свеча
    if tL ~= nil then
        if tL[0] ~= nil then
            if CandleType=="LOW" then
                aCurrentPrice=tL[0].low -- получили текущую цену (ЦПС)
            elseif CandleType=="OPEN" then
                aCurrentPrice=tL[0].open
            elseif CandleType=="HIGH" then 
                aCurrentPrice=tL[0].high
            else 
                aCurrentPrice=tL[0].close
            end
        end;
    end;
    return aCurrentPrice
end

function GetXsar(TickerName, LineCode)
    -- получаем текущие значения Боллинлжера.
    -- LineCode может иметь значения: "High", "Middle", "Low"
    local NbbL=getNumCandles(TickerName.."_BB_Sudak")
    tbbL, nbbL, lbbL = getCandlesByIndex (TickerName.."_BB_Sudak", 0, NbbL-1, 1)  -- last свеча, средняя линия Боллинджера
    iBB_Local_Middle = tbbL[0].close -- тек значение средней BB Local
    tbbL, nbbL, lbbL = getCandlesByIndex (TickerName.."_BB_Sudak", 1, NbbL-1, 1)  -- last свеча, верхняя линия Боллинджера
    iBB_Local_High = tbbL[0].close -- тек значение верхней BB Local
    tbbL, nbbL, lbbL = getCandlesByIndex (TickerName.."_BB_Sudak", 2, NbbL-1, 1)  -- last свеча, нижняя линия Боллинджера
    iBB_Local_Low = tbbL[0].close -- тек значение нижней BB Local
    if LineCode == "High" then return iBB_Local_High end
    if LineCode == "Middle" then return iBB_Local_Middle end
    if LineCode == "Low" then return iBB_Local_Low end
end

function PriceCrossMAToUp(TickerName)
    -- Функция возвращает TRUE, если пересекли среднюю линию Боллинджера снизу вверх
    if GetLastPrice(TickerName, "OPEN")<GetBollinger(TickerName, "Middle")
        and GetLastPrice(TickerName, "LAST")>GetBollinger(TickerName, "Middle")
    then return true
    else return false
    end;
end

function PriceCrossMAToDown(TickerName)
    -- Функция возвращает TRUE, если пересекли среднюю линию Боллинджера снизу вверх
    if GetLastPrice(TickerName, "OPEN")>GetBollinger(TickerName, "Middle")
        and GetLastPrice(TickerName, "LAST")<GetBollinger(TickerName, "Middle")
    then return true
    else return false
    end;
end

function PriceEnterToBollingerFromDown(TickerName)
    -- Функция возвращает TRUE, если пересекли нижнюю линию Боллинджера снизу вверх
    -- (то есть вошли внутрь канала Боллинджера снизу).
    if GetLastPrice(TickerName, "LOW")<GetBollinger(TickerName, "Low")
        and GetLastPrice(TickerName, "LAST")>GetBollinger(TickerName, "Low")
    then return true
    else return false
    end;
end

function PriceEnterToBollingerFromUp(TickerName)
    -- Функция возвращает TRUE, если пересекли верхнюю линию Боллинджера сверху вниз
    -- (то есть вошли внутрь канала Боллинджера сверху).
    if GetLastPrice(TickerName, "HIGH")>GetBollinger(TickerName, "High")
        and GetLastPrice(TickerName, "LAST")<GetBollinger(TickerName, "High")
    then return true
    else return false
    end;
end

--Функция Обработки отдельного тикера
function Obrabotka(sTickerName, sNum)
    -- Теперь откываем файлы с нашим тикетом, и читаем оттуда значения: LastPrice, LastDirection, Объем Позы.
    LastPrice = tonumber(GetValueFromFile(ParamPath..sTickerName.."_LastPrice.txt"));
    LastDirection = GetValueFromFile(ParamPath..sTickerName.."_LastDirection.txt");
    iVolume = tonumber(GetValueFromFile(ParamPath..sTickerName.."_Volume.txt"));
    local CurrentPrice=GetLastPrice(sTickerName) -- вытаскиваем из графика текущую цену.
    local profit = tonumber(GetValueFromFile(ParamPath..sTickerName.."_Profit.txt"));
    -- Логируем текущую и последнюю цены и разницу между ними.
    WLOG(sTickerName.. " Current="..CurrentPrice.. " Last="..LastPrice.." Razn = "..((CurrentPrice-LastPrice) - (CurrentPrice-LastPrice)%aProskalzivanie[sTickerName]).. " FlatBBSize = "..FiboSize(sTickerName).." Vol="..iVolume.." LastDir="..LastDirection.." FiboStepSize = "..FiboLevels(sTickerName).." Profit="..profit);
  
    -- Теперь проверяем, не надо ли докупиться или скинуть шорт
    if (((CurrentPrice<LastPrice-FiboSize(sTickerName) and LastDirection=="S") or (CurrentPrice<LastPrice-FiboLevels(sTickerName))) and iVolume<0) and PriceEnterToBollingerFromDown(sTickerName) then
        -- Покупаем или Начинаем
        if LastDirection=="S" then
            WLOG("BUY PROFIT "..(LastPrice-CurrentPrice));
            SetValueToFile(ParamPath..sTickerName.."_Profit.txt", profit + LastPrice-CurrentPrice) -- начислили прибыль и записали.
        else
            WLOG("BUY PROFIT NEW LEVEL"..(LastPrice-CurrentPrice));
            SetValueToFile(ParamPath..sTickerName.."_Profit.txt", profit + LastPrice-CurrentPrice) -- начислили прибыль и записали.
        end
        if (DoFire(sTickerName, "B", CurrentPrice) == "") then
            iVolume = tonumber(iVolume)+aLotSize[sTickerName];
            SetValueToFile(ParamPath..sTickerName.."_LastPrice.txt", CurrentPrice)
            SetValueToFile(ParamPath..sTickerName.."_LastDirection.txt", "B")
            SetValueToFile(ParamPath..sTickerName.."_Volume.txt", tostring(iVolume))
            LastPrice = CurrentPrice; -- чтобы не продать сразу на следующем условии.
        end
    end
    
    -- Теперь проверяем, не надо ли еще продать? Если поза нуль, то набираем в шорт
    if (((CurrentPrice>LastPrice+FiboLevels(sTickerName)) or (CurrentPrice>LastPrice+FiboSize(sTickerName) and LastDirection=="B")) or iVolume==0) and PriceEnterToBollingerFromUp(sTickerName) and iVolume>maxVolume then
        -- Продаем
        if LastDirection=="B" then
            WLOG("SELL AGAIN");
        else
            WLOG("SELL NEW LEVEL");
        end
        if (DoFire(sTickerName, "S", CurrentPrice) == "") then
            iVolume = tonumber(iVolume)-aLotSize[sTickerName];
            SetValueToFile(ParamPath..sTickerName.."_LastPrice.txt", CurrentPrice)
            SetValueToFile(ParamPath..sTickerName.."_LastDirection.txt", "S")
            SetValueToFile(ParamPath..sTickerName.."_Volume.txt", tostring(iVolume))
        end
    end
end;

function FiboLevels (sTickerName) -- Функция увеличения размера уровня в зависимости от позиции по тикеру
  local Volume = math.abs(iVolume / aLotSize[sTickerName]); 
  local iniStepSize = fiboStepSize[sTickerName];
  local newStepSize = iniStepSize + (iniStepSize * fibo)^Volume;
  return newStepSize;
end;  

function FiboSize (sTickerName) -- Функция определение ширины между линиями Боллинжера
 local CurrentPrice=GetLastPrice(sTickerName, "OPEN") -- вытаскиваем из графика текущую цену.
 local Size=0

    if full_flat_size then        
        Size = math.floor (GetBollinger(sTickerName, "High") - GetBollinger(sTickerName, "Low"))
        return Size
    end
    
    if CurrentPrice >= GetBollinger(sTickerName, "Middle") and full_flat_size==false then
        Size = math.floor (GetBollinger(sTickerName, "High") - GetBollinger(sTickerName, "Middle"))
        return Size
    else
        Size = math.floor (GetBollinger(sTickerName, "Middle") - GetBollinger(sTickerName, "Low"))
        return Size
    end
end;

function DoFire(SEC_CODE, p_dir, p_price) -- Функция - СДЕЛКА ПО РЫНКУ!
    if p_dir == "B" then AAA = 1 else AAA = -1 end
    t = {
            ["CLASSCODE"]=aClassCode[SEC_CODE],
            ["SECCODE"]=SEC_CODE,
            ["ACTION"]="NEW_ORDER", -- новая сделка.
            ["ACCOUNT"]=aAccountCode[SEC_CODE],
            ["CLIENT_CODE"]=CLIENT_CODE,
            ["TYPE"]="L", -- "M" "L". По M давал ошибку на TQBR.
            ["OPERATION"]=p_dir, -- направление сделки, "B" или "S"
            ["QUANTITY"]=tostring(aLotSize[SEC_CODE]), -- объем, (акции - в лотах, а не штуках).
            ["PRICE"]=tostring(p_price+(aProskalzivanie[SEC_CODE]*AAA)), -- цену лимитки ставим для мгновенного исполнения.
            ["TRANS_ID"]="1"
        }
    
    if DemoMode==false then -- Если всё по серьезному, то ...
        res1 = sendTransaction(t) -- ... передаем сделку по рынку.
    end
    
    if (res1~="") then -- Ошибочка вышла. Логируем ошибку.
        WLOG("SendTransaction Error = "..res1);
    end
    
    local l_file1=io.open(SdelkaLog, "a")
    l_file1:write(os.date()..";SECCODE="..SEC_CODE..";PRICE="..p_price..";DIR="..p_dir.."\n")
    l_file1:close()
        
    return res1
end

function GetValueFromFile(FileName) -- Читаем параметр из файла.
    local f = io.open(FileName, "r");
    if f == nil then -- если файла нет, но создаем пустой.
        f = io.open(FileName,"w");
        DefaultValueForFile = "0" -- по умолчанию пишем нуль.
        -- Для LastDirection надо бы писать не нуль, а "B", но пусть будет нуль, т.к.
        -- этого условия достаточно для открытия начальной сделки.
        f:write(DefaultValueForFile)
        f:close();
        -- Открывает уже гарантированно существующий файл в режиме "чтения/записи"
        f = io.open(FileName, "r");
    end;
    aValue = f:read("*l")
    f:close()
    return aValue
end

function SetValueToFile(FileName, aValue) -- Пишем параметр в файл.
    local ff=io.open(FileName, "w") -- используем "w", а не "a", чтобы перезаписать существующий.
    ff:write(aValue)
    ff:close()
end

function OnStop(stop_flag)
    is_run=false
end

function WLOG(st) -- Универсальная функция записи в лог.
    local l_file=io.open(LogFileName, "a") -- используем "a", чтобы добавить новую строку.
    l_file:write(os.date().." "..st.."\n")
    l_file:close()
end

 

Xsar:

Скрытый текст

Settings = 
{
    Name = "xSarATR",

    periodATR =21,
    
    ddd=3,
    line=
    {
        {
            Name = "xSarATR",
            Color = RGB(0, 192, 0),
            Type = TYPET_BAR,
            Width = 2
        },
        {
            Name = "xSarATR",
            Color = RGB(192, 0, 0),
            Type =  TYPET_BAR,
            Width = 2
        }
    ,
        {
            Name = "xSarATR",
            Color = RGB(0, 0, 255),
            Type = TYPE_TRIANGLE_UP,
            Width = 3
        }
    ,
        {
            Name = "xSarATR",
            Color = RGB(255, 0, 0),
            Type = TYPE_TRIANGLE_DOWN,
            Width = 3
        }
    }
}

---------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------
function cached_SAR()
    local cache_H={}
    local cache_L={}
    local cache_SAR={}
    local cache_ST={}
    local AMA2={}
    local CC={}
    local CC_N={}    
    local ATR={}
    
    return function(ind,  _p4,_ddd)

        local index = ind
        local ZZZ = 0
                
        if index == 1 then
            cache_H={}
            cache_L={}
            cache_SAR={}
            cache_ST={}
            AMA2={}
            CC={}
            CC_N={}
            ATR={}

------------------
            CC[index]=C(index)
            CC_N[index]=(C(index)+H(index)+L(index))/3
            cache_H[index]=H(index)
            cache_L[index]=L(index)
            cache_SAR[index]=L(index)-2*(H(index)-L(index))
            AMA2[index]=(C(index)+O(index))/2
            cache_ST[index]=1
            ATR[index]=math.abs(H(index)-L(index))
            return nil
        end
        ------------------------------
            ZZZ=math.max(math.abs(H(index)-L(index)),math.abs(H(index)-C(index-1)),math.abs(L(index)-C(index-1)))
            ATR[index]=(ATR[index-1]*(_p4-1)+ZZZ)/_p4
            cache_SAR[index]=cache_SAR[index-1]
            CC[index]=C(index)
            AMA2[index]=(2/(_p4/2+1))*CC[index]+(1-2/(_p4/2+1))*AMA2[index-1]
            CC_N[index]=(C(index)-AMA2[index])/2+AMA2[index]
            cache_ST[index]=cache_ST[index-1]
            cache_H[index]=cache_H[index-1] 
            cache_L[index]=cache_L[index-1]
---------------------------------------------------------------------------------------
        if index ==2 then
            return nil
        end
----------------------------------------------------------------------        
        if cache_ST[index]==1 then
                
                if cache_H[index] < CC[index] then 
                    cache_H[index]=CC[index]
                end
            cache_SAR[index]=math.max((cache_H[index]-ATR[index]*_ddd),cache_SAR[index-1])
            if (cache_SAR[index] > CC_N[index])and(cache_SAR[index] > C(index)) then 
                cache_ST[index]=0
                cache_L[index]=CC[index]
                cache_SAR[index]=cache_L[index]+ATR[index]*_ddd*1
                return  nil,cache_SAR[index],  nil,cache_H[index]-ATR[index]*_ddd
            end
            return cache_SAR[index], nil, nil, nil
        end
---------------------------------------------------------------------------------------
        if cache_ST[index]==0 then
                if cache_L[index] > CC[index] then 
                    cache_L[index]=CC[index]
                end
            cache_SAR[index]=math.min((cache_L[index]+ATR[index]*_ddd),cache_SAR[index-1])
            if (cache_SAR[index] < CC_N[index])and (cache_SAR[index] < C(index)) then 
                cache_ST[index]=1
                cache_H[index]=CC[index]
                cache_SAR[index]=cache_H[index]-ATR[index]*_ddd*1
                return cache_SAR[index], nil, cache_L[index]+ATR[index]*_ddd,nil
            end
        return nil,cache_SAR[index],  nil, nil
        end


    end
end
---------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------

function Init()
    mySAR = cached_SAR()
    return 4
end
function OnCalculate(index)
    return mySAR(index, Settings.periodATR,Settings.ddd)
end

 

 

Изменено пользователем eu_tomat
спойлеры, оформление кода

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Рекомендуемые сообщения

@DESHLI Просьба на будущее:

  1. Любой код оформлять соответствующим тегом.
  2. Длинный код прятать спойлер.

И текущая просьба: проверить, правильно ли я перенёс код в теги, не потерялось ли там что-нибудь.

 

2 часа назад, serafim сказал:

биржа что ли ? https://smart-lab.ru/blog/534384.php

@DESHLI Кстати, предупреждать надо. У нас на форуме словосочетание "торговый робот" несёт иной смысл.

 

3 часа назад, DESHLI сказал:

У меня есть два торговых робота: один "Судак", а другой "Xsar"

В какой среде эти роботы запускаются?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
41 минуту назад, eu_tomat сказал:

В какой среде эти роботы запускаются?

полагаю терминал QUIK (Quickly Updatable Information Kit) - программа, предназначенная для просмотра и анализа биржевых котировок, а также осуществления сделок.

Скрипты QLua (Lua,  интерпретированный под QUIK)

Как по мне какая то дичь :blink:

Изменено пользователем serafim

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
2 часа назад, DESHLI сказал:

Как обьединить двух торговых роботов?

Какой смысл вкладывается в слово "объединить"? Есть два робота, иногда они могут принимать сходные решения по сделкам, а иногда противоположные. Что требуется делать в каждом конкретном случае?

 

Применительно к биржевым роботам, код – это последнее, чем стоит заниматься. Какой язык там будет использоваться в конечном итоге: QLua или MQL – не играет значительной роли. Сначала следует проработать логику. Нужно описать логику одного робота, затем описать логику другого, и в конце концов определить логику взаимодействия этих двух роботов. И тогда уже станет понятно, как объединить код этих двух роботов.

 

@DESHLI Опиши своими словами, как каждый из этих двух роботов принимает решение о времени и размере сделки. А потом вместе подумаем, как можно объединить их логику. И если получится что-то внятное, то и про код подумаем.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Автор куда-то пропал, и спросить теперь не у кого. Но чтобы не вводить в заблуждение наших читателей, припишу "QUIK" к его вопросам. И на другой его вопрос о торговых роботах оставлю ссылку.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Язык Lua

 

И один из них робот а другой код это индикатор, и то есть мне надо как-то сделать так, чтобы этот робот работал по индикаторе. То-есть Судак должен работпть по индикатору Xsar

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
20 минут назад, DESHLI сказал:

Язык Lua

Ещё раз спрошу: в какой среде должен исполняться этот код на языке Lua?

 

22 минуты назад, DESHLI сказал:

И один из них робот а другой код это индикатор, и то есть мне надо как-то сделать так, чтобы этот робот работал по индикаторе. То-есть Судак должен работпть по индикатору Xsar

Хорошо, Xsrar это индикатор. Объясни теперь, что показывает индикатор Xsar, и по какому алгоритму торгует Судак.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.


×
×
  • Создать...