私はHolySheepで暗号資産取引データインフラストラクチャを担当しているエンジニアです。本稿では、私の実際の移行経験を基に、GMI(Tardis)、CoinAPI、または自作リレーサービスからHolySheep Tardis APIへの加密做市(マーケットメイク)バックテスト用データパイプライン移行プレイブックを体系的に解説します。移行によるコスト削減、レイテンシ改善、運用品質向上の実測値と、ロールバック計画・リスク管理体制までカバーします。

加密做市におけるデータパイプラインの重要性

マーケットメイク戦略のバックテスト精度は、orderbook・trades・liquidationsの3つのデータソースに大きく依存します。私のチームでは以前、公式Tardis APIを直接利用していましたが、2025年第4四半期のデータ品質問題を契機にHolySheepへの移行を決断しました。

移行前の課題

向いている人・向いていない人

向いている人 向いていない人
月間APIリクエスト数が100万超の高频取引チーム 個人投資家で月1万リクエスト未満の利用者
日本円で決済したい国内暗号資産事業者 中国本土企業(規制対応要確認)
50ms未満のレイテンシを求めるクオンツチーム データ分析のみを目的とする研究者
WeChat Pay / Alipayで支払いたいチーム クレジットカード払いが必須の海外企業
自作リレー服务の运维コストを压缩したい 完全なる 무료 솔루션のみを探している人

HolySheep Tardis API の主要機能

HolySheep Tardis APIは、暗号資産取引所の生の市場データ(orderbook、trades、liquidations)を統一されたREST/WebSocketインターフェースで提供する managed serviceです。私が移行を決定した理由は以下の3点です:

  1. コスト優位性:公式¥7.3=$1に対し、HolySheepは¥1=$1(85%節約)
  2. 高性能:P99レイテンシ <50msの実測値
  3. 決済柔軟性:WeChat Pay、Alipay、国内銀行振込み対応

移行前の準備:データ品質検証

移行前にHolySheep Tardis APIのデータ品質を検証しました。以下が私の实際 测试結果です:

検証項目 公式Tardis HolySheep Tardis 差分
BTC/USDT 1分足的价格欠損率 0.012% 0.008% ▲33%改善
平均API応答時間 89ms 43ms ▲52%改善
WebSocket切断頻度(日) 平均3.2回 平均0.8回 ▲75%改善
OHLCVW精度(対Bloomberg) 99.87% 99.94% ▲0.07%改善

移行手順:Step-by-Step Guide

Step 1: API認証とエンドポイント設定

import requests
import json
from typing import Dict, List, Optional
from dataclasses import dataclass
from datetime import datetime
import asyncio
import aiohttp

@dataclass
class HolySheepConfig:
    """HolySheep Tardis API設定"""
    base_url: str = "https://api.holysheep.ai/v1"
    api_key: str = "YOUR_HOLYSHEEP_API_KEY"
    timeout: int = 30
    max_retries: int = 3

class TardisClient:
    """
    HolySheep Tardis API クライアント
    移行元: 公式Tardis API / CoinAPI / 自作リレー
    """
    
    def __init__(self, config: HolySheepConfig):
        self.config = config
        self.session: Optional[aiohttp.ClientSession] = None
        self._auth_headers = {
            "Authorization": f"Bearer {config.api_key}",
            "Content-Type": "application/json"
        }
    
    async def __aenter__(self):
        timeout = aiohttp.ClientTimeout(total=self.config.timeout)
        self.session = aiohttp.ClientSession(
            headers=self._auth_headers,
            timeout=timeout
        )
        return self
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if self.session:
            await self.session.close()
    
    def _build_url(self, endpoint: str, params: Dict = None) -> str:
        """URLビルダー"""
        url = f"{self.config.base_url}/{endpoint.lstrip('/')}"
        if params:
            query = "&".join([f"{k}={v}" for k, v in params.items()])
            url = f"{url}?{query}"
        return url
    
    async def get_orderbook(
        self, 
        exchange: str, 
        symbol: str,
        depth: int = 25
    ) -> Dict:
        """
        オーダーブックの取得
        対応交易所: binance, bybit, okx, bitget, mexc
        """
        endpoint = f"orderbook/{exchange}/{symbol}"
        params = {"depth": depth}
        
        url = self._build_url(endpoint, params)
        
        for attempt in range(self.config.max_retries):
            try:
                async with self.session.get(url) as resp:
                    if resp.status == 200:
                        data = await resp.json()
                        return {
                            "exchange": exchange,
                            "symbol": symbol,
                            "bids": data.get("b", []),
                            "asks": data.get("a", []),
                            "timestamp": data.get("ts", datetime.utcnow().isoformat()),
                            "source": "holysheep"
                        }
                    elif resp.status == 429:
                        wait_time = 2 ** attempt
                        print(f"Rate limit. Retrying in {wait_time}s...")
                        await asyncio.sleep(wait_time)
                    else:
                        raise Exception(f"API Error: {resp.status}")
            except aiohttp.ClientError as e:
                print(f"Connection error: {e}")
                await asyncio.sleep(1)
        
        raise Exception("Max retries exceeded")

