New challenge - find high of last day

Moderator: admin

New challenge - find high of last day

Postby iamrich » Wed Aug 18, 2010 2:35 pm

I have a stream, eg. 15 min, and i want to find out the high of the last two days.

function Init()
...
indicator:requiredSource(core.Bar);
indicator:type(core.Indicator);
...
end

function Prepare()
source= instance.source;
...
end

function Update(period)
if source:hasData(period) then

-- with source.high[period] i can find out the high values
source.high[period]
-- with source:date(period) the date
source:date(period)

...
end

What is the easiest and fastest way to find out the high of the last two days. Consider my stream is eg 15 minutes.

Then I want to draw a horizontal line of the last two days high in my current view. Or at least from the high to the end of the stream.
that will be done with: core.host:execute(...)
Theoretical it's enough to make the line on the screen only (on the visible area only). If I make a horizontal line with Marketscope is it only on the screen or is it all over the stream? (i hope that question is clear)

Some suggestions are welcome.

btw: function Update(period) runs always first through the complete stream from 0 to source:size()-1. Is that correct? And then with every new update of period in realtime. Correct?
iamrich
 
Posts: 10
Joined: Tue Aug 03, 2010 9:33 am

Re: New challenge - find high of last day

Postby Nikolay.Gekht » Thu Aug 19, 2010 9:15 am

The simplest way is to just load day data and find the info there. You can take a look at PIVOT for example. I'll try to prepare the simpler example today, ok? Another point to "Let's write together articles". :-)
Nikolay.Gekht
FXCodeBase: Site Admin
 
Posts: 1235
Joined: Wed Dec 16, 2009 6:39 pm
Location: Cary, NC

Re: New challenge - find high of last day

Postby Nikolay.Gekht » Thu Aug 19, 2010 1:30 pm

iamrich wrote:btw: function Update(period) runs always first through the complete stream from 0 to source:size()-1. Is that correct? And then with every new update of period in realtime. Correct?

In common yes. When the indicator is initially applied on the chart, the update function is called for each bar of the source starting from 0 and to source:size() - 1. The update function for the last (currently unfinished) candle is called almost for every tick. But. In case the user scrolls the chart back and more data is loaded as result, the update function is called again starting from new 0 bar of the source.

Because during such operation the bar indexes can be shifted (e.g. bar 0 before scroll becomes to bar N when a chart is scrolled back to N bars), it is not recommended to keep indexes of bars in the indicator to remember a bar. The bookmarks is more safe method to do that.

The article about accessing yesterday's value is here: viewtopic.php?f=28&t=1868
Nikolay.Gekht
FXCodeBase: Site Admin
 
Posts: 1235
Joined: Wed Dec 16, 2009 6:39 pm
Location: Cary, NC

Re: New challenge - find high of last day

Postby iamrich » Thu Aug 19, 2010 4:25 pm

Thanks for "The article about accessing yesterday's value is here: viewtopic.php?f=28&t=1868"
Generally I understood and could follow the article and it's very helpful.

What I didn't understand is ... see comments in bold
from = getYesterday(source:date(source:first())); -- source:first() sounds good; is anyway always 0, isn't it?
-- load to "now" is the source is up to "now" or
-- to the day which corresponds to the latest
-- day of the collection.
if source:isAlive() then
to = 0; -- WHY THIS CAN BE 0? from and to would be 0. Is totally unclear to me
else
to = getYesterday(source:date(source:size() – 1));
end
days = host:execute("getHistory", 1, source:instrument(), "D1", from, to, source:isBid());

In my particular case i need simply the last day (one day) i would be not needed to read the full stream.
If i read just
days = host:execute("getHistory", 1, source:instrument(), "D1",getYesterday(NOW) , NOW, source:isBid());
it should be enough? (maybe NOW should be substituted to source:size()-1 if NOW is updated during scolling)
iamrich
 
Posts: 10
Joined: Tue Aug 03, 2010 9:33 am

Re: New challenge - find high of last day

Postby Nikolay.Gekht » Thu Aug 19, 2010 8:28 pm

- source:first() sounds good; is anyway always 0, isn't it?

1) Generally, in case source is a pure price and this price is loaded from the FXCM's price server, the index of the oldest available price is 0.

But there are other ways to get prices, for example - indicators (such as Show Index, Heikin-Ashi) - which can produce other :first() value, which will not be equal to 0.

So, if you want to make your indicator "completely compatible" with the spec, I recommend to use :first() value always to check the oldest available bar.

0 vs NOW


