# -*- coding: utf-8 -*- """Local test for trade endpoints (mock QMT). Run with QMT pythonw.""" import sys, io, os, json, time, urllib.request if sys.stdout is None: sys.stdout = io.StringIO() if sys.stderr is None: sys.stderr = io.StringIO() RESULT = r"C:\Users\Docker\Development\qmt_bridge\tests\trade_test.txt" SRC = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "src") sys.path.insert(0, SRC) def log(msg): with open(RESULT, "a") as f: f.write(msg + "\n") import bridge_util, bridge_http_server, bridge_data_adapter class FakeCtx: def get_market_data_ex(self, fields, codes, **kw): import pandas as pd df = pd.DataFrame({"open": [1.0], "close": [1.1]}) return {codes[0]: df} def get_full_tick(self, codes): return {c: {"lastPrice": 1.0} for c in codes} def get_instrument_detail(self, code): return {"InstrumentName": "贵州茅台", "UpStopPrice": 1438.67, "DownStopPrice": 1177.09, "OpenDate": "20010827"} def get_trading_dates(self, start="", end=""): return ["20260818", "20260819", "20260820"] bridge_util.CTX = FakeCtx() bridge_util.ACCOUNT = "TESTACC" def fake_trade(acct, atype, dtype): if dtype == "position": return [type("Pos", (), {"m_strInstrumentID": "600519", "m_strInstrumentName": "贵州茅台", "m_nVolume": 100, "m_nCanUseVolume": 50, "m_nFrozenVolume": 50, "m_dOpenPrice": 1500.0, "m_dFloatProfit": 1000.0})()] return [type("Pos", (), {"m_strInstrumentID": "600519.SH", "m_nVolume": 100, "m_nCanUseVolume": 50, "m_dOpenPrice": 1500.0})()] bridge_util.QMT_API["get_trade_detail_data"] = fake_trade bridge_http_server.PORT = 18631 bridge_http_server.TOKEN = "" bridge_http_server.ACCOUNT = "TESTACC" # prime the trade cache by simulating a strategy-thread refresh bridge_util._refresh_trade_cache("TESTACC", "STOCK") bridge_http_server.start_server() time.sleep(0.8) BASE = "http://127.0.0.1:18631" def get(path): with urllib.request.urlopen(BASE + path, timeout=5) as r: return json.loads(r.read().decode()) def check_positions(): """Assert /trade/positions returns semantic fields + summary.""" r = get("/trade/positions") data = r["data"] assert r["ok"] is True, "positions ok flag" assert "summary" in data, "summary present" assert data["summary"]["count"] == 1, "summary count" pos = data["positions"][0] assert pos["stock_code"] == "600519.SH", "stock_code full" assert pos["stock_name"] == "贵州茅台", "stock_name" assert pos["volume"] == 100, "volume" assert pos["available"] == 50, "available" assert pos["avg_price"] == 1500.0, "avg_price" assert pos["price"] == 1500.0, "price fallback to open" assert pos["market_value"] == 150000.0, "market_value computed" assert pos["profit"] == 1000.0, "profit from m_dFloatProfit" assert pos["profit_pct"] == 0.67, "profit_pct computed" # raw m_* fields preserved as superset assert pos["m_strInstrumentID"] == "600519", "raw m_strInstrumentID" assert pos["m_nVolume"] == 100, "raw m_nVolume" log("OK positions enriched -> %s" % str(r)[:200]) for ep in ["/health", "/openapi.json", "/docs", "/data/kline?code=600519.SH&period=1d&count=1", "/data/quote?code=600519.SH", "/data/instrument?code=600519.SH", "/data/calendar/trading_dates?start=20260818&end=20260820", "/trade/positions", "/trade/asset", "/trade/orders", "/trade/trades"]: try: if ep == "/trade/positions": check_positions() continue r = get(ep) log("OK %s -> %s" % (ep, str(r)[:80])) except Exception as e: log("FAIL %s -> %s" % (ep, str(e)[:80])) bridge_http_server.stop_server() log("=== done ===")