Trong thế giới quantitative trading, chất lượng dữ liệu quyết định 90% thành bại của chiến lược. Bài viết này tôi chia sẻ kinh nghiệm thực chiến 3 năm trong việc thu thập, xử lý và tối ưu dữ liệu lịch sử từ Bybit — một trong những sàn có khối lượng giao dịch futures lớn nhất thế giới.

Tại sao chọn Tardis cho dữ liệu Bybit

Sau khi thử qua nhiều nhà cung cấp (CoinAPI, CryptoCompare, Binance Historical), tôi nhận ra Tardis Machine có 3 lợi thế cạnh tranh rõ rệt:

Kiến trúc hệ thống thu thập dữ liệu

Trước khi đi vào code, tôi muốn chia sẻ kiến trúc tổng thể mà tôi đã xây dựng cho team quantitative tại công ty trước đây:

┌─────────────────────────────────────────────────────────────┐
│                    HỆ THỐNG DATA PIPELINE                    │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌──────────┐    ┌──────────────┐    ┌──────────────────┐   │
│  │  Tardis  │───▶│  PostgreSQL  │───▶│  Backtesting     │   │
│  │  API     │    │  (Time-series│    │  Engine          │   │
│  │          │    │   Extension) │    │  (Backtrader/    │   │
│  └──────────┘    └──────────────┘    │   VectorBT)      │   │
│                                       └──────────────────┘   │
│                                              │               │
│                                              ▼               │
│                                    ┌──────────────────┐      │
│                                    │  HolySheep AI    │      │
│                                    │  Strategy Opt    │      │
│                                    └──────────────────┘      │
└─────────────────────────────────────────────────────────────┘

Cài đặt và Authentication

Đầu tiên, bạn cần tạo tài khoản Tardis và lấy API key. Sau đó cài đặt các dependencies cần thiết:

pip install requests pandas pyarrow sqlalchemy asyncpg python-dotenv aiohttp asyncio

File cấu hình môi trường .env:

# Tardis API Configuration
TARDIS_API_KEY=your_tardis_api_key_here
TARDIS_BASE_URL=https://api.tardis.ml/v1

Database Configuration

POSTGRES_HOST=localhost POSTGRES_PORT=5432 POSTGRES_DB=bybit_klines POSTGRES_USER=quant_user POSTGRES_PASSWORD=secure_password

HolySheep AI for Strategy Optimization

HOLYSHEEP_API_KEY=YOUR_HOLYSHEEP_API_KEY HOLYSHEEP_BASE_URL=https://api.holysheep.ai/v1

Export dữ liệu K-line từ Bybit

Đây là phần core của bài viết. Tôi sẽ chia sẻ code production-ready với xử lý rate limiting, retry logic và batch processing.

import requests
import pandas as pd
import time
import logging
from datetime import datetime, timedelta
from typing import List, Dict, Optional
from dataclasses import dataclass
from concurrent.futures import ThreadPoolExecutor, as_completed

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@dataclass
class BybitKlineConfig:
    """Cấu hình cho việc export dữ liệu K-line từ Bybit qua Tardis"""
    symbol: str = "BTCUSDT"
    interval: str = "1m"  # 1m, 5m, 15m, 1h, 4h, 1d
    start_date: str = "2024-01-01"
    end_date: str = "2024-12-31"
    exchange: str = "bybit"
    
    def validate(self) -> bool:
        valid_intervals = ["1m", "5m", "15m", "1h", "4h", "1d"]
        if self.interval not in valid_intervals:
            raise ValueError(f"Interval must be one of {valid_intervals}")
        return True


