일자별 백테스팅 매매 내역 분석을 하는데
시각화 된 차트와 수치화 된 dataframe이 싱크가 안 맞는 문제를 발견했다.
문제를 파악해보니 Backtrader 라이브러리를 백테스팅에 사용 중인데,
하루에 여러 ticker를 다루는 매매를 적용하고자 단일 dataframe에 혼재한 데이터를 넣어서 사용한 게 문제였다.
이렇게 하니, 예외 처리를 위해 코드와 변수는 복잡해지고 의도치 않은 결과가 나오게 된다.
왜 처음에 이렇게 설게를 했을까 생각해보니,
커스터마이징 했던 백테스팅 로직에서 backtrader 라이브러리로 넘어가는 과정에서
변경점을 최소화 하고 싶어서 했던 선택인 듯 하다.
복잡도를 내리고, 코드를 줄이는 방법으로 다시 개선한다.
그 과정에서 validation 로직도 강화하여 정확한 input / output 을 체크한다.
1. Validation 로직 추가
def validate_five_minute_continuous(df: pd.DataFrame):
if df.shape[0] <= 1:
return # 비교할 게 없음
timestamps = df["timestamp"].reset_index(drop=True)
time_deltas = timestamps.diff().dropna()
for i in range(len(time_deltas)):
prev_time = timestamps[i]
curr_time = timestamps[i + 1]
# 마지막 구간이고 15:20 → 15:30일 때만 10분 허용
if (
prev_time.time() == pd.to_datetime("15:20:00").time()
and curr_time.time() == pd.to_datetime("15:30:00").time()
and i == len(time_deltas) - 1
):
if time_deltas.iloc[i] != pd.Timedelta(minutes=10):
raise ValueError(
f"마지막 구간이 10분이 아님: {prev_time} -> {curr_time}"
)
else:
if time_deltas.iloc[i] != pd.Timedelta(minutes=5):
raise ValueError(f"5분봉 연속성이 깨짐: {prev_time} -> {curr_time}")
2. Backtrader 코드 정리
변경되는 코드를 관리하기 위해 불필요한 변수를 제거하고, 이를 다루는 함수들도 삭제해준다.
3. Backtesting 로직 정리
# Run backtesting
top_stock_list = [
df.sort_values(by='timestamp').reset_index(drop=True)
for df in top_stock_list
]
top_stock_list = sorted(top_stock_list, key=lambda df: df.iloc[0]['timestamp'])
if top_stock_list:
top_stock_df = pd.concat(top_stock_list, ignore_index=True)
top_stock_df = top_stock_df.sort_values(by='timestamp').reset_index(drop=True)
for df in top_stock_list:
validate_time_df(df)
validate_five_minute_continuous(df)
result = run_backtesting(df, current_cash, prohibited_stock)[0]
trade_df = pd.concat([trade_df, result.trade_df])
current_cash = result.cash
prohibited_stock = result.prohibited_stock
if not trade_df.empty:
trade_df['datetime'] = pd.to_datetime(trade_df['datetime'])
top_stock_df['timestamp'] = pd.to_datetime(top_stock_df['timestamp'])
top_stock_df = top_stock_df.rename(columns={'timestamp': 'datetime'})
trade_df = pd.merge(
trade_df,
top_stock_df,
how='left',
on=['datetime', 'code'],
)
total_trades = pd.concat([total_trades, trade_df])
return_pct = (result.cash - day_cash) / day_cash * 100
print(f"💸 잔고 : {day_cash} -> {result.cash}")
print(f"📊 수익률 : {return_pct:.2f}%")
day_cash = current_cash
거래일: 41
거래 수: 50.0
순수익: 48,796,340 원
📊 수익률: 48.80%
✅ 승률: 46.00%
'Quant' 카테고리의 다른 글
백테스팅 결과 분석 (1) | 2025.07.16 |
---|---|
재진입 필터링 및 데이터 분석 (2) | 2025.07.10 |
연산 최적화 (3) | 2025.07.07 |
보조지표 추가, ADX (0) | 2025.07.06 |
전략 최적화 (3) (1) | 2025.07.05 |