개인 프로젝트2026.09

QuantPilot

전략 기반 주식 자동매매

설정한 전략대로 국내 주식을 자동으로 사고파는 1인용 자동매매 앱이다. 실시간 시세에 맞춰 전략이 매수 · 매도를 판단하고, 주문부터 체결 확인 · 손익 반영까지 사람 손 없이 이어진다. 돈이 걸린 만큼 주문 전후에 안전장치도 두었다.

GitHub ↗

QuantPilot 키 비주얼 — 이동평균 교차 매수 · 목표가 매도 신호가 표시된 전략 상세 화면과 대시보드 · 주문 내역 화면

한눈에 보기

기간
2026.09
형태
개인 프로젝트
기술
Python 3.12 · FastAPI · SQLAlchemy(async) · Alembic · Celery · Redis · PostgreSQL · Flutter(Riverpod)

직접 만든 기능

  • 전략 엔진 — 이동평균 교차 · 가격 기준 매매 규칙
  • 시세 수집기와 전략 러너, Celery 주문 작업
  • 리스크 가드 · 비상 정지 · 일일 손실 한도
  • 한국투자증권(KIS) 연동 — 토큰 · 주문 · 체결 확인
  • WebSocket 실시간 전달과 Flutter 앱

화면

백엔드 없이 띄울 수 없어, Flutter 앱을 웹으로 빌드하고 모의(paper) 데이터를 주는 가짜 API에 연결해 찍었다

  • 대시보드

  • 비상 정지 확인

  • 주문 내역

  • 내 전략

대시보드1 / 4

QuantPilot 대시보드 — 오늘 실현손익, 일일 손실 한도, 보유 포지션, 비상 정지 버튼

흐름

주문 실행 흐름

  1. TICK시세 수신
  2. SIGNAL전략 판단
  3. PENDING주문 먼저 기록
  4. BROKER증권사 호출
  5. FILLED체결 반영
  • 같은 주문 ID → 건너뜀이미 처리한 client_order_id는 다시 보내지 않음
  • 비상 정지 중 → 주문 차단실행 직전에 비상 정지를 다시 확인
  • 증권사 오류 → 최대 3번 재시도리스크 오류는 재시도하지 않음
  • 일일 손실 한도 도달 → 비상 정지 자동 발동발동 사유를 감사 로그 테이블에 기록

모든 주문은 한 실행기(OrderExecutionFacade)를 거친다.

문제 해결 과정

주문이 나가기 직전까지 다시 확인 — 실행 파이프라인

  1. 문제

    전략이 주문을 판단한 시점과 실제로 나가는 시점 사이에 비상 정지가 걸리거나, 재시도 때문에 같은 주문이 두 번 나갈 수 있었다

  2. 해결 — 무엇을 바꿨나

    주문 실행을 한 곳으로 모아 ① client_order_id로 이미 처리한 주문인지 확인 ② 실행 직전 비상 정지 재확인 ③ 증권사 호출 전에 PENDING 주문을 먼저 기록 ④ 결과 반영 ⑤ 매수는 평균 단가, 매도는 실현 손익으로 정산하는 순서로 처리했다. 증권사 오류만 최대 3번 재시도하고, 체결 확인은 새로 체결된 수량만 반영해 두 번 세지 않는다

  3. 결과

    실행 시점 기준으로 비상 정지를 지키고, 같은 주문이 두 번 나가지 않는다

손실이 쌓이면 스스로 멈추기 — 일일 손실 한도

  1. 문제

    자동매매는 사람이 보지 않는 동안에도 손실이 계속 쌓일 수 있다

  2. 해결 — 무엇을 바꿨나

    매도 체결마다 실현 손익을 Redis에 한국 날짜별로 누적(INCRBYFLOAT)하고, 한도 아래로 떨어지면 비상 정지를 자동으로 걸어 신규 주문을 막았다. 발동 사유는 감사 로그 테이블에 남기고, 앱에서 해제 · 이력 조회를 할 수 있게 했다

  3. 결과

    하루 손실이 한도에 닿는 순간부터 신규 주문이 나가지 않는다

설계 결정

  • 실전 모드 기동 차단

    기본은 모의(paper) 모드이고, 실전(live) 모드인데 비밀 키가 기본값이거나 증권사 실전 설정이 하나라도 비어 있으면 서버가 아예 켜지지 않는다.

  • 포트 & 어댑터 구조

    기능마다 ports.py(인터페이스)를 두고 DB · Redis · 증권사는 어댑터로 붙여, 리스크 가드와 주문 실행은 테스트에서 메모리 가짜 구현으로 바꿔 끼운다.

  • 증권사 토큰 · 시세 캐시

    KIS 토큰은 만료 5분 전까지만 Redis에 캐시하고 6시간마다 새로 받는다. 시세는 30초 동안 캐시하며 전략 러너에 바로 전달한다.

검증

  • 백엔드 테스트 34개

    리스크 가드 14 · 주문 실행 12 · 설정(실전 모드 차단) 4 · 전략 엔진 2 · 주문 서비스 2개.

  • GitHub Actions CI

    백엔드는 ruff 검사 · 앱 import 확인 · pytest, Flutter 앱은 analyze · test를 돌린다.

← 프로젝트 목록으로