添加策略
This commit is contained in:
@@ -36,23 +36,26 @@ import talib.abstract as ta
|
||||
from technical import qtpylib
|
||||
|
||||
|
||||
# This class is a sample. Feel free to customize it.
|
||||
class SampleStrategy(IStrategy):
|
||||
# 基于x.py策略的MA60/MA120趋势跟踪策略
|
||||
class MA60MA120TrendStrategy(IStrategy):
|
||||
"""
|
||||
This is a sample strategy to inspire you.
|
||||
More information in https://www.freqtrade.io/en/latest/strategy-customization/
|
||||
|
||||
You can:
|
||||
:return: a Dataframe with all mandatory indicators for the strategies
|
||||
- Rename the class name (Do not forget to update class_name)
|
||||
- Add any methods you want to build your strategy
|
||||
- Add any lib you need to build your strategy
|
||||
|
||||
You must keep:
|
||||
- the lib in the section "Do not remove these libs"
|
||||
- the methods: populate_indicators, populate_entry_trend, populate_exit_trend
|
||||
You should keep:
|
||||
- timeframe, minimal_roi, stoploss, trailing_*
|
||||
基于x.py策略的MA60/MA120趋势跟踪策略
|
||||
|
||||
策略逻辑:
|
||||
1. 买入条件:
|
||||
- 价格在MA60上方且距离MA60不超过10%
|
||||
- MA60呈上升趋势(过去5天中至少3天上升)
|
||||
- 成交量放大(当日成交量大于过去5日平均)
|
||||
- 前5天中下跌天数不超过2天
|
||||
- 当天收盘价大于5天前的价格
|
||||
- 价格在MA120上方
|
||||
- MA120过去5天不是下降趋势
|
||||
- RSI小于阈值(默认70)
|
||||
|
||||
2. 卖出条件:
|
||||
- 价格跌破MA60且跌幅超过10%
|
||||
|
||||
更多信息: https://www.freqtrade.io/en/latest/strategy-customization/
|
||||
"""
|
||||
|
||||
# Strategy interface version - allow new iterations of the strategy interface.
|
||||
@@ -63,26 +66,25 @@ class SampleStrategy(IStrategy):
|
||||
can_short: bool = False
|
||||
|
||||
# Minimal ROI designed for the strategy.
|
||||
# This attribute will be overridden if the config file contains "minimal_roi".
|
||||
minimal_roi = {
|
||||
# "120": 0.0, # exit after 120 minutes at break even
|
||||
"60": 0.01,
|
||||
"30": 0.02,
|
||||
"0": 0.04,
|
||||
}
|
||||
# 基于x.py策略,使用更保守的ROI设置
|
||||
# minimal_roi = {
|
||||
# "30": 0.3, # 20% 目标收益
|
||||
# # ""
|
||||
# }
|
||||
|
||||
# Optimal stoploss designed for the strategy.
|
||||
# This attribute will be overridden if the config file contains "stoploss".
|
||||
stoploss = -0.10
|
||||
# 基于x.py策略的卖出条件:跌破MA60且跌幅超过10%
|
||||
stoploss = -1
|
||||
|
||||
# Trailing stoploss
|
||||
trailing_stop = False
|
||||
# trailing_stop = True
|
||||
# trailing_only_offset_is_reached = False
|
||||
# trailing_stop_positive = 0.01
|
||||
# trailing_stop_positive = 0.3
|
||||
# trailing_stop_positive_offset = 0.0 # Disabled / not configured
|
||||
|
||||
# Optimal timeframe for the strategy.
|
||||
timeframe = "5m"
|
||||
# 使用日线数据,因为x.py策略基于日线
|
||||
timeframe = "1d"
|
||||
|
||||
# Run "populate_indicators()" only for new candle.
|
||||
process_only_new_candles = True
|
||||
@@ -92,13 +94,15 @@ class SampleStrategy(IStrategy):
|
||||
exit_profit_only = False
|
||||
ignore_roi_if_entry_signal = False
|
||||
|
||||
# Hyperoptable parameters
|
||||
buy_rsi = IntParameter(low=1, high=50, default=30, space="buy", optimize=True, load=True)
|
||||
sell_rsi = IntParameter(low=50, high=100, default=70, space="sell", optimize=True, load=True)
|
||||
short_rsi = IntParameter(low=51, high=100, default=70, space="sell", optimize=True, load=True)
|
||||
exit_short_rsi = IntParameter(low=1, high=50, default=30, space="buy", optimize=True, load=True)
|
||||
# 移除原有的RSI参数,因为新策略使用固定值
|
||||
# 可以添加其他可优化的参数
|
||||
ma60_period = IntParameter(low=30, high=90, default=60, space="buy", optimize=True, load=True)
|
||||
ma120_period = IntParameter(low=60, high=180, default=120, space="buy", optimize=True, load=True)
|
||||
rsi_threshold = IntParameter(low=50, high=80, default=70, space="buy", optimize=True, load=True)
|
||||
volume_surge_multiplier = DecimalParameter(low=0.5, high=2.0, default=1.0, space="buy", optimize=True, load=True)
|
||||
|
||||
# Number of candles the strategy requires before producing valid signals
|
||||
# 需要更多数据来计算MA120和趋势指标
|
||||
startup_candle_count: int = 200
|
||||
|
||||
# Optional order type mapping.
|
||||
@@ -114,16 +118,21 @@ class SampleStrategy(IStrategy):
|
||||
|
||||
plot_config = {
|
||||
"main_plot": {
|
||||
"tema": {},
|
||||
"sar": {"color": "white"},
|
||||
"ma60": {"color": "blue", "width": 2},
|
||||
"ma120": {"color": "orange", "width": 2},
|
||||
},
|
||||
"subplots": {
|
||||
"MACD": {
|
||||
"macd": {"color": "blue"},
|
||||
"macdsignal": {"color": "orange"},
|
||||
},
|
||||
"RSI": {
|
||||
"rsi": {"color": "red"},
|
||||
"rsi_threshold": {"color": "gray", "type": "line", "width": 1},
|
||||
},
|
||||
"Volume": {
|
||||
"volume": {"color": "lightblue"},
|
||||
"volume_5ma": {"color": "blue"},
|
||||
},
|
||||
"Signals": {
|
||||
"buy_signal": {"color": "green", "type": "scatter", "marker": "^"},
|
||||
"sell_signal": {"color": "red", "type": "scatter", "marker": "v"},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -143,284 +152,211 @@ class SampleStrategy(IStrategy):
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
Adds several different TA indicators to the given DataFrame
|
||||
|
||||
Performance Note: For the best performance be frugal on the number of indicators
|
||||
you are using. Let uncomment only the indicator you are using in your strategies
|
||||
or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
|
||||
基于x.py策略的技术指标计算
|
||||
实现MA60/MA120趋势跟踪策略的所有必要指标
|
||||
:param dataframe: Dataframe with data from the exchange
|
||||
:param metadata: Additional information, like the currently traded pair
|
||||
:return: a Dataframe with all mandatory indicators for the strategies
|
||||
"""
|
||||
|
||||
# Momentum Indicators
|
||||
# 基础移动平均线指标
|
||||
# ------------------------------------
|
||||
|
||||
# MA60 - 使用可优化参数
|
||||
dataframe['ma60'] = ta.SMA(dataframe, timeperiod=60)
|
||||
|
||||
# MA120 - 使用可优化参数
|
||||
dataframe['ma120'] = ta.SMA(dataframe, timeperiod=120)
|
||||
|
||||
# RSI指标
|
||||
dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
|
||||
|
||||
# 成交量相关指标
|
||||
# ------------------------------------
|
||||
|
||||
# 成交量5日移动平均
|
||||
dataframe['volume_5ma'] = dataframe['volume'].rolling(window=5).mean()
|
||||
|
||||
# 成交量放大信号:当日成交量大于过去5日平均的倍数
|
||||
dataframe['volume_surge'] = dataframe['volume'] > (dataframe['volume_5ma'] * 1)
|
||||
|
||||
# 价格与MA60关系指标
|
||||
# ------------------------------------
|
||||
|
||||
# ADX
|
||||
dataframe["adx"] = ta.ADX(dataframe)
|
||||
|
||||
# # Plus Directional Indicator / Movement
|
||||
# dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
|
||||
# dataframe['plus_di'] = ta.PLUS_DI(dataframe)
|
||||
|
||||
# # Minus Directional Indicator / Movement
|
||||
# dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
|
||||
# dataframe['minus_di'] = ta.MINUS_DI(dataframe)
|
||||
|
||||
# # Aroon, Aroon Oscillator
|
||||
# aroon = ta.AROON(dataframe)
|
||||
# dataframe['aroonup'] = aroon['aroonup']
|
||||
# dataframe['aroondown'] = aroon['aroondown']
|
||||
# dataframe['aroonosc'] = ta.AROONOSC(dataframe)
|
||||
|
||||
# # Awesome Oscillator
|
||||
# dataframe['ao'] = qtpylib.awesome_oscillator(dataframe)
|
||||
|
||||
# # Keltner Channel
|
||||
# keltner = qtpylib.keltner_channel(dataframe)
|
||||
# dataframe["kc_upperband"] = keltner["upper"]
|
||||
# dataframe["kc_lowerband"] = keltner["lower"]
|
||||
# dataframe["kc_middleband"] = keltner["mid"]
|
||||
# dataframe["kc_percent"] = (
|
||||
# (dataframe["close"] - dataframe["kc_lowerband"]) /
|
||||
# (dataframe["kc_upperband"] - dataframe["kc_lowerband"])
|
||||
# )
|
||||
# dataframe["kc_width"] = (
|
||||
# (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) / dataframe["kc_middleband"]
|
||||
# )
|
||||
|
||||
# # Ultimate Oscillator
|
||||
# dataframe['uo'] = ta.ULTOSC(dataframe)
|
||||
|
||||
# # Commodity Channel Index: values [Oversold:-100, Overbought:100]
|
||||
# dataframe['cci'] = ta.CCI(dataframe)
|
||||
|
||||
# RSI
|
||||
dataframe["rsi"] = ta.RSI(dataframe)
|
||||
|
||||
# # Inverse Fisher transform on RSI: values [-1.0, 1.0] (https://goo.gl/2JGGoy)
|
||||
# rsi = 0.1 * (dataframe['rsi'] - 50)
|
||||
# dataframe['fisher_rsi'] = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1)
|
||||
|
||||
# # Inverse Fisher transform on RSI normalized: values [0.0, 100.0] (https://goo.gl/2JGGoy)
|
||||
# dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1)
|
||||
|
||||
# # Stochastic Slow
|
||||
# stoch = ta.STOCH(dataframe)
|
||||
# dataframe['slowd'] = stoch['slowd']
|
||||
# dataframe['slowk'] = stoch['slowk']
|
||||
|
||||
# Stochastic Fast
|
||||
stoch_fast = ta.STOCHF(dataframe)
|
||||
dataframe["fastd"] = stoch_fast["fastd"]
|
||||
dataframe["fastk"] = stoch_fast["fastk"]
|
||||
|
||||
# # Stochastic RSI
|
||||
# Please read https://github.com/freqtrade/freqtrade/issues/2961 before using this.
|
||||
# STOCHRSI is NOT aligned with tradingview, which may result in non-expected results.
|
||||
# stoch_rsi = ta.STOCHRSI(dataframe)
|
||||
# dataframe['fastd_rsi'] = stoch_rsi['fastd']
|
||||
# dataframe['fastk_rsi'] = stoch_rsi['fastk']
|
||||
|
||||
# MACD
|
||||
macd = ta.MACD(dataframe)
|
||||
dataframe["macd"] = macd["macd"]
|
||||
dataframe["macdsignal"] = macd["macdsignal"]
|
||||
dataframe["macdhist"] = macd["macdhist"]
|
||||
|
||||
# MFI
|
||||
dataframe["mfi"] = ta.MFI(dataframe)
|
||||
|
||||
# # ROC
|
||||
# dataframe['roc'] = ta.ROC(dataframe)
|
||||
|
||||
# Overlap Studies
|
||||
# ------------------------------------
|
||||
|
||||
# Bollinger Bands
|
||||
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"]
|
||||
# 价格在MA60上方且距离MA60不超过10%
|
||||
dataframe['price_above_ma60'] = (
|
||||
(dataframe['close'] > dataframe['ma60']) &
|
||||
((dataframe['ma60'] - dataframe['close']) / dataframe['close'] < 0.1)
|
||||
)
|
||||
dataframe["bb_width"] = (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe[
|
||||
"bb_middleband"
|
||||
]
|
||||
|
||||
# Bollinger Bands - Weighted (EMA based instead of SMA)
|
||||
# weighted_bollinger = qtpylib.weighted_bollinger_bands(
|
||||
# qtpylib.typical_price(dataframe), window=20, stds=2
|
||||
# )
|
||||
# dataframe["wbb_upperband"] = weighted_bollinger["upper"]
|
||||
# dataframe["wbb_lowerband"] = weighted_bollinger["lower"]
|
||||
# dataframe["wbb_middleband"] = weighted_bollinger["mid"]
|
||||
# dataframe["wbb_percent"] = (
|
||||
# (dataframe["close"] - dataframe["wbb_lowerband"]) /
|
||||
# (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"])
|
||||
# )
|
||||
# dataframe["wbb_width"] = (
|
||||
# (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) /
|
||||
# dataframe["wbb_middleband"]
|
||||
# )
|
||||
|
||||
# # EMA - Exponential Moving Average
|
||||
# dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)
|
||||
# dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
|
||||
# dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
|
||||
# dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21)
|
||||
# dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
|
||||
# dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)
|
||||
|
||||
# # SMA - Simple Moving Average
|
||||
# dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3)
|
||||
# dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5)
|
||||
# dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10)
|
||||
# dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21)
|
||||
# dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50)
|
||||
# dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100)
|
||||
|
||||
# Parabolic SAR
|
||||
dataframe["sar"] = ta.SAR(dataframe)
|
||||
|
||||
# TEMA - Triple Exponential Moving Average
|
||||
dataframe["tema"] = ta.TEMA(dataframe, timeperiod=9)
|
||||
|
||||
# Cycle Indicator
|
||||
|
||||
# 价格在MA120上方
|
||||
dataframe['price_above_ma120'] = dataframe['close'] > dataframe['ma120']
|
||||
|
||||
# 前一天在MA60附近(95%-105%)
|
||||
dataframe['prev_near_ma'] = (
|
||||
(dataframe['close'].shift(1) >= dataframe['ma60'].shift(1) * 0.95) &
|
||||
(dataframe['close'].shift(1) <= dataframe['ma60'].shift(1) * 1.05)
|
||||
)
|
||||
|
||||
# 前一天在MA60下方
|
||||
dataframe['prev_below_ma'] = dataframe['close'].shift(1) < dataframe['ma60'].shift(1)
|
||||
|
||||
# 突破信号:前一天在MA60附近或下方,当天在MA60上方
|
||||
dataframe['breakthrough'] = (
|
||||
(dataframe['prev_below_ma'] | dataframe['prev_near_ma']) &
|
||||
dataframe['price_above_ma60']
|
||||
)
|
||||
|
||||
# MA60趋势指标
|
||||
# ------------------------------------
|
||||
# Hilbert Transform Indicator - SineWave
|
||||
hilbert = ta.HT_SINE(dataframe)
|
||||
dataframe["htsine"] = hilbert["sine"]
|
||||
dataframe["htleadsine"] = hilbert["leadsine"]
|
||||
|
||||
# Pattern Recognition - Bullish candlestick patterns
|
||||
|
||||
# 计算MA60的上升趋势(过去5天中至少3天上升)
|
||||
trend_days = 5
|
||||
ma_trend = []
|
||||
for i in range(len(dataframe)):
|
||||
if i < trend_days - 1:
|
||||
ma_trend.append(False)
|
||||
else:
|
||||
start_idx = i - trend_days + 1
|
||||
ma_values = dataframe['ma60'].iloc[start_idx:i+1].values
|
||||
if pd.isna(ma_values).any():
|
||||
ma_trend.append(False)
|
||||
else:
|
||||
# 检查是否呈上升趋势(至少3天上升)
|
||||
ma_trend.append(sum(ma_values[1:] > ma_values[:-1]) > trend_days - 3)
|
||||
dataframe['ma_uptrend'] = ma_trend
|
||||
|
||||
# MA120趋势指标
|
||||
# ------------------------------------
|
||||
# # Hammer: values [0, 100]
|
||||
# dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe)
|
||||
# # Inverted Hammer: values [0, 100]
|
||||
# dataframe['CDLINVERTEDHAMMER'] = ta.CDLINVERTEDHAMMER(dataframe)
|
||||
# # Dragonfly Doji: values [0, 100]
|
||||
# dataframe['CDLDRAGONFLYDOJI'] = ta.CDLDRAGONFLYDOJI(dataframe)
|
||||
# # Piercing Line: values [0, 100]
|
||||
# dataframe['CDLPIERCING'] = ta.CDLPIERCING(dataframe) # values [0, 100]
|
||||
# # Morningstar: values [0, 100]
|
||||
# dataframe['CDLMORNINGSTAR'] = ta.CDLMORNINGSTAR(dataframe) # values [0, 100]
|
||||
# # Three White Soldiers: values [0, 100]
|
||||
# dataframe['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(dataframe) # values [0, 100]
|
||||
|
||||
# Pattern Recognition - Bearish candlestick patterns
|
||||
|
||||
# 计算MA120的趋势(过去5天)不能为下降
|
||||
ma120_trend = []
|
||||
for i in range(len(dataframe)):
|
||||
if i < 4:
|
||||
ma120_trend.append(True) # 数据不足时不限制
|
||||
else:
|
||||
window = dataframe['ma120'].iloc[i-4:i+1].values
|
||||
if pd.isna(window).any():
|
||||
ma120_trend.append(True)
|
||||
else:
|
||||
# 只要有一天不是上升就不是下降趋势
|
||||
ma120_trend.append((window[1:] >= window[:-1]).all())
|
||||
dataframe['ma120_not_downtrend'] = ma120_trend
|
||||
|
||||
# 价格动量指标
|
||||
# ------------------------------------
|
||||
# # Hanging Man: values [0, 100]
|
||||
# dataframe['CDLHANGINGMAN'] = ta.CDLHANGINGMAN(dataframe)
|
||||
# # Shooting Star: values [0, 100]
|
||||
# dataframe['CDLSHOOTINGSTAR'] = ta.CDLSHOOTINGSTAR(dataframe)
|
||||
# # Gravestone Doji: values [0, 100]
|
||||
# dataframe['CDLGRAVESTONEDOJI'] = ta.CDLGRAVESTONEDOJI(dataframe)
|
||||
# # Dark Cloud Cover: values [0, 100]
|
||||
# dataframe['CDLDARKCLOUDCOVER'] = ta.CDLDARKCLOUDCOVER(dataframe)
|
||||
# # Evening Doji Star: values [0, 100]
|
||||
# dataframe['CDLEVENINGDOJISTAR'] = ta.CDLEVENINGDOJISTAR(dataframe)
|
||||
# # Evening Star: values [0, 100]
|
||||
# dataframe['CDLEVENINGSTAR'] = ta.CDLEVENINGSTAR(dataframe)
|
||||
|
||||
# Pattern Recognition - Bullish/Bearish candlestick patterns
|
||||
|
||||
# 阳线条件:收盘价 > 开盘价,且阳线幅度至少0.5%
|
||||
dataframe['is_bullish'] = (
|
||||
(dataframe['close'] > dataframe['open']) &
|
||||
((dataframe['close'] - dataframe['open']) / dataframe['open'] >= 0.005)
|
||||
)
|
||||
|
||||
# 前5天的下跌天数不超过2天
|
||||
dataframe['is_down_day'] = dataframe['close'] < dataframe['open']
|
||||
dataframe['down_days_in_5'] = dataframe['is_down_day'].rolling(window=5, min_periods=1).sum()
|
||||
dataframe['not_too_many_downs'] = dataframe['down_days_in_5'] <= 2
|
||||
|
||||
# 当天收盘价大于第五天前的价格
|
||||
dataframe['price_higher_than_5days_ago'] = dataframe['close'] > dataframe['close'].shift(5)
|
||||
|
||||
# RSI条件 - 使用可优化参数
|
||||
dataframe['rsi_lt_threshold'] = dataframe['rsi'] < 70
|
||||
|
||||
# 综合买入信号
|
||||
# ------------------------------------
|
||||
# # Three Line Strike: values [0, -100, 100]
|
||||
# dataframe['CDL3LINESTRIKE'] = ta.CDL3LINESTRIKE(dataframe)
|
||||
# # Spinning Top: values [0, -100, 100]
|
||||
# dataframe['CDLSPINNINGTOP'] = ta.CDLSPINNINGTOP(dataframe) # values [0, -100, 100]
|
||||
# # Engulfing: values [0, -100, 100]
|
||||
# dataframe['CDLENGULFING'] = ta.CDLENGULFING(dataframe) # values [0, -100, 100]
|
||||
# # Harami: values [0, -100, 100]
|
||||
# dataframe['CDLHARAMI'] = ta.CDLHARAMI(dataframe) # values [0, -100, 100]
|
||||
# # Three Outside Up/Down: values [0, -100, 100]
|
||||
# dataframe['CDL3OUTSIDE'] = ta.CDL3OUTSIDE(dataframe) # values [0, -100, 100]
|
||||
# # Three Inside Up/Down: values [0, -100, 100]
|
||||
# dataframe['CDL3INSIDE'] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100]
|
||||
|
||||
# # Chart type
|
||||
# # ------------------------------------
|
||||
# # Heikin Ashi Strategy
|
||||
# heikinashi = qtpylib.heikinashi(dataframe)
|
||||
# dataframe['ha_open'] = heikinashi['open']
|
||||
# dataframe['ha_close'] = heikinashi['close']
|
||||
# dataframe['ha_high'] = heikinashi['high']
|
||||
# dataframe['ha_low'] = heikinashi['low']
|
||||
|
||||
# Retrieve best bid and best ask from the orderbook
|
||||
# ------------------------------------
|
||||
"""
|
||||
# first check if dataprovider is available
|
||||
if self.dp:
|
||||
if self.dp.runmode.value in ('live', 'dry_run'):
|
||||
ob = self.dp.orderbook(metadata['pair'], 1)
|
||||
dataframe['best_bid'] = ob['bids'][0][0]
|
||||
dataframe['best_ask'] = ob['asks'][0][0]
|
||||
"""
|
||||
|
||||
dataframe["buy_signal"] = (
|
||||
dataframe["price_above_ma60"] &
|
||||
dataframe["ma_uptrend"] &
|
||||
dataframe['ma60'].notna() &
|
||||
dataframe["volume_surge"] &
|
||||
dataframe["not_too_many_downs"] &
|
||||
dataframe["price_higher_than_5days_ago"] &
|
||||
dataframe['price_above_ma120'] &
|
||||
dataframe['ma120_not_downtrend']
|
||||
# &
|
||||
# dataframe['rsi_lt_threshold']
|
||||
)
|
||||
|
||||
# 卖出信号:价格跌破MA60且跌幅超过10%
|
||||
dataframe['sell_signal'] = (
|
||||
(~dataframe['price_above_ma60']) &
|
||||
((dataframe['ma60'] - dataframe['close']) / dataframe['ma60'] > 0.1) &
|
||||
dataframe['ma60'].notna()
|
||||
)
|
||||
|
||||
return dataframe
|
||||
|
||||
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
Based on TA indicators, populates the entry signal for the given dataframe
|
||||
基于x.py策略的买入信号逻辑
|
||||
实现MA60/MA120趋势跟踪策略的买入条件
|
||||
:param dataframe: DataFrame
|
||||
:param metadata: Additional information, like the currently traded pair
|
||||
:return: DataFrame with entry columns populated
|
||||
"""
|
||||
# 多头买入信号 - 基于x.py的buy_signal逻辑
|
||||
dataframe.loc[
|
||||
(
|
||||
# Signal: RSI crosses above 30
|
||||
(qtpylib.crossed_above(dataframe["rsi"], self.buy_rsi.value))
|
||||
& (dataframe["tema"] <= dataframe["bb_middleband"]) # Guard: tema below BB middle
|
||||
& (dataframe["tema"] > dataframe["tema"].shift(1)) # Guard: tema is raising
|
||||
& (dataframe["volume"] > 0) # Make sure Volume is not 0
|
||||
# 价格在MA60上方且距离MA60不超过10%
|
||||
dataframe["price_above_ma60"] &
|
||||
# MA60呈上升趋势(过去5天中至少3天上升)
|
||||
dataframe["ma_uptrend"] &
|
||||
# MA60数据有效
|
||||
dataframe['ma60'].notna() &
|
||||
# 成交量放大(当日成交量大于过去5日平均)
|
||||
dataframe["volume_surge"] &
|
||||
# 前5天中下跌天数不超过2天
|
||||
dataframe["not_too_many_downs"] &
|
||||
# 当天收盘价大于5天前的价格
|
||||
dataframe["price_higher_than_5days_ago"] &
|
||||
# 价格在MA120上方
|
||||
dataframe['price_above_ma120'] &
|
||||
# MA120过去5天不是下降趋势
|
||||
dataframe['ma120_not_downtrend'] &
|
||||
# RSI小于阈值
|
||||
dataframe['rsi_lt_threshold'] &
|
||||
# 确保成交量不为0
|
||||
(dataframe["volume"] > 0)
|
||||
),
|
||||
"enter_long",
|
||||
] = 1
|
||||
|
||||
dataframe.loc[
|
||||
(
|
||||
# Signal: RSI crosses above 70
|
||||
(qtpylib.crossed_above(dataframe["rsi"], self.short_rsi.value))
|
||||
& (dataframe["tema"] > dataframe["bb_middleband"]) # Guard: tema above BB middle
|
||||
& (dataframe["tema"] < dataframe["tema"].shift(1)) # Guard: tema is falling
|
||||
& (dataframe["volume"] > 0) # Make sure Volume is not 0
|
||||
),
|
||||
"enter_short",
|
||||
] = 1
|
||||
|
||||
return dataframe
|
||||
|
||||
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
Based on TA indicators, populates the exit signal for the given dataframe
|
||||
基于x.py策略的卖出信号逻辑
|
||||
实现MA60/MA120趋势跟踪策略的卖出条件
|
||||
:param dataframe: DataFrame
|
||||
:param metadata: Additional information, like the currently traded pair
|
||||
:return: DataFrame with exit columns populated
|
||||
"""
|
||||
# 多头卖出信号 - 基于x.py的sell_signal逻辑
|
||||
# dataframe.loc[
|
||||
# (
|
||||
# # 价格跌破MA60且跌幅超过10%
|
||||
# (~dataframe['price_above_ma60']) &
|
||||
# ((dataframe['ma60'] - dataframe['close']) / dataframe['ma60'] > 0.1) &
|
||||
# dataframe['ma60'].notna() &
|
||||
# # 确保成交量不为0
|
||||
# (dataframe["volume"] > 0)
|
||||
# ),
|
||||
# "exit_long",
|
||||
# ] = 1
|
||||
|
||||
dataframe.loc[
|
||||
(
|
||||
# Signal: RSI crosses above 70
|
||||
(qtpylib.crossed_above(dataframe["rsi"], self.sell_rsi.value))
|
||||
& (dataframe["tema"] > dataframe["bb_middleband"]) # Guard: tema above BB middle
|
||||
& (dataframe["tema"] < dataframe["tema"].shift(1)) # Guard: tema is falling
|
||||
& (dataframe["volume"] > 0) # Make sure Volume is not 0
|
||||
# 价格跌破MA60且跌幅超过10%
|
||||
(dataframe['close'] < dataframe['ma120']) &
|
||||
dataframe['ma120'].notna() &
|
||||
# 确保成交量不为0
|
||||
(dataframe["volume"] > 0)
|
||||
),
|
||||
"exit_long",
|
||||
] = 1
|
||||
|
||||
dataframe.loc[
|
||||
(
|
||||
# Signal: RSI crosses above 30
|
||||
(qtpylib.crossed_above(dataframe["rsi"], self.exit_short_rsi.value))
|
||||
&
|
||||
# Guard: tema below BB middle
|
||||
(dataframe["tema"] <= dataframe["bb_middleband"])
|
||||
& (dataframe["tema"] > dataframe["tema"].shift(1)) # Guard: tema is raising
|
||||
& (dataframe["volume"] > 0) # Make sure Volume is not 0
|
||||
),
|
||||
"exit_short",
|
||||
] = 1
|
||||
|
||||
return dataframe
|
||||
|
||||
Reference in New Issue
Block a user