인공지능과 경제

페어트레이딩전략 pair trading 총정리

2에코랩 2023. 3. 9.

오늘은 퀀트투자에서 많이 활용하는 페어트레이딩전략pair trading을 살펴보겠습니다.


오늘은 퀀트투자에서 많이 활용하는 페어트레이딩전략pair trading을 살펴보고자 합니다.

Time series X and Y are cointegrated and highly correlated. 출처 : medium.com

 

페어트레이딩(pair trading) 전략은 두 개의 상호관련성이 높은 주식을 쌍으로 선택하여, 둘 간의 가격차이를 이용해 수익을 추구하는 투자 전략입니다. 일반적으로, 두 주식의 가격 차이가 정상분포를 따르는 것으로 가정하며, 가격 차이가 일정 범위를 벗어날 때 매수 또는 매도를 진행합니다.

 

이전에는 이러한 전략을 수동으로 구현했지만, 이제는 컴퓨터 프로그램을 사용하여 자동화할 수 있습니다. 이를 퀀트 페어트레이딩(quant pair trading)이라고도 부르며, 퀀트 투자자들이 주로 사용하고 있습니다. 페어트레이딩 전략은 주식시장의 변동성이 높은 시기에서 유용하며, 일반적으로 장기적으로 안정적인 수익을 추구하는 가치 투자 전략과는 대조적입니다.

 

가치투자 전략과 페어트레이딩 전략은 다음과 같은 이유로 대조적입니다. 먼저 투자 철학에서 차이가 납니다. 가치투자 전략은 장기적인 투자철학에 기반을 두고 있으며, 기업의 기본적인 가치를 기반으로 저평가된 기업을 찾아 투자합니다. 반면에, 페어트레이딩 전략은 단기적인 투자철학에 기반을 두고 있으며, 두 개의 상관관계가 높은 종목을 찾아서 상대적으로 가격이 높은 종목을 공매도하고, 가격이 낮은 종목을 매수합니다.

 

분석 대상에서도 차이가 납니다. 가치투자 전략은 기업의 재무상태와 가치에 대한 분석을 중심으로 하지만, 페어트레이딩 전략은 상관관계 분석에 중점을 두고 있습니다. 투자 대상도 차이가 납니다. 가치투자 전략은 기업 자체에 투자하는 것이며, 기업의 경영환경, 성과, 성장 잠재력 등을 고려합니다. 반면에, 페어트레이딩 전략은 두 종목 간의 상관관계에 투자하는 것이므로, 기업 자체의 가치나 성과보다는 두 종목 간의 가격 차이나 상관관계에 대한 분석에 더 많은 비중을 둡니다.

 

다음의 표는 가치투자 전략과 페어트레이딩 전략의 차이를 정리한 것입니다.

구분 가치투자전략 페어트레이딩전략
투자방식 개별 기업의 가치를 분석하여 저평가된 기업 주식에 투자 두 개의 기업을 선택하고, 비슷한 업종이나 시장에서 비교 분석 후 거래
리스크 기업의 재무상태, 경영성과 등에 따라 리스크가 존재 시장 전반적인 추세에 더 많은 영향을 받아 리스크가 존재
수익률 장기적으로 안정적인 수익을 추구 단기적으로 빠른 수익을 추구
전략 장점 안정적이며 장기적으로 높은 수익 가능 시장 전반의 하락에도 수익을 추구 가능
전략 단점 기업분석 및 평가가 어려울 수 있음 특정 시장 상황이나 업종 추세에 크게 영향을 받음

 

 

따라서, 가치투자 전략과 페어트레이딩 전략은 서로 다른 투자 철학과 방법을 가지고 있으며, 분석 대상과 투자 대상도 서로 다르기 때문에 대조적이라고 할 수 있습니다.

 

페어 트레이딩 전략이 퀀트 투자에서 중요한 이유는 다음과 같습니다.


