What is the point of view of the quantopian community regarding technical indicator ?

Is someone successfully using technical indicators in their trading algorithms ? I'm trying to use them in combination with simple fundamental information about the company (eps, p/e, etc ..), and by using specific strategies (simple ones combining RSI, MACD, etc ..) to different sectors. But nothing seems to have a consistent performance. Any inputs from your experience ? Thanks,

24 responses

Since you asked for opinions, my opinion is that they are largely worthless; either you have a real, verifiable edge, you know what it is and why it works, and when it might not work, and you exploit it to the hilt, or you are just throwing spaghetti against the wall and seeing what sticks. When I start to feel myself throwing spaghetti, I usually just stop, and switch to research or more basic thinking.

That said, some people swear by them, so hopefully they'll speak up too!

I have been on both sides of the fence. Ultimately I think they are self fulfilling. The behavioral argument is that since enough people use them, then price will respond to certain indicators. For example: resistance lines, you will often see a stock bounce off a near time high and having trouble breaking the level. Resistance lines shouldn't actually matter, but a price near all time highs will have a different effect psychologically than one trading in normal ranges. Because of that, we see price bounce of resistance lines until 'breaking' through or reversing. The issue is that while technicals can help one read the market, there is not a single one out there that can reliably pick directionality (if there is such an indicator please let me know because I too would like to be rich).

That said, my take is that technicals can augment a fundamentally sound strategy by helping choose entry/exit points but as standalone trading rules they can't provide much edge.

Depends which indicators you are using . I stick to two indicators only : Moving average and Rate of change , that's it. using 20 indicators just to determine entries and exit points is ridiculous (IMO). Here 's a simple ( boring) strategy I've been using for years. I use the U.S sector ETF offered by SPDR, and buy whatever ETFs trading above their 10 month MA at the end of the month, repeat at the end of each month. It's simple ,boring, but never failed me in the past 17 years. When in cash , i buy SHY ( still trying to figure how to replace cash with SHY here). Anyway, what i'm trying to say is keep it simple.

Cheers

P.S. @Market Tech helped me with this algo. :)

207
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 talib

def initialize(context):

context.stocks = [sid(21638), sid(21644), sid(21646), sid(21647), sid(21648), sid(21649), sid(21650), sid(21651), sid(21524)]

context.price={}

schedule_function(func=CalcIndicators, date_rule=date_rules.month_end())
schedule_function(func=HandleExits,    date_rule=date_rules.month_end())
schedule_function(func=HandleEntries,  date_rule=date_rules.month_end())

def handle_data(context, data):
record(PositionCount = len([1 for stock in context.portfolio.positions if context.portfolio.positions[stock].amount > 0]))
record(SettledCash   = context.account.settled_cash)

context.account.leverage= 3

def HandleEntries(context, data):

eligibleStocks = []
for stock in data:
if (data[stock].close_price > data[stock].tenMonthMA):
eligibleStocks.append(stock)

eligibleStocksCount = float(len(eligibleStocks))
for stock in eligibleStocks:
order_target_percent(stock,0.1111)

def HandleExits(context, data):
inEligibleStocks = []
for stock in data:
if (data[stock].close_price < data[stock].tenMonthMA):
inEligibleStocks.append(stock)

for stock in inEligibleStocks:
order_target_percent(stock,0.0)

def CalcIndicators(context, data):
closes = history(252, "1d", "close_price").resample("1m")
closes.dropna()
tenMonthMA = closes.apply(talib.MA, timeperiod=8)

for stock in data:
data[stock].tenMonthMA = tenMonthMA[stock][-1]

There was a runtime error.

Single security trading using technical indicators will probably never earn you the alpha you may tease out of backtesting. TA for single securities is just too easy to curve fit. This in addition to the fact that you're competing with every other trader out there (often much smarter traders); competing with such traders would tend to mean that you think you're smarter than they are. Which might be the case. But probability would infer that you are not.

Where TA can introduce information that only your algo might possess is where you combine multiple securities into a gestalt like profile that you then use as one of your driving forces to govern your individual trades. For instance, I've been playing with simple oscillator type indicators where I blend all the metrics from each security into a single dominating filter value. I can then use this value as a gross risk on / risk off style switch. And/or, in a more elaborate technique, determine when the long term trend (for the group) is in one direction, then identifying, on a smaller time scale, when jigs or jags pressure this gross filter out of its boundaries. At this time I can then examine the outliers, those securities which zigged when the balance of the group zagged, and then decide if it's time to trade those outliers.

