Adding ATR trailing stop to algo

If i want to calculate an ATR 14 trailing stop..to say AAPL, could i use TA-Lib as follows:

atr14 = ta.ATR(timeperiod=14)

atr_14 = atr14(data)

aapl_atr14 = atr_14[context.aapl]

Would this work?

Is there another method for calculating ATR trailing stops?

Thanks for the input!

14 responses

Peter,
Need guidance on some EXIT logic using the ATR stop on Daily data for aapl (out of a basket of 10 stocks):

• If LONG aapl, Then
• .. If Close Close + aapl_atr14
-...Then Sell entire position

How would this look in code?

Thanks!

Conversely:
- If SHORT aapl, Then
- .. If Close > Close + aapl_atr14
-...Then Sell entire position

...basically need to know if there's a parameter i can call that checks if:

1. I have a position in the security either LONG or SHORT... (from a set of securities)
2. ..if so....If I"m LONG or SHORT in that security
3. Then add the logic:
4. If LONG then: If Close (is less than) Close -aapl_atr14, Then SELL entire position
5. If SHORT then: If Close (is greater than) Close +aapl_atr14, Then SELL entire position

How would 4 and 5 look in code?

Thanks again!

(Sorry - I deleted the wrong post.)

I've made a start so maybe you can build on this? I've disabled the volume slippage model (I think) otherwise the algo would keep selling the same security repeatedly in the case of partial fills.

P.

26
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 math

atr14 = ta.ATR(timeperiod=14)

def initialize(context):
context.stocks = [sid(2),  sid(21), sid(24), sid(31), sid(32), \
sid(37), sid(39), sid(40), sid(41), sid(47)]
context.start = True
set_slippage(slippage.VolumeShareSlippage(volume_limit=1000000, price_impact=0))

def handle_data(context, data):
if context.start:
# Open some long positions
position_value = context.portfolio.cash / len(context.stocks)
for stock in context.stocks:
if stock.security_end_date > get_datetime():
qty = math.floor(position_value / data[stock].close_price)
order(stock, qty)
log.info("Ordering %s shares of %s" % (qty, stock.symbol))
context.start = False

atr_14 = atr14(data)

for stock in context.stocks:
if stock.security_end_date < get_datetime():
break
atr = atr_14[stock]
if context.portfolio.positions[stock]['amount'] > 0:
if data[stock].close_price < context.portfolio.positions[stock]['cost_basis'] - atr:
qty = context.portfolio.positions[stock]['amount']
order(stock, - qty)
log.info("Closing LONG position of %s shares of %s" % (qty, stock.symbol))

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.

Thanks Peter...will surely take a look! Would "context.portfolio.positions[stock]" define 1) what open positions i have? 2) whether each of those open positions are long OR short.....given a long/short strategy?

I believe that

 context.portfolio.positions[stock]


is the net position for the security, with a short position denoted by a -ve amount. So purchasing 100 shares at $100 and 100 shares later at$120 would give an 'amount' of 200 and a 'cost_basis' of \$110. It's an assumption I haven't checked thoroughly.

P.

Here is an algo that enters trades based on candle patterns and exits with ATR stops. It may give you some further ideas.

(I've just realised it's a bad teaching example. The '-100, 100' indicator values have nothing to do with the '-100, 100' position sizes.)

P.

263
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 zipline.transforms.ta as ztt

OpenOrders = {}

@batch_transform(window_length=20, refresh_period=0)
def get_data(datapanel):
Open   = datapanel['open_price']
High   = datapanel['high']
Low    = datapanel['low']
Close  = datapanel['close_price']
Volume = datapanel['volume']
return Open, High, Low, Close, Volume

def initialize(context):
context.stocks = [sid(26126), sid(12160), sid(6683), sid(12882), sid(20281), \
sid(2518),  sid(7696),  sid(9883), sid(6413),  sid(754)]

