Category Archives: Finance

[Python] Modern Portfolio Theory (현대 포트폴리오 이론) 파이썬으로 구현하기 (1)

현대 포트폴리오 이론은 해리 마코위츠가 만든 이론으로, 자산을 분산투자하여 포트폴리오를 만들게 되면 분산투자 전보다 위험을 감소시킬 수 있다는 이론이다.

관련된 설명은 여기에 정말 잘 나와 있다. 내가 어줍짢게 주절 거리는 것 보다 링크를 참고하는게 설명이 더 잘 나와 있다.

 

그래서 이번 시간에는 이 이론을 Python으로 한번 적용해보려고 한다.

참고 정보는 Mastering Pandas for Finance 에서 발췌했다.

 

델타항공, 코카콜라 그리고 시스코에 투자한다고 가정했을 때 이를 현대 포트폴리오 이론에 적용 해보려고 한다.


기본 정보 도출

def get_historical_closes(ticker, start_date, end_date):

    """
    시작일과 종료일을 받아서 종가를 구한다.
    :param ticker: stock symbole
    :param start_date: startdate
    :param end_date: enddate
    :return: pivtoed data
    """
    raw_data = web.DataReader(ticker, "yahoo", start=start_date, end=end_date)
    data = raw_data.to_frame()['Adj Close'].reset_index()
    data.rename(columns={'minor': 'Ticker', 'Adj Close': 'Close'}, inplace=True)
    pivoted = data.pivot(index='Date', columns='Ticker')
    pivoted.columns = pivoted.columns.droplevel(0)
    
    return pivoted
closes = closes = get_historical_closes(['DAL', 'KO', 'SYY']
                               , '2010-01-01'
                               , '2016-05-10')

closes[len(closes) - 5:len(closes) - 1]
Ticker         DAL     KO  \
Date                        
2016-05-04  41.297  44.98   
2016-05-05  41.616  45.06   
2016-05-06  41.905  45.32   
2016-05-09  42.400  45.24   

Ticker        SYY  
Date               
2016-05-04  48.52  
2016-05-05  48.87  
2016-05-06  49.37  
2016-05-09  49.72   

여기에서 로그값을 구해 보자.

def calc_daily_returns(closes):
    return np.log(closes/closes.shift(1))

daily_returns = calc_daily_returns(closes)
daily_returns[len(daily_returns) - 5:len(daily_returns) - 1]
Ticker        DAL     KO  \
Date                       
2016-05-04 -0.035  0.003   
2016-05-05  0.008  0.002   
2016-05-06  0.007  0.006   
2016-05-09  0.012 -0.002   

Ticker            SYY  
Date                   
2016-05-04  2.062e-04  
2016-05-05  7.188e-03  
2016-05-06  1.018e-02  
2016-05-09  7.064e-03

그리고 이 정보들을 연도 별로 취합해서 지수 값을 구해 보자.

def calc_annual_returns(daily_returns):
    return np.exp(daily_returns.groupby(lambda date: date.year).sum()) - 1   

annual_returns = calc_annual_returns(daily_returns)

annual_returns
Ticker    DAL     KO    SYY
2010    0.123  0.189  0.073
2011   -0.358  0.095  0.035
2012    0.467  0.065  0.119
2013    1.326  0.172  0.189
2014    0.805  0.053  0.134
2015    0.040  0.051  0.058
2016   -0.147  0.073  0.241

포트폴리오 위험도(분산) 구하기

 

이제 이 자료들을 바탕으로 포트폴리오의 위험도, 즉 분산 값을 구해보고자 한다.

 \sigma_p^2 = \sum_i \sum_j w_i w_j \sigma_i \sigma_j \rho_{ij}

여기서 pij 값은 i 와 j의 상관 계수 값을 의미한다.

그리고 이 식은 다음과 같이 압축 될 수 있다.

관련 계산식 등은 여기에서 참고하면 좋다.

조만간 블로그에서 수학식을 입력할 수 있는 방법을 강구해 보도록 해야겠다.

def calc_portfolio_var(returns, weights=None):
    
    if weights is None:
        weights = np.ones(returns.columns.size) / returns.columns.size 
    sigma = np.cov(returns.T, ddof=0)
    return (weights * sigma * weights.T).sum()

