Добрый день. Уже много лет я пользуюсь кодом для расчёта среднего спреда по акции:
Код
--Внутри колбека OnParam
--Считаем средний спред
local tablebid = getParamEx(class_code, fff, "bid") --получаем таблицу "bid"
local bid=tablebid.param_value --из таблицы берём значение
local tableoffer = getParamEx(class_code, fff, "offer") --получаем таблицу "offer"
local offer=tableoffer.param_value --из таблицы берём значение
local p_spread = (offer - bid) / bid * 100 --считаем текущий спред по бумаге
local elem = average_spreads[fff] --создаём таблицу для хранения расчётных данных
if elem == nil then
average_spreads[fff] = { Count = 1, Spread = p_spread, Avr = p_spread}
elem = average_spreads[fff]
else
elem.Spread = p_spread
elem.Avr = (elem.Avr * elem.Count + p_spread) / (elem.Count + 1)
elem.Count = elem.Count + 1
spread[fff]=elem.Avr
end
Его написал один добрый человек на каком-то трейдерском форуме по Луа. Уже не помню где и кто. Этот код считает средний спред безостановочно по всем значениям, даже если их будет миллион. Подскажите пожалуйста как видоизменить код, чтобы он считал спред за последние 1000 значений? Мне приходит на ум только создание таблицы с 1000 полями, которые будут обновляться, а старые (ненужные) удаляться. Может быть есть более красивый вариант? Благодарю за советы
Добрый день. Прошу дать совет. У меня два робота. У каждого своя стратегия, но каждому из них нужен минутный график фьючерса РТС. Оба робота подписываются на него с помощью колбека.
Код
function DataSource(class,sec,interval)
ds[sec] = CreateDataSource(class,sec,interval)
ds[sec]:SetUpdateCallback(function(...) mycallbackforallstocks(class,sec,...) end)
return ds[sec]
end
Индикаторы у них разные, поэтому расчёт индикаторов не дублируется. Тем не менее вопрос: не происходит ли двойной работы по получению графика? Если да, то как этого избежать? И стоит ли этого избегать (может быть это маленькая нагрузка, которую можно игнорировать). Спасибо
Все инструменты ликвидные. Например SBER (TQBR) Нилом являются ВСЕ значения внутри цикла for i=1,candles do (я специально это проверил). То есть функция не выдаёт ни одного значения. ---Вот код. Может быть он какой-то устаревший по сравнению с версией терминала.
Код
Settings = {
Name = "*ATR (Average True Range)",
Period = 14,
line = {{
Name = "Horizontal line",
Type = TYPE_LINE,
Color = RGB(140, 140, 140)
},
{
Width = 3,
Name = "ATR_Up",
Type = TYPE_HISTOGRAM,
Color = RGB(0, 206, 0)
},
{
Width = 3,
Name = "ATR_Down",
Type = TYPE_HISTOGRAM,
Color = RGB(221, 44, 44)
}
},
Round = "off",
Multiply = 1,
Horizontal_line="0"
}
local prev=0
function Init()
func = ATR()
return #Settings.line
end
function OnCalculate(Index)
local Out = ConvertValue(Settings, func(Index, Settings))
local HL = tonumber(Settings.Horizontal_line)
if Index == 1 then prev=0 end
if Out then
if Out > prev then
prev = Out
return HL,Out,nil
else
prev = Out
return HL,nil,Out
end
else
return HL,nil,nil
end
end
function ATR() --Average True Range ("ATR")
local f_TR = TR()
local tmp = {pp=nil, p=nil}
local it = {p=0, l=0}
return function (I, Fsettings, ds)
local Fsettings=(Fsettings or {})
local P = (Fsettings.Period or 14)
if (P > 0) then
if I == 1 then
tmp = {pp=nil, p=nil}
it = {p=0, l=0}
end
local tr = f_TR(I,ds)
if CandleExist(I,ds) then
if I~=it.p then
it = {p=I, l=it.l+1}
tmp.pp = tmp.p
end
if it.l < P then
tmp.p = (tmp.pp or 0) + tr
elseif it.l == P then
tmp.p = ((tmp.pp or 0) + tr) / P
return tmp.p
elseif it.l > P then
tmp.p = ((tmp.pp or 0) * (P-1) + tr) / P
return tmp.p
end
end
end
return nil
end
end
function TR() --True Range ("TR")
local it = {pp=0, p=0, l=0}
return function (I, ds)
if I == 1 then
it = {pp=0, p=0, l=0}
end
if CandleExist(I,ds) then
if I~=it.p then it={pp=it.p, p=I, l=it.l+1} end
if it.l == 1 then
return math.abs(GetValueEX(it.p,DIFFERENCE, ds))
else
return math.max(math.abs(GetValueEX(it.p,DIFFERENCE, ds)),
math.abs(GetValueEX(it.p,HIGH,ds) - GetValueEX(it.pp,CLOSE,ds)),
math.abs(GetValueEX(it.pp,CLOSE,ds)-GetValueEX(it.p,LOW,ds)))
end
end
return nil
end
end
SMA,MMA,EMA,WMA,SMMA,VMA = "SMA","MMA","EMA","WMA","SMMA","VMA"
OPEN,HIGH,LOW,CLOSE,VOLUME,MEDIAN,TYPICAL,WEIGHTED,DIFFERENCE,ANY = "O","H","L","C","V","M","T","W","D","A"
function CandleExist(I,ds)
return (type(C)=="function" and C(I)~=nil) or
(type(ds)=="table" and (ds[I]~=nil or (type(ds.Size)=="function" and (I>0) and (I<=ds:Size()))))
end
function Squeeze(I,P)
return math.fmod(I-1,P+1)
end
function ConvertValue(T,...)
local function r(V, R)
if R and string.upper(R)== "ON" then R=0 end
if V and tonumber(R) then
if V >= 0 then return math.floor(V * 10^R + 0.5) / 10^R
else return math.ceil(V * 10^R - 0.5) / 10^R end
else return V end
end
if arg.n > 0 then
for i = 1, arg.n do
arg[i]=arg[i] and r(arg[i] * ((T and T.Multiply) or 1), (T and T.Round) or "off")
end
return unpack(arg)
else return nil end
end
function GetValueEX(I,VT,ds)
VT=(VT and string.upper(string.sub(VT,1,1))) or ANY
if VT == OPEN then --Open
return (O and O(I)) or (ds and ds:O(I))
elseif VT == HIGH then --High
return (H and H(I)) or (ds and ds:H(I))
elseif VT == LOW then --Low
return (L and L(I)) or (ds and ds:L(I))
elseif VT == CLOSE then --Close
return (C and C(I)) or (ds and ds:C(I))
elseif VT == VOLUME then --Volume
return (V and V(I)) or (ds and ds:V(I))
elseif VT == MEDIAN then --Median
return ((GetValueEX(I,HIGH,ds) + GetValueEX(I,LOW,ds)) / 2)
elseif VT == TYPICAL then --Typical
return ((GetValueEX(I,MEDIAN,ds) * 2 + GetValueEX(I,CLOSE,ds))/3)
elseif VT == WEIGHTED then --Weighted
return ((GetValueEX(I,TYPICAL,ds) * 3 + GetValueEX(I,OPEN,ds))/4)
elseif VT == DIFFERENCE then --Difference
return (GetValueEX(I,HIGH,ds) - GetValueEX(I,LOW,ds))
else --Any
return (ds and ds[I])
end
return nil
end
dofile ("C:\\Program Files\\Lua\\5.1\\lua\\ATR.lua")--ATR
ds_v = CreateDataSource(instr[k].v_class,instr[k].v_sec,INTERVAL_D1)
ds_v:SetEmptyCallback()
sleep(1000)
func2 = ATR()
candles=ds_v:Size() --до этого места доходит и выдаёт количество свечей
long_v={}
short_v={}
for i=1,candles do
long_v[i]=func2(candles, {Period=252}, ds_v)
short_v[i]=func2(candles, {Period=5}, ds_v)
end
Сергей, спасибо за ответ. К примеру, речь идёт про акции Сбербанка. Мне постоянно нужны минутки, робот с ними работает весь день. А дневки нужны единожды в день. Минутки заказываются так:
Код
function mycallbackforallstocks(class,sec,index)
end
function DataSource(class,sec,interval)
ds[sec] = CreateDataSource(class,sec,interval)
ds[sec]:SetUpdateCallback(function(...) mycallbackforallstocks(class,sec,...) end)
return ds[sec]
end
Два вопроса: 1. Если я воспользуюсь ds[sec]:Close(), чтобы отписаться от дневок, он случайно не отпишет меня от минуток? 2. По графику, где мне нужны раз в день дневки, я заказываю минутки на постоянной основе. Будет ли КВИК делать лишнюю работу, если я буду всё время обновлять дневки (не отпишусь от них), или в данном случае этой лишней работой можно пренебречь?
Добрый день. Для расчёта одного параметра мне нужно раз в сутки заказать дневной график и посчитать по нему новое значение индикатора. В дальнейшем в течение дня график уже не нужен. Раз в день и всё. Два вопроса. 1. Как это сделать с помощью OnCleanUp()? Этот колбек для меня самый подходящий в данной ситуации. Но в момент его срабатывания графики ещё не транслируются, слишком рано. Может быть есть другой кол-бек, который сработает 1 раз в день? Мне не важно когда получать, утром, днём или вечером. Это погоды не делает. 2. Какие функции работы с графиками следует использовать, чтобы не подписываться на обновления, а 1 раз в день получить новые данные? Достаточно ли будет одного CreateDataSource(class,sec,interval)? Или всё же нужно использовать что-то ещё: SetEmptyCallback например? Я (увы) не сильно понимаю механику этих функций. Дайте пожалуйста подсказки. Заранее спасибо.
Прошу подсказать. У меня с программированием дела плохи. Еле-еле освоил Луа. Учить другие языки нет сил. Скорости КВИКа для отправки заявок мне уже не хватает. Нужен полноценный HFT. Возможно ли реализовать прямой доступ на биржу так чтобы у меня визуально всё осталось по старому: КВИК и Луа? Спасибо за ответ.
Добрый день. Из-за чего может быть такая ошибка? Unknown error. Possible unhandles exeption.
Даже не знаю какие сообщить подробности. Роботы уже давно работают и проверены на баги. Ничего необычного не происходило. Чик - и выбило скрипт с этим мессиджем.
Господа, прошу подсказать какой вариант экономнее 1 вариант
Код
func = RSI()
local rsi_count={}
for i=1,ds[бумага]:Size() do
rsi_count[i]=func(i, {Period=rsi_period, VType="Typical"}, ds[бумага])
end
rsi0=rsi_count[ds[бумага]:Size()]
rsi1=rsi_count[ds[бумага]:Size()-1]
rsi2=rsi_count[ds[бумага]:Size()-2]
или тоже самое, но с переменной num_candles
Код
func = RSI()
local rsi_count={}
num_candles=ds[бумага]:Size()
for i=1,num_candles do
rsi_count[i]=func(i, {Period=rsi_period, VType="Typical"}, ds[бумага])
end
rsi0=rsi_count[num_candles]
rsi1=rsi_count[num_candles-1]
rsi2=rsi_count[num_candles-2]
по сути, вопрос такой: тяжело ли компьютеру несколько раз получать значение ds[бумага]:Size()? введение переменной ускорит ли процесс?
В начале я инициализирую переменные. Это не быстрый процесс, потому что некоторые из них рассчитываются. В колбеке ОнПарам используется часть этих переменных. Если на момент срабатывания ОнПарам какая-то из переменных ещё равна нил, то скрипт выдаст ошибку. Скрипт от этого не выбивается. В дальнейшем, когда переменные досчитаются, эта ошибка появляться уже не будет. Как запустить ОнПарам не сразу, а (допустим) через секунду после запуска скрипта? Или запустить его после того как будут заданы все переменные? Спасибо.
Господа, мне нужно удалить элементы таблицы. Дайте пожалуйста совет как это лучше сделать. Вот код:
Код
s={}
for i=1,10 do
table.insert(s,random_max()) --в будущем вместо рандомных чисел будут номера активных заявок.
end
toLog(log, s)
for i=1,#s do
if i>0 then -- Сейчас я написал условие наугад. В будущем будет условие "удалить номер заявки из таблицы s если эта заявка снята или исполнена"
table.remove(s,i)
end
end
toLog(log, s)
фунция random_max() выдаёт случайные числа. Данные лога: таблица s ДО цикла с удалением: 1=941155680;2=1786453605;3=956870528;4=1765415360;5=1701888568;6=1306467983;7=1934583353;8=1639998291;9=498667592;10=1625028150;
таблица s ПОСЛЕ цикла с удалением: 1=1786453605;2=1765415360;3=1306467983;4=1639998291;5=1625028150;
Кхе-кхе. Я ждал, что она полностью удалится, но этого не произошло. Более того, элементы сдвинулись. Объясните пожалуйста почему так происходит и как мне удалять из таблицы нужные мне элементы.
Спасибо. То что хотел сделать, получилось. --- Подскажите пожалуйста, ваш индикатор Фракталы ( с учётом обновления) по прежнему не работает с CreateDataSourse? только с графиком?
Sergey Gorokhov написал: Let_it_go , 1) В QUIK нельзя накладывать индикаторы на индикаторы. 2) только если формулы обоих индикаторов уместить в одном скрипте. Или делать через привязку к идентификатору (см функцию getCandlesByIndex)
Сергей, спасибо за ответ. Не сердитесь пожалуйста за то что спрашиваю элементарные вещи. Вот график в КВИКе. Это индикатор
Он строится через добавление на график данных из такой таблицы:
function OnCalculate (index)
year=T(index).year
month=T(index).month
if month<10 then month="0"..month end
day=T(index).day
if day<10 then day="0"..day end
YYYYMMDD=tonumber(year..month..day.."")
v={}
v.value1=(new_table[YYYYMMDD]+Settings.aspect1)*Settings.multiplier
if v.value1<0 then v.value1=(new_table[YYYYMMDD]+Settings.aspect1)*Settings.multiplier end
return v.value1
end
Я хочу воспользоваться вашим индикатором FRACTALS и наложить на этот график верхние и нижние фракталы. Подскажите пожалуйста с чего начать. Что будет источником ds в функции GetValue(I,VT,ds). Заранее спасибо за ответ, и вообще спасибо за ваш труд!
Господа, прошу рассказать про функцию PrintDbgStr Вот я установил программу DebugView. Смотрю на экран. Если взять пример из мануала, то всё работает как задумано:
Код
PrintDbgStr("dbg from " ..getScriptPath())
Но теперь я осознанно допущу ошибку:
Код
a=step*1
PrintDbgStr("dbg from " ..a)
step=getParamEx("SPBFUT","MXZ7","SEC_PRICE_STEP").param_value
пользуюсь переменной step до того как ей присвоено значение (частая ошибка у кодеров-самоучек, а я самоучка) Жду от этой функции что она мне что то покажет, но она молчит. Прошу помощи и реальных примеров того как эта функция может быть полезной
В какой момент Вы хотите проверять состояние заявки? В момент ее получения (т.е. при вызове onOrder()) или просто когда Вы ее достаете из хранилища функцией getItem()?
Добрый день. Снятая заявка имеет параметр flags=30 Исполненная заявка имеет параметр flags=28
Как узнать флаги других состояний заявки? Например частично снятая? Из мануала ничего не понятно.
Второй вопрос. У снятой заявки запись такая withdraw_datetime={week_day=4;hour=11;ms=507;mcs=507000;day=2;month=11;sec=8;year=2017;min=58;} У заявки, которая не снималась, эта запись такая: withdraw_datetime={week_day=1;hour=0;ms=0;mcs=0;day=1;month=1;sec=0;year=1601;min=0;}
является ли это исчерпывающей характеристикой снятости-неснятости? ну например если hour==0, то заявка НЕ снятая, а если hour>0, то она снята? Спасибо.
Добрый день. Я уже давно пользуюсь этими индикаторами, но у меня старая редакция. После выхода новой редакции я пытался их применить, у меня пошли ошибки, я бросил это дело и вернулся на старую редакцию INDICATORS.ZIP --- Сейчас сижу разбираюсь с новой версией. Ошибка выскакивает даже на том примере, который приведён в файле readme
Код
dofile(getScriptPath().."\\MA.lua")
function main()
func = MA()
t_id = AllocTable()
AddColumn(t_id,1,"Price",true,QTABLE_INT_TYPE,10)
AddColumn(t_id,2,"MA",true,QTABLE_INT_TYPE,10)
CreateWindow(t_id)
SetWindowCaption(t_id,"MA")
ds = CreateDataSource("TQBR", "LKOH", INTERVAL_M5)
sleep(1000)
message (""..ds:Size(),1)
for i=1,ds:Size() do
ma_out=func(i, {Period=3, Metod = EMA, VType=ANY}, ds)
tmp=InsertRow(t_id,-1)
SetCell(t_id,tmp,1,tostring(ds:C(i)),ds:C(i))
SetCell(t_id,tmp,2,tostring(ma_out),ma_out)
end
end
выскакивает message 3106 (количество свечек), а потом ошибка:
Господа, прошу прощения за тупенький вопрос. Я не силён в математике, функциях и других простых вещах школьной программы. Я хочу задать такую зависимость: чем выше стандартное отклонение, тем шире будут полосы Боллинджера. Сейчас у меня запрограммировано так:
if sd[sec]>200 then otklonenie=5 end if sd[sec]>50 then otklonenie=4 end if sd[sec]>0 then otklonenie=3 end
То есть если рынок волатильный, то в Боллинджер будет заряжено больше стандартных отклонение (его ширина будет выше), а если рынок дохлый, то полосы будут более узкими. Я хочу, чтобы у меня были промежуточные значения. Ну например при sd[sec]==70 у меня ширина полос боллинджера будет 4,35 стандартных отклонений. Помогите это выразить через функцию. otklonenie= .......sd[sec]......... Я понимаю, что ответ может быть разным. Линия может иметь разную кривизну, но мне нужна хоть какая то стартовая подсказка, чтобы понять как это выразить математически. Спасибо заранее за помощь.
function QTable:SetValue(row, col_name, data, formatted)
-- Установить значение в ячейке
local col_ind = self.columns[col_name].id or nil
if col_ind == nil then
return false
end
local col_type = self.columns[col_name].c_type
if row==nil or col_name==nil or data==nil or col_ind==nil then
message ((row or "nil").." | "..(col_name or "nil").." | "..(data or "nil").." | "..(col_ind or "nil")),1)
end
if self.data[row][col_ind]==data then return true end
self.data[row][col_ind]=data
local col_type = self.columns[col_name].c_type
-- если для числового столбца дано НЕчисловое значение, то применяем к нему tonumber
if type(data) ~= "number" and (col_type==QTABLE_INT_TYPE or col_type==QTABLE_DOUBLE_TYPE or col_type==QTABLE_INT64_TYPE) then
data = tonumber(data) or 0
end
-- если для НЕстрокового значения уже дан отформатированный вариант, то сначала используется он
if formatted and col_type~=QTABLE_STRING_TYPE and col_type~=QTABLE_CACHED_STRING_TYPE then
return SetCell(self.t_id, row, col_ind, formatted, data)
end
-- если для столбца задана функция форматирования, то она используется
local ff = self.columns[col_name].format_function
if type(ff) == "function" then
-- в качестве строкового представления используется
-- результат выполнения функции форматирования
if col_type==QTABLE_STRING_TYPE or col_type==QTABLE_CACHED_STRING_TYPE then
return SetCell(self.t_id, row, col_ind, ff(data))
else
return SetCell(self.t_id, row, col_ind, ff(data), data)
end
else
if col_type==QTABLE_STRING_TYPE or col_type==QTABLE_CACHED_STRING_TYPE then
return SetCell(self.t_id, row, col_ind, tostring(data))
else
return SetCell(self.t_id, row, col_ind, tostring(data), data)
end
end
end
Она служит мне верой и правдой. Но иногда она выдает такую ошибку: Это он ругается на строчку if self.data[row][col_ind]==data then return true end Внутри колбека это не приводит к выбиванию скрипта, поэтому я продолжаю работать с ней. Важно: эта ошибка приходит в первую секунду после запуска, когда какое то значение ещё не определено. Потом всё работает хорошо. Вы можете видеть, что я пытаюсь отловить эту ошибку через message, но он спокойно проходит эту проверку. Подскажите где искать иэтот "нил"
Верхний график делится математически на средний график. В итоге получается красный индикатор (внизу). Теперь я хочу работать с нижним красным графиком и наложить на него другой индикатор, ну (для примера) Боллинджер. 1. Можно ли это сделать средствами КВИКа. Взять и "бросить" на этот график стандартный индикатор из списка? 2. Если нет, то как на этот график наложить индикаторы Сергея Горохова из файла INDICATORS.zip? Спасибо за подсказки.
function Sbor()
local order_line
count=0
for t=getNumberOf("orders"),1,-1 do
order_line=getItem("orders",t-1)
if order_line.class_code=="SPBFUT" then
count=count+1
end
end
orders=count
local deal_line
count=0
comiss=0
for t=getNumberOf("trades"),1,-1 do
trade_line=getItem("trades",t-1)
if trade_line.class_code=="SPBFUT" then
comiss=comiss+(trade_line.exchange_comission*0.5)
count=count+1
end
end
trades=count
sbor=0.1*(orders*2-comiss*40)
tbl:SetValue(1,'sbor', orders.." | "..trades.." | "..comiss.." | "..sbor)
return orders,trades,comiss
end
и Вы не ответили на второй вопрос. Зачем идет умножение количества сделок на сумму комиссий?
k – балл для транзакции, определенный в соответствии с таблицей 1; f – сбор, уплаченный за заключение сделки; l – балл для сделки, определенный в соответствии с таблицей 1.
параметр f я беру из таблицы моих сделок (столбец комиссия) и умножаю его на величину l l=количество сделок умноженное на балл. В моём случае балл равен 40.
Sergey Gorokhov написал: Let_it_go , Тогда ищите ошибку в формуле. почему у Вас идет умножение на два? И почему Вы умножаете комиссию на количество сделок? в формуле на которую Вы указали этого нет.
умножение на два потому что транзакцией считается выставление-снятие. Поэтому я количество заявок умножаю на два.
Господа, добрый день. Я запустил робота с большим количеством транзакций на срочном рынке. Я думал, что верно интерпретировал формулу подсчёта сбора за транзакции. Полагал, что у меня достаточное количество сделок, чтобы не попадать на этот штраф. Но всё же биржа мне начислила этот штраф: 2300 рублей. Прошу подсказать где ошибка в моём коде. По сути формула изложена в самом конце кода: sbor=0.1*(orders*2*1-(trades*comiss*40)) Вот так она выглядит на сайте биржи http://www.moex.com/a3825 Спасибо за помощь.
Код
function Sbor()
local order_line
count=0
for t=getNumberOf("orders"),1,-1 do
order_line=getItem("orders",t-1)
if order_line.class_code=="SPBFUT" then
count=count+1
end
end
orders=count
local deal_line
count=0
comiss=0
for t=getNumberOf("trades"),1,-1 do
trade_line=getItem("trades",t-1)
if trade_line.class_code=="SPBFUT" then
comiss=comiss+trade_line.exchange_comission
count=count+1
end
end
trades=count
sbor=0.1*(orders*2*1-(trades*comiss*40))
tbl:SetValue(1,'sbor', orders.." | "..trades.." | "..comiss.." | "..sbor)
return orders,trades,comiss
end
Добрый день. Прошу помочь с реализацией простого замысла.
1. Выставляется лимитная заявка. 2. Эта лимитная заявка снимается. 3. Выставить новую лимитную заявку как только приедет кол-бек OnOrder о снятии предыдущей заявки.
Параллельно торгуют другие роботы, они выставляют много заявок. Задача работать именно с нужной мне заявкой. Я не могу понять как её "пометить" в момент отправки, чтобы ждать колбека именно по ней. Спасибо.
Добрый день. Прошу подсказать как сделать перебор валютных тикеров. Для перебора акций я делаю так:
Код
ticker_list="SVAV,TGKO,TRCN,UWGN,VSMO"
for sec in string.gmatch(ticker_list,"%a+") do
---
end
Для перебора фьючерсов так:
Код
f_ticker_list="GMZ7,GZZ7,HYZ7,EuZ7,LKZ7,MMZ7,MXZ7,RNZ7,SiZ7,SPZ7,SRZ7,VBZ7"
for sec in string.gmatch(f_ticker_list,"%w+") do
---
end
А как перебрать валюты? ticker_list="USD000UTS_TOM,CNYRUB_TOM,EUR_RUB_TOM" (доллар, юань, евро). Вся проблема в нижнем пробеле Заранее спасибо за подсказки.