[퀀트] 실전 전략 코드 템플릿
관
관리자
Lv.1
02-04 22:42
·
조회 5
·
추천 0
실전 전략 코드 템플릿
바로 사용 가능한 퀀트 투자 전략 Python 코드
1. 환경 설정
1.1 필수 패키지 설치
pip install pandas numpy matplotlib
pip install FinanceDataReader pykrx yfinance
pip install ta # 기술적 지표 라이브러리
1.2 기본 임포트
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
import warnings
warnings.filterwarnings('ignore')
# 한글 폰트 설정 (차트용)
plt.rcParams['font.family'] = 'AppleGothic' # Mac
# plt.rcParams['font.family'] = 'Malgun Gothic' # Windows
plt.rcParams['axes.unicode_minus'] = False
# 데이터 수집
import FinanceDataReader as fdr
2. 데이터 수집 유틸리티
class DataCollector:
"""주식 데이터 수집 클래스"""
@staticmethod
def get_stock_data(ticker: str, start: str, end: str = None) -> pd.DataFrame:
"""
개별 종목 데이터 수집
Parameters:
-----------
ticker: 종목코드 (예: '005930' 삼성전자)
start: 시작일 (예: '2020-01-01')
end: 종료일 (기본: 오늘)
"""
if end is None:
end = datetime.now().strftime('%Y-%m-%d')
df = fdr.DataReader(ticker, start, end)
df = df.reset_index()
df.columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'change']
return df
@staticmethod
def get_kospi_list() -> pd.DataFrame:
"""KOSPI 종목 리스트"""
return fdr.StockListing('KOSPI')
@staticmethod
def get_kosdaq_list() -> pd.DataFrame:
"""KOSDAQ 종목 리스트"""
return fdr.StockListing('KOSDAQ')
@staticmethod
def get_financial_data(ticker: str) -> dict:
"""
재무 데이터 수집 (pykrx 사용)
"""
try:
from pykrx import stock
# 최근 분기 재무 데이터
today = datetime.now().strftime('%Y%m%d')
fundamental = stock.get_market_fundamental(today, today, ticker)
return fundamental.to_dict('records')[0] if len(fundamental) > 0 else {}
except Exception as e:
print(f"재무 데이터 수집 실패: {e}")
return {}
@staticmethod
def get_multiple_stocks(tickers: list, start: str, end: str = None) -> dict:
"""
여러 종목 데이터 한번에 수집
Returns:
--------
dict: {ticker: DataFrame}
"""
data = {}
for ticker in tickers:
try:
df = DataCollector.get_stock_data(ticker, start, end)
data[ticker] = df
print(f" ✓ {ticker} 수집 완료")
except Exception as e:
print(f" ✗ {ticker} 수집 실패: {e}")
return data
# 사용 예시
# collector = DataCollector()
# samsung = collector.get_stock_data('005930', '2023-01-01')
3. 전략 1: 이동평균 골든크로스
class GoldenCrossStrategy:
"""
골든크로스/데드크로스 전략
매수: 단기 이평선 > 장기 이평선 (골든크로스)
매도: 단기 이평선 < 장기 이평선 (데드크로스)
"""
def __init__(self, short_window: int = 20, long_window: int = 60):
self.short_window = short_window
self.long_window = long_window
self.name = f"GoldenCross_{short_window}_{long_window}"
def generate_signals(self, df: pd.DataFrame) -> pd.DataFrame:
"""매매 신호 생성"""
df = df.copy()
# 이동평균 계산
df['ma_short'] = df['close'].rolling(self.short_window).mean()
df['ma_long'] = df['close'].rolling(self.long_window).mean()
# 신호 생성
df['signal'] = 0
df.loc[df['ma_short'] > df['ma_long'], 'signal'] = 1 # 매수 신호
df.loc[df['ma_short'] < df['ma_long'], 'signal'] = -1 # 매도 신호
# 포지션 변화 (실제 매매 시점)
df['position'] = df['signal'].diff()
return df
def backtest(self, df: pd.DataFrame, initial_capital: float = 10000000) -> dict:
"""백테스트 실행"""
df = self.generate_signals(df)
cash = initial_capital
holdings = 0
portfolio_values = []
trades = []
for i, row in df.iterrows():
price = row['close']
position_change = row['position']
# 매수
if position_change == 2: # -1 → 1
if cash > 0:
shares = cash // price
cost = shares * price
cash -= cost
holdings += shares
trades.append({'date': row['date'], 'type': 'BUY', 'price': price, 'shares': shares})
# 매도
elif position_change == -2: # 1 → -1
if holdings > 0:
revenue = holdings * price
cash += revenue
trades.append({'date': row['date'], 'type': 'SELL', 'price': price, 'shares': holdings})
holdings = 0
# 포트폴리오 가치 기록
total_value = cash + (holdings * price)
portfolio_values.append({
'date': row['date'],
'total_value': total_value,
'cash': cash,
'holdings_value': holdings * price
})
return {
'portfolio': pd.DataFrame(portfolio_values),
'trades': pd.DataFrame(trades),
'final_value': portfolio_values[-1]['total_value'],
'return': (portfolio_values[-1]['total_value'] / initial_capital - 1) * 100
}
def plot(self, df: pd.DataFrame, result: dict):
"""결과 시각화"""
df = self.generate_signals(df)
fig, axes = plt.subplots(2, 1, figsize=(14, 10))
# 주가 + 이동평균
ax1 = axes[0]
ax1.plot(df['date'], df['close'], label='종가', alpha=0.7)
ax1.plot(df['date'], df['ma_short'], label=f'MA{self.short_window}', linestyle='--')
ax1.plot(df['date'], df['ma_long'], label=f'MA{self.long_window}', linestyle='--')
# 매수/매도 포인트
buys = df[df['position'] == 2]
sells = df[df['position'] == -2]
ax1.scatter(buys['date'], buys['close'], marker='^', color='red', s=100, label='매수')
ax1.scatter(sells['date'], sells['close'], marker='v', color='blue', s=100, label='매도')
ax1.set_title(f'{self.name} 전략')
ax1.legend()
ax1.grid(True, alpha=0.3)
# 포트폴리오 가치
ax2 = axes[1]
portfolio = result['portfolio']
ax2.plot(portfolio['date'], portfolio['total_value'], label='포트폴리오')
ax2.axhline(y=10000000, color='gray', linestyle='--', label='초기자본')
ax2.set_title(f'포트폴리오 가치 (수익률: {result["return"]:.2f}%)')
ax2.legend()
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig(f'{self.name}_result.png', dpi=150)
plt.show()
# 사용 예시
"""
# 데이터 수집
df = DataCollector.get_stock_data('005930', '2022-01-01')
# 전략 실행
strategy = GoldenCrossStrategy(short_window=20, long_window=60)
result = strategy.backtest(df)
print(f"최종 수익률: {result['return']:.2f}%")
print(f"거래 횟수: {len(result['trades'])}")
# 시각화
strategy.plot(df, result)
"""
4. 전략 2: RSI 역추세 전략
class RSIStrategy:
"""
RSI 과매도/과매수 역추세 전략
매수: RSI < 30 (과매도)
매도: RSI > 70 (과매수)
"""
def __init__(self, period: int = 14, oversold: int = 30, overbought: int = 70):
self.period = period
self.oversold = oversold
self.overbought = overbought
self.name = f"RSI_{period}_{oversold}_{overbought}"
def calculate_rsi(self, prices: pd.Series) -> pd.Series:
"""RSI 계산"""
delta = prices.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=self.period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=self.period).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi
def generate_signals(self, df: pd.DataFrame) -> pd.DataFrame:
"""매매 신호 생성"""
df = df.copy()
df['rsi'] = self.calculate_rsi(df['close'])
df['signal'] = 0
df.loc[df['rsi'] < self.oversold, 'signal'] = 1 # 과매도 → 매수
df.loc[df['rsi'] > self.overbought, 'signal'] = -1 # 과매수 → 매도
# 신호 변화 시점만 추출
df['position'] = df['signal'].diff()
return df
def backtest(self, df: pd.DataFrame, initial_capital: float = 10000000) -> dict:
"""백테스트 실행"""
df = self.generate_signals(df)
cash = initial_capital
holdings = 0
portfolio_values = []
trades = []
for i, row in df.iterrows():
price = row['close']
signal = row['signal']
position_change = row['position']
# 매수 신호 발생
if position_change > 0 and signal == 1:
if cash > 0:
shares = cash // price
cost = shares * price
cash -= cost
holdings += shares
trades.append({'date': row['date'], 'type': 'BUY', 'price': price, 'shares': shares, 'rsi': row['rsi']})
# 매도 신호 발생
elif position_change < 0 and signal == -1:
if holdings > 0:
revenue = holdings * price
cash += revenue
trades.append({'date': row['date'], 'type': 'SELL', 'price': price, 'shares': holdings, 'rsi': row['rsi']})
holdings = 0
total_value = cash + (holdings * price)
portfolio_values.append({
'date': row['date'],
'total_value': total_value,
'rsi': row['rsi']
})
return {
'portfolio': pd.DataFrame(portfolio_values),
'trades': pd.DataFrame(trades),
'final_value': portfolio_values[-1]['total_value'],
'return': (portfolio_values[-1]['total_value'] / initial_capital - 1) * 100
}
def plot(self, df: pd.DataFrame, result: dict):
"""결과 시각화"""
df = self.generate_signals(df)
fig, axes = plt.subplots(3, 1, figsize=(14, 12))
# 주가
ax1 = axes[0]
ax1.plot(df['date'], df['close'], label='종가')
trades_df = result['trades']
if len(trades_df) > 0:
buys = trades_df[trades_df['type'] == 'BUY']
sells = trades_df[trades_df['type'] == 'SELL']
ax1.scatter(buys['date'], buys['price'], marker='^', color='red', s=100, label='매수')
ax1.scatter(sells['date'], sells['price'], marker='v', color='blue', s=100, label='매도')
ax1.set_title('주가 및 매매 시점')
ax1.legend()
ax1.grid(True, alpha=0.3)
# RSI
ax2 = axes[1]
ax2.plot(df['date'], df['rsi'], label='RSI', color='purple')
ax2.axhline(y=self.oversold, color='green', linestyle='--', label=f'과매도 ({self.oversold})')
ax2.axhline(y=self.overbought, color='red', linestyle='--', label=f'과매수 ({self.overbought})')
ax2.fill_between(df['date'], self.oversold, df['rsi'].clip(upper=self.oversold), alpha=0.3, color='green')
ax2.fill_between(df['date'], self.overbought, df['rsi'].clip(lower=self.overbought), alpha=0.3, color='red')
ax2.set_ylim(0, 100)
ax2.set_title('RSI')
ax2.legend()
ax2.grid(True, alpha=0.3)
# 포트폴리오
ax3 = axes[2]
portfolio = result['portfolio']
ax3.plot(portfolio['date'], portfolio['total_value'], label='포트폴리오')
ax3.axhline(y=10000000, color='gray', linestyle='--', label='초기자본')
ax3.set_title(f'포트폴리오 가치 (수익률: {result["return"]:.2f}%)')
ax3.legend()
ax3.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig(f'{self.name}_result.png', dpi=150)
plt.show()
5. 전략 3: 볼린저 밴드 전략
class BollingerBandStrategy:
"""
볼린저 밴드 전략
매수: 가격이 하단 밴드 터치
매도: 가격이 상단 밴드 터치
"""
def __init__(self, window: int = 20, num_std: float = 2.0):
self.window = window
self.num_std = num_std
self.name = f"BollingerBand_{window}_{num_std}"
def generate_signals(self, df: pd.DataFrame) -> pd.DataFrame:
"""매매 신호 생성"""
df = df.copy()
# 볼린저 밴드 계산
df['ma'] = df['close'].rolling(self.window).mean()
df['std'] = df['close'].rolling(self.window).std()
df['upper'] = df['ma'] + (df['std'] * self.num_std)
df['lower'] = df['ma'] - (df['std'] * self.num_std)
# %B 계산 (0~1 사이 정규화)
df['pct_b'] = (df['close'] - df['lower']) / (df['upper'] - df['lower'])
# 신호 생성
df['signal'] = 0
df.loc[df['close'] <= df['lower'], 'signal'] = 1 # 하단 밴드 터치 → 매수
df.loc[df['close'] >= df['upper'], 'signal'] = -1 # 상단 밴드 터치 → 매도
df['position'] = df['signal'].diff()
return df
def backtest(self, df: pd.DataFrame, initial_capital: float = 10000000) -> dict:
"""백테스트 실행"""
df = self.generate_signals(df)
cash = initial_capital
holdings = 0
portfolio_values = []
trades = []
for i, row in df.iterrows():
price = row['close']
signal = row['signal']
position_change = row['position']
# 매수
if position_change > 0 and signal == 1:
if cash > 0:
shares = cash // price
cost = shares * price
cash -= cost
holdings += shares
trades.append({'date': row['date'], 'type': 'BUY', 'price': price, 'shares': shares})
# 매도
elif position_change < 0 and signal == -1:
if holdings > 0:
revenue = holdings * price
cash += revenue
trades.append({'date': row['date'], 'type': 'SELL', 'price': price, 'shares': holdings})
holdings = 0
total_value = cash + (holdings * price)
portfolio_values.append({
'date': row['date'],
'total_value': total_value
})
return {
'portfolio': pd.DataFrame(portfolio_values),
'trades': pd.DataFrame(trades),
'final_value': portfolio_values[-1]['total_value'],
'return': (portfolio_values[-1]['total_value'] / initial_capital - 1) * 100
}
def plot(self, df: pd.DataFrame, result: dict):
"""결과 시각화"""
df = self.generate_signals(df)
fig, axes = plt.subplots(2, 1, figsize=(14, 10))
# 주가 + 볼린저 밴드
ax1 = axes[0]
ax1.plot(df['date'], df['close'], label='종가', color='black')
ax1.plot(df['date'], df['ma'], label='MA', color='blue', linestyle='--')
ax1.fill_between(df['date'], df['lower'], df['upper'], alpha=0.2, color='blue', label='볼린저 밴드')
trades_df = result['trades']
if len(trades_df) > 0:
buys = trades_df[trades_df['type'] == 'BUY']
sells = trades_df[trades_df['type'] == 'SELL']
ax1.scatter(buys['date'], buys['price'], marker='^', color='red', s=100, label='매수')
ax1.scatter(sells['date'], sells['price'], marker='v', color='blue', s=100, label='매도')
ax1.set_title(f'{self.name} 전략')
ax1.legend()
ax1.grid(True, alpha=0.3)
# 포트폴리오
ax2 = axes[1]
portfolio = result['portfolio']
ax2.plot(portfolio['date'], portfolio['total_value'], label='포트폴리오')
ax2.axhline(y=10000000, color='gray', linestyle='--', label='초기자본')
ax2.set_title(f'포트폴리오 가치 (수익률: {result["return"]:.2f}%)')
ax2.legend()
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig(f'{self.name}_result.png', dpi=150)
plt.show()
6. 전략 4: 듀얼 모멘텀 전략
class DualMomentumStrategy:
"""
듀얼 모멘텀 전략 (Gary Antonacci)
1. 절대 모멘텀: 자산 수익률 > 무위험 수익률
2. 상대 모멘텀: 여러 자산 중 수익률 높은 것 선택
"""
def __init__(self, lookback: int = 12, risk_free_rate: float = 0.03):
"""
Parameters:
-----------
lookback: 모멘텀 계산 기간 (월)
risk_free_rate: 연간 무위험 수익률
"""
self.lookback = lookback
self.risk_free_rate = risk_free_rate
self.monthly_rf = (1 + risk_free_rate) ** (1/12) - 1
self.name = f"DualMomentum_{lookback}M"
def calculate_momentum(self, prices: pd.Series) -> float:
"""모멘텀(수익률) 계산"""
if len(prices) < self.lookback:
return np.nan
return (prices.iloc[-1] / prices.iloc[-self.lookback] - 1)
def run_monthly(self, data: dict, start_date: str, end_date: str,
initial_capital: float = 10000000) -> dict:
"""
월간 리밸런싱 백테스트
Parameters:
-----------
data: {ticker: DataFrame} 형태의 데이터
"""
# 월간 데이터로 리샘플링
monthly_data = {}
for ticker, df in data.items():
df = df.set_index('date')
monthly = df['close'].resample('M').last()
monthly_data[ticker] = monthly
# 공통 기간 추출
combined = pd.DataFrame(monthly_data)
combined = combined.dropna()
cash = initial_capital
holdings = {} # {ticker: shares}
current_position = None
portfolio_values = []
trades = []
for i in range(self.lookback, len(combined)):
date = combined.index[i]
current_prices = combined.iloc[i]
# 각 자산의 모멘텀 계산
momentums = {}
for ticker in combined.columns:
prices = combined[ticker].iloc[i-self.lookback:i+1]
mom = self.calculate_momentum(prices)
momentums[ticker] = mom
# 상대 모멘텀: 가장 높은 수익률 자산
best_ticker = max(momentums, key=momentums.get)
best_momentum = momentums[best_ticker]
# 절대 모멘텀: 무위험 수익률보다 높은가?
threshold = (1 + self.monthly_rf) ** self.lookback - 1
if best_momentum > threshold:
# 투자
target_position = best_ticker
else:
# 현금 보유
target_position = 'CASH'
# 포지션 변경
if target_position != current_position:
# 기존 포지션 청산
if current_position and current_position != 'CASH':
price = current_prices[current_position]
revenue = holdings.get(current_position, 0) * price
cash += revenue
trades.append({
'date': date,
'type': 'SELL',
'ticker': current_position,
'price': price,
'shares': holdings.get(current_position, 0)
})
holdings[current_position] = 0
# 새 포지션 진입
if target_position != 'CASH':
price = current_prices[target_position]
shares = cash // price
cost = shares * price
cash -= cost
holdings[target_position] = shares
trades.append({
'date': date,
'type': 'BUY',
'ticker': target_position,
'price': price,
'shares': shares
})
current_position = target_position
# 포트폴리오 가치 계산
holdings_value = sum(
holdings.get(t, 0) * current_prices.get(t, 0)
for t in combined.columns
)
total_value = cash + holdings_value
portfolio_values.append({
'date': date,
'total_value': total_value,
'position': current_position,
'momentums': momentums.copy()
})
return {
'portfolio': pd.DataFrame(portfolio_values),
'trades': pd.DataFrame(trades),
'final_value': portfolio_values[-1]['total_value'],
'return': (portfolio_values[-1]['total_value'] / initial_capital - 1) * 100
}
# 사용 예시
"""
# 데이터 수집 (KOSPI, KOSDAQ, 채권 대용으로 KODEX 국고채)
tickers = {
'KOSPI': 'KS11', # KOSPI 지수
'KOSDAQ': 'KQ11', # KOSDAQ 지수
'BOND': '148070', # KODEX 국고채 (또는 다른 채권 ETF)
}
data = {}
for name, ticker in tickers.items():
data[name] = DataCollector.get_stock_data(ticker, '2015-01-01')
# 전략 실행
strategy = DualMomentumStrategy(lookback=12)
result = strategy.run_monthly(data, '2016-01-01', '2024-12-31')
print(f"수익률: {result['return']:.2f}%")
"""
7. 전략 5: 가치+모멘텀 팩터 전략
class ValueMomentumStrategy:
"""
가치 + 모멘텀 복합 팩터 전략
1. 가치: PER, PBR 기준 저평가 종목
2. 모멘텀: 최근 수익률 상위 종목
3. 두 팩터 복합 점수로 종목 선정
"""
def __init__(self, n_stocks: int = 20, value_weight: float = 0.5):
self.n_stocks = n_stocks
self.value_weight = value_weight
self.momentum_weight = 1 - value_weight
self.name = f"ValueMomentum_Top{n_stocks}"
def calculate_scores(self, stock_data: pd.DataFrame) -> pd.DataFrame:
"""
팩터 점수 계산
Parameters:
-----------
stock_data: 종목별 데이터 (columns: ticker, per, pbr, return_12m, ...)
"""
df = stock_data.copy()
# 가치 점수 (낮을수록 좋음 → 역순위)
df['per_rank'] = df['per'].rank(ascending=True, pct=True)
df['pbr_rank'] = df['pbr'].rank(ascending=True, pct=True)
df['value_score'] = (df['per_rank'] + df['pbr_rank']) / 2
# 모멘텀 점수 (높을수록 좋음)
df['momentum_score'] = df['return_12m'].rank(ascending=False, pct=True)
# 복합 점수
df['total_score'] = (
df['value_score'] * self.value_weight +
df['momentum_score'] * self.momentum_weight
)
return df
def select_stocks(self, stock_data: pd.DataFrame) -> list:
"""상위 N개 종목 선정"""
df = self.calculate_scores(stock_data)
# 필터링 (기본 조건)
df = df[df['per'] > 0] # 적자 기업 제외
df = df[df['pbr'] > 0] # 자본잠식 제외
# 상위 N개 선정
top_stocks = df.nlargest(self.n_stocks, 'total_score')
return top_stocks['ticker'].tolist()
# 종목 스크리닝 예시 코드
def screen_stocks():
"""월간 종목 스크리닝"""
from pykrx import stock
import time
today = datetime.now().strftime('%Y%m%d')
# KOSPI 종목 리스트
tickers = stock.get_market_ticker_list(today, market="KOSPI")
results = []
for ticker in tickers[:100]: # 테스트용 100개
try:
# 기본 정보
fundamental = stock.get_market_fundamental(today, today, ticker)
if fundamental.empty:
continue
per = fundamental['PER'].iloc[0]
pbr = fundamental['PBR'].iloc[0]
# 12개월 수익률
ohlcv = stock.get_market_ohlcv(
(datetime.now() - timedelta(days=365)).strftime('%Y%m%d'),
today,
ticker
)
if len(ohlcv) < 200:
continue
return_12m = (ohlcv['종가'].iloc[-1] / ohlcv['종가'].iloc[0] - 1) * 100
results.append({
'ticker': ticker,
'per': per,
'pbr': pbr,
'return_12m': return_12m
})
time.sleep(0.1) # API 부하 방지
except Exception as e:
continue
return pd.DataFrame(results)
8. 성과 분석 유틸리티
class PerformanceAnalyzer:
"""백테스트 성과 분석"""
@staticmethod
def calculate_metrics(portfolio: pd.DataFrame, risk_free_rate: float = 0.03) -> dict:
"""종합 성과 지표 계산"""
returns = portfolio['total_value'].pct_change().dropna()
# 기본 수익률
total_return = (portfolio['total_value'].iloc[-1] / portfolio['total_value'].iloc[0]) - 1
n_days = len(portfolio)
n_years = n_days / 252
cagr = (1 + total_return) ** (1 / n_years) - 1
# 리스크 지표
volatility = returns.std() * np.sqrt(252)
downside_returns = returns[returns < 0]
downside_vol = downside_returns.std() * np.sqrt(252) if len(downside_returns) > 0 else 0
# 위험조정 수익률
sharpe = (cagr - risk_free_rate) / volatility if volatility > 0 else 0
sortino = (cagr - risk_free_rate) / downside_vol if downside_vol > 0 else 0
# MDD
cummax = portfolio['total_value'].cummax()
drawdown = (portfolio['total_value'] - cummax) / cummax
mdd = drawdown.min()
calmar = cagr / abs(mdd) if mdd != 0 else 0
# 승률 (일간 기준)
winning_days = (returns > 0).sum()
total_days = len(returns)
win_rate = winning_days / total_days if total_days > 0 else 0
return {
'총 수익률': f'{total_return:.2%}',
'CAGR (연평균)': f'{cagr:.2%}',
'변동성': f'{volatility:.2%}',
'샤프 비율': f'{sharpe:.2f}',
'소르티노 비율': f'{sortino:.2f}',
'최대 낙폭 (MDD)': f'{mdd:.2%}',
'칼마 비율': f'{calmar:.2f}',
'승률 (일간)': f'{win_rate:.2%}',
}
@staticmethod
def compare_strategies(results: dict) -> pd.DataFrame:
"""여러 전략 성과 비교"""
comparison = []
for name, result in results.items():
metrics = PerformanceAnalyzer.calculate_metrics(result['portfolio'])
metrics['전략'] = name
comparison.append(metrics)
return pd.DataFrame(comparison).set_index('전략')
@staticmethod
def plot_comparison(results: dict):
"""전략 비교 시각화"""
fig, ax = plt.subplots(figsize=(14, 6))
for name, result in results.items():
portfolio = result['portfolio']
normalized = portfolio['total_value'] / portfolio['total_value'].iloc[0] * 100
ax.plot(portfolio['date'], normalized, label=name)
ax.axhline(y=100, color='gray', linestyle='--', alpha=0.5)
ax.set_title('전략별 수익률 비교 (초기값 = 100)')
ax.set_ylabel('누적 수익률 (%)')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('strategy_comparison.png', dpi=150)
plt.show()
# 사용 예시
"""
# 여러 전략 실행
results = {
'골든크로스': golden_cross_result,
'RSI': rsi_result,
'볼린저밴드': bollinger_result,
}
# 비교 분석
comparison = PerformanceAnalyzer.compare_strategies(results)
print(comparison)
# 시각화
PerformanceAnalyzer.plot_comparison(results)
"""
9. 실행 예시 (통합)
def main():
"""메인 실행 함수"""
# 1. 데이터 수집
print("📊 데이터 수집 중...")
df = DataCollector.get_stock_data('005930', '2022-01-01') # 삼성전자
print(f" 데이터 수: {len(df)}개")
# 2. 전략 실행
strategies = {
'골든크로스': GoldenCrossStrategy(20, 60),
'RSI': RSIStrategy(14, 30, 70),
'볼린저밴드': BollingerBandStrategy(20, 2.0),
}
results = {}
for name, strategy in strategies.items():
print(f"\n🔄 {name} 전략 실행 중...")
result = strategy.backtest(df)
results[name] = result
print(f" 수익률: {result['return']:.2f}%")
print(f" 거래 횟수: {len(result['trades'])}")
# 3. 비교 분석
print("\n📈 성과 비교")
comparison = PerformanceAnalyzer.compare_strategies(results)
print(comparison)
# 4. 시각화
PerformanceAnalyzer.plot_comparison(results)
if __name__ == "__main__":
main()
10. 주의사항
⚠️ 백테스트 ≠ 실전
- 거래 비용: 수수료, 세금, 슬리피지 반드시 반영
- 유동성: 실제 체결 가능 여부 확인
- 과최적화: Out-of-sample 테스트 필수
- 시장 변화: 과거 성과가 미래를 보장하지 않음
✅ 실전 적용 전 체크리스트
- [ ] 최소 5년 이상 백테스트
- [ ] 여러 시장 환경에서 검증 (상승장, 하락장, 횡보장)
- [ ] 거래 비용 반영 후에도 수익 발생
- [ ] MDD가 감당 가능한 수준
- [ ] 페이퍼 트레이딩으로 검증
#퀀트전략 #Python #백테스팅 #알고리즘트레이딩 #코드템플릿
💬 0
로그인 후 댓글 작성
첫 댓글을 남겨보세요!
실시간 채팅
7개 메시지
관
관리자
09:57
/검색 투자
관
관리자
09:57
/검색 투자
관
관리자
09:57
/검색 투자
관
관리자
09:57
/검색 투자
관
관리자
09:58
/검색 미국
관
관리자
09:59
/검색 미국
관
관리자
09:59
/검색 테스트