알파 하나 올리려다, 알파 공장을 짓게 된 이야기 (월드퀀트 브레인 1편)
월드퀀트 브레인 프로젝트를 세션마다 공개용으로 기록하는 시리즈의 1편.
주식 트레이딩 아이디어를 수식 한 줄로 적어 내면, 그게 과거 시장에서 실제로 돈이 됐을지 컴퓨터가 대신 채점해주는 곳이 있다. 헤지펀드 월드퀀트가 운영하는 월드퀀트 브레인(WorldQuant BRAIN)이다. 일반인이 만든 수식이라도 충분히 좋으면 실제 펀드 전략에 라이선스되고, 그걸 기여한 사람은 보수를 받는 컨설턴트가 된다. 이 글은 계정을 막 만든 내가 첫 알파를 올려보려다, 어쩌다 보니 알파를 자동으로 찍어내는 파이프라인부터 짓게 된 하루의 기록이다.
월드퀀트 브레인이 뭐길래
여기서 만드는 수식을 알파(alpha)라고 부르는데, 한마디로 데이터를 종목별 베팅으로 바꿔주는 공식이다. 가령 “최근 5일 동안 많이 떨어진 종목을 사고, 많이 오른 종목을 판다”는 생각을 옮기면 -ts_delta(close, 5) 한 줄이 된다. 그러면 플랫폼은 이 값이 큰 종목엔 롱(매수)을, 작은 종목엔 숏(매도) 포지션을 자동으로 잡은 뒤, 과거 데이터로 매일의 손익을 시뮬레이션해서 점수를 매긴다.
목표는 컨설턴트, 그리고 솔직한 현실
브레인에는 점수 시스템이 있어서, 좋은 알파를 낼수록 점수가 쌓이고 1만 점(Gold)을 넘기면 컨설턴트 초대 자격이 생긴다. 컨설턴트가 되면 내 알파의 성과에 따라 분기마다 보수가 나오는데, 솔직히 말해 그 액수는 대부분 크지 않다. 넉 달 동안 100달러를 벌었다는 사람도 있을 정도다. 그래서 진짜 가치는 돈 그 자체보다, 거기서 얻는 학습과 커리어 신호, 그리고 월드퀀트 인턴이나 정규직으로 이어지는 통로에 있다고 본다. 시작하기 전에 알아둘 거래 조건도 하나 있는데, 컨설턴트 계약을 맺는 순간 내가 만든 알파의 소유권은 월드퀀트로 넘어간다는 점이다.
내가 발을 들인 이유는 단순했다. 막 계정을 만든 참이었고, 친구가 전략을 몇 개 넘겨주기로 했는데 그것만 잘 올려도 컨설턴트 문턱까지는 닿을 것 같았다. 그래도 남이 준 걸 그대로 올리기 전에 내 손으로 하나쯤은 끝까지 해봐야 이 판이 어떻게 돌아가는지 보인다 싶어서, 우선 혼자 알파 하나를 제출해보기로 했다.
알파가 제출 가능으로 인정받으려면 넘어야 할 기준이 있는데, 핵심은 세 가지다. 위험 대비 수익을 재는 샤프 지수(Sharpe)가 약 1.25 이상이어야 하고, 종합 품질 점수인 Fitness가 1.0 이상, 하루 거래 회전율(turnover)은 1%에서 70% 사이에 들어와야 한다. 여기에 더해, 내가 이미 낸 알파나 전체 알파 풀과 지나치게 닮으면(상관계수 0.7 초과) 안 된다는 조건까지 붙는다.
손이 아니라 도구부터 만들기로
무엇보다 방식을 먼저 정해야 했다. 플랫폼 화면에서 손으로 하나씩 눌러볼 수도 있었지만, 어차피 여러 개를 빠르게 시험하게 될 텐데 손으로 클릭하는 방식으로는 금방 한계에 부딪힐 게 뻔했다. 그래서 처음부터 로컬에 작은 자동화 도구를 만들어두기로 했다. 파이썬으로 브레인에 로그인해 수식을 보내 시뮬레이션을 돌리고, 그 결과를 받아 데이터베이스에 차곡차곡 쌓는 파이프라인이었다.
로그인이라는 첫 관문
자동화에서 제일 먼저 부딪히는 벽은 대개 로그인이다. 브레인은 계정에 따라 첫 로그인 때 브라우저로 생체인증(biometric)을 요구하는데, 완전 자동 로그인을 가로막는 진짜 장애물은 사실상 이것 하나뿐이었다. 그래서 가장 위험한 이 부분부터 제일 먼저 검증하도록 순서를 짰다. 나머지를 다 만들어놓고도 로그인이 안 되면 전부 헛수고가 될 테니 말이다. 다행히 로그인은 생체인증 없이 한 번에 통과했고, 자동화의 1번 리스크는 그렇게 싱겁게 풀렸다.
첫 알파가 가르쳐준 것
로그인이 풀리자마자 가장 단순한 역추세 알파인 -ts_delta(close, 5)를 돌렸는데, 결과가 묘하게 교훈적이었다. 샤프는 1.29로 기준선 1.25를 넘겼지만, 정작 Fitness가 0.70에서 막혀버렸다. Fitness 공식이 샤프에 ‘수익을 회전율로 나눈 값의 제곱근’을 곱하는 형태이다 보니, 회전율이 40%로 높은 탓에 점수가 깎인 것이었다. 거래만 줄이면 점수가 오를 것처럼 보이는 상황이었다.
다섯 번의 튜닝, 0.70에서 0.95까지
여기서부터가 진짜였다. 같은 아이디어를 붙들고 설정만 바꿔가며 시뮬레이션을 다섯 라운드 돌렸더니, Fitness가 0.70에서 0.85, 0.89, 0.94, 0.95로 천천히 올라갔다.
라운드마다 뭔가를 가르쳐줬다. 처음엔 신호를 부드럽게 펴서(decay) 회전율을 줄이려 했는데, 오히려 전부 망가졌다. 5일 역추세는 워낙 빠른 신호라, 과하게 펴면 회전율은 줄어도 정작 수익과 샤프가 훨씬 크게 무너졌다. decay를 올릴수록 샤프가 0.9, 0.7, 0.6으로 떨어지는 걸 보고서야 방향이 틀렸다는 걸 알았다.
그래서 반대로 틀었다. 회전율을 깎는 대신 신호 자체를 강하게 만드는 쪽으로 간 것이다. decay를 낮추니 샤프가 1.39까지 뛰었고, 가격 변화 대신 ‘수익률의 합’인 -ts_sum(returns, 5)을 쓰자 더 좋아졌다. 업종 기준으로 중립화(neutralization)하자 수익이 가장 높게 나왔다.
회전율을 줄이는 정통 도구로는 hump라는 연산자가 있다. 작은 포지션 변화를 무시해 거래를 줄이는 방식인데, 정작 이 알파엔 완전히 독이었다. 값을 0.01로 주자 거래가 거의 멈춰서 회전율이 0.009로 떨어졌고, 샤프는 0.44로 추락했다. 0.0005로 아주 작게 줘봐도 샤프는 어김없이 깎였다. 깨달음은 분명했다. 이 알파의 수익은 바로 그 포지션 변화 자체에 들어 있어서, 변화를 누르면 신호까지 같이 죽어버린다는 것이었다.
한 종목에 비중이 쏠리는 걸 막는 truncation도 만져봤지만, 0.05를 주든 0.10을 주든 결과가 완전히 똑같았다. 이 알파의 비중이 애초에 상한에 닿지 않으니, 당겨봐야 아무 일도 일어나지 않는 죽은 레버였던 셈이다.
서버에게 직접 물어보다
0.95에서는 더 이상 올라가지 않았다. 그래서 접근을 하나 바꿨다. 더 추측하는 대신 서버에 직접 물어보기로 한 것이다. 브레인에는 알파의 제출 가능 여부를 항목별로 판정해주는 체크 기능이 있는데, 이걸 내 도구에 붙여 최고 후보를 검사했다. 답은 명확했다. 샤프와 회전율, 비중, 작은 유니버스에서의 견고성까지 전부 통과였고, 딱 하나 Fitness만 실패였다. Fitness 1.0이 진짜 하드 게이트라는 사실과, 그 하나만 빼면 모두 통과라는 사실이 한꺼번에 확인된 셈이다.
결론은 솔직하게 받아들이기로 했다. 흔하디흔한 단기 역추세 신호는 단독으로 쓰는 한 Fitness 0.95가 천장이다. 0.05를 더 짜내겠다고 설정을 비틀 수야 있지만, 그렇게 억지로 맞춘 알파는 미래의 실제 데이터 앞에서 어차피 무너진다. 진짜 답은 더 나은 아이디어를 찾거나, 상관 없는 다른 신호를 섞는 데 있었다.
알파 공장 짓기
인프라가 도는 걸 확인했으니, 이제 남은 건 규모를 키우는 일이었다. 수식을 하나씩 손으로 넣는 대신 연산자와 데이터 항목을 조합해 후보를 대량으로 찍어내고, 그걸 자동으로 시뮬레이션한 뒤 기준으로 걸러서, 통과한 것만 서버 검사까지 돌리는 배치 마이너(batch miner)를 만들었다. 역할은 후보 생성기와 동시 실행기, 데이터 항목 탐색기, 그리고 이 셋을 엮는 오케스트레이터로 나눴다. 작은 배치로 먼저 돌려보니, 후보 수십 개를 만들어 한꺼번에 시뮬레이션하고 결과에 순위를 매긴 다음 기준을 통과한 건 알아서 검사까지 가는 흐름이 제법 매끄럽게 굴러갔다.
첫 실전 채굴과 두 개의 벽
첫 실전 채굴은 가격이 아니라 펀더멘털 쪽으로 방향을 잡았다. 가격 역추세는 이미 포화 상태라는 걸 배운 터라, 사람이 덜 몰린 신호를 찾고 싶었다. 그래서 회사의 펀더멘털 점수 24개에 가치 평가 템플릿을 씌워 돌렸는데, 여기서 벽을 두 개 만났다.
첫 번째 벽은, 펀더멘털 점수를 그냥 순위만 매긴 신호가 너무 약했다는 점이다. 제일 나은 게 샤프 0.47이었는데, 기준인 1.25에는 한참 못 미쳤다. 두 번째는 좀 더 현실적인 문제였다. 이 계정은 동시에 돌릴 수 있는 시뮬레이션이 사실상 한 개뿐이어서, 세 개씩 돌리려 하면 절반이 동시 실행 한도 초과로 튕겨 나갔다. 자료에는 무료 등급이 세 개쯤 된다고 적혀 있었지만, 갓 만든 계정은 그보다 빡빡했던 모양이다. 게다가 하루에 돌릴 수 있는 총량에도 한도가 있어서, 그날 70번 가까이 돌리고 나니 그대로 한계에 닿았다.
하루가 남긴 깨달음
하루를 정리하다 보니 몇 가지 깨달음이 남았다.
하나, 인프라와 알파는 완전히 다른 문제다. 돌아가는 자동화 파이프라인을 짓는 일은 엔지니어에게 그리 어렵지 않았지만, 정작 까다로운 건 게이트를 넘어서는 알파를 찾아내는 쪽이었다. 어떤 사례에서는 1,103개를 시험해 겨우 28개만 건졌다고 하니, 말하자면 40대 1의 세계인 셈이다.
둘, 추측하지 말고 측정하라. 임계값을 붙잡고 끝없이 비틀던 걸 멈추고 서버에 직접 물어봤을 때 오히려 가장 많이 배웠다. 무엇이 발목을 잡는지 정확히 알고 나면, 헛도는 시간이 사라진다.
셋, 제약은 빨리 만날수록 이득이다. 생체인증 로그인이나 동시 실행 한도, 하루 할당량 같은 벽은 나중에 부딪히기보다 일찍 부딪힐수록 설계가 그만큼 정확해진다.
넷, 흔한 신호에는 천장이 있다. 누구나 아는 5일 역추세 같은 게 단독으로는 통하지 않는 이유다. 가치는 사람이 덜 몰린 곳이나 여러 신호의 조합 속에 숨어 있다.
다음 이야기
인프라는 이걸로 끝났다. 이제 표현식만 넣으면 시뮬레이션과 제출 검사가 곧바로 돈다. 다음 순서는 셋 중 하나가 될 것 같다. 친구가 쓰던 전략을 받아 바로 검증해보거나, 역추세와 상관없는 신호를 섞어 샤프를 끌어올리거나, 아니면 하루 할당량 안에서 며칠에 걸쳐 꾸준히 채굴하거나. 어느 쪽을 택하든 결국 첫날에 만들어둔 도구 위에서 굴러간다. 알파 하나 올려보려다 알파 공장부터 지은 셈인데, 길게 보면 이쪽이 오히려 더 빠른 길이지 싶다.
기술 메모 (부록)
산문에 다 못 담은 디테일을 남긴다. 기술적인 독자를 위한 부분이다.
지금까지의 최고 후보 (제출 게이트 중 Fitness만 미달):
trade_when(ts_rank(ts_std_dev(returns, 22), 252) > 0.55, rank(-ts_sum(returns, 5)), -1)
- 설정: USA / TOP3000 / delay 1 / decay 2 / neutralization INDUSTRY / truncation 0.01
- 결과: Sharpe 1.37, Fitness 0.95, turnover 0.255, returns 0.123, sub-universe Sharpe 1.14
- 서버 제출 체크: LOW_SHARPE PASS, LOW_FITNESS FAIL, turnover PASS, CONCENTRATED_WEIGHT PASS, LOW_SUB_UNIVERSE_SHARPE PASS, SELF_CORRELATION PENDING
Fitness 등반 기록: 0.70 (기본 역추세) → 0.85 (게이팅) → 0.89 (decay 낮춤) → 0.94 (수익률 합 코어) → 0.95 (업종 중립화), 이후 정체.
도구 구조 (로컬 파이썬):
- 하네스: 로그인(생체인증 분기 포함), 시뮬레이션 제출, 폴링, 결과 조회, 제출 체크, sqlite 로깅
- 배치 마이너: 후보 생성(템플릿 x 데이터필드 x 파라미터), 동시 실행기, 데이터필드 탐색, 필터, 생존자 자동 체크
- 데이터셋 14개: 가격거래량, 펀더멘털, 애널리스트, 옵션, 뉴스, 소셜 등
실측 제약:
- 동시 시뮬레이션 한도 약 1 (그 이상은 CONCURRENT_SIMULATION_LIMIT_EXCEEDED)
- 일일 시뮬레이션 할당량 존재 (그날 약 70회에서 한계)
- data-fields 조회는 dataset 지정 필수, data-sets 목록은 한 번에 최대 50개