Timeframe
1h
Direction
Long & Short
Stoploss
-15.0%
Trailing Stop
No
ROI
0m: 10000.0%
Interface Version
3
Startup Candles
N/A
Indicators
5
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
# -*- coding: utf-8 -*-
"""
BearPullbackShortBearV2 - 熊市回调做空策略(熊市专项训练版)
核心思想:
- 只在熊市(价格 & 均线空头排列)做空;
- 熊市中等待反弹到布林上轨附近 + RSI 过热才开空;
- 止损:ATR * mult(2~4),上限 2.5%;
- 止盈:超卖信号 + 盈利 >= 3% / 6% 分两档;
- 移动止损:在大盈利时锁定部分收益。
该版本专门用于在 2021-2022 这类熊市区间上用 hyperopt 训练参数。
"""
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 BearPullbackShortBearV2(IStrategy):
INTERFACE_VERSION = 3
timeframe = "1h"
can_short = True
# 兜底硬止损(真正逻辑由 custom_exit / custom_stoploss 接管)
stoploss = -0.15
# ROI 完全交给 custom_exit
minimal_roi = {"0": 100}
# === 参数区(后续用 hyperopt 调) ===
# 判趋势用 ADX 阈值(熊市要求不那么强)
adx_threshold = IntParameter(12, 30, default=18, space="buy")
# 入场过热条件:RSI 阈值(熊市反弹一般 55~70 区间)
rsi_entry = IntParameter(55, 75, default=60, space="buy")
# 布林带标准差
bb_std = DecimalParameter(1.8, 2.6, default=2.0, space="buy")
# 超时(小时):超过这个时间,盈利或小亏就平仓
timeout_hours = IntParameter(12, 48, default=24, space="sell")
# ATR 止损倍数(最大允许亏损 = min(ATR_pct * mult, cap))
atr_mult = DecimalParameter(2.0, 4.0, default=3.0, space="sell")
# === 保护机制 ===
@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,
},
]
# === 指标计算 ===
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# 布林带
bb = ta.BBANDS(
dataframe,
timeperiod=20,
nbdevup=float(self.bb_std.value),
nbdevdn=float(self.bb_std.value),
)
dataframe["bb_upper"] = bb["upperband"]
dataframe["bb_middle"] = bb["middleband"]
dataframe["bb_lower"] = bb["lowerband"]
# RSI / ADX
dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
dataframe["adx"] = ta.ADX(dataframe)
# 趋势 EMA
dataframe["ema_50"] = ta.EMA(dataframe, timeperiod=50)
dataframe["ema_200"] = ta.EMA(dataframe, timeperiod=200)
# ATR 用于动态止损
dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)
# 判熊市:
# weak_bear: 价格 & EMA50 在 EMA200 下方
# strong_bear: 同时 ADX > 阈值
thr = int(self.adx_threshold.value)
weak_cond = (
(dataframe["close"] < dataframe["ema_200"])
& (dataframe["ema_50"] < dataframe["ema_200"])
)
strong_cond = weak_cond & (dataframe["adx"] > thr)
dataframe["bear_weak"] = np.where(weak_cond, 1, 0)
dataframe["bear_strong"] = np.where(strong_cond, 1, 0)
# 简单合并:只要 weak 即视为熊市(强弱可在入场时区分)
dataframe["bear_trend"] = dataframe["bear_weak"]
# 预备列
if "enter_tag" not in dataframe.columns:
dataframe["enter_tag"] = ""
if "exit_short" not in dataframe.columns:
dataframe["exit_short"] = 0
return dataframe
# === 入场:熊市中的反弹 + 过热 ===
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# 只做首次突破上轨的K线,避免上轨附近连环抄顶
cross_upper = (
(dataframe["close"] > dataframe["bb_upper"])
& (dataframe["close"].shift(1) <= dataframe["bb_upper"].shift(1))
)
rsi_thr = int(self.rsi_entry.value)
# 强熊市:要求 ADX 达标
cond_strong = (
(dataframe["bear_strong"] == 1)
& cross_upper
& (dataframe["rsi"] > rsi_thr)
& (dataframe["volume"] > 0)
)
# 弱熊市:只要 price < ema200 & ema50 < ema200,允许略低一点的 RSI
cond_weak = (
(dataframe["bear_trend"] == 1)
& (dataframe["bear_strong"] == 0)
& cross_upper
& (dataframe["rsi"] > rsi_thr - 5)
& (dataframe["volume"] > 0)
)
dataframe.loc[cond_strong, ["enter_short", "enter_tag"]] = (
1,
"bear_strong",
)
# dataframe.loc[cond_weak, ["enter_short", "enter_tag"]] = (1, "bear_weak")
return dataframe
# === 出场信号基准(超卖位置) ===
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
给出一个“超卖”信号,供 custom_exit 使用:
价格跌破布林下轨 或 RSI < 30。
"""
if "exit_short" not in dataframe.columns:
dataframe["exit_short"] = 0
oversold = (
(dataframe["close"] < dataframe["bb_lower"]) | (dataframe["rsi"] < 30)
) & (dataframe["volume"] > 0)
dataframe.loc[oversold, "exit_short"] = 1
return dataframe
# === 工具:ATR 动态止损(返回最大允许亏损百分比,正数) ===
def _calc_max_loss_pct(self, candle) -> float:
atr = float(candle.get("atr", 0) or 0)
close = float(candle.get("close", 0) or 1)
atr_pct = atr / close if close > 0 else 0.01
mult = float(self.atr_mult.value)
raw = atr_pct * mult
# 全局上限:2.5%,下限 0.8%
enter_tag = candle.get("enter_tag", "")
if enter_tag == "bear_strong":
cap = 0.022 # 2.2%
else:
cap = 0.018 # 1.8%,弱熊市更短线
floor = 0.010 # 至少 1%
return max(floor, min(raw, cap))
# === 核心:自定义出场逻辑 ===
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 = df.iloc[-1] if not df.empty else dataframe.iloc[-1]
except Exception:
last = dataframe.iloc[-1]
is_bear = int(last.get("bear_trend", 0)) == 1
exit_signal = int(last.get("exit_short", 0))
max_loss_pct = self._calc_max_loss_pct(last)
# 交易存活时长(小时)
duration_hrs = (current_time - trade.open_date_utc).total_seconds() / 3600
timeout = int(self.timeout_hours.value)
# 1) 止损:动态 ATR 止损
if current_profit <= -max_loss_pct:
return "bear_stop_loss"
# 2) Regime Flip:不再是熊市时,盈利或小亏就走
if not is_bear:
if current_profit > -0.005: # -0.5% 以内
return "regime_flip"
# 3) 正常熊市中,根据盈利和超卖信号出场
if exit_signal == 1:
# 第二目标:盈利 >= 6%
if current_profit >= 0.06:
return "tp2_exit"
# 第一目标:盈利 >= 3%
if current_profit >= 0.03:
return "tp1_exit"
recent_duration_hrs = duration_hrs
if is_bear and recent_duration_hrs < 6: # 持仓不到 6 小时
if current_profit <= -0.01: # 亏到 -1%
return "soft_stop"
# 4) 超时处理:超过 timeout,小亏/小赢也离场
if duration_hrs > timeout:
tag = f"timeout_exit_({timeout}h)"
if current_profit > 0:
return tag
if current_profit > -0.005:
return tag
# 其余情况不强制退出
return None
# === 动态移动止盈:在大盈利时保护仓位 ===
def custom_stoploss(
self,
pair: str,
trade: Trade,
current_time: datetime,
current_rate: float,
current_profit: float,
**kwargs,
) -> float:
"""
根据浮盈动态上调止损价位:
- 盈利 > 3% -> 锁定 0.5%
- 盈利 > 5% -> 锁定 2%
- 盈利 > 8% -> 锁定 4%
- 盈利 > 12% -> 锁定 6%
其它情况返回 1,让 custom_exit / 兜底止损处理。
"""
if current_profit > 0.12:
return 0.06
if current_profit > 0.08:
return 0.04
if current_profit > 0.05:
return 0.02
if current_profit > 0.03:
return 0.005
return 1