fix: use rolling_window_ticker for symbol-specific queries, expand window choices

- Replace removed Spot.ticker() with rolling_window_ticker for symbol-specific
ticker stats (compatible with binance-connector>=3.12.0).
- Fall back to ticker_24hr for full-market scans where rolling_window_ticker
requires symbols.
- Expand --window choices from [1h,4h,1d] to full Binance rolling window set:
1m,2m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,2d,3d,5d,7d,15d,30d.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-20 23:18:28 +08:00
parent 69f447f538
commit a9f6cf4c46
4 changed files with 17 additions and 7 deletions

View File

@@ -19,6 +19,12 @@
--- ---
## What's New in 3.0.1
- **Fix ticker API compatibility** — `rolling_window_ticker` replaces the removed `ticker` method in `binance-connector>=3.12.0`.
- **Expand ticker window choices** — `market tickers --window` now supports `1m`, `2m`, `5m`, `15m`, `30m`, `1h`, `2h`, `4h`, `6h`, `8h`, `12h`, `1d`, `2d`, `3d`, `5d`, `7d`, `15d`, `30d`.
- **Smart API fallback** — full-market scan (no symbols) falls back to 24h ticker; symbol-specific queries use rolling window.
## What's New in 3.0 ## What's New in 3.0
- **Split decision models** — portfolio (add/hold/trim/exit) and opportunity (enter/watch/skip) now use independent scoring logic. - **Split decision models** — portfolio (add/hold/trim/exit) and opportunity (enter/watch/skip) now use independent scoring logic.

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "coinhunter" name = "coinhunter"
version = "3.0.0" version = "3.0.1"
description = "Binance-first trading CLI for balances, market data, opportunity scanning, and execution." description = "Binance-first trading CLI for balances, market data, opportunity scanning, and execution."
readme = "README.md" readme = "README.md"
license = {text = "MIT"} license = {text = "MIT"}

View File

@@ -53,13 +53,15 @@ class SpotBinanceClient:
return self._call("exchange info", self._client.exchange_info, **kwargs) # type: ignore[no-any-return] return self._call("exchange info", self._client.exchange_info, **kwargs) # type: ignore[no-any-return]
def ticker_stats(self, symbols: list[str] | None = None, *, window: str = "1d") -> list[dict[str, Any]]: def ticker_stats(self, symbols: list[str] | None = None, *, window: str = "1d") -> list[dict[str, Any]]:
kwargs: dict[str, Any] = {"windowSize": window}
if symbols: if symbols:
kwargs: dict[str, Any] = {"windowSize": window}
if len(symbols) == 1: if len(symbols) == 1:
kwargs["symbol"] = symbols[0] kwargs["symbol"] = symbols[0]
else: else:
kwargs["symbols"] = symbols kwargs["symbols"] = symbols
response = self._call("ticker stats", self._client.ticker, **kwargs) response = self._call("ticker stats", self._client.rolling_window_ticker, **kwargs)
else:
response = self._call("ticker stats", self._client.ticker_24hr)
return response if isinstance(response, list) else [response] 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]]:

View File

@@ -157,7 +157,7 @@ Fields:
last_price latest traded price (float) last_price latest traded price (float)
price_change_pct change % over the selected window (float, e.g. 2.5 = +2.5%) price_change_pct change % over the selected window (float, e.g. 2.5 = +2.5%)
quote_volume quote volume over the selected window (float) quote_volume quote volume over the selected window (float)
window statistics window (enum: 1h, 4h, 1d) window statistics window (enum: 1m, 2m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 2d, 3d, 5d, 7d, 15d, 30d)
""", """,
"json": """\ "json": """\
JSON Output: JSON Output:
@@ -172,7 +172,7 @@ Fields:
last_price latest traded price (float) last_price latest traded price (float)
price_change_pct change % over the selected window (float, e.g. 2.5 = +2.5%) price_change_pct change % over the selected window (float, e.g. 2.5 = +2.5%)
quote_volume quote volume over the selected window (float) quote_volume quote volume over the selected window (float)
window statistics window (enum: 1h, 4h, 1d) window statistics window (enum: 1m, 2m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 2d, 3d, 5d, 7d, 15d, 30d)
""", """,
}, },
"market/klines": { "market/klines": {
@@ -736,8 +736,10 @@ def build_parser() -> argparse.ArgumentParser:
) )
tickers_parser.add_argument("symbols", nargs="+", metavar="SYM", help="Symbols to query (e.g. BTCUSDT ETH/USDT)") tickers_parser.add_argument("symbols", nargs="+", metavar="SYM", help="Symbols to query (e.g. BTCUSDT ETH/USDT)")
tickers_parser.add_argument( tickers_parser.add_argument(
"-w", "--window", choices=["1h", "4h", "1d"], default="1d", "-w", "--window",
help="Statistics window: 1h, 4h, 1d (default: 1d)", choices=["1m", "2m", "5m", "15m", "30m", "1h", "2h", "4h", "6h", "8h", "12h", "1d", "2d", "3d", "5d", "7d", "15d", "30d"],
default="1d",
help="Rolling statistics window (default: 1d)",
) )
_add_global_flags(tickers_parser) _add_global_flags(tickers_parser)
klines_parser = market_subparsers.add_parser( klines_parser = market_subparsers.add_parser(