Incremental Technical Analysis indicators

Because technical indicators that are calculated from a DataFrame column need the whole input vector in order to calculate new values of indicators… in a streaming environnement that’s not really appropriate because you calculate the same values several times.
I suggest reading GitHub - nardew/talipp: talipp - incremental technical analysis library for python which perfectly explains that.

talipp (or tali++) is a Python library implementing financial indicators for technical analysis. The distinctive feature of the library is its incremental computation which fits extremely well real-time applications or applications with iterative input in general.

Unlike existing libraries for technical analysis which typically have to work on the whole input vector in order to calculate new values of indicators, talipp due to its incremental architecture calculates new indicators’ values exclusively based on the delta input data. That implies, among others, it requires O(1) time to produce new values in comparison to O(n) (or worse) required by other libraries.

Supported incremental operations include:

  • appending new values to the input
  • updating the last input value
  • removing arbitrary number of the input values

Currently IncTA only support appending new values to the input.
I will try to tackle updating and removing features also.

But my priority will be to better integrates with OnlineStats (and implements indicators on top of OnlineStatsBase

I must admit that I first need to better understand it (see my question).
I need for example to be able to save previous values of an indicator (Should I use Lag? it seems to be deprecated in favor of CircBuf (from OnlineStatsBase) not CircularBuffer from DataStructures !) but there is also MovingWindow from OnlineStats
Depending only from OnlineStatsBase and not on the whole OnlineStats is probably a better idea.

Here is my current implementation idea

using Test

using OnlineStatsBase

const ATOL = 0.00001
const P = 20

const CLOSE_TMPL = [
    10.5,
    9.78,
    10.46,
    10.51,
    10.55,
    10.72,
    10.16,
    10.25,
    9.4,
    9.5,
    9.23,
    8.5,
    8.8,
    8.33,
    7.53,
    7.61,
    6.78,
    8.6,
    9.21,
    8.95,
    9.22,
    9.1,
    8.31,
    8.37,
    8.3,
    7.78,
    8.05,
    8.1,
    8.08,
    7.49,
    7.58,
    8.17,
    8.83,
    8.91,
    9.2,
    9.76,
    9.42,
    9.3,
    9.32,
    9.04,
    9.0,
    9.33,
    9.34,
    8.49,
    9.21,
    10.15,
    10.3,
    10.59,
    10.23,
    10.0,
]

mutable struct SMA_v2{Tval} <: OnlineStat{Tval}
    value::Union{Missing,Tval}
    n::Int
    input::CircBuff

    function SMA_v2{Tval}(; period = SMA_PERIOD) where {Tval}
        input = CircBuff(Tval, period, rev=false)
        new{Tval}(missing, 0, input)
    end
end

function OnlineStatsBase._fit!(ind::SMA_v2, val)
    if ind.n <= length(ind.input.value)
        ind.n += 1
    end
    fit!(ind.input, val)
    values = value(ind.input)
    ind.value = sum(values) / length(values)  # mean(values)
end

mutable struct Memory{T} <: OnlineStat{T}
    value::Union{Missing,T}
    n::Int
    ind::OnlineStat{T}
    history::CircBuff
    function Memory(ind::OnlineStat{T}; n = 3) where {T}
        history = CircBuff(T, n, rev=false)
        new{T}(missing, 0, ind, history)
    end
end
function OnlineStatsBase._fit!(memory::Memory, val)
    if memory.n <= length(memory.history.value)
        memory.n += 1
    end
    fit!(memory.ind, val)
    val = value(memory.ind)
    fit!(memory.history, val)
    memory.value = val
end
Base.lastindex(ind::Memory) = length(ind.history.value)
Base.getindex(ind::Memory, index) = ind.history[index]

@testset "simple indicators" begin
    ind = SMA_v2{Float64}(period = P)
    ind = Memory(ind, n = 3)
    fit!(ind, CLOSE_TMPL)
    @test isapprox(ind[end-2], 9.075500; atol = ATOL)
    @test isapprox(ind[end-1], 9.183000; atol = ATOL)
    @test isapprox(value(ind), 9.308500; atol = ATOL)  # or ind.value[end]
end
1 Like