2) As far as I can see, you are confused a little bit between the index of the bar and the date of the bar.

You get access to the bar in the source via index. Index is an integer number, just an order of bar inside the source. Index 0 means the oldest available bar, size() - 1 - is the newest available bar.

The date is presented a number too. But, for example, today's (Aug, 19 2010) is 40409. You can get date using source:date() method.

The getHistory/extendHistory host calls takes date not an index in the source collection. For example, get other instrument for the same range as we have in the source will be
core.host:execute("getHistory", 1, "USD/NOK", source:barSize(), source:date(0), source:date(source:size() - 1), source:isBid());

However, sometimes you don't need to specify an exact date. For example:
a) You want to tell marketscope: load the requested collection up to the current moment and then apply all incoming ticks, so the collection will contain the most fresh data. In that case you do not specify "to" date, but use a number 0 instead.
b) You want to tell marketscope: load the default number of bars (300 bars) to the specified date. In that case you specify 0 instead of the "from" date.

0 as a date cannot be used in real Marketscope life. It corresponds to Dec, 30 1899, when Forex didn't exist.
Nikolay.Gekht
FXCodeBase: Site Admin
 
Posts: 1235
Joined: Wed Dec 16, 2009 6:39 pm
Location: Cary, NC

Re: New challenge - find high of last day

Postby iamrich » Sun Aug 22, 2010 11:21 am

I have made an indicator to draw lines of day high/low when I am in a 15min period chart. On the parameters you can say how many (up to 5) and which day periods from the past you want to draw.
E.g. 1 and 5 means you want to have day high/low 1 dayperiod from the past (quasi yesterday) and 5 dayperiods (5 active days ago) from the past. Because I don't work with date, but with dayperiods, I don't care the weekend and so on.

My questions:

a) why after calling drawlines() the lines are not visible. Only when I scroll back in the history.That's pretty strange to me.
b) after changing the parameters (doubleklick on DAYHIGHLOW (EUR/USD)) I get an error [string "DayHighLow.luw"]: 86: Index is out of range

I know my indicator code is not finish yet, but I'm afraid I still didn't understand proper some basics. Maybe someone can give me a hint?

Here the code
Code: Select all
function Init()
   indicator:name("DayHighLow");
   indicator:description("Day High Low");
   indicator:requiredSource(core.Bar);
   indicator:type(core.Indicator);

   indicator.parameters:addInteger("day1", "offset day1", "Offset of day1 (1 is yesterday)", 1, 0, 50);
   indicator.parameters:addInteger("day2", "offset day2", "Offset of day2 (2 is two days ago)", 2, 0, 50);
   indicator.parameters:addInteger("day3", "offset day3", "Offset of day3 (3 is three days ago)", 0, 0, 50);
   indicator.parameters:addInteger("day4", "offset day4", "Offset of day4 (4 is four days ago)", 0, 0, 50);
   indicator.parameters:addInteger("day5", "offset day5", "Offset of day5 (5 is five days ago)", 0, 0, 50);
   
   indicator.parameters:addColor("DayHigh", "Color of Day High Line", "", core.rgb(0, 255, 0));
   indicator.parameters:addColor("DayLow", "Color of Day Low Line", "", core.rgb(255, 0, 0));
end

local first;
local source= nil;
local host;

local day1, day2, day3, day4, day5; -- days high/low to draw; 0= off; 1= 1 day ago, 2= two days ago, ...

-- async loading of data
local loading= false; -- the loading day data flag
local sourcedays= nil; -- a variable to keep days


function Prepare()
   source= instance.source;
   first= source:first();

   local name= profile:id() .. "(" .. source:name() .. ")";
   instance:name(name);
   
   host= core.host;
   
   day1= instance.parameters.day1;
   day2= instance.parameters.day2;
   day3= instance.parameters.day3;
   day4= instance.parameters.day4;
   day5= instance.parameters.day5;
      
end

-- Indicator calculation routine
function Update(period)

   if loading then -- as long async loading is active ...
     return; -- ... we wait
   end
   
   if sourcedays == nil then
      sourcedays= host:execute("getHistory", 1, source:instrument(), "D1", core.now()-100, core.now(), true);
      return;
   end

   if source:hasData(period) == false then
      return;
   end
   
   if day1 > 0 then
      drawlines(sourcedays:size() - 1 - day1, "Day1 high", "Day1 low");
   end
   if day2 > 0 then
      drawlines(sourcedays:size() - 1 - day2, "Day2 high", "Day2 low");
   end
   if day3 > 0 then
      drawlines(sourcedays:size() - 1 - day3, "Day3 high", "Day3 low");
   end
   if day4 > 0 then
      drawlines(sourcedays:size() - 1 - day3, "Day4 high", "Day4 low");
   end
   if day5 > 0 then
      drawlines(sourcedays:size() - 1 - day3, "Day5 high", "Day5 low");
   end

      
   loading= 1; -- simulate loading, lock update
   
