Back to Community
My Wilder's MA looks quite different in Notebook than in Algorithm platform...

I build the Wilder's MA with the following formular:

WMA = price today * K + EMA yesterday * (1-K)

(where K =1/N and N = the number of periods)

I implement it in Notebook and in Algorithm Platform. But it looks quite different. In Notebook it looks quite smooth. But in Algorithm platform it looks quite zick-zack.

Loading notebook preview...
10 responses

Here is in Algorithm platform.

Clone Algorithm
5
Loading...
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
"""
This is a template algorithm on Quantopian for you to adapt and fill in.
"""
import quantopian.algorithm as algo
import pandas as pd
import numpy as np

def initialize(context):
    """
    Called once at the start of the algorithm.
    """
    context.ma_length = 200
    context.spy = symbol('SPY')
    
    # Rebalance every day, 1 hour after market open.
    algo.schedule_function(
        rebalance,
        algo.date_rules.every_day(),
        algo.time_rules.market_open(hours=1),
    )

    # Record tracking variables at the end of each day.
    algo.schedule_function(
        record_vars,
        algo.date_rules.every_day(),
        algo.time_rules.market_close(),
    )

def before_trading_start(context, data):
    """
    Called every day before market open.
    """
    hist = data.history(context.spy, 'close', context.ma_length + 40, '1d')
    myWMA = WildersMA(hist, context.ma_length)   
    record(CLOSE = hist[-1])
    record(WMA = myWMA[-1])

def rebalance(context, data):
    """
    Execute orders according to our schedule_function() timing.
    """


def record_vars(context, data):
    """
    Plot variables at the end of each day.
    """
    pass


def handle_data(context, data):
    """
    Called every minute.
    """
    pass

def WildersMA(closes, length):
    wma = np.zeros(len(closes))
    k = 1.0/length
    
    for i in range(0, len(closes)):
        if i < 1:
            wma[i] = closes[i]
        else:
            wma[i] = closes[i]*k + np.roll(wma, 1)[i]*(1.0-k)

    return wma
There was a runtime error.

Glad you brought this up! There is a subtle but often overlooked issue (source of frustration, headache, annoyance) with exponentially weighted moving averages. Namely, the window length is sometimes not given. Take for example Wilders definition

WMA = price today * K + EMA yesterday * (1-K)

There isn't an explicit indication of how many 'yesterdays' to go back in the recursive calculation. It's good practice to define the window length as well as the weighting factor. If different window lengths are used then different results will happen. That is part of the issue why the notebook and the algo values, and associated graphs, are different. The window lengths are different.

Specifically there are three issues why the two two are different. Here's the short version...

  1. The notebook had no initial lookback data while the algo did (the algo starts with 240 days of additional lookback).
  2. The notebook used an 'expanding' window of lookback data (keeping the start date fixed) while the algo had a fixed length 'rolling window' of lookback.
  3. The notebook used SPY pricing which was adjusted as of the end_date. The algo used data which was adjusted as of each simulation day (similar to pipeline data).

That's the summary. For a step by step outline of the issues and how to get the two aligned take a look at the attached notebook.

Loading notebook preview...
Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

Hi Dan,

Thanks for your great explaination!

But truely to say I find the smooth one ist more reallity than the zick-zack one. I use many other chart tolls and they all show the smooth one.

How to can I get the smooth one in algo?

I find out, the longer I set in hist = data.history(context.spy, 'close', context.ma_length + 40, '1d'), the smoother it will be. But the question is: If the underlying is emited on 2010.01.01 for example and I do the backtesting from 2010.02.01, I can get so many historical datas and there for the WMA will be wrong, right?

Besides, even I use the very long back datas in the hist(), the WMA looks still have some small zick-zack on Dec.19, Sep.19, June 19 etc.(see attached backtesting below). You wrote in your notebook:
... The notebook used SPY pricing which was adjusted as of the end_date. The algo used data which was adjusted as of each simulation day (similar to pipeline data).
...

Is it possible to make the pricing in algo adjusted as of the end_date?

Thomas

@Thomas,

Wilder's MA in terms of digital signal processing is an infinite impulse response filter (IIR).
Its value today substantially depends of starting point of data.
To make it smooth and mature in IDE just increase number of data points 4-8 times.

Clone Algorithm
2
Loading...
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Wilder's MA 
import numpy as np
# ------------------------------
STOCK = symbol('SPY'); MA = 200;
# ------------------------------
def initialize(context):
    schedule_function(record_WildersMA, date_rules.every_day(), time_rules.market_close(),)

def record_WildersMA(context, data):
    prices = data.history(STOCK, 'close', 4*MA, '1d')
    myWMA = WildersMA(prices, MA) 
    print(myWMA[-1]) 
    record(price = prices[-1], WMA = myWMA[-1])

