""" 거래소 어댑터 인터페이스 placeholder. 자동매매 본격 구현 시 각 거래소 SDK 를 wrap 한 ExchangeAdapter 구현체를 이 파일에 추가. 지금은 인터페이스 + 더미 구현 (DRY-RUN 으로 로그만 출력) 만 잡아둔다. 사용 흐름: cred = exchange_keys.get_credential(active_id) adapter = make_adapter(cred) adapter.place_market_order(symbol="BTCUSDT", side="long", qty=0.01) """ from dataclasses import dataclass, asdict from typing import Optional, Dict, Any, List @dataclass class OrderResult: ok: bool order_id: Optional[str] = None filled_qty: float = 0.0 avg_price: float = 0.0 raw: Optional[Dict[str, Any]] = None error: Optional[str] = None class ExchangeAdapter: """모든 거래소 어댑터의 베이스. dry_run=True 면 실제 거래소에 보내지 않는다.""" def __init__(self, exchange: str, api_key: str, api_secret: str, passphrase: Optional[str] = None, testnet: bool = False, dry_run: bool = True): self.exchange = exchange self.api_key = api_key self.api_secret = api_secret self.passphrase = passphrase self.testnet = testnet self.dry_run = dry_run # 하위 클래스에서 override def get_balance(self, asset: str = "USDT") -> float: raise NotImplementedError def get_position(self, symbol: str) -> Optional[Dict[str, Any]]: raise NotImplementedError def place_market_order(self, symbol: str, side: str, qty: float, reduce_only: bool = False) -> OrderResult: raise NotImplementedError def close_position(self, symbol: str) -> OrderResult: raise NotImplementedError def set_leverage(self, symbol: str, leverage: int) -> bool: raise NotImplementedError class DryRunAdapter(ExchangeAdapter): """모든 메서드를 stdout 으로만 시뮬레이션. 실제 SDK 가 연결되기 전 사용.""" def get_balance(self, asset: str = "USDT") -> float: print(f"[DRY {self.exchange}] get_balance({asset}) -> 1000.0 (stub)") return 1000.0 def get_position(self, symbol: str): print(f"[DRY {self.exchange}] get_position({symbol}) -> None") return None def place_market_order(self, symbol: str, side: str, qty: float, reduce_only: bool = False) -> OrderResult: msg = f"[DRY {self.exchange}] MARKET {side.upper()} {symbol} qty={qty} reduce_only={reduce_only}" print(msg) return OrderResult(ok=True, order_id="dry-" + symbol, filled_qty=qty, avg_price=0.0, raw={"note": msg}) def close_position(self, symbol: str) -> OrderResult: msg = f"[DRY {self.exchange}] CLOSE {symbol}" print(msg) return OrderResult(ok=True, order_id="dry-close-" + symbol, raw={"note": msg}) def set_leverage(self, symbol: str, leverage: int) -> bool: print(f"[DRY {self.exchange}] set_leverage({symbol}, {leverage}) -> True") return True def make_adapter(credential: Dict[str, Any], dry_run: bool = True) -> ExchangeAdapter: """exchange_keys.get_credential() 결과로부터 어댑터 생성. 실제 거래소별 구현이 추가되기 전엔 모두 DryRunAdapter 반환.""" if not credential: return DryRunAdapter("none", "", "", dry_run=True) return DryRunAdapter( exchange=credential.get("exchange", ""), api_key=credential.get("api_key") or "", api_secret=credential.get("api_secret") or "", passphrase=credential.get("passphrase"), testnet=bool(credential.get("testnet")), dry_run=dry_run, )