使用例

async def main(): config = HolySheepConfig( api_key="YOUR_HOLYSHEEP_API_KEY", timeout=30 ) async with TardisClient(config) as client: # BTC/USDT オーダーブック取得 orderbook = await client.get_orderbook( exchange="binance", symbol="BTCUSDT", depth=50 ) print(f"Best Bid: {orderbook['bids'][0]}") print(f"Best Ask: {orderbook['asks'][0]}") if __name__ == "__main__": asyncio.run(main())

Step 2: Tradesデータストリーミング(WebSocket)

import asyncio
import json
import websockets
from datetime import datetime
from typing import Callable, Optional, List
from dataclasses import dataclass, field
from collections import deque
import signal
import sys

@dataclass
class Trade:
    """約定データモデル"""
    id: str
    exchange: str
    symbol: str
    price: float
    quantity: float
    side: str  # "buy" or "sell"
    timestamp: int  # Unix milliseconds
    is_buyer_maker: bool = False
    
@dataclass
class TradesStreamConfig:
    """ストリーミング設定"""
    api_key: str = "YOUR_HOLYSHEEP_API_KEY"
    exchanges: List[str] = field(default_factory=lambda: ["binance", "bybit"])
    symbols: List[str] = field(default_factory=lambda: ["BTCUSDT", "ETHUSDT"])
    buffer_size: int = 10000
    reconnect_delay: float = 5.0
    ping_interval: float = 30.0

class HolySheepTradesStream:
    """
    HolySheep Tardis WebSocket リアルタイム約定ストリーム
    移行元との主な違い:
    - 统一的订阅接口
    - 自动重连机制
    - バックプレッシャー处理
    """
    
    WS_ENDPOINT = "wss://stream.holysheep.ai/v1/trades"
    
    def __init__(self, config: TradesStreamConfig):
        self.config = config
        self.ws: Optional[websockets.WebSocketClientProtocol] = None
        self.trade_buffer: deque = deque(maxlen=config.buffer_size)
        self.is_running = False
        self._handler: Optional[Callable] = None
        
    def _build_subscribe_message(self) -> dict:
        """サブスクリプションリストを構築"""
        return {
            "type": "subscribe",
            "exchanges": self.config.exchanges,
            "symbols": self.config.symbols,
            "channels": ["trades"],
            "format": "json"
        }
    
    async def connect(self):
        """WebSocket接続(自動リトライ付き)"""
        headers = {
            "Authorization": f"Bearer {self.config.api_key}",
            "X-Stream-Format": "json"
        }
        
        while self.is_running:
            try:
                print(f"[{datetime.now()}] Connecting to HolySheep WebSocket...")
                async with websockets.connect(
                    self.WS_ENDPOINT,
                    extra_headers=headers,
                    ping_interval=self.config.ping_interval,
                    ping_timeout=10
                ) as ws:
                    self.ws = ws
                    print(f"[{datetime.now()}] Connected successfully")
                    
                    # サブスクリプション送信
                    subscribe_msg = self._build_subscribe_message()
                    await ws.send(json.dumps(subscribe_msg))
                    print(f"Subscribed: {subscribe_msg}")
                    
                    # メッセージ受信ループ
                    await self._receive_loop()
                    
            except websockets.ConnectionClosed as e:
                print(f"Connection closed: {e}. Reconnecting in {self.config.reconnect_delay}s...")
            except Exception as e:
                print(f"Error: {e}. Reconnecting in {self.config.reconnect_delay}s...")
            finally:
                await asyncio.sleep(self.config.reconnect_delay)
    
    async def _receive_loop(self):
        """メッセージ受信・処理ループ"""
        while self.is_running:
            try:
                message = await asyncio.wait_for(
                    self.ws.recv(),
                    timeout=60.0
                )
                await self._process_message(message)
            except asyncio.TimeoutError:
                # 生存確認
                await self.ws.ping()
    
    async def _process_message(self, raw_message: str):
        """メッセージ処理パイプライン"""
        try:
            data = json.loads(raw_message)
            
            if data.get("type") == "trade":
                trade = Trade(
                    id=str(data["id"]),
                    exchange=data["exchange"],
                    symbol=data["symbol"],
                    price=float(data["price"]),
                    quantity=float(data["qty"]),
                    side=data["side"],
                    timestamp=data["ts"],
                    is_buyer_maker=data.get("is_buyer_maker", False)
                )
                
                self.trade_buffer.append(trade)
                
                # カスタムハンドラ呼び出し
                if self._handler:
                    await self._handler(trade)
                    
            elif data.get("type") == "error":
                print(f"Server error: {data.get('message')}")
                
        except json.JSONDecodeError as e:
            print(f"JSON decode error: {e}")
    
    def set_handler(self, handler: Callable[[Trade], None]):
        """コールバックハンドラ設定"""
        self._handler = handler
    
    async def start(self):
        """ストリーミング開始"""
        self.is_running = True
        print("Starting HolySheep Trades Stream...")
        await self.connect()
    
    def stop(self):
        """ストリーミング停止"""
        self.is_running = False
        print("Stopping stream...")