def WildersMA(closes, MA):
    wma = np.zeros(len(closes))
    k = 1.0/MA
    
    for i in range(0, len(closes)):
        if i < 1:
            wma[i] = closes[i]
        else:
            wma[i] = closes[i]*k + np.roll(wma, 1)[i]*(1.- k)

    return wma[:]
There was a runtime error.

...

Clone Algorithm
5
Loading...
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
"""
This is a template algorithm on Quantopian for you to adapt and fill in.
"""
import quantopian.algorithm as algo
import pandas as pd
import numpy as np

def initialize(context):
    """
    Called once at the start of the algorithm.
    """
    context.ma_length = 200
    context.spy = symbol('SPY')
    
    # Rebalance every day, 1 hour after market open.
    algo.schedule_function(
        rebalance,
        algo.date_rules.every_day(),
        algo.time_rules.market_open(hours=1),
    )

    # Record tracking variables at the end of each day.
    algo.schedule_function(
        record_vars,
        algo.date_rules.every_day(),
        algo.time_rules.market_close(),
    )

def before_trading_start(context, data):
    """
    Called every day before market open.
    """
    hist = data.history(context.spy, 'close', context.ma_length *5, '1d')
    myWMA = WildersMA(hist, context.ma_length)   
    record(CLOSE = hist[-1])
    record(WMA = myWMA[-1])

def rebalance(context, data):
    """
    Execute orders according to our schedule_function() timing.
    """


def record_vars(context, data):
    """
    Plot variables at the end of each day.
    """
    pass


def handle_data(context, data):
    """
    Called every minute.
    """
    pass

def WildersMA(closes, length):
    wma = np.zeros(len(closes))
    k = 1.0/length
    
    for i in range(0, len(closes)):
        if i < 1:
            wma[i] = closes[i]
        else:
            wma[i] = closes[i]*k + np.roll(wma, 1)[i]*(1.0-k)

    return wma
There was a runtime error.

@Vladimir

You are right. I also notice I have to set the data points very long to get a smoother MA. But eventhough, there is still some very small zick-zack on the date of Dec.19, Sep.19, June 19 etc.

you can also see these in your attached algo if you zoom in it. In notebook there is no such zick-zack. I wonder this might be caused from the non-adjusted close in algo?

Thomas

@Thomas,

These small spikes may be the result of Quantopian’s sophisticated way to apply adjustments for splits, dividends, and other distributions.
Try it on instruments that do not pay dividends.

Clone Algorithm
0
Loading...
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Wilder's MA 
import numpy as np

def initialize(context):
    context.ma_length = 200
    context.spy = symbol('GLD')

def before_trading_start(context, data):
    hist = data.history(context.spy, 'close', context.ma_length * 5, '1d')
    myWMA = WildersMA(hist, context.ma_length)   
    record(CLOSE = hist[-1])
    record(WMA = myWMA[-1])

def WildersMA(closes, length):
    wma = np.zeros(len(closes))
    k = 1.0/length
    
    for i in range(0, len(closes)):
        if i < 1:
            wma[i] = closes[i]
        else:
            wma[i] = closes[i]*k + np.roll(wma, 1)[i]*(1.0-k)

    return wma
There was a runtime error.

@Vladmir

Thanks!

Since we can't do the live trade by Q, I hope these "spikes" will not happen by other trading platforms. :-)

@Thomas,

Quantopian applies adjustments for splits, dividends, and other distributions in a different way than most of us are used to.
Don't worry about these visual little splashes on Wilder's MA.
The Quantopian engine, when you apply trading logic or technical indicators behind the scene treats it like a smooth line.

Here is a test that proves this.
There are no sell transactions.

Clone Algorithm
2
Loading...
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Wilder's MA 
import numpy as np
import talib
# ---------------------------------------------
STOCK = symbol('SPY'); MA = 200; RSI_PERIOD = 2
# ---------------------------------------------
def initialize(context):
    schedule_function(record_WildersMA, date_rules.every_day(), time_rules.market_close(),)

def record_WildersMA(context, data):
    prices = data.history(STOCK, 'close', 4*MA, '1d')
    myWMA = WildersMA(prices, MA) 
    rsi = talib.RSI(myWMA[-20:], RSI_PERIOD)
    if rsi[-1] > 50:
        order_target_percent(STOCK, 1.0)
    else:
        order_target(STOCK, 0)
    
    print(myWMA[-1]) 
    record(price = prices[-1], WMA = myWMA[-1], rsi = rsi[-1])

def WildersMA(closes, MA):
    wma = np.zeros(len(closes))
    k = 1.0/MA
    
    for i in range(0, len(closes)):
        if i < 1:
            wma[i] = closes[i]
        else:
            wma[i] = closes[i]*k + np.roll(wma, 1)[i]*(1.- k)

    return wma[:]
There was a runtime error.

@Vladmir

Many thanks again!