매매를 하는데 있어 수익을 얻기 위해서는 싸게 사서 비싸게 팔아야한다는 것을 누구나 다 알고 계실겁니다.
그렇다면 주식이 언제쯤 "싸다"고 할 수 있을까요?
가격이 계속떨어진다고 하여 싸졌다고 할 수도 있지만 내가 매수한 다음날 가격이 떨어졌다고 하면
과연 싸게 샀다가 할 수 있을까요? 그렇지 않을 겁니다.
메리츠종금증권의 예를 들어 보겠습니다.
메리츠종금은 2019.10월을 기점으로 최고가 5,160원에 도달하고 곧 한달 사이에 4,170원이 되었습니다.
그전에 메리츠종금의 주가가 4천 중후반 ~ 5천 중반까지 갔던 것을 감안하면 4170원은 충분히 싼 가격이라고 할 수 있습니다.
그래 좋다. 싸다 싶어서 4,170원에 주식을 매수했다고 해보겠습니다.
채 몇 주가 지나지 않아 10%가 더 넘게 빠져버렸습니다.
이렇듯 평소보다 가격이 빠진다고 하여 저점이라고 판단하기에는 무리가 있습니다.
떨어질 때 사면 더 떨어져버리고 비쌀 때 사면 조금 있다가 또 떨어져버리는 주식의 가격을 예측하는 것이 애당초 무의미한 일이기 때문입니다.
그러면 도대체 우리는 언제 주식을 사야 할까요?
이를 가늠해보기 위해서 알아야 할 것이 바로 주가의 중요한 속성인 추세입니다.
주가는 떨어지기 시작하면 계속 떨어지고, 오르기 시작하면 계속 해서 오르는 성질이 있습니다.
싸다고 생각해서 샀는데 다음날, 또 다음날 계속해서 떨어진다면 결코 싸게 샀다고 할 수 없습니다.
이는 하방 추세에 매수하는 경우에 해당합니다.
주가는 하락하는 중이고 언제까지 하락하는지 알 수가 없는 마당에 내가 산 시점에 추세가 바뀌어 상승하면 다행이지만 그렇지 않고 계속 하방 추세가 유지된다면 계좌가 박살이 나겠지요.
물론 가격이 떨어지는 추세라고 할지라도 오르는 날도 당연히 존재하여 주가가 올랐다 내렸다를 반복할 수 있지만 결국에는 그 차이가 존재하여 계속 조금씩 떨어지는 상태가 될 것입니다. 그 반대의 상황도 마찬가지입니다.
그렇기 때문에 오히려 저점을 잡겠다고 하기 보다는 추세가 전환된 다음에 매수하는 것이 더 좋을 수 있습니다.
무슨 말이냐면,
예를 들어 최근 20일 동안의 평균 가격보다 오늘 주가가 더 비쌀 경우 매수하는 것이 오히려 수익을 얻을 확률이 더 높다고 할 수도 있습니다.
비록 이는 최근 주가 흐름에서 비쌀 때 샀다지만 상승 추세이기 때문에 더 비싼 가격에 팔 수 있다!는 아이디어입니다.
반대로 파는 시점은 20일 동안의 평균 가격보다 오늘 주가가 더 싸졌을 때라고 설정해보겠습니다.
이 의미는 하방 추세가 시작됐다고 판단되면 마찬가지로 하락한다는 추세에 맞춰 어떻게든 계속 떨어질 것이니 미리 매도하여 손실을 줄이겠다는 전략입니다.
이 전략(추세추종 전략)을 삼성전자를 대상으로 2019년 한해 동안 시드 100만원을 갖고 수행해보겠습니다.
import datetime
import backtrader as bt
# Create a subclass of Strategy to define the indicators and logic
class Momentum(bt.Strategy):
# list of parameters which are configurable for the strategy
params = dict(
pfast=10, # period for the fast moving average
pslow=20 # period for the slow moving average
)
def __init__(self):
self.dataclose = self.datas[0].close
self.smaSlow = bt.ind.SimpleMovingAverage(period=self.p.pslow)
self.smaFast = bt.ind.SimpleMovingAverage(period=self.p.pfast)
self.order = None
def log(self, txt, dt=None):
''' Logging function fot this strategy'''
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
def notify_order(self, order):
# 1. If order is submitted/accepted, do nothing
if order.status in [order.Submitted, order.Accepted]:
return
# 2. If order is buy/sell executed, report price executed
if order.status in [order.Completed]:
if order.isbuy():
self.log('BUY EXECUTED, Price: {0:8.2f}, Size: {1:8.2f} Cost: {2:8.2f}, Comm: {3:8.2f}'.format(
order.executed.price,
order.executed.size,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
else:
self.log('SELL EXECUTED, {0:8.2f}, Size: {1:8.2f} Cost: {2:8.2f}, Comm{3:8.2f}'.format(
order.executed.price,
order.executed.size,
order.executed.value,
order.executed.comm))
self.bar_executed = len(self) # when was trade executed
# 3. If order is canceled/margin/rejected, report order canceled
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')
self.order = None
def next(self):
cash = self.broker.get_cash()
value = self.broker.get_value()
size = int(cash / self.data.close[0])
# Order가 Pending인지 확인, 그렇다면 다시 주문할 수 없음
if self.order:
return
if not self.position: # not in the market
if self.smaSlow < self.data.close[0]:
self.order = self.buy(size=size)
elif self.getposition().size > 0: #in the market
if self.smaSlow > self.data.close[0]:
self.order = self.sell(size=self.getposition().size)
def run(args=None):
cerebro = bt.Cerebro() # create a "Cerebro" engine instance
cerebro.broker.setcash(1000000) #초기자금
#데이터 생성
data = bt.feeds.YahooFinanceData(dataname='005930.KS',
fromdate=datetime.datetime(2019, 1, 1),
todate=datetime.datetime(2019, 12, 31))
cerebro.adddata(data) #데이터 삽입
cerebro.addstrategy(Momentum) #전략적용
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.run() #수행
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.plot() #plot
if __name__ == '__main__':
run()
다음은 수행결과입니다.
100만원으로 123만원이 되었으니 23만원을 벌었습니다!
다음은 그 반대 전략입니다. (추세반대전략, 역추세전략)
20일 평균보다 가격이 떨어진 경우 싸다고 생각해서 사고 20일 평균보다 가격이 비싸진 경우에는 충분히 비싸다고 생각하여 매도하는 전략을 수행해보겠습니다.
이를 코드로 나타내고자한다면 self.sma와 self.data.close[0]의 비교를 바꿔주시면 됩니다.
def next(self):
cash = self.broker.get_cash()
value = self.broker.get_value()
size = int(cash / self.data.close[0])
# Order가 Pending인지 확인, 그렇다면 다시 주문할 수 없음
if self.order:
return
if not self.position: # not in the market
if self.smaSlow < self.data.close[0]:
self.order = self.buy(size=size)
elif self.getposition().size > 0: #in the market
if self.smaSlow > self.data.close[0]:
self.order = self.sell(size=self.getposition().size)
그렇게 변경 후 실행해보면 결과는 이렇습니다.
마지막으로 매도 포지션을 잡고 난뒤 캐쉬가 111만원이 남았습니다. 11% 수익으로 앞의 전략과 꽤 차이가 있습니다.
혹 어느분께서는 23%나 11%나 둘 다 돈 번 것 아니냐? 돈 벌면 됐지 뭐가 중요하냐고 하실 수도 있겠지만
이는 결국에는 특정 기간동안 우상향한 삼성전자라는 주식의 특성상 두 전략 모두 돈을 번 것이지 다른 주식, 다른 기간이었으면 그렇지 못한 경우도 수두룩 빽빽일 수 있습니다. 물론 둘 다 돈을 벌어 다행이지만 강조 하고 싶은 것은 주가란 오르고 내리고를 반복하는 성질, 즉 추세라는 것이 존재하고 우리는 이를 거스를 수가 없다는 것입니다.
한번 떨어지기 시작한 주식은 그 하락폭을 가늠할 수가 없어서 하락 추세에 내가 매수 포지션을 잡았다고 하면 손실이 없으면 다행이지만 떨어지면 또 언제까지 떨어질지 모릅니다. 그렇기 때문에 하락폭이 크게 열려있는 구조입니다.
그 반면 추세를 따르는 매매를 할 경우, 상승추세에 매수하여 큰 상승을 기대할 수 있는 상승폭을 크게 열어놓은 구조라는 장점이 있습니다.
그렇다고 해서 추세반대 전략이 잘못된 전략이라는 말은 절대 아닙니다. 당연히 수익이 있을 수 있습니다.
하지만 구조적으로 추세추종 전략은 하락폭은 제한된 반면 상승폭은 크게 열려있고 추세반대 전략은 하락폭이 크게 열려있고 상승폭이 제한(비싸다고 판단하면 팔아버리기에 상승중인 추세에 더 큰 상승의 효과를 보지 못함)되어 있다는 점에서 장기적으로 보았을 때 추세추종 전략이 더 높은 손익비를 가져올 것으로 기대합니다.
같이 보시면 좋을 글
2021/02/10 - [파이썬/주식 자동매매] - 주식매매프로그램 개발 노하우, 소스를 담은 전자책이 발간되었습니다.
댓글