理性做空策略 (Rational Short System) - MathModel V2
Timeframe
1h
Direction
Long & Short
Stoploss
-15.0%
Trailing Stop
No
ROI
0m: 10000.0%
Interface Version
3
Startup Candles
N/A
Indicators
6
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
# -*- coding: utf-8 -*-
"""
RationalShort - 三态理性做空策略(MathModel V2)
改动目标:
1. 用“期望 E = p * W - (1-p) * L”约束策略结构:
- 胜率 p 可以在 40%~60% 波动;
- 但要求平均盈利 W 至少 ~2%,平均亏损 L 控制在 ~2% 左右。
2. 尽量放大利润单、缩小亏损单:
- 止损改为 ATR + 上限控制,避免 -4%~-5% 的大亏损;
- exit_signal / 趋势行情允许利润拉大,再用动态移动止盈出场;
- RegimeFlip 保护:Side/Bear 单被拉成 Bull 时尽快小亏/打平离场。
3. 保留“三态模型(Bull / Side / Bear)”的大框架结构,方便后续用 hyperopt 做参数搜索。
注意:
- 该版本适用于 freqtrade 2025.10,期货 / 合约环境(can_short = True)。
- 若与旧版本共存,请将文件名改为 `RationalShortV2.py`,类名改为 `RationalShortV2`,
并在命令行用 `--strategy RationalShortV2` 运行。
"""
from datetime import datetime
import numpy as np
import talib.abstract as ta
from pandas import DataFrame
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
from freqtrade.persistence import Trade
class RationalShortv2(IStrategy):
"""理性做空策略 (Rational Short System) - MathModel V2"""
# === 1. 策略基础配置 ===
INTERFACE_VERSION = 3
timeframe = "1h"
can_short = True
# 兜底硬止损(真正逻辑由 custom_stoploss / custom_exit 接管)
stoploss = -0.15
# ROI 完全交给 custom_exit,设置一个极大值
minimal_roi = {"0": 100}
# === 2. 参数区(可后续用 hyperopt 调) ===
# ADX 阈值,区分震荡 / 趋势
adx_threshold = IntParameter(20, 40, default=25, space="buy")
# 不同状态下的 RSI 入场门槛
rsi_bull = IntParameter(80, 90, default=84, space="buy") # 牛市:极端超买
rsi_side = IntParameter(60, 80, default=68, space="buy") # 震荡:正常超买
rsi_bear = IntParameter(45, 70, default=58, space="buy") # 熊市:反弹就空
# 布林带倍数
bb_std = DecimalParameter(1.8, 2.6, default=2.0, space="buy")
# 超时时间(小时)
timeout_hours = IntParameter(6, 24, default=10, space="sell")
# ATR 止损倍数(配合上限使用)
atr_stop_mult = DecimalParameter(2.0, 4.0, default=3.0, space="sell")
# === 3. 保护机制 ===
@property
def protections(self):
return [
{"method": "CooldownPeriod", "stop_duration_candles": 1},
{
"method": "StoplossGuard",
"lookback_period_candles": 24,
"trade_limit": 5,
"stop_duration_candles": 6,
"only_per_pair": False,
},
]
# === 4. 指标计算 ===
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# A. 布林带
bollinger = ta.BBANDS(
dataframe,
timeperiod=20,
nbdevup=float(self.bb_std.value),
nbdevdn=float(self.bb_std.value),
)
dataframe["bb_upper"] = bollinger["upperband"]
dataframe["bb_middle"] = bollinger["middleband"]
dataframe["bb_lower"] = bollinger["lowerband"]
# B. RSI & ADX
dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
dataframe["adx"] = ta.ADX(dataframe)
# C. EMA200 作为大级别趋势线
dataframe["ema_200"] = ta.EMA(dataframe, timeperiod=200)
# D. ATR:用于动态止损
dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)
# E. 短期加速指标(3 根 K 的涨幅)
dataframe["roc_3"] = ta.ROC(dataframe, timeperiod=3)
# F. 标记市场状态 (1=Bull, 2=Side, 3=Bear)
thr = int(self.adx_threshold.value)
dataframe["market_state"] = 2 # 默认震荡
dataframe.loc[
(dataframe["close"] > dataframe["ema_200"]) & (dataframe["adx"] > thr),
"market_state",
] = 1
dataframe.loc[
(dataframe["close"] < dataframe["ema_200"]) & (dataframe["adx"] > thr),
"market_state",
] = 3
# G. 预备列
if "enter_tag" not in dataframe.columns:
dataframe["enter_tag"] = ""
if "exit_short" not in dataframe.columns:
dataframe["exit_short"] = 0
return dataframe
# === 5. 开仓逻辑 (分状态路由) ===
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# 只做“突破上轨的首次穿越”:上一根在下,这一根收盘在上
cross_upper = (
(dataframe["close"] > dataframe["bb_upper"]) &
(dataframe["close"].shift(1) <= dataframe["bb_upper"].shift(1))
)
# 基础条件:突破上轨 + 有量
base_cond = cross_upper & (dataframe["volume"] > 0)
# === 牛市:极端超买 + 短期爆拉,逆势短线做空(scalping) ===
cond_bull = (
base_cond &
(dataframe["market_state"] == 1) &
(dataframe["rsi"] > int(self.rsi_bull.value)) &
(dataframe["roc_3"] > 4)
)
# === 震荡:正常超买,但要求价格不在 EMA200 上方太远 ===
cond_side = (
base_cond &
(dataframe["market_state"] == 2) &
(dataframe["rsi"] > int(self.rsi_side.value)) &
(dataframe["close"] <= dataframe["ema_200"] * 1.01)
)
# === 熊市:略微反弹就做空 ===
cond_bear = (
base_cond &
(dataframe["market_state"] == 3) &
(dataframe["rsi"] > int(self.rsi_bear.value))
)
dataframe.loc[cond_bull, ["enter_short", "enter_tag"]] = (1, "bull")
dataframe.loc[cond_side, ["enter_short", "enter_tag"]] = (1, "side")
dataframe.loc[cond_bear, ["enter_short", "enter_tag"]] = (1, "bear")
return dataframe
# === 6. 平仓信号(用于配合 custom_exit) ===
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""基础 exit_short 信号:跌回布林中轨以下。"""
if "exit_short" not in dataframe.columns:
dataframe["exit_short"] = 0
dataframe.loc[
(dataframe["close"] < dataframe["bb_middle"]) & (dataframe["volume"] > 0),
"exit_short",
] = 1
return dataframe
# === 工具函数:根据 ATR 计算当前状态下的最大允许亏损 ===
def _calc_max_loss_pct(self, state: int, last_candle: DataFrame) -> float:
atr = float(last_candle.get("atr", 0))
close = float(last_candle.get("close", 0)) or 1.0
atr_pct = atr / close if close > 0 else 0.01
mult = float(self.atr_stop_mult.value)
raw = atr_pct * mult
# 不同状态给不同上限(单位:小数,0.02=2%)
if state == 1: # Bull:只允许小亏
max_cap = 0.012
elif state == 2: # Side:中等亏损
max_cap = 0.02
else: # Bear:略宽一点
max_cap = 0.025
# 同时保证不低于 0.006(避免极小 ATR 导致随便被打掉)
return max(0.006, min(raw, max_cap))
# === 7. 核心:三态风控引擎 ===
def custom_exit(
self,
pair: str,
trade: Trade,
current_time: datetime,
current_rate: float,
current_profit: float,
**kwargs,
):
"""根据市场状态决定不同的止盈 / 止损 / 超时逻辑。"""
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
try:
df = dataframe.loc[dataframe["date"] <= current_time]
last_candle = df.iloc[-1] if not df.empty else dataframe.iloc[-1]
except Exception:
last_candle = dataframe.iloc[-1]
state = int(last_candle.get("market_state", 2)) # 默认震荡
exit_signal = int(last_candle.get("exit_short", 0))
trade_duration = (current_time - trade.open_date_utc).total_seconds() / 3600
base_timeout = int(self.timeout_hours.value)
# 不同状态下最大持仓时间
if state == 1: # 牛市:短线
max_hours = max(3, int(base_timeout * 0.6))
elif state == 3: # 熊市:趋势单可多拿一会儿
max_hours = int(base_timeout * 1.5)
else: # 震荡:使用基础超时时间
max_hours = base_timeout
# 动态最大允许亏损(基于 ATR + 上限)
max_loss_pct = self._calc_max_loss_pct(state, last_candle)
enter_tag = getattr(trade, "enter_tag", "") or ""
# --------------------------------------
# 0. Regime Flip 保护:Side/Bear 单被拉成 Bull
# --------------------------------------
if state == 1 and enter_tag not in ("bull", ""):
# 状态切换到 Bull,且这单不是在 Bull 下开的
# 小亏 / 打平就走,避免被拖成大亏
if current_profit > -0.003: # -0.3% 以内
return "regime_flip"
# 更深的亏损交给后面的止损逻辑处理
# --------------------------------------
# 1. 牛市:短线 scalping
# --------------------------------------
if state == 1:
# 小止损:基于 ATR 的动态值
if current_profit <= -max_loss_pct:
return "bull_stop_loss"
# 小止盈:目标 1.5% 左右
if current_profit >= 0.015:
return "bull_scalp_profit"
# 如果已经有利润,并且触发布林中轨 exit 信号,则落袋
if exit_signal == 1 and current_profit > 0.005:
return "exit_signal"
# --------------------------------------
# 2. 震荡:布林带回归
# --------------------------------------
elif state == 2:
# 止损:动态 + 上限
if current_profit <= -max_loss_pct:
return "stop_loss"
# exit_signal 触发且 >0.5% 就离场,保证盈利单不是太小
if exit_signal == 1 and current_profit > 0.005:
return "exit_signal"
# 兜底止盈:> 3.5% 直接平仓
if current_profit >= 0.035:
return "side_trend_profit"
# --------------------------------------
# 3. 熊市:趋势行情
# --------------------------------------
elif state == 3:
# 止损:动态 + 上限
if current_profit <= -max_loss_pct:
return "bear_stop_loss"
# 如果已经吃到比较大的趋势 (>3%),且出现 exit_signal,则落袋为安
if exit_signal == 1 and current_profit > 0.025:
return "exit_signal"
# --------------------------------------
# 4. 超时强制平仓
# --------------------------------------
if trade_duration > max_hours:
tag = f"timeout_exit_({int(max_hours)}h)"
# 有利润就直接平仓
if current_profit > 0:
return tag
# 轻微亏损也不要再拖(>-0.5%)
if current_profit > -0.005:
return tag
# 不触发任何硬退出
return None
# === 8. 动态移动止盈 ===
def custom_stoploss(
self,
pair: str,
trade: Trade,
current_time: datetime,
current_rate: float,
current_profit: float,
**kwargs,
) -> float:
"""根据浮盈动态上调止损价位,保护利润。
返回值是相对于开仓价的最大允许亏损(负数)。
这里采用分段式移动止盈:
- 盈利 > 2% -> 止损抬到 +0.5%
- 盈利 > 4% -> 锁定 2%
- 盈利 > 7% -> 锁定 3%
- 盈利 > 10% -> 锁定 5%
其它情况返回 1,让 custom_exit / 兜底 stoploss 去处理。
"""
# 阶段 1:赚 2% -> 锁定 0.5%
if current_profit > 0.02:
return 0.005
# 阶段 2:赚 4% -> 锁定 2%
if current_profit > 0.04:
return 0.02
# 阶段 3:赚 7% -> 锁定 3%
if current_profit > 0.07:
return 0.03
# 阶段 4:赚 10% -> 锁定 5%
if current_profit > 0.10:
return 0.05
# 其它情况交给 custom_exit / 基础 stoploss
return 1