research: 1m 出场口径重定、费率修正,与影子测量的判据常数
起因是用户看图指出「止盈没做好」,查下来 TP=3.0 确实把右尾截早了, 而且 1m 不该沿用 5m/15m/30m 的参数——成本固定在 bp、目标随 ATR 缩放, 1m 的 3 ATR 只有 0.39% 而 30m 是 1.87%,成本占比差 5 倍。 step41(5m/15m/30m)与 step42(1m)跑同一张全网格: SL × TP × MAX_BARS × 分批(3 ATR 减半 → 剩余目标 × 剩余半仓止损位)。 - 1m 最优 SL2 / 3ATR 减半 / 剩余止损保持 2.0 / 目标 8ATR / 48 根, 样本外 8/8 币、7/7 年全面提升,均R/R夏普/回撤/剔10%PF 四项全赢 - 分批要做,但**减仓后不要动止损**。止损位 0/0.5/1/1.5/2 ATR 严格单调, 越紧越差,三组初始 SL 全一致。保本损是全表最差的一档 - SL=1.0 在 1m 上是废的:剔10%PF 0.78~0.99、中位收益 −0.122% 费率此前写的 taker 3bp / maker 1bp 隐含「原始 taker 6bp」的错误前提, 实际是原始 taker 0.040% / maker 0.016%、返 50% 后 2.0 / 0.8bp。方向是保守的, 所以首轮跑出来的数字全部偏低。exit_model 已改,费率只在分析阶段套用, 不必重跑模拟。改完 1m 的均R +8%,5m/15m/30m 只动 2%——费率只对 1m 有杠杆。 顺带查证了用户的一个假设:余量逐年递减是不是跟波动率有关。成立,而且 r = +0.989。毛/ATR 七年在 2.26~2.67 之间没有趋势,衰减的是 ATR 本身 (2021 的 22.1bp 压到 2026 的 8.8bp)。**是波动率压缩,不是 alpha 衰减。** 由此引出 ATR 门控:低 ATR 桶的毛 R 其实最高(1.16 vs 高 ATR 桶的 0.94), 断崖只在扣费之后出现。所以阈值是**费率的函数**(约 5 + 1.1×taker费), 不是市场常数。当前费率下 ≥8bp,在 5m/15m/30m 上几乎不触发,可作全局规则。 lib/shadow_budget.py 放影子测量要对照的常数:逐币预算、门控阈值、 腿→maker/taker 映射、lag 阈值、判据。记录与报表归 research/live/, 分工的理由是这些数会变——今天预算就动了四次。 out/*.feather 转为 ignore:70MB+ 且重跑可得,摘要都在 HANDOFF。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
"""出场模拟与费率模型,step41(5m/15m/30m)与 step42(1m)共用。
|
||||
|
||||
单独抽出来是因为两边必须用同一份实现——`fast_bsp3` 当初分散在两处的教训。
|
||||
|
||||
## 费率模型(用户 2026-08-27 指出)
|
||||
|
||||
原先所有数字都按「双边 taker」算(§5.3 的 6bp),这在两个方向上都错了:
|
||||
费率档位记高了(见下方 FEE_TAKER 注释),且没有区分出场性质——
|
||||
|
||||
入场 信号在收盘出现,次根开盘市价单进场 → **taker + 滑点**
|
||||
止盈 挂在目标价的限价单被动成交 → **maker,无滑点**
|
||||
止损 stop-market,触发后市价成交 → **taker + 滑点**
|
||||
超时 到点市价平 → **taker + 滑点**
|
||||
|
||||
止损做不成 maker:stop-limit 在急跌里可能不成交,那种情况下的损失远大于
|
||||
省下的 2bp。所以按出场原因分别计费,不是一刀切。
|
||||
|
||||
分批离场不额外增加费用:手续费按名义额收,入场 1.0、出场 0.5+0.5,总额不变。
|
||||
但**第一批必然是止盈成交(maker)**,这正是分批在成本上占便宜的地方。
|
||||
|
||||
## 出场配置
|
||||
|
||||
整仓 `s{SL}_tp{TP}_m{MAXB}`
|
||||
分批 `s{SL}_so{目标}_k{剩余半仓止损}_m{MAXB}`
|
||||
到 3 ATR 平一半,剩余半仓止损挪到「开仓价下方 k 个 ATR」。
|
||||
k=0 即保本损,k 等于原 SL 即止损不动,k 更大则是主动放宽。
|
||||
|
||||
## 实现
|
||||
|
||||
每笔只前推两遍:第一遍记录各价位的首次触及根,第二遍从减仓根起记录剩余
|
||||
半仓各止损位的首次触及根。之后所有配置都是解析推导,不再重复走 K 线。
|
||||
同一根内止损与目标并存时一律判止损先到,宁可低估。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
# 单边费率。用户 2026-08-27 给的实际档位:返佣前 taker 0.040% / maker 0.016%,
|
||||
# API 返 50% → taker 0.020% / maker 0.008%。
|
||||
# 注意:此前这里写的是 0.030/0.010,隐含「原始 taker 6bp」的错误前提,
|
||||
# 所以 step41/42 首轮跑出来的所有数字都偏保守(taker 高估 50%)。
|
||||
FEE_TAKER = 0.00020
|
||||
FEE_MAKER = 0.00008
|
||||
SLIP = 0.00010
|
||||
|
||||
TP, SL_, TIME = 0, 1, 2 # 出场原因编码
|
||||
|
||||
|
||||
def taker_notional(reason: np.ndarray, scaled: np.ndarray) -> np.ndarray:
|
||||
"""每笔走 taker 的名义额(入场 1.0,加上非止盈出场的部分)。滑点只发生在这上面。"""
|
||||
exit_taker = np.where(reason == TP, 0.0, 1.0)
|
||||
exit_taker = np.where(scaled == 1, 0.5 * exit_taker, exit_taker)
|
||||
return 1.0 + exit_taker
|
||||
|
||||
|
||||
def fee_of(reason: np.ndarray, scaled: np.ndarray,
|
||||
fee_taker: float = FEE_TAKER, fee_maker: float = FEE_MAKER) -> np.ndarray:
|
||||
"""只算手续费,不含滑点。分批时第一半必然是止盈成交(maker)。"""
|
||||
exit_fee = np.where(reason == TP, fee_maker, fee_taker)
|
||||
exit_fee = np.where(scaled == 1, 0.5 * fee_maker + 0.5 * exit_fee, exit_fee)
|
||||
return fee_taker + exit_fee
|
||||
|
||||
|
||||
def cost_of(reason: np.ndarray, scaled: np.ndarray, slip: float = SLIP) -> np.ndarray:
|
||||
"""新口径:入场 taker、止盈 maker、止损/超时 taker,滑点只加在 taker 腿上。"""
|
||||
return fee_of(reason, scaled) + slip * taker_notional(reason, scaled)
|
||||
|
||||
|
||||
def all_taker_cost(reason: np.ndarray, scaled: np.ndarray) -> np.ndarray:
|
||||
"""旧口径:进出都当 taker,双边费 + 双边滑点。用来对照新旧差多少。"""
|
||||
return np.full(len(reason), 2.0 * (FEE_TAKER + SLIP))
|
||||
|
||||
|
||||
def flat_cost(reason: np.ndarray, scaled: np.ndarray) -> np.ndarray:
|
||||
"""研究口径的固定 5bp,用于和 step28/35/37 的历史数字对齐。"""
|
||||
return np.full(len(reason), 0.0005)
|
||||
|
||||
|
||||
def slip_budget(gross: np.ndarray, reason: np.ndarray, scaled: np.ndarray) -> float:
|
||||
"""盈亏平衡的单边滑点上限(bp):毛收益扣掉手续费后,摊到走 taker 的名义额上。"""
|
||||
net_of_fee = gross.mean() - fee_of(reason, scaled).mean()
|
||||
return net_of_fee / taker_notional(reason, scaled).mean() * 10000
|
||||
|
||||
|
||||
def cfg_name(sl: float, target, maxb: int, rstop=None) -> str:
|
||||
"""整仓 s{SL}_tp{T}_m{B};分批 s{SL}_so{T}_k{K}_m{B}。target=None 表示不设目标。"""
|
||||
t = f"{target:g}" if target is not None else "R"
|
||||
if rstop is None:
|
||||
return f"s{sl:g}_tp{t}_m{maxb}"
|
||||
return f"s{sl:g}_so{t}_k{rstop:g}_m{maxb}"
|
||||
|
||||
|
||||
def walk_exits(cdf: pd.DataFrame, sig: pd.DataFrame, sls, tps, maxbs,
|
||||
scale_at: float = 3.0, runners=(5.0, 6.0, 8.0, None),
|
||||
runner_stops=(0.0, 0.5, 1.0, 1.5, 2.0)) -> pd.DataFrame:
|
||||
"""前推每笔信号,解析出全部出场配置的结果。
|
||||
|
||||
每个配置四列:`{cfg}_g` 毛收益率、`{cfg}_r` 出场原因、`{cfg}_c` 是否分批、
|
||||
`{cfg}_b` 持仓根数。另有 `s{SL}_mfe` 各初始止损下的最大有利偏移。
|
||||
"""
|
||||
high = cdf["high"].to_numpy(float)
|
||||
low = cdf["low"].to_numpy(float)
|
||||
open_ = cdf["open"].to_numpy(float)
|
||||
close = cdf["close"].to_numpy(float)
|
||||
atr = cdf["atr"].to_numpy(float)
|
||||
n = len(cdf)
|
||||
horizon = max(maxbs)
|
||||
ups = sorted(set(list(tps) + [scale_at] + [r for r in runners if r]))
|
||||
downs = sorted(set(list(sls) + list(runner_stops)))
|
||||
out = []
|
||||
|
||||
for s, d in zip(sig["entry_idx"].astype(int), sig["direction"].astype(int)):
|
||||
e = s + 1
|
||||
if e >= n - 1:
|
||||
continue
|
||||
a = atr[s]
|
||||
if not np.isfinite(a) or a <= 0:
|
||||
continue
|
||||
entry = open_[e]
|
||||
end = min(e + horizon, n - 1)
|
||||
|
||||
# 第一遍:各价位的首次触及根(与止损无关,纯价格事件)
|
||||
up_bar = {t: None for t in ups}
|
||||
dn_bar = {L: None for L in downs}
|
||||
mfe = {sl: 0.0 for sl in sls}
|
||||
alive = {sl: True for sl in sls}
|
||||
for j in range(e, end + 1):
|
||||
adv = (high[j] - entry) / a if d == 1 else (entry - low[j]) / a
|
||||
ret = (entry - low[j]) / a if d == 1 else (high[j] - entry) / a
|
||||
for L in downs:
|
||||
if dn_bar[L] is None and ret >= L:
|
||||
dn_bar[L] = j
|
||||
for sl in sls:
|
||||
if alive[sl]:
|
||||
if dn_bar[sl] is not None and dn_bar[sl] == j:
|
||||
alive[sl] = False # 同根内止损优先,不更新 MFE
|
||||
elif adv > mfe[sl]:
|
||||
mfe[sl] = adv
|
||||
for t in ups:
|
||||
if up_bar[t] is None and adv >= t:
|
||||
up_bar[t] = j
|
||||
for sl in sls:
|
||||
mfe[sl] = mfe[sl]
|
||||
|
||||
# 第二遍:从减仓根起,剩余半仓各止损位的首次触及根
|
||||
j0 = up_bar[scale_at]
|
||||
dn_after = {L: None for L in runner_stops}
|
||||
if j0 is not None:
|
||||
for j in range(j0, end + 1):
|
||||
ret = (entry - low[j]) / a if d == 1 else (high[j] - entry) / a
|
||||
for L in runner_stops:
|
||||
if dn_after[L] is None and ret >= L:
|
||||
dn_after[L] = j
|
||||
if all(v is not None for v in dn_after.values()):
|
||||
break
|
||||
|
||||
row = {"sig_idx": s, "direction": d, "atr_pct": a / entry}
|
||||
row.update({f"s{sl:g}_mfe": mfe[sl] for sl in sls})
|
||||
|
||||
def resolve(target, stop_bar, stop_ret, cap, start):
|
||||
"""在 cap 根之前,目标与止损谁先到。返回 (毛收益率, 原因, 出场根)。"""
|
||||
tb = up_bar[target] if target is not None else None
|
||||
if tb is not None and tb <= cap and (stop_bar is None or tb < stop_bar):
|
||||
return target * a / entry, TP, tb
|
||||
if stop_bar is not None and stop_bar <= cap:
|
||||
return stop_ret, SL_, stop_bar
|
||||
k = min(cap, n - 1)
|
||||
return d * (close[k] - entry) / entry, TIME, k
|
||||
|
||||
for sl in sls:
|
||||
dead = dn_bar[sl]
|
||||
for b in maxbs:
|
||||
cap = e + b
|
||||
for t in tps:
|
||||
g, r, xb = resolve(t, dead, -sl * a / entry, cap, e)
|
||||
c = cfg_name(sl, t, b)
|
||||
row[f"{c}_g"], row[f"{c}_r"], row[f"{c}_c"], row[f"{c}_b"] = g, r, 0, xb - e + 1
|
||||
# 分批:先看能否走到减仓点
|
||||
scaled_ok = (j0 is not None and j0 <= cap
|
||||
and (dead is None or j0 < dead))
|
||||
for rn in runners:
|
||||
for k in runner_stops:
|
||||
c = cfg_name(sl, rn, b, k)
|
||||
if not scaled_ok:
|
||||
g, r, xb = resolve(scale_at, dead, -sl * a / entry, cap, e)
|
||||
row[f"{c}_g"], row[f"{c}_r"] = g, r
|
||||
row[f"{c}_c"], row[f"{c}_b"] = 0, xb - e + 1
|
||||
else:
|
||||
rg, rr, rxb = resolve(rn, dn_after[k], -k * a / entry, cap, j0)
|
||||
row[f"{c}_g"] = 0.5 * (scale_at * a / entry) + 0.5 * rg
|
||||
row[f"{c}_r"], row[f"{c}_c"] = rr, 1
|
||||
row[f"{c}_b"] = rxb - e + 1
|
||||
out.append(row)
|
||||
return pd.DataFrame(out)
|
||||
|
||||
|
||||
def stat(g: pd.DataFrame, cfg: str, label: str, cost_fn=cost_of, minn: int = 25) -> dict:
|
||||
"""给一个配置出统计。`滑点余量bp` 是滑点的盈亏平衡上限。"""
|
||||
gross = g[f"{cfg}_g"].to_numpy()
|
||||
if len(gross) < minn:
|
||||
return {}
|
||||
reason, scaled = g[f"{cfg}_r"].to_numpy(), g[f"{cfg}_c"].to_numpy()
|
||||
r = gross - cost_fn(reason, scaled)
|
||||
w, o = r[r > 0], r[r <= 0]
|
||||
sd = r.std(ddof=1)
|
||||
t10 = r[r <= np.quantile(r, 0.90)]
|
||||
return {"口径": label, "笔数": len(r),
|
||||
"胜率": f"{(r > 0).mean() * 100:.1f}%",
|
||||
"毛bp": f"{gross.mean() * 10000:.2f}",
|
||||
"净均收益": f"{r.mean() * 100:+.3f}%",
|
||||
"中位": f"{np.median(r) * 100:+.3f}%",
|
||||
"PF": f"{w.sum() / abs(o.sum()):.2f}" if len(o) else "inf",
|
||||
"t值": f"{r.mean() / (sd / np.sqrt(len(r))):+.2f}",
|
||||
"剔10%PF": f"{t10[t10 > 0].sum() / abs(t10[t10 <= 0].sum()):.2f}" if (t10 <= 0).any() else "inf",
|
||||
"滑点余量bp": f"{slip_budget(gross, reason, scaled):.2f}",
|
||||
"止盈占比": f"{(reason == TP).mean() * 100:.0f}%",
|
||||
"均持仓": f"{g[f'{cfg}_b'].mean():.1f}"}
|
||||
|
||||
|
||||
def rstat(g: pd.DataFrame, cfg: str, label: str, sl: float, cost_fn=cost_of) -> dict:
|
||||
"""R 倍数口径:固定风险仓位下,不同初始止损之间唯一可比的量。
|
||||
|
||||
SL 越宽,每笔风险越大、仓位越小,直接比百分比收益会把仓位差异算成策略优势。
|
||||
"""
|
||||
gross = g[f"{cfg}_g"].to_numpy()
|
||||
reason, scaled = g[f"{cfg}_r"].to_numpy(), g[f"{cfg}_c"].to_numpy()
|
||||
net = gross - cost_fn(reason, scaled)
|
||||
risk = sl * g["atr_pct"].to_numpy()
|
||||
r = net / risk
|
||||
return {"口径": label, "笔数": len(r), "风险%": f"{risk.mean() * 100:.2f}%",
|
||||
"净均收益": f"{net.mean() * 100:+.3f}%",
|
||||
"均R": f"{r.mean():+.3f}", "中位R": f"{np.median(r):+.3f}",
|
||||
"R夏普": f"{r.mean() / r.std(ddof=1):.3f}",
|
||||
"最差1%R": f"{np.percentile(r, 1):.2f}",
|
||||
"胜率": f"{(r > 0).mean() * 100:.1f}%"}
|
||||
@@ -0,0 +1,128 @@
|
||||
"""影子测量要对照的那条线:逐币滑点预算、ATR 门控、lag 阈值。
|
||||
|
||||
记录与报表由 `research/live/shadow_hb.py` / `shadow_report.py` 负责,
|
||||
**这里只放从回测推出来的常数和判据**。分工的理由是这些数会变——
|
||||
2026-08-27 一天之内预算就动了四次(3.91 → 11.06 → 14.25 → 15.19bp),
|
||||
费率也从 3/1bp 改成 2/0.8bp。写死在 live 侧的任何一份都会静默过期。
|
||||
|
||||
## 预算的定义
|
||||
|
||||
余量 = (毛均收益 − 实际手续费) / taker 名义额
|
||||
|
||||
分母是 **taker 名义额**(入场 1.0 + 非止盈出场的部分),所以这个数的含义是
|
||||
**每条 taker 腿能承受多少 bp 滑点**。止盈挂限价是 maker、不吃滑点,
|
||||
不能混进分母——否则会被那 60% 零滑点的止盈腿稀释,低估真实成本。
|
||||
|
||||
对照时用入场滑点即可(入场必然是 taker),无需等出场腿。
|
||||
|
||||
## 口径必须一致,否则数字不可比
|
||||
|
||||
预算算在这套口径上,live 侧的信号路径要对齐,缺一项数就不作数:
|
||||
|
||||
滤网 同向(h1_agree)**+ 中枢阶梯**。只有同向会显著偏低,见 §1.4
|
||||
门控 ATR ≥ ATR_GATE_BP
|
||||
出场 SL 2.0 / 3 ATR 减半 / 剩余半仓止损保持 2.0 / 目标 8 ATR / 48 根
|
||||
费率 taker 2.0bp、maker 0.8bp(返佣后)
|
||||
|
||||
> 曾经的 `毛均收益 − 6.0bp` 是错的:6bp 假设双边 taker 且原始费率 0.12%,
|
||||
> 而实际是原始 taker 0.040%、返 50% 后 2.0bp,且止盈是 maker。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
from .exit_model import FEE_TAKER, FEE_MAKER
|
||||
|
||||
# ATR 门控。阈值是**费率的函数**,不是市场常数——低 ATR 的信号毛质量其实
|
||||
# 更好(毛R 1.16 vs 高 ATR 桶的 0.94),断崖只在扣费之后出现。见 §5.3。
|
||||
ATR_GATE_BP = 8.0
|
||||
|
||||
# lag 探针。补丁后实测 506~642ms,理论下限约 500ms。
|
||||
# 超阈值要**停止开新仓**,不能只打日志——退化是静默的,不崩也不报错。
|
||||
LAG_ALARM_MS = 800.0
|
||||
LAG_WINDOW = 30
|
||||
|
||||
# 逐币滑点预算(bp / taker 腿)。取 2026 年——那是七年里 ATR 最低的一年,
|
||||
# 也是当前所处的环境,**不要用全样本平均值**,低波动是现状不是尾部情形。
|
||||
BUDGET_BP = {
|
||||
"BTC": 8.58, # 全期 14.61。ATR 中位 2026 仅 6.5bp,11 个币里最低,
|
||||
# 门控要刷掉 58.5% 的信号 —— **不建议作为 1m 交易标的**,
|
||||
# 但流动性最好,适合做滑点测量的乐观边界
|
||||
"BNB": 15.48, # 全期 22.11。2026 门控只保留 25.2%
|
||||
"ETH": 20.64, # 全期 20.80
|
||||
"SOL": 16.83, # 全期 28.29
|
||||
"LINK": 9.18, # 全期 26.24。2026 波动压缩得厉害
|
||||
"LTC": 9.63, # 全期 23.63
|
||||
"AVAX": 15.02, # 全期 29.00
|
||||
"XRP": 17.04, # 全期 24.52
|
||||
"DOGE": 18.55, # 全期 28.48
|
||||
"ADA": 22.28, # 全期 23.53。2026 唯一没被波动压缩的
|
||||
"TRX": np.nan, # 2026 门控后只剩 4.2% 的信号,当前环境基本不能做
|
||||
}
|
||||
|
||||
# 组合口径(8 个样本外币合计)。单币样本薄时用这个判整体。
|
||||
BUDGET_PORTFOLIO_2026 = 15.19
|
||||
BUDGET_PORTFOLIO_RECENT = 19.17 # 2025+2026
|
||||
BUDGET_PORTFOLIO_FULL = 25.39 # 全样本外
|
||||
|
||||
# 腿 → 是否 taker。这不是日志字段,**这就是成本模型本身**:
|
||||
# 止盈挂限价是 maker 不吃滑点,止损是 stop-market、超时是市价,都吃。
|
||||
# 止损做不成 maker——stop-limit 在急跌里可能不成交,损失远大于省下的费。
|
||||
LEG_IS_TAKER = {
|
||||
"entry": True,
|
||||
"scale": False, # 3 ATR 减半,限价
|
||||
"runner_tp": False, # 剩余半仓到 8 ATR,限价
|
||||
"stop": True, # stop-market
|
||||
"timeout": True, # 48 根到点市价平
|
||||
}
|
||||
|
||||
|
||||
def gate_threshold_bp(fee_taker: float = FEE_TAKER) -> float:
|
||||
"""换费率档位时的粗略指引,**不是** ATR_GATE_BP 的定义式。
|
||||
|
||||
拟合自 taker 0/1.5/3/6bp 上实测的最优阈值 5/8/9/12bp。当前费率下
|
||||
公式给 7.2 而实测峰值是 8——不必调和:均R 在 5~12bp 之间只在
|
||||
0.972~0.987 之间动,**曲线是平的,精确值不重要**。
|
||||
要记住的是阈值随费率上移,换 VIP 档 / 换交易所要重扫,别抄 8bp。
|
||||
"""
|
||||
return 5.0 + 1.1 * fee_taker * 1e4
|
||||
|
||||
|
||||
def fee_rate(leg: str) -> float:
|
||||
return FEE_TAKER if LEG_IS_TAKER[leg] else FEE_MAKER
|
||||
|
||||
|
||||
def budget_of(symbol: str) -> float:
|
||||
"""取该币的预算。未收录或当前环境不可做的返回 nan。"""
|
||||
return BUDGET_BP.get(symbol.upper(), np.nan)
|
||||
|
||||
|
||||
def lag_healthy(recent_lag_ms, alarm_ms: float = LAG_ALARM_MS,
|
||||
window: int = LAG_WINDOW) -> bool:
|
||||
"""滚动中位数是否还在阈值内。False 时应告警并**停止开新仓**。
|
||||
|
||||
用中位数而非均值:单根的网络抖动不该触发停机,持续退化才该。
|
||||
"""
|
||||
x = np.asarray(recent_lag_ms, dtype=float)[-window:]
|
||||
if len(x) < window:
|
||||
return True
|
||||
return bool(np.median(x) <= alarm_ms)
|
||||
|
||||
|
||||
def verdict(measured_slip_bp: float, symbol: str | None = None,
|
||||
budget_bp: float | None = None) -> str:
|
||||
"""把实测滑点翻成 §6 第 4 步的四档判断。
|
||||
|
||||
`measured_slip_bp` 用**每条 taker 腿**的中位滑点,不是每笔的总滑点。
|
||||
"""
|
||||
if budget_bp is None:
|
||||
budget_bp = budget_of(symbol) if symbol else BUDGET_PORTFOLIO_2026
|
||||
if not np.isfinite(budget_bp):
|
||||
return "该币当前环境预算不足,不作交易标的"
|
||||
if measured_slip_bp <= 5:
|
||||
return "可行,进真实 dry run"
|
||||
if measured_slip_bp <= 10:
|
||||
return "可行但仅中高波动期,门控阈值要上调"
|
||||
if measured_slip_bp <= budget_bp:
|
||||
return "仅 2021 型牛市能做,常态不要开"
|
||||
return "放弃该腿"
|
||||
Reference in New Issue
Block a user