
디버깅 노가다 진행 중...
기존에 crypto_trading_server 프로젝트에 backtest, db, algorithm, binance 등 여러 모듈을 가지고 처리했다. 백테스트를 하려고 바이너리 크레이트를 만드려고 하는데 라이브러리 형태가 아니라서 가져다 쓰기가 불편했고 examples로 만들자니 마음에 들지 않았다. 나중에 재활용할 수 있는 모듈들을 분리하기로 했다. 폴더 구조는 아래와 같다.
trading_project/
├── crates/
│ ├── trading_core/ # 핵심 거래 로직
│ │ └── src/
│ │ ├── algorithm/ # 거래 알고리즘
│ │ │ ├── ema_inflection.rs
│ │ │ ├── ema_disparity.rs
│ │ │ ├── stop_hunting.rs
│ │ │ └── mod.rs
│ │ ├── backtest/ # 백테스팅
│ │ │ ├── types.rs
│ │ │ └── ...
│ │ ├── binance/ # 바이낸스 API
│ │ │ ├── client.rs
│ │ │ ├── types.rs
│ │ │ └── mod.rs
│ │ ├── indicator.rs
│ │ └── klines.rs
│ │
│ ├── trading_server/ # 서버 애플리케이션
│ │ ├── src/
│ │ │ └── worker/
│ │ └── .claude/ # 문서 및 계획
│ │
│ └── backtest/ # 백테스트 관련
│
└── Cargo.toml # 프로젝트 설정
root 폴더를 잡고 workspace의 member로 각각 크레이트들을 추가하였다.
trading_core를 메인 라이브러리로 잡고 backtest와 trading_server에서 사용하도록 했다.
backtest 바이너리 크레이트를 만들었다. 소스는 아래와 같다.
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_line_number(true)
.with_file(true)
.init();
let mut engine = ready_for_backtest().await?;
Ok(())
}
async fn ready_for_backtest() -> anyhow::Result<()> {
let ema_double_filter_strategy = EmaDoubleFilterStrategy::new(500);
// db init
let connection_string = "postgresql://postgres:2677@192.168.1.114:5432/postgres"; // linux
let db_client = DbClient::new(connection_string).await?;
let res = trading_core::backtest::run_backtest(&db_client, "BTCUSDT", Box::new(ema_double_filter_strategy), 60*24*180, 100.0).await?;
trading_core::backtest::print_report(&res);
Ok(())
}
run_backtest함수에 사용할 알고리즘 전략을 Box<dyn StrategyExecutor> 형태로 매개변수로 넣을 수 있도록 설계하였다.
pub trait StrategyExecutor: Send + Sync {
fn process(&mut self, candle: &Candle) -> Option<Signal>;
fn get_state(&self) -> String;
fn reset(&mut self);
}
백테스트에 쓰일 전략은 StrategyExecutor을 구현하도록 했다. 각 전략은 다르지만 Signal을 받아서 매수, 매도, 손절 등 실제 거래처럼 처리하기 위함이다.
백테스팅을 돌렸는데 거래내역이 하나도 없어서 당황스러웠다. 위 사진처럼 실제 차트와 로그를 비교하며 어느 부분을 개선해야 할지 찾아야 할 것 같다. 이제부터 시작인 것 같다.