class TardisBybitExporter:
    """
    Tardis Bybit K-line Data Exporter
    Author: 3 năm kinh nghiệm Quantitative Trading
    """
    
    BASE_URL = "https://api.tardis.ml/v1"
    RATE_LIMIT = 10  # requests per second
    MAX_RETRIES = 3
    BATCH_SIZE = 1000  # records per request
    
    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"
        })
        self.last_request_time = 0
        
    def _rate_limit_wait(self):
        """Đảm bảo không vượt quá rate limit của API"""
        elapsed = time.time() - self.last_request_time
        if elapsed < (1 / self.RATE_LIMIT):
            time.sleep((1 / self.RATE_LIMIT) - elapsed)
        self.last_request_time = time.time()
    
    def _make_request(self, endpoint: str, params: Dict) -> Optional[Dict]:
        """Thực hiện request với retry logic"""
        for attempt in range(self.MAX_RETRIES):
            try:
                self._rate_limit_wait()
                response = self.session.get(
                    f"{self.BASE_URL}{endpoint}",
                    params=params,
                    timeout=30
                )
                
                if response.status_code == 200:
                    return response.json()
                elif response.status_code == 429:
                    # Rate limited - wait and retry
                    wait_time = int(response.headers.get("Retry-After", 60))
                    logger.warning(f"Rate limited. Waiting {wait_time}s...")
                    time.sleep(wait_time)
                elif response.status_code == 404:
                    logger.error(f"Data not found for params: {params}")
                    return None
                else:
                    logger.error(f"API Error {response.status_code}: {response.text}")
                    
            except requests.exceptions.RequestException as e:
                logger.error(f"Request failed (attempt {attempt + 1}): {e}")
                if attempt < self.MAX_RETRIES - 1:
                    time.sleep(2 ** attempt)  # Exponential backoff
                    
        return None
    
    def get_historical_klines(
        self, 
        config: BybitKlineConfig,
        start_timestamp: int,
        end_timestamp: int
    ) -> List[Dict]:
        """
        Lấy dữ liệu K-line lịch sử trong một khoảng thời gian
        
        Args:
            config: Cấu hình export
            start_timestamp: Thời điểm bắt đầu (milliseconds)
            end_timestamp: Thời điểm kết thúc (milliseconds)
            
        Returns:
            List chứa các candle data
        """
        all_klines = []
        current_start = start_timestamp
        
        while current_start < end_timestamp:
            params = {
                "exchange": config.exchange,
                "symbol": config.symbol,
                "interval": config.interval,
                "start": current_start,
                "end": end_timestamp,
                "limit": self.BATCH_SIZE
            }
            
            data = self._make_request("/klines", params)
            
            if data and "data" in data:
                klines = data["data"]
                all_klines.extend(klines)
                
                # Cập nhật cursor cho request tiếp theo
                if klines:
                    last_candle = klines[-1]
                    current_start = last_candle["timestamp"] + 1
                    
                logger.info(f"Fetched {len(klines)} klines. Total: {len(all_klines)}")
            else:
                break
                
        return all_klines
    
    def export_to_dataframe(self, klines: List[Dict]) -> pd.DataFrame:
        """Chuyển đổi dữ liệu API thành DataFrame pandas"""
        
        df = pd.DataFrame(klines)
        
        # Chuẩn hóa các cột
        if not df.empty:
            df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms")
            df["open_time"] = pd.to_datetime(df["open_time"], unit="ms")
            df["close_time"] = pd.to_datetime(df["close_time"], unit="ms")
            
            # Chuyển đổi kiểu dữ liệu
            numeric_cols = ["open", "high", "low", "close", "volume"]
            for col in numeric_cols:
                if col in df.columns:
                    df[col] = pd.to_numeric(df[col], errors="coerce")
            
            df = df.sort_values("timestamp").reset_index(drop=True)
            
        return df


========== SỬ DỤNG ==========

if __name__ == "__main__": # Khởi tạo exporter exporter = TardisBybitExporter(api_key="your_tardis_api_key") # Cấu hình export config = BybitKlineConfig( symbol="BTCUSDT", interval="1m", start_date="2024-06-01", end_date="2024-12-31" ) config.validate() # Chuyển đổi ngày thành timestamp start_ts = int(datetime.strptime(config.start_date, "%Y-%m-%d").timestamp() * 1000) end_ts = int(datetime.strptime(config.end_date, "%Y-%m-%d").timestamp() * 1000) # Export dữ liệu klines = exporter.get_historical_klines(config, start_ts, end_ts) df = exporter.export_to_dataframe(klines) # Lưu thành file parquet để tiết kiệm storage df.to_parquet(f"bybit_{config.symbol}_{config.interval}.parquet", index=False) logger.info(f"Exported {len(df)} candles to parquet file") # Thống kê print(f""" === DATA EXPORT SUMMARY === Symbol: {config.symbol} Interval: {config.interval} Date Range: {config.start_date} - {config.end_date} Total Candles: {len(df):,} Date Range: {df['timestamp'].min()} to {df['timestamp'].max()} File Size: {pd.io.common.get_filepath_or_buffer(f'bybit_{config.symbol}_{config.interval}.parquet')[1] or 'N/A'} """)

