From ac9e9193add9333792267214cf5628f84943755f Mon Sep 17 00:00:00 2001 From: yanweidong Date: Sun, 6 Sep 2026 11:34:23 +0800 Subject: [PATCH] fix bug --- api/qmt_rest_new.py | 20 ++ api/qmt_rest_rele.py | 193 +++++++++++++++--- docs/arch/state.py | 4 +- py-client/config/__init__.py | 6 +- .../__pycache__/__init__.cpython-311.pyc | Bin 8555 -> 8585 bytes .../grid_take_profit.cpython-311.pyc | Bin 5120 -> 4227 bytes .../libs/__pycache__/signal.cpython-311.pyc | Bin 3599 -> 3599 bytes py-client/libs/grid_take_profit.py | 2 +- py-client/libs/signal.py | 4 +- py-client/main.py | 2 +- .../sdk/__pycache__/models.cpython-311.pyc | Bin 17717 -> 17993 bytes .../sdk/__pycache__/portfolio.cpython-311.pyc | Bin 5005 -> 5672 bytes .../sdk/__pycache__/trade.cpython-311.pyc | Bin 3296 -> 2646 bytes py-client/sdk/models.py | 28 +-- py-client/sdk/portfolio.py | 8 + .../ipo/__pycache__/boot.cpython-311.pyc | Bin 3879 -> 4486 bytes .../trend/__pycache__/boot.cpython-311.pyc | Bin 10699 -> 10747 bytes .../trend/__pycache__/open.cpython-311.pyc | Bin 7406 -> 7224 bytes .../trend/__pycache__/order.cpython-311.pyc | Bin 9144 -> 8170 bytes .../__pycache__/positions.cpython-311.pyc | Bin 9660 -> 9015 bytes .../trend/__pycache__/runtime.cpython-311.pyc | Bin 2295 -> 2245 bytes .../trend/__pycache__/watch.cpython-311.pyc | Bin 5030 -> 5030 bytes py-client/strategy/trend/boot.py | 2 +- py-client/strategy/trend/order.py | 2 +- py-client/strategy/trend/positions.py | 2 +- py-client/strategy/trend/runtime.py | 2 +- py-client/strategy/trend/watch.py | 2 +- .../zt/__pycache__/runtime.cpython-311.pyc | Bin 1237 -> 1267 bytes .../zt/__pycache__/state.cpython-311.pyc | Bin 8925 -> 8955 bytes py-client/strategy/zt/runtime.py | 2 +- py-client/strategy/zt/state.py | 2 +- py-client/test.py | 15 +- 32 files changed, 235 insertions(+), 61 deletions(-) diff --git a/api/qmt_rest_new.py b/api/qmt_rest_new.py index ae337fc..c952772 100644 --- a/api/qmt_rest_new.py +++ b/api/qmt_rest_new.py @@ -186,6 +186,24 @@ class HoldingHandler(BaseHandler): holding = format_holding(positions) self.write_json({"data": holding}) +class OrgHandler(BaseHandler): + def get(self, handler_type): + result = safe_call( + get_trade_detail_data, self.acc(), 'stock', handler_type + ) or [] + + data = [] + for item in result: + fields = {} + for name in dir(item): + if name.startswith('_'): + continue + value = getattr(item, name) + if not callable(value): + fields[name] = value + data.append(fields) + self.write_json({"data": data}) + # get_trade_detail_data('account') - Query account assets class AssetsHandler(BaseHandler): def get(self): @@ -318,6 +336,7 @@ def format_holding(positions): 'Direction': position.m_nDirection, 'Volume': position.m_nVolume, 'OpenPrice': position.m_dOpenPrice, + 'OpenCost':position.m_dOpenCost, 'FloatProfit': position.m_dFloatProfit, 'MarketValue': position.m_dMarketValue, 'StockHolder': position.m_strStockHolder, @@ -372,6 +391,7 @@ def make_app(): # Portfolio (r"/api/portfolio", PortfolioHandler), (r"/api/portfolio/positions", HoldingHandler), + (r"/api/portfolio/org/(account|order|deal|position)",OrgHandler), (r"/api/portfolio/assets", AssetsHandler), (r"/api/portfolio/order", OrderHandler), (r"/api/portfolio/deal", DealHandler), diff --git a/api/qmt_rest_rele.py b/api/qmt_rest_rele.py index 26c36f0..c952772 100644 --- a/api/qmt_rest_rele.py +++ b/api/qmt_rest_rele.py @@ -2,17 +2,18 @@ import json import locale import os -import datetime -from pathlib import Path +import sys +from urllib.request import Request, urlopen from tornado.web import Application, RequestHandler, HTTPError from tornado.ioloop import IOLoop import logging # Configuration ACCOUNT_ID = os.environ.get('QMT_ACCOUNT_ID', '') -DATA_DIR = os.environ.get('QMT_DATA_DIR', 'D:\qmt_strategy_data') +DATA_DIR = os.environ.get('QMT_DATA_DIR', r'D:\qmt_strategy_data') TOKEN="QMTbyYanweidong" PORT = 10086 +PASS_CODES_URL = "http://139.224.247.176:13499/a/pass_codes" # =================================== logging.basicConfig(level=logging.INFO) @@ -32,6 +33,36 @@ def safe_call(func, *args, **kwargs): ) from e +def get_pass_codes(account_id): + request = Request( + PASS_CODES_URL, + headers={"Accept": "application/json", "User-Agent": "big-qmt/1"}, + ) + with urlopen(request, timeout=10) as response: + payload = json.load(response) + + remote_codes = payload.get("data") + if not isinstance(remote_codes, list): + raise ValueError("pass_codes response data must be an array") + + positions = safe_call( + get_trade_detail_data, account_id, 'stock', 'position' + ) or [] + position_codes = [ + position.m_strInstrumentID + '.' + position.m_strExchangeID + for position in positions + ] + + codes = [] + seen = set() + for code in remote_codes + position_codes: + code = str(code).strip() + if code and code not in seen: + seen.add(code) + codes.append(code) + return codes + + # ============= BaseHandler ============= AUTH_EXEMPT = set() @@ -61,6 +92,14 @@ class BaseHandler(RequestHandler): def acc(self): return self.application.accountID + def write_json(self, data, default=None): + self.write(json.dumps( + data, + separators=(',', ':'), + ensure_ascii=False, + default=default, + )) + # ============= 1. ContextInfo properties ============= # "/api/v2/context/info" @@ -80,7 +119,7 @@ class ContextInfoHandler(BaseHandler): "timetag":ctx.timetag, "universe": ctx.get_universe(), } - self.write(data, separators=(',', ':'), ensure_ascii=False) + self.write_json(data) # ============= 2. Data queries (ContextInfo get_*) ============= STOCK_HANDLER = { @@ -118,25 +157,26 @@ class StockGetHandler(BaseHandler): # 响应 - self.write(json.dumps({ + self.write_json({ "stock_code": query_vals, "ref": result - }, separators=(',', ':'), ensure_ascii=False, default=str)) + }, default=str) # Aggregate assets, positions, and orders in one request. class PortfolioHandler(BaseHandler): def get(self): - account_data = safe_call(get_trade_detail_data, self.acc(), 'stock', 'account') - positions = safe_call(get_trade_detail_data, self.acc(), 'stock', 'position') or [] - orders = safe_call(get_trade_detail_data, self.acc(), 'stock', 'order') or [] + account_id = self.acc() + account_data = safe_call(get_trade_detail_data, account_id, 'stock', 'account') + positions = safe_call(get_trade_detail_data, account_id, 'stock', 'position') or [] + orders = safe_call(get_trade_detail_data, account_id, 'stock', 'order') or [] result = { "assets": format_assets(account_data), "positions": format_holding(positions), "orders": [fixed_fields(order) for order in orders], } - self.write(json.dumps(result, separators=(',', ':'), ensure_ascii=False)) + self.write_json(result) # get_trade_detail_data('position') - Query positions in the wrapped format @@ -144,13 +184,31 @@ class HoldingHandler(BaseHandler): def get(self): positions = safe_call(get_trade_detail_data, self.acc(), 'stock', 'position') or [] holding = format_holding(positions) - self.write(json.dumps({"data": holding}, separators=(',', ':'), ensure_ascii=False)) + self.write_json({"data": holding}) + +class OrgHandler(BaseHandler): + def get(self, handler_type): + result = safe_call( + get_trade_detail_data, self.acc(), 'stock', handler_type + ) or [] + + data = [] + for item in result: + fields = {} + for name in dir(item): + if name.startswith('_'): + continue + value = getattr(item, name) + if not callable(value): + fields[name] = value + data.append(fields) + self.write_json({"data": data}) # get_trade_detail_data('account') - Query account assets class AssetsHandler(BaseHandler): def get(self): _data = safe_call(get_trade_detail_data, self.acc(), 'stock', 'account') - self.write(json.dumps(format_assets(_data), separators=(',', ':'), ensure_ascii=False)) + self.write_json(format_assets(_data)) class OrderHandler(BaseHandler): def get(self): @@ -158,13 +216,13 @@ class OrderHandler(BaseHandler): if ret is None: ret = [] result = [fixed_fields(obj) for obj in ret] - self.write(json.dumps(result, separators=(',', ':'), ensure_ascii=False)) + self.write_json(result) class DealHandler(BaseHandler): def get(self): deals = safe_call(get_trade_detail_data, self.acc(), 'stock', 'deal') or [] rets = [fixed_fields(deal) for deal in deals] - self.write(json.dumps({"deals": rets}, separators=(',', ':'), ensure_ascii=False)) + self.write_json({"deals": rets}) # ContextInfo.get_full_tick() - Get full tick data class FullTickHandler(BaseHandler): @@ -176,7 +234,7 @@ class FullTickHandler(BaseHandler): ret = safe_call(self.ctx().get_full_tick, stocks) if not ret: raise HTTPError(500, "Failed to get tick data") - self.write(json.dumps(ret, separators=(',', ':'), ensure_ascii=False, default=str)) + self.write_json(ret, default=str) # passorder() - Submit a general trading order class PassorderHandler(BaseHandler): @@ -204,14 +262,14 @@ class PassorderHandler(BaseHandler): logger.exception("passorder failed") raise HTTPError(502, reason="QMT order submission failed") from e - self.write(json.dumps({ + self.write_json({ "status": "success", "opType": opType, "stockCode": stockCode, "strategy_name": strategy_name, "local_order_id": order_id, "order_ref": str(order_ref) - }, separators=(',', ':'), ensure_ascii=False)) + }) class CancelByIdHandler(BaseHandler): @@ -223,21 +281,28 @@ class CancelByIdHandler(BaseHandler): raise HTTPError(400, "order_id cannot be empty") cancelable = safe_call(can_cancel_order, order_id, self.acc(), 'stock') if not cancelable: - self.write(json.dumps({ + self.write_json({ "status": "failed", "order_id": order_id, "message": "Order does not exist or cannot currently be canceled" - }, separators=(',', ':'), ensure_ascii=False)) + }) return result = safe_call(cancel, order_id, self.acc(), 'stock', self.ctx()) - self.write(json.dumps({ + self.write_json({ "status": "success" if result is not False else "failed", "order_id": order_id, - }, separators=(',', ':'), ensure_ascii=False)) + }) + +# get_ipo_data() - Get today's new stock and bond offerings +class IpoDataHandler(BaseHandler): + def post(self): + data = json.loads(self.request.body) + typ = data.get('type', 'STOCK') + ret = safe_call(get_ipo_data, typ) + self.write_json(ret) # sys: Python version information class PythonVersionHandler(BaseHandler): def get(self): - import sys version_info = { "python_version": sys.version, "python_version_info": { @@ -248,7 +313,73 @@ class PythonVersionHandler(BaseHandler): "serial": sys.version_info.serial, } } - self.write(json.dumps(version_info, separators=(',', ':'), ensure_ascii=False)) + self.write_json(version_info) + + +def format_assets(account_data): + info = account_data[0] if account_data else None + if not info: + raise HTTPError(500, "Failed to get account data") + return { + "total": round(info.m_dBalance, 2), + "available": round(info.m_dAvailable, 2), + } + +def format_holding(positions): + holding = {} + for position in positions: + stock = position.m_strInstrumentID + '.' + position.m_strExchangeID + holding[stock] = { + 'StockCode': stock, + 'TradeID':position.m_strTradeID, + 'StockName': position.m_strInstrumentName, + 'Direction': position.m_nDirection, + 'Volume': position.m_nVolume, + 'OpenPrice': position.m_dOpenPrice, + 'OpenCost':position.m_dOpenCost, + 'FloatProfit': position.m_dFloatProfit, + 'MarketValue': position.m_dMarketValue, + 'StockHolder': position.m_strStockHolder, + 'FrozenVolume': position.m_nFrozenVolume, + 'CanUseVolume': position.m_nCanUseVolume, + 'OnRoadVolume': position.m_nOnRoadVolume, + 'YesterdayVolume': position.m_nYesterdayVolume, + 'LastPrice': position.m_dLastPrice, + 'ProfitRate': position.m_dProfitRate, + 'FutureTradeType': position.m_eFutureTradeType, + 'ExpireDate': position.m_strExpireDate + } + return holding + +TRADE_DETAIL_FIELDS = ( + 'm_strOrderSysID', 'm_strInstrumentID', 'm_strExchangeID', + 'm_strInstrumentName', 'm_nOffsetFlag', 'm_nOrderStatus', + 'm_nVolumeTotal', 'm_nVolumeTraded', 'm_nOrderTime', + 'm_strInsertDate', 'm_strInsertTime', 'm_strRemark', + 'm_dPrice', 'm_dTradePrice', 'm_dTradeAmount', +) +MISSING = object() + + +def fixed_fields(obj, fields=TRADE_DETAIL_FIELDS): + result = {} + for field in fields: + try: + value = getattr(obj, field, MISSING) + except TypeError: + continue + if value is MISSING: + continue + if not callable(value): + result[field] = str(value) + if not result: + attrs = getattr(obj, '__dict__', {}) + result = { + key: str(value) for key, value in attrs.items() + if not key.startswith('_') and not callable(value) + } + return result + # ============= Route registration ============= def make_app(): @@ -256,15 +387,17 @@ def make_app(): # ContextInfo properties (r"/api/context/info", ContextInfoHandler), (r"/api/get/(stock_name|open_date|last_volume|total_share|svol|bvol|divid_factors|etf_info|etf_iopv|instrumentdetail|his_st_data)", StockGetHandler), - - # V2 + + # Portfolio (r"/api/portfolio", PortfolioHandler), (r"/api/portfolio/positions", HoldingHandler), + (r"/api/portfolio/org/(account|order|deal|position)",OrgHandler), (r"/api/portfolio/assets", AssetsHandler), (r"/api/portfolio/order", OrderHandler), (r"/api/portfolio/deal", DealHandler), - + (r"/api/data/full_tick", FullTickHandler), + (r"/api/trade/ipo_data", IpoDataHandler), (r"/api/trade/cancel_by_id", CancelByIdHandler), (r"/api/trade/passorder", PassorderHandler), @@ -286,11 +419,9 @@ def init(ContextInfo): try: ContextInfo.accountID = ACCOUNT_ID ContextInfo.set_account(ACCOUNT_ID) - # Load the symbol universe only when configured. - pass_codes_path = Path(DATA_DIR) / "pass_codes.json" - with pass_codes_path.open("r", encoding="utf-8") as stream: - codes = json.load(stream) - ContextInfo.set_universe(list(codes)) + + codes = get_pass_codes(ContextInfo.accountID) + ContextInfo.set_universe(list(codes)) # Api App app = make_app() diff --git a/docs/arch/state.py b/docs/arch/state.py index 267230a..cff2b61 100644 --- a/docs/arch/state.py +++ b/docs/arch/state.py @@ -15,7 +15,7 @@ from sdk import OrderItem, PositionItem PENDING_TIME_OUT = 3600 -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class StateItem: code: str base_order_id: str = "" @@ -27,7 +27,7 @@ class StateItem: added_cost: float = 0.0 -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class PendingOrder: order_id: str code: str diff --git a/py-client/config/__init__.py b/py-client/config/__init__.py index 01d4b98..43e68f4 100644 --- a/py-client/config/__init__.py +++ b/py-client/config/__init__.py @@ -7,7 +7,7 @@ from pathlib import Path import yaml -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class SignalConfig: """单个交易信号的数据源及开仓限制配置。""" @@ -21,7 +21,7 @@ class SignalConfig: gt_last_price_is_open: bool = False -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class GlobalConfig: """所有主机共享的系统配置。""" @@ -37,7 +37,7 @@ class GlobalConfig: signals: dict[str, SignalConfig] = field(default_factory=dict) -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class AccountConfig: """当前主机所使用的账户及交易策略参数。""" diff --git a/py-client/config/__pycache__/__init__.cpython-311.pyc b/py-client/config/__pycache__/__init__.cpython-311.pyc index 309fe19be19a53e57901037c63f4750f878c2867..afff991bbe39b292cc2fafd61853dbaef7943d9f 100644 GIT binary patch delta 58 zcmaFu)alH><`h~0 delta 28 kcmeBle(l7)oR^o2fq{Xc?(dY1-20fAMD#YlWLh8x0Di9s`v3p{ diff --git a/py-client/libs/__pycache__/grid_take_profit.cpython-311.pyc b/py-client/libs/__pycache__/grid_take_profit.cpython-311.pyc index 3e8861356eed150c88c87cb6a4d79b38df443e89..1ab8cd07e471814ce9a71a5ce0f73edb6ed032ea 100644 GIT binary patch delta 465 zcmZqBXjbG~&dbZiz`(%3cyLbETJDK_5{xS*s)ur{;aJAVz_6NeW1BydxP^;VOnH7$ zc1&SuVqQs1Qf9htVQxvx3bqL_FtZwb z01*&%@{?x@X-MgV6smy;a}WV`KiDVolWz;ja#?{w8f3()$xOnHTp|}3#V#_6U11cP k+%K%|Aby2W>;n@kqwog-Ek=V644A}6kk}V25@0s~0O!tOYXATM delta 1292 zcmaJ=Urbw79RAL^w}qCr&`>g|7`qjPGHozZ!$UJAE*iv46Ht?8b*a5~ zIoAFZza`|v+BfDNq3r=q2 zW3C%Q)TK{CBc+U~}3`ReWR{POO#&&#RwPpQ`;wQT?p0krZabON|R=8;qoN62=L0tu3! zadK6d#wV6IiBpZ{>_cEY+yI|gr2gKbFeYCrnx@0!Q*zN9j>ME{S>?1Ik0-_20Q_MM zNJ=CYiC&yMJ>!3dDbg?tE91(<$S?~>#$@JaaxAP&_ygn7NHRRGp7#Hn@B#pp3*j(@ zygO8RKImETu1syI`IpXZCAJa^rxs28yOm%r0B3z-QBdVropek}Ounf41Vao(`(#vA zVoG%4{Fr=6WdXhfSAxg$Buny&i{?s2Gw!3S<$6}XtQ4j@UDrd~PH(~K&6;=Y&RlRG z#PSNM;7+ydygRfmZZ`eUH}HMmz;@qYp>Htn4sE+b1$QWWCO5b*5KFR}Rmr_;@Ep{! z)iWP8K0G`;%2jVyod6b2_O}RkTEu==!<}QKztz>?BkVW;A7y@{n@!pij*h@d;|QuT z#*EW_imTvp4*gX<7|l2SIP-8D2mY!{+MABcJyy}g_x68SVen|(&E4)^PSM5TDGvO! zR#x_Cqs{kOp9a8%tp2XeHK%Qz3sRnVw~4nvyjn*~*R!1k;@zhv(z@T$OZd9~jdu6hVN&dbZiz`(%3cyP`}ZWcZOFd_t5 delta 20 bcmeB|>6hVN&dbZiz`(#@Z#``zHwzyCE|LTb diff --git a/py-client/libs/grid_take_profit.py b/py-client/libs/grid_take_profit.py index e2e74ca..cc75a02 100644 --- a/py-client/libs/grid_take_profit.py +++ b/py-client/libs/grid_take_profit.py @@ -20,7 +20,7 @@ class GridState(str, Enum): STEADY = "steady" # 仍处于当前峰值网格,继续持有 -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class GridObservation: """一次网格观察的不可变结果。""" diff --git a/py-client/libs/signal.py b/py-client/libs/signal.py index d0a892c..2ce60a9 100644 --- a/py-client/libs/signal.py +++ b/py-client/libs/signal.py @@ -4,12 +4,12 @@ import secrets from .http import get_json -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class SignalItem: signal_key: str = ""; code: str = ""; name: str = ""; desc: str = ""; last_close: float = 0 tech_indicator: dict[str, float] = field(default_factory=dict) -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class SignalResult: code: str = ""; total: int = 0; updated: str = ""; data: dict[str, SignalItem] = field(default_factory=dict); message: str = "" diff --git a/py-client/main.py b/py-client/main.py index 787dc9f..0ca4dab 100644 --- a/py-client/main.py +++ b/py-client/main.py @@ -38,7 +38,7 @@ from strategy.trend.boot import StartTrend from strategy.zt.boot import StartZT from strategy.ipo import AutoBuyIpo -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class StrategyDefinition: mutex_scope: str start_strategy: object diff --git a/py-client/sdk/__pycache__/models.cpython-311.pyc b/py-client/sdk/__pycache__/models.cpython-311.pyc index ae084b37f1a3d334143b818cf2ff8e99d0c2c392..556142deeee4d644072f5dc1be661681f6e018ba 100644 GIT binary patch delta 2455 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 diff --git a/py-client/sdk/__pycache__/portfolio.cpython-311.pyc b/py-client/sdk/__pycache__/portfolio.cpython-311.pyc index 664573f2a6004948f25bf4c675f9ee1394024278..665186bcc6ec70c982b8190c7ddcec0d42361d30 100644 GIT binary patch delta 1669 zcmZuxUuauZ82?W0y~$15G-=YTTbHC+YqfW)?WWtTbqjQ91(i+5KqO^^m~$8IOmoAz zH*PDHb|XS@PS7K!uxZ_#+p12{A@o6zfzOgjAI1eiC@J&F$$Y8^@|`TWY$oBy$M@&= z{XIGN#I>Q0tA4)^0N|2y;dtEpe#b9!&jUaebVw5lX%`Jqr6Q*#8m69#oR(=9^;YDx zLL;=TBB$MS6ZKc*w1;-nj*6UC>1G-7d4@MpwbG>?vyG?5TdsWiUR#?Dzn z$(w|QY){h#fVyVMdHC3Mg#dg>n))+j7Cx1EmPB~9BQp1vYt}{OEln#N0Zl0%0)X|U z&+mM5DUr?P%yQ9Qn?G$bsvrb2WfnEbkuAM&jJ0Eg>S4N7GK&@;mYy4MB$MT5LakdGHd)>a0M;}B%tM`D4fY+p z-4?EGP1V})_iHFxi%qNy)?yR2*u<^SZiQ2; z;nae9JJ7x8UJdlt)ZTkI_Z$1j-$Y?i0N{o+M#B8Elyb2+|6R(HZTzsD*gAm4O3XZA z;T0qj!g<5DuV7f*ly>;=VshqmDcQqs%0uZmy1T`gElpzE0f1=;y8u9QGhDYL<#pRp z3|pVJn2JU>LO%d3ORNXw2m;1glE|81QU0e zx1L?@UerI!m!+*-Qv2Lk9Ee!PQs}aX%*=FvIOwOdb=3=5(S=9AFvkflN&wo<; zHKfoa(FS`8>7GW|{YZX|2fZ;8l!9qc1iy&!YT=z&P2>&jxyXL=`d% zBWD*v2%!^m)dG({gL+LBuZL|yOE-eJ7d#b~LJ$+)jGjuPk9Iy9)JT-S9vme*`T5|L zIZSg%lf|QIuytT$0E1i&8BeS!?|neVt840$56B|+5k~wQ(R_}Y$rQ8GdM4xeGMQs06b>Fl}%LP5j4)cFPQ+sxk{deI}_!D)1{f|ME^)IcMn2P`a delta 1119 zcmZuwOKTHR6h1dInaQI`+Ps>kshz$%8jJPSTCLK8bWs(dAebs8O_DmD2#Q1_qQEhf;udaH%erB%b8Kx?GZZimP?G6y#|huFEBbXLz(OmsFnR ziMm`0@%9DKJH7ZtTe!=%W!mS>J7(2WgyGgib)!rE#c)A7j9to<5(NO~BtD9CqpJ)A zz5_l{uCW9%RqIXxfF}ui)d>KArim;7Y(-94rh~o80kq;_mc(0Xaet0F6FS)pXw+JqZWF_ zavL8)ChJKMTRVj$Dcorz%r zF>>YKoya4L40&qJ@TY@iEqqx8ubjUlVK627FpEvb(>c(6%br@URP0*S^+GGQW4fkY z^@6@aPv%D1!nx$|>@?kXNSvTCLB|#5iWju(RYNr25j`O>L4lUxMJ|=CnsHVLTj;^& zO-$m`q|UPVHaW>gQB6I+Ow%vu0ntXV5b^)X^_E;|$;VsrW0cEclCUJI$s$uVjD_^j z{3sQD^ZrBM5jaN&{zgeabLI4d`GI6O4FI^m{cQ&DS^6Q%;gQUI{$Jcy=+sS{*DT|- YxPXn!;;|x|-h{p38`XaN=fIcn8#Hb35&!@I diff --git a/py-client/sdk/__pycache__/trade.cpython-311.pyc b/py-client/sdk/__pycache__/trade.cpython-311.pyc index f261d4b4337bdaceca3b37f1aa37fabd5ab82dae..a24d20702a870631c9c7684e43999c768ff49723 100644 GIT binary patch delta 272 zcmaDLc};|GIWI340|Ns?PxtIBQ}&5`_Ze*_zEhHyN)c{hjbclYPLW9wZ()sMPmxWL zXkm%sNRbR?(3IP3&sfbmc^1cJMvKXQoI57xafvZEa{DCSK~6dPpG zl`%2R+Zb1FtTDJCx?#(IAc-*(Hu@KsA+pkqxuv8+;4MyG&b{yE{NDYZ`Z<#L5RXRy zAhYv!`LEti{9t$5zw!l>54O~@#)fd+G_QA506~ELo{~G7`J&01fo8B11U7?5EhX4&{v#6w0Jb6~0ElUl zA(AuzmnK@4Wr;}ud&c9c>#hp!h{{T};kb>p(FIZE&fWUc`lHE{#Uc$j2Veuk=*ZnrD%W?FL&RZf>$_1o zq1U3bWL_h=F57yRb>x=5Xd6tu Any: return kind() -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class OrderItem: """由 QMT 委托明细解析得到的标准订单记录。""" id: str @@ -64,13 +64,14 @@ class OrderItem: ) -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class PositionItem: stock_code: str = "" stock_name: str = "" trade_id:str = "" direction: Any = None volume: int = 0 + open_cost: float = 0.0 open_price: float = 0.0 float_profit: float = 0.0 market_value: float = 0.0 @@ -88,7 +89,7 @@ class PositionItem: def from_dict(cls, data: dict[str, Any], code: str = "") -> "PositionItem": return cls( stock_code=str(data.get("StockCode") or code), stock_name=str(data.get("StockName") or ""), - trade_id=str(data.get("TradeID") or ""), + trade_id=str(data.get("TradeID") or ""),open_cost=_number(data.get("OpenCost")), direction=data.get("Direction"), volume=_number(data.get("Volume"), int), open_price=_number(data.get("OpenPrice")), float_profit=_number(data.get("FloatProfit")), market_value=_number(data.get("MarketValue")), stock_holder=str(data.get("StockHolder") or ""), @@ -107,6 +108,7 @@ class PositionItem: trade_id=str(data.get("TradeID") or ""), direction=data.get("Direction"), volume=_number(data.get("Volume"), int), + open_cost=_number(data.get("OpenCost")), open_price=_number(data.get("OpenPrice")), float_profit=_number(data.get("FloatProfit")), market_value=_number(data.get("MarketValue")), @@ -122,7 +124,7 @@ class PositionItem: ) -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class Assets: total: float = 0.0 available: float = 0.0 @@ -136,7 +138,7 @@ class Assets: ) -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class Portfolio: assets: Assets positions: dict[str, PositionItem] @@ -152,7 +154,7 @@ def _trade_datetime(data: dict[str, Any]) -> datetime | None: return None -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class Tick: last_price: float = 0.0 last_close: float = 0.0 @@ -169,7 +171,7 @@ class Tick: ) -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class HistoryDataRequest: length: int = 10 period: str = "" @@ -178,7 +180,7 @@ class HistoryDataRequest: skip_paused: bool = True -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class MarketDataRequest: fields: list[str] = field(default_factory=list) stocks: list[str] = field(default_factory=list) @@ -189,7 +191,7 @@ class MarketDataRequest: count: int = 0 -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class FinancialDataRequest: tabname: str = ""; colname: str = ""; market: str = ""; code: str = "" report_type: str = ""; barpos: int = 0 @@ -197,22 +199,22 @@ class FinancialDataRequest: start_date: str = ""; end_date: str = "" -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class FactorDataRequest: field_list: list[str] = field(default_factory=list); stock_list: list[str] = field(default_factory=list) stock_code: str = ""; start_date: str = ""; end_date: str = "" -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class BSMPriceRequest: option_type: str; object_prices: Any; strike_price: float; risk_free: float; sigma: float; days: int; dividend: float -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class BSMIVRequest: option_type: str; object_prices: float; strike_price: float; option_price: float; risk_free: float; days: int; dividend: float -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class LocalDataRequest: stock_code: str; start_time: str = ""; end_time: str = ""; period: str = ""; divid_type: str = ""; count: int = 0 diff --git a/py-client/sdk/portfolio.py b/py-client/sdk/portfolio.py index e5b90e5..e4e3df2 100644 --- a/py-client/sdk/portfolio.py +++ b/py-client/sdk/portfolio.py @@ -6,6 +6,14 @@ from .models import Assets, OrderItem, Portfolio, PositionItem class PortfolioMixin: + def org(self, datatype: str) -> list[dict[str, Any]]: + """查询 account、order、deal 或 position,返回原始字段字典列表。""" + datatype = str(datatype).strip().lower() + if datatype not in {"account", "order", "deal", "position"}: + raise ValueError(f"unsupported org datatype: {datatype}") + response = self._get_json(f"/api/portfolio/org/{datatype}") + return response["data"] + def portfolio(self) -> Portfolio: data = self._get_json("/api/portfolio") or {} positions = { diff --git a/py-client/strategy/ipo/__pycache__/boot.cpython-311.pyc b/py-client/strategy/ipo/__pycache__/boot.cpython-311.pyc index 6c6e81c817bf6fabf18932b99bb6c7e6d41f56e9..129bc605aa3fe408f60b8417e7ed855d40400cea 100644 GIT binary patch delta 1645 zcmZ`&ZERCj7=F+FZf|e9c3*Ux(1jnhvW<3wWw?r#2rD6uLG=eE+KiqK%5K+s?rp|Q zwc{f?kpz~L=v0U!Awzp7`O$dSgT$3g)HzAt920#3<1mh2W7<29%IHKp=`{p^% z`@ZM%c`vSgf4%#nra1use(64Y_?|lHUW0z039MgxugQ6n-kdM#L%=~s!cMHtZ~&J% zwmNGz>sn^_KBb2^+o56fAK~t2*&%L-OV(n~2}suAdR%uxNY-OM0ilM;za|7eHZn$! zqkj4;YC!K@{Syr#^csDW|1M&N{@Dcpl=vA0a2c&I20E29H>`{RN*q@Fw7@meVZlrH z3Qa+=Bw%jXPB5gBxPpnt-1syNJL!+YZlqF842p{-m~N&U`FhP&;!7gdN&eNZ3=*KcrnPmNRP@#moT7rZbj1H+W>ANYaM}jZA+rM>BFygBt)W zK3m9#Buma0Qs!78Lu%<&nWBEWLwT#l0RTj>qek+hM4{g*q{fpqQsy8zm@!j^nNJVX zjgB@iQ=jE13>rqB;0z%u)g7IvmVV)QC$$v-Y5~BgascD7pn9unpsI$KfD^rU70)Tx zTzzLn>8dJSb4u5|r)J{qyK3V(<*e&d_gQyE4OZ3Qzhm1fN_$mlpHtfHSbD@cquy