What's unique about the Q's platform is that it seems to have been built exquisitely for this purpose. Add to this the ability to perform fundamental filters that can build groups of similar behaving stocks and you've got the potential for a fairly unique trading treatment few others are probably leveraging. So yes, TA used as a grand zeitgeist sieve can possibly be used to build edge holding strategies here.

I dont believe it can predict the market or produce by itself the retrurns everyone is after. But I do believe and look for wasy to use TA to indicate you to that something changed in the market which means you must change your strategy , for example I want to find out when big downturns starts so I can move to cash or protect myself. I believe that TA can give this kind of picture to take decisions. I have to admit I didnt find the right mixture yet, so if anyone have a good algo code of TA that can really indicate of signifcant changes in market or downturns, please share

@ Joe , check out the MAC - US ( Moving Average Cross over ) system developed by Georg Vbra ( iMarketsignals.com) . Very simple lagging system , that should keep long term investor on the right side of the market . Here are the rules, from his blog:

Buy signal for the S&P 500
A buy signal occurs when the 34-day exponential moving average (EMA) of the S&P 500 becomes greater than 1.001 times the 200-day EMA.

Sell signal for the S&P 500
A sell signal occurs when the 40-day simple moving average (MA) of the S&P 500 crosses below the 200-day MA.*

Someone on TradingView , actually coded as an indicator . Georg actually did a back test not long ago on this , you can read more here

He uses the SP 500 index only , not sure if this is applicable on other stocks

The overwhelming body of academic studies that have looked at technical analysis show it doesn't provide abnormal returns. You will always find a number of people who anecdotally swear by it given their own experience, discount academics as living in ivory towers, and it rapidly devolves into something similar to an argument about religion. I believe in the scientific method and replicable results and have read through a good number of those studies, so that's my basis for not engaging in technical analysis. For those who take a more skeptical view of the scientific method I'd present two more "common sense" arguments:
1. Markets are made up of participants. If a widely held "edge" of any kind is well known and effective, then a large part of the market will use those strategies, thus arbitraging away said edge.
2. How many technical analysis hedge funds exist that use the typical chartist methods, and what is their AUM? If you're sitting around the poker table and you don't know who the chump is its probably you. If no significant AUM bases their investments on technical analysis, then you're implicitly assuming the majority of the hedge fund world and those who invest their money with them are the chumps at this table, not using a widely known and successful investment method. That may be true, but just be aware that you're going with that idea.

In essence, if you believe you are applying technical analysis in a way that is absolutely unique, profitable, and for some reason very difficult or impossible for any other reasonably smart person to come up with, then it may be a good idea. If you're just applying something you've read about elsewhere, it can't rationally provide you an edge going forward and you're most probably just curve fitting. If there is an edge to technical analysis the current acceptable term to use in polite company is "behavioral finance", which attempts to bring some rigor to the ideas that Ethan posted and is probably worth a google.

@Kevin Q., Nice wrap up. Posts like yours should make it into the "algo writers handbook" that the Q should compile from all the shiny knowledge nuggets it has gathered here.

Technical indicators get a bad rap because they're intertwined with TA. They are visually pleasing and thus commonly found in the toolbox of retail traders. I think it's reasonable to doubt rigor of someone daytrading 3 stocks off of charts decked with indicators. But that is a problem of process and not the indicators themselves. Manually adjusting parameters and hitting the backtest button over and over isn't anymore rigorous a process just because you're not looking at charts. Replacing a moving average with a Kalman Filter doesn't give you +10 Alpha points.

A moving average is just a moving average. I'd be more worried about trusting things like crowd-sourced sentiment data and complicated machine learning algos.

95% from 100 best algos by statistical metrics I found on this forum use technical indicators.

Some of them:

https://www.quantopian.com/posts/spy-and-sh-minute-data by Grant Kiehne

If I missed something good with fundamental approach then publish it here to compare.

780
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
from scipy import stats
import numpy as np
from pytz import timezone
import pandas as pd

threshold = 1.5 # z-score difference threshold

