Sonar 지표를 활용한 트레이딩 전략(Sonar Indicator Trading Strategy)

Sonar 지표의 개념과 유래

Sonar 지표는 Goichi Hosoda에 의해 개발된 지표로, 일목균형표(Ichimoku Kinko Hyo)의 핵심 구성 요소 중 하나이다. 이 지표는 가격의 모멘텀을 측정하고 추세의 강도를 평가하는 데 사용된다.

Sonar 지표는 두 개의 선으로 구성되며, 각각 선행 스팬 A와 선행 스팬 B의 이격도를 나타낸다. 이 지표는 일목균형표의 구름대(Kumo)와 유사한 역할을 하지만, 선행 스팬의 이격도에 초점을 맞춘다는 점에서 차이가 있다.

Sonar 지표 계산 방법

Sonar 지표는 다음과 같은 단계를 통해 계산된다:

  1. 선행 스팬 A와 선행 스팬 B 계산:
  • 선행 스팬 A = (전환선 + 기준선) / 2를 26일 앞으로 투영
  • 선행 스팬 B = (52일간의 최고가 + 최저가) / 2를 26일 앞으로 투영
  1. 선행 스팬 A와 선행 스팬 B의 이격도 계산:
  • 이격도 = 선행 스팬 A – 선행 스팬 B
  1. Sonar 상한선과 하한선 계산:
  • 상한선 = 이격도의 일정 기간(예: 14일) 이동평균 + 표준편차 * 상수(예: 2)
  • 하한선 = 이격도의 일정 기간(예: 14일) 이동평균 – 표준편차 * 상수(예: 2)

Sonar 지표의 상한선과 하한선은 선행 스팬 A와 B의 이격도가 평균에서 벗어나는 정도를 나타낸다. 이격도가 상한선을 넘어설 때는 강력한 상승 추세를, 하한선을 하회할 때는 강력한 하락 추세를 시사한다.

Sonar 지표를 활용한 매매 전략

Sonar 지표를 활용한 매매 전략은 다음과 같다:

  1. 추세 추종 전략:
  • Sonar 지표의 이격도가 상한선을 넘어설 때 매수하고, 하한선을 하회할 때 매도한다.
  • 이는 강력한 추세를 따르는 전략으로, 추세의 지속성을 전제로 한다.
  1. 추세 전환 포착 전략:
  • Sonar 지표의 이격도가 상한선이나 하한선에서 반전할 때 추세 전환 신호로 해석한다.
  • 상한선에서 하락 반전할 때 매도, 하한선에서 상승 반전할 때 매수 신호로 활용할 수 있다.
  1. 일목균형표와의 조합:
  • Sonar 지표를 일목균형표의 다른 구성 요소(예: 전환선, 기준선, 구름대 등)와 함께 활용한다.
  • 일목균형표의 신호와 Sonar 지표의 신호가 일치할 때 매매 신호의 신뢰도가 높아진다.

실제 트레이딩에서는 Sonar 지표를 다른 기술적 지표 및 시장 상황과 함께 종합적으로 분석하여 의사결정을 내리는 것이 바람직하다.

파이썬을 활용한 Sonar 지표 백테스팅

파이썬을 사용하여 Sonar 지표 기반의 트레이딩 전략을 백테스팅해보자. 다음 라이브러리를 활용할 것이다:

  • pandas: 데이터 조작 및 분석
  • numpy: 수치 계산
  • matplotlib: 데이터 시각화
  • FinanceDataReader: 주가 데이터 수집

먼저 가상환경을 활성화하고 필요한 라이브러리를 설치한다.

source venv/bin/activate
pip install pandas numpy matplotlib FinanceDataReader

FinanceDataReader를 설치하면 종속 모듈도 함께 설치된다.

다음으로 Sonar 지표를 계산하고 매매 신호를 생성하는 코드를 작성해보자.

import FinanceDataReader as fdr
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 데이터 가져오기
symbol = "VOO"
start_date = "1900-01-01"
end_date = "2023-05-01"

try:
    data = fdr.DataReader(symbol, start_date, end_date)
except Exception as e:
    print(f"Error occurred while fetching data: {e}")
    exit(1)

# Sonar 지표 계산 함수
def calculate_sonar(data, n=26, m=52, t=26, s=14, k=2):
    data = data.copy()

    # 전환선, 기준선 계산
    data['turning_line'] = (data['High'].rolling(window=9).max() + data['Low'].rolling(window=9).min()) / 2
    data['standard_line'] = (data['High'].rolling(window=26).max() + data['Low'].rolling(window=26).min()) / 2

    # 선행 스팬 A, B 계산
    data['leading_span_a'] = ((data['turning_line'] + data['standard_line']) / 2).shift(t)
    data['leading_span_b'] = ((data['High'].rolling(window=m).max() + data['Low'].rolling(window=m).min()) / 2).shift(t)

    # 이격도 계산
    data['disparity'] = data['leading_span_a'] - data['leading_span_b']

    # 상한선, 하한선 계산
    data['upper'] = data['disparity'].rolling(window=s).mean() + k * data['disparity'].rolling(window=s).std()
    data['lower'] = data['disparity'].rolling(window=s).mean() - k * data['disparity'].rolling(window=s).std()

    return data