end


function drawlines(dayperiod, texthigh, textlow)

   core.host:execute("drawLine", dayperiod, sourcedays:date(dayperiod), sourcedays.high[dayperiod], source:date(source:size()-1), sourcedays.high[dayperiod], instance.parameters.DayHigh, core.LINE_DOT, 5, "Hello und so");
   core.host:execute("drawLine", dayperiod+10000, sourcedays:date(dayperiod), sourcedays.low[dayperiod], source:date(source:size()-1), sourcedays.low[dayperiod], instance.parameters.DayLow, core.LINE_DOT, 5, "Hello und so");
   core.host:execute("drawLabel", dayperiod, source:date(source:size()-1), sourcedays.high[dayperiod], texthigh);
   core.host:execute("drawLabel", dayperiod+10000, source:date(source:size()-1), sourcedays.low[dayperiod], textlow);

end

function AsyncOperationFinished(cookie, success, error)
   if cookie == 1 then
      assert(success, error);
      loading= false;
      instance:updateFrom(source:first());
      return core.ASYNC_REDRAW;
   end
   return 0;
end
iamrich
 
Posts: 10
Joined: Tue Aug 03, 2010 9:33 am

Re: New challenge - find high of last day

Postby Nikolay.Gekht » Mon Aug 23, 2010 9:51 am

I looked over the code. The extremely good for the first attempt of the data-loading indicators.

In fact, there is only one serious problem I see which can cause the errors. You don't set loading flag after the request to load the data. So, if the indicator is called again before the data is loaded - it will fail (as it happend in your example). So, just add loading = true right after core.host:execute("getHistory"). And the indicator starts to work.

However, I recommend to do a number of additional changes:

1) If you want to load the data "up to now" don't use core:now(). This function returns the local date time while the server always works in EST/EDT time zone. Just use 0 to get the history up to now and get it subscribed, so the collection will always provide you the most recent data. For example, when a new day appears you will not have to reload the data. The candle for new day will just appear as well.

2) The idea to protect the indicator from permanent repaining is great, but here is better way to do that. Do you work only when the indicator is called to the latest of available bar and do it only when a new latest available bar appears.

3) I do not recommend to use bar indexes for any other purposes. As well as you don't recommend to keep them stored. The price series can be extended/compressed in background, so the same bar can have other number. So, I introduced the id for each line in your code.

A few small things was also fixed. But, once again, the excellent first work!

Please find the fixed code below. I commended every change I made.
Code: Select all
function Init()
   indicator:name("DayHighLow");
   indicator:description("Day High Low");
   indicator:requiredSource(core.Bar);
   indicator:type(core.Indicator);

   indicator.parameters:addInteger("day1", "offset day1", "Offset of day1 (1 is yesterday)", 1, 0, 50);
   indicator.parameters:addInteger("day2", "offset day2", "Offset of day2 (2 is two days ago)", 2, 0, 50);
   indicator.parameters:addInteger("day3", "offset day3", "Offset of day3 (3 is three days ago)", 0, 0, 50);
   indicator.parameters:addInteger("day4", "offset day4", "Offset of day4 (4 is four days ago)", 0, 0, 50);
   indicator.parameters:addInteger("day5", "offset day5", "Offset of day5 (5 is five days ago)", 0, 0, 50);

   indicator.parameters:addColor("DayHigh", "Color of Day High Line", "", core.rgb(0, 255, 0));
   indicator.parameters:addColor("DayLow", "Color of Day Low Line", "", core.rgb(255, 0, 0));
end

local first;
local source= nil;
local host;

local day1, day2, day3, day4, day5; -- days high/low to draw; 0= off; 1= 1 day ago, 2= two days ago, ...

-- async loading of data
local loading= false; -- the loading day data flag
local sourcedays= nil; -- a variable to keep days


function Prepare()
   source= instance.source;
   first= source:first();

   local name= profile:id() .. "(" .. source:name() .. ")";
   instance:name(name);

   host= core.host;

   day1= instance.parameters.day1;
   day2= instance.parameters.day2;
   day3= instance.parameters.day3;
   day4= instance.parameters.day4;
   day5= instance.parameters.day5;

end

local lastProceed = nil;

