Есть простой индикатор по типу parabolic. на вход принимает свечи и отдает свой результат в виде цены. function Oncalculate(idx) — блаблабла return psar end
Хочу без добавления индикатора на график, добавить его код в свой скрипт и скормить ему 20-30 последних свечей и получить ответы.
Подскажите, как можно реализовать? Понимаю, что нужно делать через CreateDataSource, но навыка не хватает.
Пользователь
Сообщений: Регистрация: 15.06.2023
30.03.2025 21:24:19
Вот ссылка на индикатор, , на что нужно обратить внимание, это в анонимной функции на передачу ds! ds = CreateDataSource, то есть мы передаем методы на которые подписаны, это делается именно для того, чтоб без извинений и переделок можно было индикатор использовать в алгоритмах, через наследование.
Пользователь
Сообщений: Регистрация: 30.03.2025
30.03.2025 21:36:19
Цитата
VPM написал: Вот ссылка на индикатор, , на что нужно обратить внимание, это в анонимной функции на передачу ds! ds = CreateDataSource, то есть мы передаем методы на которые подписаны, это делается именно для того, чтоб без извинений и переделок можно было индикатор использовать в алгоритмах, через наследование.
Чисто не понял вас:) По ссылке по-моему вообще о другом пишут.
Пользователь
Сообщений: Регистрация: 30.03.2025
30.03.2025 21:38:42
VPM, или может я не правильно высказался. Мне по сути нужно поменять Oncalculate() индикатора на function psar() и подать уже на эту функцию свечи.
Пользователь
Сообщений: Регистрация: 15.06.2023
30.03.2025 22:21:17
Посмотрите это пример, он рабочий подставите свои данный и по выводите.
Код
-- Global settings
local CLASS_CODE = "SPBFUT" -- Код класса инструмента
local SEC_CODE = "CRM5" -- Код инструмента
local TRADE_ACCOUNT = "" -- Счет
local LOTS = 1 -- Количество лотов
-- Инициализация данных
local dailyTrend = "none"
local entryPrice = 0
local sl = 0
local tp = 0
-- Функция получения свечей
function getCandles(ds, count)
local candles = {}
local size = ds:Size()
--message( 'size = ' .. size)
for i = 0, count do
if size >= 1 then
candles[i] = {
high = ds:H(size - i) or 0,
low = ds:L(size - i) or 0,
open = ds:O(size - i) or 0,
close = ds:C(size - i) or 0
}
end
end
return candles
end
-- Расчет ATR
function calculateATR(ds, period)
local sumTR = 0
local size = ds:Size()
for i = 1, period do
local prevClose = ds:C(size - i + 1) or 0
local high = ds:H(size - i) or 0
local low = ds:L(size - i) or 0
local TR = math.max(
high - low,
math.abs(high - prevClose),
math.abs(low - prevClose)
)
sumTR = sumTR + TR
end
return sumTR / period
end
-- Функция определения тренда на D1
function getDailyTrend()
local ds_D1 = CreateDataSource(CLASS_CODE, SEC_CODE, INTERVAL_D1)
local dailyCandles = getCandles(ds_D1, 4)
local atr = calculateATR(ds_D1, 4)
local o0 = dailyCandles[0].open or ds_D1:O(ds_D1:Size()) or 0
--message('o0 = ' .. tostring(o0) .. '; dailyCandles[0].open = ' .. tostring(dailyCandles[0].open) )
local HH = math.max(
dailyCandles[1].high,
dailyCandles[2].high,
dailyCandles[3].high
)
local LL = math.min(
dailyCandles[1].low,
dailyCandles[2].low,
dailyCandles[3].low
)
local trend = "range"
if o0 > HH then trend = "uptrend"
elseif o0 < LL then trend = "downtrend"
else trend = "range"
end
local daily = {['trend'] = trend,
['hh'] = HH,
['ll'] = LL,
['o'] = o0,
['atr'] = atr,
['targetB'] = atr + o0,
['targetS'] = o0 - atr
}
return daily
end
-- Проверка структуры на H1
function checkH1Structure(trend)
local ds_H1 = CreateDataSource(CLASS_CODE, SEC_CODE, INTERVAL_H1)
local h1Candles = getCandles(ds_H1, 5)
local atr = calculateATR(ds_H1, 14)
local currentClose = ds_H1:C(ds_H1:Size()) or 0
if trend == "uptrend" then
return h1Candles[1].high > h1Candles[2].high and
h1Candles[2].low < h1Candles[3].low and
currentClose > h1Candles[1].high
elseif trend == "downtrend" then
return h1Candles[1].low < h1Candles[2].low and
h1Candles[2].high > h1Candles[3].high and
currentClose < h1Candles[1].low
end
return false
end
-- Поиск точки входа на M1
function checkM1Entry(trend)
local ds_M1 = CreateDataSource(CLASS_CODE, SEC_CODE, INTERVAL_M1)
local m1Candles = getCandles(ds_M1, 20)
local atr = calculateATR(ds_M1, 14)
local currentClose = ds_M1:C(ds_M1:Size()) or 0
if trend == "uptrend" then
if currentClose > m1Candles[0].high then
entryPrice = currentClose
sl = entryPrice - atr
tp = entryPrice + 2*atr
return true
end
elseif trend == "downtrend" then
if currentClose < m1Candles[0].low then
entryPrice = currentClose
sl = entryPrice + atr
tp = entryPrice - 2*atr
return true
end
end
return false
end
-- Отправка ордера
function placeOrder(direction)
local transaction = {
ACTION = "NEW_ORDER",
CLASSCODE = CLASS_CODE,
SECCODE = SEC_CODE,
ACCOUNT = TRADE_ACCOUNT,
OPERATION = direction == "uptrend" and "B" or "S",
PRICE = entryPrice,
QUANTITY = tostring(LOTS),
STOPPRICE = tostring(sl),
EXPIRY = "GTC"
}
local res = SendTransaction(transaction)
if res then
message("Order placed: "..direction..
" | Price: "..entryPrice..
" | SL: "..sl..
" | TP: "..tp)
else
message("Error placing order!")
end
end
local is_run
function OnInit()
--strategy = Strategy:new(config)
--strategy:start()
is_run = true
message('Стратегия 3ТФ инициализирована')
end
function OnStop()
--strategy:stop()
is_run = false
message('Стратегия 3ТФ остановлена')
end
function OnTransReply(r)
--strategy:handle_transaction_reply(r)
end
function OnTrade(t)
--strategy:handle_trade(t)
end
function OnOrder(o)
--strategy:handle_order(o)
end
-- Основной обработчик
function main()
message('Стратегия 3ТФ запущена')
local daily = getDailyTrend()
message( 'Стратегия 3ТФ dailyTrend = ' .. tostring(daily.trend)
..'; HH = '.. tostring(daily.hh)
..'; LL = '.. tostring(daily.ll)
..'; o0 = '.. tostring(daily.o)
..'; atr = '.. tostring(daily.atr)
..'; targetB = '.. tostring(daily.targetB)
..'; targetS = '.. tostring(daily.targetS)
)
daily.trend = "uptrend"
while is_run do
if daily.trend ~= "range" then
if checkH1Structure(daily.trend) then
if checkM1Entry(daily.trend) then
placeOrder(daily.trend)
end
end
end
sleep(1000)
end
end
Пользователь
Сообщений: Регистрация: 30.03.2025
30.03.2025 23:00:32
Все равно мало что понял. Код индикатора:
Код
function OnCalculate(idx)
if idx<Settings.Period_ATR then
return nil
else
if idx==Settings.Period_ATR then
psar={}
psar[idx]=L(idx)
long=true
hmax=H(idx)
per_ATR=Settings.Period_ATR
local TR=0
for js=(idx-per_ATR+1),idx do
TR=(TR+H(js)-L(js))
end
Old_ATR=TR/per_ATR
revers=true
else
if idx~=old_idx then
local TR=0
for js=(idx-per_ATR+1),idx do
TR=(TR+H(js)-L(js))
end
local ATR=TR/per_ATR
af=ATR/(Old_ATR+ATR)
af=af/10
Old_ATR=ATR
if long then
if hmax<H(idx-1) then
hmax=H(idx-1)
end
psar[idx]=psar[idx-1]+af*(hmax-psar[idx-1])
end
if short then
if lmin>L(idx-1) then
lmin=L(idx-1)
end
psar[idx]=psar[idx-1]+af*(lmin-psar[idx-1])
end
revers=true
end
if long and L(idx)<psar[idx] and revers then
psar[idx]=hmax
short=true
long=false
lmin=L(idx)
af=Step
revers=false
end
if short and H(idx)>psar[idx] and revers then
psar[idx]=lmin
long=true
short=false
hmax=H(idx)
af=Step
revers=false
end
end
old_idx=idx
return psar[idx]
end
Дальше я хочу поместить его в свой скрипт, чтобы получилось примерно следующее:
Код
function OnInit()
function PSAR(idx)
if idx<Settings.Period_ATR then
return nil
else
if idx==Settings.Period_ATR then
psar={}
psar[idx]=L(idx)
long=true
hmax=H(idx)
per_ATR=Settings.Period_ATR
local TR=0
for js=(idx-per_ATR+1),idx do
TR=(TR+H(js)-L(js))
end
Old_ATR=TR/per_ATR
revers=true
else
if idx~=old_idx then
local TR=0
for js=(idx-per_ATR+1),idx do
TR=(TR+H(js)-L(js))
end
local ATR=TR/per_ATR
af=ATR/(Old_ATR+ATR)
af=af/10
Old_ATR=ATR
if long then
if hmax<H(idx-1) then
hmax=H(idx-1)
end
psar[idx]=psar[idx-1]+af*(hmax-psar[idx-1])
end
if short then
if lmin>L(idx-1) then
lmin=L(idx-1)
end
psar[idx]=psar[idx-1]+af*(lmin-psar[idx-1])
end
revers=true
end
if long and L(idx)<psar[idx] and revers then
psar[idx]=hmax
short=true
long=false
lmin=L(idx)
af=Step
revers=false
end
if short and H(idx)>psar[idx] and revers then
psar[idx]=lmin
long=true
short=false
hmax=H(idx)
af=Step
revers=false
end
end
old_idx=idx
return psar[idx]
end
end
function main()
function getCandles(ds, count) local candles = {}
local size = ds:Size()
for i = 0, count do
if size >= 1 then
candles[i] = {
high = ds:H(size - i) or 0,
low = ds:L(size - i) or 0,
open = ds:O(size - i) or 0,
close = ds:C(size - i) or 0
}
end
end
return candles
end
local ds_H1 = CreateDataSource(CLASS_CODE, SEC_CODE, INTERVAL_H1)
local h1Candles = getCandles(ds_H1, 5)
psar=PSAR(h1Candles) --только в цикле по количеству свечей
end
Но, не понимаю как передавать свечи в функцию индикатора, да еще и в цикле получается.
Пользователь
Сообщений: Регистрация: 30.03.2025
30.03.2025 23:11:27
Вот сделал так, но естественно получаю ошибку(test2.lua:9: attempt to compare nil with number):
Код
function OnInit()
Period_ATR=14
old_idx=0
long=false
short=false
revers=false
function PSAR(idx)
if idx<Period_ATR then
return nil
else
if idx==Period_ATR then
psar={}
psar[idx]=L(idx)
long=true
hmax=H(idx)
per_ATR=Period_ATR
local TR=0
for js=(idx-per_ATR+1),idx do
TR=(TR+H(js)-L(js))
end
Old_ATR=TR/per_ATR
revers=true
else
if idx~=old_idx then
local TR=0
for js=(idx-per_ATR+1),idx do
TR=(TR+H(js)-L(js))
end
local ATR=TR/per_ATR
af=ATR/(Old_ATR+ATR)
af=af/10
Old_ATR=ATR
if long then
if hmax<H(idx-1) then
hmax=H(idx-1)
end
psar[idx]=psar[idx-1]+af*(hmax-psar[idx-1])
end
if short then
if lmin>L(idx-1) then
lmin=L(idx-1)
end
psar[idx]=psar[idx-1]+af*(lmin-psar[idx-1])
end
revers=true
end
if long and L(idx)<psar[idx] and revers then
psar[idx]=hmax
short=true
long=false
lmin=L(idx)
af=Step
revers=false
end
if short and H(idx)>psar[idx] and revers then
psar[idx]=lmin
long=true
short=false
hmax=H(idx)
af=Step
revers=false
end
end
old_idx=idx
return psar[idx]
end
end
end
function getCandles(ds, count)
local candles = {}
local size = ds:Size()
for i = 0, count do
if size >= 1 then
candles[i] = {
high = ds:H(size - i) or 0,
low = ds:L(size - i) or 0,
open = ds:O(size - i) or 0,
close = ds:C(size - i) or 0
}
end
end
return candles
end
function main()
ds = CreateDataSource('TQBR', 'SBER', INTERVAL_M5)
local Candles = getCandles(ds, 20)
for i=1,ds:Size() do
message("Значение индикатора:" ..tostring(PSAR()))
end
end
Пользователь
Сообщений: Регистрация: 15.06.2023
30.03.2025 23:26:52
Ну хорошо давайте, по порядку. OnCalculate(idx) это функция обратного вызова, специальная для создания индикаторов, все что она делает передает idx. Передавать ее некуда нельзя. Но можно вынести из нее алгоритм расчет в отдельную функцию PSAR(idx) и уже работать с ней. Вот для индикатора. function OnCalculate(idx) PSAR(idx) end Для работы с ней в main(), нужно подписаться на источник вот этот момент из моего примера.
local ds_D1 = CreateDataSource(CLASS_CODE, SEC_CODE, INTERVAL_D1) затем передаем, а здесь получаем свечи local dailyCandles = getCandles(ds_D1, 4) далее расчет индикатора local atr = calculateATR(ds_D1, 4)
Вместо расчета АТР поставьте расчет собственного по аналогии. Удачи.
Пользователь
Сообщений: Регистрация: 15.06.2023
30.03.2025 23:32:04
Добавьте в пример вот эту функцию получения цены.
local string_upper,string_sub=string.upper,string.sub; function Value(I,VType,ds)
local Out = nil local VType=(VType and string_upper(string_sub(VType,1,1))) or "A" if VType == "O" then --Open Out = (O and O(I)) or (ds and ds:O(I)) elseif VType == "H" then --High Out = (H and H(I)) or (ds and ds:H(I)) elseif VType == "L" then --Low Out = (L and L(I)) or (ds and ds:L(I)) elseif VType == "C" then --Close Out = (C and C(I)) or (ds and ds:C(I)) elseif VType == "V" then --Volume Out = (V and V(I)) or (ds and ds:V(I)) elseif VType == "X" then --DateTime Out = (T and T(I)) or (ds and ds:T(I)) elseif VType == "A" then --Any if ds then Out = ds[I] end end return Out end
Пользователь
Сообщений: Регистрация: 15.06.2023
30.03.2025 23:35:48
В OnInit() нельзя расчеты вести, она запускается до создания потока в main и предназначена для инициализации!