window = 16*390 # window length in minutes
window_roll = 30 # window length for rolling stats

def initialize(context):

context.stocks = [sid(8554),sid(32268)] # SPY & SH
# context.stocks = [sid(19920),sid(32265)] # QQQ & PSQ

context.prices = np.zeros([window,len(context.stocks)])
context.volumes = np.zeros([window,len(context.stocks)])

context.tic_count = 0

context.previous_datetime = None
context.new_day = None
context.order_submitted = False

print 'threshold = ' + str(threshold)

def handle_data(context, data):

current_datetime = get_datetime().astimezone(timezone('US/Eastern'))

if context.tic_count < window:
for i, stock in enumerate(context.stocks):
context.prices[context.tic_count,i] = data[stock].price
context.volumes[context.tic_count,i] = data[stock].volume
else:
context.prices = np.roll(context.prices,-1,axis=0)
context.volumes = np.roll(context.volumes,-1,axis=0)
for i, stock in enumerate(context.stocks):
context.prices[-1,i] = data[stock].price
context.volumes[-1,i] = data[stock].volume

context.tic_count += 1

if context.tic_count < window:
context.previous_datetime = current_datetime
return

# skip tic if any orders are open or any stocks did not trade
for stock in context.stocks:
if bool(get_open_orders(stock)) or data[stock].datetime < get_datetime():
return

if current_datetime.day != context.previous_datetime.day:
context.new_day = True
context.previous_datetime = current_datetime

# limit trading to once per day and within specified time window
if not context.new_day:
return
return

# if all conditions met, execute trading logic

p = pd.rolling_mean(context.prices,window_roll)[window_roll-1:]
# p = context.prices[window_roll-1:]
v = pd.rolling_sum(context.volumes,window_roll)[window_roll-1:]

dv = np.multiply(p,v) # compute dollar volumes

# normalize dollar volume with z-score
dv_z = stats.zscore(dv, axis=0, ddof=1)

# compute z-score difference
end = dv_z.shape[0]-1
delta_z = dv_z[end,1] - dv_z[end,0]

record(delta_z = delta_z)

# plot capital invested (e.g. how much capital is invested)
capital = capital_invested(context, data)
record(capital_invested = capital)

# plot cash
cash = context.portfolio.cash
record(cash = cash)

# plot positions value
positions_value = context.portfolio.positions_value
record(positions_value = positions_value)

# trade using z-score difference indicator (set global threshold above)
for i, stock in enumerate(context.stocks):

position = context.portfolio.positions[stock].amount     # current number of shares
# shares = cash/data[stock].price/len(context.stocks)/.5   # desired position (using margin)
shares = cash/data[stock].price/len(context.stocks)  # desired position (no margin)
long_short = (-1)**i

if delta_z > threshold:
order(stock,(long_short*shares)-position)
context.new_day = False

elif delta_z < -threshold:
order(stock,(-long_short*shares)-position)
context.new_day = False

def capital_invested(context, data):

# get a sum total of capital spent or borrowed for all current positions
capital = 0.0  # initialize to zero

# check every stock in current positions (also works with set_universe)
for stock in context.portfolio.positions:

# get amount of shares in current position for this stock
amount = context.portfolio.positions[stock].amount

# get the cost basis of the shares (how much we spent on average per share)
cost_basis = context.portfolio.positions[stock].cost_basis

# check if position is a short trade (negative amount)
amount = max(amount, -amount) # change amount to a positive number

# add dollar amount to the 'spent' total
capital += amount * cost_basis

# return amount of capital tied up in positions
return capital

# Converts all time-zones into US EST to avoid confusion
loc_dt = get_datetime().astimezone(timezone('US/Eastern'))
# if loc_dt.hour > 10 and loc_dt.hour < 15:
if loc_dt.hour == 12 and loc_dt.minute == 0:
return True
else:
return False
There was a runtime error.
151
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
# Keller/Butler - Elastic Asset Allocation (EAA)
#
# Source:
#   Wouter J. Keller and Adam Butler
#   "From Flexible Asset Allocations (FAA) to Elastic Asset Allocation (EAA)"
#   December 30, 2014 (v0.90), revised January 16, 2015 (v0.92)
#
# Implementation:
#   Alex (https://www.quantopian.com/users/54190a3694339e5d3a0000af)
#   March 10, 2014 (v0.6)
# Modified:
#   Yoyoteng (https://www.quantopian.com/users/54b8a7b4f44758bf5c000aea)
#   March 13, 2014
#
#   http://papers.ssrn.com/sol3/papers.cfm?abstract_id=2543979
#
# Known issues:
#   Dividend adjustment requires len(assets) <= 5 (Quantopian Fetcher limit)
#
# Version Log:
#   v0.6 - Dividend adjustment via Yahoo data (Thanks to F. Chandelier)
#          Truncating z-score at 0 rather than momentum
#   v0.5 - initial open-source release