h-4T+*X1)Lk!p3xM^)X%Fp(qc$Bv!V=Q+d zuah=*73~tqm_;&T2?z3dJ^e(50KhN*=+P7ad;%qy1}33awEB55=YOKQcTovVBNpX< zM%9GFLIPI2bc*+smnkeIAS4&_0N@M+0)d5z3yU9ry*PDc;mpkaYuE2zJNfX+(R<@Z z7r*~z89cZ+xiInh;<=w6e0C!C@Ywl#Gud>7-o z!jT;z&JvA+o;59}VGa`07|oi8LL7OSU8#`Hc~9+xIWMcro>)cR zR+YCs_57yROl*F{9wH>z+pwRPyuwzFF}A^yQz@KJr&5+Pl^QCV^ql5YEa9+`A0e&u zlGa?dBy+5g9XUv3mbKO{Cj$=?#jY7Wd%);O59(>cs?E~uv7S#44`uZXVRc1R=5XyC z*qusjPb7Bk?oSX_Q})p>0U2PJUC3i4n#<#&p4m$F0{|19(S&Hk-KwbLcAuwv+?~F8 XXqa35(c|vP^*VAR?-Klvv6uN9t^A+Y delta 1160 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 diff --git a/py-client/strategy/trend/__pycache__/open.cpython-311.pyc b/py-client/strategy/trend/__pycache__/open.cpython-311.pyc index 9159fce2fb1139aadc9c23ae251a316641e4563a..2cd8ab37006b20c7f99f732e23da0cf0a7440e08 100644 GIT binary patch delta 2481 zcmbtVeQZ-z6hH5~-)}2h*N?%zU{JD8m>>kE3FtN$lK`49qD|?0t9>o)a{KClywOR} zs3|gh8ZnYM%?6k$5k?}C>|aqZq{AjGFIglJ82n>K^e_G}=DzL~nr$)0m*(``bI<*q zdw%EKd;i#U^?CbwyWIi+Ft+@x^zCW8hfq$h-qTD7=9sP5E>sDQ6Lhabs1{r&=w4o^ z5o!hB38uG1s1sJ4pnFRN^Iq_;Jbm@Jx|e8vV>1izDJbN27^Yr#4N?jvVQl?VKq3rX zA`R^=Ks7RFi~uCs&;_DsH6=0X-=xR3Q2~ja_fc>0HMBm{Lb^&dYjjz`=@;zt z*7IhNR~xp}&;;8;G_F$pjC zB7gQA1u#w-UP*G8%=G_n-sZn&XmjMF>h}cX`?G=bdWXr(r3K72V&?QCxF}OQb%ut? z%N2WHa)n<=6&Y9i!zrXzGos+YZ7w=uY z_Ta;h9(^|Y@c5UrV;3e)aV9eD(dbY@5H)Ks6iTGxav;)LA~~-jKE8-;9mVbY{zYvb*=?cJI#~2;>e(Q?_W%7R}nC z)3&mSSFBA``Urth@>t0&0Py45_6|RJ-EZl#GS}DFc6yngyiHwJ=4T6mWn24>Hu6TB zrQ6NiXs_K_&)lqU>UJ}~R1n>62Jrx(QAru0MqP`kOG3j7T>K&UbY*p3n0+yB)?Am zqU5zS!rW=}!C+GK6B-?j#Dx^TY#v_x>i)@39-JD!cWw0k=%t5O#(Ml_l26w$c(eVo70&7STH1NY$z5H<1(tn6RYq9+m}FLQTB5PpAnr4od#3}G^RkkSz4EF z##1$h*o`atF6b5IotdL|`1;#?{YYwxZ_M$HL!C2@su`1G+GLx?vSixioVG8UvvWNA z7&xB&3yYzSITzUY%;q~*-)*b!%(k;TMh2#=%{goH91u+FjKiDtHBUKOa*mcPWoo&r zPC9D{@5FCTCsqFlKTAj#_yO#B*>6UzdX`BUMfwrGoKd@4;c8*Md3Beolh>2*;4v1$ z#Hg!@Ahu5YS}{gAw*!EmL7jMZCl>3}4?N3>ed+~IWnC9G?7&Im5?136>0rC?1Qy5D zSx+vl6Np;_S+T(HtKXKVRkz|k)AyMtcpV<OhhsvW+bZ5kI#gu{_|7~!@-_&DavsMCOV{xzVj*oyOcV%l;j xA*5nrJL&@f{3Iu5)mfjisXPnwzd7~=0-wAEaywZ{RL?=NSfgI4+(6?1{{TN-^6~%x delta 2440 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 tMlj4SRCR462xk^1<*kPK#1Ap{ zQg~Y!QuvlJF)*xVf(kP%V_;xd4dXCG38(M}GiVA-d~tg69!5>Zl*!K+-ITdfSyFgx z7#J8*cvD$Y_`odwRF)J0FiS9)K~rdRBvUFg9}fcq!!5S_qLkF4;v(M3H(1*hEM2T( z%JYk|V+uj zn**ObOvX=Bq)2A6GJA*FE!OhHlH`mcUYJyo9Eb&0sldR%@QcGHH$SB`C)KV<6BIZg sBR?}te$75}@-z+=TWLnc4-A;ZN08VT5b=?jfl2%dqu2)~6jfjg0Jzju3;+NC delta 419 zcmX>q_+5~9IWI340|NsCqwdr!y@|Y%jC&@k7wV*Nq;Tg5=ZZv$FfuSPq_U;(tl?e8 z$iT3g5u_Fbm>Ap{QutaJQurtCkYyK55eR0`6r6bHG#hJhNn%Or#V zq_U*&+AuIMq<~m_U>1KWONs!PC78;RA_Qg$2Qz4jY_?)bWtQS)U|_h#S`0F%hzG=G z%P&evEh;YJo4l5_T}N6krnsaiu_QIUGNz;`H7_Nms5Gx6GdESQpt4Aifq?-`fEdMX zlk3-p}U1rwCKxrzut>H<_2c!=XqNBnGj!Jh3D>qlgcts7L|C z0_#y?U|{&gVUwGmQks)$SELOJH;^r?jFV5Z&lHfjz<`1#Cvm9R$}lQ^V8A3kg2cXn Uh>y$+OyXA<#Xc~hr~=yv05+## None: for position in positions: if position.volume <= 0: continue - log.info("[启动] %s %s,持仓=%d,可用=%d,成本=%.2f,现价=%.2f,盈亏=%.2f%%", position.stock_code, position.stock_name, position.volume, position.can_use_volume, position.open_price, position.last_price, position.profit_rate * 100) + log.info("[启动] %s %s %s,持仓=%d,可用=%d,成本=%.2f(%.2f),现价=%.2f,盈亏=%.2f%%",position.trade_id, position.stock_code, position.stock_name, position.volume, position.can_use_volume, position.open_price,position.open_cost, position.last_price, position.profit_rate * 100) diff --git a/py-client/strategy/trend/order.py b/py-client/strategy/trend/order.py index de8bef6..ef0065d 100644 --- a/py-client/strategy/trend/order.py +++ b/py-client/strategy/trend/order.py @@ -19,7 +19,7 @@ TRACKED_STATUSES = BUSY_STATUSES | COMPLETED_STATUSES CANCELABLE_STATUSES = {"49", "50", "51", "52"} -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class PlaceOrderRequest: """``OrderBook.place`` 提交委托所需的全部参数。""" op: int diff --git a/py-client/strategy/trend/positions.py b/py-client/strategy/trend/positions.py index 9904f20..d5e4cdd 100644 --- a/py-client/strategy/trend/positions.py +++ b/py-client/strategy/trend/positions.py @@ -15,7 +15,7 @@ import logging as log LOSS_TIERS = [-50.0] -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class TradeDecision: """一次止盈或补仓判断的统一结果。""" diff --git a/py-client/strategy/trend/runtime.py b/py-client/strategy/trend/runtime.py index 0bd2189..f650c15 100644 --- a/py-client/strategy/trend/runtime.py +++ b/py-client/strategy/trend/runtime.py @@ -13,7 +13,7 @@ from .order import OrderBook from .watch import DipWatch -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class Runtime: """集中保存趋势策略运行期间共享的依赖和状态。 diff --git a/py-client/strategy/trend/watch.py b/py-client/strategy/trend/watch.py index d883e3e..b5f3626 100644 --- a/py-client/strategy/trend/watch.py +++ b/py-client/strategy/trend/watch.py @@ -4,7 +4,7 @@ from datetime import datetime, timedelta from threading import Lock -@dataclass(frozen=True, slots=True) +@dataclass(slots=True) class _Entry: last_close: float expires_at: datetime diff --git a/py-client/strategy/zt/__pycache__/runtime.cpython-311.pyc b/py-client/strategy/zt/__pycache__/runtime.cpython-311.pyc index bcde782ce677a61dd8998d34149f2f562eb92dfd..53255cb9adc479dc497b4448076ffa4e236ab1df 100644 GIT binary patch delta 58 zcmcc0`I(b@IWI340|NsC None: + """只读查询四类原始数据,输出 JSON 便于核对字段。""" + with Client(BASE_URL, TOKEN) as client: + for datatype in ("account", "order", "deal", "position"): + print(f"\n=== {datatype} ===") + try: + result = client.org(datatype) + print(f"记录数:{len(result)}") + print(json.dumps(result, ensure_ascii=False, indent=2, default=str)) + except Exception as exc: + print(f"查询失败:{exc}") + + +def main1() -> None: order = { "opType": 23, "orderType": 1101,