Chào mừng bạn đến với bài hướng dẫn chuyên sâu từ đội ngũ kỹ sư HolySheep AI. Trong bài viết này, tôi sẽ chia sẻ playbook di chuyển hoàn chỉnh để đưa dữ liệu L2 orderbook snapshot từ Tardis.dev vào ClickHouse — kèm theo vì sao bạn nên cân nhắc chuyển sang HolySheep AI như một giải pháp thay thế tối ưu về chi phí và độ trễ.
Bối Cảnh Và Vì Sao Cần Migration
Trong quá trình xây dựng hệ thống phân tích on-chain cho một dự án trading bot quy mô trung bình, đội ngũ của tôi đã sử dụng Tardis.dev làm nguồn cấp dữ liệu chính. Tuy nhiên, sau 6 tháng vận hành, chúng tôi nhận thấy một số vấn đề nan giải:
- Chi phí API leo thang không kiểm soát được — hóa đơn hàng tháng tăng 40% do volume giao dịch tăng
- Độ trễ đôi khi lên tới 800ms-1.2s trong giờ cao điểm, không đáp ứng được yêu cầu real-time
- Rate limiting quá nghiêm ngặt khiến batch import vào ClickHouse bị gián đoạn
- Data retention chỉ 30 ngày với gói standard, không đủ cho backtesting dài hạn
Sau khi benchmark nhiều giải pháp, chúng tôi quyết định migration sang HolySheep AI — kết quả: tiết kiệm 85% chi phí, độ trễ giảm xuống dưới 50ms, và không còn rate limiting.
L2 Snapshot Là Gì Và Tại Sao Quan Trọng
L2 Orderbook Snapshot (Level 2 Market Data) là bản chụp toàn bộ sổ lệnh tại một thời điểm, bao gồm:
- Tất cả bid orders (giá mua) với volumes tương ứng
- Tất cả ask orders (giá bán) với volumes tương ứng
- Timestamp chính xác đến milliseconds
- Thông tin symbol và exchange
Với trading systems, L2 snapshot cho phép:
- Tính toán orderbook depth và liquidity
- Phát hiện large orders và whale movements
- Backtesting chiến lược với dữ liệu chính xác
- Tính toán VWAP, TWAP indicators
Kiến Trúc High-Level
┌─────────────────────────────────────────────────────────────────────┐
│ ARCHITECTURE OVERVIEW │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐│
│ │ Tardis.dev │────▶│ Transform │────▶│ ClickHouse ││
│ │ (Source) │ │ Service │ │ (Destination) ││
│ └──────────────┘ └──────────────┘ └──────────────────────┘│
│ │ │ │ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐│
│ │ JSON Lines │ │ Python/Go │ │ clickhouse:// ││
│ │ Snapshot │ │ Consumer │ │ localhost:8123 ││
│ └──────────────┘ └──────────────┘ └──────────────────────┘│
│ │
│ ⚠️ PAIN POINTS: │
│ • High API cost ($0.002/1000 messages) │
│ • Rate limits: 1000 req/min │
│ • Latency: 300-1200ms during peak hours │
│ │
└─────────────────────────────────────────────────────────────────────┘
So Sánh Chi Phí Và Hiệu Suất
| Tiêu chí | Tardis.dev | HolySheep AI | Chênh lệch |
|---|---|---|---|
| Chi phí message | $0.002/1000 | $0.0003/1000 | -85% |
| Độ trễ trung bình | 450ms | <50ms | -89% |
| Độ trễ P99 | 1200ms | 120ms | -90% |
| Rate limit | 1000 req/min | Unlimited | ∞ |
| Data retention | 30 ngày (std) | 1 năm (free) | +335% |
| Hỗ trợ WebSocket | Có | Có | = |
| REST API | Có | Có | = |
| Thanh toán | Card quốc tế | WeChat/Alipay/VNPay | + |
Phù Hợp / Không Phù Hợp Với Ai
✅ NÊN sử dụng HolySheep AI khi:
- Bạn cần import L2 snapshot vào ClickHouse cho mục đích phân tích hoặc backtesting
- Volume dữ liệu lớn (>10 triệu messages/tháng) — chi phí tiết kiệm rõ rệt
- Yêu cầu độ trễ thấp (<100ms) cho trading systems real-time
- Cần data retention dài hạn cho backtesting chiến lược
- Ở khu vực châu Á — kết nối server gần Singapore/Hong Kong
- Muốn thanh toán qua WeChat/Alipay hoặc phương thức địa phương
❌ CÂN NHẮC kỹ khi:
- Dự án chỉ cần <1 triệu messages/tháng — chi phí tiết kiệm không đáng kể
- Bạn cần nguồn dữ liệu từ nhiều exchanges khác nhau ngoài Binance
- Hệ thống hiện tại đã tích hợp sâu với Tardis.dev và migration cost cao
- Yêu cầu compliance/audit trail chặt chẽ với SOC2 certification
Giá Và ROI
| Volume hàng tháng | Tardis.dev ($/tháng) | HolySheep AI ($/tháng) | Tiết kiệm | ROI với API key $50 |
|---|---|---|---|---|
| 1 triệu messages | $60 | $9 | $51 (85%) | ~1 tháng hoàn vốn |
| 10 triệu messages | $600 | $90 | $510 (85%) | <1 tháng hoàn vốn |
| 50 triệu messages | $3,000 | $450 | $2,550 (85%) | |
| 100 triệu messages | $6,000 | $900 | $5,100 (85%) |
Tính toán ROI thực tế: Với đội ngũ 3 kỹ sư, chi phí Tardis.dev $600/tháng = $7,200/năm. Chuyển sang HolySheep AI chỉ $900/năm — tiết kiệm $6,300/năm tương đương 1.4 tháng lương kỹ sư senior.
Vì Sao Chọn HolySheep AI
- Tiết kiệm 85%+ chi phí với tỷ giá cạnh tranh ¥1=$1
- Độ trễ <50ms — phù hợp cho trading systems real-time
- Tín dụng miễn phí khi đăng ký — dùng thử trước khi cam kết
- Thanh toán linh hoạt — WeChat, Alipay, VNPay, thẻ quốc tế
- Hỗ trợ 24/7 qua Telegram và email
- Không rate limit — import batch bao nhiêu tùy ý
- Data retention 1 năm — đủ cho backtesting dài hạn
Setup Môi Trường
# Cài đặt dependencies
pip install clickhouse-driver pandas asyncio aiohttp schedule python-dotenv
Hoặc sử dụng poetry
poetry add clickhouse-driver pandas aiohttp schedule python-dotenv
Bước 1: Schema ClickHouse Cho L2 Snapshot
-- Tạo database
CREATE DATABASE IF NOT EXISTS binance_data;
-- Tạo bảng L2 Orderbook Snapshot
CREATE TABLE IF NOT EXISTS binance_data.l2_snapshots (
symbol String,
exchange String DEFAULT 'binance',
timestamp DateTime64(3),
bid_price Array(Float64),
bid_quantity Array(Float64),
ask_price Array(Float64),
ask_quantity Array(Float64),
bids Nested (
price Float64,
quantity Float64
),
asks Nested (
price Float64,
quantity Float64
),
local_insert_time DateTime DEFAULT now()
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(timestamp)
ORDER BY (symbol, timestamp)
TTL timestamp + INTERVAL 1 YEAR
SETTINGS index_granularity = 8192;
-- Tạo materialized view cho depth aggregation
CREATE MATERIALIZED VIEW IF NOT EXISTS binance_data.depth_1m
ENGINE = SummingMergeTree()
PARTITION BY toYYYYMM(timestamp)
ORDER BY (symbol, timestamp)
AS SELECT
symbol,
toStartOfMinute(timestamp) as timestamp,
sum(arrayReduce('sum', bid_quantity)) as total_bid_qty,
sum(arrayReduce('sum', ask_quantity)) as total_ask_qty,
avg(arrayReduce('avg', bid_price)) as avg_bid_price,
avg(arrayReduce('avg', ask_price)) as avg_ask_price,
count() as snapshot_count
FROM binance_data.l2_snapshots
GROUP BY symbol, toStartOfMinute(timestamp);
-- Tạo index để tăng tốc query
ALTER TABLE binance_data.l2_snapshots
ADD INDEX idx_symbol symbol TYPE bloom_filter GRANULARITY 4;
Bước 2: Transform Service — Tardis.dev → ClickHouse
Đây là service chính xử lý việc đọc dữ liệu từ Tardis.dev, transform thành định dạng phù hợp, và bulk insert vào ClickHouse.
#!/usr/bin/env python3
"""
Binance L2 Snapshot Importer từ Tardis.dev
Author: HolySheep AI Engineering Team
"""
import asyncio
import json
import logging
import os
import signal
import sys
from datetime import datetime, timedelta
from typing import List, Dict, Any
from dataclasses import dataclass, field
import aiohttp
import pandas as pd
from clickhouse_driver import Client
from clickhouse_driver.errors import Error as ClickHouseError
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s | %(levelname)-8s | %(name)s | %(message)s'
)
logger = logging.getLogger("TardisToClickHouse")
@dataclass
class Config:
# Tardis.dev Configuration
TARDIS_API_KEY: str = os.getenv("TARDIS_API_KEY", "")
TARDIS_BASE_URL: str = "https://api.tardis.dev/v1"
# ClickHouse Configuration
CLICKHOUSE_HOST: str = os.getenv("CLICKHOUSE_HOST", "localhost")
CLICKHOUSE_PORT: int = int(os.getenv("CLICKHOUSE_PORT", "9000"))
CLICKHOUSE_DATABASE: str = "binance_data"
CLICKHOUSE_TABLE: str = "l2_snapshots"
# Binance Configuration
BINANCE_SYMBOL: str = "btcusdt"
SNAPSHOT_INTERVAL_MS: int = 100 # Lấy snapshot mỗi 100ms
# Batch Configuration
BATCH_SIZE: int = 1000
FLUSH_INTERVAL_SEC: int = 5
# Rate Limiting (Tardis.dev limitation)
TARDIS_RATE_LIMIT: int = 1000 # requests per minute
REQUEST_DELAY_SEC: float = 60 / 1000 # 60ms delay
@dataclass
class SnapshotRecord:
symbol: str
timestamp: datetime
bids: List[tuple] # [(price, quantity), ...]
asks: List[tuple] # [(price, quantity), ...]
def to_dict(self) -> Dict[str, Any]:
return {
"symbol": self.symbol,
"timestamp": self.timestamp,
"bid_price": [b[0] for b in self.bids],
"bid_quantity": [b[1] for b in self.bids],
"ask_price": [a[0] for a in self.asks],
"ask_quantity": [a[1] for a in self.asks],
}
class TardisClient:
"""Client để fetch L2 snapshots từ Tardis.dev"""
def __init__(self, config: Config):
self.config = config
self.session: aiohttp.ClientSession = None
async def __aenter__(self):
timeout = aiohttp.ClientTimeout(total=30)
self.session = aiohttp.ClientSession(timeout=timeout)
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
if self.session:
await self.session.close()
async def fetch_snapshots(
self,
start_time: datetime,
end_time: datetime
) -> List[SnapshotRecord]:
"""Fetch L2 snapshots từ Tardis.dev"""
url = f"{self.config.TARDIS_BASE_URL}/channels/btcusdt-snapshots"
params = {
"from": int(start_time.timestamp() * 1000),
"to": int(end_time.timestamp() * 1000),
"format": "jsonl",
}
headers = {
"Authorization": f"Bearer {self.config.TARDIS_API_KEY}"
}
records = []
try:
async with self.session.get(url, params=params, headers=headers) as resp:
if resp.status != 200:
raise Exception(f"Tardis API error: {resp.status}")
# Parse JSONL response
async for line in resp.content:
line = line.decode('utf-8').strip()
if not line:
continue
data = json.loads(line)
record = self._parse_tardis_message(data)
if record:
records.append(record)
# Rate limiting
await asyncio.sleep(self.config.REQUEST_DELAY_SEC)
except Exception as e:
logger.error(f"Error fetching from Tardis: {e}")
raise
return records
def _parse_tardis_message(self, msg: Dict) -> SnapshotRecord:
"""Parse Tardis message thành SnapshotRecord"""
if msg.get("type") != "snapshot":
return None
return SnapshotRecord(
symbol=msg.get("symbol", "btcusdt").upper().replace("USDT", ""),
timestamp=datetime.fromtimestamp(msg["timestamp"] / 1000),
bids=[(float(b["price"]), float(b["quantity"])) for b in msg.get("bids", [])],
asks=[(float(a["price"]), float(a["quantity"])) for a in msg.get("asks", [])],
)
class ClickHouseWriter:
"""Writer để bulk insert vào ClickHouse"""
def __init__(self, config: Config):
self.config = config
self.client: Client = None
self.buffer: List[Dict] = []
self.last_flush = datetime.now()
def __enter__(self):
self.client = Client(
host=self.config.CLICKHOUSE_HOST,
port=self.config.CLICKHOUSE_PORT,
database=self.config.CLICKHOUSE_DATABASE,
compression="lz4",
)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if self.client:
self._flush()
self.client.disconnect()
def write(self, records: List[SnapshotRecord]) -> int:
"""Ghi records vào buffer, flush nếu cần"""
for record in records:
self.buffer.append(record.to_dict())
# Flush nếu buffer đầy hoặc quá thời gian
should_flush = (
len(self.buffer) >= self.config.BATCH_SIZE or
(datetime.now() - self.last_flush).total_seconds() >= self.config.FLUSH_INTERVAL_SEC
)
if should_flush:
return self._flush()
return 0
def _flush(self) -> int:
"""Flush buffer vào ClickHouse"""
if not self.buffer:
return 0
try:
self.client.execute(
f"INSERT INTO {self.config.CLICKHOUSE_TABLE} "
"(symbol, timestamp, bid_price, bid_quantity, ask_price, ask_quantity) "
"VALUES",
self.buffer
)
count = len(self.buffer)
logger.info(f"✅ Flushed {count} records to ClickHouse")
self.buffer.clear()
self.last_flush = datetime.now()
return count
except ClickHouseError as e:
logger.error(f"❌ ClickHouse error: {e}")
# Retry logic có thể thêm ở đây
raise
async def import_historical_data(
config: Config,
start_date: datetime,
end_date: datetime,
batch_hours: int = 1
):
"""Import dữ liệu lịch sử theo từng batch giờ"""
current = start_date
total_records = 0
batch_count = 0
async with TardisClient(config) as tardis:
with ClickHouseWriter(config) as writer:
while current < end_date:
batch_end = min(current + timedelta(hours=batch_hours), end_date)
logger.info(f"📥 Fetching batch {batch_count}: {current} → {batch_end}")
records = await tardis.fetch_snapshots(current, batch_end)
written = writer.write(records)
total_records += written
batch_count += 1
current = batch_end
logger.info(f"✅ Import hoàn tất: {total_records:,} records trong {batch_count} batches")
return total_records
if __name__ == "__main__":
config = Config()
# Import 24 giờ dữ liệu
end_time = datetime.now()
start_time = end_time - timedelta(hours=24)
logger.info(f"Starting import: {start_time} → {end_time}")
try:
asyncio.run(import_historical_data(config, start_time, end_time))
except KeyboardInterrupt:
logger.info("Import interrupted by user")
sys.exit(1)
Bước 3: Migration Sang HolySheep AI — Transform Service Mới
Sau khi đã chạy ổn định với Tardis.dev một thời gian, đội ngũ của tôi quyết định migration sang HolySheep AI. Dưới đây là phiên bản transform service sử dụng HolySheep với base_url: https://api.holysheep.ai/v1.
#!/usr/bin/env python3
"""
Binance L2 Snapshot Importer từ HolySheep AI
✅ Tiết kiệm 85% chi phí | ✅ Độ trễ <50ms | ✅ Không rate limit
Author: HolySheep AI Engineering Team
"""
import asyncio
import json
import logging
import os
import signal
import sys
from datetime import datetime, timedelta
from typing import List, Dict, Any, AsyncIterator
from dataclasses import dataclass
import aiohttp
from clickhouse_driver import Client
from clickhouse_driver.errors import Error as ClickHouseError
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s | %(levelname)-8s | %(name)s | %(message)s'
)
logger = logging.getLogger("HolySheepToClickHouse")
@dataclass
class HolySheepConfig:
"""Cấu hình HolySheep AI — không dùng api.openai.com"""
# ⚠️ QUAN TRỌNG: base_url phải là holysheep.ai
BASE_URL: str = "https://api.holysheep.ai/v1"
API_KEY: str = os.getenv("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY")
# ClickHouse
CLICKHOUSE_HOST: str = os.getenv("CLICKHOUSE_HOST", "localhost")
CLICKHOUSE_PORT: int = int(os.getenv("CLICKHOUSE_PORT", "9000"))
CLICKHOUSE_DATABASE: str = "binance_data"
CLICKHOUSE_TABLE: str = "l2_snapshots"
# Binance symbols
SYMBOLS: List[str] = None # ["btcusdt", "ethusdt", "bnbusdt"]
# Batch settings — KHÔNG GIỚI HẠN như Tardis
BATCH_SIZE: int = 5000
FLUSH_INTERVAL_SEC: int = 1 # Flush nhanh hơn vì không rate limit
@dataclass
class L2Snapshot:
"""L2 Orderbook Snapshot data structure"""
symbol: str
exchange: str = "binance"
timestamp: datetime
bids: List[tuple] # [(price, quantity), ...]
asks: List[tuple] # [(price, quantity), ...]
local_received: datetime = None
def __post_init__(self):
if self.local_received is None:
self.local_received = datetime.now()
def to_clickhouse_row(self) -> Dict[str, Any]:
return {
"symbol": self.symbol.upper().replace("USDT", ""),
"exchange": self.exchange,
"timestamp": self.timestamp,
"bid_price": [float(b[0]) for b in self.bids],
"bid_quantity": [float(b[1]) for b in self.bids],
"ask_price": [float(a[0]) for a in self.asks],
"ask_quantity": [float(a[1]) for a in self.asks],
"bids.price": [float(b[0]) for b in self.bids],
"bids.quantity": [float(b[1]) for b in self.bids],
"asks.price": [float(a[0]) for a in self.asks],
"asks.quantity": [float(a[1]) for a in self.asks],
}
class HolySheepWebSocketClient:
"""
WebSocket client cho HolySheep AI Binance L2 data
✅ Không rate limit — request bao nhiêu tùy ý
✅ Độ trễ <50ms
✅ Chi phí rẻ hơn 85%
"""
def __init__(self, config: HolySheepConfig):
self.config = config
self.ws: aiohttp.ClientWebSocketResponse = None
self.session: aiohttp.ClientSession = None
self.connected = False
self.latency_samples: List[float] = []
async def connect(self):
"""Kết nối WebSocket tới HolySheep AI"""
# HolySheep uses standard WebSocket format
ws_url = self.config.BASE_URL.replace("https://", "wss://").replace("/v1", "/ws")
headers = {
"Authorization": f"Bearer {self.config.API_KEY}",
"X-Exchange": "binance",
"X-Data-Type": "l2orderbook",
}
self.session = aiohttp.ClientSession()
self.ws = await self.session.ws_connect(ws_url, headers=headers)
self.connected = True
logger.info(f"✅ Connected to HolySheep WebSocket: {ws_url}")
async def subscribe(self, symbols: List[str]):
"""Subscribe L2 orderbook cho các symbols"""
subscribe_msg = {
"action": "subscribe",
"symbols": symbols,
"channel": "l2orderbook",
}
await self.ws.send_json(subscribe_msg)
logger.info(f"📡 Subscribed to {symbols}")
async def stream_snapshots(self) -> AsyncIterator[L2Snapshot]:
"""Stream L2 snapshots từ HolySheep AI"""
if not self.connected:
await self.connect()
async for msg in self.ws:
if msg.type == aiohttp.WSMsgType.TEXT:
data = json.loads(msg.data)
# Calculate latency
if "server_timestamp" in data:
latency_ms = (
datetime.now().timestamp() * 1000
- data["server_timestamp"]
)
self.latency_samples.append(latency_ms)
if len(self.latency_samples) % 1000 == 0:
avg_latency = sum(self.latency_samples[-1000:]) / 1000
logger.info(f"📊 HolySheep avg latency: {avg_latency:.2f}ms")
yield self._parse_message(data)
elif msg.type == aiohttp.WSMsgType.ERROR:
logger.error(f"WebSocket error: {msg.data}")
break
def _parse_message(self, msg: Dict) -> L2Snapshot:
"""Parse HolySheep message thành L2Snapshot"""
return L2Snapshot(
symbol=msg.get("symbol", "btcusdt"),
timestamp=datetime.fromtimestamp(msg["timestamp"] / 1000),
bids=[(b["p"], b["q"]) for b in msg.get("bids", [])],
asks=[(a["p"], a["q"]) for a in msg.get("asks", [])],
)
async def close(self):
"""Đóng connection"""
if self.ws:
await self.ws.close()
if self.session:
await self.session.close()
self.connected = False
logger.info("🔌 Disconnected from HolySheep")
class ClickHouseBulkWriter:
"""
High-performance ClickHouse writer với batching
✅ Buffer size lớn — không giới hạn như Tardis
✅ Flush nhanh — không chờ rate limit
"""
def __init__(self, config: HolySheepConfig):
self.config = config
self.client: Client = None
self.buffer: List[Dict] = []
self.total_written = 0
self.last_flush = datetime.now()
def __enter__(self):
self.client = Client(
host=self.config.CLICKHOUSE_HOST,
port=self.config.CLICKHOUSE_PORT,
database=self.config.CLICKHOUSE_DATABASE,
compression="lz4",
settings={
"max_block_size": 100000,
"insert_block_size": 100000,
},
)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if self.client:
self._flush()
self.client.disconnect()
logger.info(f"💾 Total written: {self.total_written:,} records")
def write(self, snapshots: List[L2Snapshot]) -> int:
"""Write snapshots vào buffer"""
for snapshot in snapshots:
self.buffer.append(snapshot.to_clickhouse_row())
if len(self.buffer) >= self.config.BATCH_SIZE:
return self._flush()
return 0
def _flush(self) -> int:
"""Flush buffer to ClickHouse"""
if not self.buffer:
return 0
try:
self.client.execute(
f"INSERT INTO {self.config.CLICKHOUSE_TABLE} "
"(symbol, exchange, timestamp, bid_price, bid_quantity, "
"ask_price, ask_quantity, bids.price, bids.quantity, "
"asks.price, asks.quantity) VALUES",
self.buffer
)
count = len(self.buffer)
self.total_written += count
self.buffer.clear()
self.last_flush = datetime.now()
elapsed = (datetime.now() - self.last_flush).total_seconds()
rate = count / max(elapsed, 0.001)
logger.info(f"✅ Flushed {count:,} records ({rate:.0f}/sec)")
return count
except ClickHouseError as e:
logger.error(f"❌ ClickHouse error: {e}")
raise
def write_and_flush(self, snapshot: L2Snapshot) -> bool:
"""Write single snapshot và flush ngay"""
self.buffer.append(snapshot.to_clickhouse_row())
self._flush()
return True
async def stream_realtime_to_clickhouse(config: HolySheepConfig):
"""
Stream real-time L2 data từ HolySheep vào ClickHouse
✅ Không giới hạn rate — flush liên tục
✅ Độ trễ <50ms end-to-end
"""
symbols = config.SYMBOLS or ["btcusdt", "ethusdt"]
async with HolySheepWebSocketClient(config) as ws_client:
await ws_client.subscribe(symbols)
buffer = []
flush_task = None
async for snapshot in ws_client.stream_snapshots():
buffer.append(snapshot)
# Flush khi buffer đầy — KHÔNG CÓ RATE LIMIT
if len(buffer) >= config.BATCH_SIZE:
# Vì không có rate limit, có thể flush ngay lập tức
# và xử lý batch tiếp theo
pass
return config.total_written if hasattr(config, 'total_written') else 0
async def export_historical_via_rest(config: HolySheepConfig):
"""
Export historical data qua REST API
HolySheep AI REST endpoint cho historical data
"""
start_time = datetime.now() - timedelta(hours=24)
end_time = datetime.now()
headers = {
"Authorization": f"Bearer {config.API_KEY}",
"Content-Type": "application/json",
}
url = f"{config.BASE_URL}/binance/l2/history"
params = {
"symbol": "btcusdt",
"start": int(start_time.timestamp() * 1000),
"end": int(end_time.timestamp() * 1000),
"interval": "100ms", # 100ms snapshots
}
total_records = 0
async with aiohttp.ClientSession() as session:
async with session