본문 바로가기

Python/Investment

Python으로 저항선/지지선 구현 및 차트 생성

반응형

Python으로 저항선/지지선 구현 및 차트 생성

오랜만에 주식과 관련된 Python 코드 포스트를 작성하는 것 같다.
이번 포스트에서는 주식의 차트 분석에서 가장 많이 사용되어 지는 저항선/지지선을 Python 코드로 구현해보는 시간을 가져보려한다.
저항선/지지선은 개인적으로 눈으로 직접 보고 차트에 적용하는 것을 선호하긴 하지만 Python 코드로 간단하게 참고용으로 생성할 수 있다면 좋을 것 같아서 시도해보았다.

목차

저항선/지지선이란?

우선 간단하게 저항선과 지지선이 무엇인지 알아보자.


✔ 저항선

  • 주식 가격이 상승하던 추세에서 상승을 멈추고 하락하기 시작하는 가격 수준
  • 특정 가격에서 매도 압력이 매수 압력을 초과하는 지점

✔ 지지선

  • 주식 가격이 하락하던 추세에서 하락을 멈추고 상승하기 시작하는 가격 수준
  • 특정 가격에서 매수 압력이 매도 압력을 초과하는 지점

자세한 내용은 예전에 작성해준 포스트로 확인 부탁드린다.


그렇다면, 저항선과 지지선을 수학적?으로 구현하려면 어떻게 해야할까?
필자의 경우 국소 최대/최소값을 활용해보고자 했다.
국소 최대/최소값이 무엇인지 간단하게 정리만 하고 넘어가자.

국소 최대/최소값

✔ 국소 최대값

  • 어떤 함수가 주어진 점에서의 값이 그 점의 주변에서 다른 모든 점의 함수값보다 크거나 같을 때, 그 점에서의 함수값을 국소 최대값이라고 한다.
  • 주변 점들에 비해 가장 큰 값

✔ 국소 최소값

  • 어떤 함수가 주어진 점에서의 값이 그 점의 주변에서 다른 모든 점의 함수값보다 작거나 같을 때, 그 점에서의 함수값을 국소 최소값이라고 한다.
  • 주변 점들에 비해 가장 작은 값

국소 최대/최소값은 함수가 특정 구간에서 가지는 최대값과 최소값을 의미하며, 주로 수학과 공학에서 함수의 특성을 분석할 때 사용된다.

국소 최대/최소값은 scipy를 통해 계산이 가능하다.
scipy의 find_peaks를 통해 국소 최대/최소값을 구해보자.

국소 최대/최소값 계산

국소 최대/최소값을 계산하기 전에 우선 데이터를 불러오자.
종가 데이터가 필요한데, 이는 pykrx 모듈을 통해 엑셀로 우선 저장해두고서 진행했다.
샘플은 삼성전자의 최근 3년 데이터로 진행하겠다.

삼성전자의 최근 3년 데이터


import pandas as pd
import numpy as np
from scipy.signal import find_peaks
import matplotlib.pyplot as plt
df = pd.read_excel("삼성전자_최근_3년.xlsx")

데이터프레임에서 우선 종가 데이터만 추출한다.

# 종가 데이터 추출
prices = df['종가'].values

국소 최대값, 최소값을 계산하기 위한 scipy의 find_peak에 대해 간단하게 소개만 하고 다음으로 넘어가겠다.

# 기본 사용법
peaks, properties = find_peaks(
       x, 
       height=None, 
       threshold=None, 
       distance=None, 
       prominence=None, 
       width=None, 
       wlen=None, 
       rel_height=0.5, 
       plateau_size=None)
  • x
    입력 데이터로, 일반적으로 1차원 배열.
  • height
    피크의 최소 높이를 지정. 이를 통해 높이가 일정 기준 이상인 피크만을 검출할 수 있다.
  • threshold
    피크 주변의 최소한의 변화량을 지정. 이 값보다 큰 변화량을 가진 데이터 포인트만을 피크로 간주.
  • distance
    인접한 피크들 사이의 최소 거리를 샘플 수로 지정. 이를 통해 너무 가까이 있는 피크를 제거할 수 있다.
  • prominence
    피크의 두드러진 정도(높이)를 지정. 피크의 중요도를 나타내며, 이 값을 통해 보다 중요한 피크를 식별할 수 있다.
  • width
    피크의 최소 너비를 지정. 피크의 지속 시간이나 폭을 기준으로 필터링할 때 사용.
  • wlen
    prominence나 width를 계산할 때 사용되는 윈도우의 길이.
  • rel_height
    폭을 계산할 때 사용되는 상대 높이.
  • plateau_size
    피크가 평탄한 영역의 최소 크기를 지정.

📌 반환값

  • peaks
    입력 데이터 x에서 찾아진 피크의 인덱스를 포함하는 배열.
  • properties
    피크들의 속성을 담은 딕셔너리로, 예를 들어 피크의 높이, prominence, 너비 등이 포함될 수 있다.


추출된 종가 데이터로 이제 국소 최대/최소값을 find_peaks를 통해 계산해보자.
여러 옵션들이 있지만 우선 간단하게 prominence(돌출 정도)만 조절해서 구해보았다.
이 값이 클 수록 데이터 내에서 더 두드러지고 중요한 값을 식별할 수 있으며, 낮은 변동성의 작은 값은 제외된다.
반대로 낮을 수록 더 많은 값이 포함되어 세밀한 데이터 변동도 포착할 수 있다.
여러 값들로 시도해보았는데 우선 5600으로 설정하여 진행해보았다.

