fix: resolve merge conflicts and lint issues
- Merge origin/main changes (flattened buy/sell commands, --doc flag, aliases) - Fix spinner placement for buy/sell commands - Fix duplicate alias key 'p' in canonical subcommands - Remove unused mypy ignore comments in spot_client.py - Fix nested with statements in tests Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,10 @@ from __future__ import annotations
|
|||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from requests.exceptions import RequestException, SSLError # type: ignore[import-untyped]
|
from requests.exceptions import ( # type: ignore[import-untyped]
|
||||||
|
RequestException,
|
||||||
|
SSLError,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SpotBinanceClient:
|
class SpotBinanceClient:
|
||||||
@@ -56,7 +59,7 @@ class SpotBinanceClient:
|
|||||||
response = self._call("24h ticker", self._client.ticker_24hr, symbol=symbols[0])
|
response = self._call("24h ticker", self._client.ticker_24hr, symbol=symbols[0])
|
||||||
else:
|
else:
|
||||||
response = self._call("24h ticker", self._client.ticker_24hr, symbols=symbols)
|
response = self._call("24h ticker", self._client.ticker_24hr, symbols=symbols)
|
||||||
return response if isinstance(response, list) else [response] # type: ignore[no-any-return]
|
return response if isinstance(response, list) else [response]
|
||||||
|
|
||||||
def ticker_price(self, symbols: list[str] | None = None) -> list[dict[str, Any]]:
|
def ticker_price(self, symbols: list[str] | None = None) -> list[dict[str, Any]]:
|
||||||
if not symbols:
|
if not symbols:
|
||||||
@@ -65,7 +68,7 @@ class SpotBinanceClient:
|
|||||||
response = self._call("ticker price", self._client.ticker_price, symbol=symbols[0])
|
response = self._call("ticker price", self._client.ticker_price, symbol=symbols[0])
|
||||||
else:
|
else:
|
||||||
response = self._call("ticker price", self._client.ticker_price, symbols=symbols)
|
response = self._call("ticker price", self._client.ticker_price, symbols=symbols)
|
||||||
return response if isinstance(response, list) else [response] # type: ignore[no-any-return]
|
return response if isinstance(response, list) else [response]
|
||||||
|
|
||||||
def klines(self, symbol: str, interval: str, limit: int) -> list[list[Any]]:
|
def klines(self, symbol: str, interval: str, limit: int) -> list[list[Any]]:
|
||||||
return self._call("klines", self._client.klines, symbol=symbol, interval=interval, limit=limit) # type: ignore[no-any-return]
|
return self._call("klines", self._client.klines, symbol=symbol, interval=interval, limit=limit) # type: ignore[no-any-return]
|
||||||
|
|||||||
@@ -10,8 +10,19 @@ from . import __version__
|
|||||||
from .audit import read_audit_log
|
from .audit import read_audit_log
|
||||||
from .binance.spot_client import SpotBinanceClient
|
from .binance.spot_client import SpotBinanceClient
|
||||||
from .config import ensure_init_files, get_binance_credentials, load_config
|
from .config import ensure_init_files, get_binance_credentials, load_config
|
||||||
from .runtime import get_runtime_paths, install_shell_completion, print_output, self_upgrade, with_spinner
|
from .runtime import (
|
||||||
from .services import account_service, market_service, opportunity_service, trade_service
|
get_runtime_paths,
|
||||||
|
install_shell_completion,
|
||||||
|
print_output,
|
||||||
|
self_upgrade,
|
||||||
|
with_spinner,
|
||||||
|
)
|
||||||
|
from .services import (
|
||||||
|
account_service,
|
||||||
|
market_service,
|
||||||
|
opportunity_service,
|
||||||
|
trade_service,
|
||||||
|
)
|
||||||
|
|
||||||
EPILOG = """\
|
EPILOG = """\
|
||||||
examples:
|
examples:
|
||||||
@@ -294,7 +305,6 @@ _CANONICAL_SUBCOMMANDS = {
|
|||||||
"t": "tickers",
|
"t": "tickers",
|
||||||
"k": "klines",
|
"k": "klines",
|
||||||
"pf": "portfolio",
|
"pf": "portfolio",
|
||||||
"p": "portfolio",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_COMMANDS_WITH_SUBCOMMANDS = {"account", "market", "opportunity"}
|
_COMMANDS_WITH_SUBCOMMANDS = {"account", "market", "opportunity"}
|
||||||
|
|||||||
@@ -8,7 +8,12 @@ import unittest
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from coinhunter.config import ensure_init_files, get_binance_credentials, load_config, load_env_file
|
from coinhunter.config import (
|
||||||
|
ensure_init_files,
|
||||||
|
get_binance_credentials,
|
||||||
|
load_config,
|
||||||
|
load_env_file,
|
||||||
|
)
|
||||||
from coinhunter.runtime import get_runtime_paths
|
from coinhunter.runtime import get_runtime_paths
|
||||||
|
|
||||||
|
|
||||||
@@ -89,6 +94,8 @@ class ConfigRuntimeTestCase(unittest.TestCase):
|
|||||||
),
|
),
|
||||||
):
|
):
|
||||||
paths = get_runtime_paths()
|
paths = get_runtime_paths()
|
||||||
with patch("coinhunter.config.ensure_runtime_dirs", side_effect=PermissionError("no write access")):
|
with (
|
||||||
with self.assertRaisesRegex(RuntimeError, "Set COINHUNTER_HOME to a writable directory"):
|
patch("coinhunter.config.ensure_runtime_dirs", side_effect=PermissionError("no write access")),
|
||||||
|
self.assertRaisesRegex(RuntimeError, "Set COINHUNTER_HOME to a writable directory"),
|
||||||
|
):
|
||||||
ensure_init_files(paths)
|
ensure_init_files(paths)
|
||||||
|
|||||||
@@ -57,8 +57,10 @@ class TradeServiceTestCase(unittest.TestCase):
|
|||||||
self.assertEqual(client.calls[0]["timeInForce"], "GTC")
|
self.assertEqual(client.calls[0]["timeInForce"], "GTC")
|
||||||
|
|
||||||
def test_spot_market_buy_requires_quote(self):
|
def test_spot_market_buy_requires_quote(self):
|
||||||
with patch.object(trade_service, "audit_event", return_value=None):
|
with (
|
||||||
with self.assertRaisesRegex(RuntimeError, "requires --quote"):
|
patch.object(trade_service, "audit_event", return_value=None),
|
||||||
|
self.assertRaisesRegex(RuntimeError, "requires --quote"),
|
||||||
|
):
|
||||||
trade_service.execute_spot_trade(
|
trade_service.execute_spot_trade(
|
||||||
{"trading": {"dry_run_default": False}},
|
{"trading": {"dry_run_default": False}},
|
||||||
side="buy",
|
side="buy",
|
||||||
@@ -72,8 +74,10 @@ class TradeServiceTestCase(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_spot_market_buy_rejects_qty(self):
|
def test_spot_market_buy_rejects_qty(self):
|
||||||
with patch.object(trade_service, "audit_event", return_value=None):
|
with (
|
||||||
with self.assertRaisesRegex(RuntimeError, "accepts --quote only"):
|
patch.object(trade_service, "audit_event", return_value=None),
|
||||||
|
self.assertRaisesRegex(RuntimeError, "accepts --quote only"),
|
||||||
|
):
|
||||||
trade_service.execute_spot_trade(
|
trade_service.execute_spot_trade(
|
||||||
{"trading": {"dry_run_default": False}},
|
{"trading": {"dry_run_default": False}},
|
||||||
side="buy",
|
side="buy",
|
||||||
@@ -87,8 +91,10 @@ class TradeServiceTestCase(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_spot_market_sell_rejects_quote(self):
|
def test_spot_market_sell_rejects_quote(self):
|
||||||
with patch.object(trade_service, "audit_event", return_value=None):
|
with (
|
||||||
with self.assertRaisesRegex(RuntimeError, "accepts --qty only"):
|
patch.object(trade_service, "audit_event", return_value=None),
|
||||||
|
self.assertRaisesRegex(RuntimeError, "accepts --qty only"),
|
||||||
|
):
|
||||||
trade_service.execute_spot_trade(
|
trade_service.execute_spot_trade(
|
||||||
{"trading": {"dry_run_default": False}},
|
{"trading": {"dry_run_default": False}},
|
||||||
side="sell",
|
side="sell",
|
||||||
|
|||||||
Reference in New Issue
Block a user