均线金叉 + 成交量放大策略
Timeframe
15m
Direction
Long Only
Stoploss
-8.0%
Trailing Stop
Yes
ROI
0m: 8.0%, 30m: 7.0%, 60m: 6.0%, 120m: 5.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
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# flake8: noqa: F401
# isort: skip_file
# --- Do not remove这些 imports ---
import numpy as np
import pandas as pd
from datetime import datetime, timedelta, timezone
from pandas import DataFrame
from typing import Dict, Optional, Union, Tuple
from freqtrade.strategy import (
IStrategy,
Trade,
Order,
PairLocks,
informative, # @informative decorator
# Hyperopt Parameters
BooleanParameter,
CategoricalParameter,
DecimalParameter,
IntParameter,
RealParameter,
# timeframe helpers
timeframe_to_minutes,
timeframe_to_next_date,
timeframe_to_prev_date,
timeframe_to_seconds,
timeframe_to_msecs,
)
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
class MAVolumeStrategy(IStrategy):
"""
均线金叉 + 成交量放大策略
策略逻辑:
买入:均线金叉 + 成交量放大
卖出:均线死叉或破重要支撑
止损:宽松止损 15%-20%,甚至不用严格止损
特点:
1. 基于移动平均线金叉死叉信号
2. 结合成交量确认信号强度
3. 宽松的止损设置,减少过早止损
4. 重点关注趋势转换点
"""
INTERFACE_VERSION = 3
# 策略时间框架
timeframe = "15m"
# 是否可以做空
can_short: bool = False
# ROI设置 - 优化为更积极的利润目标
minimal_roi = {
"2880": 0.015, # 48小时后1.5%收益
"1440": 0.02, # 24小时后2%收益
"720": 0.025, # 12小时后2.5%收益
"480": 0.03, # 8小时后3%收益
"360": 0.035, # 6小时后3.5%收益
"240": 0.04, # 4小时后4%收益
"180": 0.045, # 3小时后4.5%收益
"120": 0.05, # 2小时后5%收益
"60": 0.06, # 1小时后6%收益
"30": 0.07, # 30分钟后7%收益
"0": 0.08 # 立即8%收益
}
# 止损设置 - 动态止损,减少噪音
stoploss = -0.08 # 放宽到8%,减少噪音止损
# 追踪止损 - 更保守的设置
trailing_stop = True
trailing_stop_positive = 0.02 # 2%开始追踪
trailing_stop_positive_offset = 0.025 # 2.5%偏移
trailing_only_offset_is_reached = True
# 只处理新K线
process_only_new_candles = True
# 策略参数 - 暂时禁用exit_signal,只用高胜率信号
use_exit_signal = False # 禁用exit_signal,避免低胜率信号
exit_profit_only = False
ignore_roi_if_entry_signal = False
# 策略启动所需的K线数量
startup_candle_count: int = 100
# 仓位调整参数
position_adjustment_enable = True
max_entry_position_adjustment = 2 # 最多加仓2次
# 订单类型
order_types = {
"entry": "limit",
"exit": "limit",
"stoploss": "market",
"stoploss_on_exchange": False
}
# 订单时间
order_time_in_force = {
"entry": "GTC",
"exit": "GTC"
}
# 策略参数 - 优化后的参数
# 均线参数 - 调整为更敏感的参数
fast_ma_period = IntParameter(5, 15, default=8, space="buy")
slow_ma_period = IntParameter(15, 30, default=21, space="buy")
# 成交量参数 - 降低阈值
volume_ma_period = IntParameter(10, 20, default=15, space="buy")
volume_threshold = DecimalParameter(1.1, 2.0, default=1.3, space="buy")
# 支撑位参数
support_ma_period = IntParameter(30, 60, default=50, space="sell")
def informative_pairs(self):
return []
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
计算技术指标
"""
# 移动平均线 - 用于金叉死叉判断
dataframe["ema_fast"] = ta.EMA(dataframe, timeperiod=self.fast_ma_period.value)
dataframe["ema_slow"] = ta.EMA(dataframe, timeperiod=self.slow_ma_period.value)
# 长期均线 - 用于支撑位判断
dataframe["sma_support"] = ta.SMA(dataframe, timeperiod=self.support_ma_period.value)
# 成交量指标
dataframe["volume_ma"] = dataframe["volume"].rolling(window=self.volume_ma_period.value).mean()
dataframe["volume_ratio"] = dataframe["volume"] / dataframe["volume_ma"]
# 均线金叉死叉信号
dataframe["ma_golden_cross"] = (
(dataframe["ema_fast"] > dataframe["ema_slow"]) &
(dataframe["ema_fast"].shift(1) <= dataframe["ema_slow"].shift(1))
)
dataframe["ma_death_cross"] = (
(dataframe["ema_fast"] < dataframe["ema_slow"]) &
(dataframe["ema_fast"].shift(1) >= dataframe["ema_slow"].shift(1))
)
# 成交量放大信号
dataframe["volume_surge"] = dataframe["volume_ratio"] >= self.volume_threshold.value
# 价格位置指标
dataframe["price_above_fast_ma"] = dataframe["close"] > dataframe["ema_fast"]
dataframe["price_above_slow_ma"] = dataframe["close"] > dataframe["ema_slow"]
dataframe["price_above_support"] = dataframe["close"] > dataframe["sma_support"]
# 趋势强度
dataframe["trend_strength"] = (
(dataframe["ema_fast"] > dataframe["ema_slow"]).astype(int) +
(dataframe["close"] > dataframe["ema_fast"]).astype(int) +
(dataframe["close"] > dataframe["ema_slow"]).astype(int)
)
# 支撑位强度
dataframe["support_strength"] = (
(dataframe["close"] > dataframe["sma_support"]).astype(int) +
(dataframe["low"] > dataframe["sma_support"]).astype(int)
)
# RSI - 辅助判断
dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
# MACD - 趋势确认
macd = ta.MACD(dataframe)
dataframe["macd"] = macd["macd"]
dataframe["macdsignal"] = macd["macdsignal"]
dataframe["macdhist"] = macd["macdhist"]
# 布林带 - 价格位置
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
dataframe["bb_lowerband"] = bollinger["lower"]
dataframe["bb_middleband"] = bollinger["mid"]
dataframe["bb_upperband"] = bollinger["upper"]
dataframe["bb_percent"] = (
(dataframe["close"] - dataframe["bb_lowerband"]) /
(dataframe["bb_upperband"] - dataframe["bb_lowerband"])
)
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
入场条件:均线金叉 + 成交量放大
"""
# 基础条件
base_conditions = (
(dataframe["volume"] > 0) &
(dataframe["ema_fast"].notna()) &
(dataframe["ema_slow"].notna()) &
(dataframe["volume_ma"].notna())
)
# 主要入场条件:均线金叉 - 放宽条件
ma_golden_cross = (
dataframe["ma_golden_cross"] | # 标准金叉
(dataframe["ema_fast"] > dataframe["ema_slow"] * 0.999) # 接近金叉
)
# 成交量确认 - 大幅放宽
volume_confirmation = (
dataframe["volume_surge"] | # 成交量放大
(dataframe["volume_ratio"] >= 1.1) # 成交量稍微放大即可
)
# 价格位置确认 - 大幅放宽
price_position = (
dataframe["price_above_fast_ma"] | # 价格在快线之上
(dataframe["close"] > dataframe["ema_slow"] * 0.95) | # 价格接近慢线
(dataframe["close"] > dataframe["ema_fast"] * 0.98) # 价格接近快线
)
# 趋势确认 - 大幅放宽
trend_confirmation = (
(dataframe["trend_strength"] >= 0) | # 任何趋势条件
(dataframe["ema_fast"] > dataframe["ema_fast"].shift(1)) | # 快线上升
(dataframe["ema_slow"] > dataframe["ema_slow"].shift(1)) | # 慢线上升
(dataframe["close"] > dataframe["close"].shift(1)) # 价格上升
)
# 辅助条件 - 大幅放宽
additional_conditions = (
(dataframe["rsi"] < 80) & # RSI不过度超买
(dataframe["bb_percent"] < 0.98) # 不在布林带上轨附近
)
# 综合入场条件 - 大幅简化
dataframe.loc[
base_conditions &
ma_golden_cross &
volume_confirmation &
price_position &
trend_confirmation &
additional_conditions,
"enter_long"
] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
出场条件:均线死叉或破重要支撑
"""
# 基础条件
base_conditions = (
(dataframe["volume"] > 0) &
(dataframe["ema_fast"].notna()) &
(dataframe["ema_slow"].notna())
)
# 主要出场条件1:均线死叉 - 非常严格的条件
ma_death_cross = (
dataframe["ma_death_cross"] & # 标准死叉
(dataframe["close"] < dataframe["ema_slow"] * 0.98) & # 价格明显低于慢线2%
(dataframe["ema_fast"] < dataframe["ema_fast"].shift(2)) # 快线持续下降
)
# 主要出场条件2:跌破重要支撑 - 非常严格的条件
support_break = (
(dataframe["close"] < dataframe["sma_support"] * 0.95) & # 跌破支撑5%
(dataframe["close"].shift(1) >= dataframe["sma_support"].shift(1) * 0.97) & # 之前接近支撑
(dataframe["ema_fast"] < dataframe["ema_fast"].shift(3)) & # 快线持续下降
(dataframe["volume_ratio"] < 0.8) # 成交量萎缩
)
# 趋势转弱 - 非常严格的条件
trend_weak = (
(dataframe["trend_strength"] <= 0) & # 趋势强度为0
(dataframe["ema_fast"] < dataframe["ema_fast"].shift(3)) & # 快线持续下降
(dataframe["close"] < dataframe["close"].shift(2)) & # 价格持续下降
(dataframe["volume_ratio"] < 0.9) # 成交量萎缩
)
# 价格跌破快线 - 非常严格的条件
price_break_fast = (
(dataframe["close"] < dataframe["ema_fast"] * 0.95) & # 价格跌破快线5%
(dataframe["close"].shift(1) >= dataframe["ema_fast"].shift(1) * 0.98) & # 之前接近快线
(dataframe["ema_fast"] < dataframe["ema_fast"].shift(2)) & # 快线持续下降
(dataframe["volume_ratio"] < 0.8) # 成交量萎缩
)
# 综合出场条件 - 极其保守的出场,大幅减少小亏损
dataframe.loc[
base_conditions &
(ma_death_cross | support_break | trend_weak | price_break_fast),
"exit_long"
] = 1
return dataframe
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
current_rate: float, current_profit: float, **kwargs) -> float:
"""
智能动态止损 - 减少噪音止损,增加确认条件
"""
# 获取当前数据
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if len(dataframe) == 0:
return self.stoploss
last_candle = dataframe.iloc[-1].squeeze()
# 获取关键指标
trend_strength = last_candle.get("trend_strength", 0)
support_strength = last_candle.get("support_strength", 0)
volume_ratio = last_candle.get("volume_ratio", 1.0)
ema_fast = last_candle.get("ema_fast", 0)
ema_slow = last_candle.get("ema_slow", 0)
current_price = last_candle.get("close", 0)
# 计算持仓时间
trade_duration = (current_time - trade.open_date_utc).total_seconds() / 3600 # 小时
# 如果有利润,保护利润
if current_profit > 0.03: # 有3%以上利润
return -0.015 # 1.5%止损(保护大部分利润)
elif current_profit > 0.02: # 有2%以上利润
return -0.02 # 2%止损
elif current_profit > 0.01: # 有1%以上利润
return -0.025 # 2.5%止损
# 根据趋势强度和确认条件调整止损
if (trend_strength >= 2 and
support_strength >= 1 and
current_price > ema_fast and
volume_ratio >= 0.8): # 趋势强且确认
return -0.06 # 6%止损(趋势强,放宽止损)
elif (trend_strength >= 1 and
current_price > ema_slow and
volume_ratio >= 0.7): # 趋势中等且确认
return -0.07 # 7%止损(趋势中等)
elif trade_duration > 24: # 持仓超过24小时
return -0.05 # 5%止损(长期持仓收紧)
else:
return self.stoploss # -0.08 (8%止损)
def custom_exit(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float,
current_profit: float, **kwargs) -> Optional[Union[str, bool]]:
"""
自定义退出逻辑 - 确保3%利润目标
"""
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if len(dataframe) == 0:
return None
last_candle = dataframe.iloc[-1].squeeze()
# 获取关键指标
ema_fast = last_candle.get("ema_fast", 0)
ema_slow = last_candle.get("ema_slow", 0)
sma_support = last_candle.get("sma_support", 0)
current_price = last_candle.get("close", 0)
trend_strength = last_candle.get("trend_strength", 0)
volume_ratio = last_candle.get("volume_ratio", 1.0)
rsi = last_candle.get("rsi", 50)
# 计算持仓时间
trade_duration = (current_time - trade.open_date_utc).total_seconds() / 3600 # 小时
# 如果达到3%利润目标,立即退出
if current_profit >= 0.03:
return "profit_target_3%"
# 如果接近3%目标且有强趋势,等待更高利润
if (current_profit >= 0.025 and trend_strength >= 2 and
current_price > ema_fast and current_price > ema_slow and
volume_ratio >= 0.8): # 增加成交量确认
# 继续持有,等待更高利润
pass
# 如果达到2.5%且有利润,考虑退出
elif current_profit >= 0.025:
# 检查是否应该退出(增加确认条件)
if (trend_strength <= 1 and
current_price < ema_fast * 0.995 and
volume_ratio < 0.8): # 趋势弱+价格跌破+成交量萎缩
return "time_near_target_exit"
# 如果达到2%利润且趋势转弱,退出
elif (current_profit >= 0.02 and trend_strength <= 0 and
current_price < ema_slow * 0.98): # 增加价格确认
return "trend_weak_exit"
# 如果均线死叉且有利润,立即退出(增加确认)
elif (last_candle.get("ma_death_cross", False) and
current_profit > 0.015 and
current_price < ema_slow * 0.99 and
volume_ratio < 0.9): # 死叉+利润+价格确认+成交量确认
return "ma_death_cross_exit"
# 如果跌破支撑且有利润,立即退出(增加确认)
elif (current_price < sma_support * 0.97 and # 更严格的支撑跌破
current_profit > 0.01 and
volume_ratio < 0.8 and # 成交量萎缩确认
trend_strength <= 0): # 趋势转弱确认
return "support_break_exit"
# 如果价格跌破快线且有利润,退出(增加确认)
elif (current_price < ema_fast * 0.97 and # 更严格的价格跌破
current_profit > 0.015 and
volume_ratio < 0.8 and # 成交量萎缩确认
rsi < 40): # RSI超卖确认
return "price_break_fast_exit"
# 如果持仓时间过长且有利润,退出
elif (current_profit > 0.01 and trade_duration > 48): # 48小时
return "time_profit_exit"
# 如果持仓时间过长且接近目标,退出
elif (current_profit > 0.02 and trade_duration > 24): # 24小时
return "time_near_target_exit"
# 如果连续亏损且趋势转弱,强制退出
elif (current_profit < -0.02 and
trend_strength <= 0 and
current_price < ema_slow * 0.95 and
trade_duration > 12): # 12小时
return "force_exit_loss"
return None
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: Optional[float], max_stake: float,
leverage: float, entry_tag: Optional[str], side: str,
**kwargs) -> float:
"""
动态仓位调整 - 根据信号强度调整仓位
"""
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if len(dataframe) == 0:
return proposed_stake
last_candle = dataframe.iloc[-1].squeeze()
# 获取信号强度指标
trend_strength = last_candle.get("trend_strength", 0)
volume_ratio = last_candle.get("volume_ratio", 1.0)
support_strength = last_candle.get("support_strength", 0)
# 计算信号强度分数
signal_strength = (
trend_strength * 0.4 + # 趋势强度权重40%
min(volume_ratio, 3.0) * 0.3 + # 成交量权重30%
support_strength * 0.3 # 支撑强度权重30%
)
# 根据信号强度调整仓位
if signal_strength >= 2.5: # 强信号
return proposed_stake * 1.5 # 增加50%仓位
elif signal_strength >= 2.0: # 中等信号
return proposed_stake * 1.2 # 增加20%仓位
else: # 弱信号
return proposed_stake * 0.8 # 减少20%仓位
def adjust_trade_position(self, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float,
min_stake: Optional[float], max_stake: float,
current_entry_rate: float, current_exit_rate: float,
current_entry_profit: float, current_exit_profit: float,
**kwargs) -> Optional[float]:
"""
加仓逻辑 - 对高胜率单进行加仓
"""
dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
if len(dataframe) == 0:
return None
last_candle = dataframe.iloc[-1].squeeze()
# 获取当前指标
trend_strength = last_candle.get("trend_strength", 0)
volume_ratio = last_candle.get("volume_ratio", 1.0)
ema_fast = last_candle.get("ema_fast", 0)
ema_slow = last_candle.get("ema_slow", 0)
current_price = last_candle.get("close", 0)
# 加仓条件1:有利润且趋势强劲
if (current_profit > 0.01 and # 有1%以上利润
trend_strength >= 2 and # 趋势强劲
current_price > ema_fast and # 价格在快线之上
current_price > ema_slow and # 价格在慢线之上
volume_ratio >= 1.2): # 成交量放大
# 计算加仓金额(不超过原仓位的50%)
stake_amount = trade.stake_amount * 0.5
return stake_amount
# 加仓条件2:接近利润目标且趋势持续
elif (current_profit > 0.02 and # 有2%以上利润
trend_strength >= 1 and # 趋势持续
current_price > ema_fast * 0.99 and # 价格接近快线
volume_ratio >= 1.1): # 成交量稍微放大
# 计算加仓金额(不超过原仓位的30%)
stake_amount = trade.stake_amount * 0.3
return stake_amount
return None
def leverage(self, pair: str, current_time: datetime, current_rate: float,
proposed_leverage: float, max_leverage: float, entry_tag: Optional[str],
side: str, **kwargs) -> float:
"""
杠杆设置 - 保守的杠杆使用
"""
# 基于均线策略,使用较低的杠杆
return min(proposed_leverage, 2.0) # 最大2倍杠杆