maxv, maxv_prop = find_peaks(prices, prominence=5600) # 국소 최대값
minv, minv_prop = find_peaks(-prices, prominence=5600) # 국소 최소값

이제 maxv에는 국소 최대값의 index가, minv에는 국소 최소값의 index가 담기게 된다.



이 값들로 어떤 가격대가 계산되었는지 새로운 데이터 프레임을 생성하여 확인해보자.

저항선/지지선 구간 가격 데이터

계산된 국소 최대/최소값으로 저항/지지 구간의 가격 데이터를 새로운 데이터프레임으로 생성해보자.


# 국소 최대값 및 최소값에 해당하는 가격을 데이터프레임으로 생성
resistance_prices = prices[maxv]
support_prices = prices[minv]

lines_df = pd.DataFrame({
    'Type': ['저항구간']*len(resistance_prices) + ['지지구간']*len(support_prices),
    'Price': np.concatenate([resistance_prices, support_prices]),
    'Date': np.concatenate([df['날짜'].values[maxv], df['날짜'].values[minv]])
}).sort_values(by='Date').reset_index(drop=True)

이렇게 하면 이제 lines_df에는 저항/지지구간의 가격대와 날짜가 저장된다.


차트를 생성할 때는 해당 데이터프레임을 사용하진 않지만 참고용으로 둬도 좋을 것 같다.

저항선/지지선 차트 생성

이제 구해둔 국소 최대/최소값으로 차트를 생성해보자.
여기서는 matplotlib를 사용할 것이며, 종가선 차트 위에 저항선과 지지선을 초록색의 수평선으로, 저항구간의 가격대를 파란색 화살표와 가격, 지지구간의 가격대를 빨산색의 화살표와 가격으로 차트에 표시해보았다.

# 차트 생성
plt.figure(figsize=(14, 7))

# 종가선 차트
plt.plot(df['날짜'], df['종가'], label='Close Price', color='black')


# 저항선 및 지지선 표시 및 가격대 표시
for idx in maxv:
    plt.axhline(y=prices[idx], color='#10a37f', linestyle='--', linewidth=0.5)
    plt.scatter(df['날짜'].iloc[idx], prices[idx], color='#2825e3', marker='v', s=100)
    plt.text(df['날짜'].max(), prices[idx], f"{prices[idx]} KRW", verticalalignment='center', horizontalalignment='right', color='#2825e3')

for idx in minv:
    plt.axhline(y=prices[idx], color='#10a37f', linestyle='--', linewidth=0.5)
    plt.scatter(df['날짜'].iloc[idx], prices[idx], color='#f33b6e', marker='^', s=100)
    plt.text(df['날짜'].max(), prices[idx], f"{prices[idx]} KRW", verticalalignment='center', horizontalalignment='right', color='#f33b6e')

plt.title('Support and Resistance Lines')
plt.xlabel('Date')
plt.ylabel('Price')
plt.legend()
plt.grid(False)
plt.xticks(rotation=45)
plt.tight_layout()

plt.show()

저항선과 지지선을 명확하게 확인 하기 위해 plt.grid(False)로 설정하여 진행했으며, 해당 코드로 출력된 차트는 아래와 같다.



prominence를 낮추면 더 많은 저항/지지선을 확인할 수 있게 된다.
우선 현재의 결과만 봤을 때 개인적으로 썩 마음에 드는 결과는 아니지만 find_peaks를 좀 더 세밀하게 조절하면 좋은 결과가 나오지 않을까 싶다.
해당 결과로만 봤을 때 아래와 같이 참고는 가능할 것 같다.
과거의 지지선은 미래의 저항선으로, 과거의 저항선이 미래의 지지선이 될 수 있다는 점을 생각해봤을 때 과거와 현재를 비교해서 저항/지지선을 구한다면 이렇게 될 수 있을 것이다.



현재 삼성전자는 종가 기준 79,600원에서 가격이 다시 하락했는데(이 때 고가는 79,800원), 해당 구간은 과거에도 가격이 하락했던 구간인 것을 확인할 수 있다.

또한 최근 73,400원 구간에서 다시 반등을 시도하고 있는데, 해당 가격대는 과거에 가격이 하락했던 저항구간이었으며, 더 먼 과거에도 지지/저항구간으로 작용했던 것을 알 수 있다.


이렇게 Python으로 저항/지지선을 간단하게 구해보는 방법을 알아보았다.

물론 주식에서 저항/지지선(차트 분석 자체)은 주관적인 관점이 들어가기 때문에, 각 투자자의 경험, 분석 방법, 시장에 대한 인식에 따라 그 해석이 달라질 수 있다.

즉, 이러한 기술적 지표들을 사용할 때는 다양한 시장 상황과 경제 지표, 다른 보조지표 등과 함께 종합적으로 고려하여 신중한 투자 결정을 내리는 것이 중요하다.

그럼에도, 이렇게 코드로 간단하게 먼저 확인하고 의미있어 보이는 구간을 확인한다면, 시간을 절약할 수 있는 좋은 방법이라고 생각한다.

커피 한 잔으로
저를 응원해주세요!
반응형

loading