When you are backtesting an idea you often need to start from day 1 of a stock's trading history and loop forward to the most current day. In essence, pretending each day is the current day at that point in time. Thus, you are looping back 200 - 1 data points for each day in the series. This isn't such a big deal with a stock such as Google whose trading history is rather limited (2004). But, take a stock like IBM with a more extensive trading history and your code is going to bog down with each call to the SMA indicator. Throw 20,000 securities into your backtest and the looping adds up.
Therefore, running calculations are the preferred method in order to spin just once through the data points. So, in order to calculate the running simple moving average for closing prices you apply the following formula:
\(SMA_{today} = SMA_{yesterday} + ((Price_{today} - Price_{today - n}) /n)\)
Where
- \(n\) = number of values included in your rolling computational window.
def cumulative_sma(bar, series, prevma): """ Returns the cumulative or unweighted simple moving average. Avoids sum of series per call. Keyword arguments: bar -- current index or location of the value in the series series -- list or tuple of data to average prevma -- previous average (n - 1) of the series. """ if bar <= 0: return series[0] return prevma + ((series[bar] - prevma) / (bar + 1.0))
def running_sma(bar, series, period, prevma): """ Returns the running simple moving average - avoids sum of series per call. Keyword arguments: bar -- current index or location of the value in the series series -- list or tuple of data to average period -- number of values to include in average prevma -- previous simple moving average (n - 1) of the series """ if period < 1: raise ValueError("period must be 1 or greater") if bar <= 0: return series[0] elif bar < period: return cumulative_sma(bar, series, prevma) return prevma + ((series[bar] - series[bar - period]) / float(period))And the example call and results:
prices = [10, 15, 25, 18, 13, 16] prevsma = prices[0] #1st day nothing to average so return itself. for bar, close in enumerate(prices): currentsma = running_sma(bar, prices, 3, prevsma) print "Today's 3-day SMA = %.4f" % currentsma prevsma = currentsma ------- Results ---------------- Today's 3-day SMA = 10.0000 Today's 3-day SMA = 12.5000 Today's 3-day SMA = 16.6667 Today's 3-day SMA = 19.3333 Today's 3-day SMA = 18.6667 Today's 3-day SMA = 15.6667
12 comments:
Hi Mike,
I'm a recruiter at Google and was curious if you might be interested in exploring opportunities with us.
Please let me know if you'd like to chat.
Sincerely,
Christian Bogeberg | bogeberg@google.com
Engineering Recruiter | Google Inc., CA
Voice:650.762.6645 | Fax:650.963.3269
I love your website! did you create this yourself or did you outsource it? Im looking for a blog design thats similar so thats the only reason I'm asking. Either way keep up the nice work I was impressed with your content really..
Thanks! I used the Daily Edition theme from themesheep. I believe they have a few other free ones to choose from and of course paid ones as well. Wordpress makes it pretty easy to try out different themes from various sources.
MT
[...] that we’ve tackled Running Simple Moving Averages (SMA)…let’s move on to Exponential Moving Averages (EMA). You may wonder why we’re not [...]
[...] covered Running SMAs and EMAs…let’s dig into Running Sums or often called Running Totals. Formula as [...]
[...] You also need the Simple Moving Average, which you can find in one of my previous posts here. [...]
Thanks for the algo.
Is there a way to calculate the value without requiring the
series[bar - period] data point as this requires me to store previous data points in a sliding window calculation.
Thanks for good info:)
How to use this with lists?
I am getting errors when I try:
print "Today's 3-day SMA = %.4f" % currentsma
TypeError: float argument required, not str
Some solutions?
The example code is using a list (prices). Are you asking is there a way to pass a full list to running_sma to obtain list of running simple moving averages? Yes, but you'd have to restructure running_sma to expect a list of values instead of a value & previous value. Then perform the for bar, close in enumerate(prices) logic within the running_sma function. And of course, then return a list of values back instead of a value. This is basically what I did in my statio package on github - https://github.com/TaylorTree/statio.
Here's the sample code from the statio sma_values function:
def sma_values(values, period=None): """Returns list of running simple moving averages.
:param values: list of values to iterate and compute stats. :param period: (optional) # of values included in computation. * None - includes all values in computation. :rtype: list of simple moving averages.
Examples: >>> values = [34, 30, 29, 34, 38, 25, 35] >>> results = sma_values(values, 3) #using 3 period window. >>> ["%.2f" % x for x in results] ['34.00', '32.00', '31.00', '31.00', '33.67', '32.33', '32.67'] """ if period: if period < 1: raise ValueError("period must be 1 or greater")
period_n = float(period) period = int(period)
results = [] lastval = None for bar, newx in enumerate(values): if lastval == None: lastval = float(newx)
elif (not period) or (bar < period): lastval += ((newx - lastval) / (bar + 1.0))
else: lastval += ((newx - values[bar - period]) / period_n)
results.append(lastval)
return results
No way I know of to avoid storing the previous data point in order to obtain the running calculation. Just have to decide where you'd like to store it. You could store it and then use it in the call to your function. Or you could create a class for your logic and memoize the previous data point (cache the value) in the class. Then the object will keep track of it for you through the window.
Hi Mike, there's an error in the code here. The pval = 0.0 should be in an else statement.
Believe you are referring to the Running Variance post in regard to the pval = 0.0 error. I have corrected the code in that post. Thanks! MT
Post a Comment