본문 바로가기
주식분석/Quant 분석(프로그래밍)

지지선과 저항선

by hustler78 2023. 3. 5.
728x90
반응형

주식차트에서 지지선과 저항선이라는 용어가 있습니다. 차트로 보면 바로 아실 텐데요, 굳이 풀어서 설명하자면 아래와 같습니다.

 

지지선
Support Level
○ 주식이 하락하다가 너무 하락이 됐다고 느껴졌는지, 어느 가격대에서 갑자기 매수물량이 대거 등장하면서 더 이상 하락하는 경향을 막아주는 가격대 
저항선
Resistance Level
○ 주식이 상승하다 너무 상승이 크다고 생각됐는지, 어느 가격대에서 갑자기 매도물량이 쏟아져 나오며 더 이상 상승하는 경향을 막는 가격대

 

 

예를 들어 제가 가지고 있는 국동(005320) 주식을 보시죠. 차트는 아래와 같습니다.

 

2023.3.3 에 조회한 국동 일봉차트

 

혹시 지지선이 보이시나요? 바로 아래와 같습니다.

 

하락할만 하면 다시 매수세가 쳐들어와 가격이 반전되고, 재차 하락하다 그 레벨까지 주가가 떨어지면 다시 주가가 상승을 합니다. 그 주가 레벨을 "지지" 해주는 것이죠. 이제 국동(005320)의 차트 범위를 약 2년으로 확장하여 보면 아래와 같습니다. 

 

넓은 범위를 보니 저항이 되는 부분도 살짝 보이네요. 내친김에 지지선도 하나 더 찾아보겠습니다.

 

 

저는 국동 매매를 통해 한 번은 쪽박, 한번은 약수익을 얻었습니다. 순전히 지지선, 저항선으로만 주가를 판단하는 우를 범하였었죠. 제 매수 타이밍을 한번 볼까요?

첫 번째 매수에서는 지지선 부근의 가격에서 진입하여 저항선까지의 상승을 노렸습니다. 결과는 대폭망. 두 번째 진입에서는 그래도 지지선을 잘 버티며 올라주고 있네요.

주식이라는 것이 매수와 매도의 힘겨루기로 가격이 결정이 됩니다. 느끼기에 주식의 가격은  그 기업의 건전성이나 체력 등으로 결정되는 진정한 가치와 상관이 없습니다(물론 오랜 기간의 주가 움직임은 결국 그 회사의 진정한 가치로 수렴하겠지만요.) 차트를 보면 그 투자자들의 매수 심리와 매도 심리를 얼추 알 수 있습니다.

 

 

이제 좀 조악하지만, 주식의 지지선과 저항선을 찾는 코딩을 해보도록 하겠습니다.

 

 

 

python code

import pandas as pd
import numpy as np
import yfinance
from mpl_finance import candlestick_ohlc
import mplfinance
import matplotlib.dates as mpl_dates
import matplotlib.pyplot as plt
import datetime
from dateutil.relativedelta import relativedelta
import FinanceDataReader as fdr

observation_date = 5

def is_Support(df, i):
    # c1 = df.Low[i] < df.Low[i - 1] < df.Low[i - 2] < df.Low[i - 3]
    # c2 = df.Low[i] < df.Low[i + 1] < df.Low[i + 2] < df.Low[i + 3]
    # return c1 & c2
    if df.Low[i] == np.min(df.Low[i - observation_date:i + observation_date + 1]):
        return True
    else:
        return False

def is_Resistance(df, i):
    # c1 = df.High[i] > df.High[i - 1] > df.High[i - 2] > df.High[i - 3]
    # c2 = df.High[i] > df.High[i + 1] > df.High[i + 2] > df.High[i + 3]
    # return c1 & c2
    if df.High[i] == np.max(df.High[i - observation_date:i + observation_date + 1]):
        return True
    else:
        return False