먼저 weight는 가중치다. 여기서 따지자면, 각각 몇주 씩을 샀는지로 나타낼 수 있다.

현재 함수에서는 가중치가 없을 경우, 모두 동일하게 1 값을 주도록 설정되어 있다.

calc_portfolio_var(annual_returns)

결과는 0.3483651771289426 이다.

1과 가까울 수록, 상관계수가 높다. 상관계수가 높을 수록, 두 자산이 함께 움직인 다는 것을 의미한다. 함께 움직인다는  것은, 위험을 공유할 가능성이 크다는 것이다.

이처럼 가깝게 움직이지 않는 자산들로 포트폴리오를 구성하면, 수익률을 낮추지 않고서 리스크를 감소시킬 수 있는 효과를 가져올 수 있다.

지금 예제로 든 주식들에 함께 투자하면, 위험성이 어느 정도 낮은 것 (상관계수가 낮은 것) 으로 보여지고 있다.

 


샤프지수 구하기

 

어느정도 3 주식 조합이 괜찮아 보이는 것 같으니, 이제 이 투자 비율을 어떻게 가져가야 수익성을 극대화 할 수 있는지 머리를 굴려볼 차례다.

샤프지수, 또는 샤프 비율이란 펀드 한 단위의 위험자산에 투자함으로써 얻은 초과수익의 정도를 나타내는 지표다.

다시 말하면 1이라는 위험을 부담하는 대신 얻은 대가, 즉 초과 수익이 얼마인가를 측정하는 지표이다.

예상되는 포트폴리오 수익에 무위험이자율을 빼고, 이를 다시 포트폴리오 분산으로 나누면 샤프지수를 구할 수 있다.

def shrape_ratio(returns, weights=None, risk_free_rate=0.015):
    
    n = returns.columns.size
    if weights is None:
        weights = np.ones(n) / n
    var = calc_portfolio_var(returns, weights)
    means = returns.mean()
    return (means.dot(weights) - risk_free_rate) / np.sqrt(var)

여기서는 무위험이자율을 0.015라고 임의로 가정했다.

shrape_ratio(annual_returns)
0.89554903141826914

샤프 지수가 약 1이다. 보통 1은 넘겨야 한다고 많이 들 얘기하는데, 우리나라는 주식의 위험성이 너무 크고 펀드의 만기도 짧아 (1년이내) 0.5 조차 넘기기 힘들다.

이 정도 면 .. 썩 좋다고 볼수는 없을 것 같다.

 


샤프지수로 포트폴리오 수익률 극대화 하기

이제 우리가 할일은 샤프 지수를 만땅으로 가져오는 것이다. 그 말인 즉슥, 앞서 언급한 3개의 주식을 어떻게 분배해서 가져가야 샤프 지수를 최대로 가져 올 수 있는지 고민해 봐야 하는 것이다.

scipy.optimize.fmin 은 함수와 seed값이 주어졌을때, 해당 함수에서 나올 수 있는 최소값을 도출하는 함수다. 이 함수를 역으로 활용해서, 요래조래 weight를 조절하면서 제일 작은 음의 샤프 지수 값을 리턴하게 한 다면, 최대 샤프지수를 가져올 수 있을 것이다.

def negative_sharpe_ratio(weights, returns, risk_free_rate):

    weights2 = sp.append(weights, 1 - np.sum(weights))
    print(-sharpe_ratio(returns, weights2, risk_free_rate))
    return -sharpe_ratio(returns, weights2, risk_free_rate)


def optimize_portfolio(returns, risk_free_rate):

    n = returns.columns.size

    w0 = np.ones(n - 1, dtype=float) * 1.0 / n
    w1 = sciopt.fmin(negative_sharpe_ratio
                     , w0
                     , args=(returns, risk_free_rate))

    final_w = sp.append(w1, 1 - np.sum(w1))
    final_sharpe = sharpe_ratio(returns, final_w, risk_free_rate)
    return final_w, final_sharpe

print(optimize_portfolio(annual_returns, 0.0003))

 

Optimization terminated successfully.
         Current function value: -1.405577
         Iterations: 41
         Function evaluations: 77
[ 0.06238759  0.55659982]
(array([ 0.06238759,  0.55659982,  0.38101259]), 1.405577062459429)