def handle_data(context, data):
# Get a rolling window of price and volume data
results = get_data(data)
if results is None:
return
Open, High, Low, Close, Volume = results[0], results[1], results[2], results[3], results[4]

for stock in data:
# Check the security is still trading
if stock.security_end_date < get_datetime():
# Skip this iteration of the 'for' loop
continue

# Calculate some indicators
ATR        = float(ztt.talib.ATR(High[stock], Low[stock],Close[stock], timeperiod=5)[-1:])
ThreeWhiteSoldiers     = ztt.talib.CDL3WHITESOLDIERS(Open[stock], High[stock], Low[stock], Close[stock])[-1:]
IdenticalThreeCrows    = ztt.talib.CDLIDENTICAL3CROWS(Open[stock], High[stock], Low[stock], Close[stock])[-1:]

# Do we have a position in this securiy?
if stock in OpenOrders.keys():
cost     = context.portfolio.positions[stock]['cost_basis']
position = context.portfolio.positions[stock]['amount']
if OpenOrders[stock]['Type'] == 'Long':
# Should we close a LONG position?
if data[stock].close_price < cost - ATR:
order(stock, -position)
print "STOPPED OUT - LONG"
del OpenOrders[stock]
else:
# Should we close a SHORT position?
if data[stock].close_price > cost + ATR:
order(stock, -position)
print "STOPPED OUT - SHORT"
del OpenOrders[stock]
#print OpenOrders
# Skip this iteration of the 'for' loop
continue

if context.portfolio.positions[stock]['amount'] <> 0 and stock not in OpenOrders.keys():
# We have an open position in a security we think we have closed i.e. a pending order
# so skip this iteration of the 'for' loop
log.info("Partial fill for %s shares of %s" % (context.portfolio.positions[stock]['amount'], stock.symbol))
continue

if IdenticalThreeCrows == -100:
# Enter a SHORT position
order(stock, -100)
OpenOrders[stock] = {'Type' : 'Short'}
elif ThreeWhiteSoldiers == 100:
# Enter a LONG position
order(stock, 100)
OpenOrders[stock] = {'Type' : 'Long'}

record(Cash=context.portfolio.cash)


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.

for @batch_transform(window_length=20, refresh_period=0)....does '20' corresponding to last 20 trading days? For 'refresh_period' , if i want to check it on a daily basis, would i enter '1'? what does refresh_period = 0 correspond to?

...Also if i wanted to add in , say, 2 more exit signals? Such that i have 3 total exit signals, and whichever is triggered first, i will stop out of that postion? could you provide some insight how that would look in code?
Thanks!

Yes, the '20' denotes the length of a rolling windows of trading days. To see what 'batch_transform is doing run this:

@batch_transform(window_length=4, refresh_period=0)
def get_data(datapanel):
Close  = datapanel['close_price']
return Close

def initialize(context):
context.stocks =[sid(2)]
def handle_data(context, data):
Close = get_data(data)
if Close is None:
return
print Close


You will see that in daily mode the batch transform updates each day for a frequency of 0 and 1. Try a frequency of 2.

To add more exit signals imply use the 'or' syntax:

if exit_condition_1 or \
exit_condition_2 0r \
exit_condition_3:
order(stock, -position)


P.

Thanks Peter..that's exactly what i was looking for. Had another question:

1. How to calculate daily drawdowns on a) current positions b) portfolio?
2. How to calculate 1% of a given position?

Could you give some guidance what the syntax would look like?
Thanks much!

Hello Peter,
Can you possibly reply to above post?

Thanks!

I haven't done any drawdown calculations yet, sorry. There is a post here that might help: https://www.quantopian.com/posts/calculation-of-cumulative-max-drawdown

To calculate 1% of a position in a particular security use:

data[sid(24)].close_price * context.portfolio.positions[sid(24)].amount * 0.01


P.

Hi Peter,
Your algo above has given me an idea how to solve my problem with comparing past values with new values that you tried to solve for me earlier.
Thanks
Omar