Tối ưu hiệu suất với Async Processing

Với dataset lớn (hơn 10 triệu rows), bạn cần sử dụng async để tăng tốc độ download. Dưới đây là phiên bản async với benchmark thực tế:

import asyncio
import aiohttp
import pandas as pd
from datetime import datetime, timedelta
from typing import List, Dict, Tuple
import json
from dataclasses import dataclass
import time

@dataclass
class AsyncExportConfig:
    """Cấu hình cho async exporter - tối ưu performance"""
    max_concurrent_requests: int = 5
    request_timeout: int = 60
    retry_count: int = 5
    backoff_factor: float = 2.0
    semaphore_limit: int = 5


class AsyncTardisExporter:
    """
    Async Tardis Bybit K-line Data Exporter
    Hỗ trợ concurrent requests với semaphore pattern
    """
    
    BASE_URL = "https://api.tardis.ml/v1"
    
    def __init__(self, api_key: str, config: AsyncExportConfig = None):
        self.api_key = api_key
        self.config = config or AsyncExportConfig()
        self.session: aiohttp.ClientSession = None
        self.semaphore = asyncio.Semaphore(self.config.semaphore_limit)
        
    async def __aenter__(self):
        connector = aiohttp.TCPConnector(
            limit=self.config.max_concurrent_requests,
            ttl_dns_cache=300
        )
        timeout = aiohttp.ClientTimeout(total=self.config.request_timeout)
        self.session = aiohttp.ClientSession(
            connector=connector,
            timeout=timeout,
            headers={
                "Authorization": f"Bearer {self.api_key}",
                "Content-Type": "application/json"
            }
        )
        return self
        
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if self.session:
            await self.session.close()
    
    async def _fetch_with_retry(
        self, 
        session: aiohttp.ClientSession,
        params: Dict
    ) -> Optional[Dict]:
        """Fetch với exponential backoff retry"""
        
        async def _single_request():
            async with self.semaphore:  # Limit concurrent requests
                async with session.get(
                    f"{self.BASE_URL}/klines",
                    params=params
                ) as response:
                    if response.status == 200:
                        return await response.json()
                    elif response.status == 429:
                        retry_after = int(response.headers.get("Retry-After", 60))
                        await asyncio.sleep(retry_after)
                        return None
                    else:
                        return None
        
        for attempt in range(self.config.retry_count):
            try:
                result = await _single_request()
                if result:
                    return result
                await asyncio.sleep(self.config.backoff_factor ** attempt)
            except Exception as e:
                if attempt == self.config.retry_count - 1:
                    raise
                await asyncio.sleep(self.config.backoff_factor ** attempt)
                
        return None
    
    async def _fetch_time_range(
        self,
        session: aiohttp.ClientSession,
        symbol: str,
        interval: str,
        start_ts: int,
        end_ts: int
    ) -> List[Dict]:
        """Fetch dữ liệu trong một khoảng thời gian cụ thể"""
        params = {
            "exchange": "bybit",
            "symbol": symbol,
            "interval": interval,
            "start": start_ts,
            "end": end_ts,
            "limit": 1000
        }
        
        result = await self._fetch_with_retry(session, params)
        return result.get("data", []) if result else []
    
    async def export_parallel(
        self,
        symbol: str,
        interval: str,
        start_ts: int,
        end_ts: int,
        chunk_days: int = 30
    ) -> List[Dict]:
        """
        Export dữ liệu với parallel fetching
        Chia nhỏ thành các chunk theo ngày để tận dụng concurrency
        """
        
        # Tính toán các chunk cần fetch
        chunk_size_ms = chunk_days * 24 * 60 * 60 * 1000
        chunks = []
        current_ts = start_ts
        
        while current_ts < end_ts:
            chunk_end = min(current_ts + chunk_size_ms, end_ts)
            chunks.append((current_ts, chunk_end))
            current_ts = chunk_end
            
        logger.info(f"Fetching {len(chunks)} chunks in parallel...")
        
        all_data = []
        start_time = time.time()
        
        async with self:
            tasks = [
                self._fetch_time_range(
                    self.session,
                    symbol,
                    interval,
                    start,
                    end
                )
                for start, end in chunks
            ]
            
            # Execute all tasks concurrently
            results = await asyncio.gather(*tasks, return_exceptions=True)
            
            for result in results:
                if isinstance(result, list):
                    all_data.extend(result)
                elif isinstance(result, Exception):
                    logger.error(f"Chunk fetch failed: {result}")
        
        elapsed = time.time() - start_time
        logger.info(f"Exported {len(all_data)} records in {elapsed:.2f}s")
        logger.info(f"Average speed: {len(all_data)/elapsed:.0f} records/second")
        
        return all_data


