Day trading strategy example - failed upside breakout
10 responses

Thanks...I'll have to read it over carefully, and then will probably have questions. --Grant

@Grant Look forward to seeing this coded up! Would be nice to see a decent day trading strategy that makes money. What and how large would the universe be? Or what initial filter to do a initial screen on the universe to parse into a basket?

Here's an average true range (ATR) example using TA-LIB (copied from Peter's post on https://www.quantopian.com/posts/batch-average-true-range).

One general question is how to handle time. The convention seems to be to ignore wall-clock time and just deal in trading minutes/days. As I understand, TA-LIB ignores datetime stamps and in effect, assumes data equally spaced in time. But of course there are weekends, holidays, and early closings. It seems that older data (based on wall-clock time) should carry less weight than more recent data. But maybe the effect is relatively small and already compensated by the trading rules?

63
Backtest from to with initial capital
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
# http://en.wikipedia.org/wiki/Average_true_range

import talib

def initialize(context):
context.sid = sid(26578) # GOOG

def handle_data(context, data):
high        = history(15, '1d', 'high')
low         = history(15, '1d', 'low')
close       = history(15, '1d', 'close_price')
ATR         = talib.ATR(high[context.sid], low[context.sid], close[context.sid])
record(ATR=float(ATR[-1:]))
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.

Anony - what happened to your trading stake? We can take offline if you like, I am curious (and there are no private messages here)...

Grant: Check out my ATR post here. Older data does carry less weight because of the exponential weighting.

Anony: When calculating the ATR on minute bars, the risk is HUGE. On daily bars, a 5xATR jump is enormous. On minute bars, 5xATR is not so big... The price jump is relative to the time period you're examining, so the distribution of ATR values will be completely different.

# Set up the ATR indicator.
atr_data = ta.ATR(timeperiod=15)

def initialize(context):
context.stock = sid(26578) # GOOG

def handle_data(context, data):
atr = atr_data(data)[context.stock]
record(ATR=atr)

You can insert this snippet into your code to view the timestamp on the bars. I assume you only receive bars during regular trading hours, so weekends and evenings would be excluded.

def handle_data(context, data):
output = str('Date: {0}  '.format( data[context.stock].datetime.strftime('%Y-%b-%d  %H:%M)))
log.info(output)

Seems like the example I posted above using the history API would be best, since it provides an algo "warm-up" (trailing data available at algo start). Correct?

@ Anony, I don't understand your question "minutely data is including weekend?" Also, regarding the charting, it is a mystery why Quantopian does not provide better charting. I've wondered if it might be related to the restriction of distributing their data set?

Anthony,

Because the characteristics of this data can be very different from the full window, one usually does not want to use 'warm-up' data (i.e. the interim indicator values calculated before the full length of the time window has been reached).

For example, using warm-up data for a 150 day moving average, you are really looking at a 1,2,3,4,5,...150 moving average. As a result, the initial numbers will have much higher variance than the full 150 day SMA.

Here is an example. The Talib SMA does not show a value for the indicator until the full time window of 150 daily bars have been observed. Once we have the 'correct' indicator value after 150 daily bars, let's say we start to calculate a 'warm-up' SMA value as if we had started our algorithm on that day (i.e. we started trading on Bar 150). After another 150 bars, these two indicators will of course be the same. Before then, however, they can be very different. In addition, the variance will be much higher at the start of the observations giving a whippiness to the front end of the series. This dies down with more observations due to the law of large numbers.

If you want to use this data, it is entirely up to you...

I've noticed in the Simple Transforms section of the API reference guide:
Note that simple transforms don't have a warmup period in backtesting. As an example, if your algorithm uses mavg(3), the window for the moving average won't be filled until after the third day. The first day mavg(3) will report the first day's price, the second day will report the average of the first two days, etc. However, in paper trading and live trading the simple transforms are warmed up.

This is NOT how one would trade in practice (assuming that warming-up data means calculating indicators using only information from the start date). I haven't paper traded Quantopian, so I can't test this. But if I were to start trading an algorithm today, I would have access to all available historical data and be able to calculate the correct initial indicator values to use in the algorithm.

31
Backtest from to with initial capital
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
import numpy as np
import math
from collections import deque
WINDOW = 150

# Set up the indicator
sma_data = ta.MA(timeperiod=WINDOW)

def initialize(context):
context.stock = sid(26578)  # Tesla
context.history = deque(maxlen=WINDOW)
context.bar_count = 0

def handle_data(context, data):
context.bar_count += 1
stock = context.stock
stock_data = data[stock]
price = stock_data.price
talib_sma = sma_data(data)[stock]
if context.bar_count >= WINDOW:
context.history.append(price)
sma = float( sum(context.history) ) / len(context.history)
else:
sma = None

record(price=price, talib_sma=talib_sma, warmup_sma=sma)
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.

Anony,

Thanks for the example of accumulating minutely data with deques (I'll need to learn a bit more about those at some point). Now that I understand that the ATR is on minutes, I can sort things out (it wasn't clear from your original post, since you used "period" without units).

Regarding the full release of the history API, it was never revealed why it got delayed. As I recall, Quantopian was planning to provide up to 5 days of minutely data in a trailing window, via history. I was kinda surprised by the limited release (and it is sorta confusing, since it returns hybrid data--a mix of current-day minutely and daily).

Grant

Thanks Anony,

Not sure I understand all the trader-speak, but I do understand scaling the time base. I'll see if I can work something up with a pandas dataframe that can be fed into the TA-LIB ATR (per Peter's example). As I understand, the core of this approach is to be able to crank through a list of securities, so pandas should be handy. As a side note, it'd be better if the full history API were fully released, since the coding will be a bit of a kludge otherwise.

Grant