if __name__ == '__main__':
    plt.rcParams['figure.figsize'] = [12, 7]  # rcParam을 활용하여 그래프의 기본값(크기, 선의 색, 두께 등)을 설정할 수 있다.
    # plt.rcParams['axes.grid'] = True

    # rc를 이용해 graph의 font를 확인할 수 있다.
    plt.rc('font', size=10)

    end_date = datetime.datetime.today()
    start_date = end_date - relativedelta(years=1)

    stk_code = '005930'
    # stk_code = '065770'
    
    df = load_data(stk_code, start_date, end_date)
    df['Date'] = pd.to_datetime(df.index)
    df['Date'] = df['Date'].apply(mpl_dates.date2num)
    df = df.loc[:, ['Date', 'Open', 'High', 'Low', 'Close']]
    # date2num : Convert datetime objects toMatplotlib

    s = np.mean(df.High - df.Low)

    levels_support = []
    levels_resistance = []
    for i in range(2, df.shape[0] - 3):
        if is_Support(df, i):
            l = df.Low[i]
            if np.sum([abs(l - x[1]) < s * 2 for x in levels_support]) == 0:
                levels_support.append((i, l))
        elif is_Resistance(df, i):
            l = df.High[i]
            if np.sum([abs(l - x[1]) < s * 2 for x in levels_resistance]) == 0:
                levels_resistance.append((i, l))

    fig, ax = plt.subplots()
    candlestick_ohlc(ax, df.values, width=0.6, \
                     colorup='red', colordown='blue', alpha=0.8)
    date_format = mpl_dates.DateFormatter('%y/%b/%d')
    ax.xaxis.set_major_formatter(date_format)
    fig.autofmt_xdate()

    fig.tight_layout()

    for i, level in enumerate(levels_support):
        label_name = 'support line' if i == 0 else None
        plt.hlines(level[1], xmin=df['Date'][level[0]], xmax=max(df['Date']), colors='silver', label=label_name)

    for i, level in enumerate(levels_resistance):
        label_name = 'resistance line' if i == 0 else None
        plt.hlines(level[1], xmin=df['Date'][level[0]], xmax=max(df['Date']), colors='skyblue', label=label_name)
    plt.legend()
    plt.show()

 

 

간략히 알아보겠습니다.

 

def is_Support(df, i):   # 지지점을 찾기
    # 아래는 i 번째 저점 데이터가 향후 3일, 과거 3일 중 아래로 볼록 한 모양으로 가장 작은 점 찾기
    # c1 = df.Low[i] < df.Low[i - 1] < df.Low[i - 2] < df.Low[i - 3]
    # c2 = df.Low[i] < df.Low[i + 1] < df.Low[i + 2] < df.Low[i + 3]
    # return c1 & c2
    
    # i는 date에 대한 index이고
    # 시고저종 데이터중 저가를 대상으로
    # i-5일, i+5일 사이에서 가장 저점이 i날인 경우 그날이 지지되는 날이라고 판단
    if df.Low[i] == np.min(df.Low[i - observation_date:i + observation_date + 1]):
        return True
    else:
        return False

 


def is_Resistance(df, i):  # 저항점 찾기
    # i번째 고가 데이터가 위로 볼록한 모양으로 향후 3일, 과거 3일 중 최대일 때,
    # 하지만 주석 처리
    # c1 = df.High[i] > df.High[i - 1] > df.High[i - 2] > df.High[i - 3]
    # c2 = df.High[i] > df.High[i + 1] > df.High[i + 2] > df.High[i + 3]
    # return c1 & c2
    
    # i 번째 고가 데이터가 향후 5일, 과거 5일 중 가장 높은 점일 때
    # 그 점을 저항점이라고 판단함
    if df.High[i] == np.max(df.High[i - observation_date:i + observation_date + 1]):
        return True
    else:
        return False

 

 