요래 조래 머리를 굴려본 결과, 델타항공에 6%, 코카콜라에 56프로, 시스코에 38% 를 투자한다면 샤프지수 1.40을 얻을 수 있다는 결론이 나왔다.

 

현대 포트폴리오 이론에 따라서 쌩뚱 맞은 주식들의 투자비율을 어떻게 가져가야 하는지 알아보았다.

물론 이 이론이 완벽한 것은 아니니

maxresdefault

그냥 Python으로 구현해보는 것에 만족하길 바란다.

 

다음 포스팅에서 예상 수익률을 구해보려고 한다.

[Python] 알고리즘 트레이딩 (2) – Python Zipline 으로 일단 주식 사보기

앞선 포스팅에서는 주식을 하는 사람이라면 다 아는 이동평균선을 Python으로 만들면서 장난을 쳐보았다. 이번 포스팅에서는 조금 더 진지하게 알고리즘 트레이딩을 구현해보려고 한다.

zipline은 Python으로 알고리즘 트레이딩을 할 수 있는 라이브러리다.

zipline은 마찬가지로 Python으로 짜여진 quantopian 이라고 하는 실시간 트레이딩 엔진을 사용하고 있다.

zipline 뿐만 아니라 quantopian 사이트에 방문해보는 것도 재밌을 것 같다.

관심 있는 분들은 꼭 한번 들어가 보길 바란다.

 

주저리 주저리 설명을 보는 것 보다, 실제로 작동하는 코드를 설명과 함께 보는 것이 더 효율적일 것 같다.

 

import zipline as zp
import zipline.utils.factory as zpf
from zipline.api import order, sid, symbol

import pandas as pd
import pandas.io.data as web
import numpy as np

from datetime import datetime
import matplotlib.pyplot as plt

class BuyGoogle(zp.TradingAlgorithm):
    
    trace = False
    
    def __init__(self, trace=False):
        BuyGoogle.trace = trace
        super(BuyGoogle, self).__init__()
        
    def initialize(context):
        print("Initialize")
        print(context)
        print("finish initialize")
            
    def handle_data(self, context):
        print("Handle Data...")
        print(context)
        self.order(symbol("GOOGL"), 1)
        print("Finish handling")

 

import 한 클래스들. 그리고 zipline의 trading algorithms 을 변수로 받아서 Google을 사는 클래스를 구현해보았다.

보면 알겠지만, 일단 그냥 아무런 의미 없이 한주를 사는 클래스다. 그 외에는 그냥 로그를 찍는 느낌으로 context 를 찍어낸다.

handle_Data를 상속받아서 작성했는데, 저 handle_Data는 하루에 한번 실행된다.

context 내의 내용을 받아서 알고리즘 트레이딩을 구현할 수 있을 것이다. 일단 그냥 나는 한주만 산다.

data = zpf.load_from_yahoo(stocks=['GOOGL'], 
                           indexes={}, 
                           start=datetime(1990, 1, 1), 
                           end=datetime(2015, 12, 31),
                           adjusted=False)
data.plot(figsize=(12,8))

GOOGL_1990~2015

pandas와 비슷한 방식으로 google의 종가를 가져올 수 있다.

 

이제 주식을 사보자.

# 2015년 1월 1일 부터 15일간 사본다.
result = BuyGoogle().run(data['2015-01-01':'2015-01-15'])

결과

Initialize
BuyGoogle(
    capital_base=100000.0
    sim_params=
SimulationParameters(
    period_start=2015-01-02 00:00:00+00:00,
    period_end=2015-01-15 00:00:00+00:00,
    capital_base=100000.0,
    data_frequency=daily,
    emission_rate=daily,
    first_open=2015-01-02 14:31:00+00:00,
    last_close=2015-01-15 21:00:00+00:00),
    initialized=False,
    slippage=VolumeShareSlippage(
    volume_limit=0.25,
    price_impact=0.1),
    commission=PerShare(cost=0.03, min trade cost=None),
    blotter=Blotter(
    transact_partial=(VolumeShareSlippage(
    volume_limit=0.25,
    price_impact=0.1), PerShare(cost=0.03, min trade cost=None)),
    open_orders=defaultdict(<class 'list'>, {}),
    orders={},
    new_orders=[],
    current_dt=2006-01-01 00:00:00+00:00),
    recorded_vars={})
