1.
Quantopian블로그에 의미있는 자료가 실렸습니다. Quant Strategies Implemented by the Quantopian Community는 Quantopian을 이용하는 트래이더들이 가장 많이 이용하는 전략을 순위를 매겨서 소개하고 있습니다.
이중에서 1위부터 3위까지의 전략을 아래에 연결했습니다. 확인해보시고 나머지는 직접 검색해보시길 바랍니다.
Google Search Terms predict market movements
OLMAR implementation – fixed bug
System based on Easy Volatility Investing by Tony Cooper @ Double-Digit Numerics
2.
Quantopian은 위의 통계를 기초로 하여 전체 전략을 Mean Reversion, Momentum, Value, Sentiment 및 Seasonality의 범주로 분류해 통계를 내놓았습니다.
Mean Version Strategy의 대표적인 사례로 Ernie Chan’s EWA/EWC pair trading을 소개합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
import numpy as np import pandas as pd from collections import deque from pytz import timezone window_days = 28 # window length in minutes window_minutes = window_days*390 # window length in minutes; each trading day has 390 minutes def initialize(context): context.max_notional = 100000 context.min_notional = -100000 context.stocks = [sid(14516), sid(14517)] context.evec = [0.943, -0.822] context.unit_shares = 20 context.tickers = [int(str(e).split(' ')[0].strip("Security(")) for e in context.stocks] context.prices = pd.DataFrame({ k : pd.Series() for k in context.tickers } ) context.previous_datetime = None context.new_day = None set_commission(commission.PerShare(cost=0.00)) def handle_data(context, data): # 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 current_datetime = get_datetime().astimezone(timezone('US/Eastern')) # detect new trading day if context.previous_datetime is None or current_datetime.day != context.previous_datetime.day: context.new_day = True context.previous_datetime = current_datetime #log.info("len price: {lp}, window: {window}".format(lp = len(context.prices), window = window_days)) if len(context.prices)<window_days and context.new_day: context.previous_datetime = get_datetime().astimezone(timezone('US/Eastern')) if intradingwindow_check(context): newRow = pd.DataFrame({k:float(data[s].price) for k,s in zip(context.tickers, context.stocks) },index=[0]) context.prices = context.prices.append(newRow, ignore_index = True) context.new_day = False else: if intradingwindow_check(context) and context.new_day: #context.new_day = False comb_price_past_window = np.zeros(len(context.prices)) for ii,k in enumerate(context.tickers): comb_price_past_window += context.evec[ii]*context.prices[k] meanPrice = np.mean(comb_price_past_window); stdPrice = np.std(comb_price_past_window) comb_price = sum([e*data[s].price for e,s in zip(context.evec, context.stocks)]) h = (comb_price - meanPrice)/stdPrice current_amount = []; cash_spent = []; for ii, stock in enumerate(context.stocks): current_position = context.portfolio.positions[stock].amount new_position = context.unit_shares * (-h) * context.evec[ii] current_amount.append(new_position) cash_spent.append((new_position - current_position)*data[stock].price) order(stock, new_position - current_position) context.new_day = False #log.info("ordered!") notionals = [] for ii,stock in enumerate(context.stocks): #notionals.append((context.portfolio.positions[stock].amount*data[stock].price)/context.portfolio.starting_cash) notionals.append((context.portfolio.positions[stock].amount*data[stock].price)/context.portfolio.starting_cash) log.info("h = {h}, comb_price = {comb_price}, notionals = {notionals}, total = {tot}, price0 = {p0}, price1 = {p1}, cash = {cash}, amount = {amount}, new_cash = {nc}".\ format(h = h, comb_price = comb_price, notionals = notionals, \ tot = context.portfolio.positions_value + context.portfolio.cash, p0 = data[context.stocks[0]].price, \ p1 = data[context.stocks[1]].price, cash = context.portfolio.cash, amount = current_amount, \ nc = context.portfolio.cash - sum(cash_spent))) newRow = pd.DataFrame({k:float(data[s].price) for k,s in zip(context.tickers, context.stocks) },index=[0]) context.prices = context.prices.append(newRow, ignore_index = True) context.prices = context.prices[1:len(context.prices)] record(h = h, mPri = meanPrice) record(comb_price = comb_price) record(not0 = notionals[0], not1 = notionals[1]) #if not context.new_day: # log.info("time = {time}, cash = {cash}".format(cash = context.portfolio.cash, time = current_datetime)) #record(price0 = data[context.stocks[0]].price*abs(context.evec[0]), price1 = data[context.stocks[1]].price*abs(context.evec[1])) #record(price0 = data[context.stocks[0]].price, price1 = data[context.stocks[1]].price) #record(port = context.portfolio.positions_value, cash = context.portfolio.cash) def intradingwindow_check(context): # 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 == 15 and loc_dt.minute > 0: return True else: return False |
다음은 Momentum Strategy의 대표로 Mebane Faber Relative Strength Strategy with MA Rule Value Strategy을 소개합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# http://papers.ssrn.com/sol3/papers.cfm?abstract_id=962461 # SPY EFA AGG VNQ GLD def initialize(context): context.secs = [sid(8554),sid(22972),sid(25485),sid(26669),sid(26807)] set_commission(commission.PerShare(cost=.005)) leverage = 1.0 context.top_k = 1 context.weight = leverage/context.top_k import numpy as np @batch_transform(refresh_period=20, window_length=61) def trailing_return(datapanel): if datapanel['price'] is None: return None pricedf = np.log(datapanel['price']) return pricedf.ix[-1]-pricedf.ix[0] def reweight(context,data,wt,min_pct_diff=0.1): liquidity = context.portfolio.positions_value+context.portfolio.cash orders = {} pct_diff = 0 for sec in wt.keys(): target = liquidity*wt[sec]/data[sec].price current = context.portfolio.positions[sec].amount orders[sec] = target-current pct_diff += abs(orders[sec]*data[sec].price/liquidity) if pct_diff > min_pct_diff: #log.info(("%s ordering %d" % (sec, target-current))) for sec in orders.keys(): order(sec, orders[sec]) def handle_data(context, data): ranks = trailing_return(data) abs_mom = lambda x: data[x].mavg(20)-data[x].mavg(200) if ranks is None: return ranked_secs = sorted(context.secs, key=lambda x: ranks[x], reverse=True) top_secs = ranked_secs[0:context.top_k] wt = dict(((sec,context.weight if sec in top_secs and abs_mom(sec) > 0 else 0.0) for sec in context.secs)) reweight(context,data,wt) |
Value전략은 Using the Fetcher with Quandl, Sentiment 전략은 Ranking and Trading on “Days to Cover”을 소개하고 있습니다.
이상을 정리하여 2014년 2월 ‘An introduction to implementing 5 basic quant strategies on Quantopian’라는 이름으로 Bay Area Algorithmic Trading Group 행사때 발표한 자료입니다.
3.
Quantopian은 Python을 이용하여 전략을 개발합니다. InteractiveBroker API를 이용하여 실거래서비스도 제공합니다. 소셜 알고리즘트레이딩입니다. 한국의 경우 이런 모델이 가능할까요? 시장구조가 KRX 독점입니다. 차이를 만들어 알파를 찾기가 힘들어 보입니다. 그래서 저는 부정적입니다.
요즘 Python for Data Analysis라는 책을 스터디하고 있는데, Numpy와 Pandas 등이 주요 내용입니다. 공부할겸 Quantopian 소스 살펴보는 것도 좋을 거 같네요. 🙂
저도 Python을 공부하려고 하는이유중 하나가 quantopian때문입니다. 시간이 많이 걸리겠지만..