Спасибо за ответ. Вторая часть вопроса была немного неверно мной изложена. Дело обстоит так - мне надо принимать таблицу обезличенных сделок весь торговый день. Если при этом не предпринимать никаких мер, то файл принимает огромные размеры и начинает тормозить не только Эксель, но и комп. Поэтому я решил выйти из положения так - установить лимит выведенных по DDE строк и при его превышении сбрасывать таблицу на диск, очищать ее и затем продолжать вывод с первой строки. И так до окончания торгов. И если все обстоит так как Вы написали (т.е. Quik - клиент), то мне неясна реализация самого процесса вывода. Хотя, наверное, стоит почитать о том, как устанавливается связь и осуществляется вывод через по DDE с учетом того, что запросы шлет Квик, а обрабатывает и выполняет Эксель. Еще раз спасибо.
при выводе таблиц по DDE в Эксель Quik - это сервер или клиент? Если сервер, то где в Эксель находятся ячейки с DDE запросом и почему вывод таблицы обезличенных сделок идет построчно с записью в каждую новую строку?
Я так понимаю, что вывод организован через библиотеку DLL. Для ускорения работы по DDE в Эксель рекомендуют использовать метод SetLinkOnData, но в нем в качестве параметра нужно указать DDE-ссылку с запросом к серверу. В выводимой таблице я таких ссылок не обнаружил. Посему вопрос - где можно эту ссылку увидеть, в какую ячейку выводятся данные и как организовано копирование из одной ячейки в строки. Хотя бы в общих чертах, чтобы понять куда рыть. Заранее благодарен за ответ.
Для правильного расчета индикаторов мне необходимо посчитать количество свечек на графике. В моменты отсутствия торгов свечи на графике просто пропускаются. Возможно ли вывести на месте отсутствующих свечек цену закрытия сессии на каждой свече вплоть до начала следующей сессии. Если такой вопрос уже поднимался, прошу поделиться ссылкой.
Для построения нужно передвинуть метки за экстремумы. На скринах видно, что при построении тренда вверх все строится нормально, а при построении тренда вверх одно значение не верно и поэтому возникает зеленая линия, обведенная овалом.
package.path = package.path .. ";" .. getWorkingFolder() .."\\LuaIndicators\\modules\\" .. "?.lua";
require ("Format");
require ("Round");
--require ("Lines");
Settings={
Name = "0_11.03.2019",--имя индикатора
AssetID = "sber",--идентификатор иструмента
LabelName = "D:\метка.bmp",--имя файла метки "down-254095_1280_1",
stopper = false,--обработка на каждом тике
complect = 0,
-- начальное местоположение меток
fplace = 110,
splace = 8,
thplase = 30,
Yvalue = 0,
line=
{
{
Name = "ChTrnd",
Type =TYPE_LINE,
Width = 2,
Color = RGB(255,255, 0)
},
{
Name = "UpTrend",
Type =TYPE_LINE,
Width = 2,
Color = RGB(0,255,0)
},
{
Name = "DnTrend",
Type =TYPE_LINE,
Width = 2,
Color = RGB(0,255, 0)
},
{
Name = "APointLvl",
Type =TYPE_LINE,
Width = 1,
Color = RGB(120,90,140)
},
{
Name = "BPointLvl",
Type =TYPE_LINE,
Width = 1,
Color = RGB(120,90,140)
},
{
Name = "IntLine1",
Type =TYPE_LINE,
Width = 2,
Color = RGB(105,105,105)--DimGray
},
{
Name = "IntLine2",
Type =TYPE_LINE,
Width = 2,
Color = RGB(0,0,255)--Blue
},
{
Name = "IntLine3",
Type =TYPE_LINE,
Width = 2,
Color = RGB(218,165,32)--Goldenrod
},
{
Name = "IntLine4",
Type =TYPE_LINE,
Width = 2,
Color = RGB(255,0,0)--Red
},
{
Name = "IntLine5",
Type =TYPE_LINE,
Width = 2,
Color = RGB(0,128,0)--Green
},
{
Name = "IntLine6",
Type =TYPE_POINT,
Width = 1,
Color = RGB(255,0,0)--Red
},
{
Name = "fLine1",
Type =TYPE_LINE,
Width = 1,
Color = RGB(120,90,140)
},
{
Name = "fLine2",
Type =TYPE_LINE,
Width = 1,
Color = RGB(120,90,140)
},
{
Name = "ChannelFormingZone",
Type =TYPE_HISTOGRAM,
Width = 1,
Color = RGB(255,255, 0)
},
{
Name = "pLine1",
Type =TYPE_LINE,
Width = 1,
Color = RGB(120,90,140)
},
{
Name = "pLine2",
Type =TYPE_LINE,
Width = 1,
Color = RGB(120,90,140)
},
{
Name = "ChannelActionZone",
Type =TYPE_HISTOGRAM,
Width = 1,
Color = RGB(255,255, 0)
},
{
Name = "ChannelActionZone",
Type =TYPE_HISTOGRAM,
Width = 1,
Color = RGB(255,255, 0)
},
{
Name = "ExtLine1",
Type =TYPE_POINT,
Width = 1,
Color = RGB(0,255,255)--Aqua
},
{
Name = "ExtLine2",
Type =TYPE_POINT,
Width = 1,
Color = RGB(0,255,255)--Aqua
},
{
Name = "ExtLine3",
Type =TYPE_POINT,
Width = 1,
Color = RGB(0,255,255)--Aqua
},
{
Name = "ExtLine4",
Type =TYPE_POINT,
Width = 1,
Color = RGB(0,255,255)--Aqua
},
{
Name = "ExtLine5",
Type =TYPE_POINT,
Width = 1,
Color = RGB(0,255,255)--Aqua
},
{
Name = "ExtLine6",
Type =TYPE_POINT,
Width = 1,
Color = RGB(0,255,255)--Aqua
},
{
Name = "ChTrndExt",
Type =TYPE_POINT,
Width = 1,
Color = RGB(120,90,140)--Aqua
},
{
Name = "ChUpExt",
Type =TYPE_POINT,
Width = 1,
Color = RGB(120,90,140)--Aqua
},
{
Name = "ChDnExt",
Type =TYPE_POINT,
Width = 1,
Color = RGB(120,90,140)--Aqua
}
}
}
--глобальные переменные
-- Внимание, название всех параметров меток должны писаться большими буквами
label={
--TEXT="метка";
IMAGE_PATH=Settings.LabelName;
--TRANSPARENCY=50,
--TRANSPARENT_BACKGROUND=1,
--YVALUE=Settings.Yvalue,
DATE=0,
TIME=0;
--R=255;
--G=255;
--B=255;
}
t={}--исходная таблица данных свечек
tposixByTime={}--индексы свечек проиндексированные по времени
tposixOldLabelTime={}--таблица старых значений даты-времени выведенных на чарт меток в POSIX-формате
count=0--ограничитель количества циклов
Label_Id = {}--массив с идентификаторами меток
ScriptPath=""--полный путь к иконке (для метки)
flagIndExist=false--флаг существования индикатора на чарте
flagLblModify=false--флаг, сигнализирующий о перемещении метки
a=0
b=0
c=0
k=0
v=0
tDT={}
g_PriceStep = 0--шаг цены
g_PriceScale = 0--точность цены
g_tChLines={}
g_APointIdx=0;
g_BPointIdx=0;
g_APointLvl=0;
g_BPointLvl=0;
function Init()
--объявление переменных. Присваиваем начальные значения чтобы сразу типизировать.
local n = 0--количество строк в таблице (количество свечек)
local l = ""--подпись инструмента
local NmbrOfCandles = 0--номер последней справа свечи на графике
local CandleIndex = 0
--local tDT={}
---[[
if Settings.AssetID == "" then
message("Необходимо установить идентификатор инструмента")
end
--ScriptPath = getScriptPath().."\\"..Settings.LabelName; --путь к файлу метки
--message("Init_ScriptPath "..ScriptPath);
--label["IMAGE_PATH"]=ScriptPath;
--номер предпоследней справа свечи на графике. Нумерация начинается с нуля.
NmbrOfCandles = getNumCandles(Settings.AssetID)-2;
--получим таблицу с данными всех свечек, проиндексированную по номеру свечки
--t - таблица значений свечек
--n - количество полученных свечек(строк таблицы)
--l - подпись к инструменту
--текущую свечу не получаем
t,n,l = getCandlesByIndex(Settings.AssetID,0,0,NmbrOfCandles);
--[[
for i=0, NmbrOfCandles-1 do
message("Init_t "..tostring(t[i].high))
end
--]]
--получить индекс двух свечек, номера которых заданы во входных параметрах
--и установить метки
CandleIndex = NmbrOfCandles-Settings.splace;
tposixOldLabelTime["splace"], Label_Id["splace"] = PUTLABEL(CandleIndex);
CandleIndex = NmbrOfCandles-Settings.fplace;
tposixOldLabelTime["fplace"], Label_Id["fplace"] = PUTLABEL(CandleIndex);
CandleIndex = NmbrOfCandles-Settings.thplase;
tposixOldLabelTime["thplase"], Label_Id["thplase"] = PUTLABEL(CandleIndex);
--берем из таблицы только время-дату свечи
count = 0;
for k=0,NmbrOfCandles-1 do
count = count + 1; if count > 100000 then break end;
--преобразуем в POSIX-формат поле datetime
--транспонируем таблицу - индексируем по времени
tDT[k] = os.time (t[k].datetime);
tposixByTime[tDT[k]] = k;
end
--message("Init_tposixByTime_"..tostring(tDT[Size()-5]))--1539075600
--message("Init_tposixByTime_"..tostring(tDT[1550826000]))
return 27
end
function OnCalculate(index)
local info={}
local CandleIndex
local LblIdF = 0
local LblIdS = 0
local indput
local IndLines={}
local VChTrend
local VAPointLvl
local VBPointLvl
local flagIndChange=false--true-перестроить индикатор, false - не перестраивать
if index==1 then
info = getDataSourceInfo();
--шаг цены
g_PriceStep = getParamEx(info.class_code, info.sec_code, 'SEC_PRICE_STEP').param_value
--точность цены
g_PriceScale = getParamEx(info.class_code, info.sec_code, 'SEC_SCALE').param_value
end
---[[
--если флаг перемещения метки не установлен
if flagLblModify==false then
--message("OnCalculate_flagLblModify_"..tostring(flagLblModify))
--проверяем перемещалась ли первая метка
LblIdF=IsLabelChange("fplace");
--проверяем вторую метку
LblIdS=IsLabelChange("splace");
--если индикатор существует, то выходим без расчетов, возвращаем массив линий
if flagIndExist==true then
flagIndChange=false;
--иначе строим индикатор, возвращаем массив линий
else
flagIndChange=true;
end
--если метка перемещалась flagLblModify==true
else
--строим индикатор, выставляем флаг изменения индикатора,
--сбрасываем флаг метки, возвращаем индикатор
flagIndChange=true;
LblIdF=Label_Id.fplace
LblIdS=Label_Id.splace
end
--message("OnCalculate_flagLblModify_"..tostring(flagLblModify))
--message("OnCalculate_LblIdF_"..tostring(LblIdF).." "..tostring(LblIdS))
--рассчитываем индикатор или выводим линии без расчета
VChTrend,VChUp,VChDn,VAPointLvl,VBPointLvl,VIntLine1=PUTINDICATOR(LblIdF,LblIdS,flagIndChange,index)
--]]
if index==0 then VChTrend=nil end
if index >= Size() then VChTrend=nil end
return VChTrend,VChUp,VChDn,VAPointLvl,VBPointLvl,VIntLine1,VIntLine2,VIntLine3,VIntLine4,VIntLine5,VIntLine6,VfLine1,VfLine2,VFormingTime,VpLine1,VpLine2,VActionTme,VActionTime,VExtLine1,VExtLine2,VExtLine3,VExtLine4,VExtLine5,VExtLine6,VChTrndExt,VChUpExt,VChDnExt
end
function IsLabelChange(place)
--функция принимает индекс массива Label_Id("строка")
--устанавливает глобальные флаги и
--записывает posix-время в глобальную таблицу tposixOldLabelTime
--возврашает Id метки
local LblId = 0
local posixNewLblTime = 0
local posixOldLblTime = 0
--проверяем перемещалась ли метка
--считываем Id метки из массива
LblId = Label_Id[place];
--message("IsLabelChange_LabelId_"..tostring(place))
--считываем posix-время для этой метки
posixOldLblTime = tposixOldLabelTime[place];
--преобразуем время метки с чарта в posix-формат
--если функция вернула nil, время не меняем
if GETLABLEPOSIXTIME (LblId)== 0 then
posixNewLblTime = posixOldLblTime
else
posixNewLblTime = GETLABLEPOSIXTIME (LblId)
end
--если время не совпадает, выставляем флаги отсутствия индикатора и модификации метки
if posixOldLblTime ~= posixNewLblTime then
--выставляем флаги
--метка перемещена
flagLblModify=true;
--индикатор не построен
flagIndExist=false;
--изменяем время метки в массиве
tposixOldLabelTime[place] = posixNewLblTime;
end
return LblId
end
PUTLABEL = function (index)
--[[функция для установки метки на чарт по индексу
принимает индекс свечи
возвращает время метки в posix-формате и Id метки
входные параметры:
-индекс свечи
-строка с названием точки для индексации массива установленных меток
вызывается в Init()
--]]
local Date=""--дата свечки
local Time=""--время свечки
local Yvalue=0
LabelId=0
--message ("PUTLABEL_index.."..tostring(index));
--дата и время свечи
Date=t[index].datetime.year..Format.FTEXT(t[index].datetime.month)..Format.FTEXT(t[index].datetime.day); --дата
Time=Format.FTEXT(t[index].datetime.hour)..Format.FTEXT(t[index].datetime.min)..Format.FTEXT(t[index].datetime.sec);
--привязка к оси У
Yvalue = t[index].high;
--заполнить структуру данных метки
label={
IMAGE_PATH=Settings.LabelName;
YVALUE=Settings.Yvalue+Yvalue;
DATE=Date,
TIME=Time
};
--добавляем метку на чарт и запоминаем Id метки в массиве, проиндексированном принятой строкой
LabelId = AddLabel(Settings.AssetID,label);
--message ("PUTLABEL LabelId "..tostring(LabelId).." "..tostring(os.time (t[index].datetime)))
return os.time (t[index].datetime), LabelId
end
function GETLABLEPOSIXTIME (LabelId)
--функция принимает Id метки и возвращает ее время в posix-формате
--при перемещении метки временно Id может стать нулем, поэтому функция выдает ошибку ноль
local id--Id метки
local NewLbLParam = {}--структура параметров метки
local LabelTime = ""--строка с данными даты-времени метки
local dt={}--структура в формате datetime
local err=0
local retval
---[[
id = LabelId;
--message("GETLABLEPOSIXTIME_LabelId_"..tostring(LabelId))
--получаем таблицу с параметрами метки (в нижнем регистре в формате строки)
if LabelId==0 then retval=err
else
NewLbLParam = GetLabelParams(Settings.AssetID, id);
--преобразуем в формат datetime
dt.year,dt.month,dt.day = string.match(NewLbLParam.date,"(%d%d%d%d)(%d%d)(%d%d)")
if #NewLbLParam.time == 5 then
LabelTime="0".. NewLbLParam.time
else LabelTime="".. NewLbLParam.time
end
if tostring(NewLbLParam.time)=="0" then
LabelTime="000000"
end
dt.hour,dt.min,dt.sec = string.match(LabelTime,"(%d%d)(%d%d)(%d%d)");
--преобразуем в формат POSIX
retval = os.time (dt)
end;
return retval;
end
function PUTINDICATOR(LblId1,LblId2,boolIndChange,idx)
local a=0
local i
local j
local k
local TrndH=0
local TrndL=0
local h
local l
local CndlIdx1=0
local CndlIdx2=0
local tABIdx={}
local IntervalCndlNmbr=0
local koef
local p1koef
local p2koef
local tChTrend={}
local tChUp={}
local tChDn={}
local ChUpMax=0
local ChDnMin=0
local posixLblTime1
local posixLblTime2
local APointIdx
local BPointIdx
local APointLvl
local BPointLvl
local idxUpMax
local idxDnMin
local AidxUp
local AidxDn
local BidxUp
local BidxDn
local ChTrnd
local ChUp
local ChDn
local APntLvl
local BPntLvl
local StepsUp
local TrUp=false
local TrDn=false
local Lu
local tfLine1={}
local tfLine2={}
local tpLine1={}
local tpLine2={}
local Aidx
local Bidx
g_tChLines["ChTrend"]=tChTrend;
g_tChLines["ChUp"]=tChUp;
g_tChLines["ChDn"]=tChDn;
g_tChLines["fLine1"]=tfLine1;
g_tChLines["fLine2"]=tfLine2;
g_tChLines["pLine1"]=tpLine1;
g_tChLines["pLine2"]=tpLine2;
--если метка перемещалась или индикатор строится впервые рассчитываем линии и выводим их
if boolIndChange then
--для каждой свечи в интервале между метками получить максимальное и минимальное
--значение цены
--сравниваем время меток для задания переменной цикла
--если GETLABLEPOSIXTIME вернула ошибку
if GETLABLEPOSIXTIME (LblId1)==0 or LblId1==0 then
--считываем старое время из массива
posixLblTime1 = tposixOldLabelTime.fplace
--иначе считываем новое время метки
else posixLblTime1 = GETLABLEPOSIXTIME (LblId1)
end
--так же и для второй метки
if GETLABLEPOSIXTIME (LblId2)==0 or LblId2==0 then
posixLblTime2 = tposixOldLabelTime.splace
else posixLblTime2 = GETLABLEPOSIXTIME (LblId2)
end
--получаем значения индекса свечи по его posix-времени
CndlIdx1=tposixByTime[posixLblTime1];
CndlIdx2=tposixByTime[posixLblTime2];
--если метка перемещена вправо за последнюю свечу, то индекс = максимальному
if CndlIdx1 == nil then CndlIdx1 = Size()-5 end
if CndlIdx2 == nil then CndlIdx2 = Size()-4 end
--находим более раннюю свечу
if CndlIdx1 < CndlIdx2 then
j=CndlIdx1;
k=CndlIdx2
else
j=CndlIdx2;
k=CndlIdx1
end
--для всех свечей внутри диапазона меток
for i = j, k, 1 do
--находим максимальное и минимальное значение и индекс свечки
h = t[i].high;
l = t[i].low;
if h~=0 then
if h >= TrndH then
TrndH = h;
tABIdx["hi"] = i--индекс свечки
end;
end
if l~=0 then
if TrndL==0 then TrndL = l end;
if l <= TrndL then
TrndL = l;
tABIdx["lo"] = i--индекс свечки
end;
end
end
--находим количество свечек на интервале тренда (от точек А и В)
IntervalCndlNmbr = tABIdx["hi"] - tABIdx["lo"];
--если получили отрицательное число(слева индекс максимума, тренд вниз), то инвертируем
if IntervalCndlNmbr < 0 then
TrUp=false;
TrDn=true;
APointIdx=tABIdx["hi"];
BPointIdx=tABIdx["lo"];
APointLvl=TrndH;
BPointLvl=TrndL;
else
TrUp=true;
TrDn=false;
APointIdx=tABIdx["lo"];
BPointIdx=tABIdx["hi"];
APointLvl=TrndL;
BPointLvl=TrndH;
IntervalCndlNmbr=-IntervalCndlNmbr
end;
--вычисляем коэффициент наклона прямой
--отрицательный (тренд вниз),положительный (тренд вверх)
koef = (APointLvl - BPointLvl)/IntervalCndlNmbr
--цикл для вычисления значений всеx линий
j=0;
for i=APointIdx, BPointIdx, 1 do
--вычисляем значения точек основного тренда
--i-индекс свечи, j - номер шага
tChTrend[i] = Round.math_round(APointLvl+koef*j,g_PriceScale);
--заносим значения тренда в глобальную таблицу
g_tChLines.tChTrend = tChTrend[i]
--вычисляем значения точек верхней и нижней границы канала
h = t[i].high;
l = t[i].low;
if h - tChTrend[i] > 0 and h~=0 then
if
ChUpMax < h - tChTrend[i]
then
--максимальное отклонение от тренда вверх
ChUpMax = h - tChTrend[i];
--индекс свечи с максимальным отклонением от тренда
idxUpMax = i;
end
end
if tChTrend[i] -l > 0 and l~=0 then
if
ChDnMin < tChTrend[i] -l
then
ChDnMin = tChTrend[i] - l;
idxDnMin = i;
end
end
j=j+1;
end
--количество шагов от точки А
StepsUp=Round.math_round(ChUpMax/koef,0)
--сдвиг назад для тренда вверх, сдвиг вперед для тренда вниз
AidxUp = APointIdx - StepsUp;
BidxUp = BPointIdx - StepsUp;
--количество шагов от точки А
StepsDn=Round.math_round(ChDnMin/koef,0)
--сдвиг назад для тренда вверх, сдвиг вперед для тренда вниз
AidxDn = APointIdx + StepsDn;
BidxDn = BPointIdx + StepsDn;
--цикл по сдвинутому назад диапазону
j=0;
for i=AidxUp, BidxUp, 1 do
--линия тренда верхней границы канала
tChUp[i] = math_round(APointLvl+koef*j,g_PriceScale);
g_tChLines.tChUp = tChUp[i]
if idx==Size() then
message("PUTINDICATOR_Up "..tostring(g_tChLines.tChUp).." "..tostring(i))
end
j=j+1;
end
--цикл по сдвинутому вперед диапазону
j=0;
for i=AidxDn, BidxDn, 1 do
--линия тренда нижней границы канала
tChDn[i] = math_round(APointLvl+koef*j,g_PriceScale);
g_tChLines.tChDn = tChDn[i]
--if idx==Size() then
--message("PUTINDICATOR_Dn "..tostring(g_tChLines.tChDn).." "..tostring(i))
--end
j=j+1;
end
--вычисляем ExtLine
--koef положительный
if TrUp==true then
--линия тренда верхней границы канала
--ниже уровня точки А
j=0;
for i=AidxUp, 2*AidxUp-BidxDn, -1 do
tChUp[i] = math_round(APointLvl+koef*j,g_PriceScale);
g_tChLines.tChUp = tChUp[i]
j=j-1;
end
--выше уровня точки А
j=0;
for i=AidxUp, BidxDn, 1 do
tChUp[i] = math_round(APointLvl+koef*j,g_PriceScale);
g_tChLines.tChUp = tChUp[i]
if i==BPointIdx then
ExtLine1Level=tChUp[i]
local ExtLine1idx=i
end
if i==BidxDn then
ExtLine2Level=tChUp[i]
local ExtLine2idx=i
end
j=j+1;
end
--линия медианы
j=0;
--ниже уровня точки А
for i=APointIdx, AidxUp, -1 do
tChTrend[i] = Round.math_round(APointLvl+koef*j,g_PriceScale);
g_tChLines.tChTrend = tChTrend[i]
if i==AidxUp then
ExtLine6Level=tChTrend[i]
local ExtLine6idx=i
end
j=j-1;
end
j=0;
--выше уровня точки B
for i=BPointIdx, BidxDn, 1 do
tChTrend[i] = Round.math_round(BPointLvl+koef*j,g_PriceScale);
g_tChLines.tChTrend = tChTrend[i]
if i==BidxDn then
ExtLine3Level=tChTrend[i]
local ExtLine3idx=i
end
j=j+1;
end
--линия тренда нижней границы канала
j=0;
--ниже уровня точки А
for i=AidxDn, AidxUp, -1 do
tChDn[i] = math_round(APointLvl+koef*j,g_PriceScale);
g_tChLines.tChDn = tChDn[i]
if i==APointIdx then
ExtLine4Level=tChDn[i]
local ExtLine4idx=i
end
if i==AidxUp then
ExtLine5Level=tChDn[i]
local ExtLine5idx=i
end
j=j-1;
end
end
--koef отрицательный
if TrDn==true then
--линия тренда нижней границы канала
--от уровня точки B и ниже
j=0;
for i=BidxDn, BidxUp, 1 do
tChDn[i] = math_round(BPointLvl+koef*j,g_PriceScale);
g_tChLines.tChDn = tChDn[i]
if i==BPointIdx then
ExtLine1Level=tChDn[i]
local ExtLine1idx=i
end
if i==BidxUp then
ExtLine2Level=tChDn[i]
local ExtLine2idx=i
end
j=j+1;
end
--линия медианы
--от уровня выше точки А до точки А
j=0;
for i=APointIdx, AidxDn, -1 do
tChTrend[i] = Round.math_round(APointLvl+koef*j,g_PriceScale);
g_tChLines.tChTrend = tChTrend[i]
if i==AidxDn then
ExtLine6Level=tChTrend[i]
local ExtLine6idx=i
end
j=j-1;
end
--ниже точки В
j=0;
for i=BPointIdx, BidxUp, 1 do
tChTrend[i] = Round.math_round(BPointLvl+koef*j,g_PriceScale);
g_tChLines.tChTrend = tChTrend[i]
if i==BidxUp then
ExtLine3Level=tChTrend[i]
local ExtLine3idx=i
end
j=j+1;
end
--линия тренда верхней границы канала
--от уровня выше точки А до точки А
j=0;
for i=AidxUp, AidxDn, -1 do
tChUp[i] = math_round(APointLvl+koef*j,g_PriceScale);
g_tChLines.tChUp = tChUp[i]
if i==APointIdx then
ExtLine4Level=tChUp[i]
local ExtLine4idx=i
end
if i==AidxDn then
ExtLine5Level=tChUp[i]
local ExtLine5idx=i
end
j=j-1;
end
--от уровня ниже точки В
j=0;
for i=BidxUp, 2*BidxUp-AidxDn, 1 do
tChUp[i] = math_round(APointLvl+koef*j,g_PriceScale);
g_tChLines.tChUp = tChUp[i]
j=j+1;
end
end
if TrUp==true then
--уровни IntLine
IntLine1=tChUp[APointIdx]
IntLine2=tChTrend[AidxDn]
IntLine3=tChUp[AidxDn]
IntLine4=tChTrend[BidxUp]
IntLine5=tChDn[BidxUp]
IntLine6=tChDn[BPointIdx]
end
if TrDn==true then
IntLine1=tChDn[APointIdx]
IntLine2=tChTrend[AidxUp]
IntLine3=tChDn[AidxUp]
IntLine4=tChTrend[BidxDn]
IntLine5=tChUp[BidxDn]
IntLine6=tChUp[BPointIdx]
end
--вычисление значений fLne1 и fLne2
j=0
for i=BPointIdx,Size()-1 do
tfLine1[i] = Round.math_round(BPointLvl-koef*j,g_PriceScale);
tfLine2[i] = Round.math_round(BPointLvl-koef*j/2,g_PriceScale);
g_tChLines.tfLine1 = tfLine1[i]
g_tChLines.tfLine2 = tfLine2[i]
--message("PUTINDICATOR tfLine2_i "..tostring(g_tChLines.tfLine2).." "..tostring(i));
j=j+1
end
--вычисление значений pLine
if TrUp then
--1-ая точка - значение цены - APointLvl, значение времени - APointIdx
--2-ая точка - значение цены - IntLine6, значение времени - BidxDn
--3-я точка - значение цены - IntLine6, значение времени - 2*BidxDn-BPointIdx
p1koef=Round.math_round((IntLine6-APointLvl)/(BidxDn-APointIdx),g_PriceScale)
p2koef=Round.math_round((IntLine6-APointLvl)/(2*BidxDn-BPointIdx-APointIdx),g_PriceScale)
end
if TrDn then
--1-ая точка - значение цены - APointLvl, значение времени - APointIdx
--2-ая точка - значение цены - IntLine6, значение времени - BidxUp
--3-я точка - значение цены - IntLine6, значение времени - 2*BidxUp-BPointIdx
p1koef=Round.math_round((IntLine6-APointLvl)/(BidxUp-APointIdx),g_PriceScale)
p2koef=Round.math_round((IntLine6-APointLvl)/(2*BidxUp-BPointIdx-APointIdx),g_PriceScale)
end
j=0
for i=APointIdx,Size()-1 do
tpLine1[i] = Round.math_round(APointLvl+p1koef*j,g_PriceScale);
g_tChLines.tpLine1 = tpLine1[i]
tpLine2[i] = Round.math_round(APointLvl+p2koef*j,g_PriceScale);
g_tChLines.tpLine2 = tpLine2[i]
j=j+1
end
g_APointIdx=APointIdx;
g_BPointIdx=BPointIdx;
g_APointLvl=APointLvl;
g_BPointLvl=BPointLvl;
ChTrnd=GETTRENDLINE(1,g_APointIdx,g_BPointIdx,"ChTrend")
if TrUp then
Aidx=AidxUp
Bidx=BidxDn
end
if TrDn then
Aidx=AidxDn
Bidx=BidxUp
end
ChTrndExt=GETTRENDLINE(25,Aidx,Bidx,"ChTrend")
ChUp=GETTRENDLINE(2,AidxUp,BidxUp,"ChUp")
ChUpExt=GETTRENDLINE(26,Aidx,Bidx,"ChUp")
ChDn=GETTRENDLINE(3,AidxDn,BidxDn,"ChDn")
ChDnExt=GETTRENDLINE(27,Aidx,Bidx,"ChDn")
if TrUp then
Aidx=AidxUp
Bidx=BidxUp
end
if TrDn then
Aidx=AidxDn
Bidx=BidxDn
end
APntLvl=GETHORLINE(g_APointLvl,4,Aidx)
BPntLvl=GETHORLINE(g_BPointLvl,5,Bidx)
IntLin1=GETHORLINE(IntLine1,6,g_APointIdx)
if TrUp then Lu=AidxDn end
if TrDn then Lu=AidxUp end
IntLin2=GETHORLINE(IntLine2,7,Lu)
IntLin3=GETHORLINE(IntLine3,8,Lu)
if TrUp then Lu=BidxUp end
if TrDn then Lu=BidxDn end
IntLin4=GETHORLINE(IntLine4,9,Lu)
IntLin5=GETHORLINE(IntLine5,10,Lu)
IntLin6=GETHORLINE(IntLine6,11,g_BPointIdx)
fLne1=GETFLINE(12,g_BPointIdx,"fLine1")
fLne2=GETFLINE(13,g_BPointIdx,"fLine2")
if TrUp then
Aidx=AidxUp
Bidx=BidxDn
ActionIdx=2*Bidx-Aidx
end
if TrDn then
Aidx=AidxDn
Bidx=BidxUp
ActionIdx=2*Bidx-Aidx
end
APntTme=GETVERTLINE(Aidx,14,g_APointLvl)
pLne1=GETTRENDLINE(15,APointIdx,Size()-1,"pLine1")
pLne2=GETTRENDLINE(16,APointIdx,Size()-1,"pLine2")
BPntTme=GETVERTLINE(Bidx,17,g_BPointLvl)
ActionZone=GETVERTLINE(ActionIdx,18,g_BPointLvl)
Lu=BPointIdx
ExtLine1=GETHORLINE(ExtLine1Level,19,Lu)
if TrUp then
Lu=BidxDn
end
if TrDn then
Lu=BidxUp
end
ExtLine2=GETHORLINE(ExtLine2Level,20,Lu)
ExtLine3=GETHORLINE(ExtLine3Level,21,Lu)
Lu=APointIdx
ExtLine4=GETHORLINE(ExtLine4Level,22,Lu)
if TrUp then
Lu=AidxUp
end
if TrDn then
Lu=AidxDn
end
ExtLine5=GETHORLINE(ExtLine5Level,23,Lu)
ExtLne6=GETHORLINE(ExtLine6Level,24,Lu)
--перестроили индикатор-устанавливаем флаг
flagIndExist=true;
flagLblModify=false
end
return ChTrnd,ChUp,ChDn,APntLvl,BPntLvl,IntLin1,IntLin2,IntLin3,IntLin4,IntLin5,IntLin6,fLne1,fLne2,APntTme,pLne1,pLne2,BPntTme,ExtLine1,ExtLine2,ExtLine3,ExtLine4,ExtLine5,ExtLne6,ChTrndExt,ChUpExt,ChDnExt
end
function math_round (num, idp)
local mult = 10^(idp or 0)
return math.floor(num * mult + 0.5) / mult
end
function GETHORLINE(Level,LineNumber,Luch)
--Luch - индекс свечи от которой начнется прямая
local indx
local Line
local z
indx=Size()
for i=0,indx do
if Luch==nil then
Line=Level
else
if i<=Luch then
Line=nil
else
Line=Level
end
end
z=SetValue(i, LineNumber, Line)
--if Level==ExtLine5Level then
--message("GETHORLINE_z "..tostring(z))
--end
end
return Line
end
function GETTRENDLINE(LineNumber,APntIdx,BPntIdx,Trend)
---[[
local Line
local indx
local TrLine={}
indx=Size()-1;
for i=0,indx do
if i < APntIdx then
TrLine[i]=nil
SetValue(i+1, LineNumber, TrLine[i])
end
if i > BPntIdx then
TrLine[i]=nil
SetValue(i+1, LineNumber, TrLine[i])
end
if i>=APntIdx and i<=BPntIdx then
TrLine[i]=g_tChLines[Trend][i]
if LineNumber==2 then
message("GETTRENDLINE "..tostring( g_tChLines[Trend][i]).." "..tostring(i))
end
SetValue(i+1, LineNumber, TrLine[i])
end
end
return TrLine
--]]
end
function GETFLINE(LineNumber,BPntIdx,Trend)
---[[
local Line
local indx
local TrLine={}
--message("GETTRENDLINE LineNumber Trend "..tostring(LineNumber).." "..tostring(Trend));
SetValue(1, LineNumber, nil)
indx=Size();
--message("GETFLINE BPntIdx "..tostring(BPntIdx));
for i=1,indx do
if i < BPntIdx then
TrLine[i]=nil
SetValue(i+1, LineNumber, TrLine[i])
end
if i >= BPntIdx then
TrLine[i]=g_tChLines[Trend][i]
SetValue(i+1, LineNumber, TrLine[i])
--message("GETFLINE BPntIdx "..tostring(TrLine[i]));
end
end
return TrLine
--]]
end
function GETVERTLINE(PointIndex,LineNumber,PointValue)
local value
for i=0,Size()-1 do
if i<=PointIndex then
value=nil
SetValue(i, LineNumber, value)
end
if i==PointIndex+1 then
value=PointValue
SetValue(i, LineNumber, value)
end
if i>PointIndex+1 then
value=nil
SetValue(i, LineNumber, value)
end
end
return value
end
function OnDestroy ()
local tmp
tmp=DelAllLabels(Settings.AssetID)
end
Format={}
function Format.FTEXT (V)
--функция подставляет нули при их отсутствии на первой позиции даты-времени
V=tostring (V)
if string.len (V) == 1 then V = "0".. V end
return V
end
return Format
Round={}
function Round.math_round (num, idp)
local mult = 10^(idp or 0)
return math.floor(num * mult + 0.5) / mult
end
return Round
Sergey Gorokhov написал: а вообще, для передачи чего либо в функцию, придуманы аргументы функции.
Как работать с функциями и для чего нужны аргументы и почему в одних случаях используются локальные переменные, передаваемые через стек (по значению), а в других глобальные переменные(по ссылке), я тоже в курсе. И в данном случае не имеет абсолютно никакого значения как передается в функцию массив, поскольку это ссылочная переменная, и передавая в функцию имя массива (которое одновременно является ссылкой на массив) мы тем самым даем доступ ко всем элементам массива, т.е работать мы будем не с копиями данных, как при обращении по значению, а с самими данными. Дело в том, что в данном случае я заполняю массив в коде как глобальную таблицу, а потом использую его только для чтения. И в чем тут ошибка я решительно не могу понять. Ведь массив заполняется правильно, а вот при считывании возникает ошибка, причем такая, что равны первый и последний элемент массива. Каким боком потокобезопасные функции к передаче данных в функцию я так и не понял. Их всего четыре и ни одна не подходит под рассматриваемую ситуацию. Можно было бы притянуть каким-то макаром функцию insert, но я не вставляю данные в таблицу после ее формирования, а только считываю их. А формируется первоначальная таблица правильно, что и видно при выводе ее значений. Код могу выложить, не проблема. Немного громоздкий, но зато весь откомментирован.
Получается, что при передаче массива в функцию передается неправильно только одно значение. Специально вывел рядом со значениями массива индексы свечей - они совпадают. Как так может быть, что при передаче массива неправильно передается лишь одно значение?
function GETTRENDLINE(LineNumber,APntIdx,BPntIdx,Trend)
---[[
local Line
local indx
local TrLine={}
indx=Size()-1;
for i=0,indx do
if i < APntIdx then
TrLine[i]=nil
SetValue(i+1, LineNumber, TrLine[i])
end
if i > BPntIdx then
TrLine[i]=nil
SetValue(i+1, LineNumber, TrLine[i])
end
if i>=APntIdx and i<=BPntIdx then
TrLine[i]=g_tChLines[Trend][i]
if LineNumber==2 then
message("GETTRENDLINE "..tostring( g_tChLines[Trend][i]).." "..tostring(i))
end
SetValue(i+1, LineNumber, TrLine[i])
end
end
return TrLine
--]]
end
В коде индикатора вычисляю значения и записываю их в массив
Код
j=0;
for i=AidxUp, BidxUp, 1 do
--линия тренда верхней границы канала
tChUp[i] = math_round(APointLvl+koef*j,g_PriceScale);
g_tChLines.tChUp = tChUp[i]
if idx==Size() then
message("PUTINDICATOR_Up "..tostring(g_tChLines.tChUp).." "..tostring(i))
end
j=j+1;
end
вызываю функцию
Код
ChUp=GETTRENDLINE(2,AidxUp,BidxUp,"ChUp")
и получаю странный результат - в передаваемом массиве внутри функции и в нем же при вычислении не совпадает последнее значение массива
Мне нужен именно первый вариант для построения таблицы по всем свечкам. Но в таком виде как Вы написали скрипт выдает синтаксическую ошибку в строке 20. Если же заменить у dt скобки на квадратные, то все выводимые значения нулевые. Что не так?
Недавно я пытался получить разъяснения по поводу получения данных с графика. Получил такой ответ:"вместо getCandlesByIndex() в индикаторе надо использовать функции O, H, L, C, V, T которые вернут данные о свечке по ее индексу на которую наложен индикатор."
НаписАл код:
Код
Settings = {
Name = "0_Candle",
line =
{
{
Name = "__Trend",
Color = RGB(255, 255, 128),
Width = 2,
Type = TYPE_LINE
}
}
}
dt={}
function Init()
local Num=Size()
for i=1, Num do
dt=T(i)
message(tostring(dt(i).hour))
end
return 1
end
function OnCalculate(index)
return Line1
end
И получил ошибку обращения к таблице dt. Что в коде не так?
Вчера я выкладывал код индикатора, который работает на всех ТФ Н4 и ниже и отказывается работать на дневках и выше. И вот какие раскопал неожиданности. В функции Init() я создаю таблицу всех свечек, затем вот таким кодом вывожу ее:
Код
t,n,l = getCandlesByIndex(Settings.AssetID,0,0,NmbrOfCandles);
for i=0, NmbrOfCandles do
message("Init_t "..tostring(t[i]))
end
При запуске индикатора получаю шестнадцатиричные значения - вроде как таблица заполнена. Затем в функции PUTINDICATOR, которая вызывается из OnCalculate в строке:
Код
h = t[i].high;
на каждой свече получаю ошибку с описанием "попытка индексации неизвестного (?) поля (nil value). Никаких преобразований с таблицей в коде после ее создания я не произвожу, только считываю значения. Еще раз обращаю внимание, что код нормально работает на ТФ Н4 и ниже. Как такое может быть?
Да, растянул график по горизонтали и увидел штрихи. Это ж надо так гениально воплотить идею, чтобы длина штриха была равна ширине свечки, а расстояние между штрихами можно было разглядеть только под микроскопом!!!
Sergey Gorokhov написал: Проблема в толщине линии, укажите Width = 1,
Не помогло.
Приблизьте график.
Это была шутка? Сергей, все осталось без изменения. В диалоговом окне редактирования линия указана дашдот, а на чарте сплошная. Кроме того, прошу Вас ответить на вопрос выше - как такое может быть, что на Н4 и ниже все норм, а при переходе на дневки и выше массив перестает переиндексироваться.
Локализовал проблему о которой писАл выше. Если в код Init() в цикл добавить отладочные выражения, то видно, что в строке tposixByTime[tDT[k]] = k не происходит присваивания и длина массива равна нулю, хотя с массивом tDT[k] все ОК, и его длина при проверке после выхода из цикла равна количеству свечей. В чем засада? З.Ы. На ТФ Н4 все работает, на дневном - нет.
Код
for k=0,NmbrOfCandles-1 do
count = count + 1; if count > 100000 then break end;
--преобразуем в POSIX-формат поле datetime
--транспонируем таблицу - индексируем по времени
tDT[k] = os.time (t[k].datetime);
tposixByTime[tDT[k]] = k;
if k==1 then
message("Init_tDT_ "..tostring(tDT[k]))
end
if k==NmbrOfCandles-1 then
message("Init_tDT_tposixByTime_ "..tostring(#tDT).." "..tostring(#tposixByTime))
end
end
package.path = package.path .. ";" .. getWorkingFolder() .."\\LuaIndicators\\modules\\" .. "?.lua";
require ("Format");
require ("Round");
require ("Lines");
Settings={
Name = "0_28.02.2019_0",--имя индикатора
AssetID = "sber",--идентификатор инструмента
LabelName = "D:\метка.bmp",--имя файла метки "down-254095_1280_1",
stopper = false,--обработка на каждом тике
complect = 0,
-- начальное местоположение меток
fplace = 100,
splace = 5,
Yvalue = 0,
line=
{
{
Name = "ChTrnd",
Type =TYPE_LINE,
Width = 1,
Color = RGB(255,255, 0)
},
{
Name = "UpTrend",
Type =TYPE_LINE,
Width = 1,
Color = RGB(0,255,0)
},
{
Name = "DnTrend",
Type =TYPE_LINE,
Width = 1,
Color = RGB(0,255, 0)
},
{
Name = "APointLvl",
Type =TYPE_LINE,
Width = 1,
Color = RGB(120,90,140)
},
{
Name = "BPointLvl",
Type =TYPE_LINE,
Width = 1,
Color = RGB(120,90,140)
},
{
Name = "IntLine1",
Type =TYPE_LINE,
Width = 1,
Color = RGB(105,105,105)--DimGray
},
{
Name = "IntLine2",
Type =TYPE_LINE,
Width = 1,
Color = RGB(0,0,255)--Blue
},
{
Name = "IntLine3",
Type =TYPE_LINE,
Width = 1,
Color = RGB(218,165,32)--Goldenrod
},
{
Name = "IntLine4",
Type =TYPE_LINE,
Width = 1,
Color = RGB(255,0,0)--Red
},
{
Name = "IntLine5",
Type =TYPE_LINE,
Width = 1,
Color = RGB(0,128,0)--Green
},
{
Name = "IntLine6",
Type =TYPE_DASHDOT,
Width = 2,
Color = RGB(255,0,0)--Red
}
}
}
--глобальные переменные
-- Внимание, название всех параметров меток должны писаться большими буквами
label={
--TEXT="метка";
IMAGE_PATH=Settings.LabelName;
--TRANSPARENCY=50,
--TRANSPARENT_BACKGROUND=1,
--YVALUE=Settings.Yvalue,
DATE=0,
TIME=0;
--R=255;
--G=255;
--B=255;
}
t={}--исходная таблица данных свечек
tposixByTime={}--индексы свечек проиндексированные по времени
tposixOldLabelTime={}--таблица старых значений даты-времени выведенных на чарт меток в POSIX-формате
count=0--ограничитель количества циклов
Label_Id = {}--массив с идентификаторами меток
ScriptPath=""--полный путь к иконке (для метки)
flagIndExist=false--флаг существования индикатора на чарте
flagLblModify=false--флаг, сигнализирующий о перемещении метки
a=0
b=0
c=0
k=0
v=0
tDT={}
g_PriceStep = 0--шаг цены
g_PriceScale = 0--точность цены
g_tChLines={}
g_APointIdx=0;
g_BPointIdx=0;
g_APointLvl=0;
g_BPointLvl=0;
function Init()
--объявление переменных. Присваиваем начальные значения чтобы сразу типизировать.
local n = 0--количество строк в таблице (количество свечек)
local l = ""--подпись инструмента
local NmbrOfCandles = 0--номер последней справа свечи на графике
local CandleIndex = 0
--local tDT={}
---[[
if Settings.AssetID == "" then
message("Необходимо установить идентификатор инструмента")
end
--ScriptPath = getScriptPath().."\\"..Settings.LabelName; --путь к файлу метки
--message("Init_ScriptPath "..ScriptPath);
--label["IMAGE_PATH"]=ScriptPath;
--номер предпоследней справа свечи на графике. Нумерация начинается с нуля.
NmbrOfCandles = getNumCandles(Settings.AssetID)-2;
--получим таблицу с данными всех свечек, проиндексированную по номеру свечки
--t - таблица значений свечек
--n - количество полученных свечек(строк таблицы)
--l - подпись к инструменту
--текущую свечу не получаем
t,n,l = getCandlesByIndex(Settings.AssetID,0,0,NmbrOfCandles);
--получить индекс двух свечек, номера которых заданы во входных параметрах
--и установить метки
CandleIndex = NmbrOfCandles-Settings.splace;
tposixOldLabelTime["splace"], Label_Id["splace"] = PUTLABEL(CandleIndex);
CandleIndex = NmbrOfCandles-Settings.fplace;
tposixOldLabelTime["fplace"], Label_Id["fplace"] = PUTLABEL(CandleIndex);
--берем из таблицы только время-дату свечи
count = 0;
for k=0,NmbrOfCandles-1 do
count = count + 1; if count > 100000 then break end;
--преобразуем в POSIX-формат поле datetime
--транспонируем таблицу - индексируем по времени
tDT[k] = os.time (t[k].datetime);
b=k;
tposixByTime[tDT[b]] = b;
--[[
if NmbrOfCandles-Settings.splace==b then
--message("Initsplace "..tostring(tDT[b]).." "..tostring(tposixByTime[b]))
end;
if NmbrOfCandles-Settings.fplace==b then
--message("Initfplace "..tostring(tDT[b]).." "..tostring(tposixByTime[b]))
end
--]]
end
return 11
end
function OnCalculate(index)
local info={}
local CandleIndex
local LblIdF = 0
local LblIdS = 0
local indput
local IndLines={}
local VChTrend
local VAPointLvl
local VBPointLvl
local flagIndChange=false--true-перестроить индикатор, false - не перестраивать
if index==1 then
info = getDataSourceInfo();
--шаг цены
g_PriceStep = getParamEx(info.class_code, info.sec_code, 'SEC_PRICE_STEP').param_value
--точность цены
g_PriceScale = getParamEx(info.class_code, info.sec_code, 'SEC_SCALE').param_value
end
if t==nil then
--номер предпоследней справа свечи на графике. Нумерация начинается с нуля.
NmbrOfCandles = getNumCandles(Settings.AssetID)-2;
--получим таблицу с данными всех свечек, проиндексированную по номеру свечки
--t - таблица значений свечек
--текущую свечу не получаем
t,n,l = getCandlesByIndex(Settings.AssetID,0,0,NmbrOfCandles);
--берем из таблицы только время-дату свечи
count = 0;
for k=0,NmbrOfCandles-1 do
count = count + 1; if count > 100000 then break end;
--преобразуем в POSIX-формат поле datetime
--транспонируем таблицу - индексируем по времени
tDT[k] = os.time (t[k].datetime);
b=k;
tposixByTime[tDT[b]] = b;
--[[
if NmbrOfCandles-Settings.splace==b then
--message("Initsplace "..tostring(tDT[b]).." "..tostring(tposixByTime[b]))
end;
if NmbrOfCandles-Settings.fplace==b then
--message("Initfplace "..tostring(tDT[b]).." "..tostring(tposixByTime[b]))
end
--]]
end
end
---[[
--если флаг перемещения метки не установлен
if flagLblModify==false then
--проверяем перемещалась ли первая метка
LblIdF=IsLabelChange("fplace");
--проверяем вторую метку
LblIdS=IsLabelChange("splace");
--если индикатор существует, то выходим без расчетов, возвращаем массив линий
if flagIndExist==true then
flagIndChange=false;
--иначе строим индикатор, возвращаем массив линий
else
flagIndChange=true;
end
--если метка перемещалась flagLblModify==true
else
--строим индикатор, выставляем флаг изменения индикатора,
--сбрасываем флаг метки, возвращаем индикатор
flagIndChange=true;
end
--рассчитываем индикатор или выводим линии без расчета
VChTrend,VChUp,VChDn,VAPointLvl,VBPointLvl,VIntLine1=PUTINDICATOR(LblIdF,LblIdS,flagIndChange,index)
--]]
if index==0 then VChTrend=nil end
if index >= Size() then VChTrend=nil end
return VChTrend,VChUp,VChDn,VAPointLvl,VBPointLvl,VIntLine1,VIntLine2,VIntLine3,VIntLine4,VIntLine5,VIntLine6
end
function IsLabelChange(place)
--функция принимает индекс массива Label_Id("строка")
--устанавливает глобальные флаги и
--записывает posix-время в глобальную таблицу tposixOldLabelTime
--возврашает Id метки
local LblId = 0
local posixNewLblTime = 0
local posixOldLblTime = 0
--проверяем перемещалась ли метка
--считываем Id метки из массива
LblId = Label_Id[place];
--считываем posix-время для этой метки
posixOldLblTime = tposixOldLabelTime[place];
--преобразуем время метки с чарта в posix-формат
--если функция вернула nil, время не меняем
if GETLABLEPOSIXTIME (LblId)== 0 then
posixNewLblTime = posixOldLblTime
else
posixNewLblTime = GETLABLEPOSIXTIME (LblId)
end
--если время не совпадает, выставляем флаги отсутствия индикатора и модификации метки
if posixOldLblTime ~= posixNewLblTime then
--выставляем флаги
--метка перемещена
flagLblModify=true;
--индикатор не построен
flagIndExist=false;
--изменяем время метки в массиве
tposixOldLabelTime[place] = posixNewLblTime;
end
return LblId
end
PUTLABEL = function (index)
--[[функция для установки метки на чарт по индексу
принимает индекс свечи
возвращает время метки в posix-формате и Id метки
входные параметры:
-индекс свечи
-строка с названием точки для индексации массива установленных меток
вызывается в Init()
--]]
local Date=""--дата свечки
local Time=""--время свечки
local Yvalue=0
LabelId=0
--message ("PUTLABELindex.."..tostring(index));
--дата и время свечи
Date=t[index].datetime.year..Format.FTEXT(t[index].datetime.month)..Format.FTEXT(t[index].datetime.day); --дата
Time=Format.FTEXT(t[index].datetime.hour)..Format.FTEXT(t[index].datetime.min)..Format.FTEXT(t[index].datetime.sec);
--привязка к оси У
Yvalue = t[index].high;
--заполнить структуру данных метки
label={
IMAGE_PATH=Settings.LabelName;
YVALUE=Settings.Yvalue+Yvalue;
DATE=Date,
TIME=Time
};
--добавляем метку на чарт и запоминаем Id метки в массиве, проиндексированном принятой строкой
LabelId = AddLabel(Settings.AssetID,label);
--message ("PUTLABELindex LabelId "..tostring(LabelId))
return os.time (t[index].datetime), LabelId
end
function GETLABLEPOSIXTIME (LabelId)
--функция принимает Id метки и возвращает ее время в posix-формате
--при перемещении метки временно Id может стать нулем, поэтому функция выдает ошибку ноль
local id--Id метки
local NewLbLParam = {}--структура параметров метки
local LabelTime = ""--строка с данными даты-времени метки
local dt={}--структура в формате datetime
local err=0
local retval
---[[
id = LabelId;
--получаем таблицу с параметрами метки (в нижнем регистре в формате строки)
if LabelId==0 then retval=err
else
NewLbLParam = GetLabelParams(Settings.AssetID, id);
--преобразуем в формат datetime
dt.year,dt.month,dt.day = string.match(NewLbLParam.date,"(%d%d%d%d)(%d%d)(%d%d)")
if #NewLbLParam.time == 5 then
LabelTime="0".. NewLbLParam.time
else LabelTime="".. NewLbLParam.time
end
dt.hour,dt.min,dt.sec = string.match(LabelTime,"(%d%d)(%d%d)(%d%d)");
--преобразуем в формат POSIX
retval = os.time (dt)
end;
return retval;
end
function PUTINDICATOR(LblId1,LblId2,boolIndChange,idx)
local a=0
local i
local j
local k
local TrndH=0
local TrndL=0
local h
local l
local CndlIdx1=0
local CndlIdx2=0
local tABIdx={}
local IntervalCndlNmbr=0
local koef
local tChTrend={}
local tChUp={}
local tChDn={}
local ChUpMax=0
local ChDnMin=0
local posixLblTime1
local posixLblTime2
local APointIdx
local BPointIdx
local APointLvl
local BPointLvl
local idxUpMax
local idxDnMin
local AidxUp
local AidxDn
local BidxUp
local BidxDn
local ChTrnd--=200
local ChUp--=210
local ChDn--=190
local APntLvl--=220
local BPntLvl--=180
local StepsUp
local TrUp=false
local TrDn=false
local Lu
g_tChLines["ChTrend"]=tChTrend;
g_tChLines["ChUp"]=tChUp;
g_tChLines["ChDn"]=tChDn;
--если метка перемещалась или индикатор строится впервые рассчитываем линии и выводим их
if boolIndChange then
--для каждой свечи в интервале между метками получить максимальное и минимальное
--значение цены
--сравниваем время меток для задания переменной цикла
--если GETLABLEPOSIXTIME вернула ошибку
if GETLABLEPOSIXTIME (LblId1)==0 or LblId1==0 then
--считываем старое время из массива
posixLblTime1 = tposixOldLabelTime.fplace
--иначе считываем новое время метки
else posixLblTime1 = GETLABLEPOSIXTIME (LblId1)
end
--так же и для второй метки
if GETLABLEPOSIXTIME (LblId2)==0 or LblId2==0 then
posixLblTime2 = tposixOldLabelTime.splace
else posixLblTime2 = GETLABLEPOSIXTIME (LblId2)
end
--получаем значения индекса свечи по его posix-времени
CndlIdx1=tposixByTime[posixLblTime1];
CndlIdx2=tposixByTime[posixLblTime2];
--если метка перемещена вправо за последнюю свечу, то индекс = максимальному
if CndlIdx1 == nil then CndlIdx1 = Size() end
if CndlIdx2 == nil then CndlIdx2 = Size() end
--находим более раннюю свечу
if CndlIdx1 < CndlIdx2 then
j=CndlIdx1;
k=CndlIdx2
else
j=CndlIdx2;
k=CndlIdx1
end
--message("PUTINDICATOR_CndlIdx1_CndlIdx2_j_k_"..tostring(CndlIdx1).."_"..tostring(CndlIdx2).."_"..tostring(j).."_"..tostring(k));
--для всех свечей внутри диапазона меток
for i = j, k, 1 do
--находим максимальное и минимальное значение и индекс свечки
h = t[i].high;
l = t[i].low;
if h~=0 then
if h >= TrndH then
TrndH = h;
tABIdx["hi"] = i--индекс свечки
end;
end
if l~=0 then
if TrndL==0 then TrndL = l end;
if l <= TrndL then
TrndL = l;
tABIdx["lo"] = i--индекс свечки
end;
end
--message("PUTINDICATOR_h_l_"..tostring(h).."_"..tostring(l));
--message("PUTINDICATOR_TrndH_TrndL_tABIdx.hi_tABIdx.lo_"..tostring(TrndH).."_"..tostring(TrndL).."_"..tostring(tABIdx["hi"]).."_"..tostring(tABIdx["lo"]));
end
--находим количество свечек на интервале тренда (от точек А и В)
IntervalCndlNmbr = tABIdx["hi"] - tABIdx["lo"];
--если получили отрицательное число(слева индекс максимума, тренд вниз), то инвертируем
if IntervalCndlNmbr < 0 then
TrUp=false;
TrDn=true;
APointIdx=tABIdx["hi"];
BPointIdx=tABIdx["lo"];
APointLvl=TrndH;
BPointLvl=TrndL;
else
TrUp=true;
TrDn=false;
APointIdx=tABIdx["lo"];
BPointIdx=tABIdx["hi"];
APointLvl=TrndL;
BPointLvl=TrndH;
IntervalCndlNmbr=-IntervalCndlNmbr
end;
--message("PUTINDICATOR_APointIdx_BPointIdx_APointLvl_BPointLvl_"..tostring(APointIdx).."_"..tostring(BPointIdx).."_"..tostring(APointLvl).."_"..tostring(BPointLvl));
--вычисляем коэффициент наклона прямой
--отрицательный (тренд вниз),положительный (тренд вверх)
koef = (APointLvl - BPointLvl)/IntervalCndlNmbr
--message("PUTINDICATOR koef "..tostring(koef));
--цикл для вычисления значений всеx линий
--заносим значения в таблицу
--tChTrend[APointIdx] = Round.math_round(APointLvl,g_PriceScale);
--g_tChLines.tChTrend = tChTrend[APointIdx]
--tChTrend[BPointIdx] = Round.math_round(BPointLvl,g_PriceScale);
--g_tChLines.tChTrend = tChTrend[BPointIdx]
j=0;
for i=APointIdx, BPointIdx, 1 do
--вычисляем значения точек основного тренда
--i-индекс свечи, j - номер шага
tChTrend[i] = Round.math_round(APointLvl+koef*j,g_PriceScale);
--заносим значения тренда в глобальную таблицу
g_tChLines.tChTrend = tChTrend[i]
--вычисляем значения точек верхней и нижней границы канала
h = t[i].high;
l = t[i].low;
if h - tChTrend[i] > 0 and h~=0 then
if
ChUpMax < h - tChTrend[i]
then
--максимальное отклонение от тренда вверх
ChUpMax = h - tChTrend[i];
--индекс свечи с максимальным отклонением от тренда
idxUpMax = i;
end
end
if tChTrend[i] -l > 0 and l~=0 then
if
ChDnMin < tChTrend[i] -l
then
ChDnMin = tChTrend[i] - l;
idxDnMin = i;
end
end
j=j+1;
end
--количество шагов от точки А
StepsUp=Round.math_round(ChUpMax/koef,0)
--сдвиг назад
AidxUp = APointIdx - StepsUp;
BidxUp = BPointIdx - StepsUp;
--количество шагов от точки А
StepsDn=Round.math_round(ChDnMin/koef,0)
--сдвиг вперед
AidxDn = APointIdx + StepsDn;
BidxDn = BPointIdx + StepsDn;
---[[
--цикл по сдвинутому назад диапазону
j=0;
for i=AidxUp, BidxUp, 1 do
--линия тренда верхней границы канала
tChUp[i] = math_round(APointLvl+koef*j,g_PriceScale);
g_tChLines.tChUp = tChUp[i]
--message("PUTINDICATOR tChUp_i "..tostring( tChUp[i]).." "..tostring(i));
j=j+1;
end
--цикл по сдвинутому вперед диапазону
j=0;
for i=AidxDn, BidxDn, 1 do
--линия тренда нижней границы канала
tChDn[i] = math_round(APointLvl+koef*j,g_PriceScale);
g_tChLines.tChDn = tChDn[i]
--message("PUTINDICATOR tChDn_i "..tostring( tChDn[i]).." "..tostring(i));
j=j+1;
end
if TrUp==true then
--уровени IntLine
IntLine1=tChUp[APointIdx]
IntLine2=tChTrend[AidxDn]
IntLine3=tChUp[AidxDn]
IntLine4=tChTrend[BidxUp]
IntLine5=tChDn[BidxUp]
IntLine6=tChDn[BPointIdx]
end
if TrDn==true then
--уровень IntLine1
IntLine1=tChDn[APointIdx]
IntLine2=tChTrend[AidxUp]
IntLine3=tChDn[AidxUp]
IntLine4=tChTrend[BidxDn]
IntLine5=tChUp[BidxDn]
IntLine6=tChUp[BPointIdx]
end
g_APointIdx=APointIdx;
g_BPointIdx=BPointIdx;
g_APointLvl=APointLvl;
g_BPointLvl=BPointLvl;
--]]
ChTrnd=GETTRENDLINE(1,g_APointIdx,g_BPointIdx,"ChTrend")
ChUp=GETTRENDLINE(2,AidxUp,BidxUp,"ChUp")
ChDn=GETTRENDLINE(3,AidxDn,BidxDn,"ChDn")
APntLvl=GETHORLINE(g_APointLvl,4,g_APointIdx)
BPntLvl=GETHORLINE(g_BPointLvl,5,g_BPointIdx)
IntLin1=GETHORLINE(IntLine1,6,g_APointIdx)
if TrUp then Lu=AidxDn end
if TrDn then Lu=AidxUp end
IntLin2=GETHORLINE(IntLine2,7,Lu)
IntLin3=GETHORLINE(IntLine3,8,Lu)
if TrUp then Lu=BidxUp end
if TrDn then Lu=BidxDn end
IntLin4=GETHORLINE(IntLine4,9,Lu)
IntLin5=GETHORLINE(IntLine5,10,Lu)
IntLin6=GETHORLINE(IntLine6,11,g_BPointIdx)
--перестроили индикатор-устанавливаем флаг
flagIndExist=true;
flagLblModify=false
end
return ChTrnd,ChUp,ChDn,APntLvl,BPntLvl,IntLin1,IntLin2,IntLin3,IntLin4,IntLin5,IntLin6
--]]
end
function math_round (num, idp)
local mult = 10^(idp or 0)
return math.floor(num * mult + 0.5) / mult
end
function GETHORLINE(Level,LineNumber,Luch)
--Luch - индекс свечи от которой начнется прямая
local indx
local Line
local z
indx=Size()
for i=0,indx do
if Luch==nil then
Line=Level
else
if i<=Luch then
Line=nil
else
Line=Level
end
end
z=SetValue(i, LineNumber, Line)
end
--message("GETHORLINE Line "..tostring(Line));
return Line
end
function GETTRENDLINE(LineNumber,APntIdx,BPntIdx,Trend)
---[[
local Line
local indx
local TrLine={}
--message("GETTRENDLINE LineNumber Trend "..tostring(LineNumber).." "..tostring(Trend));
--message("GETTRENDLINE APntIdx BPntIdx "..tostring(APntIdx).." "..tostring(BPntIdx));
SetValue(1, LineNumber, nil)
indx=Size();
for i=1,indx do
if i < APntIdx then
TrLine[i]=nil
SetValue(i+1, LineNumber, TrLine[i])
end
if i > BPntIdx then
TrLine[i]=nil
SetValue(i+1, LineNumber, TrLine[i])
end
if i>=APntIdx and i<=BPntIdx then
TrLine[i]=g_tChLines[Trend][i]
--message("GETTRENDLINE TrLine_i "..tostring(i).." "..tostring(TrLine[i]));
SetValue(i+1, LineNumber, TrLine[i])
end
end
return TrLine
--]]
end
function OnDestroy ()
local tmp
tmp=DelAllLabels(Settings.AssetID)
end
Код
Format={}
function Format.FTEXT (V)
--функция подставляет нули при их отсутствии на первой позиции даты-времени
V=tostring (V)
if string.len (V) == 1 then V = "0".. V end
return V
end
return Format
Работает пока только на Н4, на других ТФ вылетает с ошибкой, отлавливаю.
Прошу помочь в таком вопросе - меняю тип линии в Settings - реакции ноль, выводится только сплошная. Пытаюсь изменить тип в диалоге редактирования - такая же байда. Что делаю не так?
Собственно сам вопрос задан в теме. Количество заданных линий пять, но странности возникают при выводе. Сам алгоритм индикатора таков, что сначала рассчитываются трендовые линии, а затем принудительно при помощи SetValue они выводятся на график. Таких линий три.Затем пытаюсь добавить две горизонтальные линии, проходящие через концы трендовых, но горизонтальные мало того, что не отображаются, так еще и не выводятся в окне редактирования. Причем последнее обстоятельство обнаружил случайно на третий день мучений и вскипания мозга. Кто-нибудь с таким сталкивался?
Прошу ответить всех кто сталкивался с подобным поведением. Добавляю в код всего одну строку require ("Lbels"); и индикатор исчезает из списка в окне доступных индикаторов. Я так понимаю, что такое поведение интерпретатора наблюдается при ошибках в синтаксисе. Но где здесь ошибка? Может что-то не так с системными файлами или виртуальной машиной LUA? Кто сталкивался с такой проблемой и как такое можно решить?
SDL написал: Вот в этом подозрительном месте TrLine (возможно) становится каким-то скаляром:КодTrLine=g_tChLines["ChTrends"]
Спасибо, въехал. Сам виноват, надо быть внимательней. Пропустил выражение для индексирования. Надо было так :КодTrLine[i]=g_tChLines["ChTrends"]. Еще раз благодарю.
Функция ниже пишет - ошибка в строке.... попытка индексации локальной переменной TrLine в блоке if i > BPntIdx then TrLine[i]=nil SetValue(i, LineNumber, TrLine[i]) end
хотя точно в таком же блоке выше ошибки не возникает. В чем может быть проблема?
Код
function GETTRENDLINE(LineNumber,APntIdx,BPntIdx)
local Line
local indx
local TrLine={}
indx=Size();
for i=1,indx do
if i < APntIdx then
TrLine[i]=nil
SetValue(i, LineNumber, TrLine[i])
end
if i>=APntIdx and i<=BPntIdx then
TrLine=g_tChLines["ChTrends"][i]
SetValue(i, LineNumber, TrLine)
end
if i > BPntIdx then
TrLine[i]=nil
SetValue(i, LineNumber, TrLine[i])
end
end
return TrLine
end
Спасибо Егор. Еще один вопрос - будет ли корректно отрабатывать SetValue() если ее вызовы будут находится в другой функции ( в ней в цикле рассчитываются сразу все значения, которые необходимо вывести ), вызываемой из OnCalculate(), или ее необходимо разместить в теле OnCalculate(). Пример:
Код
function GETTRENDLINE(APntIdx,APntLvl,BPntIdx,BPntLvl,koe)
local Line
local indx
local TrLine
--g_PriceStep
--g_PriceScale
indx=Size();
--message(tostring(indx));
for i=0,indx do
if indx < APntIdx then
SetValue(i, 1, nil)
else
if indx > BPntIdx then
SetValue(i, 1, nil)
else
if indx==APntIdx then
SetValue(i, 1, APntLvl)
end
if indx==BPntIdx then
SetValue(i, 1, BPntLvl)
end
if indx>APntIdx then
if indx<BPntIdx then
TrLine=math_round(APntLvl*koe,g_PriceScale)
SetValue(i, 1, TrLine)
end
end
end
end
end
return
end
Здравствуйте форумчане и разработчики. Прошу пояснить такой вопрос - как работает функция OnCalculate(). Я кодом Lua выбрасываю на график две метки, затем перемещаю их руками и затем провожу расчет индикатора на интервале между метками. Ожидал, что все линии индикатора изменятся после расчета. Но...алгоритм работы функции OnCalculate() не соответствует моим ожиданиям. На скрине ниже все видно - сначала выводятся рассчитанные значения до перемещения меток и только на последней свечке (по приходу тиков) выводятся рассчитанные значения. Это так задумано изначально?
Sergey Gorokhov написал: какая разница что в этом цикле будет t из getCandlesByIndex или T(k)?
Разница существенная, поскольку для получения первоначальной t из getCandlesByIndex используется сишная откомпилированная функция, а для получения всех значений первоначальной Т(к) мне придется использовать цикл интепретатора, что в разы медленнее, а учитывая, что при запуске пересчет ведется аж два раза, картина еще печальнее. Да, переиндексировать мне придется обе таблицы и это займет примерно одинаковое время. Но это второе действие.
Sergey Gorokhov написал: Уточните что из себя представляет файл bmp.bmp (прикрепите к сообщению)
Сергей, посыпаю голову пеплом, прошу простить. Пытался вывести в качестве метки файл png, сейчас еще раз перечитал документацию и прорубило. Ваш код отработал, спасибо.
Sergey Gorokhov написал: Зачем? Если Вы в любой момент по индексу и так можете достать нужные данные без лишних функций.
Как пример - интерактивная работа с метками. Где в таблице данных метки индекс свечи? Я переместил метку, она привязана ко времени и цене свечи. Я считал эти данные. Мне не нужны раздельные данные по времени, мне нужен только индекс свечи, к которой привязана метка. Укажите мне легкий способ получения такого индекса.
Цитата
Sergey Gorokhov написал: Есть доказательства или это просто мысли? Если просто мысли тогда прошу воздержаться во избежания дезинформации других пользователей.
Хорошо, воздержусь. Ибо доказывать, что скорость работы откомпилированной сишной функции выше скорости работы интерпретатора занятие неблагодарное.
Цитата
Sergey Gorokhov написал: Что такого Вы делаете с каждой свечкой отдельно? Зачем Вам захламлять лишний кусок памяти?
Уже указал раньше - не нужны данные свечек, нужны индексы свечек к которым привязаны метки. И это не какая-то отдельная задача, а кусок кода комплексной задачи. Работать же с метками, привязанными ко времени неудобно в силу разрастания программного кода если нужно получить данные свечи. Гораздо эффективнее заранее проиндексировать все свечки по времени, получая доступ к индексу свечи по этому времени для получения конкретной свечи и работой с ее данными. Одно обращение по индексу -получить индекс свечи по смещению в таблице вместо следующей последовательности действий: -сравнить время и дату каждой свечи со временем и датой , полученными из данных метки В ЦИКЛЕ -получить индекс свечи если совпало Уверен, что вопрос эффективности данных действий очевиден. А учитывая, что: - индикатор работает в потоке терминала - разработчики всеми правдами и неправдами открещиваются от того, чтобы дать программный доступ к таким элементарным объектам как прямая, луч, канал, полигон и т.д. и предлагают все это заменить тормозной работой с метками, становится очевидно, что чаяния простых пользователей для разработчиков и компании не на первом месте, хотя в других системах торговли уже давно такие сервисы предоставляются. И потому пользователи часто срываются, как я ранее, хотя, конечно, и не стоило этого делать. Но, полазив по форуму и найдя обещания о внесении изменений "вот-вот, скоро в ближайшей версии", начинаешь нервно про себя материться и плеваться.
Sergey Gorokhov написал: Вместо getNumCandles() есть функция Size() вызвав которую в индикаторе, Вы получите количество свечек.
Принимается.
Цитата
Sergey Gorokhov написал: вместо getCandlesByIndex() в индикаторе надо использовать функции O, H, L, C, V, T которые вернут данные о свечке по ее индексу на которую наложен индикатор.
Нужны не отдельные данные по каждой свечке, а упорядоченная (проиндексированная по номеру свечки) таблица всех значений всех свечей для дальнейшего обращения к ней. Функция для работы с источником данных - это усложнение кода и, соответственно, увеличение времени обработки данных. Мой код делает все манипуляции в три строки. Вы же предлагаете обработать каждую свечу отдельно в интерпретаторе, затем свести все данные в одну таблицу и уже работать с ней вместо того, чтобы обратиться единожды к оптимизированной и откомпилированной сишной функции. Мне это напоминает удаление гланд через задницу.
Скопировал Ваш код, изменил имя своего файла на прописанное Вами в коде, поменял инструмент (акции ВТБ), изменил цену, добавил строку сообщения с Id метки. Присоединил к графику. Получил Id метки в окне сообщения, путь к файлу там же, прямую линию на графике. Метки нет. Сейчас попробую установит программу для работы с экраном (не уверен, что получится, комп на работе, админских прав нет), и попробую сделать скрины.
Sergey Gorokhov написал: Поле Идентификатор уникально для каждого графика, а не окна с графиком. То что Вы в одном окне используете один график не значит что остальные тоже так делают. В связи с чем в таком виде мы не можем зарегистрировать пожелание.
Я не случайно указал, что идентификатор нужно добавить ТОЛЬКО для инструмента верхнего уровня. Будь в одном окне хоть миллион графиков, идентификатор будет прописан ТОЛЬКО у одного инструмента или индикатора по желанию пользователя. Остальные останутся пустыми. Так что, это никак не скажется на уникальности.
Цитата
Sergey Gorokhov написал: Тем более не понятно зачем из индикатора может потребоваться Идентификатор графика на который этот индикатор накладывается, ведь в индикаторе Вы и так можете получить все данные без Идентификатора.Просьба пояснить этот момент.
Я выше не зря указал две функции без передачи идентификатора в которые НЕВОЗМОЖНО получить доступ к данным свечей. Если Вы мне укажете такой механизм без прописывания руками идентификатора графика в коде индикатора при смене инструмента (графика свечей), буду очень благодарен. Может как-то сумбурно объяснил, попробую "на пальцах". Есть индикатор. В его коде есть указанные выше функции, расположенные в Init(), что исключает возможность прописывания идентификатора после присоединения его к графику (сразу в момент присоединения вылетит ошибка). Простой пользователь (не разработчик) хочет просто кинуть индикатор на любой график без танцев с бубном. Но при такой ситуации, которая есть на данный момент, у него это не получится. Мало того, что надо прописать идентификатор на вкладке "дополнительно" - с этим можно как-то смириться, но нужно еще открыть код индикатора и прописать идентификатор в секции Settings, что для рядового пользователя крайне проблематично. В случае же если в данном окошке для графика первого слоя будет прописан идентификатор по умолчанию, в программном коде достаточно будет просто считать эту строчку и проблема будет закрыта. Как это может отразиться на уникальности я даже представить себе не могу. Кроме того, можно запретить редактирование этой строки из программного кода, оставить только возможность редактирования руками в терминале.
Цитата
Sergey Gorokhov написал: что же тут непонятного? Чтобы метки ставить на самого себя без бубна с назначениями идентификатора и передачей идентификатора в параметры.замечу в скобках, что это уже давно "зарегистрировано", а тут вот здрасьте, "просьба пояснить"
Честно говоря, я не понял о каких метках идет речь. Большая просьба кинуть ссылку на "давно "зарегистрировано"".
Sergey Gorokhov написал: Похожая проблема уже чинилась, проверьте версию терминала, рекомендуем использовать 7.19 или выше.Если с версией всё в порядке, проверьте внимательней все параметры метки.Проблема не воспроизводится, значит явно где-то просто ошиблись.
Сергей, здравствуйте. Удалось ли воспроизвести проблему? Проверил все еще раз - проблема осталась. Каково будет Ваше положительное решение?
Прошу разработчиков рассмотреть вопрос по добавлению следующих изменений в клиентский терминал: 1. На вкладке "дополнительно" окна редактирования инструмента в окно идентификатора автоматически прописывать название окна графика для инструмента верхнего слоя, естественно с возможностью редактирования и считывания прописанного по умолчанию значения строки из этого окна в программном коде индикатора. Это существенно облегчит написание кода индикаторов. В этом случае при обращении к функциям getNumCandles() и getCandlesByIndex() не придется лезть в программный код индикатора для внесения вручную изменений ID инструмента в секции Settings при смене графика инструмента, достаточно будет считать значение по умолчанию. Я, например, не использую более одного графика цены в каждом окне и меня сильно напрягает, что при смене окна и инструмента нужно лезть в программный код индикатора и вносить правки вручную. Сделать же это после присоединения индикатора к графику невозможно, поскольку указанные выше функции я использую в секции Init() и к моменту запуска индикатора эта секция уже должна получить все параметры. Для тех же, кому нужно использовать более одного графика инструмента в окне, останется возможность редактирования значения по умолчанию ручками.
2. Создать в интерфейсе кнопку с программным кодом, эмулирующим выдачу тика цены для любого инструмента. Это нужно для отладки программного кода индикаторов без подключения к серверу. Особенно это актуально в часы отсутствия торгов.
Здравствуйте форумчане. В коде ниже функция GETLABLEPOSIXTIME внутри себя рассчитывается корректно (должна возвращать время метки в формате posix, в сообщении его и выводит), но при обращении к ней в функции IsLabelChange переменная posixNewLblTime получает значение nil. Второй день бьюсь, не пойму в чем косяк. Что не так делаю?
--глобальные переменные -- Внимание, название всех параметров меток должны писаться большими буквами label={ --TEXT="метка"; IMAGE_PATH=Settings.LabelName; --TRANSPARENCY=50, --TRANSPARENT_BACKGROUND=1, --YVALUE=Settings.Yvalue, DATE=0, TIME=0; --R=255; --G=255; --B=255; }
t={}--исходная таблица данных свечек tposixByTime={}--индексы свечек проиндексированные по времени tposixOldLabelTime={}--таблица старых значений даты-времени выведенных на чарт меток в POSIX-формате count=0--ограничитель количества циклов Label_Id = {}--массив с идентификаторами меток ScriptPath=""--полный путь к иконке (для метки) flagIndExist=false--флаг существования индикатора на чарте flagLblModify=false--флаг, сигнализирующий о перемещении метки a=0 b=0 c=0 k=0 v=0 tDT={}
function Init()
--объявление переменных. Присваиваем начальные значения чтобы сразу типизировать. local n = 0--количество строк в таблице (количество свечек) local l = ""--подпись инструмента local NmbrOfCandles = 0--номер последней справа свечи на графике local CandleIndex = 0 --local tDT={}
---[[ if Settings.AssetID == "" then message("Необходимо установить идентификатор инструмента") end --ScriptPath = getScriptPath().."\\"..Settings.LabelName; --путь к файлу метки --message("Init_ScriptPath "..ScriptPath); --label["IMAGE_PATH"]=ScriptPath; --номер предпоследней справа свечи на графике. Нумерация начинается с нуля. NmbrOfCandles = getNumCandles(Settings.AssetID)-2; --получим таблицу с данными всех свечек, проиндексированную по номеру свечки --t - таблица значений свечек --n - количество полученных свечек(строк таблицы) --l - подпись к инструменту --текущую свечу не получаем t,n,l = getCandlesByIndex(Settings.AssetID,0,0,NmbrOfCandles); --получить индекс двух свечек, номера которых заданы во входных параметрах --и установить метки CandleIndex = NmbrOfCandles-Settings.splace; tposixOldLabelTime["splace"], Label_Id["splace"] = PUTLABEL(CandleIndex); --message("Initsplace "..tostring(tposixOldLabelTime["splace"])); --message("Initsplace "..tostring(Label_Id["splace"]));
CandleIndex = NmbrOfCandles-Settings.fplace; tposixOldLabelTime["fplace"], Label_Id["fplace"] = PUTLABEL(CandleIndex); --message("Initfplace "..tostring(tposixOldLabelTime["fplace"])); --message("Initfplace "..tostring(Label_Id["fplace"])); --берем из таблицы только время-дату свечи count = 0; for k=0,NmbrOfCandles-1 do count = count + 1; if count > 100000 then break end; --преобразуем в POSIX-формат поле datetime --транспонируем таблицу - индексируем по времени tDT[k] = os.time (t[k].datetime); b=k; tposixByTime[tDT[b]] = b; --[[ if NmbrOfCandles-Settings.splace==b then --message("Initsplace "..tostring(tDT[b]).." "..tostring(tposixByTime[b])) end; if NmbrOfCandles-Settings.fplace==b then --message("Initfplace "..tostring(tDT[b]).." "..tostring(tposixByTime[b])) end --]] end
return 1
end
function OnCalculate(index)
local LblIdF = 0 local LblIdS = 0 local posixNewLblTimeF = 0 local posixOldLblTimeF = 0 local posixNewLblTimeS = 0 local posixOldLblTimeS = 0 local indput local IndLines={} local ChTrend local flagIndChange=false--false-перестроить индикатор, true - не перестраивать
---[[ --если флаг перемещения метки не установлен if flagLblModify==false then --проверяем перемещалась ли первая метка LblIdF=IsLabelChange("fplace"); --проверяем вторую метку LblIdS=IsLabelChange("splace"); --если индикатор существует, то выходим без расчетов, возвращаем массив линий if flagIndExist==true then flagIndChange=true; --иначе строим индикатор, выставляем его флаг, возвращаем массив линий else flagIndChange=false; flagIndExist=true; end --если метка перемещалась flagLblModify==true else --строим индикатор, выставляем флаг индикатора, --сбрасываем флаг метки, возвращаем индикатор flagIndChange=false; flagIndExist=true; flagLblModify=false end --рассчитываем индикатор или выводим линии без расчета --ChTrend=PUTINDICATOR(LblIdF,LblIdS,flagIndChange) --]] return ChTrend end
function OnDestroy ()
local tmp tmp=DelAllLabels(Settings.AssetID) end
function IsLabelChange(place) --функция принимает индекс массива Label_Id["строка"] --устанавливает глобальные флаги и --записывает posix-время в глобальную таблицу tposixOldLabelTime --возврашает Id метки local LblId = 0 local posixNewLblTime = 0 local posixOldLblTime = 0
--проверяем перемещалась ли метка --считываем Id метки из массива LblId = Label_Id[place]; message(tostring(LblId).." IsLabelChange_LblId") --считываем posix-время для этой метки posixOldLblTime = tposixOldLabelTime[place]; message(tostring(posixOldLblTime).." IsLabelChange_posixOldLblTime"); --преобразуем время метки с чарта в posix-формат posixNewLblTime = GETLABLEPOSIXTIME (LblId); message(tostring(posixNewLblTimeF).." IsLabelChange_posixNewLblTime") ---[[ --если время не совпадает, выставляем флаги отсутствия индикатора и модификации метки if posixNewLblTime ~= nil then if posixOldLblTime ~= posixNewLblTime then --выставляем флаги --метка перемещена flagLblModify=true; --индикатор не построен flagIndExist=false; --изменяем время метки в массиве tposixOldLabelTime[place] = posixNewLblTime; --message(tostring(posixOldLblTime).." IsLabelChange_posixOldLblTime") --message(tostring(posixNewLblTimeF).." IsLabelChange_posixNewLblTime") --message("IsLabelChange_LblId "..tostring(LblId)); end end return LblId end
FTEXT = function (V) --функция подставляет нули при их отсутствии на первой позиции даты-времени V=tostring (V) if string.len (V) == 1 then V = "0".. V end return V end
PUTLABEL = function (index) --[[функция для установки метки на чарт по индексу принимает индекс свечи возвращает время метки в posix-формате и Id метки
входные параметры: -индекс свечи -строка с названием точки для индексации массива установленных меток --]] local Date=""--дата свечки local Time=""--время свечки local Yvalue=0 LabelId=0 --message ("PUTLABELindex.."..tostring(index)); --дата и время свечи Date=t[index].datetime.year..FTEXT(t[index].datetime.month)..FTEXT(t[index].datetime.day); --дата
--привязка к оси У Yvalue = t[index].high; --заполнить структуру данных метки label={ IMAGE_PATH=Settings.LabelName; YVALUE=Settings.Yvalue+Yvalue; DATE=Date, TIME=Time }; --добавляем метку на чарт и запоминаем Id метки в массиве, проиндексированном принятой строкой LabelId = AddLabel(Settings.AssetID,label); --message ("PUTLABELindex LabelId "..tostring(LabelId)) return os.time (t[index].datetime), LabelId end
function GETLABLEPOSIXTIME (LabelId)
--функция принимает Id метки и возвращает ее время в posix-формате local id--Id метки local NewLbLParam = {}--структура параметров метки local LabelTime = ""--строка с данными даты-времени метки local dt={}--структура в формате datetime local osTime=0 ---[[ id = LabelId; --message("GETLABLEPOSIXTIME_LabelId "..tostring(id)); --получаем таблицу с параметрами метки (в нижнем регистре в формате строки) NewLbLParam = GetLabelParams(Settings.AssetID, id); --message("GETLABLEPOSIXTIME_LbLParam.date "..tostring(NewLbLParam.date)); --message("GETLABLEPOSIXTIME_LbLParam.time "..tostring(NewLbLParam.time)); --преобразуем в формат datetime dt.year,dt.month,dt.day = string.match(NewLbLParam.date,"(%d%d%d%d)(%d%d)(%d%d)") if #NewLbLParam.time == 5 then LabelTime="0".. NewLbLParam.time else LabelTime="".. NewLbLParam.time end dt.hour,dt.min,dt.sec = string.match(LabelTime,"(%d%d)(%d%d)(%d%d)") --[[ message("GETLABLEPOSIXTIME_dt.year "..tostring(dt.year)); message("GETLABLEPOSIXTIME_dt.month "..tostring(dt.month)); message("GETLABLEPOSIXTIME_dt.day "..tostring(dt.day)); message("GETLABLEPOSIXTIME_dt.hour "..tostring(dt.hour)); message("GETLABLEPOSIXTIME_dt.min "..tostring(dt.min)); message("GETLABLEPOSIXTIME_dt.sec "..tostring(dt.sec)); message("GETLABLEPOSIXTIME_os.time "..tostring(os.time (dt))); --]] --преобразуем в формат POSIX osTime = os.time (dt); message("GETLABLEPOSIXTIME_os.time "..tostring(osTime)); return osTime;
end
function PUTINDICATOR(LblId1,LblId2,boolIndExist)
local i local j local k local TrndH=0 local TrndL=0 local h local l local CndlIdx1=0 local CndlIdx2=0 local ABIdx={} local IntervalCndlNmbr=0 local koef local VChLines={} local VChTrend={} local VChUp={} local VChDn={} local VChUpMax local VChDnMin local posixLblTime1 local posixLblTime2 local APointIdx local BPointIdx local APointLvl local BPointLvl local idxUpMax local idxDnMin local AidxUp local AidxDn local BidxUp local BidxDn local VChTrnd=0
Sergey Gorokhov написал: Довольно странное имя для файла с картинкой. Тем более что раньше файл назывался "метка.bmp"
Без разницы как он называется. Файл скачан из инета, формат bmp, обрезан до малого размера в paint.
Цитата
Sergey Gorokhov написал: Что значит через рабочий стол пользователя?У Вас терминал на рабочем столе установлен?
Все так и есть, папка терминала находится на рабочем столе
Цитата
Sergey Gorokhov написал: Похожая проблема уже чинилась, проверьте версию терминала, рекомендуем использовать 7.19 или выше.Если с версией всё в порядке, проверьте внимательней все параметры метки.Проблема не воспроизводится, значит явно где-то просто ошиблись.
Версия терминала 7.19.0.51. Если проблема раньше всплывала и была устранена, ткните меня носом, буду благодарен. Проверять мне уже давно нЕчего, я на проверки убил две недели. Если желаете, я скину полный код и сам файл, который пытаюсь вывести в качестве метки. На данный момент для меня это не критично, пока пишу для себя. Но как только отладку завершу, выложу в открытый доступ и если посыпятся такие же вопросы, буду перенаправлять их Вам. Я, конечно, еще слегка плаваю в синтаксисе, но в написании кода я не новичок и прежде, чем обратиться на форум техподдержки, сто раз все проверил. Что же касается параметров метки, то из кода видно, что метка задана глобальной переменной и путь к файлу прописывается сразу после его получения из кода. При этом в коде функции вывода метки я ничего не менял, только две строки в Init(): ScriptPath = getScriptPath().."\\"..Settings.LabelName; --путь к файлу метки label["IMAGE_PATH"]=ScriptPath; первая из которых получает полный путь к файлу, а вторая прописывает его в глобальной переменной. Ткните носом, где я ошибся в синтаксисе этих двух строк. Таким образом, ошибка возникает только в результате смены полного имени пути к файлу метки, остальные параметры остаются неизменными (имею ввиду дату и время). При этом, если файл находится на другом диске или не на рабочем столе на диске С, проблем нет.
Sergey Gorokhov написал: В Lua пути к папкам надо использовать через \\ вместо \
У меня в коде, приведенном в первом посте есть закомментированная строка
--ScriptPath = getScriptPath().."\"..Settings.LabelName; --путь к файлу метки.
Именно в таком виде она и не работает. .LabelName - название файла метки.
Проверьте какой точно путь получается при повторении проблемы. message(ScriptPath)
В settings поменял путь к файлу на LabelName = "down-254095_1280_1". В Init() ввел код: ScriptPath = getScriptPath().."\\"..Settings.LabelName; --путь к файлу метки message("Init_ScriptPath "..ScriptPath); label["IMAGE_PATH"]=ScriptPath;
massage вернула путь до папки LuaIndicators, слитый с именем файла (через рабочий стол пользователя)