Utilizando Otro Datos de Precios de Intervalo de Tiempo en Indicador (Indicador de Yesterday Close Value)

From FxCodeBaseWiki
Jump to: navigation, search

¿Por qué se requieren otros datos de intervalo de tiempo?

A veces un indicador requiere datos de un plazo mayor, por ejemplo, podríamos utilizar para cerrar el día anterior para los cálculos. Sin embargo, es posible encontrar estos datos en la historia de precio que se aplica el indicador a, pero… Imagínense. Obtener el high/low de ayer, los todos datos de ayer debe ser cargado. Si la historia actual es una historia de minuto, en el peor de los casos tenemos que tener por lo menos 2880 barras para poder encontrar esos datos (barras de 1440 minuto para hoy y luego barras de 1440 minuto para el conjunto de ayer). Se trata de una enorme cantidad para cargar y, a continuación para calcular. Pero el sistema tiene acceso a los datos de día, así que ¿por qué no lo usamos? Vamos a hacerlo.

Sin embargo, hay algunos puntos que debemos manejar con cuidado. Vale. Vamos a elaborar un indicador que muestra el precio cerca de ayer sobre el gráfico y discutir cada punto en detalle.

¿Qué es “Yesterday”?

El manejo de la fecha en indicadores es bastante simple. Un valor de fecha es un número, que es sólo un número de días pasados medianoche de Dec, 30 1899. También el formato es conocida como formato de la fecha del Windows Ole Automation. La parte entera de este número es que un número de días y la parte fraccionaria es una parte del día. Por lo tanto, para encontrar un calendario de medianoche fecha y hora, sólo debemos piso el valor (cortar la parte fraccionaria). Para cambiar la fecha a N hace días, nosotros sólo debemos restar N desde la fecha. Por lo tanto, si FXCM utilizado el “calendar clock” para los días de comercial (i.e. el día de comercial comienza en medianoche), el hallazgo de ayer para barra x del fuente sería bastante simple: math.floor(source:date(x)) - 1.

Por desgracia, FXCM’s día de comercial no coincide con el calendario día de comercio. En la actualidad, el día de comercio comienza en 17:00EST ayer y dura hasta 17:00EST hoy. Y para distintas configuraciones puede variar esta fecha. Es de esperar que el núcleo de indicador nos ayuda a manejar el cálculo de las fronteras de vela para una fecha determinada. Hay es una función core.getcandle que devuelve la fecha y hora de la vela del marco de tiempo especificado para que la fecha y hora pertenecen. Como puede ver, esta función requiere dos parámetros que nos permiten "sintonizar" esta función para una determinada configuración: un día comercial y un desplazamiento de la semana comercial contra el calendario. Debe usa host:execute("getTradingDayOffset") y host:execute("getTradingWeekOffset") host llamadas obtener configuraciones del día de comercio y la semana de comercio para particular aplicación del host y conexión.

Por lo tanto, la función para obtener la fecha de ayer correctamente puede ser la siguiente:

function getYesterday(date)
    local offset;
    local weekoffset;

    offset = core.host:execute("getTradingDayOffset");
    weekoffset = core.host:execute("getTradingWeekOffset");

    local today, yesterday;
    today = core.getcandle("D1", date, offset, weekoffset);
    yesterday = today - 1;
    return yesterday;
end

¿Buena? Todavía no. Hay otro punto que deberíamos manejar. Hay períodos "no comerciales" cuando ni comercio es posible ni precios son recibidos. Así que, de hecho, durante una semana sólo lunes abrevadero viernes velas existen. No hay en absoluto ninguna velas "Sábado". Así, cuando nuestro minuto pertenece a una vela del día lunes, la función nos devolverá una vela de Domingo que simplemente no existe! Por lo tanto, para los datos del lunes debemos tomar precio cerca del viernes anterior.

Manejar correctamente, hay otra función útil: core.isnontrading que comprueba si la fecha pertenece a un período de no comerciales (Viernes 17:00EST-Domingo 17:00EST) y devuelve la fecha y tiempo cuando esto período de no comerciales haya terminado. Así, para encontrar la vela adecuada "ayer" tenemos que comprobar si el calendario ayer cae no comerciales período y cambiar la fecha "ayer" en pasado para pasar este período no comerciales.