async def benchmark_async_exporter():
    """Benchmark async vs sync exporter"""
    
    api_key = "your_tardis_api_key"
    symbol = "BTCUSDT"
    interval = "1m"
    
    # Test range: 6 tháng dữ liệu
    end_ts = int(datetime.now().timestamp() * 1000)
    start_ts = end_ts - (180 * 24 * 60 * 60 * 1000)  # 180 days
    
    config = AsyncExportConfig(
        max_concurrent_requests=10,
        semaphore_limit=10
    )
    
    print(f"""
    === BENCHMARK: Async Export BTCUSDT 1m (180 days) ===
    Config: {config.max_concurrent_requests} concurrent, {config.semaphore_limit} semaphore
    """)
    
    async with AsyncTardisExporter(api_key, config) as exporter:
        start = time.time()
        data = await exporter.export_parallel(
            symbol, interval, start_ts, end_ts, chunk_days=30
        )
        elapsed = time.time() - start
        
    print(f"""
    === BENCHMARK RESULTS ===
    Total Records: {len(data):,}
    Time Elapsed: {elapsed:.2f} seconds
    Speed: {len(data)/elapsed:,.0f} records/second
    
    Comparison:
    - Sync approach (estimated): {elapsed * 5:.0f}s
    - Async speedup: ~{elapsed * 5 / elapsed:.1f}x faster
    """)


if __name__ == "__main__":
    asyncio.run(benchmark_async_exporter())

Kết quả benchmark thực tế trên VPS Singapore (4 vCPU, 8GB RAM):

Loại dữ liệuSố lượng recordsSync (giây)Async (giây)Tốc độ nhanh hơn
BTCUSDT 1m - 30 ngày~43,00042s12s3.5x
BTCUSDT 1m - 180 ngày~259,000258s45s5.7x
ETHUSDT 1h - 1 năm~8,7609s3s3x

Lưu trữ và Query với PostgreSQL

Để backtesting hiệu quả, tôi recommend dùng PostgreSQL với TimescaleDB extension. Dưới đây là schema và code để lưu trữ:

-- Schema cho dữ liệu K-line
CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;

CREATE TABLE bybit_klines (
    id BIGSERIAL,
    symbol VARCHAR(20) NOT NULL,
    interval VARCHAR(10) NOT NULL,
    open_time TIMESTAMPTZ NOT NULL,
    close_time TIMESTAMPTZ NOT NULL,
    open NUMERIC(24, 8) NOT NULL,
    high NUMERIC(24, 8) NOT NULL,
    low NUMERIC(24, 8) NOT NULL,
    close NUMERIC(24, 8) NOT NULL,
    volume NUMERIC(24, 8) NOT NULL,
    quote_volume NUMERIC(24, 8),
    trades INTEGER,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    PRIMARY KEY (symbol, interval, open_time)
);

-- Tạo hypertable cho TimescaleDB
SELECT create_hypertable('bybit_klines', 'open_time', 
    chunk_time_interval => INTERVAL '1 day',
    if_not_exists => TRUE
);

-- Index cho các query pattern thường dùng
CREATE INDEX idx_klines_symbol_interval ON bybit_klines (symbol, interval, open_time DESC);
CREATE INDEX idx_klines_close ON bybit_klines (close) WHERE close > 0;

