Блог им. kurd
Settings = {
Name = "_Zigzag"
,Delta = 0.0 -- Абсолютная сила тренда в пунктах
,Rate = 0.001 -- Относительная сила тренда
,Log = "D:\\Zigzag.csv" -- лог-файл сделок
,line = {
{Name = "Zigzag"
,Color = RGB(255,255,0) -- Жёлтый
,Type = TYPE_LINE
,Width = 1}
,{Name = "Lwr"
,Color = RGB (255,255,0) -- Жёлтый
,Type = TYPE_TRIANGLE_DOWN
,Width = 1}
,{Name = "Upr"
,Color = RGB (0,128,255) -- Тёмно-Голубой
,Type = TYPE_TRIANGLE_UP
,Width = 1}
} -- BadConditions, CurSize, ClosePrev = nil
} -- DataSourceInfo, Xtrs, DummyXtr, ScanNo = nil
function Msg()
return Title() .." - параметры:"
.."\nDelta - абсолютная сила тренда или 0;"
.."\nRate - относительная сила тренда или 0."
.."\nLog - лог-файл сделок на поворотах."
.."\nСила - минимальный уход от экстремума"
.."\n для смены направления тренда."
end
---------
function AddXtr (bar) -- bar > #Xtrs[n].FinIdx
-- Код возврата: 0 - ничего не изменилось;
-- ±2 - замена последнего экстремума более сильным;
-- ±3 - добавление экстремума, противоположного.
local node = Xtrs[#Xtrs]
local xtr = node.FinVal
if Trend() > 0 then
local mn = Lwr (xtr)
if L(bar) <= mn then
node = NewNode (bar, L(bar))
node.IniVal = math.min (mn, O(bar))
table.insert (Xtrs, node)
return -3
end
if H(bar) >= node.FinVal then
node.FinIdx = bar
node.FinVal = H(bar)
return 2
end
else -- Trend() < 0
local mx = Upr (xtr)
if H(bar) >= mx then
node = NewNode (bar, H(bar))
node.IniVal = math.max (mx, O(bar))
table.insert (Xtrs, node)
return 3
end
if L(bar) <= node.FinVal then
node.FinIdx = bar
node.FinVal = L(bar)
return -2
end
end -- if Trend() > 0
return 0
end -- AddXtr()
function DateAndTime (i) -- в баре котировок
return DataSourceInfo and DataSourceInfo.interval < 0 and
string.format ("%04d.%02d.%02d", T(i).year, T(i).month, T(i).day) or
string.format ("%04d.%02d.%02d %02d:%02d"
,T(i).year, T(i).month, T(i).day, T(i).hour, T(i).min)
end
function Lwr (val)
return Settings.Delta > 0 and val - Settings.Delta or
val * (1 - Settings.Rate)
end
function MakeDummy() -- Вместо нового экстремума или усиления старого
local dummyXtr = NewNode ( Size(), C(Size()))
SetValue (Size(), 1, C(Size()))
local finVal = Xtrs[#Xtrs].FinVal -- IniVal - будущий поворот
dummyXtr.IniVal = Trend() > 0 and Lwr (finVal) or Upr (finVal)
SetValue (Size(), Trend() > 0 and 2 or 3, dummyXtr.IniVal)
return dummyXtr
end
function NewNode (bar, val)
return { IniIdx = bar, IniVal = val, FinIdx = bar, FinVal = val }
end
function Title()
return getDataSourceInfo().sec_code .."[".. Settings.Name .."]"
end
function ToString (no) -- параметры узла Зигзага
if not no then no = #Xtrs end
return "Xtrs[".. no .."]"
.."\nIni ".. DateAndTime (Xtrs[no].IniIdx)
.." ".. Xtrs[no].IniVal
.."\nFin ".. DateAndTime (Xtrs[no].FinIdx)
.." ".. Xtrs[no].FinVal
end
function Trend (idx)
idx = idx or #Xtrs
return Xtrs[idx].IniVal > Xtrs[idx-1].FinVal and 1 or -1
end
function Upr (val)
return Settings.Delta > 0 and val + Settings.Delta or
val * (1 + Settings.Rate)
end
------------
function Init()
message (Msg())
return #Settings.line
end
function OnCalculate (index)
if BadConditions then return nil end
if index == 1 then
ScanNo = not ScanNo and 1 or ScanNo + 1
if not Sub1() then -- BadConditions, CurSize, Xtrs,
return nil -- DummyXtr, DataSourceInfo
end
elseif C(index) == ClosePrev then
elseif index >= CurSize then
Sub2()
end
ClosePrev = C(index)
return GetValue (index, 1), GetValue (index, 2), GetValue (index, 3)
end -- OnCalculate()
function OnChangeSettings()
BadConditions, ClosePrev, DataSourceInfo, ScanNo = nil
if Settings.Delta > 0 and Settings.Rate > 0 or
Settings.Delta <= 0 and Settings.Rate <= 0 then
BadConditions = true
message (Msg())
end
end
function Sub1()
BadConditions = true
CurSize = Size()
for i = 1, 3 do SetRangeValue (i, 1, CurSize, nil) end
local validIdx = 1
while validIdx <= CurSize and not CandleExist (validIdx) do
validIdx = validIdx + 1
end
if validIdx >= CurSize then
message (Title() ..": мало данных 1.")
return false
end
local cur = {}
cur.Hi = H(validIdx); cur.Lo = L(validIdx)
cur.Mx = Upr (cur.Hi); cur.Mn = Lwr (cur.Lo)
local firstRegularIdx
local bar = validIdx + 1
while bar <= CurSize do
while not CandleExist (bar) or
H(bar) >= cur.Hi and L(bar) <= cur.Lo or
H(bar) < cur.Hi and L(bar) > cur.Lo do
if CandleExist (bar) then
cur.Hi = H(bar); cur.Lo = L(bar)
cur.Mx = math.max (cur.Mx, cur.Hi)
cur.Mn = math.min (cur.Mn, cur.Lo)
end
bar = bar + 1
if bar > CurSize then
message (Title() ..": мало данных 2.")
return false
end
end -- while not H(bar) or not L(bar)
Xtrs = {}
if H(bar) > cur.Mx then
table.insert (Xtrs, NewNode (validIdx, H(validIdx)))
table.insert (Xtrs, NewNode (bar, H(bar)))
firstRegularIdx = bar + 1
break
elseif L(bar) < cur.Mn then
table.insert (Xtrs, NewNode (validIdx, L(validIdx)))
table.insert (Xtrs, NewNode (bar, L(bar)))
firstRegularIdx = bar + 1
break
end
bar = bar + 1
end -- while bar <= CurSize
if not firstRegularIdx then
message (Title() ..": мало данных 3.")
return false
end
BadConditions = false
for bar = firstRegularIdx, CurSize do
if CandleExist (bar) then AddXtr (bar) end
end
DataSourceInfo = getDataSourceInfo ()
local logFile = ScanNo == 1 and io.open (Settings.Log, "w") or nil
if logFile then
local fmt = DataSourceInfo and DataSourceInfo.interval < 0 and
"%-10s; %-10s; %-10s; %-10s; %-8s; %-10s\n" or
"%-16s; %-10s; %-16s; %-10s; %-10s; %-12s\n"
logFile:write (string.format (fmt
,"nDateTime", "nPrice", "xDateTime", "xPrice", "profit", "equity"))
end
local ttl, n, d0, p0, d1, p1 = 0, #Xtrs
for i = 1, n do
SetValue (Xtrs[i].FinIdx, 1, Xtrs[i].FinVal)
if i > 2 then
d1 = DateAndTime (Xtrs[i].IniIdx)
if Trend(i) < 0 then
p1 = Xtrs[i].IniVal;
SetValue (Xtrs[i].IniIdx, 2, p1)
else
p1 = Xtrs[i].IniVal
SetValue (Xtrs[i].IniIdx, 3, p1); p1 = -p1
end
if i > 3 then
ttl = ttl + p0 + p1
if logFile then
logFile:write (
string.format ("%s; %10.2f; %10s; %10.2f; %10.2f; %12.2f\n"
,d0, p0, d1, p1, p0 + p1, ttl))
end
end
d0, p0 = d1, p1
end
end -- for i = 1, n
if logFile then logFile:close() end
local s = Title() .."\nttl "..
string.format ("%.2f", ttl) .."\n".. ToString()
local n1, n2, n3 = Xtrs[#Xtrs-2], Xtrs[#Xtrs-1], Xtrs[#Xtrs]
Xtrs = {}; Xtrs[1], Xtrs[2], Xtrs[3] = n1, n2, n3
DummyXtr = CurSize > n3.FinIdx and MakeDummy() or nil
if ScanNo == 1 then
message (s .."\nDummyXtr "..
(DummyXtr and DateAndTime (DummyXtr.FinIdx) or "not-not"))
end
return true
end -- Sub1()
function Sub2()
if DummyXtr then
SetValue (DummyXtr.FinIdx, 1, nil)
SetValue (DummyXtr.IniIdx, Trend() > 0 and 2 or 3, nil)
DummyXtr = nil
end
local n = #Xtrs
SetValue (Xtrs[n].FinIdx, 1, nil)
SetValue (Xtrs[n].IniIdx, Trend() < 0 and 2 or 3, nil)
table.remove (Xtrs)
CurSize = Xtrs[#Xtrs].FinIdx
while CurSize < Size() do
CurSize = CurSize + 1
AddXtr (CurSize)
end
for k = n, #Xtrs do
local node = Xtrs[k]
SetValue (node.FinIdx, 1, node.FinVal)
SetValue (node.IniIdx, Trend(k) < 0 and 2 or 3, node.IniVal)
end
if Xtrs[#Xtrs].FinIdx < Size() then
DummyXtr = MakeDummy()
end
end -- Sub2()
А объективно, он не лучше и не хуже стандартных скользяшек. Сам по себе не работает, нуждается в дополнительной обвязке, чтобы получить систему.
— Вы совершенно заблуждаетесь с этим выводом…
Ещё в 90 было много вариантов построения этого индикатора и попыток формализовать системы торговли на основе его… Простых решений, позволяющих получить нормальную статистику на выходе нет..
О некоторых вопросах по этому индикатору написано выше…