import pandas as pd
import datetime as dt
import math

def initialize(context):
#
# Configuration
#

# Assets (N=7) from Paper (unadjusted):
#   SP500, EAFE, EEM, US Tech, Japan Topix, and two bonds: US Gov10y, and US HighYield. Cash = SHY, Bill = SHY
#context.active = [sid(8554), sid(22972), sid(24705), sid(19658), sid(14520), sid(23870), sid(33655)]
#context.cash = sid(23911)
#context.bill = sid(23911)

#   $MDY,$IEV, $EEM,$QQQ, $XLV,$IEF and $TLT. Cash =$IEF, Bill = SHY
#context.active = [sid(12915), sid(21769), sid(24705), sid(19920), sid(19661), sid(23870), sid(23921)]
#context.cash = sid(23870)
#context.bill = sid(23911)

# Custom assets (N=5) (adjusted yahoo data):
#   VTI, EFA, ICF, TLT, IEF. Cash = IEF, Bill = IEF (!)
context.active = [sid(22739), sid(22972), sid(22446), sid(23921), sid(23870)]
context.cash = sid(23870)
context.bill = sid(23870)

context.leverage = 1.0

# Weights:
#   [wR, wC, wV, wS, eps, wIV]
#   wi ~ zi = ( ri^wR * (1-ci)^wC / (vi^wV) * (ivi^wIV) )^(wS+eps)

#   Golden Lama EAA :
context.score_weights = (2.0, 1.0, 0.25, 1.0, 1e-6, 4.0)
#context.score_weights = (0.0, 0.0, 0.0, 1.0, 1e-6, 1.0)
#   Golden Offensive EAA: wi ~ zi = (1-ci) * ri^2
#context.score_weights = (2.0, 1.0, 0.0, 1.0, 1e-6, 0.0)
#   Golden Defensive EAA: wi ~ zi = squareroot( ri * (1-ci) )
#context.score_weights = (1.0, 1.0, 0.0, 0.5, 1e-6, 0.0)
#   Equal Weighted Return: wi ~ zi = ri ^ eps
#context.score_weights = (1.0, 0.0, 0.0, 0.0, 1e-6, 0.0)
#   Equal Weighted Hedged: wi ~ zi = ( ri * (1-ci) )^eps
#context.score_weights = (1.0, 1.0, 0.0, 0.0, 1e-6, 0.0)
#   Scoring Function Test:
#context.score_weights = (1.0, 1.0, 1.0, 1.0, 0.0, 1.0)

context.assets = set(context.active + [context.cash, context.bill])
context.alloc = pd.Series([0.0] * len(context.assets), index=context.assets)

schedule_function(
reallocate,
date_rules.month_end(days_offset=0),
time_rules.market_close(minutes=5)
)

schedule_function(
rebalance,
date_rules.month_end(days_offset=0),
time_rules.market_close(minutes=5)
)

#
# Yahoo fetcher
# (inspired by F. Chandelier)
# https://www.quantopian.com/posts/yahoo-and-fetch-comparing-q-and-y-for-12-mth-rolling-return
#
start_year = 2002
end_year = dt.datetime.today().year + 1
url_template = "http://real-chart.finance.yahoo.com/table.csv?s=%s&a=0&b=1&c=%d&d=0&e=1&f=%d&g=d&ignore=.csv"

for sym in context.active:
url = url_template % (sym.symbol, start_year, end_year)
print "Fetching %s adjusted prices: %s" % (sym.symbol, url)

fetch_csv(
url,
date_column='Date',
date_format='%Y-%m-%d',
symbol=sym,
pre_func=fetch_pre,
post_func=fetch_post
)

