From 7324a7513f3708e9e5db1fccb1db6f7b62a8986e Mon Sep 17 00:00:00 2001 From: EJMM17 <104163729+EJMM17@users.noreply.github.com> Date: Mon, 16 Feb 2026 10:11:34 -0600 Subject: [PATCH] Validate Binance symbol sync and tighten live preflight --- MAINNET_VPS_RUNBOOK_ES.md | 171 ++++++++++++++++++++++++++++++ darwin_agent/exchanges/binance.py | 70 ++++++++++-- darwin_agent/exchanges/router.py | 29 ++++- darwin_agent/main.py | 40 ++++++- test_binance_adapter_safety.py | 108 +++++++++++++++++++ test_main_runtime_compat.py | 100 +++++++++++++++++ test_router_set_leverage.py | 30 ++++++ 7 files changed, 535 insertions(+), 13 deletions(-) create mode 100644 MAINNET_VPS_RUNBOOK_ES.md create mode 100644 test_binance_adapter_safety.py create mode 100644 test_main_runtime_compat.py create mode 100644 test_router_set_leverage.py diff --git a/MAINNET_VPS_RUNBOOK_ES.md b/MAINNET_VPS_RUNBOOK_ES.md new file mode 100644 index 0000000..ebfa9e5 --- /dev/null +++ b/MAINNET_VPS_RUNBOOK_ES.md @@ -0,0 +1,171 @@ +# Darwin v4 — Guía clara para correr 24/7 en VPS (Binance Mainnet) + +## 0) Requisitos mínimos de VPS + +Recomendado para correr Darwin 24/7 de forma estable: + +- CPU: 2 vCPU (mínimo), 4 vCPU recomendado. +- RAM: 4 GB mínimo, 8 GB recomendado. +- Disco: 30 GB SSD mínimo. +- SO: Ubuntu 22.04 LTS o 24.04 LTS. +- Red: latencia estable y salida HTTPS sin bloqueos a APIs Binance. +- Hora del sistema: NTP activo (muy importante para evitar errores de timestamp). +- Seguridad: + - firewall activo (UFW) + - acceso SSH por llave + - fail2ban recomendado + +## 1) Antes de poner API keys (checklist obligatorio) + +- [ ] Ejecutar en **testnet** mínimo 48h sin errores críticos. +- [ ] Confirmar que `mode=live` solo se activará cuando todo esté estable. +- [ ] Crear API key de Binance Futures con: + - **Enable Futures** + - **NO** habilitar retiro (withdrawals). + - Restringir por IP al VPS. +- [ ] Definir límites de riesgo realistas en `config.yaml`: + - `max_total_exposure_pct` + - `halted_drawdown_pct` + - `max_consecutive_losses` + +## 2) Preparar VPS + +```bash +sudo apt update && sudo apt install -y python3.12 python3.12-venv git +mkdir -p /opt/darwin && cd /opt/darwin +git clone darwin-v4 +cd darwin-v4 +python3.12 -m venv .venv +source .venv/bin/activate +pip install --upgrade pip +pip install -r requirements.txt +``` + +## 3) Variables de entorno seguras + +Crear `/opt/darwin/darwin-v4/.env`: + +```bash +DARWIN_MODE=live +BINANCE_API_KEY=TU_API_KEY +BINANCE_API_SECRET=TU_API_SECRET +LOG_LEVEL=INFO +``` + +> Nunca subas `.env` al repo. + +## 4) Config mínima recomendada para Binance mainnet + +Archivo `config.yaml` (ejemplo base): + +```yaml +mode: live +capital: + starting_capital: 100.0 +risk: + max_total_exposure_pct: 40.0 + defensive_drawdown_pct: 6.0 + critical_drawdown_pct: 10.0 + halted_drawdown_pct: 14.0 + max_consecutive_losses: 4 +infra: + tick_interval: 30 + log_level: INFO +exchanges: + - exchange_id: binance + enabled: true + testnet: false + leverage: 10 + symbols: ["BTCUSDT", "ETHUSDT"] +``` + +## 5) Preflight antes de dejarlo solo + +```bash +cd /opt/darwin/darwin-v4 +source .venv/bin/activate +pytest -q test_binance_adapter_safety.py test_router_set_leverage.py test_main_runtime_compat.py +python -m compileall -q darwin_agent +python -m darwin_agent.main --config config.yaml --mode live --tick 30 +``` + +Observa 15-30 minutos logs antes de dejarlo 24/7. + +## 6) Ejecutar 24/7 con systemd + +Crear `/etc/systemd/system/darwin.service`: + +```ini +[Unit] +Description=Darwin Agent v4 +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/darwin/darwin-v4 +EnvironmentFile=/opt/darwin/darwin-v4/.env +ExecStart=/opt/darwin/darwin-v4/.venv/bin/python -m darwin_agent.main --config /opt/darwin/darwin-v4/config.yaml +Restart=always +RestartSec=5 +KillSignal=SIGTERM +TimeoutStopSec=30 + +[Install] +WantedBy=multi-user.target +``` + +Activar: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable darwin +sudo systemctl start darwin +sudo systemctl status darwin --no-pager +``` + +Logs en vivo: + +```bash +journalctl -u darwin -f +``` + +## 7) Operación diaria (runbook corto) + +- Revisar cada mañana: + - PnL + - drawdown + - número de errores de exchange en logs +- Si hay errores repetidos (`timestamp`, `recvWindow`, `insufficient margin`): + 1. parar servicio + 2. revisar reloj/NTP, margen y límites + 3. reiniciar con tamaño de riesgo menor + +## 8) Kill switch manual + +Paro inmediato: + +```bash +sudo systemctl stop darwin +``` + +Si quieres hard stop por riesgo, baja temporalmente: +- `max_total_exposure_pct` a 0-5 +- o cambiar `mode: test` mientras investigas. + +## 9) Actualizar sin downtime largo + +```bash +cd /opt/darwin/darwin-v4 +sudo systemctl stop darwin +git pull +source .venv/bin/activate +pip install -r requirements.txt +pytest -q test_binance_adapter_safety.py test_router_set_leverage.py test_main_runtime_compat.py +sudo systemctl start darwin +``` + +--- + +Si sigues esta guía, el siguiente paso de meter API keys queda controlado y con salvaguardas operativas. diff --git a/darwin_agent/exchanges/binance.py b/darwin_agent/exchanges/binance.py index b925b66..1b403dd 100644 --- a/darwin_agent/exchanges/binance.py +++ b/darwin_agent/exchanges/binance.py @@ -10,8 +10,8 @@ import hmac import logging import time -from datetime import datetime, timezone -from typing import Dict, List +from datetime import datetime, timezone, timedelta +from typing import Dict, List, Set from urllib.parse import urlencode from darwin_agent.interfaces.enums import ( @@ -31,6 +31,11 @@ } +def _normalize_symbol(symbol: str) -> str: + # Accept formats like BTC/USDT, btcusdt, BTC-USDT + return symbol.replace("/", "").replace("-", "").replace("_", "").upper() + + class BinanceAdapter: """ Production Binance USDⓈ-M Futures adapter. @@ -40,7 +45,7 @@ class BinanceAdapter: - Hedge mode awareness """ - __slots__ = ("_api_key", "_api_secret", "_base_url", "_session", "_testnet") + __slots__ = ("_api_key", "_api_secret", "_base_url", "_session", "_testnet", "_symbols_cache", "_symbols_cache_at") def __init__(self, api_key: str, api_secret: str, testnet: bool = False) -> None: self._api_key = api_key @@ -51,6 +56,8 @@ def __init__(self, api_key: str, api_secret: str, testnet: bool = False) -> None else "https://fapi.binance.com" ) self._session = None + self._symbols_cache: Set[str] = set() + self._symbols_cache_at: datetime | None = None @property def exchange_id(self) -> ExchangeID: @@ -102,11 +109,45 @@ async def _signed(self, method: str, path: str, params: Dict = None) -> Dict: else: raise + async def get_exchange_symbols(self, force_refresh: bool = False) -> Set[str]: + """Fetch Binance futures tradable symbols and cache briefly.""" + now = datetime.now(timezone.utc) + cache_ttl = timedelta(minutes=5) + if ( + not force_refresh + and self._symbols_cache + and self._symbols_cache_at is not None + and (now - self._symbols_cache_at) <= cache_ttl + ): + return set(self._symbols_cache) + + data = await self._public("/fapi/v1/exchangeInfo") + symbols: Set[str] = set() + for item in data.get("symbols", []): + if item.get("status") != "TRADING": + continue + name = item.get("symbol") + if name: + symbols.add(_normalize_symbol(name)) + self._symbols_cache = symbols + self._symbols_cache_at = now + return set(symbols) + + async def validate_symbols(self, symbols: List[str]) -> List[str]: + """Return list of symbols not present/tradable on Binance futures.""" + available = await self.get_exchange_symbols() + missing = [] + for raw in symbols: + sym = _normalize_symbol(raw) + if sym not in available: + missing.append(raw) + return missing + # ── IExchangeAdapter ───────────────────────────────────── async def get_candles(self, symbol: str, timeframe: TimeFrame, limit: int = 100) -> List[Candle]: data = await self._public("/fapi/v1/klines", { - "symbol": symbol, + "symbol": _normalize_symbol(symbol), "interval": _TF_MAP.get(timeframe, "15m"), "limit": limit, }) @@ -121,6 +162,7 @@ async def get_candles(self, symbol: str, timeframe: TimeFrame, limit: int = 100) ] async def get_ticker(self, symbol: str) -> Ticker: + symbol = _normalize_symbol(symbol) data = await self._public("/fapi/v1/ticker/bookTicker", {"symbol": symbol}) price_data = await self._public("/fapi/v1/ticker/price", {"symbol": symbol}) return Ticker( @@ -154,7 +196,7 @@ async def get_positions(self) -> List[Position]: async def place_order(self, request: OrderRequest) -> OrderResult: params = { - "symbol": request.symbol, + "symbol": _normalize_symbol(request.symbol), "side": "BUY" if request.side == OrderSide.BUY else "SELL", "type": "MARKET" if request.order_type == OrderType.MARKET else "LIMIT", "quantity": str(request.quantity), @@ -162,6 +204,8 @@ async def place_order(self, request: OrderRequest) -> OrderResult: if request.price and request.order_type == OrderType.LIMIT: params["price"] = str(request.price) params["timeInForce"] = "GTC" + if request.reduce_only: + params["reduceOnly"] = "true" data = await self._signed("POST", "/fapi/v1/order", params) if "orderId" not in data: return OrderResult( @@ -177,8 +221,9 @@ async def place_order(self, request: OrderRequest) -> OrderResult: ) async def close_position(self, symbol: str, side: OrderSide) -> OrderResult: + symbol = _normalize_symbol(symbol) positions = await self.get_positions() - pos = next((p for p in positions if p.symbol == symbol), None) + pos = next((p for p in positions if p.symbol == symbol and p.side == side), None) if not pos: return OrderResult( success=False, error="no_position", @@ -186,11 +231,16 @@ async def close_position(self, symbol: str, side: OrderSide) -> OrderResult: ) close_side = "SELL" if side == OrderSide.BUY else "BUY" params = { - "symbol": symbol, "side": close_side, + "symbol": _normalize_symbol(symbol), "side": close_side, "type": "MARKET", "quantity": str(pos.size), "reduceOnly": "true", } data = await self._signed("POST", "/fapi/v1/order", params) + if "orderId" not in data: + return OrderResult( + success=False, error=data.get("msg", "unknown"), + exchange_id=ExchangeID.BINANCE, + ) return OrderResult( order_id=str(data.get("orderId", "")), symbol=symbol, side=OrderSide(close_side), @@ -200,9 +250,11 @@ async def close_position(self, symbol: str, side: OrderSide) -> OrderResult: async def set_leverage(self, symbol: str, leverage: int) -> bool: try: - await self._signed("POST", "/fapi/v1/leverage", { - "symbol": symbol, "leverage": str(leverage), + data = await self._signed("POST", "/fapi/v1/leverage", { + "symbol": _normalize_symbol(symbol), "leverage": str(leverage), }) + if isinstance(data, dict) and data.get("code", 0) < 0: + return False return True except Exception: return False diff --git a/darwin_agent/exchanges/router.py b/darwin_agent/exchanges/router.py index 5b79c1a..e4f669a 100644 --- a/darwin_agent/exchanges/router.py +++ b/darwin_agent/exchanges/router.py @@ -201,11 +201,11 @@ async def set_leverage(self, symbol: str, leverage: int) -> bool: if adapter is None: return False try: - await self._timed( + result = await self._timed( adapter.set_leverage(symbol, leverage), f"set_leverage({symbol}@{target.value})", ) - return True + return bool(result) except Exception as exc: logger.warning("set_leverage failed on %s: %s", target.value, exc) return False @@ -228,6 +228,31 @@ async def get_balance(self) -> float: logger.error("get_balance failed on ALL exchanges — returning 0.0") return total + + async def close(self) -> None: + """Close all adapters that expose an async close() method.""" + for eid, adapter in self._adapters.items(): + close = getattr(adapter, "close", None) + if close is None: + continue + try: + await self._timed(close(), f"close({eid.value})") + except Exception as exc: + logger.warning("close failed on %s: %s", eid.value, exc) + + async def connect_all(self) -> None: + """Backward-compatible startup hook.""" + await self.refresh_statuses() + + async def get_all_statuses(self) -> Dict[ExchangeID, ExchangeStatus]: + """Backward-compatible status API expected by main entrypoint.""" + await self.refresh_statuses() + return self.get_exchange_statuses() + + async def disconnect_all(self) -> None: + """Backward-compatible shutdown hook.""" + await self.close() + # ── IExchangeRouter specific ───────────────────────────── def get_exchange_statuses(self) -> Dict[ExchangeID, ExchangeStatus]: diff --git a/darwin_agent/main.py b/darwin_agent/main.py index 5d9d392..eff0bea 100644 --- a/darwin_agent/main.py +++ b/darwin_agent/main.py @@ -42,6 +42,7 @@ from darwin_agent.core.agent import AgentConfig, DarwinAgent from darwin_agent.exchanges.router import ExchangeRouter from darwin_agent.exchanges.bybit import BybitAdapter +from darwin_agent.exchanges.binance import BinanceAdapter from darwin_agent.evolution.engine import EvolutionEngine from darwin_agent.evolution.diagnostics import EvolutionDiagnostics from darwin_agent.risk.portfolio_engine import PortfolioRiskEngine, RiskLimits @@ -95,8 +96,15 @@ def build_exchange_router(config: DarwinConfig) -> ExchangeRouter: api_secret=ex_cfg.api_secret, testnet=ex_cfg.testnet, ) - adapters[eid] = adapter - # Add more exchanges here as adapters are built + elif eid == ExchangeID.BINANCE: + adapter = BinanceAdapter( + api_key=ex_cfg.api_key, + api_secret=ex_cfg.api_secret, + testnet=ex_cfg.testnet, + ) + else: + raise RuntimeError(f"Unsupported exchange configured: {eid.value}") + adapters[eid] = adapter if primary is None: primary = eid @@ -106,6 +114,32 @@ def build_exchange_router(config: DarwinConfig) -> ExchangeRouter: return ExchangeRouter(adapters=adapters, primary=primary) +async def validate_exchange_symbol_sync(router: ExchangeRouter, config: DarwinConfig) -> None: + """Validate configured symbols exist on Binance futures before trading.""" + logger = logging.getLogger("darwin.main") + for ex_cfg in config.exchanges: + if not ex_cfg.enabled or ex_cfg.exchange_id.lower() != ExchangeID.BINANCE.value: + continue + adapter = router._adapters.get(ExchangeID.BINANCE) + if adapter is None: + raise RuntimeError("Binance adapter missing while binance exchange is enabled") + if not hasattr(adapter, "validate_symbols"): + logger.warning("binance adapter does not support symbol validation") + continue + missing = await adapter.validate_symbols(ex_cfg.symbols) + if missing: + msg = ( + "Configured Binance symbols are not tradable/valid: " + + ", ".join(missing) + ) + if config.is_live: + raise RuntimeError(msg) + logger.warning(msg) + else: + logger.info("binance symbols validated: %s", ", ".join(ex_cfg.symbols)) + + + def build_risk_engine(config: DarwinConfig) -> PortfolioRiskEngine: """Build PortfolioRiskEngine from config.""" limits = RiskLimits( @@ -232,6 +266,8 @@ async def run(config: DarwinConfig): for eid, status in statuses.items(): logger.info(" %s: %s", eid.value, "CONNECTED" if status.connected else "FAILED") + + await validate_exchange_symbol_sync(router, config) except Exception as exc: logger.error("exchange connection failed: %s", exc) if config.is_live: diff --git a/test_binance_adapter_safety.py b/test_binance_adapter_safety.py new file mode 100644 index 0000000..8973a5d --- /dev/null +++ b/test_binance_adapter_safety.py @@ -0,0 +1,108 @@ +import asyncio + +from darwin_agent.exchanges.binance import BinanceAdapter +from darwin_agent.interfaces.enums import OrderSide, OrderType +from darwin_agent.interfaces.types import OrderRequest, Position + + +class _FakeBinance(BinanceAdapter): + __slots__ = ("responses", "positions", "last_request", "public_payload", "public_calls") + + def __init__(self): + super().__init__("k", "s") + self.responses = [] + self.positions = [] + self.last_request = None + self.public_payload = {"symbols": []} + self.public_calls = 0 + + async def _signed(self, method: str, path: str, params=None): + self.last_request = {"method": method, "path": path, "params": params or {}} + if self.responses: + return self.responses.pop(0) + return {} + + async def get_positions(self): + return list(self.positions) + + async def _public(self, path: str, params=None): + self.public_calls += 1 + return self.public_payload + + +def test_place_order_passes_reduce_only_flag(): + adapter = _FakeBinance() + adapter.responses = [{"orderId": "1", "executedQty": "0.01", "avgPrice": "50000"}] + + req = OrderRequest( + symbol="BTCUSDT", + side=OrderSide.SELL, + order_type=OrderType.MARKET, + quantity=0.01, + reduce_only=True, + ) + res = asyncio.run(adapter.place_order(req)) + + assert res.success is True + assert adapter.last_request["params"].get("reduceOnly") == "true" + + +def test_close_position_fails_if_exchange_returns_error_payload(): + adapter = _FakeBinance() + adapter.positions = [ + Position(symbol="BTCUSDT", side=OrderSide.BUY, size=0.2, entry_price=50000) + ] + adapter.responses = [{"code": -2011, "msg": "Order would immediately trigger"}] + + res = asyncio.run(adapter.close_position("BTCUSDT", OrderSide.BUY)) + assert res.success is False + assert "trigger" in res.error.lower() + + +def test_close_position_targets_requested_side_in_hedge_mode(): + adapter = _FakeBinance() + adapter.positions = [ + Position(symbol="BTCUSDT", side=OrderSide.SELL, size=0.5, entry_price=50010), + Position(symbol="BTCUSDT", side=OrderSide.BUY, size=0.1, entry_price=50000), + ] + adapter.responses = [{"orderId": "99"}] + + res = asyncio.run(adapter.close_position("BTCUSDT", OrderSide.BUY)) + assert res.success is True + # Closing BUY should send SELL with BUY position size (0.1), not SELL size (0.5) + assert adapter.last_request["params"]["side"] == "SELL" + assert adapter.last_request["params"]["quantity"] == "0.1" + + +def test_set_leverage_returns_false_on_binance_error_code(): + adapter = _FakeBinance() + adapter.responses = [{"code": -1021, "msg": "Timestamp outside recvWindow"}] + + ok = asyncio.run(adapter.set_leverage("BTCUSDT", 20)) + assert ok is False + + +def test_validate_symbols_normalizes_and_filters_non_trading(): + adapter = _FakeBinance() + adapter.public_payload = { + "symbols": [ + {"symbol": "BTCUSDT", "status": "TRADING"}, + {"symbol": "ETHUSDT", "status": "TRADING"}, + {"symbol": "BNBUSDT", "status": "BREAK"}, + ] + } + + missing = asyncio.run(adapter.validate_symbols(["btc/usdt", "ETH-USDT", "bnbusdt", "XRPUSDT"])) + assert missing == ["bnbusdt", "XRPUSDT"] + + +def test_get_exchange_symbols_uses_cache(): + adapter = _FakeBinance() + adapter.public_payload = {"symbols": [{"symbol": "BTCUSDT", "status": "TRADING"}]} + + symbols1 = asyncio.run(adapter.get_exchange_symbols()) + symbols2 = asyncio.run(adapter.get_exchange_symbols()) + + assert symbols1 == {"BTCUSDT"} + assert symbols2 == {"BTCUSDT"} + assert adapter.public_calls == 1 diff --git a/test_main_runtime_compat.py b/test_main_runtime_compat.py new file mode 100644 index 0000000..b8d34ec --- /dev/null +++ b/test_main_runtime_compat.py @@ -0,0 +1,100 @@ +import asyncio + +from darwin_agent.config import DarwinConfig, ExchangeConfig +from darwin_agent.interfaces.enums import ExchangeID +from darwin_agent.interfaces.types import ExchangeStatus +from darwin_agent.exchanges.router import ExchangeRouter +from darwin_agent.main import build_exchange_router, validate_exchange_symbol_sync + + +class _Adapter: + def __init__(self, eid): + self.eid = eid + self.closed = False + + async def get_status(self): + return ExchangeStatus(exchange_id=self.eid, connected=True, latency_ms=1.0) + + async def close(self): + self.closed = True + + +def test_router_backward_compatible_hooks_work(): + bybit = _Adapter(ExchangeID.BYBIT) + router = ExchangeRouter(adapters={ExchangeID.BYBIT: bybit}, primary=ExchangeID.BYBIT) + + asyncio.run(router.connect_all()) + statuses = asyncio.run(router.get_all_statuses()) + assert ExchangeID.BYBIT in statuses + assert statuses[ExchangeID.BYBIT].connected is True + + asyncio.run(router.disconnect_all()) + assert bybit.closed is True + + +def test_build_exchange_router_supports_binance_config(): + config = DarwinConfig( + exchanges=[ + ExchangeConfig( + exchange_id="binance", + api_key="k", + api_secret="s", + enabled=True, + testnet=True, + ) + ] + ) + + router = build_exchange_router(config) + assert router is not None + assert ExchangeID.BINANCE in router._adapters + + +class _BinanceValidatorAdapter: + def __init__(self, missing=None): + self._missing = missing or [] + + async def validate_symbols(self, symbols): + return list(self._missing) + + +def test_validate_exchange_symbol_sync_passes_when_all_symbols_valid(): + config = DarwinConfig( + mode="live", + exchanges=[ + ExchangeConfig( + exchange_id="binance", + enabled=True, + symbols=["BTCUSDT", "ETHUSDT"], + ) + ], + ) + router = ExchangeRouter( + adapters={ExchangeID.BINANCE: _BinanceValidatorAdapter(missing=[])}, + primary=ExchangeID.BINANCE, + ) + asyncio.run(validate_exchange_symbol_sync(router, config)) + + +def test_validate_exchange_symbol_sync_raises_in_live_for_missing_symbols(): + config = DarwinConfig( + mode="live", + exchanges=[ + ExchangeConfig( + exchange_id="binance", + enabled=True, + symbols=["BTCUSDT", "BADPAIR"], + ) + ], + ) + router = ExchangeRouter( + adapters={ExchangeID.BINANCE: _BinanceValidatorAdapter(missing=["BADPAIR"])}, + primary=ExchangeID.BINANCE, + ) + + try: + asyncio.run(validate_exchange_symbol_sync(router, config)) + except RuntimeError as exc: + assert "BADPAIR" in str(exc) + else: + raise AssertionError("Expected RuntimeError for invalid live Binance symbols") diff --git a/test_router_set_leverage.py b/test_router_set_leverage.py new file mode 100644 index 0000000..52d1d6a --- /dev/null +++ b/test_router_set_leverage.py @@ -0,0 +1,30 @@ +import asyncio + +from darwin_agent.exchanges.router import ExchangeRouter +from darwin_agent.interfaces.enums import ExchangeID + + +class _AdapterTrue: + async def set_leverage(self, symbol, leverage): + return True + + +class _AdapterFalse: + async def set_leverage(self, symbol, leverage): + return False + + +def test_set_leverage_reflects_adapter_result_true(): + router = ExchangeRouter( + adapters={ExchangeID.BYBIT: _AdapterTrue()}, + primary=ExchangeID.BYBIT, + ) + assert asyncio.run(router.set_leverage("BTCUSDT", 10)) is True + + +def test_set_leverage_reflects_adapter_result_false(): + router = ExchangeRouter( + adapters={ExchangeID.BYBIT: _AdapterFalse()}, + primary=ExchangeID.BYBIT, + ) + assert asyncio.run(router.set_leverage("BTCUSDT", 10)) is False