저번 포스팅에서는 MLP를 사용하였는데, 이번 DQN 아키텍쳐는 past close price를 input으로 받아서 future close price를 output으로 뱉도록 구성하였습니다. 사실 MLP를 적용하는 것도 엄청나게 많이 응용이 될 수 있습니다. deterministic하게 price를 뱉는 게 아니라 확률분포(Gaussian이라던지..)를 도출하도록 짤 수도 있고, 내일 오를 확률과 내릴 확률을 도출하도록 짤 수도 있고, 이 이외에도 생각해보면 엄청 많이 모델을 만들 수 있을 겁니다.
그렇지만 위와 같은 다양한 아키텍쳐를 통해서 궁극적으로 실현하고자 하는 것은 결국 정보량의 우위를 통한 'Beat the market'이잖아요. 그런데 그렇게 되면 뉴럴넷의 output이 자동으로 매매를 해주는 것이 아니기 때문에 이것저것 직접 모델을 짜주어야 하는 부분이 많습니다.그러나 이의 과정을 모두 생략하고, 자동으로 매매까지 해주는!
Deep RL - 그 중에서도 DQN 작성해보도록 하겠습니다.
사실 Policy Gradient Method를 써도 되고 (DDPG, PPO 등등), Actor-critic, A3C 등등을 사용해도 되기는 합니다만, 이미 작성해놓은 코드가 있는 관계로ㅋㅋ 코드 정리도 할 겸 작성해보려고 합니다.
DQN에 관한 설명은 부가로 하지는 않겠습니다. 제가 사용한 DQN 모델은
"Human-level control through deep reinforcement learning" (Mnih et al, 2015) 논문 버전입니다. (Experience Replay + Target Network의 분리)가 적용된 모델입니다.
관심 있으신 분들은 논문 참조하시면 좋겠네요! 링크 달아놓겠습니다.
Link - https://web.stanford.edu/class/psych209/Readings/MnihEtAlHassibis15NatureControlDeepRL.pdf
Link - https://web.stanford.edu/class/psych209/Readings/MnihEtAlHassibis15NatureControlDeepRL.pdf
기본적인 Setting은 다음과 같습니다. 컨셉은 평가일에 평가가치를 가장 높이도록 하는 DQN으로 잡았습니다. 평가일을 굳이 잡은 것은 Episode가 끝나도록 해서 target 값에 Q network값이 더해지지 않고 딱 평가가치를 reward로 받을 수 있게 하기 위함입니다.
평가가치는 position * close price + cash로 나타내도록 하였습니다.
각 state에서의 target 값은 reward(현재의 평가가치) + target_Q Value(Rebalancing 이후 state의 target 값)으로 주어지게 되겠죠!
input은 최근 30일간 종가 + 현재 position(해당 Asset에 대해 얼마나 Long/Short Position인지) + 현재 보유 Cash 로 총 32개를 input으로 받도록 하였습니다.
뉴럴넷의 output은 long / hold / short으로 두고 3차원으로 뱉도록 하였습니다.
Close Price만으로 DQN을 작업하면서 어려웠던 점은 과연 내가 맞게 코드를 짰나...하는 점이었어요ㅠ 예전에 Atari Cartpole-v0 예제로 training 했을 때는 주어진 Environment가 자동으로 뱉어내기 때문에 Env에서 코드실수를 했을 가능성도 줄어들고, 확실히 training 된다는 사실을 알고 있기 때문에 score가 높지 않으면 확실히 어딘가 오류가 있는 것을 알 수 있습니다. 따라서 에러가 있는지 확인하기도 편하고, 에러가 발생할 가능성이 있는 곳도 적어서 훨씬 편했는데 여기서는 training 이후에 성능이 잘 나오지 않다고 하더라도 확인하기가 어려워서 그런지 뭔가 헷갈리기도 하고 어렵네요.. 갈 길이 먼 것 같습니다.
아무튼! 작업한 내용을 정리해보겠습니다.
Close Price는 웹에서 크롤링한 코스피/코스닥 기업 765개(더 많이 하려고 했는데 다운받는데 시간이 너무 오래 걸려서...) 기업을 대상으로 얻은 Close Price 2000개 -> 총 약 150만개 데이터 대상으로 돌려보았습니다.
뉴럴넷은 input 32 / 64 / 16 / 3(output)으로 구성하였습니다. 이는 저번 MLP때 사용한 것과 매우 유사하게 잡았습니다. learning rate은 0.001, discount rate은 0.99, activation은 모두 leaky_relu를 사용하였습니다.
결과를 보도록 하죠. training 500회마다 score를 기록하도록 하였습니다. training 횟수는 총 3만 번으로 잡았습니다.
음 몇 번 운좋게 크게 번 것 빼고는 거의 0 근처에서 왔다갔다합니다. 매일매일 한 주씩 Long / hold / short을 하도록 DQN을 구성했다보니 수익률을 뽑아내기도 좀 애매하고 다른 지표를 보기 위해서 turnover도 확인해 보았습니다.
turnover의 경우 초반에는 굉장히 높게 나왔으나, training이 끝나갈 무렵에는 거의 10을 넘어간 적이 없었습니다. Exit 날짜를 60일로 잡았으니, 최대 60 / -60까지 가능한데 보통 min turnover가 -10 이상이었고, max turnover또한 10 이하로 나왔습니다. 또한, short상태는 거의 유지하지 않고, 항상 long 상태를 유지하되 turnover를 굉장히 낮게 유지하는 전략을 취하는 것처럼 보였습니다.
한 번 실제로 매매를 어떻게 했는지 보죠.
위는 가지고 있는 데이터 중 랜덤한 주식을 뽑아 test set에서 90일을 뽑아서 돌려본 데이터입니다. 앞의 30일은 31일째를 예측하는 데이터로 사용되었습니다. 파란색은 90일간의 데이터이고, 빨간색 점들은 각각 1, 0, -1을 나타내는데요 각각 해당 시점에서 long / hold / short을 했음을 의미합니다.
흥미로운 점은 마치 turnover를 낮게 가져가려는 것처럼 hold할 경우가 굉장히 많고, long을 했다 해도 short를 어느정도 섞는 모습을 보입니다. 아래 적힌 monkey는 같은 데이터에 대해 무작위로 long / hold / short 을 하도록 하였을 때 총 10명의 monkey를 평균 낸 score입니다. 저 위에는 마치 제 DQN이 monkey를 압도한 것처럼 보이지만, 사실 cherry picking이에요ㅋㅋ...
다른 걸 한번 볼까요?
한국가스공사데이터를 바탕으로 돌려본 결과입니다. 흐음ㅋㅋㅋㅋ... 마찬가지로 turnover는 엄청 낮고 short position은 취하지 않는 걸 볼 수 있네요. monkey도 수익을 냈는데 제 알고리즘은 16000원이나 잃었네요. 차후에 training을 더 해보는 편이 좋을 것 같습니다.
monkey랑 비교해봤을 때는 DQN Agent가 3:2 정도로 우세합니다. training set이 아니고 test set인 점을 감안하면 생각보다는 엄청 훌륭하네요. 차후에 training을 더 돌려보고 evaluation을 할 수 있는 방법에 대해 고민이 계속되어야 할 것 같습니다.
뭔가 더 해볼 수 있을 것 같은데 우선 몇 번 더 돌려보고 눈으로 좀 익히고 싶네요.
이 포스팅은 여기에서 마치겠습니다!
댓글 없음:
댓글 쓰기