if __name__ == '__main__':
    plt.rcParams['figure.figsize'] = [12, 7]  # rcParam을 활용하여 그래프의 기본값(크기, 선의 색, 두께 등)을 설정할 수 있다.
    # plt.rcParams['axes.grid'] = True        # chart에 간격선(grid)을 넣을 것인지 아닌지

    # rc를 이용해 graph의 font를 확인할 수 있다.
    plt.rc('font', size=10)

    end_date = datetime.datetime.today()           # 데이터 조회 종료일을 오늘로
    start_date = end_date - relativedelta(years=1) # 데이터 조회 시작일은 오늘부터 1년전

    stk_code = '005930'          # 삼성전자 주식(005930)
    # stk_code = '065770'        # CS 주식(065770)
    
    df = load_data(stk_code, start_date, end_date)  # FinanceDataReader 라이브러리로 데이터 load
    df['Date'] = pd.to_datetime(df.index)           # dataframe에 Date칼럼 만들고 index 복사
    df['Date'] = df['Date'].apply(mpl_dates.date2num) # date 를 숫자로 변환하는 함수 적용
    df = df.loc[:, ['Date', 'Open', 'High', 'Low', 'Close']] # df 중에서 Date 및 시고저종 column 추출
    # date2num : Convert datetime objects toMatplotlib

    s = np.mean(df.High - df.Low)    # 아래 구할 지지,저항선이 번잡하게 나타나는 것을 방지하는 목적
                                     # 1년 시고저종데이터의 고점과 저점차이의 평균을 s라 함

    levels_support = []              # 지지 레벨을 담을 list
    levels_resistance = []           # 저항 레벨을 담을 list
    for i in range(2, df.shape[0] - 3):   
        if is_Support(df, i):        # 만일 i가 지지점으로 판단되면 
            l = df.Low[i]            # l = i번째 날의 저가
            if np.sum([abs(l - x[1]) < s * 2 for x in levels_support]) == 0:
                                     # 그 저가가 이미 구한 지지점들과과 각 차이가 2s보다 작지 않다면
                                     # 즉 전단계에서 구한 지지레벨들과 오밀조밀 하지 않다면
                levels_support.append((i, l))  # 지지점으로 인정하고 level_support list에 담음
        elif is_Resistance(df, i):   # 만일 i가 저항점으로 판단되면
            l = df.High[i]           # i번째 날의 고가를 l이라 두고
            if np.sum([abs(l - x[1]) < s * 2 for x in levels_resistance]) == 0:
                                     # 그것이 기존에 구한 다른 저항점들과 거리가 2s 보다 큰 경우 발생하면
                levels_resistance.append((i, l))
                                     # 그 날의 고가를 저항점으로 인식하고 level_resistance에 담음

    fig, ax = plt.subplots()
    candlestick_ohlc(ax, df.values, width=0.6, \
                     colorup='red', colordown='blue', alpha=0.8)  # ohlc 차트를 그림
    date_format = mpl_dates.DateFormatter('%y/%b/%d') 
    ax.xaxis.set_major_formatter(date_format)
    fig.autofmt_xdate()

    fig.tight_layout()

    for i, level in enumerate(levels_support):   # 각 지지선을 hline을 써서 chart 에 표기
        label_name = 'support line' if i == 0 else None
        plt.hlines(level[1], xmin=df['Date'][level[0]], xmax=max(df['Date']), colors='silver', label=label_name)

    for i, level in enumerate(levels_resistance): # 각 저항선을 hlines 을 써서 chart에 표시
        label_name = 'resistance line' if i == 0 else None
        plt.hlines(level[1], xmin=df['Date'][level[0]], xmax=max(df['Date']), colors='skyblue', label=label_name)
    plt.legend()
    plt.show()

 

참고로

2022.07.16 - [주식분석/Quant 분석(프로그래밍)] - 주식 차트 그리기 #2: candlestick_ohlc 시고저종 봉차트

 

주식 차트 그리기 #2: candlestick_ohlc 시고저종 봉차트

이번 글은 2022.07.08 - [파이썬] - 주식 차트 그리기 #1 : mplfinance 주식 차트 그리기 #1 : mplfinance 이 글은 2022.07.07 - [파이썬] - 주식 데이터 불러오기(yfinance) #1 주식 데이터 불러오기(yfinance) #1 python으

sine-qua-none.tistory.com

에서 주식 캔들차트 그리는 법을 소개해 놨습니다.

 

 

이제 결과를 보면

 

 

삼성전자(005930)의 지지선/저항선

 

 

 

CS (065770) 의 지지선/저항선

 

 

 

어떻습니까?  지지/저항선으로 보이시나요?

 

지지선/저항선을 찾는 하나의 로직일 뿐, 다른 좋은 방법이 많을 것으로 판단됩니다.  참고로 로직 중 일부는 Gianluca Malato 라는 블로거의 코드에서 차용했습니다.

 

 

728x90
반응형

댓글