이 코드는 backtrader 라이브러리를 사용하여 두 개의 단순 이동 평균(SMA)을 기반으로 간단한 거래 전략을 구현하는 Python 알고리즘 소스입니다. 본 포스팅에서는 간단하게 기본적인 흐름을 볼 수 있는 오픈 소스를 활용하여 Yahoo Finance에서 삼성전자 종목 주식에 대한 과거 데이터(2020.1.1~현재)를 가져와 SMA CrossOver 전략을 적용하고 결과를 출력해 보았습니다.
퀀트 Python 백테스팅 알고리즘 소스 기본적인 구조 및 흐름
- Strategy : 거래를 진행하게되는 전략을 수립하는 부분
- Data Feeds : 거래의 기초가 되는 데이터로 OHLCV 데이터 형태를 가져 온다.
- Broker : 거래를 진행하는 주체로 자본, 수수료등 실제 거래의 요소들을 설정.
- Cerebro : 데이터와 전략을 이용해 브로커가 거래를 진행
notify_order() 메서드는 브로커가 주문을 실행할 때 호출되며, 매수 또는 매도, 포지션 크기, 주가, 현금 및 포트폴리오 가치와 같은 주문 세부 정보를 기록합니다.
Data Feeds, Strategy 및 Broker 를 조정하는 백트레이더 엔진인 'Cerebro' 인스턴스를 생성한 후, 브로커의 초기 현금과 수수료를 설정하고 'Cerebro' 인스턴스에 데이터 피드와 거래 전략을 추가합니다.
'Cerebro' 인스턴스를 실행한 후 스크립트는 포트폴리오의 시작 및 최종 값과 수익률을 백분율로 출력한 후, Cerebro 인스턴스의 plot() 메서드를 사용하여 과거 데이터와 거래 신호를 플로팅합니다.
cerebro.broker.setcash(100000)
cerebro.broker.setcommission(0.002)
cash: 초기 자본은 100,000으로 설정
매매 수수료: 0.002% 설정
퀀트 Python 알고리즘 전체 소스 코드
from datetime import datetime
import backtrader as bt
import locale
locale.setlocale(locale.LC_ALL, 'ko_KR')
# Create a subclass of Strategy to define the indicators and logic
class SmaCross(bt.Strategy):
# list of parameters which are configurable for the strategy
params = dict(
pfast=5, # period for the fast moving average
pslow=30 # period for the slow moving average
)
def __init__(self):
sma1 = bt.ind.SMA(period=self.p.pfast) # fast moving average
sma2 = bt.ind.SMA(period=self.p.pslow) # slow moving average
self.crossover = bt.ind.CrossOver(sma1, sma2) # crossover signal
self.holding = 0
def next(self):
current_stock_price = self.data.close[0]
if not self.position: # not in the market
if self.crossover > 0: # if fast crosses slow to the upside
available_stocks = self.broker.getcash() / current_stock_price
self.buy(size=1)
elif self.crossover < 0: # in the market & cross to the downside
self.close() # close long position
def notify_order(self, order):
if order.status not in [order.Completed]:
return
if order.isbuy():
action = 'Buy'
elif order.issell():
action = 'Sell'
stock_price = self.data.close[0]
cash = self.broker.getcash()
value = self.broker.getvalue()
self.holding += order.size
print('%s[%d] holding[%d] price[%d] cash[%.2f] value[%.2f]'
% (action, abs(order.size), self.holding, stock_price, cash, value))
cerebro = bt.Cerebro() # create a "Cerebro" engine instance
cerebro.broker.setcash(100000)
cerebro.broker.setcommission(0.002)
# Create a data feed
data = bt.feeds.YahooFinanceData(dataname='005930.KS',
fromdate=datetime(2020, 1, 1),
todate=datetime.now())
cerebro.adddata(data) # Add the data feed
cerebro.addstrategy(SmaCross) # Add the trading strategy
start_value = cerebro.broker.getvalue()
cerebro.run() # run it all
final_value = cerebro.broker.getvalue()
print('* start value : %s won' % locale.format_string('%d', start_value, grouping=True))
print('* final value : %s won' % locale.format_string('%d', final_value, grouping=True))
print('* earning rate : %.2f %%' % ((final_value - start_value) / start_value * 100.0))
cerebro.plot(style='candle', barup='red', bardown='blue') # and plot it with a single command
결과 출력
Buy[1] holding[1] price[66708] cash[33354.76] value[100063.18]
Sell[1] holding[0] price[66414] cash[99440.24] value[99440.24]
Buy[1] holding[1] price[65531] cash[33384.76] value[98915.98]
Sell[1] holding[0] price[63961] cash[97120.55] value[97120.55]
Buy[1] holding[1] price[59713] cash[36001.65] value[95715.44]
Sell[1] holding[0] price[60207] cash[95596.01] value[95596.01]
Buy[1] holding[1] price[60108] cash[34872.72] value[94981.31]
Sell[1] holding[0] price[58233] cash[93186.54] value[93186.54]
Buy[1] holding[1] price[54857] cash[38020.65] value[92877.67]
Sell[1] holding[0] price[58832] cash[96239.25] value[96239.25]
Buy[1] holding[1] price[60500] cash[35117.25] value[95617.25]
Sell[1] holding[0] price[61300] cash[97292.65] value[97292.65]
* start value : 100,000 won
* final value : 97,292 won
* earning rate : -2.71 %
과거 데이터와 거래 신호를 플로팅
전반적으로 이 코드는 backtrader 라이브러리를 사용하여 기술 지표를 기반으로 거래 전략을 백테스팅하기 위한 기본 프레임워크를 알 수 있습니다. 그러나 실제 거래를 위한 위험 관리 또는 기타 중요한 기능이 포함되어 있지 않으므로 광범위한 수정 및 테스트 없이는 실시간 거래에 사용할 수는 없습니다.
댓글