인프런 커뮤니티 질문&답변

작성자 없음

작성자 정보가 삭제된 글입니다.

평생 써먹는 데이터 기반 투자법 with 파이썬 퀀트 투자

수익률이 맞는지 코드 문의 드립니다.

작성

·

198

0

안녕하세요 수강을 하여 만족스러운 강의를 들었습니다.

 

개인적으로 궁금한 것을 구현했는데 소스가 맞는지 검증 부탁드려도 될까 합니다.

 

생각해 본 부분을 짜보긴 했지만 수익률이 잘못 나온 듯하여 오류를 아무리 검증해보려고해도 알 수가 없어서 문의드립니다.

 

추가로 다른 분에게도 도움이 되길 바라고, 또한 제 코드에서도 최적화 할 부분이 보일 듯하니 조언 부탁드립니다.

 

강의에서 나온 부분이 많기에 주석과 맥락등은 일부 제거 했습니다.

 

 

[조건]

종목 TQQQ, SCHD

adj_close 값

TQQQ RSI<30 : TQQQ 3% 비중 증가 , SCHD 3%비중 감소

리밸런싱 5:5 매 반기마다 실행

 

!apt-get update -qq
!pip install yfinance

import yfinance as yf
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import numpy as np
# 수정종가
def getAdjCloseData(ticker, end=None):

  return yf.download(ticker, period='12y')['Adj Close']

# RSI 데이터
def getRSIData(closeDataSet, periods=""):

  average_periods = 14  # 이평 초기에 값은 나오지 않기 때문에 더 계산하고 자름

  delta = closeDataSet.diff() # closeDataSet - closeDataSet.shift(1)  # 변화량
  if periods != "":
    delta = delta.iloc[(periods + average_periods) * -1:]

  AU = pd.DataFrame(np.where(delta>=0, delta, 0), delta.index, delta.columns)
  AD = pd.DataFrame(np.where(delta<0, delta.abs(), 0), delta.index, delta.columns)

  # SMA
  AU_MA = getSimpleMovingAverage(AU, average_periods)
  AD_MA = getSimpleMovingAverage(AD, average_periods)

  rsi = AU_MA / (AU_MA + AD_MA) * 100

  # RSI Signal
  rsiSignal = getSimpleMovingAverage(rsi, 9)

  if periods != "":
    rsi = rsi[average_periods:]
    rsiSignal = rsiSignal[average_periods:]

  return rsi, rsiSignal

# RSI 값에 따른 리밸런싱 날짜
def getRSIRebalancingDate(closeDataSet, RSI = 30):
  rsi, rsiSignal = getRSIData(closeDataSet)
  data = rsi.copy()
  data = pd.DataFrame(data)
  
  dataIndex = data[data.iloc[:, 0] <= RSI].index

  return dataIndex

def getWeightByRSI(closeDataSet):

  RSIrebalancingDate = getRSIRebalancingDate(closeDataSet, 30) # RSI가 30이하일 때 리밸런싱 날짜
  rebalancingDate = getRebalancingDate(closeDataSet, 'half') # 동일비중 리밸런싱 할 날짜

  rebal = pd.DataFrame([[1/len(closeDataSet.columns)] * len(closeDataSet.columns)] * len(rebalancingDate),
                       index=rebalancingDate,
                       columns=closeDataSet.columns)
  rebal['period'] = 1 # 기간에 따른 리밸런싱

  rsiRebal = pd.DataFrame([[1/len(closeDataSet.columns)] * len(closeDataSet.columns)] * len(RSIrebalancingDate),
                        index=RSIrebalancingDate,
                        columns=closeDataSet.columns)

  rsiRebal['period'] = 0  # RSI에 따른 리밸런싱
  weightDf = pd.concat([rebal, rsiRebal], axis=0)
  weightDf = weightDf.sort_index()

  # 리밸런싱 날짜 별
  for i in range(1, len(weightDf)):
    if weightDf.iloc[i]['period'] == 1: # 기간에 따른 리밸런싱
      weightDf.iloc[i, weightDf.columns.get_loc("TQQQ")] = weightDf.iloc[0, weightDf.columns.get_loc("TQQQ")]
      weightDf.iloc[i, weightDf.columns.get_loc("SCHD")] = weightDf.iloc[0, weightDf.columns.get_loc("SCHD")]
    else: # RSI에 따른 리밸런싱
      weightDf.iloc[i, weightDf.columns.get_loc("TQQQ")] = weightDf.iloc[i-1, weightDf.columns.get_loc("TQQQ")] * 1.03
      weightDf.iloc[i, weightDf.columns.get_loc("SCHD")] = weightDf.iloc[i-1, weightDf.columns.get_loc("SCHD")] * 0.97
      
  return weightDf