-- Indicator calculation routine
function Update(period)

   if loading then -- as long async loading is active ...
     return; -- ... we wait
   end

   if sourcedays == nil then
      -- ng: for "now" just use 0 instead of a "to" date
      sourcedays= host:execute("getHistory", 1, source:instrument(), "D1", core.now()-100, 0, true);
      -- ng: protect ourselves from accessing to the history while data being loaded
      loading = true;
      return;
   end

   --ng: can comment it out, does not matter in our case at all
   --if source:hasData(period) == false then
   --      return;
   --end

   -- ng: draw lines only when the latest period is being calculated. no need
   -- to repeat it every time. Moreover, since the last period changes for every tick -
   -- just do it when the indicator is called for the latest period and
   -- this period is just appeared.
   if period == source:size() - 1 then
        if lastProceed ~= nil and lastProceed == source:serial(period) then
            return ;
        end
        lastProceed = source:serial(period);

       -- ng: use the index in the collection is not a good idea, since the
       -- date can be changed dynamically. So, just introudced
       if day1 > 0 then
          drawlines(sourcedays:size() - 1 - day1, "Day1 high", "Day1 low", 1);
       end
       if day2 > 0 then
          drawlines(sourcedays:size() - 1 - day2, "Day2 high", "Day2 low", 2);
       end
       if day3 > 0 then
          drawlines(sourcedays:size() - 1 - day3, "Day3 high", "Day3 low", 3);
       end
       if day4 > 0 then
          -- ng: fix day offet (was day 3)
          drawlines(sourcedays:size() - 1 - day4, "Day4 high", "Day4 low", 4);
       end
       if day5 > 0 then
          -- ng: fix day offet (was day 3)
          drawlines(sourcedays:size() - 1 - day5, "Day5 high", "Day5 low", 5);
       end
    end
end


function drawlines(dayperiod, texthigh, textlow, id)
   core.host:execute("drawLine", id * 10 + 0, sourcedays:date(dayperiod), sourcedays.high[dayperiod], source:date(source:size()-1), sourcedays.high[dayperiod], instance.parameters.DayHigh, core.LINE_DOT, 5, "Hello und so");
   core.host:execute("drawLine", id * 10 + 1, sourcedays:date(dayperiod), sourcedays.low[dayperiod], source:date(source:size()-1), sourcedays.low[dayperiod], instance.parameters.DayLow, core.LINE_DOT, 5, "Hello und so");

   core.host:execute("drawLabel", id * 10 + 0, source:date(source:size()-1), sourcedays.high[dayperiod], texthigh);
   core.host:execute("drawLabel", id * 10 + 1, source:date(source:size()-1), sourcedays.low[dayperiod], textlow);
end

function AsyncOperationFinished(cookie, success, error)
   if cookie == 1 then
      assert(success, error);
      loading = false;
      instance:updateFrom(source:first());
      return core.ASYNC_REDRAW;
   end
   return 0;
end
Nikolay.Gekht
FXCodeBase: Site Admin
 
Posts: 1235
Joined: Wed Dec 16, 2009 6:39 pm
Location: Cary, NC

Re: New challenge - find high of last day

Postby iamrich » Mon Aug 23, 2010 4:33 pm

WOW. Thanks a lot for your support and great words. I have updated my code to your suggestions and yeah, it works now.

Just a minor thing: in your code you added to check
if lastProceed ~= nil and lastProceed == source:serial(period) then
return ;
end

But then, lastProceed is always nil.
I have simply deleted and changed in
if lastProceed == source:serial(period) then
return ;
end

But source:serial(period) is a great tip.

To make the same code without dayperiods seems a bit more hard. I have to investigate a bit more. I have already tried but the bookmark is not working for the source stream, just for an output stream. Anyway.

Again, thank you.

Code: Select all
-- DayHighLow
-- draws up to 5 lines of day high/low
-- maximum 50 dayperiods back


-- TODO:
-- drawline, when tooltip works add the value of high/low in the tooltip
-- autocalculate slightly different linecolors
-- change code to work date instead of dayperiods


