고주파 거래 시스템에서 가장 중요한环节은 바로历史数据的精确重放입니다. 주문서(Order Book)의 미세한 변동까지 포착해야 정확한 시장 조성(market making) 전략을 검증할 수 있습니다. 본 튜토리얼에서는 Tardis.dev를活用하여 Binance, Bybit 등 주요 거래소의 Level 2 데이터를 다운로드하고, 이를 Python 환경에서 주문서 리플레이 시스템을 구축하는全过程을 다룹니다.

시장 조성 전략에는 실시간 시장 분석과 신호 처리가 필수적입니다. HolySheep AI를 활용하면 단일 API 키로 다중 모델을 상황에 맞게 전환하며 비용을 최적화할 수 있습니다. 먼저 월 1,000만 토큰 기준 비용 비교표를 확인해보겠습니다.

월 1,000만 토큰 기준 AI 모델 비용 비교

공급자 모델 입력 ($/MTok) 출력 ($/MTok) 월 1,000만 토큰 총비용 주요 특징
HolySheep AI DeepSeek V3.2 $0.42 $0.42 $42 비용 효율 최고, 다중 모델 지원
HolySheep AI Gemini 2.5 Flash $2.50 $2.50 $250 속도 최적화, 프로덕션 적합
HolySheep AI GPT-4.1 $8.00 $8.00 $800 최고 품질, 복잡한 분석
HolySheep AI Claude Sonnet 4.5 $15.00 $15.00 $1,500 긴 컨텍스트, 세밀한 추론
공식 OpenAI GPT-4.1 $8.00 $8.00 $800 단일 모델, 해외 결제 필수
공식 Anthropic Claude Sonnet 4.5 $15.00 $15.00 $1,500 단일 모델, 해외 결제 필수

핵심 포인트: HolySheep AI는 동일한 모델을 공식 가격대로 제공하면서도, 로컬 결제 지원과 단일 API 키로 다중 모델 전환이 가능합니다. 월 1,000만 토큰 사용 시 DeepSeek V3.2 선택 시 월 $42로 경쟁사 대비大幅 절감이 가능합니다.

이런 팀에 적합 / 비적합

✅ 이런 팀에 적합

❌ 이런 팀에는 비적합

왜 HolySheep를 선택해야 하나

시장 조성 전략에서는 시장 상황 판단, 리스크 평가, 신호 생성에 AI가 활용됩니다. HolySheep AI는 다음과 같은 장점을 제공합니다:

지금 지금 가입하고 무료 크레딧으로 시장 조성 전략 개발을 시작하세요!


1. Tardis.dev 환경 설정

1.1 Tardis.dev 소개 및 계정 생성

Tardis.dev는加密화폐 거래소의原生(native) 실시간 및历史데이터를 제공하는 전문 데이터 플랫폼입니다. Binance, Bybit, OKX, Deribit 등의 Level 2 주문서 데이터를 миллисекунд精度로 제공합니다.

# Tardis CLI 설치 (macOS/Linux)
curl --proto '=https' --tlsv1.2 -sSf https://tardis.dev/cli/installer.sh | sh

Windows의 경우

https://tardis.dev/download 에서 바이너리 다운로드

설치 확인

tardis --version

1.2 필수 데이터 구조 이해

주문서 리플레이를 위해 다음 데이터셋을 이해해야 합니다:

# 지원 거래소 목록 확인
tardis export --help

사용 가능한 exchange 목록

- binance

- binance-futures

- bybit

- okx

- deribit

- gate

- huobi


2. 역사 데이터 다운로드

2.1 Binance USDT-M 선물 데이터 다운로드

# Tardis.dev API 키 설정
export TARDIS_API_KEY="your_tardis_api_key_here"

BTC/USDT 선물 1분봉 + 주문서 데이터 다운로드

tardis download binance-futures \ --symbol "btcusdt" \ --data-types "incremental_book_L2,trade" \ --from "2024-01-01T00:00:00Z" \ --to "2024-01-02T00:00:00Z" \ --output "./data/binance_futures_btcusdt_2024-01"

다운로드 확인

ls -la ./data/binance_futures_btcusdt_2024-01/

incremental_book_L2.ndjson.gz

trade.ndjson.gz

2.2 Bybit 통합 마켓 데이터 다운로드