def getRSIPortfolioResult(closeDataSet):
  weight = getWeightByRSI(closeDataSet)
  weightDf = pd.DataFrame(weight)

  rebalancingDate = weightDf.index
  portfolio = pd.DataFrame() # 빈 데이터 프레임 생성

  totalAsset = 1 # 총 자산, 초기값 1
  start = rebalancingDate[0] # 리밸런싱 날짜, 초기값 첫 투자일

  for end in rebalancingDate[1:]:
      weight = weightDf.loc[start] # 당월 리밸런싱 비율
      weight = weight.drop('period')
      priceData = closeDataSet.loc[start:end] # 당월 가격 데이터
      cumReturn = getCumulativeReturn(priceData) # 당월 누적 수익률
      weightedCumReturn = weight * cumReturn # 당월 리밸런싱 비율이 반영된 누적 수
      netCumReturn = totalAsset * weightedCumReturn # 전월 투자 결과 반영 (이전 블록의 누적 수익을 포함시킴)
      start = end # start 갱신
      
      totalAsset = netCumReturn.iloc[-1].sum() # 총 자산 갱신
      portfolio = pd.concat([portfolio, netCumReturn]) # 매월 데이터 추가

  portfolio = portfolio.loc[~portfolio.index.duplicated(keep='last')] # 중복 데이터 제거
  portfolioCumulativeReturn = portfolio.sum(axis=1) # 포트폴리오 누적 수익률
  portfolioDayReturn = (portfolioCumulativeReturn / portfolioCumulativeReturn.shift(1)).fillna(1) # 포트폴리오 일간 수익률

  return portfolioDayReturn, portfolioCumulativeReturn



closeDataSet = pd.DataFrame()

TQQQ = getAdjCloseData("TQQQ") # TQQQ
SCHD = getAdjCloseData("SCHD") # SCHD
closeDataSet = pd.concat([TQQQ , SCHD ], axis=1)

closeDataSet.columns = asset
closeDataSet.dropna(inplace=True)

_, rsiCumReturn = getRSIPortfolioResult(closeDataSet)
rsiMomentumCAGR, rsiMomentumDD, rsiMomentumMDD = getEvaluation(rsiCumReturn)

답변 1

0

안녕하세요, 인프런 AI 인턴입니다.

님이 질문하신 코드에 대한 검증은 직접 실행해본 결과를 통해 이루어져야 하지만, 아쉽게도 저는 코드를 실행할 수 없습니다. 하지만 코드를 작성하면서 고려해야 할 사항을 몇 가지 말씀드릴 수 있습니다.

  1. getAdjCloseData 함수에서 ‘Adj Close’를 가져올 때, 종종 티커에 따라 올바르게 데이터가 다운로드되지 않는 경우가 발생할 수 있습니다. 따라서 데이터를 다운로드한 후 확인하는 과정이 필요합니다.

  2. getRSIData 함수에서 RSI를 계산할 때, 이동 평균 계산 방법(SMA, EMA 등)이 명확히 정의되어 있는지 확인해야 합니다. 해당 함수 내에 있는 getSimpleMovingAverage 함수의 정의와 동작을 정확히 이해하는 것이 중요합니다.

  3. 리밸런싱 로직에서는 각 리밸런싱 포인트와 비중 조절이 올바르게 계산되고 있는지 체크해야 합니다. 특히, RSI 기반 비중 조정 과정에서 예상과 다르게 작동할 수 있는 부분을 주의 깊게 보아야 합니다.

  4. 전체 포트폴리오 수익률을 계산하는 과정에서 cumReturn, weightedCumReturn, netCumReturn 등의 계산이 기대하는 바대로 이루어지고 있는지 확인이 필요합니다.

  5. 마지막으로, getEvaluation 함수가 호출되고 있지만, 이 함수의 정의가 제공되지 않았습니다. 해당 함수가 어떻게 포트폴리오의 CAGR(복리 연평균 성장률), DD(드로다운), MDD(최대 드로다운)를 계산하는지 이해하는 것이 중요합니다.

코드의 최적화와 오류 발견을 위해 디버깅을 할 때는 작은 단위의 데이터로 함수를 테스트하고, 각 단계에서의 출력 결과를 확인하는 것을 추천드립니다. 또한, 문제가 발생하는 부분에 대해서는 강의 Q&A 섹션을 활용하거나 해당 소스 코드를 게시할 수 있는 포럼이나 다른 수강생들과의 소통을 통해 도움을 받는 것도 좋은 방법입니다.

원하는 답변을 찾지 못하셨나요?
그럼 우측 하단의 ‘문의하기’ 에서 직접 문의해주세요.
최대한 빠르게 답변드리도록 하겠습니다!

참고 URL:
- 인프런 FAQ

작성자 없음

작성자 정보가 삭제된 글입니다.

질문하기