def handle_data(context, data):
record(leverage = context.portfolio.positions_value / context.portfolio.portfolio_value)

def rebalance(context, data):
for s in context.alloc.index:
if s in data:
order_target_percent(s, context.alloc[s] * context.leverage)

def reallocate(context, data):
h = make_history(context, data).ix[-280:]
h_low = history(300, '1d', 'low').ix[-280:]
h_high = history(300, '1d', 'high').ix[-280:]

hm = h.resample('M', how='last')[context.active]
hb = h.resample('M', how='last')[context.bill]
ret = hm.pct_change().ix[-12:]

N = len(context.active)

non_cash_assets = list(context.active)
non_cash_assets.remove(context.cash)

print "***************************************************************"

#
# Scoring
#
# excess return momentum
mom = (hm.ix[-1] / hm.ix[-2]  - hb.ix[-1] / hb.ix[-2] + \
hm.ix[-1] / hm.ix[-4]  - hb.ix[-1] / hb.ix[-4] + \
hm.ix[-1] / hm.ix[-7]  - hb.ix[-1] / hb.ix[-7] + \
hm.ix[-1] / hm.ix[-13] - hb.ix[-1] / hb.ix[-13]) / 22

# nominal return correlation to equi-weight portfolio
ew_index = ret.mean(axis=1)
corr = pd.Series([0.0] * N, index=context.active)
for s in corr.index:
corr[s] = ret[s].corr(ew_index)

# nominal return volatility
vol = ret.std()

ivol = (h_high[context.active].ix[-10:]-h_low[context.active].ix[-10:]).mean()/(h_high[context.active]-h_low[context.active]).mean()

#
# Generalized Momentum
#
# wi ~ zi = ( ri^wR * (1-ci)^wC / vi^wV / ivoli^wIV )^wS

wR  = context.score_weights[0]
wC  = context.score_weights[1]
wV  = context.score_weights[2]
wS  = context.score_weights[3]
eps = context.score_weights[4]
wIV = context.score_weights[5]

z = ((mom ** wR) * ((1 - corr) ** wC) / (vol ** wV) / (ivol ** wIV)) ** (wS + eps)
z[mom < 0.] = 0.0

#
# Crash Protection
#

num_neg = z[z <= 0].count()
cpf = float(num_neg) / N
print "cpf = %f" % cpf

#
# Security selection
#
# TopN = Min( 1 + roundup( sqrt( N ), rounddown( N / 2 ) )
top_n = min(math.ceil(N ** 0.5) + 1, N / 2)

#
# Allocation
#
top_z = z.order().index[-top_n:]
print "top_z = %s" % [i.symbol for i in top_z]

w_z = ((1 - cpf) * z[top_z] / z[top_z].sum()).dropna()
w = pd.Series([0.0] * len(context.assets), index=context.assets)
for s in w_z.index:
w[s] = w_z[s]
w[context.cash] += cpf
print "Allocation:\n%s" % w

context.alloc = w

#
# Quantopian/Yahoo history switch
#
def make_history(context, data):
df = pd.DataFrame(index=data[context.active[0]]['aclose_hist'].index, columns=context.active)
for s in context.active:
df[s] = data[s]['aclose_hist']
return df
else:
return history(300, '1d', 'price')

def fetch_pre(df):
df['aclose_hist'] = pd.Series([[]] * len(df.index), index=df.index)
return df

def fetch_post(df):
#
# Populate data[] with past 300 adjusted close prices
#
for i in xrange(0, len(df.index)):
df['aclose_hist'].ix[-i-1] = df['aclose'][-i-300:][:300]
return df
There was a runtime error.
51
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 talib
import zipline

MaxPercentEquities = 70.0
MinPercentEquities = 30.0
PercentThreshold   = 10.0
AroonPeriods       = 7
MontlyContribution = .005 # 1/2 a percent / month

def initialize(context):
set_symbol_lookup_date('2015-01-01')
context.equitiesList    = symbols("SPY","QQQ")
context.fixedIncomeList = symbols("TLT","LQD")
context.Equities        = {}
context.FixedIncome     = {}
context.Leverage        = 1.0
context.PctEquitiesMultiplier = 1.0

schedule_function(RecordPercentages)
schedule_function(CalculatePortions, date_rule=date_rules.month_start())
schedule_function(Rebalance, date_rule=date_rules.month_start())