function Init()
   indicator:name("DayHighLow");
   indicator:description("Day High Low");
   indicator:requiredSource(core.Bar);
   indicator:type(core.Indicator);

   indicator.parameters:addInteger("day1", "offset day1", "Offset of day1 (1 is dayperiod back (like yesterday))", 1, 0, 50);
   indicator.parameters:addInteger("day2", "offset day2", "Offset of day2 (2 is two dayperiods ago)", 2, 0, 50);
   indicator.parameters:addInteger("day3", "offset day3", "Offset of day3 (3 is three dayperiods ago)", 0, 0, 50);
   indicator.parameters:addInteger("day4", "offset day4", "Offset of day4 (4 is four dayperiods ago)", 0, 0, 50);
   indicator.parameters:addInteger("day5", "offset day5", "Offset of day5 (5 is five dayperiods ago)", 0, 0, 50);
   
   indicator.parameters:addColor("DayHigh", "Color of Day High Line", "", core.rgb(0, 255, 0));
   indicator.parameters:addColor("DayLow", "Color of Day Low Line", "", core.rgb(255, 0, 0));
end

local first;
local source= nil;
local host;

local day1, day2, day3, day4, day5; -- days high/low to draw; 0= off; 1= 1 day ago, 2= two days ago, ...

-- async loading of data
local loading= false; -- the loading day data flag
local sourcedays= nil; -- a variable to keep days

local lastProceed= nil;


function Prepare()
   source= instance.source;
   first= source:first();

   local name= profile:id() .. "(" .. source:name() .. ")";
   instance:name(name);
   
   host= core.host;
   
   day1= instance.parameters.day1;
   day2= instance.parameters.day2;
   day3= instance.parameters.day3;
   day4= instance.parameters.day4;
   day5= instance.parameters.day5;
      
end


-- Indicator calculation routine
function Update(period)

   if loading then -- as long async loading is active ...
     return; -- ... we wait
   end
   
   if sourcedays == nil then
      sourcedays= host:execute("getHistory", 1, source:instrument(), "D1", core.now()-100, 0, true);
      enable= true;
      return;
   end

   if period == source:size() - 1 then
      
      if lastProceed == source:serial(period) then
         return;
      end
      
      lastProceed= source:serial(period);
      
      if day1 > 0 then
         drawlines(1, sourcedays:size() - 1 - day1, string.format("Day%d high", day1), string.format("Day%d low", day1));
      end
      if day2 > 0 then
         drawlines(2, sourcedays:size() - 1 - day2, string.format("Day%d high", day2), string.format("Day%d low", day2));
      end
      if day3 > 0 then
         drawlines(3, sourcedays:size() - 1 - day3, string.format("Day%d high", day3), string.format("Day%d low", day3));
      end
      if day4 > 0 then
         drawlines(4, sourcedays:size() - 1 - day4, string.format("Day%d high", day4), string.format("Day%d low", day4));
      end
      if day5 > 0 then
         drawlines(5, sourcedays:size() - 1 - day5, string.format("Day%d high", day5), string.format("Day%d low", day5));
      end
   
   end
   
   
end


function drawlines(id, dayperiod, texthigh, textlow)

   core.host:execute("drawLine", id * 10 + 0, sourcedays:date(dayperiod), sourcedays.high[dayperiod], source:date(source:size()-1), sourcedays.high[dayperiod], instance.parameters.DayHigh, core.LINE_SOLID, 1, texthigh);
   core.host:execute("drawLine", id * 10 + 1, sourcedays:date(dayperiod), sourcedays.low[dayperiod], source:date(source:size()-1), sourcedays.low[dayperiod], instance.parameters.DayLow, core.LINE_SOLID, 1, textlow);
   
   core.host:execute("drawLabel", id * 10 + 0, source:date(source:size()-1), sourcedays.high[dayperiod], texthigh);
   core.host:execute("drawLabel", id * 10 + 1, source:date(source:size()-1), sourcedays.low[dayperiod], textlow);

end

function AsyncOperationFinished(cookie, success, error)
   if cookie == 1 then
      assert(success, error);
      loading= false;
      instance:updateFrom(source:first());
      return core.ASYNC_REDRAW;
   end
   return 0;
end
iamrich
 
Posts: 10
Joined: Tue Aug 03, 2010 9:33 am

Re: New challenge - find high of last day

Postby Nikolay.Gekht » Mon Aug 23, 2010 5:05 pm

iamrich wrote:But then, lastProceed is always nil.
I have simply deleted and changed in
if lastProceed == source:serial(period) then
return ;
end

Yes, it will work. The original variant was just more Java-y or C++-y :-) The habit, nothing else. :-)
BTW, please feel free to put this indicator to the custom indicators section (however the next monday, when beta becomes to production).
Nikolay.Gekht
FXCodeBase: Site Admin
 
Posts: 1235
Joined: Wed Dec 16, 2009 6:39 pm
Location: Cary, NC


Return to Indicator Development

Who is online

Users browsing this forum: No registered users and 12 guests