-- Compression policy cho old data (tiết kiệm 90% storage)
ALTER TABLE bybit_klines SET (
    timescaledb.compress,
    timescaledb.compress_segmentby = 'symbol,interval'
);

SELECT add_compression_policy('bybit_klines', INTERVAL '7 days');

-- Retention policy (xóa data cũ hơn 2 năm)
SELECT add_retention_policy('bybit_klines', INTERVAL '2 years');

Phù hợp / Không phù hợp với ai

Phù hợp vớiKhông phù hợp với
Kỹ sư quantitative cần dataset chính xác cho backtestingNgười mới bắt đầu, chỉ cần dữ liệu demo
Team cần xây dựng data pipeline production-gradeCá nhân không có ngân sách (Tardis bắt đầu $49/tháng)
Backtest chiến lược high-frequency (1m-5m timeframe)Chỉ cần dữ liệu daily cho swing trading
Cần độ chính xác cao, không chấp nhận data gapsNghiên cứu academic, không cần real-time accuracy

Giá và ROI

Nhà cung cấpFree TierStarter ($49/tháng)Pro ($199/tháng)Enterprise
Tardis5,000 requests/ngày100,000 requests/ngàyUnlimitedCustom pricing
CoinAPI100 req/day$79/tháng$399/thángCustom
Binance HistoricalMiễn phíN/AN/AN/A
HolySheep AITín dụng miễn phí khi đăng kýDeepSeek V3.2 $0.42/MTokGPT-4.1 $8/MTokEnterprise SLA

Tính ROI thực tế: Với 1 chiến lược quantitative đòi hỏi 2 năm dữ liệu 1m cho BTC + ETH + 5 altcoins, chi phí Tardis khoảng $49/tháng. Nếu chiến lược mang lại 5% improvement trong backtest (nhờ data chính xác hơn), với vốn $100,000, đó là $5,000/year - ROI 102x.

Vì sao chọn HolySheep AI

Sau khi export và clean dữ liệu, bước tiếp theo là strategy optimization. Đây là lúc HolySheep AI phát huy sức mạnh:

Dưới đây là code tích hợp HolySheep AI để optimize strategy parameters tự động:

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