def handle_data(context, data):
record(Leverage     = context.account.leverage)
record(AccountValue = context.portfolio.positions_value + context.portfolio.cash)

#
# Reconcile securities to make sure, when the come online, that they're in the collections
#
for stock in data:
if (stock in context.equitiesList and stock not in context.Equities):
context.Equities[stock] = zipline.protocol.SIDData(stock)
continue
if (stock in context.fixedIncomeList and stock not in context.FixedIncome):
context.FixedIncome[stock] = zipline.protocol.SIDData(stock)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def Rebalance(context, data):
if (get_open_orders()):
return

accountValue = context.portfolio.positions_value + context.portfolio.cash
#
# Adjust the % equities by this dynamic, Aroon based multiplier
#
targetPctEquities  = (MaxPercentEquities * context.PctEquitiesMultiplier) / 100.0
targetPctFixedInc  = 1.0 - targetPctEquities

eligibleEquities  = [];  eligibleFixed  = []
accruedEquities   = 0.0; accruedFixed   = 0.0
actualPctEquities = 0.0; actualPctFixed = 0.0

#
# Equities and Fixed analysis
#
for stock in context.Equities:
if (stock not in data):
continue
accruedEquities += context.portfolio.positions[stock].amount * data[stock].close_price
eligibleEquities.append(stock)

for stock in context.FixedIncome:
if (stock not in data):
continue
accruedFixed += context.portfolio.positions[stock].amount * data[stock].close_price
eligibleFixed.append(stock)

actualPctEquities = accruedEquities / accountValue * 100.0
actualPctFixed    = accruedFixed    / accountValue * 100.0

#
# Test for rebalance trigger, if less than threshold - exit
# Note: this needs work to follow the actual % changes and only react on X% change
#
if (abs(MaxPercentEquities - actualPctEquities) < PercentThreshold):
return

print("Rebalancing... ")
print("Equities % of account {0:>5.2f}".format(actualPctEquities))
print("FixedInc % of account {0:>5.2f}".format(actualPctFixed))

#
# Equities rebalance
#
eligibleCount = float(len(eligibleEquities))
if (targetPctEquities == targetPctEquities):
for stock in eligibleEquities:
order_target_percent(stock, (targetPctEquities / eligibleCount) * context.Leverage)
#
# Fixed Income rebalance
#
eligibleCount = float(len(eligibleFixed))
if (targetPctFixedInc == targetPctFixedInc):
for stock in eligibleFixed:
order_target_percent(stock, (targetPctFixedInc / eligibleCount) * context.Leverage)

#
# Simulate a fractional account contribution
#
context.Leverage += MontlyContribution

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def RecordPercentages(context, data):
if (get_open_orders()):
return

accountValue = context.portfolio.positions_value + context.portfolio.cash

# Equities
accruedValue = 0.0
for stock in context.Equities:
if (stock in data):
accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price

pctOfAccount = accruedValue / accountValue * 100.0
record(EquityPct = pctOfAccount)

# Fixed Income
accruedValue = 0.0
for stock in context.FixedIncome:
if (stock in data):
accruedValue += context.portfolio.positions[stock].amount * data[stock].close_price

pctOfAccount = accruedValue / accountValue * 100.0
record(FixedIncomePct = pctOfAccount)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def CalculatePortions(context, data):
lowDeck  = history(AroonPeriods + 1, '1d', 'low')
highDeck = history(AroonPeriods + 1, '1d', 'high')
portionForEquities = 0; portionForFixed = 0

for stock in context.Equities:
aroon = talib.AROON(highDeck[stock], lowDeck[stock], timeperiod = AroonPeriods)
portionForEquities += (aroon[1][-1] - aroon[0][-1])
portionForEquities /= len(context.Equities)

for stock in context.FixedIncome:
aroon = talib.AROON(highDeck[stock], lowDeck[stock], timeperiod = AroonPeriods)
portionForFixed += (aroon[1][-1] - aroon[0][-1])
portionForFixed /= len(context.FixedIncome)

#
# Build a 0.0 to 1.0 multiplier against which the percent of equities will be modified
#
context.PctEquitiesMultiplier = (((200 - (portionForEquities - portionForFixed)) / 4.0) / 200.0)
#
# Force a minimum of equity participation
#
context.PctEquitiesMultiplier += (MinPercentEquities / 100.0)
record(PctEquitiesMultiplier = context.PctEquitiesMultiplier)

