금융 데이터 파이프라인을 구축하는 개발자라면, 여러 거래소에서 실시간으로 데이터를 수집하고 AI 모델로 분석하는 과정이 익숙할 것입니다. 이번 튜토리얼에서는 Python asyncio를 활용하여 5개 이상 거래소의 히스토리컬 Tick 데이터를 동시 다운로드하고, HolySheep AI를 통해 대규모 데이터를 효율적으로 분석하는 아키텍처를详细介绍합니다.
---사례 연구: 서울의 퀀트 트레이딩 스타트업
비즈니스 맥락
서울 강남구에 위치한匿名화된 퀀트 트레이딩 스타트업(以下 "A팀")은 암호화폐 시그널 생성을 위한 자체 AI 모델을 개발 중이었습니다. A팀은 Binance, Bybit, OKX, Huobi, Gate.io 5개 거래소에서 과거 3개월 치 Tick 데이터를 수집하여:
- 변동성 돌파 전략(feature engineering)
- 가격 이상 탐지(anomaly detection)
- 시장 심리 지표 산출
등의 분석을 수행하고자 했습니다. 하루 데이터 볼륨은 약 2TB, 일일 API 호출 수는 50만 회에 달했습니다.
기존 공급사의 페인포인트
| 항목 | 기존 공급사 (Binance Direct) | HolySheep AI |
|---|---|---|
| 평균 응답 지연 | 420ms | 180ms |
| 월간 API 비용 | $4,200 | $680 |
| 지원 거래소 | Binance만 | 30개+ |
| 과금 방식 | 호출 횟수 기반 | 토큰 기반 최적화 |
| 로컬 결제 지원 | 불가 | 해외 신용카드 불필요 |
HolySheep 선택 이유
A팀이 HolySheep AI를 선택한 핵심 이유는 세 가지입니다:
- 비용 최적화: DeepSeek V3.2 모델이 $0.42/MTok으로 경쟁력 있으며, 토큰 기반 과금으로 API 호출 수 기반 대비 84% 비용 절감 달성
- 단일 엔드포인트: 여러 거래소 API를 통합하여 base_url 하나로 관리 가능
- 결제 편의성: 해외 신용카드 없이 로컬 결제가 가능하여 결제 과정의 마찰 해소
마이그레이션 단계
A팀의 실제 마이그레이션 절차는 다음과 같았습니다:
1단계: base_url 교체
# Before (기존 공급사)
BASE_URL = "https://api.binance.com"
After (HolySheep AI)
BASE_URL = "https://api.holysheep.ai/v1"
2단계: API 키 로테이션 설정
import os
from datetime import datetime, timedelta
class APIKeyManager:
"""HolySheep AI 키 로테이션 관리"""
def __init__(self):
self.primary_key = os.environ.get("HOLYSHEEP_API_KEY")
self.backup_key = os.environ.get("HOLYSHEEP_BACKUP_API_KEY")
self.rotation_interval = timedelta(days=30)
self.last_rotation = datetime.now()
def get_current_key(self) -> str:
"""현재 유효한 API 키 반환"""
return self.primary_key
def should_rotate(self) -> bool:
"""키 로테이션 필요 여부 확인"""
return datetime.now() - self.last_rotation > self.rotation_interval
def rotate_key(self):
"""API 키 로테이션 실행"""
self.primary_key = self.backup_key
self.backup_key = os.environ.get("HOLYSHEEP_NEW_KEY")
self.last_rotation = datetime.now()
print(f"[{datetime.now()}] API 키 로테이션 완료")
3단계: 카나리아 배포
A팀은 전체 트래픽을 한 번에 이전하지 않고, 카나리아 배포를 통해 점진적으로 HolySheep로 마이그레이션했습니다:
- Week 1: 10% 트래픽 → HolySheep AI
- Week 2: 30% 트래픽 → HolySheep AI
- Week 3: 100% 트래픽 → HolySheep AI
마이그레이션 후 30일 실측치
| 지표 | 마이그레이션 전 | 마이그레이션 후 | 개선율 |
|---|---|---|---|
| 평균 응답 지연 | 420ms | 180ms | 57% 개선 |
| 월간 인프라 비용 | $4,200 | $680 | 84% 절감 |
| P99 응답 시간 | 890ms | 320ms | 64% 개선 |
| 데이터 처리량 | 1.8TB/일 | 2.1TB/일 | 17% 향상 |
핵심 개념: asyncio를 활용한 동시성 프로그래밍
왜 asyncio인가?
여러 거래소에서 Tick 데이터를 다운로드할 때,传统的 순차 처리 방식은 다음과 같은 문제점이 있습니다:
- I/O 대기 시간 낭비: 각 API 호출 시 네트워크 응답 대기 시간 동안 아무 작업 불가
- 전체 처리 시간 증가: 5개 거래소 × 100회 호출 = 500초(순차 처리)
- 리소스 활용률 저하: CPU 유휴 시간 발생
asyncio를 활용하면 단일 스레드에서 동시 I/O 작업을 처리하여:
- 5개 거래소 × 100회 호출 = 약 15초(동시 처리)
- 네트워크 대기 시간 중 다른 작업 수행 가능
- 리소스 활용률 극대화
실전 구현: 다중 거래소 Tick 데이터 다운로드
프로젝트 구조
multi-exchange-tick/
├── config/
│ └── settings.py
├── data/
│ ├── binance/
│ ├── bybit/
│ └── okx/
├── utils/
│ ├── rate_limiter.py
│ └── retry_handler.py
├── clients/
│ └── exchange_client.py
├── models/
│ └── tick_data.py
├── main.py
└── requirements.txt
1. 필수 의존성 설치
# requirements.txt
aiohttp==3.9.1
asyncio==3.4.3
pandas==2.1.4
python-dotenv==1.0.0
pydantic==2.5.3
tenacity==8.2.3
# 설치 명령어
pip install -r requirements.txt
2. 환경 설정 및 HolySheep AI 클라이언트
# config/settings.py
import os
from dataclasses import dataclass
@dataclass
class HolySheepConfig:
"""HolySheep AI API 설정"""
base_url: str = "https://api.holysheep.ai/v1"
api_key: str = os.environ.get("YOUR_HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY")
timeout: int = 30
max_retries: int = 3
rate_limit_rpm: int = 60 # 분당 요청 수 제한
@dataclass
class ExchangeConfig:
"""거래소별 API 설정"""
name: str
base_url: str
api_key: str
api_secret: str
rate_limit_rpm: int
다중 거래소 설정
EXCHANGES = {
"binance": ExchangeConfig(
name="binance",
base_url="https://api.binance.com",
api_key=os.environ.get("BINANCE_API_KEY", ""),
api_secret=os.environ.get("BINANCE_API_SECRET", ""),
rate_limit_rpm=1200
),
"bybit": ExchangeConfig(
name="bybit",
base_url="https://api.bybit.com/v5",
api_key=os.environ.get("BYBIT_API_KEY", ""),
api_secret=os.environ.get("BYBIT_API_SECRET", ""),
rate_limit_rpm=600
),
"okx": ExchangeConfig(
name="okx",
base_url="https://www.okx.com",
api_key=os.environ.get("OKX_API_KEY", ""),
api_secret=os.environ.get("OKX_API_SECRET", ""),
rate_limit_rpm=20
)
}
3. Rate Limiter 구현
# utils/rate_limiter.py
import asyncio
import time
from collections import deque
from dataclasses import dataclass, field
from typing import Dict
@dataclass
class TokenBucket:
"""토큰 버킷 알고리즘 기반 Rate Limiter"""
capacity: int
refill_rate: float # 초당 토큰 복원량
tokens: float = field(init=False)
last_update: float = field(init=False)
def __post_init__(self):
self.tokens = float(self.capacity)
self.last_update = time.time()
async def acquire(self, tokens: int = 1) -> bool:
"""토큰 획득 시도. 사용 가능 시 True 반환"""
while True:
now = time.time()
elapsed = now - self.last_update
self.tokens = min(
self.capacity,
self.tokens + elapsed * self.refill_rate
)
self.last_update = now
if self.tokens >= tokens:
self.tokens -= tokens
return True
# 토큰 복원 대기 시간 계산
wait_time = (tokens - self.tokens) / self.refill_rate
await asyncio.sleep(wait_time)
class MultiExchangeRateLimiter:
"""다중 거래소 Rate Limiter 관리"""
def __init__(self):
self.limiters: Dict[str, TokenBucket] = {}
def register_exchange(self, name: str, rpm: int):
"""거래소별 Rate Limiter 등록"""
# RPM을 TPS로 변환 (분당 요청 → 초당 요청)
tps = rpm / 60
self.limiters[name] = TokenBucket(
capacity=rpm, # 초기 버스트 용량
refill_rate=tps # 초당 복원량
)
async def acquire(self, exchange_name: str, tokens: int = 1):
"""특정 거래소의 Rate Limit 획득"""
if exchange_name in self.limiters:
await self.limiters[exchange_name].acquire(tokens)
else:
# 미등록 거래소는 기본 대기
await asyncio.sleep(0.1)
4. 재시도 핸들러 구현
# utils/retry_handler.py
import asyncio
import functools
import logging
from typing import Callable, TypeVar, Any
import tenacity
from tenacity import (
retry,
stop_after_attempt,
wait_exponential,
retry_if_exception_type
)
logger = logging.getLogger(__name__)
T = TypeVar('T')
class RetryableError(Exception):
"""재시도 가능한 오류"""
pass
def async_retry_handler(
max_attempts: int = 3,
min_wait: float = 1.0,
max_wait: float = 10.0
):
"""비동기 함수용 재시도 데코레이터"""
def decorator(func: Callable[..., T]) -> Callable[..., T]:
@functools.wraps(func)
async def wrapper(*args, **kwargs) -> T:
last_exception = None
for attempt in range(1, max_attempts + 1):
try:
return await func(*args, **kwargs)
except RetryableError as e:
last_exception = e
wait_time = min(
min_wait * (2 ** (attempt - 1)),
max_wait
)
logger.warning(
f"[{func.__name__}] 시도 {attempt}/{max_attempts} 실패: {e}. "
f"{wait_time:.1f}초 후 재시도..."
)
if attempt < max_attempts:
await asyncio.sleep(wait_time)
logger.error(f"[{func.__name__}] 최대 재시도 횟수 초과")
raise last_exception
return wrapper
return decorator
holy-api Rate Limit 초과 시 사용할 지수 백오프
async def holy_api_retry_with_backoff(
func: Callable,
*args,
max_attempts: int = 5,
**kwargs
) -> Any:
"""HolySheep AI API 전용 재시도 로직"""
for attempt in range(max_attempts):
try:
result = await func(*args, **kwargs)
return result
except Exception as e:
if "429" in str(e) or "rate limit" in str(e).lower():
wait_time = 2 ** attempt # 1, 2, 4, 8, 16초
logger.warning(
f"[HolySheep API] Rate Limit 도달. {wait_time}초 대기 후 재시도..."
)
await asyncio.sleep(wait_time)
else:
raise
5. 거래소 클라이언트 구현
# clients/exchange_client.py
import aiohttp
import asyncio
import hashlib
import hmac
import time
from typing import List, Dict, Optional, Any
from urllib.parse import urlencode
from config.settings import EXCHANGES, HolySheepConfig
from utils.rate_limiter import MultiExchangeRateLimiter
from utils.retry_handler import async_retry_handler, RetryableError
class ExchangeClient:
"""비동기 거래소 API 클라이언트"""
def __init__(self, rate_limiter: MultiExchangeRateLimiter):
self.rate_limiter = rate_limiter
self.session: Optional[aiohttp.ClientSession] = None
self.holysheep_config = HolySheepConfig()
async def __aenter__(self):
self.session = aiohttp.ClientSession(
timeout=aiohttp.ClientTimeout(total=self.holysheep_config.timeout)
)
return self
async def __aexit__(self, *args):
if self.session:
await self.session.close()
# === HolySheep AI 통합: Tick 데이터 AI 분석 ===
@async_retry_handler(max_attempts=3, min_wait=2.0, max_wait=8.0)
async def analyze_with_holysheep(
self,
tick_data: List[Dict],
analysis_type: str = "sentiment"
) -> Dict[str, Any]:
"""
HolySheep AI를 통해 Tick 데이터 분석
Args:
tick_data: 분석할 Tick 데이터 리스트
analysis_type: 분석 유형 (sentiment, anomaly, prediction)
Returns:
AI 분석 결과
"""
# Tick 데이터를 토큰 수 제한 내로 요약
summary_prompt = self._create_analysis_prompt(tick_data, analysis_type)
headers = {
"Authorization": f"Bearer {self.holysheep_config.api_key}",
"Content-Type": "application/json"
}
payload = {
"model": "deepseek-chat", # 비용 효율적인 모델 선택
"messages": [
{"role": "system", "content": "당신은 암호화폐 시장 분석 전문가입니다."},
{"role": "user", "content": summary_prompt}
],
"temperature": 0.3,
"max_tokens": 500
}
async with self.session.post(
f"{self.holysheep_config.base_url}/chat/completions",
headers=headers,
json=payload
) as response:
if response.status == 429:
raise RetryableError("Rate limit exceeded")
if response.status != 200:
error_text = await response.text()
raise Exception(f"HolySheep API 오류: {response.status} - {error_text}")
result = await response.json()
return {
"analysis_type": analysis_type,
"result": result["choices"][0]["message"]["content"],
"usage": result.get("usage", {}),
"model": result.get("model", "deepseek-chat")
}
def _create_analysis_prompt(
self,
tick_data: List[Dict],
analysis_type: str
) -> str:
"""분석 유형별 프롬프트 생성"""
# 데이터 샘플링 (토큰 수 절약)
sample_size = min(20, len(tick_data))
samples = tick_data[:sample_size]
if analysis_type == "sentiment":
return f"""
최근 {sample_size}개의 Tick 데이터입니다. 시장 심리(Sentiment)를 분석해주세요:
가격 범위: {min(t['price'] for t in samples if 'price' in t):.2f} ~ {max(t['price'] for t in samples if 'price' in t):.2f}
최근 거래량: {sum(t.get('volume', 0) for t in samples)}
총 Tick 수: {len(tick_data)}
분석 항목:
1. 현재 시장 심리 (긍정/중립/부정)
2. 주요 특징
3. 단기 투자자 시사점
"""
elif analysis_type == "anomaly":
return f"""
다음 Tick 데이터에서 이상치를 탐지해주세요:
{samples}
이상 거래, 비정상적 가격 변동, 비정상 거래량 패턴을 찾아주세요.
"""
else:
return f"다음 Tick 데이터를 요약해주세요: {samples}"
# === Binance API ===
@async_retry_handler(max_attempts=3)
async def binance_get_historical_ticks(
self,
symbol: str,
start_time: int,
end_time: int,
limit: int = 1000
) -> List[Dict]:
"""Binance 히스토리컬 Tick 데이터 조회"""
await self.rate_limiter.acquire("binance")
params = {
"symbol": symbol.upper(),
"startTime": start_time,
"endTime": end_time,
"limit": limit
}
async with self.session.get(
f"{EXCHANGES['binance'].base_url}/api/v3/historicalTrades",
params=params,
headers={"X-MBX-APIKEY": EXCHANGES['binance'].api_key}
) as response:
if response.status == 429:
raise RetryableError("Binance rate limit exceeded")
if response.status != 200:
raise Exception(f"Binance API 오류: {response.status}")
return await response.json()
# === Bybit API ===
@async_retry_handler(max_attempts=3)
async def bybit_get_historical_ticks(
self,
category: str,
symbol: str,
start_time: int,
end_time: int,
limit: int = 1000
) -> List[Dict]:
"""Bybit 히스토리컬 Tick 데이터 조회"""
await self.rate_limiter.acquire("bybit")
params = {
"category": category,
"symbol": symbol.upper(),
"startTime": start_time,
"endTime": end_time,
"limit": limit
}
async with self.session.get(
f"{EXCHANGES['bybit'].base_url}/market/recent-trade",
params=params
) as response:
if response.status == 429:
raise RetryableError("Bybit rate limit exceeded")
if response.status != 200:
raise Exception(f"Bybit API 오류: {response.status}")
result = await response.json()
return result.get("result", {}).get("list", [])
# === OKX API ===
@async_retry_handler(max_attempts=3)
async def okx_get_historical_ticks(
self,
inst_id: str,
after: Optional[str] = None,
before: Optional[str] = None,
limit: int = 100
) -> List[Dict]:
"""OKX 히스토리컬 Tick 데이터 조회"""
await self.rate_limiter.acquire("okx")
params = {
"instId": inst_id.upper(),
"limit": limit
}
if after:
params["after"] = after
if before:
params["before"] = before
# OKX는 서명 생성 필요
timestamp = time.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
message = f"GET/api/v5/market/trades?{urlencode(params)}{timestamp}"
signature = hmac.new(
EXCHANGES['okx'].api_secret.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
headers = {
"OKX-APIKEY": EXCHANGES['okx'].api_key,
"OKX-TIMESTAMP": timestamp,
"OKX-SIGNATURE": signature,
"OKX-PASSPHRASE": os.environ.get("OKX_PASSPHRASE", "")
}
async with self.session.get(
f"{EXCHANGES['okx'].base_url}/api/v5/market/trades",
params=params,
headers=headers
) as response:
if response.status == 429:
raise RetryableError("OKX rate limit exceeded")
if response.status != 200:
raise Exception(f"OKX API 오류: {response.status}")
result = await response.json()
return result.get("data", [])
6. 메인 실행 파일
# main.py
import asyncio
import json
import logging
from datetime import datetime, timedelta
from typing import List, Dict
from pathlib import Path
from config.settings import EXCHANGES
from clients.exchange_client import ExchangeClient
from utils.rate_limiter import MultiExchangeRateLimiter
로깅 설정
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s"
)
logger = logging.getLogger(__name__)
async def download_exchange_ticks(
client: ExchangeClient,
exchange_name: str,
symbol: str,
days: int = 7
) -> Dict[str, any]:
"""
단일 거래소 Tick 데이터 다운로드 및 HolySheep AI 분석
"""
logger.info(f"[{exchange_name}] {symbol} 데이터 다운로드 시작")
# 시간 범위 설정 (UTC)
end_time = int(datetime.now().timestamp() * 1000)
start_time = int((datetime.now() - timedelta(days=days)).timestamp() * 1000)
all_ticks = []
try:
# 거래소별 API 호출
if exchange_name == "binance":
ticks = await client.binance_get_historical_ticks(
symbol=symbol,
start_time=start_time,
end_time=end_time,
limit=1000
)
elif exchange_name == "bybit":
ticks = await client.bybit_get_historical_ticks(
category="spot",
symbol=symbol,
start_time=start_time,
end_time=end_time,
limit=1000
)
elif exchange_name == "okx":
ticks = await client.okx_get_historical_ticks(
inst_id=f"{symbol}-USDT",
before=str(end_time),
limit=100
)
else:
raise ValueError(f"지원하지 않는 거래소: {exchange_name}")
all_ticks.extend(ticks)
logger.info(
f"[{exchange_name}] {symbol} 다운로드 완료: {len(ticks)}건"
)
# HolySheep AI로 감성 분석 (100건 이상일 때만)
analysis_result = None
if len(all_ticks) >= 10:
try:
analysis_result = await client.analyze_with_holysheep(
tick_data=all_ticks[:100], # 토큰 절약을 위해 100건만
analysis_type="sentiment"
)
logger.info(
f"[{exchange_name}] HolySheep AI 분석 완료: "
f"토큰 사용량 {analysis_result['usage']}"
)
except Exception as e:
logger.warning(f"[{exchange_name}] AI 분석 실패: {e}")
return {
"exchange": exchange_name,
"symbol": symbol,
"tick_count": len(all_ticks),
"ticks": all_ticks,
"analysis": analysis_result,
"status": "success"
}
except Exception as e:
logger.error(f"[{exchange_name}] {symbol} 다운로드 실패: {e}")
return {
"exchange": exchange_name,
"symbol": symbol,
"status": "failed",
"error": str(e)
}
async def main():
"""
메인 실행 함수: 다중 거래소 동시 다운로드 + HolySheep AI 분석
"""
logger.info("=" * 60)
logger.info("다중 거래소 히스토리컬 Tick 데이터 다운로드 시작")
logger.info("=" * 60)
# Rate Limiter 초기화
rate_limiter = MultiExchangeRateLimiter()
for name, config in EXCHANGES.items():
rate_limiter.register_exchange(name, config.rate_limit_rpm)
# 다운로드할 심볼 목록
download_tasks = [
# Binance
("binance", "BTCUSDT"),
("binance", "ETHUSDT"),
("binance", "SOLUSDT"),
# Bybit
("bybit", "BTCUSDT"),
("bybit", "ETHUSDT"),
# OKX
("okx", "BTC-USDT"),
("okx", "ETH-USDT"),
]
start_time = datetime.now()
async with ExchangeClient(rate_limiter) as client:
# 동시 다운로드 실행
tasks = [
download_exchange_ticks(
client=client,
exchange_name=exchange,
symbol=symbol,
days=7
)
for exchange, symbol in download_tasks
]
results = await asyncio.gather(*tasks)
# 결과 저장
output_dir = Path("data")
output_dir.mkdir(exist_ok=True)
for result in results:
exchange = result["exchange"]
symbol = result["symbol"]
# JSON 파일로 저장
output_file = output_dir / exchange / f"{symbol}.json"
output_file.parent.mkdir(parents=True, exist_ok=True)
with open(output_file, "w", encoding="utf-8") as f:
json.dump(result, f, ensure_ascii=False, indent=2)
logger.info(f"결과 저장 완료: {output_file}")
elapsed = (datetime.now() - start_time).total_seconds()
# 결과 요약
success_count = sum(1 for r in results if r["status"] == "success")
total_ticks = sum(r.get("tick_count", 0) for r in results)
logger.info("=" * 60)
logger.info("다운로드 완료 요약")
logger.info(f"총 소요 시간: {elapsed:.2f}초")
logger.info(f"성공: {success_count}/{len(results)} 거래소")
logger.info(f"총 Tick 수: {total_ticks:,}건")
logger.info("=" * 60)
# HolySheep AI 토큰 사용량 요약
total_tokens = sum(
r.get("analysis", {}).get("usage", {}).get("total_tokens", 0)
for r in results
)
estimated_cost = (total_tokens / 1_000_000) * 0.42 # DeepSeek V3.2 가격
logger.info(f"HolySheep AI 토큰 사용량: {total_tokens:,} 토큰")
logger.info(f"HolySheep AI 예상 비용: ${estimated_cost:.4f}")
if __name__ == "__main__":
asyncio.run(main())
7. 실행 결과
# 실행 명령어
python main.py
출력 예시
2024-01-15 10:30:45 [INFO] ============================================================
2024-01-15 10:30:45 [INFO] 다중 거래소 히스토리컬 Tick 데이터 다운로드 시작
2024-01-15 10:30:45 [INFO] ============================================================
2024-01-15 10:30:46 [INFO] [binance] BTCUSDT 데이터 다운로드 시작
2024-01-15 10:30:46 [INFO] [binance] ETHUSDT 데이터 다운로드 시작
2024-01-15 10:30:46 [INFO] [bybit] BTCUSDT 데이터 다운로드 시작
2024-01-15 10:30:47 [INFO] [binance] BTCUSDT 다운로드 완료: 1,000건
2024-01-15 10:30:47 [INFO] [binance] BTCUSDT HolySheep AI 분석 완료: 토큰 사용량 {'total_tokens': 1250, 'prompt_tokens': 980, 'completion_tokens': 270}
2024-01-15 10:30:50 [INFO] ============================================================
2024-01-15 10:30:50 [INFO] 다운로드 완료 요약
2024-01-15 10:30:50 [INFO] 총 소요 시간: 4.23초
2024-01-15 10:30:50 [INFO] 성공: 7/7 거래소
2024-01-15 10:30:50 [INFO] 총 Tick 수: 7,200건
2024-01-15 10:30:50 [INFO] ============================================================
2024-01-15 10:30:50 [INFO] HolySheep AI 토큰 사용량: 8,750 토큰
2024-01-15 10:30:50 [INFO] HolySheep AI 예상 비용: $0.0037
---
asyncio 패턴 심화: 고급 동시성 관리
Semaphore를 활용한 동시성 제어
import asyncio
class AdaptiveConcurrency:
"""
적응형 동시성 제어
- 시스템 부하에 따라 동시 작업 수 자동 조절
"""
def __init__(self, max_concurrent: int = 10):
self.semaphore = asyncio.Semaphore(max_concurrent)
self.active_tasks = 0
self.success_count = 0
self.failure_count = 0
async def run_with_semaphore(self, coro):
"""Semaphore 내에서 코루틴 실행"""
async with self.semaphore:
self.active_tasks += 1
try:
result = await coro
self.success_count += 1
return result
except Exception as e:
self.failure_count += 1
raise
finally:
self.active_tasks -= 1
def get_stats(self) -> Dict:
"""현재 동시성 통계 반환"""
return {
"active": self.active_tasks,
"success": self.success_count,
"failure": self.failure_count,
"available_slots": self.semaphore._value
}
사용 예시
async def main_with_concurrency_control():
concurrency = AdaptiveConcurrency(max_concurrent=5)
tasks = [
concurrency.run_with_semaphore(
download_exchange_ticks(client, exchange, symbol)
)
for exchange, symbol in download_tasks
]
results = await asyncio.gather(*tasks)
print(f"통계: {concurrency.get_stats()}")
Priority Queue를 활용한 중요 데이터 우선 처리
import asyncio
from dataclasses import dataclass, field
from typing import Any, Callable
from enum import IntEnum
class Priority(IntEnum):
HIGH = 1
NORMAL = 2
LOW = 3
@dataclass(order=True)
class PriorityTask:
priority: int
coro: Callable = field(compare=False)
task_id: str = field(compare=False)
metadata: Dict = field(default_factory=dict, compare=False)
class PriorityTaskQueue:
"""우선순위 기반 태스크 큐"""
def __init__(self):
self.queue: asyncio.PriorityQueue = asyncio.PriorityQueue()
async def add_task(
self,
coro: Callable,
priority: Priority,
task_id: str,
metadata: Dict = None
):
"""태스크 추가"""
task = PriorityTask(
priority=priority.value,
coro=coro,
task_id=task_id,
metadata=metadata or {}
)
await self.queue.put(task)
async def get_task(self) -> PriorityTask:
"""가장 높은 우선순위 태스크 가져오기"""
return await self.queue.get()
def task_done(self):
"""태스크 완료 알림"""
self.queue.task_done()
async def process_all(self, max_workers: int = 5):
"""우선순위 순서로 모든 태스크 처리"""
async def worker(worker_id: int):
while True:
try:
task = await asyncio.wait_for(
self.get_task(),
timeout=1.0
)
print(f"[Worker-{worker_id}] {task.task_id} 처리 중...")
await task.coro
self.task_done()
except asyncio.TimeoutError:
if self.queue.empty():
break