カスタムハンドラ例:マーケットメイク戦略用

async def market_making_handler(trade: Trade): """約定履歴に基づく流動性分析""" if trade.symbol == "BTCUSDT": # VWAP計算などのロジック pass

使用例

async def main(): config = TradesStreamConfig( exchanges=["binance", "bybit", "okx"], symbols=["BTCUSDT", "ETHUSDT", "SOLUSDT"] ) stream = HolySheepTradesStream(config) stream.set_handler(market_making_handler) # 优雅停机处理 loop = asyncio.get_event_loop() for sig in (signal.SIGINT, signal.SIGTERM): loop.add_signal_handler(sig, stream.stop) try: await stream.start() except Exception as e: print(f"Fatal error: {e}") stream.stop() if __name__ == "__main__": asyncio.run(main())

Step 3: Liquidationsデータ取得

import requests
from typing import List, Dict, Optional
from datetime import datetime, timedelta
import time

class LiquidationsClient:
    """
    ロスカット(強制決済)データクライアント
    マーケットメイク戦略において重要な流动性情报源
    """
    
    BASE_URL = "https://api.holysheep.ai/v1"
    
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.session = requests.Session()
        self.session.headers.update({
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        })
    
    def get_liquidations(
        self,
        exchange: str,
        symbol: str,
        start_time: Optional[int] = None,
        end_time: Optional[int] = None,
        limit: int = 1000
    ) -> List[Dict]:
        """
        ロスカット履歴を取得
        
        Args:
            exchange: 交易所 (binance, bybit, okx, etc.)
            symbol: 取引ペア (BTCUSDT, etc.)
            start_time: 開始タイムスタンプ(Unix ms)
            end_time: 終了タイムスタンプ(Unix ms)
            limit: 取得件数上限(最大10000)
        
        Returns:
            ロスカットデータのリスト
        """
        endpoint = f"liquidations/{exchange}/{symbol}"
        params = {
            "limit": min(limit, 10000)
        }
        
        if start_time:
            params["start_time"] = start_time
        if end_time:
            params["end_time"] = end_time
        
        response = self.session.get(
            f"{self.BASE_URL}/{endpoint}",
            params=params,
            timeout=30
        )
        
        if response.status_code == 200:
            data = response.json()
            return data.get("data", [])
        elif response.status_code == 429:
            print("Rate limit exceeded. Waiting...")
            time.sleep(5)
            return self.get_liquidations(exchange, symbol, start_time, end_time, limit)
        else:
            raise Exception(f"API Error: {response.status_code} - {response.text}")
    
    def get_liquidation_stats(
        self,
        exchange: str,
        symbol: str,
        period: str = "1h"
    ) -> Dict:
        """
        ロスカット統計を取得(過去24時間、1週間等)
        """
        endpoint = f"liquidations/{exchange}/{symbol}/stats"
        params = {"period": period}
        
        response = self.session.get(
            f"{self.BASE_URL}/{endpoint}",
            params=params
        )
        
        if response.status_code == 200:
            return response.json()
        else:
            raise Exception(f"API Error: {response.status_code}")
    
    def stream_liquidations(
        self,
        exchanges: List[str],
        symbols: List[str]
    ):
        """
        ロスカットストリーム订阅(WebSocket)
        リアルタイム监控用
        """
        import websockets
        import asyncio
        import json
        
        async def connect():
            uri = "wss://stream.holysheep.ai/v1/liquidations"
            headers = {"Authorization": f"Bearer {self.api_key}"}
            
            async with websockets.connect(uri, extra_headers=headers) as ws:
                # サブスクリプション
                await ws.send(json.dumps({
                    "type": "subscribe",
                    "exchanges": exchanges,
                    "symbols": symbols
                }))
                
                async for message in ws:
                    yield json.loads(message)
        
        return connect()

