금융공학/논문구현

논문 리뷰 및 구현 - Testing the Significance of Calendar Effects(2003)

냥냥펀치데스 2022. 1. 3. 17:19
728x90

논문

글쓴이

더보기

Peter Reinhard Hansen

University of North Carolina (UNC) at Chapel Hill - Department of Economics; Copenhagen Business School, Finance; Aarhus University - CREATES

Asger Lunde

CREATES; Aarhus University - School of Business and Social Sciences

James M. Nason

North Carolina State University - Department of Economics

소개

Day trade 를 직업으로 하는 사람들에게는 holiday 이전이 마지막 거래일수 있음으로 나타나는 특이현상에 대한 분석

핵심이론

  • PublicHoliday 2일전에 buy
  • 당일 sell

논문구현

python 으로 코드구현을 해보았다

사용한 라이브러리 : ccxt, backtrader, empyrical, pandas, numpy, matplotlib

사용한 환경 : colab

환경설정

import datetime
from datetime import datetime as dt
import pandas as pd
from pandas.tseries.holiday import USFederalHolidayCalendar as calendar
import numpy as np
import ccxt
import backtrader as bt
import empyrical as ep
import matplotlib.pyplot as plt
from pylab import rcParams
%matplotlib inline
rcParams['figure.figsize'] = 16,9
rcParams['figure.facecolor'] = '#eeeeee'
plt.title('dummy')
plt.plot([1,3,2,4])
plt.close()

Data 준비

def get_data(place,coin) :
    start_date = int(datetime.datetime(2020, 1, 1, 0, 0).timestamp() * 1000)

    if place == 'binance' :
      binance = ccxt.binance()
      ohlcvs = binance.fetch_ohlcv(coin, timeframe='1d', since=start_date, limit = 365)
    if place == 'okex' :
      okex = ccxt.okex()
      ohlcvs = okex.fetch_ohlcv(coin, timeframe='1d', since=start_date , limit = 365)
    if place == 'upbit' :
      upbit = ccxt.upbit()
      ohlcvs = upbit.fetch_ohlcv(coin, timeframe='1d', since=start_date , limit = 365)
    if place == 'ftx' :
      ftx = ccxt.ftx()
      ohlcvs = ftx.fetch_ohlcv(coin, timeframe='1d', since=start_date , limit = 365)
    if place == 'huobi' :
      huobi = ccxt.huobi()
      ohlcvs = huobi.fetch_ohlcv(coin, timeframe='1d', since=start_date , limit = 365)
    if place == 'bitmart' :
      bitmart = ccxt.bitmart()
      ohlcvs = bitmart.fetch_ohlcv(coin, timeframe='1d', since=start_date , limit = 365)
    if place == 'gateio' :
      gateio = ccxt.gateio()
      ohlcvs = gateio.fetch_ohlcv(coin, timeframe='1d', since=start_date , limit = 365)
    if place == 'bitrue' :
      bitrue = ccxt.bitrue()
      ohlcvs = bitrue.fetch_ohlcv(coin, timeframe='1d', since=start_date , limit = 365)
    if place == 'bittrex' :
      bittrex = ccxt.bittrex()
      ohlcvs = bittrex.fetch_ohlcv(coin, timeframe='1d', since=start_date , limit = 365)
    for idx, ohlcv in enumerate(ohlcvs):
        ohlcvs[idx] = [dt.fromtimestamp(ohlcv[0]/1000).strftime('%Y-%m-%d %H:%M:%S'), ohlcv[1], ohlcv[2], ohlcv[3], ohlcv[4],ohlcv[5]]

    df = pd.DataFrame(ohlcvs)
    df.columns = ['Time', 'Open','High','Low','Close','Volume']
    df['Time'] = pd.to_datetime(df['Time'], format='%Y-%m-%d %H:%M:%S', errors='raise')
    df.set_index('Time',inplace=True)

    return df

휴일설정

2틀전 거래를 할것임으로 2일 뒤로 shift

cal = calendar()
holidays = cal.holidays(start=binance_btc.index.min(), end=binance_btc.index.max())
binance_btc['Holiday'] = binance_btc.index.isin(holidays)
binance_btc['Holiday'] = binance_btc['Holiday'].shift(-2)
binance_btc.fillna(False)

데이터 설정

class pandasDataFeed(bt.feeds.PandasData):
    lines = ('Holiday',)
    params = (
        ('datetime', None),
        ('high', 'High'),
        ('low', 'Low'),
        ('open', 'Open'),
        ('close', 'Close'),
        ('volume', None),
        ('Holiday', 'Holiday')
    )

전략설정

class Test_Strategy(bt.Strategy):

    params = dict(
        status = 0,
    )

    def __init__(self):
        self.status = self.p.status

    def next(self):

        if self.status >= 2 :
            self.status -= 1

        if not self.position:

            # Not yet ... we MIGHT BUY if ...
            if self.data.Holiday :
                # Keep track of the created order to avoid a 2nd order
                self.order = self.buy()

                self.status = 3

        else:

            if self.status == 1:
                # Keep track of the created order to avoid a 2nd order
                self.order = self.sell()
                self.stauts = 0

코드실행

df=pandasDataFeed(dataname=binance_btc)

cerebro = bt.Cerebro()
cerebro.adddata(df)
cerebro.addstrategy(Test_Strategy)
cerebro.addanalyzer(bt.analyzers.PyFolio)
cerebro.broker.setcash(1000000)
cerebro.addsizer(bt.sizers.FixedSize, stake=5)
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
# Run over everything
results = cerebro.run()  # [15]
strat = results[0]  # [16]

# Print out the final result
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())

Analyze

strat = results[0]
pyfoliozer = strat.analyzers.getbyname('pyfolio')
returns, positions, transactions, gross_lev = pyfoliozer.get_pf_items()

SIMPLE_STAT_FUNCS = [
    ep.annual_return,
    ep.cum_returns_final,
    ep.annual_volatility,
    ep.sharpe_ratio,
    ep.calmar_ratio,
    ep.stability_of_timeseries,
    ep.max_drawdown,
    ep.omega_ratio,
    ep.sortino_ratio,
    ep.tail_ratio,
]

STAT_FUNC_NAMES = {
    'annual_return': 'Annual return',
    'cum_returns_final': 'Cumulative returns',
    'annual_volatility': 'Annual volatility',
    'sharpe_ratio': 'Sharpe ratio',
    'calmar_ratio': 'Calmar ratio',
    'stability_of_timeseries': 'Stability',
    'max_drawdown': 'Max drawdown',
    'omega_ratio': 'Omega ratio',
    'sortino_ratio': 'Sortino ratio',
    'skew': 'Skew',
    'kurtosis': 'Kurtosis',
    'tail_ratio': 'Tail ratio',
    'common_sense_ratio': 'Common sense ratio',
    'value_at_risk': 'Daily value at risk',
    'alpha': 'Alpha',
    'beta': 'Beta',
}

stats = pd.Series()
for i,stat_func in enumerate(SIMPLE_STAT_FUNCS):
    stats[STAT_FUNC_NAMES[stat_func.__name__]] = stat_func(returns)
    stats.iloc[i] = str(np.round(stats.iloc[i]  * 100,3)) + '%'

perf_stats = pd.DataFrame(stats, columns=['Backtest'])

Result