125 lines
3.8 KiB
Python
125 lines
3.8 KiB
Python
"""
|
|
SOL15mStrategy - 基于15m时间框架的SOL/USDT期货策略
|
|
|
|
核心逻辑:
|
|
- 15m EMA26/EMA52 交叉做多做空
|
|
- RSI过滤:做多要求RSI<65,做空要求RSI>35(避免超买超卖区入场)
|
|
- 使用 trailing_stop 让利润奔跑
|
|
- 宽止损(2%),给交易足够呼吸空间
|
|
|
|
使用命令:
|
|
freqtrade backtesting -c ./user_data/Chan/config/Local_Test.json --strategy SOL15mStrategy --strategy-path ./user_data/Chan/strategies --timerange=20250301-
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
import talib.abstract as ta
|
|
from pandas import DataFrame
|
|
|
|
from freqtrade.persistence import Trade
|
|
from freqtrade.strategy import IStrategy
|
|
|
|
|
|
class SOL15mStrategy(IStrategy):
|
|
INTERFACE_VERSION: int = 3
|
|
|
|
# === 基础配置 ===
|
|
timeframe = "15m"
|
|
can_short = True
|
|
startup_candle_count: int = 200
|
|
|
|
# 止损 2%
|
|
stoploss = -0.02
|
|
use_custom_stoploss = False
|
|
|
|
# trailing stop: 利润达到1.5%后开始追踪,回撤0.5%止盈
|
|
trailing_stop = True
|
|
trailing_stop_positive = 0.005
|
|
trailing_stop_positive_offset = 0.015
|
|
trailing_only_offset_is_reached = True
|
|
|
|
# ROI: 阶梯式止盈
|
|
minimal_roi = {
|
|
"0": 0.04, # 4%直接止盈
|
|
"60": 0.025, # 60分钟后 2.5%
|
|
"180": 0.015, # 3小时后 1.5%
|
|
"480": 0.005, # 8小时后 0.5%
|
|
"720": 0, # 12小时后保本退出
|
|
}
|
|
|
|
order_types = {
|
|
"entry": "market",
|
|
"exit": "market",
|
|
"stoploss": "market",
|
|
"stoploss_on_exchange": False,
|
|
}
|
|
|
|
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
# EMA
|
|
dataframe["ema26"] = ta.EMA(dataframe, timeperiod=26)
|
|
dataframe["ema52"] = ta.EMA(dataframe, timeperiod=52)
|
|
dataframe["ema100"] = ta.EMA(dataframe, timeperiod=100)
|
|
|
|
# RSI
|
|
dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
|
|
|
|
# MACD
|
|
macd = ta.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9)
|
|
dataframe["macd"] = macd["macd"]
|
|
dataframe["macdsignal"] = macd["macdsignal"]
|
|
|
|
# EMA26上穿EMA52
|
|
dataframe["ema26_cross_up_52"] = (
|
|
(dataframe["ema26"] > dataframe["ema52"])
|
|
& (dataframe["ema26"].shift(1) <= dataframe["ema52"].shift(1))
|
|
)
|
|
|
|
# EMA26下穿EMA52
|
|
dataframe["ema26_cross_dn_52"] = (
|
|
(dataframe["ema26"] < dataframe["ema52"])
|
|
& (dataframe["ema26"].shift(1) >= dataframe["ema52"].shift(1))
|
|
)
|
|
|
|
# MACD死叉
|
|
dataframe["macd_cross_dn"] = (
|
|
(dataframe["macd"] < dataframe["macdsignal"])
|
|
& (dataframe["macd"].shift(1) >= dataframe["macdsignal"].shift(1))
|
|
)
|
|
|
|
return dataframe
|
|
|
|
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
# 做多: EMA26上穿EMA52 + RSI < 65 (不在超买区)
|
|
dataframe.loc[
|
|
(dataframe["ema26_cross_up_52"])
|
|
& (dataframe["rsi"] < 65),
|
|
["enter_long", "enter_tag"],
|
|
] = (1, "ema26x52_long")
|
|
|
|
# 做空: EMA26下穿EMA52 + RSI > 35 (不在超卖区)
|
|
dataframe.loc[
|
|
(dataframe["ema26_cross_dn_52"])
|
|
& (dataframe["rsi"] > 35),
|
|
["enter_short", "enter_tag"],
|
|
] = (1, "ema26x52_short")
|
|
|
|
return dataframe
|
|
|
|
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
# 不使用信号退出,完全由 trailing_stop + ROI + stoploss 控制
|
|
return dataframe
|
|
|
|
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 1.0
|