사용자 지정 데이터 피드 만들기
사용자 지정 데이터 피드를 사용해 거래소, 시장 데이터 서비스, 자체 과거 데이터셋을 AQE로 가져오세요. 데이터 피드는 주문을 제출하지 않으며, PaperBroker나 별도의 주문 실행 어댑터와 함께 사용할 수 있습니다.
이 가이드는 AQE 0.2.0 인터페이스를 따릅니다. 특히 기존 연동을 업그레이드할 때는 전략과 동일한 AQE 리비전의 트레이트 정의를 사용하세요.
1. 두 트레이트 구현하기
섹션 제목: “1. 두 트레이트 구현하기”클라이언트, 연결 상태, 구독 작업을 소유하는 Rust 타입을 만듭니다. 해당 타입에 aq_engine::core::broker::traits::{DataFeed, DataProvider}를 구현하세요. 이 타입은 Send + Sync여야 합니다.
| 트레이트 | 필수 메서드 | 역할 |
|---|---|---|
DataFeed | connect, disconnect, is_connected | 통신 연결을 설정하고 해제하며 실제 상태를 보고합니다. |
DataProvider | get_ticker_info | 심볼, 거래 규칙, 정밀도를 포함하는 AQE Asset을 반환합니다. |
DataProvider | get_history | 요청한 심볼, UTC 날짜 범위, TimeFrame에 대한 Polars DataFrame을 반환합니다. |
DataProvider | get_latest_bar, get_latest_quote | 소스 타임스탬프를 포함한 Bar 또는 Quote를 반환합니다. |
DataProvider | subscribe_to_data_stream, unsubscribe_from_data_stream | 요청한 심볼과 타임프레임에 대한 캔들 콜백을 관리합니다. |
이 트레이트는 Rust 네이티브 비동기 메서드를 사용합니다. connect와 get_latest_quote는 명시적으로 Send 퓨처를 요구합니다. 캡처한 상태가 Send라면 async fn 구현으로 이를 충족할 수 있습니다. Send가 아닌 잠금 가드를 .await에 걸쳐 유지하지 마세요.
연결 시도가 취소되더라도 어댑터가 리소스 소유권을 유지해야 disconnect에서 정리할 수 있습니다. 연결 해제 시 백그라운드 작업을 중지하고 구독을 해제하세요. 이후에도 데이터를 계속 전달하는 분리된 워커를 만들지 마세요.
지원하는 기능만 선언하기
섹션 제목: “지원하는 기능만 선언하기”DataFeed::get_features()를 재정의하여 DataFeedFeature 값의 정적 슬라이스를 반환하세요. 기본값은 빈 슬라이스입니다.
- 이력 및 현재 가격:
HistoricalCandles,LatestCandle,LatestQuote. - 캔들 전달:
CandleStream, 그리고 구현한 경우에만IntraCandleStream. - 독립적인 호가:
QuoteStream과 상황에 맞는NativeBidAsk또는SyntheticQuotes.
해당 기능을 지원하지 않더라도 필수 트레이트 메서드는 구현해야 합니다. 지원하지 않는 작업에는 데이터를 임의로 만들거나 아무 작업 없이 성공을 반환하지 말고, 명확한 BrokerError를 반환하세요. 과거 데이터만 제공하는 어댑터도 실시간 스트리밍을 지원한다고 선언하지 않고 백테스트에 유용하게 사용할 수 있습니다.
완전한 종목 메타데이터 반환하기
섹션 제목: “완전한 종목 메타데이터 반환하기”get_ticker_info는 단순한 심볼이 아니라 Asset을 반환합니다. 공급자의 메타데이터를 바탕으로 종목 식별 정보, 자산 유형, 거래소, 거래 권한과 다음 실행 규칙을 채우세요.
| 필드 | 제공할 정보 |
|---|---|
min_order_size, quantity_base | AQE가 인식하는 최소 수량과 수량 정밀도입니다. 해당 시장의 수량 규칙과 일치하도록 유지하세요. |
max_order_size, max_total_size | 각각 주문별 한도와 심볼 전체의 합산 한도입니다. 공급자가 한도를 보고하지 않으면 None을 사용하고, 두 한도를 혼동하지 마세요. |
min_price_increment, price_base, contract_size | 알려진 경우 가격 증분, 가격 정밀도, 계약 승수를 제공합니다. 모든 종목이 주식 수량 단위를 사용하거나 승수가 1이라고 가정하지 마세요. |
trading_calendar | 생성 주체가 기록한 거래 일정 JSON과 그 출처를 담는 Option<String>입니다. 일정을 알 수 없으면 None을 유지하고, 메타데이터가 없다는 이유로 중단 없이 거래할 수 있다고 추론하지 마세요. |
fees | 제공되는 경우 수수료와 스왑 정보를 담는 AssetFees입니다. 현재 수수료 메타데이터는 각각 롱 및 숏 값을 갖는 commission.entry와 commission.exit를 사용하며, 스왑은 swap.long과 swap.short를 사용합니다. |
AssetFees::default()는 빈 비용 설정을 제공할 뿐, 해당 시장에 비용이 없다는 증거가 아닙니다. 기존 entry와 exit 비용 필드는 호환성을 위한 대체 필드로 유지됩니다. 별도의 주문 실행 시장은 데이터 공급자와 규칙 및 비용이 다를 수 있습니다. 시장 데이터만으로 주문 권한이 있다고 가정하지 말고, 주문 실행 계좌를 기준으로 메타데이터를 검증하세요.
2. 봉과 호가 정규화하기
섹션 제목: “2. 봉과 호가 정규화하기”UTC 타임스탬프와 일관된 심볼을 사용해 공급자 레코드를 AQE Bar 값으로 매핑하세요. 페이지네이션, 시간순 정렬, 중복, 요청한 타임프레임을 명시적으로 처리하세요. 알리지 않고 다른 타임프레임을 반환하거나 누락된 이력을 현재 가격으로 대체하지 마세요.
과거 데이터 프레임에는 다음 열이 필요합니다.
| 열 | 표준 표현 |
|---|---|
symbol | 문자열 |
open, high, low, close, volume | f64 |
timestamp | AQE 기본 변환기가 생성하는 i64 형식의 UTC Unix 밀리초 |
프레임을 직접 만들기보다는 기존 변환기를 사용하세요. 다음 헬퍼는 공급자 응답을 이미 Vec<Bar>로 디코딩하고 검증한 상태를 전제로 합니다.
use aq_engine::core::broker::{ traits::DataProvider, types::{Bar, BarData, BrokerError},};use polars::prelude::DataFrame;
pub fn normalise_history<D: DataProvider>( provider: &D, bars: Vec<Bar>,) -> Result<DataFrame, BrokerError> { provider .format_on_bar(BarData::Bars(bars)) .map_err(BrokerError::DataFeedError)}AQE 의존성과 호환되는 Polars 버전을 사용하세요. format_on_bar(BarData::Frame(...))는 필수 열 이름을 검사하지만 타입, 정렬 순서, 시장 데이터 품질을 완전히 검증해 주지는 않습니다.
네이티브 Quote에는 symbol, bid, ask, 호가 수량, 선택적 최근 체결 정보, UTC 타임스탬프가 포함됩니다. 가격은 유한한 양수이고 ask >= bid를 충족해야 하며, 수량은 음수가 아니어야 합니다. 봉만 제공하는 소스에서는 Quote::from_bar_close(&bar)가 호가창 수량을 임의로 만들지 않고 매수 호가와 매도 호가가 같은 합성 호가를 생성합니다. 이 표현에는 NativeBidAsk가 아니라 SyntheticQuotes를 명시하세요.
새 호가 평가 필드 포함하기
섹션 제목: “새 호가 평가 필드 포함하기”이제 Quote에는 profit_model도 포함됩니다. 공급자가 평가 모델을 제공할 수 없더라도 기존 Rust 구조체 리터럴에 이 필드를 추가하세요. #[serde(default)] 속성은 이전에 직렬화된 호가를 계속 읽을 수 있게 해 주지만, Rust 소스 코드에서 누락된 필드를 채워 주지는 않습니다.
일반적인 데이터 피드에서는 profit_model: None을 사용하세요. 다음 예제는 입력 가격과 타임스탬프가 이미 검증되었다고 가정합니다. 수량 0은 보고된 수량이 없다는 뜻이며, 유동성을 임의로 만들어낸 값이 아닙니다.
use aq_engine::core::broker::types::Quote;use chrono::{DateTime, Utc};
pub fn quote_without_valuation( symbol: String, bid: f64, ask: f64, timestamp: DateTime<Utc>,) -> Quote { Quote { profit_model: None, symbol, bid, ask, bid_size: 0.0, ask_size: 0.0, last: None, last_size: None, timestamp, }}주문 실행 브로커가 해당 계좌, 심볼, 호가에 유효한 계좌 통화 기준 계수를 제공할 수 있을 때만 BrokerQuoteProfit 모델을 추가하세요. 이 모델에는 version, account_id, currency, symbol, as_of_us와 각각의 buy_gain, buy_loss, sell_gain, sell_loss 계수가 기록됩니다. 현재 버전은 1이며 계수는 유한한 양수여야 합니다. 관측 시각은 UTC Unix 마이크로초를 사용하지만, Quote.timestamp 필드는 UTC DateTime입니다.
이 계수는 브로커가 보정한 가격 단위당 및 브로커 수량 단위당 현금 가치이며, 일반적인 외환 환산값이나 실현 손익 기록이 아닙니다. 계좌, 통화, 심볼, 최신성을 보존하세요. 계좌 간에 복사하거나 봉 종가로 임의 생성하지 마세요. Quote::from_bar_close는 의도적으로 모델을 포함하지 않습니다. 이 메타데이터는 기존 호가 기능 플래그를 대체하지 않습니다.
3. 구독 구현하기
섹션 제목: “3. 구독 구현하기”subscribe_to_data_stream은 심볼, TimeFrame, DataStreamMode, Arc<dyn Fn(Bar) + Send + Sync> 콜백을 받습니다. 모드를 준수하세요. CompletedBar는 완성된 캔들을 전달하며, Intrabar는 형성 중인 캔들에 대한 업데이트를 허용합니다. 콜백 실행은 짧고 비차단 방식으로 유지하세요.
폴링 공급자라면 다른 스케줄러를 작성하기 전에 기존 DataStreamManager와 Yahoo 어댑터를 살펴보세요. 웹소켓 공급자는 자체 읽기 작업을 관리할 수 있지만 동일한 콜백 및 구독 해제 계약을 준수해야 합니다.
호가 스트리밍은 선택 사항입니다. QuoteStream을 제공한다고 명시하려면 subscribe_to_quote_stream과 unsubscribe_from_quote_stream을 모두 재정의하세요. 각 구독에는 고유한 ID가 있습니다. 하나를 제거해도 다른 소비자의 호가나 캔들이 중단되어서는 안 됩니다. 기본 구독 메서드는 지원하지 않는다는 오류를 반환합니다. 호가 API는 거래소의 모든 틱을 제공한다고 보장하지 않습니다.
4. 공급자를 AQE에 연결하기
섹션 제목: “4. 공급자를 AQE에 연결하기”타입이 두 트레이트를 구현하면 인스턴스를 UnifiedBroker에 전달하세요. 다음 재사용 가능한 헬퍼는 요구 사항을 충족하는 모든 공급자를 받으며, 공급자의 네트워크 API를 구현하지는 않습니다.
use aq_engine::core::broker::{ paper_broker::PaperBroker, traits::{DataFeed, DataProvider}, types::AccountType, UnifiedBroker,};
pub fn backtest_with<D>(datafeed: D) -> UnifiedBroker<PaperBroker, D>where D: DataFeed + DataProvider,{ let execution = PaperBroker::new(AccountType::Paper, 100_000.0, 1); UnifiedBroker::new_backtest(execution, datafeed)}실시간 거래 또는 실시간 모의 거래에는 UnifiedBroker::new(execution, datafeed)를 사용하세요. 과거 데이터 재생에는 계속 new_backtest를 사용하세요. 재생 호가는 현재 시장 가격 엔드포인트가 아니라 이미 처리한 봉에서 가져옵니다.
이 트레이트를 구현하면 Rust 전략에서 사용할 수 있습니다. AQS 연결 양식이나 공급자 선택 항목을 추가하려면 별도의 데스크톱 연동이 필요합니다.
선택적 공급자 훅
섹션 제목: “선택적 공급자 훅”supports_historical_stream(symbol, timeframe)의 기본 반환값은 Ok(true)입니다. 유한한 데이터셋에서는 제공할 수 없는 심볼과 타임프레임 조합을 의도적으로 건너뛸 수 있도록 재정의하세요. 실제 네트워크, 인증, 데이터 오류는 오류로 유지하세요. 이 훅으로 실패한 요청을 숨기거나 알리지 않고 다른 타임프레임으로 대체하지 마세요.
configure_live_session은 기본적으로 아무 작업도 하지 않는 선택적 세션 컨텍스트 훅입니다. 이 훅으로 인해 사용자 지정 데이터 피드에 평가 메타데이터나 클라우드 연결이 필수가 되는 것은 아닙니다.
5. 실시간 사용 전에 검증하기
섹션 제목: “5. 실시간 사용 전에 검증하기”- 유효하지 않은 심볼, 빈 이력, 페이지네이션, 타임스탬프 변환, 지원하지 않는 타임프레임을 테스트합니다.
- 알려진 작은 데이터셋과 비교해 OHLCV 값과 시간순 정렬을 검증합니다.
- 재연결과 중복 메시지를 포함하여 완성 봉 전달과 봉 내부 업데이트 전달을 각각 테스트합니다.
- 호가 구독을 두 개 만든 뒤 하나를 제거하고 다른 하나가 계속 활성 상태인지 확인합니다.
- 연결 및 스트리밍 중에 연결을 해제하고 소유한 모든 리소스가 해제되는지 검증합니다.
- 실거래 계좌를 연결하기 전에 결정론적
PaperBroker백테스트를 실행합니다. - AQE 업그레이드 후 모든
Asset및Quote구조체 리터럴을 확인합니다. 제공되지 않는 값을 손익 0으로 취급하지 않으면서, 평가 메타데이터 누락, 오래되었거나 다른 계좌에 속한 모델, 합성 호가를 테스트합니다.
소스 참조
섹션 제목: “소스 참조”AQE 체크아웃에서 다음 파일부터 살펴보세요.
src/core/broker/mod.rs:DataFeed,DataProvider,DataStreamMode,UnifiedBroker.src/core/broker/types/mod.rs:Asset,Bar,Quote,BarData,BAR_DATAFRAME_COLUMNS.src/core/broker/features.rs: 지원되는 기능 플래그.src/core/broker/data_feeds/yahoo.rs: 과거 데이터 조회, 정규화, 폴링.src/core/broker/data_stream.rs: 공용 캔들 구독 관리.live-sync-protocol/src/quote_profit.rs:BrokerQuoteProfit의 적용 범위, 계수, 최신성 검사.
다음 문서: 사용자 지정 브로커 만들기 또는 브로커 및 데이터 피드.
로그인, 계정 보안, 색상 테마 설정, 이 알림 표시와 PostHog의 필수 내부 이용 현황·세션 품질·안정성 지표 수집을 위해 필수 쿠키와 저장소를 사용합니다. 광고 쿠키는 사용하지 않습니다.
