본문 바로가기
Python

[주식 분석 프로젝트] 총정리 – 한국 주식 기반 기술적 분석 (2~7편 요약)

by ramzee 2025. 4. 29.

주식 분석 프로젝트 총정리

🇰🇷 한국 주식 기반 기술적 분석 시리즈 (2~7편 요약)

이 문서는 2편부터 7편까지 다뤄온 전략 분석 흐름을 한국 주식(삼성전자, SK하이닉스 등)에 전면 적용한 통합 설명입니다.
처음 보는 사람도 쉽게 따라올 수 있도록 각 단계마다 배경 설명 → 코드 → 출력 예시 → 해석의 구조로 차근차근 정리합니다.


2편 – 이동평균선(MA) 분석

● 개념

이동평균선(Moving Average)은 일정 기간 동안의 종가 평균을 선으로 연결한 지표입니다. 주가의 일시적 등락(노이즈)을 줄이고, 전반적인 추세(상승/하락)를 부드럽게 보여주는 데 사용됩니다.

  • 단기선: MA5, MA20 (최근 5일/20일 평균)
  • 중기선: MA60
  • 장기선: MA120

● 한국 주식 실습 예시 (삼성전자)

import FinanceDataReader as fdr
import matplotlib.pyplot as plt

df = fdr.DataReader("005930", '2023-10-01')
df['MA20'] = df['Close'].rolling(window=20).mean()
df['MA60'] = df['Close'].rolling(window=60).mean()

plt.figure(figsize=(14, 5))
plt.plot(df['Close'], label='종가')
plt.plot(df['MA20'], label='MA20')
plt.plot(df['MA60'], label='MA60')
plt.title('삼성전자 이동평균선')
plt.legend()
plt.grid(True)
plt.show()

● 해석

  • MA20이 MA60을 상향 돌파: 골든크로스 (상승 신호)
  • MA20이 MA60을 하향 돌파: 데드크로스 (하락 신호)

3편 – 볼린저 밴드 & RSI

● 볼린저 밴드 개념

20일 이동평균선을 기준으로, 위아래로 2표준편차 범위를 띄운 밴드를 구성합니다. 주가가 이 밴드를 벗어날 때를 과매수/과매도 구간으로 판단할 수 있습니다.

● RSI 개념

RSI(Relative Strength Index)는 최근 14일 간의 상승폭과 하락폭을 비교하여 주가의 힘(모멘텀)을 나타내는 지표입니다.

  • RSI > 70: 과매수 (매도 고려)
  • RSI < 30: 과매도 (매수 고려)

● 실습 코드

df['MA20'] = df['Close'].rolling(window=20).mean()
df['STD20'] = df['Close'].rolling(window=20).std()
df['Upper'] = df['MA20'] + 2 * df['STD20']
df['Lower'] = df['MA20'] - 2 * df['STD20']

delta = df['Close'].diff()
gain = delta.where(delta > 0, 0)
loss = -delta.where(delta < 0, 0)
avg_gain = gain.rolling(14).mean()
avg_loss = loss.rolling(14).mean()
rs = avg_gain / avg_loss
df['RSI'] = 100 - (100 / (1 + rs))
df['RSI'].tail()
Date
2025-04-21    38.666667
2025-04-22    37.662338
2025-04-23    43.624161
2025-04-24    48.507463
2025-04-25    61.904762
Name: RSI, dtype: float64

4편 – 매매 전략 정의

● 전략 조건

  • 매수: RSI < 30 AND 종가 < 하단 밴드
  • 매도: RSI > 70 OR 종가 > 상단 밴드

● 포지션 보유 구현

df['Position'] = 0
position = 0
for i in range(1, len(df)):
    if (df['RSI'][i] < 30) and (df['Close'][i] < df['Lower'][i]) and position == 0:
        df.loc[df.index[i], 'Position'] = 1
        position = 1
    elif ((df['RSI'][i] > 70) or (df['Close'][i] > df['Upper'][i])) and position == 1:
        df.loc[df.index[i], 'Position'] = 0
        position = 0
    else:
        df.loc[df.index[i], 'Position'] = df.loc[df.index[i-1], 'Position']

5편 – 여러 종목에 전략 확장

● 목적

