AI는 코드를 빠르게 만들어준다. 하지만 빠르게 만든 코드가 곧 믿을 수 있는 코드는 아니다. 투자 분석에서 코드 오류는 단순한 버그로 끝나지 않는다. 수익률이 과장되고, 손실이 숨겨지고, 실제로는 불가능한 전략이 그럴듯하게 보일 수 있다.
그래서 AI 코딩의 핵심은 "대신 짜달라"가 아니다. 핵심은 "검증 가능한 초안을 빠르게 만들고, 사람이 확인할 수 있는 구조로 다듬는 것"이다. AI를 잘 쓰는 투자자는 질문을 구체적으로 만들고, 중간 결과를 확인하고, 오류의 원인을 추적하고, 재현 가능한 형태로 코드를 남긴다.
AI 생성 이미지
AI에게 코드를 요청하는 기본 원칙
AI에게 "주식 분석 코드 짜줘"라고 요청하면 대체로 그럴듯한 코드가 나온다. 문제는 그 코드가 내 데이터에 맞는지, 내가 원하는 계산을 하는지 알기 어렵다는 점이다.
좋은 요청에는 최소한 세 가지가 들어가야 한다.
첫째, 입력 데이터의 형태다. 예를 들어 데이터프레임 이름이 df이고 컬럼이 Date, Open, High, Low, Close, Volume인지 알려줘야 한다.
둘째, 계산할 내용이다. 단순 수익률인지 로그 수익률인지, 20일 이동평균인지 60일 이동평균인지, MDD와 샤프 비율까지 계산할 것인지 명확히 적어야 한다.
셋째, 원하는 출력 형태다. 새 컬럼을 추가할지, 요약 표를 만들지, 차트를 그릴지, 함수로 만들지 정해야 한다.
예를 들어 나쁜 요청은 이렇다.
애플 주가 분석 코드 만들어줘.
좋은 요청은 이렇다.
pandas 데이터프레임
df가 있고 인덱스는 날짜, 컬럼은Open,High,Low,Close,Volume입니다.Close기준으로 일간 수익률, 누적 수익률, 20일 이동평균, 60일 이동평균을 계산하는 코드를 작성해주세요. 결과는 기존df에 새 컬럼으로 추가하고, 마지막에는df.tail()로 결과를 확인하게 해주세요. 결측치는 이동평균 계산 때문에 생기는 초기 구간만 허용합니다.
이렇게 요청하면 AI는 훨씬 덜 추측한다. 또한 결과를 주피터 노트북에서 한 번에 실행하지 말고 셀 단위로 나누어 실행해야 한다. 데이터 불러오기, 컬럼 확인, 지표 계산, 결측치 확인, 시각화를 분리하면 어디서 문제가 생겼는지 바로 알 수 있다.
print(df.columns)
print(df.head())
print(df.isna().sum())AI가 만든 코드라도 이 세 줄은 자주 실행해야 한다. 투자 데이터 분석에서 대부분의 오류는 복잡한 모델보다 단순한 컬럼명 불일치, 날짜 정렬 오류, 결측치 처리 실패에서 시작된다.
LLM API를 파이썬 프로젝트에 연결하기
챗봇 화면에서 코드를 복사해 쓰는 것만으로도 많은 일을 할 수 있다. 하지만 나중에는 파이썬 프로젝트 안에서 LLM을 직접 호출하고 싶어진다. 예를 들어 매일 관심 종목 뉴스를 요약하거나, 백테스트 결과를 투자 메모 형태로 정리하거나, 전략 설정값을 JSON으로 생성하게 만들 수 있다.
이때 API 키를 코드에 직접 쓰면 안 된다. API 키는 비밀번호와 같다. 깃허브에 올라가거나 노트북 파일에 남으면 다른 사람이 사용할 수 있다. 안전한 기본 방식은 환경 변수에 저장하는 것이다. OpenAI 파이썬 클라이언트는 OPENAI_API_KEY 환경 변수를 읽어 사용할 수 있다.1
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="사용 가능한 최신 모델명으로 교체",
input="pandas로 일간 수익률을 계산하는 간단한 예제를 보여줘."
)
print(response.output_text)여기서 모델명은 고정된 정답이 아니다. API 제공자는 새 모델을 출시하거나 기존 모델명을 바꿀 수 있다. 따라서 실제 프로젝트에서는 사용하는 서비스의 공식 문서에서 현재 지원되는 모델명을 확인한 뒤 넣어야 한다. 예제 코드의 핵심은 특정 모델명이 아니라, API 키를 코드에 직접 쓰지 않고 클라이언트 호출부를 분리하는 구조다.
터미널에서는 운영체제에 따라 다음처럼 환경 변수를 설정한다.
# macOS / Linux
export OPENAI_API_KEY="your_api_key_here"# Windows PowerShell
setx OPENAI_API_KEY "your_api_key_here"프로젝트에서는 API 호출 부분을 분석 코드와 분리하는 편이 좋다. 예를 들어 llm_client.py에 LLM 호출 함수를 두고, 노트북에서는 그 함수를 불러와 사용한다. 이렇게 하면 모델명, 프롬프트, 예외 처리를 한곳에서 관리할 수 있다.
def ask_llm(prompt: str) -> str:
try:
response = client.responses.create(
model="사용 가능한 최신 모델명으로 교체",
input=prompt
)
return response.output_text
except Exception as e:
return f"LLM 호출 실패: {e}"중요한 점은 LLM 호출 결과를 곧바로 매매 판단에 연결하지 않는 것이다. LLM은 리서치 문장, 코드 초안, 요약, 체크리스트를 만드는 데 유용하다. 그러나 최종 계산과 검증은 파이썬 코드와 사람이 맡아야 한다.
오류 메시지를 제대로 설명하게 만들기
오류가 났을 때 많은 사람이 오류 메시지만 복사해서 AI에게 묻는다.
이 오류 고쳐줘.
이 방식도 어느 정도는 통한다. 하지만 금융 데이터 분석에서는 환경과 데이터 형태가 중요하다. 같은 오류 메시지라도 pandas 버전, 인덱스 타입, 컬럼명, 결측치 여부에 따라 원인이 달라질 수 있다.
AI에게 오류를 물을 때는 다음 정보를 함께 제공하자.
실행 환경: 파이썬 버전, 주피터 노트북 여부
주요 라이브러리 버전: pandas, numpy, scikit-learn 등
입력 데이터 예시:
df.head().to_dict()또는df.head()출력실행한 코드
전체 오류 메시지
기대한 결과와 실제 결과
예시 프롬프트는 다음과 같다.
아래 코드는 주가 데이터에서 20일 이동평균과 매수 신호를 만드는 코드입니다. 기대한 결과는
ma20과signal컬럼이 추가되는 것입니다. 하지만 오류가 발생합니다. 원인을 설명하고, 수정 코드와 재발 방지 체크리스트를 제시해주세요.환경: Python 3.12, pandas 2.x, Jupyter Notebook
데이터 컬럼:['date', 'close', 'volume']
코드: ...
오류 메시지: ...
AI에게 "수정 코드만" 요구하지 말고 "왜 틀렸는지"를 함께 요구해야 한다. 그래야 다음에 비슷한 오류를 직접 고칠 수 있다.
디버깅에서 가장 좋은 습관은 작은 데이터로 재현하는 것이다. 전체 10년치 데이터를 넣기 전에 5행짜리 샘플 데이터로 같은 오류가 나는지 확인하자.
sample = df.head(5).copy()
print(sample)작은 샘플에서 코드가 맞게 작동하면 전체 데이터로 확장한다. 작은 샘플에서도 틀리면 로직 자체가 잘못된 것이다.
백테스트 코드를 요청할 때 반드시 적을 것
AI에게 백테스트 코드를 요청할 때는 더 조심해야 한다. 백테스트 코드는 겉보기에는 잘 돌아가도 내부 로직이 틀릴 수 있다. 특히 미래 데이터를 사용하거나, 포지션이 중복으로 열리거나, 수수료가 반영되지 않으면 성과가 크게 왜곡된다.
백테스트 프롬프트에는 다음을 넣어야 한다.
진입 규칙: 언제 매수하거나 매도할지
청산 규칙: 언제 포지션을 닫을지
포지션 중복 여부: 이미 보유 중이면 새 포지션을 열지 않을지
손절과 익절 조건
수수료와 슬리피지
리밸런싱 주기
출력 형식: 거래 내역, 누적 수익률, MDD, 샤프 비율, 차트 등
실제 ChatGPT를 활용한 백테스트 사례에서도 사용자는 EMA, ATR, 진입·청산 조건, 손절·익절, 기존 포지션이 있을 때 새 포지션을 만들지 않는 조건, 수수료와 슬리피지, 손익 곡선과 성과 통계를 구체적으로 요청했다.2 중요한 것은 AI가 코드를 만들었다는 사실이 아니라, 사람이 각 조건이 실제 코드에 반영됐는지 확인했다는 점이다.
예시 프롬프트는 다음과 같다.
df에는 날짜 인덱스와Open,High,Low,Close,Volume컬럼이 있습니다.
20일 이동평균을 계산하고, 종가가 20일 이동평균 위로 올라가면 다음 날 시가에 매수합니다. 종가가 20일 이동평균 아래로 내려가면 다음 날 시가에 매도합니다. 이미 포지션이 있으면 추가 매수하지 않습니다. 수수료는 매수·매도 각각 0.05%, 슬리피지는 매수·매도 각각 0.03%로 반영합니다.
출력은 거래 내역 데이터프레임, 일별 자산 곡선, 누적 수익률, MDD, 샤프 비율로 만들어주세요. 미래 데이터 사용이 없도록 신호와 체결 시점을 분리해주세요.
AI가 만든 백테스트 코드를 받은 뒤에는 다음을 직접 확인해야 한다.
# 날짜 정렬 확인
assert df.index.is_monotonic_increasing
# 신호가 다음 날 수익률에 적용되는지 확인
# 오늘 종가로 만든 신호를 오늘 수익률에 곱하면 미래 데이터 사용일 수 있다.
df["strategy_return"] = df["signal"].shift(1) * df["return"]
# 결측치 확인
print(df[["signal", "return", "strategy_return"]].isna().sum())특히 shift(1)은 자주 등장하는 안전장치다. 오늘 장이 끝난 뒤 알 수 있는 신호라면, 실제 매매는 적어도 다음 시점부터 가능하다고 보는 것이 자연스럽다. 물론 분봉, 일봉, 종가 체결 가정에 따라 달라질 수 있으므로 전략 설명과 코드가 일치해야 한다.
구조화 출력은 API 계약처럼 다루기
AI에게 전략 아이디어를 표나 JSON으로 받는 일도 많다. 예를 들어 AI에게 여러 전략 후보를 만들게 하고, 각 전략의 이름, 필요한 데이터, 진입 조건, 청산 조건, 위험 요인을 JSON으로 받는 식이다.
이때 JSON은 "그럴듯한 텍스트"가 아니라 "프로그램이 읽을 계약"으로 다뤄야 한다. 필수 필드가 빠지거나, 예상하지 못한 키가 들어오거나, 허용하지 않은 값이 들어오면 코드가 조용히 틀어질 수 있다.
파이썬에서는 Pydantic 같은 도구로 LLM 출력의 구조를 검증할 수 있다. 구조화된 LLM 출력은 가능하면 제공자 수준에서 형식을 강제하고, 파이썬에서도 다시 검증하는 편이 안전하다.3
from typing import Literal
from pydantic import BaseModel, Field, ConfigDict
class StrategySpec(BaseModel):
model_config = ConfigDict(extra="forbid")
name: str = Field(description="전략 이름")
asset_universe: list[str]
signal_type: Literal["momentum", "value", "moving_average"]
rebalance: Literal["daily", "weekly", "monthly"]
entry_rule: str
exit_rule: str
risk_notes: list[str]extra="forbid"는 예상하지 못한 필드가 들어오면 거부한다. Literal은 허용할 값을 제한한다. 예를 들어 rebalance에는 daily, weekly, monthly만 들어올 수 있다.
구조화 출력 기능은 서비스와 SDK 버전에 따라 호출 방식이 달라질 수 있다. 어떤 환경에서는 JSON 스키마를 직접 넘기고, 어떤 환경에서는 Pydantic 모델을 바로 지정할 수 있다. 따라서 아래 코드는 개념 예시로 보고, 실제 코드는 사용하는 LLM 제공자의 최신 문서에 맞춰 조정해야 한다.
# 개념 예시: 실제 인자 이름은 SDK 버전에 따라 달라질 수 있다.
response = client.responses.create(
model="사용 가능한 최신 모델명으로 교체",
input="월간 리밸런싱 모멘텀 전략 아이디어를 하나 만들어줘.",
# response_format 또는 text_format 등에 StrategySpec/JSON Schema를 지정
)
# 응답 텍스트 또는 JSON을 받은 뒤에는 반드시 Pydantic으로 다시 검증한다.
# spec = StrategySpec.model_validate(parsed_json)검증에 실패하면 무조건 통과시키지 말아야 한다. 오류 내용을 AI에게 다시 보내 "수정된 JSON만 출력하라"고 요청할 수는 있다. 다만 재시도 횟수는 제한해야 한다. 계속 실패하면 사람이 프롬프트나 스키마를 고쳐야 한다.
AI가 만든 코드를 신뢰하기 전 확인할 것
AI 코딩에서 가장 위험한 태도는 "실행됐으니 맞겠지"라고 생각하는 것이다. 실행되는 코드와 맞는 코드는 다르다. 특히 투자 분석에서는 숫자가 나왔다는 이유만으로 믿으면 안 된다.
AI가 만든 코드는 다음 순서로 확인하자.
작은 샘플 데이터에서 작동하는가?
입력 컬럼과 출력 컬럼이 명확한가?
결측치와 이상값을 어떻게 처리하는가?
날짜 인덱스가 정렬되어 있는가?
미래 데이터를 사용하지 않는가?
수수료, 슬리피지, 세금 등 비용 가정이 반영되어 있는가?
벤치마크와 비교할 수 있는가?
같은 코드를 다시 실행했을 때 같은 결과가 나오는가?
간단한 테스트 코드도 도움이 된다.
def test_return_calculation():
prices = pd.Series([100, 110, 99])
returns = prices.pct_change()
assert round(returns.iloc[1], 4) == 0.1000
assert round(returns.iloc[2], 4) == -0.1000이런 테스트는 거창하지 않아도 된다. 수익률 계산, MDD 계산, 리밸런싱 날짜 선택처럼 핵심 함수 몇 개만 검증해도 실수를 크게 줄일 수 있다.
재현 가능한 AI 코딩 환경 만들기
AI와 함께 코딩할수록 기록이 중요해진다. 오늘은 잘 돌아간 코드가 한 달 뒤에는 돌아가지 않을 수 있다. 패키지 버전이 바뀌었거나, 데이터 파일이 바뀌었거나, 프롬프트가 달라졌을 수 있다.
프로젝트 폴더에는 최소한 다음을 남기자.
ai_quant_lab/
data/
raw/
processed/
notebooks/
01_data_check.ipynb
02_backtest_ma.ipynb
src/
indicators.py
backtest.py
llm_client.py
tests/
test_indicators.py
prompts/
code_generation.md
debug_template.md
requirements.txt
README.mdrequirements.txt에는 패키지 버전을 남긴다. 아래 버전은 예시일 뿐이며, 실제로는 본인의 환경에서 작동을 확인한 버전을 기록해야 한다.
pandas==2.2.3
numpy==2.1.0
scikit-learn==1.5.2
pydantic==2.9.2
openai==사용한_버전랜덤성이 있는 모델을 쓸 때는 시드도 고정한다.
import numpy as np
RANDOM_SEED = 42
np.random.seed(RANDOM_SEED)주피터 노트북에는 실행 순서를 명확히 남겨야 한다. 중간 셀을 건너뛰고 실행해야만 결과가 나오는 노트북은 좋은 노트북이 아니다. 위에서 아래로 다시 실행했을 때 같은 결과가 나와야 한다.
프롬프트도 저장해두자. 어떤 질문으로 AI가 코드를 만들었는지 남겨야 나중에 수정할 수 있다. AI가 만든 코드를 그대로 믿지 않는 것만큼, AI에게 무엇을 시켰는지 기록하는 것도 중요하다.
AI 코딩의 목표는 속도가 아니라 검증 가능성이다
AI는 투자자의 코딩 속도를 크게 높여준다. 데이터 불러오기, 지표 계산, 차트 작성, 오류 해석, 테스트 코드 초안 작성까지 많은 일을 도와준다. 하지만 AI가 빠르게 만들어준 코드일수록 더 차분하게 확인해야 한다.
좋은 AI 코딩 습관은 단순하다.
요청은 구체적으로 한다.
코드는 단계별로 실행한다.
중간 결과를 확인한다.
오류는 맥락과 함께 질문한다.
백테스트는 가정과 체결 시점을 점검한다.
구조화 출력은 스키마로 검증한다.
프로젝트는 다시 실행 가능하게 남긴다.
이 원칙을 지키면 AI는 위험한 자동 조종 장치가 아니라 유능한 개발 파트너가 된다. 투자 아이디어를 코드로 바꾸는 시간은 줄이고, 검증에 쓰는 시간은 늘릴 수 있다. 퀀트 투자에서 중요한 것은 더 많은 코드를 쓰는 것이 아니라, 믿을 수 있는 실험을 반복하는 것이다.
참고
1How to Integrate ChatGPT's API With Python Projects
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.
