Загружаем каталог…
Загружаем каталог…
LLM 제안과 규칙 엔진(_guard_decisions)이 나뉘는 판단 흐름 시세 조회와 주문 실행을 분리한 자율매매 아키텍처 지난 글에서는 한국투자증권 Open API 연동 과정에서 만난 버그들을 다뤘다. 이번엔 조금 다른 이야기다. 이 자율매매 엔진은 판단을 어디서, 어떻게 내리는가. 그리고 그 판단을 얼마나 믿어도 되는가. 결론부터 말하면, 이 시스템에서 로컬 LLM은 매매를 결정하지 않는다 . 후보를 제안할 뿐이고, 실제로 주문이 나갈지 말지는 전부 규칙 기반 코드가 정한다. 이 글은 왜 그렇게 설계했는지, 그리고 그 경계선이 코드에서 정확히 어디에 그어져 있는지에 대한 기록이다. 왜 로컬 LLM인가 Ollama로 EXAONE 3.5 7.8B 모델을 돌린다. 클라우드 LLM API를 안 쓰는 이유는 단순하다. 매 사이클(5분)마다 계좌 잔고와 보유 종목, 기술적 지표를 프롬프트에 실어 보내야 하는데, 이걸 외부 API로 매번 보내면 지연 시간도 늘고 계좌 정보가 외부로 나간다. 로컬에서 돌리면 네트워크 의존이 없고, API 요금 걱정 없이 5분마다 마음껏 호출할 수 있다. 대신 대가가 있다. 7.8B급 로컬 모델은 GPT-4급 모델보다 추론 품질이 떨어진다. 이 트레이드오프를 감안하고 설계한 게 다음 절의 핵심이다 — 모델이 틀려도 시스템이 무너지지 않게 만드는 것 . 판단이 흐르는 경로 한 사이클( run_cycle )이 실행될 때 벌어지는 일을 순서대로 적으면 이렇다. 1. 후보 종목 압축 _rank_candidates 가 DB에 있는 후보 종목 전체의 가격 히스토리를 계산해서 기술적 편향 점수( technical_bias.score )를 매긴다. 절대값이 25 미만인 약한 신호는 버리고, 남은 것 중 점수 절대값이 큰 순서로 최대 12개만 추린다. 여기서 이미 대부분의 종목이 걸러진다 — LLM은 전체 종목을 보는 게 아니라 이미 한 번 필터링된 12개짜리 요약을 받는다. 2. 프롬프트 구성 _build_prompt 가 총자산, 현금, 누적수익률, 최대낙폭, 시장 국면, 보유 종목, 그리고 앞서 추린 후보 목록을 텍스트 한 덩어리로 만든다. 프롬프트 안에는 이런 지시문도 박혀 있다. 시장 국면 {market_regime}. structural_decline/recession_rebalance/bearish 국면에서는 신규 매수를 제안하지 마라. 위험 한도를 지키면서 가용 현금을 전액 분산하도록 매수 후보를 충분히 선택하라. 3. 모델 호출 ask_local_model 이 Ollama의 /api/generate 에 system 프롬프트와 user 프롬프트를 실어 던진다. temperature 0.1 로 낮게 고정해서 매 사이클마다 결과가 크게 요동치지 않게 한다. 응답 안에서 json ... 블록을 정규식으로 뽑아내는데( extract_json_block ), 이 파싱이 실패하면 그냥 decisions 가 빈 리스트로 처리된다 — 모델이 이상한 형식으로 답해도 시스템이 에러로 죽지 않고 조용히 "이번엔 제안 없음"으로 넘어간다. 핵심: LLM은 제안만 하고, 결정은 코드가 한다 모델이 뽑아낸 decisions 는 그대로 실행되지 않는다. _guard_decisions 라는 규칙 엔진을 반드시 통과해야 한다. 이 함수가 하는 일은 크게 두 갈래다. 첫째, LLM과 무관하게 강제로 발동하는 룰이 있다. 보유 종목의 수익률이 손절 기준(기본 -5%)에 닿으면 LLM이 뭐라고 하든 무조건 매도 결정을 만들어 넣는다. 익절 기준(기본 +12%)에 닿아도 마찬가지로 절반 매도를 강제한다. 이 두 개는 모델의 제안 목록과 별도로, 코드가 직접 만들어서 앞에 끼워 넣는 결정이다. 둘째, LLM이 제안한 매수/매도 후보들은 아래 조건을 전부 통과해야 실제 주문으로 이어진다. 일일 손실 한도(기본 -3%)를 넘긴 상태면 신규 매수 전체 차단 시장 국면이 bearish / structural_decline / recession_rebalance 로 분류되면 신규 매수 전체 차단 기술점수가 25 미만이면 차단 ( weak_signal ) 당일 등락률이 +15% 이상이면 추격 매수 차단 ( price_spike ) 종목당 비중이 설정된 최대치를 넘으면 차단 ( position_limit ) 최근 체결한 종목이면 쿨다운 시간 동안 재매수 차단 ( cooldown ) 목표 노출 비중(시장 국면별로 계산되는 값)에 이미 도달했으면 차단 ( target_exposure_reached ) 사이클당 최대 주문 개수를 넘으면 차단 ( cycle_order_limit ) 차단된 항목은 그냥 버려지는 게 아니라 blocked_decisions 에 사유 코드와 함께 기록된다. 그래서 "이번 사이클에 왜 안 샀는지"를 나중에 그대로 다시 읽을 수 있다. 실제로 이 로그 덕분에 이전 글에서 다룬 대량 미체결 문제(유동성 무시한 사이징)를 원인까지 추적할 수 있었다. 이 이중 구조를 한 문장으로 요약하면: LLM은 "이 종목이 좋아 보인다"고 말할 수 있을 뿐이고, "실제로 사도 되는지"는 8개 넘는 하드코드 조건이 전부 통과해야 결정된다. 모델이 헛소리를 해도 손절/익절 강제 룰과 국면 차단, 비중 한도가 그 위에 얹혀 있어서 계좌 전체가 한 번의 나쁜 추론으로 무너지지 않는다. 이것도 한계가 있다 규칙 엔진이 안전판 역할을 하긴 하지만, 만능은 아니다. 모델이 보는 건 텍스트 요약뿐이다. 호가창 깊이, 실시간 뉴스, 거시 지표는 프롬프트에 없다. 가격 히스토리에서 뽑은 기술적 지표와 손익 숫자가 전부다. 규칙 자체가 틀릴 수 있다. 손절/익절 임계값, 국면 분류 로직, 비중 한도 — 이 숫자들은 전부 사람이 정한 값이다. 실제로 이전 글에서 워크포워드 백테스트를 돌려보니 이 조합의 수익률이 마이너스였다. 규칙이 안전하다는 것과 규칙이 돈을 번다는 것은 다른 문제다. 파싱 실패는 조용히 넘어간다. JSON 추출이 실패하면 그냥 제안이 없는 걸로 처리되는데, 이건 안전한 방향의 실패이긴 하지만 모델이 왜 실패했는지는 로그만 보고는 알기 어렵다. 7.8B 모델의 추론 한계. 프롬프트에 명시한 "국면별 매수 금지" 같은 지시를 무시하고 매수를 제안하는 경우도 있다 — 그래서 애초에 국면 차단을 프롬프트 지시가 아니라 _guard_decisions 코드 레벨에서 한 번 더 강제한다. 모델을 신뢰하지 않고 이중으로 막은 것 자체가 이 한계를 인정한 설계다. 정리하면, 이 프로젝트에서 로컬 LLM의 역할은 "똑똑한 트레이더"가 아니라 "정량 필터를 통과한 후보 중에서 그럴듯한 이유를 골라주는 조수"에 가깝다. 실제 안전장치는 전부 평범한 if문으로 짜여 있다. 다음 글에서는 이 규칙들이 실전에서 어떤 결과를 냈는지, 백테스트 숫자를 좀 더 파고들어 볼 예정이다. 이 프로젝트는 학습/실험 목적의 모의투자 시스템이며, 실제 투자 조언이 아닙니다. 코드는 개인 레포에서 관리 중입니다. GitHub 저장소: sunwoo8478/kis-paper-trading
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
로컬 LLM을 자율매매 판단에 어떻게 붙였나. LLM 제안과 규칙 엔진(_guard_decisions)이 나뉘는 판단 흐름 시세 조회와 주문 실행을 분리한 자율매매 아키텍처 지난 글에서는 한국투자증권 Open API 연동 과정에서 만난 버그들을 다뤘다. 이번엔 조금 다른 이야기다. 이 자율매매 엔진은 판단을 어디서, 어떻게 내리는가. 그리고 그 판단을 얼마나 믿어도 되는가. 결론부터 말하면, 이 시스템에서 로컬 LLM은 매매를 결정하지 않는다 . 후보를 제안할 뿐이고, 실제로 주문이 나갈지 말지는 전부 규칙 기반 코드가 정한다. 이 글은 왜 그렇게 설계했는지, 그리고 그 경계선이 코드에서 정확히 어디에 그어져 있는지에 대한 기록이다. 왜 로컬 LLM인가 Ollama로 EXAONE 3.5 7.8B 모델을…
Открыть источник로컬 LLM을 자율매매 판단에 어떻게 붙였나. LLM 제안과 규칙 엔진(_guard_decisions)이 나뉘는 판단 흐름 시세 조회와 주문 실행을 분리한 자율매매 아키텍처 지난 글에서는 한국투자증권 Open API 연동 과정에서 만난 버그들을 다뤘다. 이번엔 조금 다른 이야기다. 이 자율매매 엔진은 판단을 어디서, 어떻게 내리는가. 그리고 그 판단을 얼마나 믿어도 되는가. 결론부터 말하면, 이 시스템에서 로컬 LLM은 매매를 결정하지 않는다 . 후보를 제안할 뿐이고, 실제로 주문이 나갈지 말지는 전부 규칙 기반 코드가 정한다. 이 글은 왜 그렇게 설계했는지, 그리고 그 경계선이 코드에서 정확히 어디에 그어져 있는지에 대한 기록이다. 왜 로컬 LLM인가 Ollama로 EXAONE 3.5 7.8B 모델을…
Открыть источник