diff --git a/api/QMT_API.py b/api/QMT_API.py index 79e9d09..17a9cc9 100644 --- a/api/QMT_API.py +++ b/api/QMT_API.py @@ -23,9 +23,14 @@ locale.setlocale(locale.LC_CTYPE, 'chinese') def safe_call(func, *args, **kwargs): try: return func(*args, **kwargs) + except HTTPError: + raise except Exception as e: - logger.error(f"{func.__name__} call failed: {e}") - return None + logger.exception("%s call failed", func.__name__) + raise HTTPError( + 502, + reason="QMT upstream call failed: %s" % func.__name__, + ) from e # ============= BaseHandler ============= @@ -662,23 +667,31 @@ class PassorderHandler(BaseHandler): quickTrade = int(data.get('quickTrade', 2)) strategy_name = str(data.get('strategyName', '')).strip() order_id = str(data.get('orderId', '')).strip() - # QMT stores strategyName in the order remark; preserve the signal key and local order ID. - # Put the local ID first so restart reconciliation still works if QMT truncates the remark. - remark = '|'.join(part for part in (order_id, strategy_name) if part)[:24] + except (json.JSONDecodeError, KeyError, TypeError, ValueError) as e: + raise HTTPError(400, reason="Invalid order parameters: %s" % e) from e + + # QMT stores strategyName in the order remark; preserve the signal key and local order ID. + # Put the local ID first so restart reconciliation still works if QMT truncates the remark. + remark = '|'.join(part for part in (order_id, strategy_name) if part)[:24] + try: order_ref = passorder(opType, orderType, self.acc(), stock, pr_type, price, volume, remark, quickTrade, self.ctx()) - if not order_ref: - raise HTTPError(502, "QMT did not return a valid order reference") - self.write(json.dumps({ - "status": "success", - "opType": opType, - "stock": stock, - "strategy_name": strategy_name, - "local_order_id": order_id, - "order_ref": str(order_ref) - }, ensure_ascii=False)) + except HTTPError: + raise except Exception as e: logger.exception("passorder failed") - raise HTTPError(400, f"Order submission failed: {str(e)}") + raise HTTPError(502, reason="QMT order submission failed") from e + + if not order_ref: + raise HTTPError(502, reason="QMT did not return a valid order reference") + + self.write(json.dumps({ + "status": "success", + "opType": opType, + "stock": stock, + "strategy_name": strategy_name, + "local_order_id": order_id, + "order_ref": str(order_ref) + }, ensure_ascii=False)) # algo_passorder() - Submit an algorithmic order class AlgoPassorderHandler(BaseHandler): @@ -1428,7 +1441,7 @@ def make_app(): (r"/api/trade/debt_contract", DebtContractHandler), (r"/api/trade/assure_contract", AssureContractHandler), (r"/api/trade/enable_short_contract", EnableShortContractHandler), - (r"/api/trade/ipo_data", IpoDataHandler), + (r"/api/trade/ipo_data", IpoDataHandler), (r"/api/trade/new_purchase_limit", NewPurchaseLimitHandler), # Reference functions diff --git a/api/__pycache__/QMT_API.cpython-311.pyc b/api/__pycache__/QMT_API.cpython-311.pyc index 44f7722..f0f9421 100644 Binary files a/api/__pycache__/QMT_API.cpython-311.pyc and b/api/__pycache__/QMT_API.cpython-311.pyc differ diff --git a/AUDIT_AND_REMEDIATION.md b/docs/AUDIT_AND_REMEDIATION.md similarity index 100% rename from AUDIT_AND_REMEDIATION.md rename to docs/AUDIT_AND_REMEDIATION.md diff --git a/todo.md b/docs/todo.md similarity index 100% rename from todo.md rename to docs/todo.md diff --git a/py-client/main.py b/py-client/main.py index ddf0dbb..3070605 100644 --- a/py-client/main.py +++ b/py-client/main.py @@ -18,7 +18,7 @@ if PROJECT_ROOT not in sys.path: from sdk import APIError, Client from strategy.trend.boot import StartTrend - +from strategy.ipo import AutoBuyIpo @dataclass(frozen=True, slots=True) class StrategyDefinition: @@ -124,6 +124,9 @@ def main() -> int: configure_logging(config.global_config.qmt_data_dir) wait_for_qmt_api() + # 自动打新 + AutoBuyIpo() + STRATEGIES[config.account_config.strategy].start_strategy() return 0 except (OSError, yaml.YAMLError, ValueError, RuntimeError, KeyError) as exc: diff --git a/py-client/sdk/trade.py b/py-client/sdk/trade.py index e3fb494..335802c 100644 --- a/py-client/sdk/trade.py +++ b/py-client/sdk/trade.py @@ -7,7 +7,7 @@ ORDER_TYPE_VOLUME, PR_TYPE_LATEST, QUICK_TRADE_NOW = 1101, 5, 2 class TradeMixin: account_type: str - def passorder(self, op_type, stock, volume, order_type=0, pr_type=0, price=0, quick_trade=0, strategy_name=""): + def passorder(self, op_type, stock, volume, order_type=0, pr_type=0, price=0.0, quick_trade=0, strategy_name=""): body = {"opType": op_type, "stock": stock, "price": price, "volume": volume} for key, value in (("orderType", order_type), ("prType", pr_type), ("quickTrade", quick_trade), ("strategyName", strategy_name)): if value: body[key] = value diff --git a/py-client/strategy/ipo/boot.py b/py-client/strategy/ipo/boot.py new file mode 100644 index 0000000..04e7f8f --- /dev/null +++ b/py-client/strategy/ipo/boot.py @@ -0,0 +1,22 @@ +from sdk import Client +import config + +def AutoBuyIpo() -> None: + client = Client( + config.global_config.qmt_base_url, + config.global_config.qmt_token, + config.HTTP_TIMEOUT, + ) + + result = client.ipo_data("STOCK") + for stock in result: + ipo_price = result[stock]['issuePrice'] # 发行价 + maxPurchaseNum = result[stock]['maxPurchaseNum'] # 可申购额度 + client.passorder( + op_type=23, + stock=stock, + volume=maxPurchaseNum, + pr_type=11, + price=ipo_price, + strategy_name="新股申购", + ) \ No newline at end of file diff --git a/py-client/strategy/trend/__pycache__/order.cpython-311.pyc b/py-client/strategy/trend/__pycache__/order.cpython-311.pyc index ebcc8ab..e194695 100644 Binary files a/py-client/strategy/trend/__pycache__/order.cpython-311.pyc and b/py-client/strategy/trend/__pycache__/order.cpython-311.pyc differ diff --git a/py-client/strategy/trend/__pycache__/runtime.cpython-311.pyc b/py-client/strategy/trend/__pycache__/runtime.cpython-311.pyc index 59df6ca..e81d92c 100644 Binary files a/py-client/strategy/trend/__pycache__/runtime.cpython-311.pyc and b/py-client/strategy/trend/__pycache__/runtime.cpython-311.pyc differ diff --git a/py-client/strategy/trend/boot.py b/py-client/strategy/trend/boot.py index 33e2350..feed013 100644 --- a/py-client/strategy/trend/boot.py +++ b/py-client/strategy/trend/boot.py @@ -84,7 +84,10 @@ def StartTrend() -> None: storeState.reconcile(positions, orders, deals) # 获取本策略的信号开仓数据 - signals = init_signals(config.global_config,["morning","tail","arbitrage"]) + signals = init_signals( + config.global_config, + config.account_config.signal_allow, + ) run = Runtime( client=client, global_cfg=config.global_config, @@ -182,11 +185,3 @@ def RunOnce(run: Runtime, signals) -> None: # 8. 持仓计算。当前 Go 版本的 managePositions 为空,保留扩展入口。 manage_positions(run, ticks, positions, market_ok,assets.available) - - -def SignalFilter(signals, allowed_names): - """只保留账户配置明确允许使用的信号。""" - if not allowed_names: - return [] - allowed = set(allowed_names) - return [signal for signal in signals if signal.signal_key in allowed] diff --git a/py-client/tests/__pycache__/test_trend.cpython-311.pyc b/py-client/tests/__pycache__/test_trend.cpython-311.pyc index bb3a3d8..b7abf4a 100644 Binary files a/py-client/tests/__pycache__/test_trend.cpython-311.pyc and b/py-client/tests/__pycache__/test_trend.cpython-311.pyc differ