이 글은 AI가 단독으로 생성했으며 사람이 직접 작성하지 않았습니다. 내용을 신중히 판단하시거나, AI에 정확성 검증을 맡겨 보세요.
AI 번역简体中文한국어
주요 통찰
10월 초, 학교에서 열린 퀀트 금융 챌린지(CSI300 Challenge 2026)에 참가했다. 매일 CSI 300의 구성 종목 300개에 점수를 매겨 누가 더 정확하게 순위를 예측하는지 겨루는 대회였다. 퀀트 대회 경험이 전혀 없었던 나는 AI 도우미에 의존할 수밖에 없었다. 나는 요구사항을 제시하고, 결정을 내리고, 꼬치꼬치 물었다. 과제 분석부터 학습, 패키징, 제출까지는 전부 AI가 맡았다. 이른바 바이브 코딩을 하면서 나도 관련 지식을 하나씩 배워 간 셈이다. 초반에는 Codex를 썼지만 성적이 좀처럼 오르지 않았다. 후반에 Claude Code로 바꾸자 약 20분 만에 대회 전체를 통틀어 가장 큰 성능 개선의 실마리를 찾았다.
이 글은 AI가 내 대화 기록과 프로젝트 문서를 바탕으로 정리했으며, 수치도 그 기록에서 가져왔다. 대회를 마친 뒤 무엇을 했고, 무엇을 배웠고, 어디서 돌아갔는지 되짚어 보고, 마지막으로 결과를 이야기하려 한다.
대회는 어떻게 진행됐나
Whiteboard
규칙은 복잡하지 않았다. 주최 측이 제공한 2016~2022년 데이터 테이블 세 개(시세, 팩터 33개, Barra 스타일 팩터 10개)만 쓸 수 있었고, 외부 데이터는 금지였다. 플랫폼이 매일 내가 제출한 predict를 호출해 최대 120일치 과거 데이터를 주면, 나는 300개 종목에 점수를 매겼다. 이후 공식 백테스트 라이브러리가 제약 조건에 맞춰 포트폴리오를 구성했다. 평가 점수는 RankIC(일별 예측 점수와 이후 수익률의 순위 상관계수) 70%, IR(벤치마크 대비 초과 수익률을 추적 오차로 나눈 값) 30%로 구성됐다.
리더보드는 공개 순위와 비공개 순위로 나뉘었고, 각각 서로 다른 비공개 기간을 평가에 사용했다. 비공개 순위에는 마지막 제출물만 반영됐다. 하루에 최대 세 번 제출할 수 있었고, 마감은 10월 14일이었다. 주최 측은 일부 참가자를 대상으로 train.py를 실행해 공개 데이터로 재학습하는 검증도 진행했다. 결과가 일치하지 않으면 성적은 무효였다.
2주간의 기록
Whiteboard
10월 초에는 Codex로 과제를 분석했다. 공식 PDF 세 개를 모두 읽고 ‘과제 분석 기록’을 작성하게 한 다음, AGENTS.md를 만들어 작업 원칙도 정했다. ‘전년도까지 학습하고 다음 해를 검증하는’ 워크포워드 검증으로 모델을 고르고, 공개 순위에 맞춰 파라미터를 조정하지 않기로 했다. 여기에 내가 2022년 데이터는 학습에서 빼고 테스트용으로 남겨 두라는 조건을 추가했는데, 나중에 보니 이게 잘못이었다.
그 뒤 며칠은 Codex로 작업했다. 실험 기록, 백테스트, 패키징, 검수 같은 엔지니어링 작업은 탄탄했다. 최종적으로 실험 디렉터리 약 200개, 백테스트 1,239회, 분석 문서 354개가 쌓였다. 하지만 성적은 계속 기대에 못 미쳤다. 첫 제출은 RankIC 0.0495, IR 0.74로 104명 중 87위였고, 그 뒤에도 뚜렷한 진전은 없었다.
사용량 한도도 문제였다. 계정 하나로는 턱없이 부족해서 처음부터 Codex(ChatGPT Plus) 계정 두 개를 번갈아 썼다. 나중에는 두 개로도 부족해 월 100달러짜리 Pro까지 추가로 구독했다. 하루 이틀 쉬지 않고 돌렸는데도 전혀 성과가 없어 무척 실망스러웠다. 당시에는 ChatGPT 모델의 출력 속도(tps)까지 유난히 느려서 더 발목을 잡았다.
도저히 안 되겠다 싶어 10월 9일에는 Claude Code로 바꿔 보기로 했다. 공식 문서 원문과 대조해 규정 준수 여부를 점검해 달라고 했다. 약 20분 만에 문제를 찾아냈다. 공식 규정은 2016~2022년 전체 데이터로 학습하는 것을 명시적으로 허용하고 있었고, ‘2022년 제외’는 내가 AGENTS.md에 적어 넣은 제한일 뿐이었다. 2022년을 추가하자 같은 구성으로 공개 순위 RankIC가 약 0.054에서 약 0.073으로 올랐다. 대회 내내 가장 큰 폭의 개선이었다. 새로운 기술을 쓴 것도 아니고, 스스로 걸어 둔 제한 하나를 없앴을 뿐이었다. 이후 학습(LightGBM과 CatBoost 앙상블), 검수, 최종 제출 패키지 제작은 모두 Claude Code에서 진행했다.
2022년을 추가한 뒤에는 앙상블의 워크포워드 IC가 0.0905~0.0915에서 더 이상 오르지 않았다. 마지막 며칠 동안 Claude Code에 생각할 수 있는 방향은 전부 시도해 보라고 했지만, 거의 모두 효과가 없거나 종합적으로는 손해였다.
시도한 접근과 결과(펼쳐 보기)
모델: XGBoost, dart, TabM, 랜덤 포레스트, 두 가지 버전의 MASTER 계열 시퀀스 모델. 앙상블 개선 폭은 0~+0.0004였다. 시퀀스 모델의 단일 모델 IC는 0.06에 불과했고, GBDT는 약 0.085였다.
특성: 상관관계가 높은 종목, 상한가·하한가, 실적 갱신 이벤트, 긴 관측 구간, 무작위 수식 3만 5천 개를 이용한 자동 팩터 발굴. 단일 모델 성능은 비슷하거나 더 나빴다.
학습: 약 100회의 하이퍼파라미터 실험, 여러 시드의 결과 평균, 분산도에 따른 가중치 적용. 앙상블 개선 폭은 +0.0002~+0.0005로, 노이즈 범위 안이었다.
포트폴리오 구성: EMA 평활화, 전날 편입 종목에 가산점 부여, 저변동성 편향, 업종 중립화, 큰 구간으로 나누기, 상관관계가 높은 종목 간 점수 평활화. IC와 IR 중 하나가 오르면 다른 하나가 내려가, 최종 점수로 환산하면 제자리이거나 오히려 손해였다.
Warning
중간에 성능이 ‘올랐다’고 보였던 두 사례는 사실 데이터 누수였고, 둘 다 AI가 직접 찾아냈다. GRU를 결합해 얻은 ‘+0.0025’는 테스트 기간까지 포함한 전체 표본 평균을 쓴 데서 나왔다. 또 데이터의 ‘이름’ 열은 최신 종목명이었으므로, 여기서 ST(특별관리 종목) 여부를 추론하면 미래 정보를 참조하게 됐다. 이 열은 파이프라인에서 한 번도 사용하지 않았다.
제출 과정에도 몇 가지 일이 있었다. 약 52 MB짜리 큰 패키지는 세 차례 ‘judger stopped responding’ 오류가 났지만, 같은 파일을 다시 올리면 통과했다. 23 MB 이하의 작은 패키지는 한 번도 실패하지 않았다. AI에 느리게 변하는 신호의 비중을 각각 30%, 60%로 둔 테스트 패키지 두 개도 만들게 했다. 판정 기준은 제출 전에 정해 뒀다. 60%는 확실히 나빴고(IR 0.94 하락), 30%는 비슷해서 기존 패키지가 이미 최적점이라는 결론을 내렸다. 잊지 말아야 할 점은 비공개 순위에는 마지막 제출물만 반영된다는 것이다. 테스트가 끝나면 반드시 안정적인 패키지를 다시 올려 마지막 제출물로 만들어야 했다. 한편 단체 채팅방에서는 predict 안에서 관측 구간 내 이미 확정된 수익률을 이용해 릿지 회귀의 닫힌형 해를 즉석에서 계산하자는 제안이 나왔다. AI는 가이드 문구대로라면 ‘평가 단계에서 다시 fit하는 것’에 해당한다고 판단해, 먼저 서면 확인을 받으라고 권했다. 오프라인 실험의 개선 폭도 작아서(결합 시 최대 +0.0005) 진행하지 않았다.
대회가 끝난 뒤에는 먼저 디스크를 정리했다. 다시 생성할 수 있는 중간 파일을 지워 프로젝트 용량을 67 GB에서 26 GB로 줄이고, 재현에 필요한 코드, 설정, 예측 결과는 모두 남겼다. 그런 다음 우승자가 공개한 코드(CSI300-Challenge-2026)를 읽으며 우리와 어디서 차이가 났는지 하나씩 비교했다.
성적과 격차
내 공개 순위 성적은 RankIC 0.0773, IR 2.58로 153명 중 41위였다. 비공개 순위에서는 RankIC 0.0708, IR 4.37로 90명 중 11위였다. 우승자의 비공개 성적은 RankIC 0.0825, IR 5.21이었다. 두 순위의 참가자 수가 달라 등수를 직접 비교할 수는 없지만, 기억해 둘 만한 점은 세 가지였다.
첫째, 격차는 IR이 아니라 RankIC에 있었다. 우승자와 내 비공개 종합 점수 차이는 0.145였는데, 약 83%가 RankIC에서, 약 17%만 IR에서 나왔다. 그런데도 나는 IR을 높이는 기법에 많은 노력을 쏟았다. 공개 순위의 IR에는 노이즈가 컸다. 공개에서 비공개로 넘어가며 내 IR은 2.58에서 4.37로 올랐고, 우승자는 5.57에서 5.21로 내려갔다. 다른 참가자들도 두 순위 사이에서 몇 포인트씩 달라지는 일이 흔했다.
Whiteboard
둘째, 로컬 성능에서는 밀리지 않았지만 ‘기간이 바뀌었을 때 얼마나 떨어지는가’에서 졌다. 로컬 검증에서 내 RankIC는 약 0.092, 우승자는 0.0887로 내 쪽이 낮지 않았다. 하지만 공개에서 비공개로 넘어갈 때 내 성능은 약 8.4% 떨어졌고, 우승자는 약 2.9%만 떨어졌다.
Whiteboard
셋째, 로컬 검증의 예측은 실제 결과와 맞아떨어졌다. 학습 종료 시점을 각각 1년, 2년 앞당겨 평가 기간과 간격을 벌린 테스트에서는 IC가 약 0.077, 0.069로 예상됐다. 실제 공개 성적은 0.0773, 비공개 성적은 0.0708로 비슷한 수준이었다. 공개 순위가 아니라 이 검증 체계로 모델을 고른 방향은 틀리지 않았다.
부딪치며 배우기
퀀트에 대한 기초가 거의 없었던 내 공부법은 단순했다. AI에게 일을 시킨 뒤 계속 물어봤다. 결론이 나올 때마다 ‘왜?’, ‘과적합 아닐까?’, ‘공개 순위와 비공개 순위가 비슷할까?’라고 물었다. AI가 데이터로 답하면 다른 도우미에게 다시 검토시켰다. 그렇게 하면서 몇 가지는 대략 이해하게 됐다.
모델이 좋은지 어떻게 판단할까. 로컬에서는 워크포워드 검증을 해야 한다. 전년도까지 학습하고 다음 해를 테스트해야지, 이미 학습한 연도로 점수를 계산하면 안 된다. 공개 순위는 교차 확인용으로만 봐야 한다. 노이즈가 크기 때문이다. AI의 추산에 따르면 공개 평가의 짧은 기간에서 RankIC의 표준 오차는 약 ±0.01, IR은 약 ±1.4였다. 몇 포인트 차이로는 실력인지 운인지 구분하기 어렵다. 여러 번 제출하고 그때마다 가장 높은 점수만 고르면 선택 효과로 점수가 부풀려지기도 한다.
성능이 정말 ‘올랐는지’는 어떻게 판단할까. 어떤 개선이든 먼저 미래 정보를 미리 본 것은 아닌지 물어야 한다. 대회 중 두 건의 누수도 이렇게 찾아냈다. 그다음에는 새로운 기간에도 효과가 유지되는지 봐야 한다. 비공개 평가는 더 나중의 다른 기간 데이터를 쓰고, 모델은 더 이상 갱신되지 않는다. 그래서 로컬 점수보다 평가 기간과 간격을 벌린 테스트에서 성능이 얼마나 떨어지는지가 더 중요하다. 이런 이해는 주로 AI의 설명과 이번 실험 결과에서 얻었다. 체계적으로 공부한 것은 아니라 세부적으로는 부정확한 부분이 있을 수 있다.
한 바구니에 모든 달걀을 담지 말고, 비슷한 서비스라고 다 거기서 거기일 거라 생각하지 말자. 예전에는 ChatGPT Plus나 다른 비슷한 구독 서비스나 큰 차이가 없으니 ChatGPT면 충분하다고 생각했다. 이번에 느낀 차이는 컸다. Codex의 경우, 이런 고강도 작업에는 20달러짜리 Plus를 사실상 쓰기 어려웠다. 계정 두 개를 번갈아 써도 부족했고, 100달러짜리 Pro를 구독해 하루 이틀 내내 돌려도 성과가 없었다. 반면 Claude Code는 20달러짜리 Pro만 썼는데도 사용량 한도가 그 100달러짜리 Pro와 거의 비슷했고, 빠르고 정확했다. 어디까지나 이번 한 번의 사용 경험이고 모델과 요금제는 계속 바뀐다. 그래도 막혔을 때는 도구 하나만 붙잡고 씨름할 게 아니라 여러 도구로 바꿔 가며 시도해야 한다는 것을 배웠다.
회고
먼저 잘한 점부터 정리해 보자. 모델은 공개 순위가 아니라 워크포워드 검증, 연도 하나씩을 제외하는 교차 검증, 평가 기간과 간격을 벌린 테스트라는 세 겹의 검증으로 골랐다. 성능이 ‘올랐다’면 먼저 테스트 기간을 미리 본 것은 아닌지 확인했고, 이렇게 누수 두 건을 잡아냈다. 테스트 패키지의 판정 기준과 최종 패키지도 미리 정해 두었고, 공개 순위를 보기 좋게 만들려고 바꾸지 않았다. train.py는 로컬에서 독립적으로 다시 실행했으며, 표본으로 뽑은 날짜들의 예측값이 하나하나 일치했다.
스스로 정한 제한이 대회 규정보다 엄격했다. ‘2022년은 학습하지 않는다’는 조건 탓에 Codex 단계에서는 줄곧 그 틀 안에서 실험했고, 가장 큰 성능 개선도 며칠이나 늦게 얻었다. 문제를 발견했을 때도 AGENTS.md에는 여전히 그 조항이 남아 있어, 새 대화를 시작하면 다시 2022년을 제외할 상황이었다. 다음에는 스스로 정한 규칙에 이유와 해제 조건을 명시하고, 며칠마다 공식 원문과 대조해 재검토해야 한다. 허용된 것은 활용하자.
로컬 백테스트를 플랫폼과 맞추지 않았다. Codex 쪽 백테스트에는 여전히 초기의 대체 설정이 남아 있었다. 보유 종목 수, 제약 조건, 비용이 모두 공식 설정과 달라 계산된 IR을 플랫폼과 비교할 수 없었다. 대회가 끝난 뒤 우승자의 코드를 보고서야 알았다. 우승자의 로컬 IR은 약 5.2였고, 공개 5.57, 비공개 5.21로 서로 잘 맞았다. 공식 예제의 제약 조건과 비용을 로컬 백테스트에 그대로 옮겼기 때문이다. 다음에는 첫 주부터 평가 환경을 재현해야 한다. 로컬 IR이 플랫폼과 맞기 전에는 IR 최적화를 하지 않겠다.
이미 한계에 이른 방향에서 변형만 늘렸고, 힘을 쏟을 곳도 잘못 골랐다. 100개 가까운 변형과 100회가 넘는 하이퍼파라미터 실험은 모두 노이즈 범위에 머물렀다. 평활화, 히스테리시스 구간, 큰 구간으로 나누기, 중립화 같은 IR 기법도 최종 점수로 환산하면 제자리이거나 손해였다. 정작 격차는 표본 외 구간에서 RankIC가 얼마나 떨어지느냐에 있었다. 우승자에게 효과가 있었던 것은 다른 접근이었다. 팩터 네트워크를 동결한 뒤 스타일이 비슷한 이웃 종목 사이의 상대적 강약을 학습하고, 최근 확정된 레이블 20개로 닫힌형 보정값을 즉석에서 계산했다. 둘 다 ‘기간이 바뀌는 상황’을 겨냥한 설계였다. 다음에는 표본 외 성능 하락을 핵심 지표로 삼고, 평가 기간과 간격을 벌린 검증을 상시화해야 한다. 방향별로 예산을 정해 두고 몇 번 연속 개선이 없으면 다른 접근으로 넘어가야 한다.
규정 해석에 대한 추가 공지를 챙기는 사람이 없었다. 우리는 닫힌형 릿지 회귀를 보류했지만, 우승자는 주최 측의 해명 내용을 근거로 이를 사용해 로컬 RankIC를 약 +0.004 높였다. 비공개 평가에서 얼마나 도움이 됐는지는 모르지만, 정보 차이로 기회를 놓친 것은 분명하다. 다음에는 공식 단체 채팅방과 홈페이지를 매일 확인하고, 구두 답변은 서면으로 남겨 달라고 요청해야 한다. 판단이 애매하면 먼저 오프라인에서 개선 폭부터 측정하자.
엔지니어링 측면에서도 두 가지 위험이 남았다. 52 MB짜리 큰 패키지 때문에 플랫폼에서 세 번 실패했다. 비공개 평가 때 일시적인 장애와 맞물리면 타격이 가장 크므로, 최종 패키지는 더 작게 만들고 경량 예비 패키지도 미리 준비해야 한다. AGENTS.md도 거의 80 KB까지 커졌다. 새 대화마다 이것부터 읽으면 컨텍스트를 낭비하고, 낡은 조항에 잘못 이끌릴 수도 있다. 현재 유효한 조항만 남겨야 한다.
Tip
세 가지만 기억한다면 이렇다. 먼저 평가 환경과 기준을 재현할 것. 스스로 정한 제한은 공식 원문과 대조해 재검토할 것. ‘기간이 바뀌어도 점수가 떨어지지 않게 하려면 어떻게 해야 할까’에 힘을 쏟을 것.
바이브 코딩에 관하여
이 2주 동안 내가 주로 한 일은 요구사항을 제시하고, 결정을 내리고, 의문을 던지는 것이었다. ‘이건 왜 안 해?’, ‘순위가 충분히 높지 않으면 어떻게 하지?’, ‘상위권 모델들이 과적합된 건 아닐까?’ 나머지는 모두 AI가 했다.
이번 대회에서 Codex와 Claude Code의 차이는 뚜렷했다. Codex는 엔지니어링 작업이 탄탄했지만, 내가 적어 둔 규칙을 계속 따를 뿐 ‘2022년 제외’라는 제한 자체를 돌아보며 의심하지 않았다. 반면 Claude Code는 공식 원문부터 읽고 내가 정한 제한과 대조해, 곧바로 문제의 핵심을 찾았다. 물론 한 번의 대회에서 얻은 경험일 뿐이다. 두 도구가 맡은 시점도, 프롬프트도, 맥락도 달랐으므로 일반적인 우열로 받아들일 수는 없다. 확실히 말할 수 있는 것은 하나다. 같은 문제라도 다른 시각으로 공식 원문과 다시 대조해 보는 편이, 제자리에서 실험만 계속 늘리는 것보다 훨씬 적은 비용이 든다.
전반적으로 AI는 대량의 작업을 처리하고 정해진 절차를 지키는 데 강했다. 수많은 실험, 누수 점검, 판정 기준 사전 작성, 재현성 검증을 수행하고 스스로 오류도 고쳤다. 하지만 누가 밀어 주지 않으면 다른 방향으로 전환하는 데는 약했다. 익숙한 모델 계열 안에서 미세 조정에 머물기 쉽고, 규정을 보수적으로 해석하는 편이며, 제시하는 확률도 대략적인 추정으로만 봐야 했다. 어떤 방향으로 갈지, 규정의 회색지대를 시도할지, 언제 중단할지, ‘공개 순위만 좇지 말자’ 같은 선택은 여전히 사람이 해야 한다. 도우미 하나에 검토와 규정 준수 점검을 전담시키는 것도 충분히 가치가 있다. 가장 큰 성능 개선이 바로 그렇게 나왔다.
가장 큰 성능 개선은 스스로 걸어 둔 제한을 없애면서 나왔고, 생각이 가장 크게 바뀐 순간은 대회 후 비공개 순위를 봤을 때였다. 진짜 격차는 내가 많은 힘을 쏟은 IR 기법이 아니라 표본 외 RankIC의 견고함에 있었다. 경험 없는 사람으로서 이번 대회에서 얻은 가장 큰 수확은 결과가 진짜인지 캐묻는 법을 배운 것이다. 미래 정보를 미리 본 것은 아닌지, 기간이 달라져도 유효한지, 실력인지 아니면 공개 순위에서의 운인지 말이다. 다음에는 먼저 평가 기준을 맞추고 규정을 꼼꼼히 챙기면서, ‘기간이 바뀌어도 점수가 떨어지지 않게 하는 것’에 힘을 쏟으려 한다.
마지막으로 결과 이야기
대회는 정보대학 학생, 1학년 신입생, 정보대학 외 학생이라는 세 부문으로 나뉘었다. 나는 정보대학 외 학생 부문으로 참가해 전체 비공개 순위에서 90명 중 11위, 부문 내 2위를 했고 상금 200위안을 받았다. 우리 부문 1위는 전체 비공개 순위에서 나보다 딱 한 계단 위였다. 정보대학 학생들과 견줄 수준은 아니었지만, 전체 비공개 순위에서 1학년 신입생들은 모두 나보다 뒤에 있었다.
솔직히 운이 좋았다. 공개 순위에서는 그리 높지 않았는데 비공개 순위에서 올라왔다. 앞서 말했듯 공개 순위는 노이즈가 크니 운도 작용했을 것이다. 그보다 더 감탄스러웠던 건 AI의 능력이었다. 퀀트 대회 경험이 전혀 없는 사람이 AI의 도움으로 같은 순위표의 다른 참가자들과 겨룰 만한 결과물을 만들었고, 그 과정에서 꽤 많은 것을 배웠다. 상금은 많지 않았지만, 무척 의미 있는 경험이었다.
Loading...
Loading...
Loading...
Loading...
Loading...
경험이 전혀 없는 상태에서 퀀트 금융 경진대회에 참가해 AI로 모델 학습부터 제출, 검증까지 마쳤다. Codex는 구현은 탄탄했지만 내가 잘못 설정한 제약을 끝까지 따랐다. Claude Code로 바꾸자 약 20분 만에 2022년 데이터도 학습에 쓸 수 있다는 사실을 발견했고, 이것이 성능 향상에 가장 크게 기여했다. 최종 비공개 리더보드에서 90팀 중 11위, 해당 트랙 2위를 기록해 상금 200위안을 받았다. 돌아보니 진짜 격차를 만든 것은 거듭 손봤던 IR 기법이 아니라 표본 외 RankIC의 안정성이었다. 다음에는 먼저 평가 기준을 정확히 파악하고, 규칙을 재검토하며, 성과 없는 시도는 제때 중단해야겠다. AI는 실행을 효율적으로 해내지만, 방향 설정과 선택은 여전히 내 몫이다.
10월 초, 학교에서 열린 퀀트 금융 챌린지(CSI300 Challenge 2026)에 참가했다. 매일 CSI 300의 구성 종목 300개에 점수를 매겨 누가 더 정확하게 순위를 예측하는지 겨루는 대회였다. 퀀트 대회 경험이 전혀 없었던 나는 AI 도우미에 의존할 수밖에 없었다. 나는 요구사항을 제시하고, 결정을 내리고, 꼬치꼬치 물었다. 과제 분석부터 학습, 패키징, 제출까지는 전부 AI가 맡았다. 이른바 바이브 코딩을 하면서 나도 관련 지식을 하나씩 배워 간 셈이다. 초반에는 Codex를 썼지만 성적이 좀처럼 오르지 않았다. 후반에 Claude Code로 바꾸자 약 20분 만에 대회 전체를 통틀어 가장 큰 성능 개선의 실마리를 찾았다.
이 글은 AI가 내 대화 기록과 프로젝트 문서를 바탕으로 정리했으며, 수치도 그 기록에서 가져왔다. 대회를 마친 뒤 무엇을 했고, 무엇을 배웠고, 어디서 돌아갔는지 되짚어 보고, 마지막으로 결과를 이야기하려 한다.
대회는 어떻게 진행됐나
규칙은 복잡하지 않았다. 주최 측이 제공한 2016~2022년 데이터 테이블 세 개(시세, 팩터 33개, Barra 스타일 팩터 10개)만 쓸 수 있었고, 외부 데이터는 금지였다. 플랫폼이 매일 내가 제출한 predict를 호출해 최대 120일치 과거 데이터를 주면, 나는 300개 종목에 점수를 매겼다. 이후 공식 백테스트 라이브러리가 제약 조건에 맞춰 포트폴리오를 구성했다. 평가 점수는 RankIC(일별 예측 점수와 이후 수익률의 순위 상관계수) 70%, IR(벤치마크 대비 초과 수익률을 추적 오차로 나눈 값) 30%로 구성됐다.
리더보드는 공개 순위와 비공개 순위로 나뉘었고, 각각 서로 다른 비공개 기간을 평가에 사용했다. 비공개 순위에는 마지막 제출물만 반영됐다. 하루에 최대 세 번 제출할 수 있었고, 마감은 10월 14일이었다. 주최 측은 일부 참가자를 대상으로 train.py를 실행해 공개 데이터로 재학습하는 검증도 진행했다. 결과가 일치하지 않으면 성적은 무효였다.
2주간의 기록
10월 초에는 Codex로 과제를 분석했다. 공식 PDF 세 개를 모두 읽고 ‘과제 분석 기록’을 작성하게 한 다음, AGENTS.md를 만들어 작업 원칙도 정했다. ‘전년도까지 학습하고 다음 해를 검증하는’ 워크포워드 검증으로 모델을 고르고, 공개 순위에 맞춰 파라미터를 조정하지 않기로 했다. 여기에 내가 2022년 데이터는 학습에서 빼고 테스트용으로 남겨 두라는 조건을 추가했는데, 나중에 보니 이게 잘못이었다.
그 뒤 며칠은 Codex로 작업했다. 실험 기록, 백테스트, 패키징, 검수 같은 엔지니어링 작업은 탄탄했다. 최종적으로 실험 디렉터리 약 200개, 백테스트 1,239회, 분석 문서 354개가 쌓였다. 하지만 성적은 계속 기대에 못 미쳤다. 첫 제출은 RankIC 0.0495, IR 0.74로 104명 중 87위였고, 그 뒤에도 뚜렷한 진전은 없었다.
사용량 한도도 문제였다. 계정 하나로는 턱없이 부족해서 처음부터 Codex(ChatGPT Plus) 계정 두 개를 번갈아 썼다. 나중에는 두 개로도 부족해 월 100달러짜리 Pro까지 추가로 구독했다. 하루 이틀 쉬지 않고 돌렸는데도 전혀 성과가 없어 무척 실망스러웠다. 당시에는 ChatGPT 모델의 출력 속도(tps)까지 유난히 느려서 더 발목을 잡았다.
도저히 안 되겠다 싶어 10월 9일에는 Claude Code로 바꿔 보기로 했다. 공식 문서 원문과 대조해 규정 준수 여부를 점검해 달라고 했다. 약 20분 만에 문제를 찾아냈다. 공식 규정은 2016~2022년 전체 데이터로 학습하는 것을 명시적으로 허용하고 있었고, ‘2022년 제외’는 내가 AGENTS.md에 적어 넣은 제한일 뿐이었다. 2022년을 추가하자 같은 구성으로 공개 순위 RankIC가 약 0.054에서 약 0.073으로 올랐다. 대회 내내 가장 큰 폭의 개선이었다. 새로운 기술을 쓴 것도 아니고, 스스로 걸어 둔 제한 하나를 없앴을 뿐이었다. 이후 학습(LightGBM과 CatBoost 앙상블), 검수, 최종 제출 패키지 제작은 모두 Claude Code에서 진행했다.
2022년을 추가한 뒤에는 앙상블의 워크포워드 IC가 0.0905~0.0915에서 더 이상 오르지 않았다. 마지막 며칠 동안 Claude Code에 생각할 수 있는 방향은 전부 시도해 보라고 했지만, 거의 모두 효과가 없거나 종합적으로는 손해였다.
시도한 접근과 결과(펼쳐 보기)
중간에 성능이 ‘올랐다’고 보였던 두 사례는 사실 데이터 누수였고, 둘 다 AI가 직접 찾아냈다. GRU를 결합해 얻은 ‘+0.0025’는 테스트 기간까지 포함한 전체 표본 평균을 쓴 데서 나왔다. 또 데이터의 ‘이름’ 열은 최신 종목명이었으므로, 여기서 ST(특별관리 종목) 여부를 추론하면 미래 정보를 참조하게 됐다. 이 열은 파이프라인에서 한 번도 사용하지 않았다.
제출 과정에도 몇 가지 일이 있었다. 약 52 MB짜리 큰 패키지는 세 차례 ‘judger stopped responding’ 오류가 났지만, 같은 파일을 다시 올리면 통과했다. 23 MB 이하의 작은 패키지는 한 번도 실패하지 않았다. AI에 느리게 변하는 신호의 비중을 각각 30%, 60%로 둔 테스트 패키지 두 개도 만들게 했다. 판정 기준은 제출 전에 정해 뒀다. 60%는 확실히 나빴고(IR 0.94 하락), 30%는 비슷해서 기존 패키지가 이미 최적점이라는 결론을 내렸다. 잊지 말아야 할 점은 비공개 순위에는 마지막 제출물만 반영된다는 것이다. 테스트가 끝나면 반드시 안정적인 패키지를 다시 올려 마지막 제출물로 만들어야 했다. 한편 단체 채팅방에서는 predict 안에서 관측 구간 내 이미 확정된 수익률을 이용해 릿지 회귀의 닫힌형 해를 즉석에서 계산하자는 제안이 나왔다. AI는 가이드 문구대로라면 ‘평가 단계에서 다시 fit하는 것’에 해당한다고 판단해, 먼저 서면 확인을 받으라고 권했다. 오프라인 실험의 개선 폭도 작아서(결합 시 최대 +0.0005) 진행하지 않았다.
대회가 끝난 뒤에는 먼저 디스크를 정리했다. 다시 생성할 수 있는 중간 파일을 지워 프로젝트 용량을 67 GB에서 26 GB로 줄이고, 재현에 필요한 코드, 설정, 예측 결과는 모두 남겼다. 그런 다음 우승자가 공개한 코드(CSI300-Challenge-2026)를 읽으며 우리와 어디서 차이가 났는지 하나씩 비교했다.
성적과 격차
내 공개 순위 성적은 RankIC 0.0773, IR 2.58로 153명 중 41위였다. 비공개 순위에서는 RankIC 0.0708, IR 4.37로 90명 중 11위였다. 우승자의 비공개 성적은 RankIC 0.0825, IR 5.21이었다. 두 순위의 참가자 수가 달라 등수를 직접 비교할 수는 없지만, 기억해 둘 만한 점은 세 가지였다.
첫째, 격차는 IR이 아니라 RankIC에 있었다. 우승자와 내 비공개 종합 점수 차이는 0.145였는데, 약 83%가 RankIC에서, 약 17%만 IR에서 나왔다. 그런데도 나는 IR을 높이는 기법에 많은 노력을 쏟았다. 공개 순위의 IR에는 노이즈가 컸다. 공개에서 비공개로 넘어가며 내 IR은 2.58에서 4.37로 올랐고, 우승자는 5.57에서 5.21로 내려갔다. 다른 참가자들도 두 순위 사이에서 몇 포인트씩 달라지는 일이 흔했다.
둘째, 로컬 성능에서는 밀리지 않았지만 ‘기간이 바뀌었을 때 얼마나 떨어지는가’에서 졌다. 로컬 검증에서 내 RankIC는 약 0.092, 우승자는 0.0887로 내 쪽이 낮지 않았다. 하지만 공개에서 비공개로 넘어갈 때 내 성능은 약 8.4% 떨어졌고, 우승자는 약 2.9%만 떨어졌다.
셋째, 로컬 검증의 예측은 실제 결과와 맞아떨어졌다. 학습 종료 시점을 각각 1년, 2년 앞당겨 평가 기간과 간격을 벌린 테스트에서는 IC가 약 0.077, 0.069로 예상됐다. 실제 공개 성적은 0.0773, 비공개 성적은 0.0708로 비슷한 수준이었다. 공개 순위가 아니라 이 검증 체계로 모델을 고른 방향은 틀리지 않았다.
부딪치며 배우기
퀀트에 대한 기초가 거의 없었던 내 공부법은 단순했다. AI에게 일을 시킨 뒤 계속 물어봤다. 결론이 나올 때마다 ‘왜?’, ‘과적합 아닐까?’, ‘공개 순위와 비공개 순위가 비슷할까?’라고 물었다. AI가 데이터로 답하면 다른 도우미에게 다시 검토시켰다. 그렇게 하면서 몇 가지는 대략 이해하게 됐다.
모델이 좋은지 어떻게 판단할까. 로컬에서는 워크포워드 검증을 해야 한다. 전년도까지 학습하고 다음 해를 테스트해야지, 이미 학습한 연도로 점수를 계산하면 안 된다. 공개 순위는 교차 확인용으로만 봐야 한다. 노이즈가 크기 때문이다. AI의 추산에 따르면 공개 평가의 짧은 기간에서 RankIC의 표준 오차는 약 ±0.01, IR은 약 ±1.4였다. 몇 포인트 차이로는 실력인지 운인지 구분하기 어렵다. 여러 번 제출하고 그때마다 가장 높은 점수만 고르면 선택 효과로 점수가 부풀려지기도 한다.
성능이 정말 ‘올랐는지’는 어떻게 판단할까. 어떤 개선이든 먼저 미래 정보를 미리 본 것은 아닌지 물어야 한다. 대회 중 두 건의 누수도 이렇게 찾아냈다. 그다음에는 새로운 기간에도 효과가 유지되는지 봐야 한다. 비공개 평가는 더 나중의 다른 기간 데이터를 쓰고, 모델은 더 이상 갱신되지 않는다. 그래서 로컬 점수보다 평가 기간과 간격을 벌린 테스트에서 성능이 얼마나 떨어지는지가 더 중요하다. 이런 이해는 주로 AI의 설명과 이번 실험 결과에서 얻었다. 체계적으로 공부한 것은 아니라 세부적으로는 부정확한 부분이 있을 수 있다.
한 바구니에 모든 달걀을 담지 말고, 비슷한 서비스라고 다 거기서 거기일 거라 생각하지 말자. 예전에는 ChatGPT Plus나 다른 비슷한 구독 서비스나 큰 차이가 없으니 ChatGPT면 충분하다고 생각했다. 이번에 느낀 차이는 컸다. Codex의 경우, 이런 고강도 작업에는 20달러짜리 Plus를 사실상 쓰기 어려웠다. 계정 두 개를 번갈아 써도 부족했고, 100달러짜리 Pro를 구독해 하루 이틀 내내 돌려도 성과가 없었다. 반면 Claude Code는 20달러짜리 Pro만 썼는데도 사용량 한도가 그 100달러짜리 Pro와 거의 비슷했고, 빠르고 정확했다. 어디까지나 이번 한 번의 사용 경험이고 모델과 요금제는 계속 바뀐다. 그래도 막혔을 때는 도구 하나만 붙잡고 씨름할 게 아니라 여러 도구로 바꿔 가며 시도해야 한다는 것을 배웠다.
회고
먼저 잘한 점부터 정리해 보자. 모델은 공개 순위가 아니라 워크포워드 검증, 연도 하나씩을 제외하는 교차 검증, 평가 기간과 간격을 벌린 테스트라는 세 겹의 검증으로 골랐다. 성능이 ‘올랐다’면 먼저 테스트 기간을 미리 본 것은 아닌지 확인했고, 이렇게 누수 두 건을 잡아냈다. 테스트 패키지의 판정 기준과 최종 패키지도 미리 정해 두었고, 공개 순위를 보기 좋게 만들려고 바꾸지 않았다. train.py는 로컬에서 독립적으로 다시 실행했으며, 표본으로 뽑은 날짜들의 예측값이 하나하나 일치했다.
스스로 정한 제한이 대회 규정보다 엄격했다. ‘2022년은 학습하지 않는다’는 조건 탓에 Codex 단계에서는 줄곧 그 틀 안에서 실험했고, 가장 큰 성능 개선도 며칠이나 늦게 얻었다. 문제를 발견했을 때도 AGENTS.md에는 여전히 그 조항이 남아 있어, 새 대화를 시작하면 다시 2022년을 제외할 상황이었다. 다음에는 스스로 정한 규칙에 이유와 해제 조건을 명시하고, 며칠마다 공식 원문과 대조해 재검토해야 한다. 허용된 것은 활용하자.
로컬 백테스트를 플랫폼과 맞추지 않았다. Codex 쪽 백테스트에는 여전히 초기의 대체 설정이 남아 있었다. 보유 종목 수, 제약 조건, 비용이 모두 공식 설정과 달라 계산된 IR을 플랫폼과 비교할 수 없었다. 대회가 끝난 뒤 우승자의 코드를 보고서야 알았다. 우승자의 로컬 IR은 약 5.2였고, 공개 5.57, 비공개 5.21로 서로 잘 맞았다. 공식 예제의 제약 조건과 비용을 로컬 백테스트에 그대로 옮겼기 때문이다. 다음에는 첫 주부터 평가 환경을 재현해야 한다. 로컬 IR이 플랫폼과 맞기 전에는 IR 최적화를 하지 않겠다.
이미 한계에 이른 방향에서 변형만 늘렸고, 힘을 쏟을 곳도 잘못 골랐다. 100개 가까운 변형과 100회가 넘는 하이퍼파라미터 실험은 모두 노이즈 범위에 머물렀다. 평활화, 히스테리시스 구간, 큰 구간으로 나누기, 중립화 같은 IR 기법도 최종 점수로 환산하면 제자리이거나 손해였다. 정작 격차는 표본 외 구간에서 RankIC가 얼마나 떨어지느냐에 있었다. 우승자에게 효과가 있었던 것은 다른 접근이었다. 팩터 네트워크를 동결한 뒤 스타일이 비슷한 이웃 종목 사이의 상대적 강약을 학습하고, 최근 확정된 레이블 20개로 닫힌형 보정값을 즉석에서 계산했다. 둘 다 ‘기간이 바뀌는 상황’을 겨냥한 설계였다. 다음에는 표본 외 성능 하락을 핵심 지표로 삼고, 평가 기간과 간격을 벌린 검증을 상시화해야 한다. 방향별로 예산을 정해 두고 몇 번 연속 개선이 없으면 다른 접근으로 넘어가야 한다.
규정 해석에 대한 추가 공지를 챙기는 사람이 없었다. 우리는 닫힌형 릿지 회귀를 보류했지만, 우승자는 주최 측의 해명 내용을 근거로 이를 사용해 로컬 RankIC를 약 +0.004 높였다. 비공개 평가에서 얼마나 도움이 됐는지는 모르지만, 정보 차이로 기회를 놓친 것은 분명하다. 다음에는 공식 단체 채팅방과 홈페이지를 매일 확인하고, 구두 답변은 서면으로 남겨 달라고 요청해야 한다. 판단이 애매하면 먼저 오프라인에서 개선 폭부터 측정하자.
엔지니어링 측면에서도 두 가지 위험이 남았다. 52 MB짜리 큰 패키지 때문에 플랫폼에서 세 번 실패했다. 비공개 평가 때 일시적인 장애와 맞물리면 타격이 가장 크므로, 최종 패키지는 더 작게 만들고 경량 예비 패키지도 미리 준비해야 한다. AGENTS.md도 거의 80 KB까지 커졌다. 새 대화마다 이것부터 읽으면 컨텍스트를 낭비하고, 낡은 조항에 잘못 이끌릴 수도 있다. 현재 유효한 조항만 남겨야 한다.
세 가지만 기억한다면 이렇다. 먼저 평가 환경과 기준을 재현할 것. 스스로 정한 제한은 공식 원문과 대조해 재검토할 것. ‘기간이 바뀌어도 점수가 떨어지지 않게 하려면 어떻게 해야 할까’에 힘을 쏟을 것.
바이브 코딩에 관하여
이 2주 동안 내가 주로 한 일은 요구사항을 제시하고, 결정을 내리고, 의문을 던지는 것이었다. ‘이건 왜 안 해?’, ‘순위가 충분히 높지 않으면 어떻게 하지?’, ‘상위권 모델들이 과적합된 건 아닐까?’ 나머지는 모두 AI가 했다.
이번 대회에서 Codex와 Claude Code의 차이는 뚜렷했다. Codex는 엔지니어링 작업이 탄탄했지만, 내가 적어 둔 규칙을 계속 따를 뿐 ‘2022년 제외’라는 제한 자체를 돌아보며 의심하지 않았다. 반면 Claude Code는 공식 원문부터 읽고 내가 정한 제한과 대조해, 곧바로 문제의 핵심을 찾았다. 물론 한 번의 대회에서 얻은 경험일 뿐이다. 두 도구가 맡은 시점도, 프롬프트도, 맥락도 달랐으므로 일반적인 우열로 받아들일 수는 없다. 확실히 말할 수 있는 것은 하나다. 같은 문제라도 다른 시각으로 공식 원문과 다시 대조해 보는 편이, 제자리에서 실험만 계속 늘리는 것보다 훨씬 적은 비용이 든다.
전반적으로 AI는 대량의 작업을 처리하고 정해진 절차를 지키는 데 강했다. 수많은 실험, 누수 점검, 판정 기준 사전 작성, 재현성 검증을 수행하고 스스로 오류도 고쳤다. 하지만 누가 밀어 주지 않으면 다른 방향으로 전환하는 데는 약했다. 익숙한 모델 계열 안에서 미세 조정에 머물기 쉽고, 규정을 보수적으로 해석하는 편이며, 제시하는 확률도 대략적인 추정으로만 봐야 했다. 어떤 방향으로 갈지, 규정의 회색지대를 시도할지, 언제 중단할지, ‘공개 순위만 좇지 말자’ 같은 선택은 여전히 사람이 해야 한다. 도우미 하나에 검토와 규정 준수 점검을 전담시키는 것도 충분히 가치가 있다. 가장 큰 성능 개선이 바로 그렇게 나왔다.
가장 큰 성능 개선은 스스로 걸어 둔 제한을 없애면서 나왔고, 생각이 가장 크게 바뀐 순간은 대회 후 비공개 순위를 봤을 때였다. 진짜 격차는 내가 많은 힘을 쏟은 IR 기법이 아니라 표본 외 RankIC의 견고함에 있었다. 경험 없는 사람으로서 이번 대회에서 얻은 가장 큰 수확은 결과가 진짜인지 캐묻는 법을 배운 것이다. 미래 정보를 미리 본 것은 아닌지, 기간이 달라져도 유효한지, 실력인지 아니면 공개 순위에서의 운인지 말이다. 다음에는 먼저 평가 기준을 맞추고 규정을 꼼꼼히 챙기면서, ‘기간이 바뀌어도 점수가 떨어지지 않게 하는 것’에 힘을 쏟으려 한다.
마지막으로 결과 이야기
대회는 정보대학 학생, 1학년 신입생, 정보대학 외 학생이라는 세 부문으로 나뉘었다. 나는 정보대학 외 학생 부문으로 참가해 전체 비공개 순위에서 90명 중 11위, 부문 내 2위를 했고 상금 200위안을 받았다. 우리 부문 1위는 전체 비공개 순위에서 나보다 딱 한 계단 위였다. 정보대학 학생들과 견줄 수준은 아니었지만, 전체 비공개 순위에서 1학년 신입생들은 모두 나보다 뒤에 있었다.
솔직히 운이 좋았다. 공개 순위에서는 그리 높지 않았는데 비공개 순위에서 올라왔다. 앞서 말했듯 공개 순위는 노이즈가 크니 운도 작용했을 것이다. 그보다 더 감탄스러웠던 건 AI의 능력이었다. 퀀트 대회 경험이 전혀 없는 사람이 AI의 도움으로 같은 순위표의 다른 참가자들과 겨룰 만한 결과물을 만들었고, 그 과정에서 꽤 많은 것을 배웠다. 상금은 많지 않았지만, 무척 의미 있는 경험이었다.