finish...
Handle Data...
BarData(SortedDict(<function AlgorithmSimulator.__init__.<locals>._get_removal_date at 0x7ff6904ebe18>, [(0, SIDData({'price': 529.549988, 'type': 4, '_sid': 0, '_freqstr': None, 'volume': 1000000000, 'sid': 0, '_initial_len': 3, 'dt': Timestamp('2015-01-02 00:00:00+0000', tz='UTC'), 'source_id': 'DataFrameSource-f0269ab25f28863c365bfd07c474cf5b'}))]))
Finish.
...........생략....................
Handle Data...
BarData(SortedDict(<function AlgorithmSimulator.__init__.<locals>._get_removal_date at 0x7ff6904ebe18>, [(0, SIDData({'price': 504.01000999999997, 'type': 4, '_sid': 0, '_freqstr': None, 'volume': 1000000000, 'sid': 0, '_initial_len': 3, 'dt': Timestamp('2015-01-15 00:00:00+0000', tz='UTC'), 'source_id': 'DataFrameSource-f0269ab25f28863c365bfd07c474cf5b'}))]))
Finish.
result
 algo_volatility  algorithm_period_return     alpha  \
2015-01-02 21:00:00         0.000000             0.000000e+00 -0.021200   
2015-01-05 21:00:00         0.000003            -3.000000e-07 -0.020398   
2015-01-06 21:00:00         0.001176            -1.288001e-04 -0.030992   
2015-01-07 21:00:00         0.000966            -1.589005e-04 -0.029256   
2015-01-08 21:00:00         0.001062            -1.064002e-04 -0.025784   
2015-01-09 21:00:00         0.001749            -3.543003e-04 -0.033546   
2015-01-12 21:00:00         0.001763            -5.376005e-04 -0.036167   
2015-01-13 21:00:00         0.002602            -2.535011e-04 -0.024737   
2015-01-14 21:00:00         0.002967             3.529929e-05 -0.015598   
2015-01-15 21:00:00         0.002907            -1.185993e-04 -0.017516   

                     benchmark_period_return           ...            \
2015-01-02 21:00:00                -0.000340           ...             
2015-01-05 21:00:00                -0.018612           ...             
2015-01-06 21:00:00                -0.027340           ...             
2015-01-07 21:00:00                -0.016028           ...             
2015-01-08 21:00:00                 0.001574           ...             
2015-01-09 21:00:00                -0.006843           ...             
2015-01-12 21:00:00                -0.014882           ...             
2015-01-13 21:00:00                -0.017422           ...             
2015-01-14 21:00:00                -0.023134           ...             
2015-01-15 21:00:00                -0.032168           ...             

                     starting_value  trading_days  \
2015-01-02 21:00:00        0.000000             1   
2015-01-05 21:00:00        0.000000             2   
2015-01-06 21:00:00      519.460022             3   
2015-01-07 21:00:00     1013.280030             4   
2015-01-08 21:00:00     1515.449982             5   
2015-01-09 21:00:00     2027.640016             6   
2015-01-12 21:00:00     2503.600005             7   
2015-01-13 21:00:00     2982.359988             8   
2015-01-14 21:00:00     3512.599916             9   
2015-01-15 21:00:00     4047.439944            10   

                                                          transactions  \