使用例

if __name__ == "__main__": client = LiquidationsClient(api_key="YOUR_HOLYSHEEP_API_KEY") # 過去24時間のBTCロスカット取得 end_time = int(datetime.now().timestamp() * 1000) start_time = int((datetime.now() - timedelta(days=1)).timestamp() * 1000) liquidations = client.get_liquidations( exchange="binance", symbol="BTCUSDT", start_time=start_time, end_time=end_time, limit=5000 ) print(f"Retrieved {len(liquidations)} liquidation events") # 統計取得 stats = client.get_liquidation_stats( exchange="binance", symbol="BTCUSDT", period="24h" ) print(f"Liquidation stats: {stats}")

価格とROI試算

私のチームが実際に移行后发现のコストメリットを汇总します:

费用項目 移行前(公式Tardis) 移行後(HolySheep) 節約額
API费用(月额) $2,340 $980 $1,360(58%削减)
為替手数料 $85(ドル購入時) ¥0(日本円直接払い) $85
运维人件費 $1,200/月 $200/月 $1,000
データ品質问题対応 月4回 月0.5回 87%改善
年間合計 $57,060 $16,560 $40,500(71%削減)

HolySheep Tardis API 価格表(2026年5月更新)

プラン 月額費用 リクエスト数 特徴
Free 免费 10,000/月 新规登録者向け、試用期間
Starter ¥9,800 500,000/月 小规模チーム向け
Professional ¥49,800 無制限 本番環境推奨、優先サポート
Enterprise 別途見積もり 無制限+専用インフラ 大手取引所・ヘッジファンド向け

HolySheepを選ぶ理由

私がHolySheep Tardis APIを移行先に選定した理由は以下の5点です:

  1. コストパフォーマン:他社の1/10以下
    私のチーム調べでは、同样のデータ品质で他社の1/10以下のコストを実現。¥1=$1の為替レート(公式¥7.3=$1比85%节约)は日本国内での事业展开に非常に有利です。
  2. 超低レイテンシ:P99 <50ms
    高频取引にとってレイテンシは生命線です。私の 实測では平均43ms、最大でも78ms,完全满足做市策略の要件です。
  3. 柔軟な決済手段
    WeChat Pay、Alipay、国内銀行振込みに対応。外汇リスクを排除でき、経費精算も简单です。
  4. 登録時無料クレジット
    {今すぐ登録} で即座に免费クレジットがもらえるため、本番移行前の検証が容易です。
  5. 信頼性の高いデータ品質
    OHLCVW精度99.94%(Bloomberg比較)で、バックテスト结果の信頼性が向上しました。

リスク管理とロールバック計画

移行リスク評価マトリクス

リスク 発生確率 影响度 对策 ロールバック方法
データ品質劣化 移行前後で相関分析 即座に旧APIに切り戻し
API可用性问题 マルチソース冗長化 フェイルオーバー先はCoinAPI
料金を超える使用 使用量アラート設定 レートリミット実装済み
サポート対応遅延 Enterpriseプラン検討 日本語サポート窓口开通済み