There was a runtime error.

The frontier between technical analysis (TA) and quantitative analysis (QA) is rather thin, though QA has better press these days. For example, moving averages are found in both TA and engineering like signal processing. Why would one be more noble than the other? I don't make any distinction between QA and TA methods as long as they remain testable.

Now, as systematic traders, we can draw an interesting parallel with cooking: the strategy being the recipe and its indicators the ingredients.

In cooking, nobody would expect to make a great meal out of combining random ingredients in an even more random way. In trading, however, it is not that uncommon. I call this thought-less backtesting.

The often missing question is a simple why? Why is this indicator part of the strategy? What are you trying to capture with it? What is its added-value? How can you tell whether it is working or not? Simple yet tough questions to answer.

I developed a short-only strategy that captures an intraday mean-reverting pattern that I found was repeating across a basket of similar securities. It did well historically and continued working decently after I started trading it. The strategy had a weakness however: the pattern wouldn't be strong enough to fight a solid bull market. In this adverse environment, short signals would keep being fired and with my short bias I'd get punched in the face.

So I added a technical indicator that would prevent me from trading in this environment. Which indicator? A simple 10-day moving average: if close(t-1) > sma(10, t-1), ignore signals on day t. Was it the best indicator for the job? Probably not. I tested a bunch of different indicators and ended up with this one because it correlates with rising prices, has a relatively short lookback period and is very simple. In other words, it does the job well enough for me.

Retrospectively (in backtests), filtering out signals was a losing proposition though: it hurt performance and had minimal impact on risk. From a pure quant guy's perspective, this was probably a suboptimal choice and chances are that he'd come up with an optimal (or rather optimized) solution much better than mine. This is where I believe qualitative thinking is required. I did not expect this filter to improve my edge - just to protect it. Insurance is not free, even if it's only for your peace of mind as I prepared for this 'worst case' scenario.

Is my approach better? I don't know. What matters to me is that it made (and still makes) sense and increased my confidence in this trading system.

So, do technical indicators add value? I would say that they can, but they are not the 'edge'. They are just tools that are useful for capturing this so called edge by enabling us, the traders, to quantify and identify trading setups that we've observed seem to work. I doubt that blindly applying technical indicators leads to success. Academic studies seem to confirm that naïvely applying technical trading rules doesn't work (yet still seem to think or hope that naïvely applying more sophisticated methods works :) .

Ingredients are not sacred. The art of cuisine is sacred. – Tanith Tyrr

PS: I'm a very bad cook. :)

The good thing of TA is that it is easy to test and validate, compared to other models still in use like CAPM, Efficient-Market.
TA has been tested and it shows no predictive power. Some say that before the 2000 markets it had some predictive power but
no predictive power anymore. But they still have some value when trying to get a market overview.

The good thing of TA is that it is easy to test and validate, compared to other models still in use like CAPM, Efficient-Market.
TA has been tested and it shows no predictive power. Some say that before the 2000 markets it had some predictive power but
no predictive power anymore. But they still have some value when trying to get a market overview.

If somebody knows links to good pure fundamental Quantopian algos without any technical analysis calculation
like:
ROC- Momentum
Moving Averages - Mean
Volatility - Standard Deviation
Commodity Channel Index - Standardize
Bollinger Bands - Z Score
Median - Minimum Euclidean distances
etc
and their combinations.
Please publish it here to compare.

@Alexis P,
I agree

Ingredients are not sacred. The art of cuisine is sacred.


@Alexis P., Good points and nicely said. Thanks for your input.

@Lionel do you have an algo for the MAC - US system mentioned above?

As Vladimir mentioned, a lot of the ingredients are the same. The trouble with technical analysis is the haphazard way in which people usually use the technologies, which result in statistically dubious overfit algorithms.

That said, I bet there are a lot of great "technical analysis" indicators which have some real insight, if analyzed. I remember screenshots from Linda Raschke's screen, and Teresa Lo's too. I haven't really followed any of those folks since I stopped day trading in the 90s.