Debemos modifica nuestra función un poco:

function getYesterday(date)
    local offset;
    local weekoffset;

    offset = core.host:execute("getTradingDayOffset");
    weekoffset = core.host:execute("getTradingWeekOffset");

    local today, yesterday;
    today = core.getcandle("D1", date, offset, weekoffset);
    yesterday = today - 1;

    local nontrading, nontradingend;
    nontrading, nontradingend = core.isnontrading(yesterday, offset);
    if nontrading then
        -- if the calendar yesterday date is non-trading (Friday 17:00 EST - Sunday 17:00 EST)
        -- shift three days back against the non-trading period end (Sunday 17:00 EST).
        yesterday = nontradingend - 3;
    end

    return yesterday;
end

Por lo tanto, ahora, tengan la función getYesterday() podemos más fácil encuentra cómo muchos días necesitamos. El día más viejo es getYesterday(source:date(source:first())), i.e. la barra de “yesterday” que corresponde a la antigua barra disponible. ¿Y uno más reciente? Hay dos casos. La colección de origen podría ser bien un histórico (i.e. una colección cargada hasta la fecha especificada, que nunca cambiará para la ventana de gráfico) o “alive” (i.e. cargado hasta “now” y creciendo continuamente nuevas barras vienen). Según estos casos, la fecha “to” podría ser un día de “yesterday” del más reciente barra disponible (getYesterday(source:date(source:size() - 1)) o … ahora tan bien. Especificar “now”, usamos el valor 0.

Cargando los Datos

Utilizando Host Cargar Datos

OK, ahora sabemos que los datos que necesitamos. El siguiente paso es conseguir estos datos. Marketscope proporciona una interfaz host que permite ejecuta unas funciones de interface específica de la aplicación. Uno de estos funciones es “getHistory”. Por este llame Marketscope carga el instrumento en el intervalo de tiempo especificado y en el rango de tiempo especificado. Véase host:execute(“getHistory”, …) para detalles.

local days;     -- a variable to keep days

-- the function loads the proper day history
function loadData()
    if source:size() < source:first() then
        return ;
    end

    local from, to;
    -- load the data from the date of proper yesterday for
    -- the first available bar of the source
    from = getYesterday(source:date(source:first()));
    -- load to "now" if 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;
    else
        to = getYesterday(source:date(source:size()  1));
    end
    days = host:execute("getHistory", 1, source:instrument(), "D1", from, to, source:isBid());
end

Este importante que después de la llame “getHistory”, Marketscope sólo starts cargar los datos. Toma algún tiempo, pero Marketscope no espere a que los datos están completamente cargados evitar colgantes del indicador. Cuando cargando de los datos se finalizado, Marketscope notificará el indicador por llamar a la función AsyncOperationFinished. Además, Marketscope no se preocupan por el estado del indicador y continuará llamando indicador actualizarse.

Por lo tanto, hay dos puntos que hacer.

Proteger Indicador desde Actualización Mientra los Datos Se Carga

En primer lugar, debemos proteger el indicador de actualización mientras se cargan los datos.

Proteger el indicador de ser recalculado, sólo podemos volver desde la función de actualiza mientra mientras se está cargando los datos. Sólo anida la variable global “loading” por lo tanto que todas funciones podría “know” que datos se está cargando.

local days;         -- day source data
local loading;      -- the loading day data flag

function Prepare()
    ...
    days = nil;
    loading = false;
    ...
end

function Update(period, mode)
    if loading then 
        -- do nothing if data is being loaded
        return ;
    end

    if days == nil then
        loadData();
        return ;
    end
    ...
end        

function AsyncOperationFinished(cookie, success, error)
    if cookie == 1 then
        assert(success, error);
        loading = false;
    end
    return 0;
end

function loadData()
    if loading then
        return ;
    end

    local from, to;

    -- load the data from the date of proper yesterday for
    -- the first available bar of the source
    from = getYesterday(source:date(source:first()));
    -- load to "now" if 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;
    else
        to = getYesterday(source:date[source:size() - 1]);
    end
    loading = true;
    days = host:execute("getHistory", 1, source:instrument(), "D1", from, to, source:isBid());
end

Forzando Actualiza Cuando Datos Se Carga

Ahora bien, tenemos que forzar la actualización de indicador cuando se cargan los datos. Existe una función instance:updateFrom() que fuerza el indicador ser recalculado desde el período especificado. También cuando AsyncOperationFinished devuelve el valor core.ASYNC_REDRAW, Marketscope redibuja el indicador en la ventana del gráfico.

Así, pues no hemos calculado nada aún, tenemos que actualizar nuestro indicador desde los datos más antiguos. Arreglarlo AsyncOperationFinished un poco.

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

Obtenido Datos

Casi terminado. La última pregunta es cómo obtener los datos. Podemos calcular la fecha del día. Por supuesto, nosotros podemos simplemente recorrer la historia del día y averiguar la fecha, pero… Es un poco largo, ¿no? Sin embargo, hay una función que se encuentra la fecha sólo para log2(n) operations - core.findDate().

Por lo tanto, podemos construir la primera versión de nuestra indicador. Vamos a hacerlo y probarlo.

function Init()
    indicator:name("Show Yesterday Close");
    indicator:description("The indicator shows yesterday's close value on the chart");
    indicator:requiredSource(core.Tick);
    indicator:type(core.Indicator);

    indicator.parameters:addColor("clr", "Indicator Line Color", "", core.rgb(255, 255, 0));
end

local source;       -- indicator source data
local first;        -- first available source data
local days;         -- day source data
local loading;      -- the loading day data flag
local loadedFrom;   -- the date from which the days was loaded
local SYC;          -- yesterday close output

local host;
local offset;
local weekoffset;


function Prepare()
    local name;

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

    source = instance.source;
    first = source:first();
    loading = false;
    days = nil;

    host = core.host;
    offset = host:execute("getTradingDayOffset");
    weekoffset = host:execute("getTradingWeekOffset");

    SYC = instance:addStream("SYC", core.Line, name, "SYC", instance.parameters.clr, first);
end

function Update(period, mode)
    if period < first then
        return ;
    end
    if loading then
        return ;
    end

    -- load the data if there is no day
    if days == nil then
        SYC:setBookmark(1, source:first());
        loadData();
        return ;
    end

    -- try to find the value in the days collection
    local yesterday;
    yesterday = getYesterday(source:date(period));
    if yesterday < loadedFrom then
        SYC:setBookmark(1, period);
        loadData();
        return ;
    end

    local i;
    i = core.findDate(days, yesterday, false);
    if i >= 0 then
        SYC[period] = days.close[i];
    end
end

function AsyncOperationFinished(cookie, success, error)
    if cookie == 1 then
        assert(success, error);
        loading = false;
        local i = SYC:getBookmark(1);
        if i < source:first() then
            i = source:first();
        end
        instance:updateFrom(i);
        return core.ASYNC_REDRAW;
    end
    return 0;
end

-- the function loads the proper day history
function loadData()
    if source:size() < source:first() then
        return ;
    end

    if loading then
        return ;
    end

    local from, to;

    if days == nil then
        -- initial data loading

        -- load the data from the date of proper yesterday for
        -- the first available bar of the source
        from = getYesterday(source:date(source:first()));
        -- load to "now" if 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;
        else
            to = getYesterday(source:date(source:size() - 1));
        end
        loading = true;
        loadedFrom = from;
        days = host:execute("getHistory", 1, source:instrument(), "D1", from, to, source:isBid());
    else
        from = getYesterday(source:date(source:first()));
        to = days:date(0);
        loading = true;
        loadedFrom = from;
        host:execute("extendHistory", 1, days, from, to);
    end
end

-- returns the beginning of the "yesterday" date for
-- the specified date
function getYesterday(date)
    local today, yesterday;

    -- get beginning of the today's candle and round it up to the second
    today = core.getcandle("D1", date, offset, weekoffset);
    today = math.floor(today * 86400 + 0.5) / 86400;

    -- get calendar yesterday
    yesterday = today - 1;

    local nontrading, nontradingend;
    nontrading, nontradingend = core.isnontrading(yesterday, offset);
    if nontrading then
        -- if the calendar yesterday date is non-trading (Friday 17:00 EST - Sunday 17:00 EST)
        -- shift three days back against the non-trading period end (Sunday 17:00 EST).
        yesterday = nontradingend - 3;
    end
    return yesterday;
end

Ahora aplica el indicador a el gráfico.

Yesterdayclose img1.png

Funciona!!!! Pero parada, parada, stop… Cuando intentamos cargar más datos en pasado aparentemente dejó de trabajar!

Yesterdayclose img2.png

Cómo Procesar Scroll Back

¡¿Qué ha pasado?! Vale. Recuerde, cargan los datos a partir de ayer para la más antigua de las velas existentes. Cuando empezamos a desplazarsenuestra espalda gráfico, más datos se cargó. ¿Pero quien ha actualizado nuestra colección de barras de día? Por supuesto, nadie lo hizo. Por lo tanto, tenemos que forzar Marketscope cargar nuevos datos cuando la colección de origen se extiende en pasado. Buena noticia es que hay otra llame de cool de la interfaz de host: host:execute(“extendHistory”) que nos ayuda a cargar sólo los datos más en ya cargado historia en vez de cargando las todas historia nuevo. Porque historia puede ser extendido sólo en pasado, es demasiado fácil comprobar si se cargan los datos nuevos. Cuando ocurre, el valor “yesterday” estamos tratando de encontrar es anterior a la fecha hemos cargado previamente la historia del día de.

Por lo tanto, hacer la siguiente modificación en nuestro código:

  1. Mantiene la fecha “from” cargamos la historia del día anterior.
  2. En el caso de la colección de día ya existe, ampliarlo en lugar de cargar los datos más antiguos.

Por lo tanto, anida nuevo loadedFrom variable global. Y cambia las funciones de Update y loadData.

Hay una nueva versión de indicador:

function Init()
    indicator:name("Show Yesterday Close");
    indicator:description("The indicator shows yesterday's close value on the chart");
    indicator:requiredSource(core.Tick);
    indicator:type(core.Indicator);

    indicator.parameters:addColor("clr", "Indicator Line Color", "", core.rgb(255, 255, 0));
end

local source;       -- indicator source data
local first;        -- first available source data
local days;         -- day source data
local loading;      -- the loading day data flag
local loadedFrom;   -- the oldest yesterday bar we already loaded
local SYC;          -- yesterday close output

local host;
local offset;
local weekoffset;

function Prepare()
    local name;

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

    source = instance.source;
    first = source:first();
    loading = false;
    days = nil;

    host = core.host;
    offset = host:execute("getTradingDayOffset");
    weekoffset = host:execute("getTradingWeekOffset");
    SYC = instance:addStream("SYC", core.Line, name, "SYC", instance.parameters.clr, first);
end

function Update(period, mode)
    if period < first then
        return ;
    end
    if loading then
        return ;
    end

    -- load the data if there is no day
    if days == nil then
        loadData();
        return ;
    end

    -- try to find the value in the days collection
    local yesterday;
    yesterday = getYesterday(source:date(period));

    -- check whether such day bar is not loaded yet
    if yesterday < loadedFrom then
        loadData();
        return ;
    end

    -- find the day data
    local i;
    i = core.findDate(days, yesterday, false);
    if i >= 0 then
        SYC[period] = days.close[i];
    end
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

-- the function loads the proper day history
function loadData()
    if source:size() < source:first() then
        return ;
    end

    if loading then
        return ;
    end

    local from, to;

    if days == nil then
        -- initial data loading

        -- load the data from the date of proper yesterday for
        -- the first available bar of the source
        from = getYesterday(source:date(source:first()));
        -- load to "now" if 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;
        else
            to = getYesterday(source:date(source:size() - 1));
        end
        loading = true;
        loadedFrom = from;
        days = host:execute("getHistory", 1, source:instrument(), "D1", from, to, source:isBid());
    else
        from = getYesterday(source:date(source:first()));
        to = days:date(0);
        loading = true;
        loadedFrom = from;
        host:execute("extendHistory", 1, days, from, to);
    end
end

-- returns the beginning of the "yesterday" date for
-- the specified date
function getYesterday(date)
    local today, yesterday;

    -- get beginning of the today's candle and round it up to the second
    today = core.getcandle("D1", date, offset, weekoffset);
    today = math.floor(today * 86400 + 0.5) / 86400;

    -- get calendar yesterday
    yesterday = today - 1;

    local nontrading, nontradingend;
    nontrading, nontradingend = core.isnontrading(yesterday, offset);
    if nontrading then
        -- if the calendar yesterday date is non-trading (Friday 17:00 EST - Sunday 17:00 EST)
        -- shift three days back against the non-trading period end (Sunday 17:00 EST).
        yesterday = nontradingend - 3;
    end
    return yesterday;
end

Instala el indicador y probarlo. Wow. Ahora, cuando la versión anterior dejó de funcionar durante el desplazamiento, la nueva versión funciona de todos modos! ¡ Felicidades!

Yesterdayclose img3.png

Notas

Cómo Depurar Tales Indicadores Utilizando Using SDK Debugger?

Primera, debe prepara los datos. Puede guarda archivos de los datos de precio utilizando Marketscope. Simplemente abra algún instrumento en ambas resoluciones (por ejempl 30 minutos y 1 día) y a continuación guarda los dos historias en el formato de datos de Indicore SDK (File->Export to Indicore…). También puede descargar los datos automáticamente a través de SDK Debugger utilizando el servidor del quotes manager (Tools->Load Quotes).

Cuando comienza el indicador, elija el la historia de timeframe más pequeña en los parámetros del indicador. También, por favor comprueba el casilla de verificación de “Predeliver Data” en los parámetros del indicador permitir el debugger muestra los todos historia disponible a el indicador, como Marketscope hace. Sin este opción, el debugger sólo mostrará la historia sólo hasta el bar actualmente simulado. Cuando el indicador ejecuta la llamada “getHistory”, el prompt para datos aparece. Elija los datos de día guardado anteriormente desde Marketscope en que prompt o usa datos desde el servidor del quotes manager.

¿Cómo Controlar Timeframes Distinto 1 Día?

Hacer el indicador de ejemplo más simple, utiliza un intervalo de tiempo de 1-día. Sin embargo, desea hacer lo mismo para los otros datos de intervalo de tiempo, por ejemplo, usa 1-hora precio de cierre en vez de una día de precio cierre. Para ello, puede:

  • Permiten al usuario elegir el intervalo de tiempo. Agregar un parámetro de cadena a la lista de parámetros de indicadores y, a continuación, establecer el indicador de selector de plazos para ese parámetro. Como resultado, en la función de preparación, el parámetro correspondiente contendrá el código del marco de tiempo elegido por el usuario.
function Init()
    ...
    indicator.parameters:addString("TF", "Timeframe", "", "m1");
    indicator.parameters:setFlag("TF", core.FLAG_PERIODS);
    ...
end
  • Calcula la longitud de una vela del intervalo de tiempo especificado en día. Usa la función [c]core.getcandle()[/c] obtener la fecha de inicio de algún vela y la fecha de inicio del siguiente vela. La diferencia entre estos dos valores es la longitud de la vela en días. Recomiendo también calcular todas las fechas en segundos en vez de días para evitar errores de redondeo.
local candleLength;  -- length of candle in seconds

function Prepare()
    ...
    offset = host:execute("getTradingDayOffset");
    weekoffset = host:execute("getTradingWeekOffset");
    ...
    local thisCandle, nextCandle;
    thisCandle, nextChandle = core.getcandle(instance.parameters.TF, core.now(), offset, weekoffset);
    candleLength = math.floor((nextCandle - thisCandle) * 86400 + 0.5);
    ...
end
  • Utilizar la longitud de vela calculada anteriormente en vez del valor de 1 día en su función “getPreviousBar”. Por favor nota que mientra la longitud de la vela podría ser diferente, el período de no comerciales está aún en días. Por lo tanto, manejar con cuidado.
function getPreviousBar(date)
    local curr, prev;

    -- get beginning of the today's candle and round it up to the second
    curr = core.getcandle(instance.parameters.TF, date, offset, weekoffset);
    today = math.floor(today * 86400 + 0.5);

    -- get calendar yesterday
    prev = (curr  candleLength) / 86400;

    local nontrading, nontradingend;
    nontrading, nontradingend = core.isnontrading(prev, offset);
    if nontrading then
        prev = prev - 2;
        prev = math.floor(prev * 86400 + 0.5);

        -- get calendar yesterday
        prev = (prev  candleLength) / 86400;
    end
    return prev;
end


Este Artí­culo en Otros Idiomas

Language: English  • español • français • русский • 中文 • 中文(繁體)‎