블로그 포스팅을 안 하다가 이제 다시 꾸준히 작성해보려 한다.
개발을 안 했던거는 아니고, 지난 1년동안 시스템 트레이딩을 개발했지만
늘 열심히 했던 것도 아니고, 결과도 불투명했고, 방향도 못 잡아서 공개하기 부끄러운 부분이 있었다.
그래도, 최근 열심히 한 덕에 방향을 잡았고 결과까지 노력하는 과정도 이제 남겨보려 한다.
아직 아는 것보다 모르는 게 더 많고, 실수도 많지만 어차피 블로그 아무도 안 보는데 일기처럼 남겨볼까 한다.
오랜만에 진심으로 열심히 하고 좋아하는게 생겨서 좋다.
현재 단계는 Data ETL, Trading Bot, Back Testing 프로세스를 구축했고
실시간 데이터를 확보하며 Quant 전략을 보강하는 단계이다.
오늘은 그 보강 단계 중 Parameter Optimization 과정을 정리해보려 한다.
시스템에 사용되는 인자는 많지만, 전략에 영향을 주는 독립변수(X) 4개를 임의로 추출했다.
- base_market_cap : 시가총액
- buy_rsi : RSI 기반 매수 시점
- sell_rsi : RSI 기반 매도 시점
- threshold: 손절가 (0.97이면 3% 이상 시 손절)
이를 기반으로 확인할 수 있는 종속변수(Y)는 아래와 같다. (거래일 31일 기준 데이터)
- total_trades: 총 거래 횟수
- wining_trades: 수익률 0% 이상인 거래 횟수
- win_rate : winnning_trades / total_trades
- total_profit: *수익률
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
file_path = 'parameter-optimization.csv' # 파일 경로를 지정하세요
df = pd.read_csv(file_path)
1) Pairplot 활용 변수 간 관계 시각화
# Pairplot을 활용한 관계 분석
sns.pairplot(df, hue="base_market_cap", vars=["buy_rsi", "sell_rsi", "threshold", "total_profit"])
plt.show()
까만색이 base_market_cap이 3,000억인 경우이다.
전반적으로 base_market_cap이 3,000억 이상인 경우를 필터링 할 때 수익률이 좋아 보인다.
2) Correlation heatmap 시각화
# 상관관계 Heatmap
plt.figure(figsize=(10, 6))
sns.heatmap(df.corr(), annot=True, cmap="coolwarm", fmt=".2f")
plt.title("Correlation Heatmap")
plt.show()
수익률에 집중을 하면
1) base_market_cap : 0.56 (높을 수록 수익률 ▲)
2) buy_rsi & thres_hold : 0.33 & 0.35 (낮을 수록 수익률 ▲)
독립 변수는 아니지만 total_trades가 낮을 수록 total_profit이 높다.
- 1) 같은 종목 샀팔 샀팔 해서 좋을 게 없다
- 2) 매매 횟수가 많을수록 sleepage 만 증가하지, 줄이는게 좋다 (트레이더 친구 의견이랑은 반대이다)
3) Boxplot 활용
# Boxplot을 활용한 base_market_cap 별 total_profit 분포
plt.figure(figsize=(10, 6))
sns.boxplot(x="base_market_cap", y="total_profit", data=df)
plt.title("Total Profit Distribution by Base Market Cap")
plt.show()
- 확실히 1)의 결과와 같이 거래대금이 높을 수록 좋다.
- buy_rsi의 경우도 낮은게 좋아 보인다.
- 그런데 최대치는 비슷해서, 큰 의미는 없는 듯 하다.
- sell_rsi의 경우 79, 80이 82보다 좋은 것은 명확해 보인다.
- 79, 80의 차이가 경미해서 조금 더 파악이 필요하다.
plt.figure(figsize=(10, 6))
sns.boxplot(x="threshold", y="total_profit", data=df)
plt.title("Total Profit Distribution by threshold")
plt.show()
# Scatterplot을 활용한 threshold vs total_profit
plt.figure(figsize=(10, 6))
sns.scatterplot(x="threshold", y="total_profit", hue="base_market_cap", data=df, palette="viridis")
plt.title("Threshold vs Total Profit")
plt.show()
- 손절 %는 높은게 좋다. (0.97이면 3%, 0.985면 1.5%)
실제 결과는?
# total_profit을 기준으로 내림차순 정렬 (큰 값이 위로)
df_sorted = df.sort_values(by="total_profit", ascending=False)
df_sorted.iloc[:10
base_market_cap | buy_rsi | sell_rsi | threshold | total_trades | wining_trades | win_rate | total_profit | |
1 | 300000000000 | 75 | 80 | 0.970 | 79 | 31 | 39.24 | 41.98 |
2 | 300000000000 | 78 | 79 | 0.970 | 81 | 32 | 39.51 | 41.41 |
3 | 300000000000 | 75 | 79 | 0.970 | 79 | 31 | 39.24 | 41.35 |
4 | 300000000000 | 72 | 82 | 0.970 | 77 | 31 | 40.26 | 40.92 |
5 | 300000000000 | 72 | 79 | 0.970 | 75 | 28 | 37.33 | 40.06 |
6 | 300000000000 | 78 | 80 | 0.970 | 80 | 31 | 38.75 | 39.78 |
7 | 300000000000 | 72 | 80 | 0.970 | 75 | 30 | 40.00 | 39.50 |
8 | 250000000000 | 75 | 80 | 0.970 | 82 | 32 | 39.02 | 39.37 |
9 | 300000000000 | 72 | 79 | 0.975 | 73 | 28 | 38.36 | 38.48 |
10 | 300000000000 | 78 | 82 | 0.970 | 80 | 31 | 38.75 | 38.28 |
상위 10위 내 거래대금은 모두 3,000억
상위 5위 내 손절가 비율은 모두 3%
buy_rsi & sell_rsi는 더 자세히 보면
- 1위는 75/80이고, 분석 수치를 극대화한 것(72, 82)은 4위이다.
- 2, 3위는 굳이 선택할 필요는 없을 듯 하다.
Before | After | |
base_market_cap | 250000000000 | 300000000000 |
buy_rsi | 75 | 75 |
sell_rsi | 80 | 80 |
thresghold | 0.97 | 0.97 |
처음에 직관으로 설정했던 수치들이 대부분 맞았고,
거래대금도 scoring 로직에 별도로 추가할 필요 없이, filtering만 처리해도 될 듯 하다.
TODO
- paramter optimization 작업 시 소요 시간이 매우 오래 걸린다.
- 연산량이 많은 작업이고, for문을 4중 5중 중첩(Grid search 적용)해서 여러 개 돌리니 시간이 오래 걸린다.
- 리팩토링을 통해 연산 효율화 작업은 진행했기에, 더 근본적인 속도 개선 작업이 필요하다 (비동기, 멀티 프로세스 등)
'Quant' 카테고리의 다른 글
BackTesting : 디버깅 🐛 (2) | 2025.02.05 |
---|---|
BackTesting : 매매 내역 분석 (0) | 2025.02.03 |
Quant : Backtesting - 재무제표 기반 (0) | 2022.04.27 |
Quant : Visualization - Seaborn (0) | 2022.04.13 |
Quant : Visualization - Matplotlib (0) | 2022.04.13 |