db attribution 출처 : medium.com

  • 다양한 시장 조건에서 안정적인 수익을 얻을 수 있습니다: 페어 트레이딩 전략은 단일 종목에 의존하지 않고, 두 개의 종목을 동시에 거래하므로 시장 조건에 상관없이 안정적인 수익을 얻을 수 있습니다.
  • 리스크를 분산할 수 있습니다: 두 개의 종목을 거래하므로, 특정 종목에 대한 노출을 분산할 수 있습니다. 이를 통해 포트폴리오 리스크를 감소시킬 수 있습니다.
  • 보유 기간이 짧아 포트폴리오 회전율이 높습니다: 페어 트레이딩 전략은 대개 단기간 보유를 기반으로 하므로 포트폴리오 회전율이 높습니다. 이를 통해 빠른 시간 내에 수익을 얻을 수 있습니다.
  • 거래 신호가 명확합니다: 페어 트레이딩 전략은 통계학적 분석에 기반하여 거래 신호를 생성하므로 명확합니다. 이를 통해 거래 결정에 대한 주관적인 판단을 줄일 수 있습니다.
  • 장기적인 수익을 예측할 수 있습니다: 페어 트레이딩 전략은 두 종목 간의 상관관계를 분석하여 거래를 결정하므로, 장기적인 수익을 예측할 수 있습니다. 이는 단기적인 변동성에 노출되는 것보다 포트폴리오의 장기적인 안정성을 높일 수 있습니다.

 

페어트레이딩 전략의 방법과 절차를 표로 정리하면 다음과 같습니다.


illustrates the number of pairs traded for a given year 출처 : medium.com

단계 페어트레이딩 전략 방법과 절차
1단계 두 개의 주식 선택
2단계 주식 간의 상관관계 분석
3단계 상관관계에 따라 매수/매도 비율 결정
4단계 매매 시점 결정
5단계 포트폴리오 리밸런싱
  1. 두 개의 주식 선택: 페어트레이딩 전략에서는 두 개의 주식을 선택하여 이를 페어(pair)로 취급합니다. 이 때, 선정되는 주식은 기업의 업종, 시가총액, 시장지분율 등을 고려하여 선정합니다.
  2. 주식 간의 상관관계 분석: 선정된 두 개의 주식 간의 상관관계를 분석합니다. 이 때, 보통 상관계수(correlation coefficient)를 이용하여 상관관계를 측정합니다.
  3. 상관관계에 따라 매수/매도 비율 결정: 상관관계 분석 결과, 두 주식이 양의 상관관계를 가지고 있다면 한 주식을 매수하고 다른 주식을 동시에 매도하는 것이 일반적입니다. 이 때, 각 주식의 매수/매도 비율은 상관관계 분석 결과에 따라 결정됩니다.
  4. 매매 시점 결정: 주식의 매매 시점은 일반적으로 상관관계가 일시적으로 불안정해지는 경우를 탐지하여 매매를 수행합니다. 이 때, 보통 통계적인 방법을 이용하여 이러한 상황을 감지합니다.
  5. 포트폴리오 리밸런싱: 페어트레이딩 전략에서는 포트폴리오를 주기적으로 리밸런싱하여 최적의 수익을 추구합니다. 이 때, 리밸런싱 기간은 전략에 따라 다르게 결정될 수 있습니다.

 

퀀트투자에서 페어트레이딩 전략의 알고리즘 종류는 다음과 같습니다.


illustrates a  seaborn  heatmap of a sector tested for cointegration 출처 : medium.com

알고리즘 종류 설명
거래량 비교 두 종목의 거래량을 비교하여, 동일한 산업군 또는 시장에서 거래되는 두 종목의 가격 차이를 활용하여 거래합니다.
상관관계 분석 두 종목의 가격 차이를 분석하여, 상관관계를 파악하고, 특정 상관관계에 의거하여 거래합니다.
차분 분석 두 종목의 가격 차이가 일정 범위를 벗어나면 거래합니다.
평균 회귀 두 종목의 가격 차이가 일시적으로 벗어난 경우, 장기적으로 평균 수준으로 회귀할 것이라는 가정을 바탕으로 거래합니다.
공적분 분석 두 종목의 가격 차이가 장기적으로 일정한 비율을 유지하는 관계를 가지고 있는지 분석하여, 그 비율을 벗어난 경우 거래합니다.
칼만 필터 두 종목의 가격 차이를 분석하고, 칼만 필터를 활용하여 추세를 예측하고, 추세에 따라 거래합니다.

 

 

각각의 알고리즘에서 Yahoo Finance API를 사용하는 파이썬 코드 예시를 살펴보도록 하겠습니다.

 

 

거래량 비교 알고리즘

import yfinance as yf

# Define the stock symbols
symbols = ["AAPL", "MSFT"]

# Download the stock data
data = yf.download(symbols, start="2022-01-01", end="2022-02-28")

# Calculate the stock returns
returns = data['Adj Close'].pct_change()