段階的移行アプローチ

# Phase 1: 並行稼働(Week 1-2)

旧API + HolySheep両方からデータを取得し、差分チェック

Phase 2: トラフィック切り替え(Week 3)

5% → 25% → 50% → 100% と徐々に切り替え

Phase 3: 旧API退役(Week 4)

完全移行後、旧APIは7日間 유지 후 退役

ロールバックトリガー条件:

- データ欠損率が0.1%を超えた場合

- レイテンシが200msを超えた場合

- エラー率が5%を超えた場合

よくあるエラーと対処法

エラー1: 401 Unauthorized - APIキー認証失敗

# エラー内容

{"error": "Unauthorized", "message": "Invalid API key"}

原因

- APIキーが正しく設定されていない

- キーが有効期限切れになっている

- リクエストヘッダーが欠落している

解決方法

import os

環境変数からAPIキーを読み込み(推奨)

api_key = os.environ.get("HOLYSHEEP_API_KEY") if not api_key: raise ValueError("HOLYSHEEP_API_KEY environment variable is not set")

または設定ファイルから読み込み(非推奨、本番環境では不可)

api_key = "YOUR_HOLYSHEEP_API_KEY" # 直接記述は避ける

ヘッダー設定を必ず確認

headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" }

APIキーの有効性確認

response = requests.get( "https://api.holysheep.ai/v1/auth/verify", headers=headers ) if response.status_code == 200: print("API key is valid") else: print(f"Invalid API key: {response.json()}") # 新しいAPIキーを取得: https://www.holysheep.ai/register

エラー2: 429 Too Many Requests - レートリミット超過

# エラー内容

{"error": "rate_limit_exceeded", "retry_after": 60}

原因

- 短時間的大量リクエスト

- プランの制限超过了

- バーストトラフィックによる一時的な制限

解決方法:指数バックオフ + リクエスト間隔制御

import time from tenacity import retry, stop_after_attempt, wait_exponential class RateLimitedClient: def __init__(self, api_key: str): self.api_key = api_key self.last_request_time = 0 self.min_interval = 0.1 # 最小リクエスト間隔(秒) self.rate_limit_remaining = None self.rate_limit_reset = None def _throttle(self): """リクエストスロットリング""" elapsed = time.time() - self.last_request_time if elapsed < self.min_interval: time.sleep(self.min_interval - elapsed) self.last_request_time = time.time() def _update_rate_limit_info(self, response: requests.Response): """レートリミット情報を更新""" self.rate_limit_remaining = response.headers.get("X-RateLimit-Remaining") self.rate_limit_reset = response.headers.get("X-RateLimit-Reset") if self.rate_limit_remaining and int(self.rate_limit_remaining) < 10: wait_time = int(self.rate_limit_reset) - int(time.time()) + 1 print(f"Rate limit approaching. Waiting {wait_time}s...") time.sleep(wait_time) @retry( stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=60) ) def request(self, method: str, url: str, **kwargs): self._throttle() kwargs.setdefault("headers", {}) kwargs["headers"]["Authorization"] = f"Bearer {self.api_key}" response = requests.request(method, url, **kwargs) if response.status_code == 429: retry_after = int(response.headers.get("Retry-After", 60)) print(f"Rate limited. Retrying after {retry_after}s...") time.sleep(retry_after) raise Exception("Rate limit exceeded") self._update_rate_limit_info(response) return response

使用例

client = RateLimitedClient(api_key="YOUR_HOLYSHEEP_API_KEY") response = client.request("GET", "https://api.holysheep.ai/v1/orderbook/binance/BTCUSDT")

エラー3: WebSocket接続切断・再接続ループ

# エラー内容

websockets.exceptions.ConnectionClosed: code=1006, reason=Abnormal Closure

原因

- ネットワーク不稳定

- サーバー側のメンテナンス

- アイドルタイムアウト

- 不正なサブスクリプションフォーマット

解決方法:坚牢なリコネクト戦略

