こんにちは、HolySheep AI テクニカルリサーチャーの田尻です。本日は、暗号資産トレーディングやクオンツ分析において避けて通れない「L2 深度データ」の取得方法について、の実機検証を交えながら詳しく解説します。
私は2024年から約18ヶ月間にわたり、OKX を始めとする主要取引所の板情報(orderbook)取得と分析mundoを重ねてきました。この過程で Tardis(タルディス)の API を多用するようになりましたが、その処理・分析には HolySheep AI を活用しています。本ガイドがその方の参考になれば幸いです。
L2 深度データとは?なぜ重要か
L2 深度データとは、板情報(Order Book)における最良気配値(Best Bid/Ask)から数段階の奥行きを持つ価格・数量情報を指します。OKX の永続契約では、通常20段階の深度データが提供されます。
- 流動性分析:大口注文の気配読める
- 滑り試算:成行注文の約定価格予測
- 市場構造解析:Bid/Ask スプレッドと深さの相関
- アルゴリズム取引:MM Bot やアービトラージ戦略の基盤データ
Tardis Historical API の概要
Tardis(https://tardis.dev)は、主要取引所の歴史的市場データを REST API および WebSocket で提供するSaaSです。OKX 以外にも Bybit、 Binance、Deribit など40以上の取引所に対応しています。
対応データ種別
| データ種別 | 粒度 | 保持期間 | 用途 |
|---|---|---|---|
| L2 Orderbook | .tick | 最大2年 | 深度分析、スプレッド監視 |
| Trades | 1 trade | 無制限 | 約定履歴、 VWAP 計算 |
| OHLCV | 1m-1D | 無制限 | チャート、分析 |
| Liquidations | event | 1年 | 強制決済分析 |
環境構築と前提条件
まずは必要な環境をセットアップします。本ガイドでは Python 3.10+ を前提とします。
# 必要なライブラリのインストール
pip install requests pandas aiohttp asyncio nest-asyncio
プロジェクト構造
mkdir -p tardis_okx_project/{data,logs,analysis}
cd tardis_okx_project
requirements.txt
cat > requirements.txt << 'EOF'
requests==2.31.0
pandas==2.1.4
aiohttp==3.9.1
nest-asyncio==1.5.8
python-dotenv==1.0.0
EOF
OKX 永続契約 L2 深度データ取得の実装
方法1:REST API(バッチダウンロード)
# tardis_client.py
import requests
import pandas as pd
from datetime import datetime, timedelta
import time
import os
from dotenv import load_dotenv
load_dotenv()
class TardisClient:
BASE_URL = "https://tardis.dev/api/v1"
API_TOKEN = os.getenv("TARDIS_API_TOKEN") # 環境変数から取得
def __init__(self):
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {self.API_TOKEN}",
"Content-Type": "application/json"
})
def get_okx_perpetual_symbols(self):
"""OKX 永続契約のシンボル一覧取得"""
url = f"{self.BASE_URL}/exchange-symbols/oku"
response = self.session.get(url)
response.raise_for_status()
symbols = response.json()
# 永続契約(USDT-M Futures)のみをフィルタリング
perpetual_symbols = [
s for s in symbols
if "USDT" in s.get("symbol", "") and "swap" in s.get("symbol", "").lower()
]
return perpetual_symbols
def download_l2_orderbook(
self,
symbol: str,
start_date: datetime,
end_date: datetime,
symbol_id: str = "okx"
):
"""
OKX L2 深度データをダウンロード
Parameters:
symbol: 例 "BTC-USDT-SWAP"
start_date: 開始日時
end_date: 終了日時
symbol_id: 取引所識別子
"""
# ミリ秒タイムスタンプに変換
from_ts = int(start_date.timestamp() * 1000)
to_ts = int(end_date.timestamp() * 1000)
url = f"{self.BASE_URL}/historical/{symbol_id}/{symbol}/orderbook-levels"
params = {
"from": from_ts,
"to": to_ts,
"limit": 10000, # 1リクエストあたりの最大件数
"format": "json"
}
all_data = []
pagination_token = None
print(f"📥 {symbol} L2深度データ取得開始: {start_date} → {end_date}")
while True:
if pagination_token:
params["page_token"] = pagination_token
response = self.session.get(url, params=params)
if response.status_code == 429:
# レートリミット回避
retry_after = int(response.headers.get("Retry-After", 60))
print(f"⏳ レート制限: {retry_after}秒待機")
time.sleep(retry_after)
continue
response.raise_for_status()
data = response.json()
if "data" in data:
all_data.extend(data["data"])
# ページネーション処理
pagination_token = data.get("meta", {}).get("next_page_token")
if not pagination_token:
break
# 次のリクエスト前に待機(レートリミット対策)
time.sleep(0.1)
print(f"✅ 取得完了: {len(all_data):,}件のレコードを処理")
return all_data
def save_to_csv(self, data: list, filename: str):
"""JSON データを CSV に変換して保存"""
if not data:
print("⚠️ 保存するデータがありません")
return None
df = pd.DataFrame(data)
# タイムスタンプをdatetimeに変換
if "timestamp" in df.columns:
df["datetime"] = pd.to_datetime(df["timestamp"], unit="ms")
# 必須カラムの順序整列
columns_order = ["datetime", "timestamp", "symbol", "side", "price", "size"]
available_cols = [c for c in columns_order if c in df.columns]
df = df[available_cols]
filepath = f"data/{filename}"
df.to_csv(filepath, index=False)
print(f"💾 保存完了: {filepath} ({len(df):,}行)")
return filepath
使用例
if __name__ == "__main__":
client = TardisClient()
# BTC-USDT-SWAP の過去1時間のデータを取得
end_time = datetime.utcnow()
start_time = end_time - timedelta(hours=1)
btc_data = client.download_l2_orderbook(
symbol="BTC-USDT-SWAP",
start_date=start_time,
end_date=end_time
)
if btc_data:
client.save_to_csv(btc_data, "btc_usdt_swap_orderbook.csv")
方法2:WebSocket リアルタイム取得
# tardis_websocket.py
import asyncio
import aiohttp
import json
import nest_asyncio
from datetime import datetime
from typing import Callable, Optional
import pandas as pd
nest_asyncio.apply() # Jupyter環境での互換性対応
class TardisWebSocketClient:
WS_URL = "wss://tardis.dev/ws"
def __init__(self, api_token: str):
self.api_token = api_token
self.websocket = None
self.buffer = []
self.max_buffer_size = 10000
self.is_connected = False
async def connect(self):
"""WebSocket 接続確立"""
self.websocket = await aiohttp.ClientSession().ws_connect(
self.WS_URL,
headers={"Authorization": f"Bearer {self.api_token}"}
)
self.is_connected = True
print("🔌 WebSocket 接続完了")
async def subscribe_orderbook(
self,
exchange: str = "okx",
symbol: str = "BTC-USDT-SWAP"
):
"""
L2 Orderbook チャンネルにサブスクライブ
"""
subscribe_message = {
"type": "subscribe",
"channel": "orderbook",
"exchange": exchange,
"symbol": symbol
}
await self.websocket.send_json(subscribe_message)
print(f"📡 購読開始: {exchange}/{symbol} orderbook")
# 購読確認メッセージを受信
response = await self.websocket.receive()
ack = json.loads(response.data)
print(f"📨 購読確認: {ack}")
return ack
async def stream_orderbook(self, duration_seconds: int = 300):
"""
指定秒数分の板情報をストリーミング
Parameters:
duration_seconds: 取得時間(秒)
"""
start_time = datetime.utcnow()
message_count = 0
async for msg in self.websocket:
if msg.type == aiohttp.WSMsgType.CLOSED:
print("❌ WebSocket 接続が切断されました")
break
if msg.type == aiohttp.WSMsgType.ERROR:
print(f"⚠️ WebSocket エラー: {msg.data}")
continue
try:
data = json.loads(msg.data)
message_count += 1
# 板情報をバッファに追加
if data.get("type") == "data":
orderbook_snapshot = data.get("data", {})
self.buffer.append({
"timestamp": datetime.utcnow().isoformat(),
"symbol": data.get("symbol"),
"bids": orderbook_snapshot.get("bids", []),
"asks": orderbook_snapshot.get("asks", []),
"best_bid": orderbook_snapshot.get("bids", [[None]])[0][0],
"best_ask": orderbook_snapshot.get("asks", [[None]])[0][0]
})
# バッファ上限に達したらflush
if len(self.buffer) >= self.max_buffer_size:
self._flush_buffer()
# 経過時間チェック
elapsed = (datetime.utcnow() - start_time).total_seconds()
if elapsed >= duration_seconds:
print(f"⏹ 指定時間 ({duration_seconds}秒) 到達")
break
except json.JSONDecodeError as e:
print(f"⚠️ JSON パースエラー: {e}")
continue
# 最終バッファをflush
self._flush_buffer()
print(f"✅ ストリーミング完了: {message_count:,}件のメッセージを受信")
return self.buffer
def _flush_buffer(self):
"""バッファをCSVに保存"""
if not self.buffer:
return
df = pd.DataFrame(self.buffer)
filename = f"data/orderbook_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}.csv"
df.to_csv(filename, index=False)
print(f"💾 中間保存: {filename} ({len(self.buffer):,}行)")
self.buffer = [] # バッファクリア
async def close(self):
"""接続切断"""
if self.websocket:
await self.websocket.close()
self.is_connected = False
print("🔌 WebSocket 切断完了")
非同期メイン処理
async def main():
import os
from dotenv import load_dotenv
load_dotenv()
api_token = os.getenv("TARDIS_API_TOKEN")
client = TardisWebSocketClient(api_token)
try:
await client.connect()
await client.subscribe_orderbook("okx", "BTC-USDT-SWAP")
await client.stream_orderbook(duration_seconds=60) # 60秒間取得
finally:
await client.close()
実行
if __name__ == "__main__":
asyncio.run(main())
HolySheheep AI でのデータ分析
取得した L2 深度データは、HolySheheep AI を活用することで高度な分析が可能です。HolySheheep AI は業界最安水準の料金耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿準耿