# Calculate the average trading volume
avg_volume = data['Volume'].rolling(window=20).mean()

# Calculate the volume ratio
volume_ratio = data['Volume'][symbols[0]] / data['Volume'][symbols[1]]

# Set the threshold for the volume ratio
threshold = 1.5

# Generate the buy/sell signals
buy_signal = (volume_ratio > threshold)
sell_signal = (volume_ratio < (1 / threshold))

# Calculate the portfolio returns
portfolio_returns = (returns[symbols[0]] * buy_signal[symbols[0]]) + (returns[symbols[1]] * sell_signal[symbols[1]])

 

상관관계 분석 알고리즘

import yfinance as yf
import numpy as np
import pandas as pd
from scipy import stats

# Define the two stocks for pairs trading
stock1 = "AAPL"
stock2 = "MSFT"

# Download historical data for the two stocks
data = yf.download([stock1, stock2], period="1y", interval="1d")["Adj Close"]

# Calculate the spread between the two stocks' prices
spread = data[stock1] - data[stock2]

# Calculate the rolling correlation between the two stocks' prices
rolling_corr = np.zeros(len(data))
for i in range(50, len(data)):
    corr = stats.pearsonr(data[stock1][i-50:i], data[stock2][i-50:i])[0]
    rolling_corr[i] = corr

# Calculate the z-score of the current spread value based on the rolling correlation
zscore = (spread - np.mean(spread)) / np.std(spread)
zscore -= rolling_corr * zscore

# Define the trading signals based on the z-score
buy_signal = zscore < -2
sell_signal = zscore > 2

# Print the trading signals and the current spread value
print("Buy signal:", buy_signal[-1])
print("Sell signal:", sell_signal[-1])
print("Spread:", spread[-1])

차분 분석 알고리즘

import yfinance as yf
import pandas as pd

# Define the tickers for the pair
stock1 = "AAPL"
stock2 = "MSFT"

# Download the historical prices for the two stocks
prices = yf.download([stock1, stock2], start="2015-01-01", end="2022-03-01")['Close']

# Compute the difference between the two stocks' prices
diff = prices[stock1] - prices[stock2]

# Compute the z-score for the difference
zscore = (diff - diff.mean()) / diff.std()

# Define the threshold for entering and exiting a position
entry_threshold = 2
exit_threshold = 0

# Initialize the position variables
position1 = 0
position2 = 0

# Define the trade signals based on the z-score
signals = pd.Series(0, index=zscore.index)
signals[zscore > entry_threshold] = 1
signals[zscore < -entry_threshold] = -1
signals[abs(zscore) < exit_threshold] = 0

# Iterate through the signals and execute trades
for i in range(1, len(signals)):
    if signals[i-1] == 0 and signals[i] == 1:
        position1 = 1
        position2 = -1
    elif signals[i-1] == 0 and signals[i] == -1:
        position1 = -1
        position2 = 1
    elif signals[i-1] == 1 and signals[i] == -1:
        position1 = 0
        position2 = 0
    elif signals[i-1] == -1 and signals[i] == 1:
        position1 = 0
        position2 = 0

    # Apply the position changes to the portfolio value
    prices_diff = prices.iloc[i][stock1] - prices.iloc[i][stock2]
    portfolio_value = position1 * prices.iloc[i][stock1] + position2 * prices.iloc[i][stock2]
    print(f"{prices.index[i]} - {stock1}: {position1} shares, {stock2}: {position2} shares, Portfolio Value: {portfolio_value:.2f}")

 

평균 회귀 알고리즘

import yfinance as yf

# Define the two assets to trade as a pair
asset_1 = yf.Ticker("AAPL")
asset_2 = yf.Ticker("MSFT")

# Get historical data for the two assets
hist_1 = asset_1.history(period="1y")
hist_2 = asset_2.history(period="1y")

# Calculate the spread between the two assets
spread = hist_1['Close'] - hist_2['Close']

# Calculate the mean and standard deviation of the spread
mean_spread = spread.mean()
std_spread = spread.std()

# Calculate the upper and lower bounds for the spread
upper_bound = mean_spread + std_spread
lower_bound = mean_spread - std_spread

# Get the most recent prices for the two assets
price_1 = asset_1.info['regularMarketPrice']
price_2 = asset_2.info['regularMarketPrice']

# Calculate the current spread between the two assets
current_spread = price_1 - price_2

