dev 6
This commit is contained in:
@@ -23,9 +23,14 @@ locale.setlocale(locale.LC_CTYPE, 'chinese')
|
|||||||
def safe_call(func, *args, **kwargs):
|
def safe_call(func, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
except HTTPError:
|
||||||
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{func.__name__} call failed: {e}")
|
logger.exception("%s call failed", func.__name__)
|
||||||
return None
|
raise HTTPError(
|
||||||
|
502,
|
||||||
|
reason="QMT upstream call failed: %s" % func.__name__,
|
||||||
|
) from e
|
||||||
|
|
||||||
|
|
||||||
# ============= BaseHandler =============
|
# ============= BaseHandler =============
|
||||||
@@ -662,23 +667,31 @@ class PassorderHandler(BaseHandler):
|
|||||||
quickTrade = int(data.get('quickTrade', 2))
|
quickTrade = int(data.get('quickTrade', 2))
|
||||||
strategy_name = str(data.get('strategyName', '')).strip()
|
strategy_name = str(data.get('strategyName', '')).strip()
|
||||||
order_id = str(data.get('orderId', '')).strip()
|
order_id = str(data.get('orderId', '')).strip()
|
||||||
# QMT stores strategyName in the order remark; preserve the signal key and local order ID.
|
except (json.JSONDecodeError, KeyError, TypeError, ValueError) as e:
|
||||||
# Put the local ID first so restart reconciliation still works if QMT truncates the remark.
|
raise HTTPError(400, reason="Invalid order parameters: %s" % e) from e
|
||||||
remark = '|'.join(part for part in (order_id, strategy_name) if part)[:24]
|
|
||||||
|
# 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())
|
order_ref = passorder(opType, orderType, self.acc(), stock, pr_type, price, volume, remark, quickTrade, self.ctx())
|
||||||
if not order_ref:
|
except HTTPError:
|
||||||
raise HTTPError(502, "QMT did not return a valid order reference")
|
raise
|
||||||
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 Exception as e:
|
except Exception as e:
|
||||||
logger.exception("passorder failed")
|
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
|
# algo_passorder() - Submit an algorithmic order
|
||||||
class AlgoPassorderHandler(BaseHandler):
|
class AlgoPassorderHandler(BaseHandler):
|
||||||
@@ -1428,7 +1441,7 @@ def make_app():
|
|||||||
(r"/api/trade/debt_contract", DebtContractHandler),
|
(r"/api/trade/debt_contract", DebtContractHandler),
|
||||||
(r"/api/trade/assure_contract", AssureContractHandler),
|
(r"/api/trade/assure_contract", AssureContractHandler),
|
||||||
(r"/api/trade/enable_short_contract", EnableShortContractHandler),
|
(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),
|
(r"/api/trade/new_purchase_limit", NewPurchaseLimitHandler),
|
||||||
|
|
||||||
# Reference functions
|
# Reference functions
|
||||||
|
|||||||
Binary file not shown.
@@ -18,7 +18,7 @@ if PROJECT_ROOT not in sys.path:
|
|||||||
|
|
||||||
from sdk import APIError, Client
|
from sdk import APIError, Client
|
||||||
from strategy.trend.boot import StartTrend
|
from strategy.trend.boot import StartTrend
|
||||||
|
from strategy.ipo import AutoBuyIpo
|
||||||
|
|
||||||
@dataclass(frozen=True, slots=True)
|
@dataclass(frozen=True, slots=True)
|
||||||
class StrategyDefinition:
|
class StrategyDefinition:
|
||||||
@@ -124,6 +124,9 @@ def main() -> int:
|
|||||||
configure_logging(config.global_config.qmt_data_dir)
|
configure_logging(config.global_config.qmt_data_dir)
|
||||||
wait_for_qmt_api()
|
wait_for_qmt_api()
|
||||||
|
|
||||||
|
# 自动打新
|
||||||
|
AutoBuyIpo()
|
||||||
|
|
||||||
STRATEGIES[config.account_config.strategy].start_strategy()
|
STRATEGIES[config.account_config.strategy].start_strategy()
|
||||||
return 0
|
return 0
|
||||||
except (OSError, yaml.YAMLError, ValueError, RuntimeError, KeyError) as exc:
|
except (OSError, yaml.YAMLError, ValueError, RuntimeError, KeyError) as exc:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ ORDER_TYPE_VOLUME, PR_TYPE_LATEST, QUICK_TRADE_NOW = 1101, 5, 2
|
|||||||
class TradeMixin:
|
class TradeMixin:
|
||||||
account_type: str
|
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}
|
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)):
|
for key, value in (("orderType", order_type), ("prType", pr_type), ("quickTrade", quick_trade), ("strategyName", strategy_name)):
|
||||||
if value: body[key] = value
|
if value: body[key] = value
|
||||||
|
|||||||
22
py-client/strategy/ipo/boot.py
Normal file
22
py-client/strategy/ipo/boot.py
Normal file
@@ -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="新股申购",
|
||||||
|
)
|
||||||
Binary file not shown.
Binary file not shown.
@@ -84,7 +84,10 @@ def StartTrend() -> None:
|
|||||||
storeState.reconcile(positions, orders, deals)
|
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(
|
run = Runtime(
|
||||||
client=client,
|
client=client,
|
||||||
global_cfg=config.global_config,
|
global_cfg=config.global_config,
|
||||||
@@ -182,11 +185,3 @@ def RunOnce(run: Runtime, signals) -> None:
|
|||||||
|
|
||||||
# 8. 持仓计算。当前 Go 版本的 managePositions 为空,保留扩展入口。
|
# 8. 持仓计算。当前 Go 版本的 managePositions 为空,保留扩展入口。
|
||||||
manage_positions(run, ticks, positions, market_ok,assets.available)
|
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]
|
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user