2015-01-02 21:00:00                                                 []   
2015-01-05 21:00:00  [{'amount': 1, 'price': 519.490022, 'order_id'...   
2015-01-06 21:00:00  [{'amount': 1, 'price': 506.67001500000003, 'o...   
2015-01-07 21:00:00  [{'amount': 1, 'price': 505.179994, 'order_id'...   
2015-01-08 21:00:00  [{'amount': 1, 'price': 506.940004, 'order_id'...   
2015-01-09 21:00:00  [{'amount': 1, 'price': 500.75000099999994, 'o...   
2015-01-12 21:00:00  [{'amount': 1, 'price': 497.08999800000004, 'o...   
2015-01-13 21:00:00  [{'amount': 1, 'price': 501.82998799999996, 'o...   
2015-01-14 21:00:00  [{'amount': 1, 'price': 505.95999299999994, 'o...   
2015-01-15 21:00:00  [{'amount': 1, 'price': 504.04000999999994, 'o...   

                     treasury_period_return  
2015-01-02 21:00:00                  0.0212  
2015-01-05 21:00:00                  0.0204  
2015-01-06 21:00:00                  0.0197  
2015-01-07 21:00:00                  0.0196  
2015-01-08 21:00:00                  0.0203  
2015-01-09 21:00:00                  0.0198  
2015-01-12 21:00:00                  0.0192  
2015-01-13 21:00:00                  0.0191  
2015-01-14 21:00:00                  0.0186  
2015-01-15 21:00:00                  0.0177  

[10 rows x 38 columns]

채결했던 주문에 대해서도 볼 수 있다.

result.iloc[1].orders
[{'amount': 1,
  'commission': 0.03,
  'created': Timestamp('2015-01-02 00:00:00+0000', tz='UTC'),
  'dt': Timestamp('2015-01-05 00:00:00+0000', tz='UTC'),
  'filled': 1,
  'id': 'b540e952e5b24d42ba533b49b1460bc0',
  'limit': None,
  'limit_reached': False,
  'reason': None,
  'sid': Equity(0, symbol='GOOGL', asset_name=None, exchange=None, start_date=Timestamp('1970-01-01 00:00:00+0000', tz='UTC'), end_date=Timestamp('2116-02-20 23:53:38.427387903+0000', tz='UTC'), first_traded=None, auto_close_date=None),
  'status': 1,
  'stop': None,
  'stop_reached': False},
 {'amount': 1,
  'commission': None,
  'created': Timestamp('2015-01-05 00:00:00+0000', tz='UTC'),
  'dt': Timestamp('2015-01-05 00:00:00+0000', tz='UTC'),
  'filled': 0,
  'id': 'af6837dcf91f41dfb8dca39675fea87f',
  'limit': None,
  'limit_reached': False,
  'reason': None,
  'sid': Equity(0, symbol='GOOGL', asset_name=None, exchange=None, start_date=Timestamp('1970-01-01 00:00:00+0000', tz='UTC'), end_date=Timestamp('2116-02-20 23:53:38.427387903+0000', tz='UTC'), first_traded=None, auto_close_date=None),
  'status': 0,
  'stop': None,
  'stop_reached': False}]

그래서 결과는?

result[['starting_cash', 'ending_cash', 'ending_value']]
 starting_cash    ending_cash  ending_value
2015-01-02 21:00:00  100000.000000  100000.000000      0.000000
2015-01-05 21:00:00  100000.000000   99480.509978    519.460022
2015-01-06 21:00:00   99480.509978   98973.839963   1013.280030
2015-01-07 21:00:00   98973.839963   98468.659969   1515.449982
2015-01-08 21:00:00   98468.659969   97961.719965   2027.640016
2015-01-09 21:00:00   97961.719965   97460.969964   2503.600005
2015-01-12 21:00:00   97460.969964   96963.879966   2982.359988
2015-01-13 21:00:00   96963.879966   96462.049978   3512.599916
2015-01-14 21:00:00   96462.049978   95956.089985   4047.439944
2015-01-15 21:00:00   95956.089985   95452.049975   4536.090090

왠지 약간 손해본 느낌이다.

좀 더 정확히 보자

result.portfolio_value
2015-01-02 21:00:00             NaN
2015-01-05 21:00:00   -3.000000e-07
2015-01-06 21:00:00   -1.285001e-04
2015-01-07 21:00:00   -3.010430e-05
2015-01-08 21:00:00    5.250864e-05
2015-01-09 21:00:00   -2.479265e-04
2015-01-12 21:00:00   -1.833651e-04
2015-01-13 21:00:00    2.842522e-04
2015-01-14 21:00:00    2.888736e-04
2015-01-15 21:00:00   -1.538932e-04
Name: portfolio_value, dtype: float64
result['returns']
2015-01-02 21:00:00    0.000000e+00
2015-01-05 21:00:00   -3.000000e-07
2015-01-06 21:00:00   -1.285001e-04
2015-01-07 21:00:00   -3.010430e-05
2015-01-08 21:00:00    5.250864e-05
2015-01-09 21:00:00   -2.479265e-04
2015-01-12 21:00:00   -1.833651e-04
2015-01-13 21:00:00    2.842522e-04
2015-01-14 21:00:00    2.888736e-04
2015-01-15 21:00:00   -1.538932e-04
Name: returns, dtype: float64

그렇다. 손해를 봤다.

result.portfolio_value.plot(figsize=(12, 8))

resut_GOOGL_201501

 

좀 더 화끈하게, 2015년 내내 한주씩 사보자.

result_for_2015 = BuyGoogle().run(data['2015'])
result_for_2015.portfolio_value.plot(figsize=(12, 8))

GOOGL_2015

외쳐 Google!

 

[Python] 알고리즘 트레이딩 (1) – 이동 평균

한 동안 주식 관련 Python 포스팅을 안했던 것 같다.

나름 요즘 말도 안되는 objective-c 작업을 하느라 정신이 없었다.

오늘은 예전에 moving average 관련 포스팅을 잠깐 했었는데, 이번엔 이를 이용해서 알고리즘 트레이딩 예제를 만들어  볼까 한다.

Mastering for Pandas에서 몇몇 기법을 참고했다.

 

그리고 pandas에서 deprecated 처리된 많은 메소드 등을 최신 버전에 맞춰서 컨버팅 했다.

 

import pandas as pd
import pandas.io.data as web
import numpy as np
from datetime import datetime
import matplotlib.pyplot as plt
%matplotlib inline

# 2000.1.1 ~ 2015.12.31 Apple stock price by yahoo finance
AAPL = web.DataReader("AAPL", "yahoo", datetime(1997, 1, 1), datetime(2015, 12, 31))

# Moving Average 
# pd.rolling_mean이 deprecated 되었다. 이제 이런 식으로 작성해야 한다
AAPL['MA7'] = pd.Series.rolling(AAPL['Adj Close'], 7).mean()
AAPL['MA30'] = pd.Series.rolling(AAPL['Adj Close'], 30).mean()
AAPL['MA90'] = pd.Series.rolling(AAPL['Adj Close'], 90).mean()
AAPL['MA120'] = pd.Series.rolling(AAPL['Adj Close'], 120).mean()

이동평균을 구했다.

이제 1997년과 2014년 주식 추이를 보도록 하자.

# 1997년 8월 경에 잡스가 돌아온다고 선언했던 것 같다.
# 시장이 기대감에 폭주했다가 이내 제자리를 찾았다
AAPL['1997'][['Adj Close', 'MA7', 'MA30', 'MA90', 'MA120']].plot(figsize=(12, 8))

APPL1997

# 아이폰과 맥 판매가 쩔던 시절
AAPL['2014'][['Adj Close', 'MA7', 'MA30', 'MA90', 'MA120']].plot(figsize=(12, 8))

APPL2014

증권전문가 그랜빌은 200일 이동평균선이 신뢰할만하다고 주장하기도 했다. 한국은 대부분의 HTS가 120일 이동평균선을 보고 있다. 관련 내용은 여기에 잘 나와있다.

이동 평균선을 구하는 건 여기저기에 많이 나와있으니 자세한 설명은 생략하도록 하고, 이번에 해볼 것은 가중 이동 평균이다.

이동평균의 경우에는 반영하는 속도가 느리기 때문에, 이런 이동평균에 가중치를 더해 이동평균을 구하기도 한다.

현재에 가까운 가격일 수록 비중을 더 두는 방식이다. n 일간의 가중이동평균을 구한다고 한다면, 첫째날에는 가격 x N 을, 그리고 마지막날에는 가격 x 1 을 해서 이동평균을 구한다고 생각하면 된다.

python에서 가중이동평균을 구해보도록 하자.

 

# 가중치 설정
weight = 90
AAPL_EWMA = AAPL[['Adj Close']].copy()
AAPL_EWMA['MA90'] = pd.Series.rolling(AAPL_EWMA, weight).mean()
AAPL_EWMA['EWMA90'] = pd.Series.ewm(AAPL_EWMA['Adj Close'], span=weight, adjust=True).mean()
AAPL_EWMA['2014'].plot(figsize=(12, 8))

APPL_adj_2014 잘 보면 가중이동 평균이 조금 더 민감하게 반응하는 것을 볼 수 있다.

 

이를 바탕으로 이른바 크로스오버라고 불리우는,  주식시장에 있어서 변곡점(이렇게 부르나?)을 찾고자 한다. 이동평균곡선이 만나는 그때, 그때를 찾아서 트레이딩을 하는 것이다.

 

AAPL['2004-10':'2005-08'][['Adj Close', 'MA30']].plot(figsize=(12, 8))

APPL_adj_2004-10~2005-08

위 그래프에서 2005년 8월 초 쯤을 주목해보자. 주식이 30일 이동평균 아래로 떨어지면서 만나는 지점이 있다. 이는 이제 곧 주가가 떨어질 거라는 신호다.

반대로 2005년 7월 중순처럼, 위로 올라가면서 만나는 지점에서는, 주가가 상승하는 모양을 보이고 있다.

 

AAPL['2011-10':'2012-05'][['Adj Close', 'MA30', 'MA90']].plot(figsize=(12, 8))

APPL_adj_2011-10~2012-05

위와 같이 서로 다른 두개의 이동평균을 가지고도 계산해 볼 수 있다. 이 경우에는 더 범위가 좁은 날짜의 움직임을 기준으로 삼는다.

 

 

보면 알겠지만 굉장히 기초적이고 단순한 시스템 트레이딩 방식이다.

존경하는 분께서 이동평균선을 이용한 트레이딩에 대한 글에 대해 굉장히 잘써주셨다.

2장에서는 Python 트레이딩 라이브러리인 zipline에 대해서 알아보려고 한다.

[Python] Pandas로 옵션 정보가져오기

Pandas로 주식 정보 뿐만 아니라 Option 정보도 가져올 수 있다.

 


# 기본세팅
import pandas as pd
import numpy as np
import pandas.io.data as web
from datetime import datetime

import matplotlib.pyplot as plt
%matplotlib inline

본격적으로 옵션정보를 가져와보자. 주식정보를 가져오는 것이랑 똑같다.

# 이렇게 한다음
aapl_options = web.Options('AAPL', 'yahoo')
# 모든 데이터를 가져온 다음에, 인덱스를 리셋한다.
aapl_options = aapl_options.get_all_data().reset_index()

# 적절하게 정렬 및 필요한 데이터만 뽑기
aos = aapl_options.sort(['Expiry', 'Strike'])[['Expiry', 'Strike', 'Type', 'IV', 'Bid', 'Ask', 'Underlying_Price']]

# IV 에서 %를 지우고 Float을 유지한다.
aos['IV'] = aos['IV'].apply(lambda x: float(x.strip('%')))
aos[:5]

대략이런모양이 나온당.

Expiry Strike Type IV Bid Ask Underlying_Price
0 2016-01-15 34.29 call 329.69 62.55 62.90 96.96
1 2016-01-15 34.29 put 262.50 0.00 0.01 96.96
2 2016-01-15 35.71 call 329.69 61.15 61.50 96.96
3 2016-01-15 35.71 put 268.75 0.00 0.02 96.96
4 2016-01-15 37.14 call 306.25 59.70 60.05 96.96

 

옵션의 만기일만 따로 뽑아볼수도 있을 것이다.


aos['Expiry'].unique()

array([‘2016-01-15T00:00:00.000000000+0000’,
‘2016-01-22T00:00:00.000000000+0000’,
‘2016-01-29T00:00:00.000000000+0000’,
‘2016-02-05T00:00:00.000000000+0000’,
‘2016-02-12T00:00:00.000000000+0000’,
‘2016-02-19T00:00:00.000000000+0000’,
‘2016-02-26T00:00:00.000000000+0000’,
‘2016-03-18T00:00:00.000000000+0000’,
‘2016-04-15T00:00:00.000000000+0000’,
‘2016-06-17T00:00:00.000000000+0000’,
‘2016-07-15T00:00:00.000000000+0000’,
‘2016-10-21T00:00:00.000000000+0000’,
‘2017-01-20T00:00:00.000000000+0000’,
‘2018-01-19T00:00:00.000000000+0000′], dtype=’datetime64[ns]’)

데이터를 하나 샘플로 뽑아보도록 하자

aos.loc[499] 

Expiry 2016-02-26 00:00:00
Strike 105
Type call
IV 33.37
Bid 1.81
Ask 1.87
Underlying_Price 96.96
Name: 499, dtype: object

aos.loc[500] 

Expiry 2016-02-26 00:00:00
Strike 105
Type put
IV 37.06
Bid 10.15
Ask 10.35
Underlying_Price 96.96
Name: 500, dtype: object

[Python] Panda, Numpy, Matplotlib 로 주가 분석해보기 (예제)

사실 이전에 올렸던 포스트들의 재탕이긴 한데, 그래도 공부하는 차원에서 올려 놓는다.

import pandas as pd
import numpy as np
import pandas.io.data as web

from matplotlib import pyplot as plt
%matplotlib inline
# 마이크로소프트 주식을 야후에서 가져옴.
msft = web.DataReader('MSFT', "yahoo", '2011-01-01', '2016-01-05')

# 그래프로 그려볼 Ticker들
tickers = ['AAPL', 'MSFT', 'GE', 'IBM', 'AA', 'DAL', 'UAL', 'PEP', 'KO']
# 주식을 읽어와서 합쳐주는 Function
def get(tickers, start, end):
  def data(ticker):
    return web.DataReader(ticker, 'yahoo', start, end)
  datas = map(data, tickers)
  return pd.concat(datas, keys=tickers, names=['Ticker', 'Date'])
# 읽어오기
all_data = get(tickers, '2001-01-01', '2016-01-01')

# 종가만 뽑아온다.
closing_prices = all_data[['Adj Close']].reset_index()
# 그래프를 살짝 그려보자
daily_close.plot(grid=True, figsize=(20, 10))

stock_ex1

언제봐도 아름답다

# 이제 마이크로소프트 사의 주식 거래량을 뽑아와보자
msftV = all_data.Volume.loc['MSFT']
plt.bar(msftV.index, msftV)
plt.gcf().set_size_inches(20, 6)

stock_ex2

도대체 2006년 말에 무슨일이 있었던 걸까?

# 이번엔 마이크로소프트의 주가랑 거래량을 한번에 그려보도록 하겠다.
top = plt.subplot2grid((4, 4), (0, 0), rowspan=3, colspan=4)
top.plot(daily_close.index, daily_close['MSFT'], label='Adjusted Close')
plt.title('MSFT Adjusted Close Price from 2001-2014')
plt.legend(loc=2)
bottom = plt.subplot2grid((4, 4), (3, 0), rowspan=1, colspan=4)
bottom.bar(msftV.index, msftV)
plt.title('Microsoft Daily Trading Volume')
plt.gcf().set_size_inches(12, 8)
plt.subplots_adjust(hspace=0.75)

stock_ex3

이번엔 CandleStick 형태의 차트를 뽑아와보자.

# 이제 2014년 정보만 뽑아와보자
subset = all_data.loc['MSFT'].loc['2014-12':'2014-12']
# 인덱스를 초기화 시킨다. (0, 1, 2, 3... 이 인덱스가 된다.)
subset = subset.reset_index()
import matplotlib.dates as mdates

# 시간을 숫자(Integer)형태로 바꿨다.
subset['date_num'] = subset['Date'].apply(lambda date: mdates.date2num(date.to_pydatetime()))

# 그리고 이것을 Tuple에 넣어 두었다.
subset_as_tuples = [tuple(x) for x in subset[['date_num', 'Open', 'High', 'Low', 'Close']].values]

from matplotlib.dates import DateFormatter
week_formatter = DateFormatter('%b %d') # ex) Jan 15

# 월요일만 가져온다!!
from matplotlib.dates import (WeekdayLocator, MONDAY)
mondays = WeekdayLocator(MONDAY)</pre>
<pre>
# X축에서 월요일 만 뽑아온다음, 이를 Week 형태로 바꾸었다.

plt.figure(figsize=(12, 8))
fig, ax = plt.subplots()
ax.xaxis.set_major_locator(mondays)
ax.xaxis.set_major_formatter(week_formatter)
from matplotlib.finance import candlestick_ohlc
candlestick_ohlc(ax, subset_as_tuples, width=0.6, colorup='g', colordown='r')

stock_ex4