# Check if the current spread is outside of the upper or lower bounds
if current_spread > upper_bound:
    # The spread is too wide, so we short asset_1 and long asset_2
    # (i.e. we sell asset_1 and buy asset_2)
    print("Sell", asset_1.info['symbol'], "and buy", asset_2.info['symbol'], "to capture the mean reversion.")
elif current_spread < lower_bound:
    # The spread is too narrow, so we long asset_1 and short asset_2
    # (i.e. we buy asset_1 and sell asset_2)
    print("Buy", asset_1.info['symbol'], "and sell", asset_2.info['symbol'], "to capture the mean reversion.")
else:
    # The spread is within the bounds, so we do nothing
    print("The spread between", asset_1.info['symbol'], "and", asset_2.info['symbol'], "is within the bounds.")

공적분 분석 알고리즘

import yfinance as yf
import statsmodels.api as sm

# define the two stocks to be tested for cointegration
symbol1 = "AAPL"
symbol2 = "MSFT"

# download the historical data for the two stocks
df1 = yf.download(symbol1, start="2022-01-01", end="2022-02-28")
df2 = yf.download(symbol2, start="2022-01-01", end="2022-02-28")

# extract the adjusted close prices for the two stocks
y1 = df1["Adj Close"]
y2 = df2["Adj Close"]

# run the cointegration test
results = sm.tsa.stattools.coint(y1, y2)

# extract the test statistic and p-value
test_stat = results[0]
p_value = results[1]

# check if the two stocks are cointegrated
if p_value < 0.05:
    print("The two stocks are cointegrated with a p-value of", p_value)
else:
    print("The two stocks are not cointegrated with a p-value of", p_value)

칼만 필터 알고리즘

import yfinance as yf
import numpy as np
from pykalman import KalmanFilter

# define the pair of stocks to trade
stock1 = "AAPL"
stock2 = "MSFT"

# retrieve historical prices from Yahoo Finance API
prices = yf.download([stock1, stock2], period="1y")["Adj Close"]

# calculate the spread between the two stocks
spread = prices[stock1] - prices[stock2]

# set up Kalman filter parameters
kf = KalmanFilter(initial_state_mean=[spread.iloc[0], 0],
                  n_dim_obs=1,
                  n_dim_state=2,
                  observation_matrices=[np.ones((1, 2))],
                  observation_covariance=0.01,
                  transition_matrices=[[1, 1], [0, 1]],
                  transition_covariance=[[0.01, 0], [0, 0.01]],
                  transition_offsets=[0, 0])

# iterate through the spread data and use Kalman filter to estimate the state
state_means, state_covs = kf.filter(spread.values)

# calculate the z-score of the spread data
z_score = (spread.values - state_means[:, 0]) / np.sqrt(state_covs[:, 0, 0])

# enter a long position if z-score is below a certain threshold
entry_threshold = -1.5
long_entry = z_score < entry_threshold

# exit the long position if z-score returns to the mean
exit_threshold = 0
long_exit = z_score > exit_threshold

# enter a short position if z-score is above a certain threshold
short_entry = z_score > -entry_threshold

# exit the short position if z-score returns to the mean
short_exit = z_score < -exit_threshold

# calculate the position size for each stock based on the spread data
position_size1 = np.where(long_entry, 1, 0) - np.where(long_exit, 1, 0) - np.where(short_entry, -1, 0) + np.where(short_exit, 1, 0)
position_size2 = -position_size1

# calculate the portfolio value
portfolio_value = position_size1 * prices[stock1] + position_size2 * prices[stock2]

# plot the portfolio value over time
portfolio_value.plot()

 

 

오늘은 퀀트투자에서 많이 활용하는 페어트레이딩전략pair trading을 살펴보았습니다.


퀀트투자에서 많이 활용하는 페어트레이딩 전략을 오늘 살펴보았습니다. 가치투자전략과 대조적인 페어트레이딩전략은 퀀트투자의 알고리즘에서 많이 활용되며, 이에 구체적인 페어트레이딩전략 알고리즘의 다양한 예시를 살펴보았습니다.

 

퀀트투자 알고리즘은 예시일뿐이니 참고만 하시기 바라며, 투자는 어디까지나 본인의 결정과 책임에 따른 것임을 유의하시기 바라며 페어트레이딩전략 pair trading 총정리 포스팅을 마치겠습니다. 감사합니다.

반응형

댓글

💲 추천 글