Search code examples
octavetime-seriesvectorizationmoving-average

Perform a vectorized exponential moving average in octave


In GNU Octave, would like to calculate an n-day exponential moving average of a vector without using a for-loop.

I am able to do this with a for loop but it is inefficient. I would like to use the filter function, however I am unsure how to get this to work correctly.


Solution

  • Reinventing the wheel on octave exponential moving average for a vector is silly. Just copy and paste movavg.m function defined in the octave financial package here: https://octave.sourceforge.io/financial:

    function [varargout] = movavg(asset, lead, lag, alpha = 0) 
      if nargin < 3 || nargin > 4 
        print_usage (); 
      endif 
      if lead > lag 
        error ("lead must be <= lag") 
      elseif ischar (alpha) 
        if ! strcmpi (alpha, "e") 
          error ("alpha must be 'e' if it is a char"); 
        endif 
      elseif ! isnumeric (alpha) 
        error ("alpha must be numeric or 'e'") 
      endif 
      ## Compute the weights 
      if ischar (alpha) 
        lead = exp(1:lead); 
        lag  = exp(1:lag); 
      else 
        lead = (1:lead).^alpha; 
        lag  = (1:lag).^alpha; 
      endif 
      ## Adjust the weights to equal 1 
      lead = lead / sum (lead); 
      lag  = lag / sum (lag); 
      short = asset; 
      long  = asset; 
      for i = 1:length (asset) 
        if i < length (lead) 
          ## Compute the run-in period 
          r        = length (lead) - i + 1:length(lead); 
          short(i) = dot (asset(1:i), lead(r))./sum (lead(r)); 
        else 
          short(i) = dot (asset(i - length(lead) + 1:i), lead); 
        endif 
        if i < length (lag) 
          r       = length (lag) - i + 1:length(lag); 
          long(i) = dot (asset(1:i), lag(r))./sum (lag(r)); 
        else 
          long(i) = dot (asset(i - length(lag) + 1:i), lag); 
        endif 
      endfor 
      if nargout > 0 
        varargout{1} = short; 
      else 
        plot((1:length(asset))', [asset(:), long(:), short(:)]); 
      endif 
      if nargout > 1 
        varargout{2} = long; 
      endif 
    endfunction 
    

    And invoke thustly:

    foo = [NaN; 1;4;8;10;-3;3;4;0;0;3;4;5;6;7;8;9];     
    lead = 7 
    lag = 7
    alpha = 'e' 
    movavg(foo, lead, lag, 'e')
    

    Which prints:

           NaN
           NaN
           NaN
           NaN
           NaN
           NaN
           NaN
       3.39851
       1.24966
       0.45742
       2.06175
       3.28350
       4.37315
       5.40325
       6.41432
       7.42128
       8.42441