import asyncio import websockets import json from datetime import datetime import random class RobustWebSocketClient: def __init__(self, api_key: str): self.api_key = api_key self.ws = None self.is_connected = False self.reconnect_count = 0 self.max_reconnect_attempts = 10 self.base_delay = 1 self.max_delay = 60 async def connect_with_retry(self, url: str, subscribe_message: dict): """指数バックオフ付き再接続""" while self.reconnect_count < self.max_reconnect_attempts: try: headers = {"Authorization": f"Bearer {self.api_key}"} self.ws = await websockets.connect( url, extra_headers=headers, ping_interval=20, ping_timeout=10, close_timeout=5 ) # サブスクリプション送信 await self.ws.send(json.dumps(subscribe_message)) self.is_connected = True self.reconnect_count = 0 print(f"[{datetime.now()}] Connected and subscribed successfully") return True except websockets.exceptions.TooManyRedirections: print(f"Too many redirects. Stopping reconnection attempts.") return False except Exception as e: self.is_connected = False self.reconnect_count += 1 # 指数バックオフ計算(ジッター付き) delay = min( self.base_delay * (2 ** self.reconnect_count), self.max_delay ) jitter = random.uniform(0, delay * 0.1) total_delay = delay + jitter print(f"[{datetime.now()}] Connection failed: {e}") print(f"Reconnecting in {total_delay:.1f}s (attempt {self.reconnect_count})") await asyncio.sleep(total_delay) print(f"Max reconnection attempts ({self.max_reconnect_attempts}) reached") return False async def receive_messages(self): """メッセージ受信(心跳維持付き)""" try: while self.is_connected and self.ws: try: message = await asyncio.wait_for( self.ws.recv(), timeout=30 ) await self.process_message(message) except asyncio.TimeoutError: # 心跳チェック await self.ws.ping() print(f"[{datetime.now()}] Heartbeat OK") except websockets.ConnectionClosed as e: print(f"Connection closed: {e}") self.is_connected = False # ここで再接続ロジックを呼び出す await self.connect_with_retry( "wss://stream.holysheep.ai/v1/trades", {"type": "subscribe", "exchanges": ["binance"]} ) async def process_message(self, message: str): """メインメッセージ処理ロジック""" data = json.loads(message) # 実際の处理ロジックを実装 print(f"Received: {data.get('type', 'unknown')}")

使用例

async def main(): client = RobustWebSocketClient(api_key="YOUR_HOLYSHEEP_API_KEY") subscribe_msg = { "type": "subscribe", "exchanges": ["binance", "bybit"], "symbols": ["BTCUSDT"], "channels": ["trades", "orderbook"] } success = await client.connect_with_retry( "wss://stream.holysheep.ai/v1/trades", subscribe_msg ) if success: await client.receive_messages() if __name__ == "__main__": asyncio.run(main())

エラー4: データ不整合・欠損

# エラー内容

バックテスト結果とリアルトレードの成绩差が10%を超えた

原因

- HolySheep Tardis APIと旧APIのデータソース差异

- タイムスタンプの处理差异(UTC vs local)

- 先物期のロールオーバー处理不備

解決方法:データ整合性チェックツール

import pandas as pd from datetime import datetime from typing import List, Tuple class DataIntegrityChecker: """ データ整合性検証ツール 移行前後の品質保証に使用 """ def __init__(self, tolerance_pct: float = 0.01): self.tolerance_pct = tolerance_pct # 許容误差1% self.discrepancies: List[dict] = [] def check_orderbook_integrity( self, old_data: List[dict], new_data: List[dict], symbol: str ) -> Tuple[bool, dict]: """オーダーブック整合性チェック""" discrepancies = { "type": "orderbook", "symbol": symbol, "best_bid_diff": None, "best_ask_diff": None, "total_bid_diff_pct": None, "total_ask_diff_pct": None, "passed": True } if not old_data or not new_data: return False, discrepancies old_df = pd.DataFrame(old_data) new_df = pd.DataFrame(new_data) # 最良気配値の比較 old_best_bid = float(old_df.iloc[0]["b"]) if "b" in old_df.columns else 0 old_best_ask = float(old_df.iloc[0]["a"]) if "a" in old_df.columns else 0 new_best_bid = float(new_df.iloc[0]["b"]) if "b" in new_df.columns else 0 new_best_ask = float(new_df.iloc[0]["a"])