This commit is contained in:
2026-09-15 20:02:05 +08:00
parent bfe89ba122
commit 04daeff141
37 changed files with 2674 additions and 1426 deletions

View File

@@ -0,0 +1,90 @@
"""ZT 正T/反T 的触发与数量规则:纯函数,不碰网络、存储和线程。
设计要点:
* 以建仓价 ``base_cost`` 为中枢,中性带 ``zt_t_band_pct`` 内不做任何动作。
现价低于下沿只考虑正T高于上沿只考虑反T —— 同一时刻只可能命中一种方向,
天然满足"一只股票每天只做一轮",不需要额外的冲突仲裁。
* 数量一律再受券商现实约束封顶:卖出封顶 ``can_use_volume``T+1 只在这里
体现买入封顶可用资金。正T 当天买入的份额当天不可卖,因此它的平仓腿
能卖多少完全由 ``can_use_volume`` 决定,卖不动就自然留成隔夜持仓。
"""
from .rounds import KIND_LONG_T, KIND_SHORT_T
LOT = 100
def choose_kind(price: float, base_cost: float, band_pct: float) -> str | None:
"""按现价相对建仓价的位置决定本轮方向;中性带内返回 None。"""
if price <= 0 or base_cost <= 0 or band_pct < 0:
return None
if price <= base_cost * (1 - band_pct / 100):
return KIND_LONG_T
if price >= base_cost * (1 + band_pct / 100):
return KIND_SHORT_T
return None
def price_allowed(price: float, max_price: float) -> bool:
"""高价股不参与做 T。"""
return 0 < price <= max_price
def entry_volume(kind: str, *, price: float, open_hands: int, sell_ratio: float,
base_qty: int, can_use_volume: int, available: float) -> int:
"""开仓腿计划数量0 表示不提交。"""
if price <= 0:
return 0
if kind == KIND_LONG_T:
# 正T 买入:按手数取量,再受可用资金封顶。
affordable = int(max(0.0, available) // (price * LOT)) * LOT
return max(0, min(open_hands * LOT, affordable))
if kind == KIND_SHORT_T:
# 反T 卖出:按建仓数量比例取整手,再受可卖库存封顶。
planned = int(base_qty * sell_ratio) // LOT * LOT
return max(0, min(planned, _whole_lots(can_use_volume)))
return 0
def exit_volume(kind: str, *, residual_qty: int, price: float,
can_use_volume: int, available: float) -> int:
"""平仓腿可提交数量0 表示当前无法平仓T+1 冻结或资金不足)。"""
if residual_qty <= 0 or price <= 0:
return 0
if kind == KIND_LONG_T:
return max(0, min(residual_qty, _whole_lots(can_use_volume)))
if kind == KIND_SHORT_T:
affordable = int(max(0.0, available) // (price * LOT)) * LOT
return max(0, min(residual_qty, affordable))
return 0
def entry_triggered(kind: str, price: float, base_cost: float, *, band_pct: float,
rebound_confirmed: bool, retrace_confirmed: bool) -> bool:
"""开仓腿是否满足触发条件。"""
if choose_kind(price, base_cost, band_pct) != kind:
return False
if kind == KIND_LONG_T:
return rebound_confirmed # 低吸要等反弹确认,不接下跌中的飞刀
return retrace_confirmed # 高抛要等盈利网格回撤,不追最高点
def exit_triggered(kind: str, price: float, entry_avg_price: float, *,
buy_fall_pct: float, profit_step_pct: float,
rebound_confirmed: bool) -> bool:
"""平仓腿是否满足触发条件。"""
if entry_avg_price <= 0:
return False
if kind == KIND_SHORT_T:
# 反T 买回:较卖出均价回落 buy_fall_pct 且已见反弹。
target = entry_avg_price * (1 - buy_fall_pct / 100)
return price <= target and rebound_confirmed
if kind == KIND_LONG_T:
# 正T 卖出:较买入均价上涨一个网格步长。
return price >= entry_avg_price * (1 + profit_step_pct / 100)
return False
def _whole_lots(volume: int) -> int:
return max(0, int(volume)) // LOT * LOT