자동매매 시스템을 다시 만들기로 결심한 뒤 가장 먼저 한 일은 매매 전략을 작성하는 것이 아니었다.
먼저 개발 환경부터 다시 정리했다.
이전 버전을 운영하면서 데이터베이스 용량 문제, 복잡해진 구조, 흩어진 문서 때문에 유지보수가 점점 어려워졌기 때문이다. 이번에는 단순히 ‘당장 동작하는 프로그램’이 아니라, 장기간 운영하면서 끊임없이 개선할 수 있는 단단한 구조를 만드는 데 집중하기로 했다.
현재 구축한 개발 환경
현재는 다음과 같은 환경으로 구성되어 있다. 백테스트와 실거래를 모두 감당할 수 있는 최소한의 실용적인 조합이라고 생각한다.
- 운영체제: Windows
- 백엔드 및 매매 엔진: Python 3.11
- 데이터베이스: PostgreSQL 17
- DB 연결 및 마이그레이션: psycopg 3, SQLAlchemy 2, Alembic
- API 서버: FastAPI, Uvicorn
- 거래소 연동: 업비트 REST API, WebSocket
- 스케줄링: APScheduler
- 프론트엔드: React, TypeScript, Vite
- 서버 상태 조회: TanStack Query
- 스타일: Tailwind CSS
기술 스택 선정의 이유
유행하는 기술을 무작정 도입하기보다는, 지금 내 상황과 시스템의 목적에 맞는 기술을 고르는 데 시간을 썼다.
Oracle 대신 PostgreSQL을 선택한 이유
이전에는 Oracle을 사용했다.
Oracle 자체의 성능이나 안정성이 부족해서 변경한 것은 아니다. 가장 큰 이유는 무료 버전의 용량 제한이었다.
업비트에서 수백 개 종목의 실시간 체결 데이터를 수집하다 보면 데이터베이스는 생각보다 훨씬 빠르게 차오른다. 12GB 제한 안에서는 고작 15일 치의 틱(Tick) 데이터만 유지할 수 있었다.
백테스트에는 더 긴 기간의 데이터가 필수적이다. 과거 데이터가 부족하면, 특정 시장 상황에만 우연히 들어맞는 전략을 대단한 전략으로 착각하는 오류를 범할 수 있다.
이번에 도입한 PostgreSQL에서는 틱 데이터를 날짜별 파티션으로 나누어 저장하도록 설계했다.

이 구조를 사용하면 특정 날짜의 데이터를 빠르게 조회할 수 있고, 보관 기간이 지난 데이터도 해당 파티션만 삭제해 정리할 수 있다.
현재는 로컬 디스크 용량을 고려했을 때 1년 이내의 데이터 보관이 가능하다고 판단했다. TimescaleDB 같은 확장 기능도 검토했지만, 현재 규모에서는 PostgreSQL의 기본 파티셔닝만으로 충분하다고 판단해 도입하지 않았다.
Java 대신 Python을 선택한 이유
Java도 충분히 좋은 선택지였다.
장기간 구동되는 서버, 멀티스레드 제어, 강한 타입 시스템이 요구되는 대규모 서비스라면 Java가 더 유리할 수 있다. 하지만 지금 나에게 실행 성능보다 중요한 것은 ‘전략을 빠르게 실험하고 수정하는 사이클‘이었다.
자동매매 개발은 다음 과정의 무한 반복이다.
- 아이디어를 전략 코드로 작성한다.
- 과거 데이터로 백테스트한다.
- 결과를 분석하고 진입/청산 조건을 수정한다.
- 모의투자 환경에서 다시 검증한다.
Python은 상대적으로 적은 코드로 이 과정을 가장 빠르게 반복할 수 있는 언어다. 업비트 REST API 호출, WebSocket 수신, PostgreSQL 읽기/쓰기, 백테스트 로직까지 하나의 언어로 통일할 수 있다는 점도 컸다. 현재 매매 시스템의 주요 병목은 CPU 연산이 아니라 API 호출이나 DB I/O 같은 입출력 작업에 몰려 있다. 현재 시스템의 규모와 입출력 중심 구조에서는 Python으로도 충분한 성능을 확보할 수 있다고 판단했다. 게다가 FastAPI를 활용하면 매매 엔진의 데이터를 프론트엔드로 넘겨주는 API를 아주 간결하게 구축할 수 있다.
결국 Python을 선택한 가장 큰 이유는 “가장 빠른 언어”여서가 아니라, 지금의 나에게 가장 빠르게 실험하고 개선할 수 있는 언어였기 때문이다.
모니터링은 React와 TypeScript
매매 시스템은 거래를 실행하는 것만큼 현재 상태를 정확하게 보여주는 것도 중요하다.
현재 잔고와 보유 종목, 실현 손익, 전략별 성과, 데이터 수집 지연 등을 한눈에 확인할 수 있도록 React와 TypeScript로 모니터링 화면을 구성했다.
Vite를 사용해 개발 환경과 빌드 구성을 단순화했고, 서버 데이터 조회와 주기적인 갱신은 TanStack Query가 담당한다.
실시간성이 필요한 데이터는 짧은 간격으로 조회하고, 자주 변하지 않는 통계 데이터는 더 긴 주기로 조회하도록 분리했다. 모든 데이터를 무조건 1초마다 요청하지 않기 위한 선택이다.
차트는 무엇으로 만들었나
별도로 전략을 분석하는 화면의 캔들 차트에는 TradingView의 Lightweight Charts를 사용했다. 캔들, 가격축, 진입·청산 시점을 표현하기에는 직접 구현하는 것보다 검증된 금융 차트 라이브러리를 사용하는 편이 적합했기 때문이다.
개발 환경보다 중요한 것
개발 환경을 정하면서 가장 중요하게 생각한 것은 유명한 기술을 많이 사용하는 것이 아니었다.
- 실제로 필요한가?
- 혼자서 유지보수할 수 있는가?
- 장애가 발생했을 때 원인을 찾을 수 있는가?
- 백테스트와 실거래가 같은 규칙으로 동작하는가?
이 네 가지를 기준으로 불필요한 기술은 최대한 제외했다.
PostgreSQL도, Python도, React도 완벽해서 선택한 것은 아니다. 지금 만들고 있는 시스템의 규모와 목적에 가장 잘 맞는 조합이라고 판단했기 때문이다.
마치며
이제 프로그램이 실행될 바닥은 어느 정도 만들어졌다.
하지만 개발 환경을 세팅했다고 자동매매 시스템이 완성된 것은 아니다. 실시간 데이터를 안정적으로 수집하고, 백테스트와 실제 매매 사이의 차이를 줄이며, 예상하지 못한 상황에서도 자금을 보호할 수 있어야 한다.
다음 글에서는 업비트의 실시간 체결 데이터를 어떻게 수집하고 PostgreSQL에 저장했는지 기록해보려고 한다.
이 글은 개인적인 개발 과정과 경험을 기록한 글이며, 특정 투자 또는 수익을 권유하거나 보장하지 않습니다.
개발일지 이어보기