I appreciate all the discussion on this topic. As a beginner in algorithmic trading, I am tempted to try various combinations of indicators and metrics to find something that has worked in the past, and then test it forward. But based on what many of you are saying, my efforts are probably futile unless I add something unique to the equation. Now the question is, what can I add that is unique?

BTW, I wanted to share that TradingView has a very simple indicator programming language called "Pine Script" that makes it very simple to create custom indicators. I am going to create a few indicators just for fun, and perhaps eventually come up with something useful.

Taking a step back, I have to admit that I am having a lot of fun with Quantopian. I used to play video games in the evenings, but now I hack on algos for hours, trying to get a higher "score". I know there is only a very small chance I will be good enough to make money, but I was never really that good of a gamer and I still had fun. :) There is also the side benefit that my newly acquired skills in Python will be useful in many professions.

@Tristan R., You have a healthy refreshing perspective. Thanks for that.

Regarding TradingView and PineScript, TV is a super resource. A stunning resource even. Along the lines of Quandl. In fact blending them is a given and encouraged. Pinescript on the other hand needs some serious reformulation. Control structures, conditionals, logicals, are all weird or missing. Here's an indicator I wrote; examine the conditional statements therein, ugly!

study(title = "TrailingRangeOffset", shorttitle = "TRO", overlay=true)

periods = 10
atr = atr(periods)
rangeOffsetMultiplier = input(title="RangeOffsetMultiplier", type=float, defval=3.0)
mtrOffset = atr * rangeOffsetMultiplier

priorTrail = na(priorTrail[1]) ?
low - mtrOffset :
priorTrail[1] > low ?
priorTrail[1] < high ?
low - mtrOffset > priorTrail[1] ?
low - mtrOffset :
priorTrail[1] :
high + mtrOffset < priorTrail[1] ?
high + mtrOffset :
priorTrail[1] :
low - mtrOffset > priorTrail[1] ?
low - mtrOffset :
priorTrail[1]
isUp = na(isUp[1]) ?
close > low  :
priorTrail[1] > low ?
priorTrail[1] < high ?
low - mtrOffset > priorTrail[1] ?
true :
isUp[1] :
high + mtrOffset < priorTrail[1] ?
false :
isUp[1] :
low - mtrOffset > priorTrail[1] ?
true :
isUp[1]
plot(priorTrail, color = isUp ? green : red, linewidth=2)


Incidently I've coded this for Quantopian too...

What special ingredients can you add to a vanilla strategy that uses vanilla indicators to spice it up? I suggest creating composites from baskets of securities and then use common indicators against those composites. Imagine taking the RSI for 100 securities and constructing a single RSI from them? What does that master-RSI tell you? Can you use it to gauge general market trend or behavior? What if you took a 21 period RSI and a 3 period RSI and performed the same treatment? One for Bias and one for Trade. When the Bias is > 50, and the Trade is < 50, take individual Trades that are extreme. Something like that. The simple story is, because Quantopian gives you easy access to baskets. Use them.

In my opinion TA is not totally useless, it really depends on how indicators are used. Rules and accounting is basically what trading algos boil down to, TA can supply rules but I'd argue accounting/risk management is more important. A problem with most indicators is signal lag, you can only buy/sell at spot prices, not their averages or some other derived value.

I find the intuition behind some indicators silly really. Moving average crosses / momentum algos are good examples of statistically absurd ideas, an average is the most probable value, but the strategy bets on deviations from this value; reversion back to a mean would make more sense. That being said, one of my most consistent algos uses short term momentum signals and I've found they work well. Since a price series isn't stationary none of the signals make much sense anyway. Bets on stationaryish spreads are more likely to work over the long term IMO. TA on derived values like a pair spread is probably more meaningful than on its components.

TA is quantifiable, so it conveys information and shouldn't be useless, what really matters is what's done with the information.

@Market Tech - I see nothing "weird" in your code example. This syntax of a conditional expression, condition ? thenExpression : elseExpression, has been around in C since 1970, and continues to be alive and well in C++ and Java. Things become more complicated and harder to read and debug when you nest and chain a lot of them - but that is true of every type of expression.

Python has a different conditional expression syntax: thenExpression if condition else elseExpression. I find it counterintuitive, because it reverses the C order of its components, as well as Python's own if condition : thenStatement newline else: elseStatement. But it's probably only a matter of individual preference which form you like better.