# 매매 신호 생성 함수
def get_signals(data):
    signals = pd.DataFrame(index=data.index)
    signals['signal'] = 0

    signals['signal'][data['disparity'] > data['upper']] = 1
    signals['signal'][data['disparity'] < data['lower']] = -1

    signals['positions'] = signals['signal'].diff()

    return signals

# Sonar 지표 계산
sonar_data = calculate_sonar(data)

# 매매 신호 생성
signals = get_signals(sonar_data)

# 백테스팅
initial_capital = 10000
positions = pd.DataFrame(index=signals.index).fillna(0.0)
positions[symbol] = 100 * signals['signal']

portfolio = positions.multiply(sonar_data['Adj Close'], axis=0)
pos_diff = positions.diff()

portfolio['holdings'] = (positions.multiply(sonar_data['Adj Close'], axis=0)).sum(axis=1)
portfolio['cash'] = initial_capital - (pos_diff.multiply(sonar_data['Adj Close'], axis=0)).sum(axis=1).cumsum()

portfolio['total'] = portfolio['cash'] + portfolio['holdings']
portfolio['returns'] = portfolio['total'].pct_change()

# 결과 출력
print(f"Final portfolio value: ${portfolio['total'][-1]:,.2f}")
print(f"Total returns: {100 * (portfolio['total'][-1] / initial_capital - 1):.2f}%")

# 시각화
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(15, 15), gridspec_kw={'height_ratios': [3, 1, 1]})

ax1.plot(sonar_data['Adj Close'], label='Close Price')
ax1.plot(sonar_data['leading_span_a'], label='Leading Span A')
ax1.plot(sonar_data['leading_span_b'], label='Leading Span B')
ax1.fill_between(sonar_data.index, sonar_data['leading_span_a'], sonar_data['leading_span_b'], where=sonar_data['leading_span_a'] >= sonar_data['leading_span_b'], color='lightgreen', alpha=0.5, label='Cloud (Bullish)')
ax1.fill_between(sonar_data.index, sonar_data['leading_span_a'], sonar_data['leading_span_b'], where=sonar_data['leading_span_a'] < sonar_data['leading_span_b'], color='lightcoral', alpha=0.5, label='Cloud (Bearish)')
ax1.set_ylabel('Price')
ax1.set_title('Price Chart with Ichimoku Cloud')
ax1.legend(loc='upper left')

ax2.plot(sonar_data['disparity'], label='Disparity')
ax2.plot(sonar_data['upper'], label='Upper Band')
ax2.plot(sonar_data['lower'], label='Lower Band')
ax2.set_ylabel('Disparity')
ax2.set_title('Sonar Indicator')
ax2.legend(loc='upper left')

ax3.plot(portfolio['total'], label='Portfolio Value')
ax3.set_ylabel('Portfolio Value')
ax3.set_title('Portfolio Performance')
ax3.legend(loc='upper left')

plt.tight_layout()
plt.show()

위 코드에서는 FinanceDataReader를 사용하여 VOO ETF의 전체 기간 주가 데이터를 가져온다. start_date를 가능한 한 오래전 날짜로 설정하여 모든 데이터를 가져올 수 있도록 한다.

calculate_sonar 함수는 주어진 데이터를 바탕으로 Sonar 지표를 계산한다. 먼저 전환선과 기준선을 계산하고, 이를 이용하여 선행 스팬 A와 B를 구한다. 그런 다음 선행 스팬 A와 B의 이격도를 계산하고, 이격도의 상한선과 하한선을 구한다.

get_signals 함수는 계산된 Sonar 지표를 기준으로 매매 신호를 생성한다. 이격도가 상한선을 초과할 때 매수 신호(1), 하한선을 하회할 때 매도 신호(-1)를 생성한다.

백테스팅을 위해 초기 자본금을 10,000달러로 설정하고, 생성된 매매 신호를 바탕으로 포트폴리오 가치를 계산한다. 최종적으로 총 수익률과 포트폴리오 가치를 출력한다.

matplotlib을 사용하여 주가, Sonar 지표, 포트폴리오 가치를 시각화한다. 주가 차트에는 일목균형표의 구름대도 함께 표시하여 Sonar 지표와의 관계를 나타낸다.

코드 실행 중 발생할 수 있는 오류를 처리하기 위해 try-except 문을 사용하였다. 데이터를 가져오는 과정에서 오류가 발생하면 에러 메시지를 출력하고 프로그램을 종료한다.

Leave a Reply

error: Content is protected !!