refactor: rewrite to CoinHunter V2 flat architecture
Replace the V1 commands/services split with a flat, direct architecture: - cli.py dispatches directly to service functions - New services: account, market, trade, opportunity - Thin Binance wrappers: spot_client, um_futures_client - Add audit logging, runtime paths, and TOML config - Remove legacy V1 code: commands/, precheck, review engine, smart executor - Add ruff + mypy toolchain and fix edge cases in trade params Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
80
tests/test_account_market_services.py
Normal file
80
tests/test_account_market_services.py
Normal file
@@ -0,0 +1,80 @@
|
||||
"""Account and market service tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from coinhunter.services import account_service, market_service
|
||||
|
||||
|
||||
class FakeSpotClient:
|
||||
def account_info(self):
|
||||
return {
|
||||
"balances": [
|
||||
{"asset": "USDT", "free": "120.0", "locked": "0"},
|
||||
{"asset": "BTC", "free": "0.01", "locked": "0"},
|
||||
{"asset": "DOGE", "free": "1", "locked": "0"},
|
||||
]
|
||||
}
|
||||
|
||||
def ticker_price(self, symbols=None):
|
||||
prices = {
|
||||
"BTCUSDT": {"symbol": "BTCUSDT", "price": "60000"},
|
||||
"DOGEUSDT": {"symbol": "DOGEUSDT", "price": "0.1"},
|
||||
}
|
||||
if not symbols:
|
||||
return list(prices.values())
|
||||
return [prices[symbol] for symbol in symbols]
|
||||
|
||||
def ticker_24h(self, symbols=None):
|
||||
rows = [
|
||||
{"symbol": "BTCUSDT", "lastPrice": "60000", "priceChangePercent": "4.5", "quoteVolume": "10000000", "highPrice": "61000", "lowPrice": "58000"},
|
||||
{"symbol": "ETHUSDT", "lastPrice": "3000", "priceChangePercent": "3.0", "quoteVolume": "8000000", "highPrice": "3050", "lowPrice": "2900"},
|
||||
{"symbol": "DOGEUSDT", "lastPrice": "0.1", "priceChangePercent": "1.0", "quoteVolume": "200", "highPrice": "0.11", "lowPrice": "0.09"},
|
||||
]
|
||||
if not symbols:
|
||||
return rows
|
||||
wanted = set(symbols)
|
||||
return [row for row in rows if row["symbol"] in wanted]
|
||||
|
||||
def exchange_info(self):
|
||||
return {"symbols": [{"symbol": "BTCUSDT", "status": "TRADING"}, {"symbol": "ETHUSDT", "status": "TRADING"}, {"symbol": "DOGEUSDT", "status": "BREAK"}]}
|
||||
|
||||
|
||||
class FakeFuturesClient:
|
||||
def balance(self):
|
||||
return [{"asset": "USDT", "balance": "250.0", "availableBalance": "200.0"}]
|
||||
|
||||
def position_risk(self, symbol=None):
|
||||
return [{"symbol": "BTCUSDT", "positionAmt": "0.02", "notional": "1200", "entryPrice": "59000", "markPrice": "60000", "unRealizedProfit": "20"}]
|
||||
|
||||
|
||||
class AccountMarketServicesTestCase(unittest.TestCase):
|
||||
def test_account_overview_and_dust_filter(self):
|
||||
config = {
|
||||
"market": {"default_quote": "USDT"},
|
||||
"trading": {"dust_usdt_threshold": 10.0},
|
||||
}
|
||||
payload = account_service.get_overview(
|
||||
config,
|
||||
include_spot=True,
|
||||
include_futures=True,
|
||||
spot_client=FakeSpotClient(),
|
||||
futures_client=FakeFuturesClient(),
|
||||
)
|
||||
self.assertEqual(payload["overview"]["spot_equity_usdt"], 720.1)
|
||||
self.assertEqual(payload["overview"]["futures_equity_usdt"], 250.0)
|
||||
symbols = {item["symbol"] for item in payload["positions"]}
|
||||
self.assertNotIn("DOGEUSDT", symbols)
|
||||
self.assertIn("BTCUSDT", symbols)
|
||||
|
||||
def test_market_tickers_and_scan_universe(self):
|
||||
config = {
|
||||
"market": {"default_quote": "USDT", "universe_allowlist": [], "universe_denylist": []},
|
||||
"opportunity": {"min_quote_volume": 1000},
|
||||
}
|
||||
tickers = market_service.get_tickers(config, ["btc/usdt", "ETH-USDT"], spot_client=FakeSpotClient())
|
||||
self.assertEqual([item["symbol"] for item in tickers["tickers"]], ["BTCUSDT", "ETHUSDT"])
|
||||
|
||||
universe = market_service.get_scan_universe(config, spot_client=FakeSpotClient())
|
||||
self.assertEqual([item["symbol"] for item in universe], ["BTCUSDT", "ETHUSDT"])
|
||||
Reference in New Issue
Block a user