class HolySheepStrategyOptimizer:
    """
    Tích hợp HolySheep AI để optimize quantitative strategy parameters
    """
    
    BASE_URL = "https://api.holysheep.ai/v1"
    
    def __init__(self, api_key: str):
        self.api_key = api_key
        
    def optimize_rsi_strategy(
        self,
        symbol: str,
        price_data: List[Dict],
        initial_capital: float = 10000,
        optimization_goal: str = "max_sharpe_ratio"
    ) -> Dict:
        """
        Sử dụng AI để tìm optimal RSI parameters
        
        Args:
            symbol: Trading pair (VD: BTCUSDT)
            price_data: List chứa OHLCV data [{open, high, low, close, volume}]
            initial_capital: Vốn ban đầu
            optimization_goal: "max_sharpe_ratio" | "max_total_return" | "min_max_drawdown"
            
        Returns:
            Dict chứa optimal parameters và performance metrics
        """
        
        prompt = f"""
        Bạn là một Quantitative Trading Engineer chuyên nghiệp.
        
        Hãy phân tích dữ liệu giá của {symbol} và đề xuất optimal RSI strategy parameters.
        
        Dữ liệu: {json.dumps(price_data[:100])}  # Gửi 100 candles đầu làm sample
        
        Yêu cầu:
        1. Tìm optimal RSI period (thường 7-21)
        2. Tìm optimal overbought/oversold levels (70/30 hoặc 80/20)
        3. Đề xuất additional filters (volume, volatility)
        4. Backtest và so sánh với buy-and-hold
        
        Trả về JSON format:
        {{
            "optimal_rsi_period": number,
            "overbought_level": number,
            "oversold_level": number,
            "volume_filter": boolean,
            "volume_threshold": number (nếu enabled),
            "expected_sharpe_ratio": number,
            "expected_total_return_pct": number,
            "max_drawdown_pct": number,
            "trade_frequency_per_month": number,
            "confidence_score": number (0-1)
        }}
        """
        
        response = requests.post(
            f"{self.BASE_URL}/chat/completions",
            headers={
                "Authorization": f"Bearer {self.api_key}",
                "Content-Type": "application/json"
            },
            json={
                "model": "deepseek-chat",  # Model rẻ nhất, chất lượng tốt
                "messages": [
                    {"role": "system", "content": "Bạn là chuyên gia Quantitative Trading."},
                    {"role": "user", "content": prompt}
                ],
                "temperature": 0.3,  # Low temperature cho technical analysis
                "max_tokens": 1000
            },
            timeout=60
        )
        
        if response.status_code == 200:
            result = response.json()
            content = result["choices"][0]["message"]["content"]
            
            # Parse JSON từ response
            try:
                # Tìm JSON trong response (có thể có markdown code block)
                if "```json" in content:
                    content = content.split("``json")[1].split("``")[0]
                elif "```" in content:
                    content = content.split("``")[1].split("``")[0]
                    
                return json.loads(content.strip())
            except json.JSONDecodeError:
                return {"error": "Failed to parse AI response", "raw": content}
        else:
            return {"error": f"API Error: {response.status_code}"}
    
    def analyze_multi_timeframe(
        self,
        symbol: str,
        klines_1h: List[Dict],
        klines_4h: List[Dict],
        klines_1d: List[Dict]
    ) -> Dict:
        """
        Phân tích đa khung thời gian để tìm confluence signals
        """
        
        prompt = f"""
        Phân tích đa khung thời gian cho {symbol}:
        
        1H timeframe: {len(klines_1h)} candles, latest close: {klines_1h[-1]['close'] if klines_1h else 'N/A'}
        4H timeframe: {len(klines_4h)} candles, latest close: {klines_4h[-1]['close'] if klines_4h else 'N/A'}  
        1D timeframe: {len(klines_1d)} candles, latest close: {klines_1d[-1]['close'] if klines_1d else 'N/A'}
        
        Xác định:
        1. Trend direction trên mỗi timeframe (bullish/bearish/neutral)
        2. Confluence zones (vùng hỗ trợ/kháng cự trùng nhau)
        3. Entry points với risk/reward ratio tốt nhất
        4. Position sizing recommendations
        
        Trả về JSON với detailed analysis.
        """
        
        response = requests.post(
            f"{self.BASE_URL}/chat/completions",
            headers={
                "Authorization": f"Bearer {self.api_key}",
                "Content-Type": "application/json"
            },
            json={
                "model": "deepseek-chat",
                "messages": [
                    {"role": "user", "content": prompt}
                ],
                "temperature": 0.2,
                "max_tokens": 1500
            },
            timeout=60
        )
        
        if response.status_code == 200:
            return response.json()["choices"][0]["message"]["content"]
        return {"error": "Failed to get analysis"}
    
    def calculate_cost(self, num_tokens: int, model: str = "deepseek-chat") -> float:
        """
        Tính chi phí cho request (để estimate)
        
        Pricing 2026:
        - DeepSeek V3.2: $0.42/MTok (model: deepseek-chat)
        - GPT-4.1: $8/MTok
        - Claude Sonnet 4.5: $15/MTok
        - Gemini 2.5 Flash: $2.50/MTok
        """
        pricing = {
            "deepseek-chat": 0.42,
            "gpt-4.1": 8.0,
            "claude-sonnet-4.5": 15.0,
            "gemini-2.5-flash": 2.50
        }
        
        price_per_million = pricing.get(model, 0.42)
        return (num_tokens / 1_000_000) * price_per_million


========== SỬ DỤNG ==========

if __name__ == "__main__": optimizer = HolySheepStrategyOptimizer(api_key="YOUR_HOLYSHEEP_API_KEY") # Sample price data (thường load từ database) sample_klines = [ {"timestamp": 1704067200000, "open": 42000, "high": 42500, "low": 41800, "close": 42300, "volume": 1500}, {"timestamp": 1704070800000, "open": 42300, "high": 42800, "low": 42200, "close": 42600, "volume": 1800}, # ... thêm nhiều data points ] * 100 # Simulate 100 candles # Optimize RSI strategy result = optimizer.optimize_rsi_strategy( symbol="BTCUSDT", price_data=sample_klines, initial_capital=10000,