# Bybit USDT perpetual 데이터
tardis download bybit \
  --symbol "BTCUSDT" \
  --data-types "incremental_book_L2,trade,ticker" \
  --from "2024-06-01T00:00:00Z" \
  --to "2024-06-07T00:00:00Z" \
  --output "./data/bybit_btcusdt_2024-06"

압축 해제

gunzip -k ./data/bybit_btcusdt_2024-06/*.gz

데이터 샘플 확인

head -n 1 ./data/bybit_btcusdt_2024-06/incremental_book_L2.json

3. Python 주문서 리플레이 시스템 구축

3.1 프로젝트 구조 및 의존성

# requirements.txt

numpy>=1.24.0

pandas>=2.0.0

sortedcontainers>=2.4.0

asyncio-log>=0.4.0

openai>=1.0.0

aiohttp>=3.9.0

pip install -r requirements.txt

3.2 주문서 클래스 구현

import json
import gzip
import time
from sortedcontainers import SortedDict
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Tuple
from datetime import datetime
from enum import Enum

class Side(Enum):
    BUY = "buy"
    SELL = "sell"

@dataclass
class OrderBook:
    """Level 2 주문서 관리 클래스"""
    symbol: str
    bids: SortedDict = field(default_factory=SortedDict)  # price -> quantity
    asks: SortedDict = field(default_factory=SortedDict)
    sequence: int = 0
    last_update_time: float = 0
    
    def update_book(self, updates: List[Dict]) -> None:
        """주문서 업데이트 처리 (Tardis incremental_book_L2 포맷)"""
        for update in updates:
            side = Side.BUY if update["side"] == "buy" else Side.SELL
            price = float(update["price"])
            quantity = float(update["quantity"])
            
            book = self.bids if side == Side.BUY else self.asks
            
            if quantity == 0:
                # 주문 삭제
                if price in book:
                    del book[price]
            else:
                # 주문 삽입 또는 수정
                book[price] = quantity
            
            self.sequence += 1
    
    def get_spread(self) -> Tuple[float, float]:
        """현재 스프레드 반환"""
        best_bid = self.bids.peekitem(-1)[0] if self.bids else 0
        best_ask = self.asks.peekitem(0)[0] if self.asks else float('inf')
        return best_bid, best_ask
    
    def get_mid_price(self) -> float:
        """중간가 반환"""
        best_bid, best_ask = self.get_spread()
        return (best_bid + best_ask) / 2
    
    def get_level(self, depth: int = 10) -> Dict:
        """주문서 특정 깊이의 레벨 데이터 반환"""
        return {
            "timestamp": self.last_update_time,
            "bids": [(price, qty) for price, qty in self.bids.items()[-depth:]],
            "asks": [(price, qty) for price, qty in list(self.asks.items())[:depth]],
            "spread": self.get_spread()[1] - self.get_spread()[0],
            "mid_price": self.get_mid_price()
        }

print("✓ OrderBook 클래스 로드 완료")

3.3 주문서 리플레이 엔진

import gzip
import json
from pathlib import Path
from typing import Generator, Dict, Optional
from datetime import datetime

class OrderBookReplayEngine:
    """주문서 리플레이 엔진 - Tardis 데이터 파일 재생"""
    
    def __init__(self, data_dir: str):
        self.data_dir = Path(data_dir)
        self.order_books: Dict[str, OrderBook] = {}
        
    def load_book_snapshot(self, exchange: str, symbol: str) -> OrderBook:
        """초기 주문서 스냅샷 로드"""
        ob = OrderBook(symbol=symbol)
        snapshot_file = self.data_dir / f"{exchange}_{symbol}_snapshot.json"
        
        if snapshot_file.exists():
            with open(snapshot_file) as f:
                data = json.load(f)
                ob.bids = SortedDict({float(p): q for p, q in data["bids"]})
                ob.asks = SortedDict({float(p): q for p, q in data["asks"]})
                ob.sequence = data.get("sequence", 0)
        
        self.order_books[symbol] = ob
        return ob
    
    def replay_incremental(self, exchange: str, symbol: str) -> Generator[Dict, None, None]:
        """증분 업데이트 파일 리플레이"""
        book = self.order_books.get(symbol)
        if not book:
            book = OrderBook(symbol=symbol)
            self.order_books[symbol] = book
        
        incremental_file = self.data_dir / f"incremental_book_L2.json"
        
        if not incremental_file.exists():
            incremental_file = self.data_dir / f"incremental_book_L2.ndjson.gz"
            if incremental_file.exists():
                with gzip.open(incremental_file, 'rt') as f:
                    for line in f:
                        data = json.loads(line)
                        yield from self._process_message(book, data)
            return
        
        with open(incremental_file) as f:
            for line in f:
                data = json.loads(line)
                yield from self._process_message(book, data)
    
    def _process_message(self, book: OrderBook, message: Dict) -> Generator[Dict, None, None]:
        """메시지 처리 및 상태 변경 감지"""
        msg_type = message.get("type", "")
        timestamp = message.get("timestamp", 0)
        
        if msg_type == "snapshot":
            # 스냅샷으로 초기화
            book.bids = SortedDict({
                float(p): q for p, q in message.get("bids", [])
            })
            book.asks = SortedDict({
                float(p): q for p, q in message.get("asks", [])
            })
            
        elif msg_type == "update":
            book.update_book(message.get("updates", []))
            book.last_update_time = timestamp
            
            yield {
                "timestamp": timestamp,
                "symbol": book.symbol,
                "mid_price": book.get_mid_price(),
                "spread": book.get_spread()[1] - book.get_spread()[0],
                "best_bid": book.bids.peekitem(-1)[0] if book.bids else 0,
                "best_ask": book.asks.peekitem(0)[0] if book.asks else 0,
                "bid_depth": sum(book.bids.values()),
                "ask_depth": sum(book.asks.values()),
                "sequence": book.sequence
            }

print("✓ OrderBookReplayEngine 클래스 로드 완료")

4. 시장 조성 전략 구현

4.1 HolySheep AI 클라이언트 설정

import openai
from typing import Optional, Dict, List
import json

class HolySheepAIClient:
    """HolySheep AI API 클라이언트 - 시장 분석 및 신호 생성용"""
    
    def __init__(self, api_key: str):
        self.client = openai.OpenAI(
            api_key=api_key,
            base_url="https://api.holysheep.ai/v1"
        )
        self.model_configs = {
            "deepseek": {
                "model": "deepseek-chat",
                "cost_per_1m": 0.42,
                "use_case": "대량 신호 처리"
            },
            "gemini": {
                "model": "gemini-2.0-flash",
                "cost_per_1m": 2.50,
                "use_case": "빠른 시장 판단"
            },
            "gpt4": {
                "model": "gpt-4.1",
                "cost_per_1m": 8.00,
                "use_case": "복잡한 분석"
            }
        }
    
    def analyze_market_conditions(
        self, 
        orderbook_data: Dict,
        model: str = "gemini"
    ) -> Dict:
        """주문서 데이터 기반 시장 상황 분석"""
        
        prompt = f"""당신은高频市場 조성 전문가입니다.
        
현재 주문서 상태:
- 중간가: ${orderbook_data.get('mid_price', 0):,.2f}
- 스프레드: ${orderbook_data.get('spread', 0):,.4f}
- 매수호가 총 수량: {orderbook_data.get('bid_depth', 0):,.4f}
- 매도호가 총 수량: {orderbook_data.get('ask_depth', 0):,.4f}

분석 요청:
1. 현재 시장 유동성 상태 평가 (높음/중간/낮음)
2. 스프레드 적정성 판단
3. 권장 입찰-spread 폭 (bps 단위)
4. 리스크 수준 (1-10)

JSON 형식으로 응답:
{{
    "liquidity": "high/medium/low",
    "spread_adequacy": "tight/normal/wide",
    "recommended_bid_spread_bps": number,
    "recommended_ask_spread_bps": number,
    "risk_level": 1-10,
    "action": "increase/hold/decrease exposure"
}}"""

        try:
            response = self.client.chat.completions.create(
                model=self.model_configs[model]["model"],
                messages=[
                    {"role": "system", "content": "당신은 전문 금융 분석가입니다. JSON만 반환하세요."},
                    {"role": "user", "content": prompt}
                ],
                temperature=0.3,
                max_tokens=500
            )
            
            result = json.loads(response.choices[0].message.content)
            result["model_used"] = model
            result["cost"] = response.usage.total_tokens / 1_000_000 * \
                           self.model_configs[model]["cost_per_1m"]
            return result
            
        except Exception as e:
            print(f"API 오류: {e}")
            return {
                "liquidity": "medium",
                "spread_adequacy": "normal",
                "recommended_bid_spread_bps": 5,
                "recommended_ask_spread_bps": 5,
                "risk_level": 5,
                "action": "hold",
                "error": str(e)
            }

HolySheep AI 클라이언트 초기화

HOLYSHEEP_API_KEY = "YOUR_HOLYSHEEP_API_KEY" ai_client = HolySheepAIClient(HOLYSHEEP_API_KEY) print("✓ HolySheep AI 클라이언트 초기화 완료")

4.2 시장 조성 전략 클래스

import random
from dataclasses import dataclass

@dataclass
class MarketMakerState:
    """시장 조성 전략 상태"""
    symbol: str
    base_spread_bps: float = 5.0  # 기본 스프레드 (basis points)
    position_limit: float = 1.0   # 최대 포지션
    current_position: float = 0.0
    inventory_skew: float = 0.0  # 재고 편향
    
    # PnL 추적
    total_pnl: float = 0.0
    realized_pnl: float = 0.0
    trade_count: int = 0

class MarketMakingStrategy:
    """시장 조성 전략 - 주문서 리플레이에 연결"""
    
    def __init__(
        self, 
        state: MarketMakerState,
        ai_client: Optional[HolySheepAIClient] = None,
        use_ai_optimization: bool = False
    ):
        self.state = state
        self.ai_client = ai_client
        self.use_ai_optimization = use_ai_optimization
        
    def calculate_quote_prices(
        self, 
        mid_price: float, 
        spread_bps: float = None
    ) -> Tuple[float, float]:
        """호가 가격 계산"""
        if spread_bps is None:
            spread_bps = self.state.base_spread_bps
        
        spread_amount = mid_price * (spread_bps / 10000)
        bid_price = mid_price - (spread_amount / 2)
        ask_price = mid_price + (spread_amount / 2)
        
        return bid_price, ask_price
    
    def evaluate_risk(self, orderbook_data: Dict) -> Dict:
        """리스크 평가 및 위치 조정"""
        risk_metrics = {
            "position_ratio": abs(self.state.current_position) / self.state.position_limit,
            "unrealized_pnl": self.state.total_pnl - self.state.realized_pnl,
            "volatility_estimate": orderbook_data.get("spread", 0) / orderbook_data.get("mid_price", 1)
        }
        
        # 위치 한도 체크
        if risk_metrics["position_ratio"] > 0.9:
            risk_metrics["should_flatten"] = True
        else:
            risk_metrics["should_flatten"] = False
            
        return risk_metrics
    
    def optimize_with_ai(self, orderbook_data: Dict) -> Dict:
        """AI를 통한 스프레드 최적화"""
        if not self.ai_client or not self.use_ai_optimization:
            return {
                "spread_bps": self.state.base_spread_bps,
                "position_size": 0.1
            }
        
        analysis = self.ai_client.analyze_market_conditions(orderbook_data)
        
        return {
            "spread_bps": analysis.get("recommended_bid_spread_bps", self.state.base_spread_bps),
            "position_size": 0.1 * (1 - risk_level/10),
            "action": analysis.get("action", "hold")
        }
    
    def on_trade(
        self, 
        price: float, 
        quantity: float, 
        side: str
    ) -> None:
        """거래 실행 처리"""
        if side == "buy":
            self.state.current_position += quantity
            # 롱 포지션 -> 매도 호가 수익
            self.state.realized_pnl += quantity * self.state.base_spread_bps * price / 10000
        else:
            self.state.current_position -= quantity
            # 숏 포지션 -> 매수 호가 수익  
            self.state.realized_pnl += quantity * self.state.base_spread_bps * price / 10000
        
        self.state.total_pnl = self.state.realized_pnl
        self.state.trade_count += 1

print("✓ MarketMakingStrategy 클래스 로드 완료")

5. 백테스트 실행 및 결과 분석

5.1 백테스트 메인 루프

import time
from datetime import datetime

def run_backtest(
    data_dir: str,
    symbol: str,
    use_ai: bool = True,
    simulation_speed: float = 1.0
) -> Dict:
    """백테스트 실행"""
    
    # 엔진 초기화
    engine = OrderBookReplayEngine(data_dir)
    engine.load_book_snapshot("binance-futures", symbol)
    
    # 전략 초기화
    mm_state = MarketMakerState(
        symbol=symbol,
        base_spread_bps=5.0,
        position_limit=1.0
    )
    
    ai_client = HolySheepAIClient(HOLYSHEEP_API_KEY) if use_ai else None
    strategy = MarketMakingStrategy(
        state=mm_state,
        ai_client=ai_client,
        use_ai_optimization=use_ai
    )
    
    # 결과 수집
    results = {
        "trades": [],
        "equity_curve": [],
        "metrics": {
            "total_trades": 0,
            "total_pnl": 0.0,
            "max_drawdown": 0.0,
            "win_rate": 0.0
        }
    }
    
    equity = 10000.0
    trades_list = []
    
    print(f"🔄 백테스트 시작: {symbol}")
    print(f"   AI 최적화: {'활성화' if use_ai else '비활성화'}")
    
    for i, tick in enumerate(engine.replay_incremental("binance-futures", symbol)):
        # 1,000틱마다 상태 출력
        if i % 1000 == 0:
            mid = tick["mid_price"]
            spread = tick["spread"]
            print(f"   [{i:,} ticks] Mid: ${mid:,.2f} | Spread: ${spread:.4f}")
        
        # AI 기반 스프레드 최적화 (10틱마다)
        if use_ai and i % 10 == 0:
            params = strategy.optimize_with_ai(tick)
            spread_bps = params["spread_bps"]
        else:
            spread_bps = strategy.state.base_spread_bps
        
        # 호가 계산
        bid, ask = strategy.calculate_quote_prices(tick["mid_price"], spread_bps)
        
        # 모의 거래 (확률적 실행)
        if random.random() < 0.001:  # 0.1% 확률로 거래
            trade_side = "buy" if random.random() < 0.5 else "sell"
            trade_qty = random.uniform(0.01, 0.1)
            
            strategy.on_trade(tick["mid_price"], trade_qty, trade_side)
            trades_list.append({
                "timestamp": tick["timestamp"],
                "side": trade_side,
                "price": tick["mid_price"],
                "quantity": trade_qty
            })
        
        # 에쿼티 업데이트
        equity += strategy.state.realized_pnl * 0.01
        results["equity_curve"].append({
            "timestamp": tick["timestamp"],
            "equity": equity
        })
    
    # 최종 결과
    results["metrics"]["total_trades"] = len(trades_list)
    results["metrics"]["total_pnl"] = strategy.state.total_pnl
    results["trades"] = trades_list
    
    print(f"\n✅ 백테스트 완료")
    print(f"   총 거래 수: {results['metrics']['total_trades']:,}")
    print(f"   총 PnL: ${results['metrics']['total_pnl']:.2f}")
    print(f"   최종 에쿼티: ${equity:,.2f}")
    
    return results

백테스트 실행

results = run_backtest( data_dir="./data/binance_futures_btcusdt_2024-01", symbol="btcusdt", use_ai=True )

5.2 결과 시각화

import matplotlib.pyplot as plt
import pandas as pd

def plot_backtest_results(results: Dict, save_path: str = "backtest_results.png"):
    """백테스트 결과 시각화"""
    
    equity_df = pd.DataFrame(results["equity_curve"])
    equity_df["timestamp"] = pd.to_datetime(equity_df["timestamp"], unit="ms")
    
    fig, axes = plt.subplots(2, 2, figsize=(14, 10))
    
    # 1. 에쿼티 커브
    axes[0, 0].plot(equity_df["timestamp"], equity_df["equity"], "b-", linewidth=1)
    axes[0, 0].set_title("Equity Curve", fontsize=12, fontweight="bold")
    axes[0, 0].set_xlabel("Time")
    axes[0, 0].set_ylabel("Equity (USD)")
    axes[0, 0].grid(True, alpha=0.3)
    
    # 2. 거래 분포
    trades_df = pd.DataFrame(results["trades"])
    if not trades_df.empty:
        buy_trades = trades_df[trades_df["side"] == "buy"]
        sell_trades = trades_df[trades_df["side"] == "sell"]
        axes[0, 1].scatter(range(len(buy_trades)), buy_trades["price"], 
                          alpha=0.6, label="Buy", color="green", s=10)
        axes[0, 1].scatter(range(len(sell_trades)), sell_trades["price"],
                          alpha=0.6, label="Sell", color="red", s=10)
        axes[0, 1].set_title("Trade Execution", fontsize=12, fontweight="bold")
        axes[0, 1].legend()
        axes[0, 1].grid(True, alpha=0.3)
    
    # 3. 손익 막대 그래프
    metrics = results["metrics"]
    labels = ["Total PnL", "Trades", "Win Rate"]
    values = [metrics["total_pnl"], metrics["total_trades"], 
              metrics["win_rate"] * 100]
    colors = ["green" if v >= 0 else "red" for v in values]
    axes[1, 0].bar(labels, values, color=colors, alpha=0.7)
    axes[1, 0].set_title("Performance Metrics", fontsize=12, fontweight="bold")
    axes[1, 0].grid(True, alpha=0.3, axis="y")
    
    # 4. 요약 텍스트
    summary_text = f"""
    Backtest Summary
    ─────────────
    Total Trades: {metrics['total_trades']:,}
    Total PnL: ${metrics['total_pnl']:.2f}
    Max Drawdown: ${metrics['max_drawdown']:.2f}
    Win Rate: {metrics['win_rate']*100:.1f}%
    """
    axes[1, 1].text(0.1, 0.5, summary_text, fontsize=11, 
                    family="monospace", verticalalignment="center")
    axes[1, 1].axis("off")
    axes[1, 1].set_title("Summary", fontsize=12, fontweight="bold")
    
    plt.tight_layout()
    plt.savefig(save_path, dpi=150)
    plt.show()
    
    print(f"✅ 차트가 저장됨: {save_path}")

결과 시각화

plot_backtest_results(results)

자주 발생하는 오류 해결

오류 1: Tardis 데이터 파일 형식 호환性问题

# 오류 메시지 예시:

"Error: Unable to parse incremental_book_L2.ndjson.gz"

해결 방법 1: Tardis CLI 최신 버전으로 업그레이드

tardis --version

1.5.0 미만이 경우 업그레이드

curl --proto '=https' --tlsv1.2 -sSf https://tardis.dev/cli/installer.sh | sh

해결 방법 2: 압축 파일 직접 파싱

import gzip def parse_tardis_gzip(filepath: str): """Tardis gzip 압축 파일 직접 파싱""" records = [] with gzip.open(filepath, 'rt') as f: for line in f: try: record = json.loads(line.strip()) records.append(record) except json.JSONDecodeError: continue # 잘못된 라인 스킵 return records

사용 예시

records = parse_tardis_gzip("./data/incremental_book_L2.ndjson.gz") print(f"✓ {len(records):,} 레코드 파싱 완료")

오류 2: HolySheep API 키 인증 오류

# 오류 메시지 예시:

"AuthenticationError: Invalid API key"

해결 방법 1: API 키 환경 변수 설정 확인

import os HOLYSHEEP_API_KEY = os.environ.get("HOLYSHEEP_API_KEY") if not HOLYSHEEP_API_KEY: HOLYSHEEP_API_KEY = "YOUR_HOLYSHEEP_API_KEY"

해결 방법 2: HolySheep AI 대시보드에서 키 확인

https://www.holysheep.ai/dashboard/api-keys

해결 방법 3: base_url 확인 (가장 흔한 오류)

client = openai.OpenAI( api_key=HOLYSHEEP_API_KEY, base_url="https://api.holysheep.ai/v1" # ✅ 올바른 URL # base_url="https://api.openai.com/v1" # ❌ 잘못된 URL )

키 유효성 검증

try: models = client.models.list() print(f"✓ API 키 유효. 사용 가능한 모델: {len(models.data)}개") except Exception as e: print(f"❌ API 인증 실패: {e}") print(" https://www.holysheep.ai/register 에서 새 키 발급")

오류 3: 주문서 시퀀스 불일치 (Sequence Gap)

# 오류 메시지 예시:

"Sequence mismatch: expected 12345, got 12350"

원인: 데이터 파일에서 메시지가 누락됨

해결 방법 1: 스냅샷 기반 재초기화

class ResilientOrderBook(OrderBook): """시퀀스 갭을 처리하는 주문서 클래스