전략은 종목마다 성과가 다르게 나타납니다. 한국의 대표 우량주들에 같은 전략을 적용해 성과를 비교합니다.

● 대상 종목

  • 삼성전자 (005930)
  • SK하이닉스 (000660)
  • LG에너지솔루션 (373220)
  • 현대차 (005380)

6편 – 전략 조건 최적화

전략 수익률 향상을 위해 RSI 기준값(25~35), 볼린저 표준편차(1.8~2.2) 범위를 바꾸어 실험합니다.

def run_strategy(df, rsi_buy=30, rsi_sell=70, std_n=2.0):
    df = df.copy()
    df['MA20'] = df['Close'].rolling(window=20).mean()
    df['STD20'] = df['Close'].rolling(window=20).std()
    df['Upper'] = df['MA20'] + (std_n * df['STD20'])
    df['Lower'] = df['MA20'] - (std_n * df['STD20'])

    delta = df['Close'].diff()
    gain = delta.where(delta > 0, 0)
    loss = -delta.where(delta < 0, 0)
    avg_gain = gain.rolling(window=14).mean()
    avg_loss = loss.rolling(window=14).mean()

    epsilon = 1e-10
    avg_loss = avg_loss.replace(0, epsilon)

    rs = avg_gain / avg_loss
    df['RSI'] = 100 - (100 / (1 + rs))

    buy_signal = (df['RSI'] < rsi_buy) & (df['Close'] < df['Lower'])
    sell_signal = (df['RSI'] > rsi_sell) | (df['Close'] > df['Upper'])

    df['Position'] = 0
    position = 0
    for i in range(1, len(df)):
        if buy_signal.iloc[i] and position == 0:
            df.at[df.index[i], 'Position'] = 1
            position = 1
        elif sell_signal.iloc[i] and position == 1:
            df.at[df.index[i], 'Position'] = 0
            position = 0
        else:
            df.at[df.index[i], 'Position'] = df.at[df.index[i-1], 'Position']

    df['Daily Return'] = df['Close'].pct_change()
    df['Strategy Return'] = df['Daily Return'] * df['Position'].shift(1)
    df['Cumulative Return'] = (1 + df['Strategy Return']).cumprod()
    df['Cumulative BuyHold'] = (1 + df['Daily Return']).cumprod()

    return df
results = []
for rsi_buy in [25, 30, 35]:
  for std in [1.8, 2.0, 2.2]:
    ret = run_strategy(df, rsi_buy=rsi_buy, std_n=std)
    results.append({'RSI_Buy': rsi_buy, 'STD': std, 'Return': ret})

● 결과 정리

RSI_Buy  STD   Return
30       2.0   1.23
25       2.2   1.20

7편 – 전략 성과 평가

● 주요 지표

  • Final Return: 누적 수익률
  • Sharpe Ratio: 리스크 대비 수익
  • Max Drawdown: 최대 낙폭
  • Win Rate: 수익 거래 비율

● 평가 코드

df = yf.download("005930.KS", period="1y")

# 2. ✅ MultiIndex를 평탄화
if isinstance(df.columns, pd.MultiIndex):
    df.columns = df.columns.get_level_values(0)

# 3. 전략 적용
df = run_strategy(df)

# 4. 평가
returns = df['Strategy Return'].dropna()
sharpe = (returns.mean() / returns.std()) * np.sqrt(252)
max_dd = ((1 + returns).cumprod().cummax() - (1 + returns).cumprod()).max()
win_rate = (returns > 0).sum() / len(returns)

print({
    '수익률' : win_rate,
    'Sharpe': sharpe,
    'Max Drawdown': max_dd,
    'Win Rate': win_rate
})

● 예시 출력

{
'수익률': 0.23140495867768596, 
'Sharpe': 0.15301356064010455, 
'Max Drawdown': 0.46449534541370807, 
'Win Rate': 0.23140495867768596
}

✅ 결론

  • 기술적 분석 전략은 한국 주식에서도 효과적으로 활용 가능
  • 단계별로 전략 수립 → 확장 → 조건 변경 → 성과 평가 구조가 중요
  • Sharpe/MDD 같은 리스크 지표를 반드시 포함해야 실전에서 활용 가능