Files
Chan/strategies/HeikinAshi_BTC.py
T

147 lines
6.3 KiB
Python

# --- Do not remove these libs ---
from statistics import median
from freqtrade.strategy import IStrategy
from technical.util import resample_to_interval, resampled_merge
from pandas import DataFrame
import talib.abstract as ta
from technical import qtpylib
### Now you can use logger.info('asfd') to log
# freqtrade plot-dataframe --strategy HeikinAshi_BTC --datadir user_data/data/binance -c ./user_data/Chan/HeikinAshi_BTC.json --timerange=20250309-
# freqtrade backtesting -c ./user_data/Chan/config/HeikinAshi_BTC.json --strategy HeikinAshi_BTC --strategy-path ./user_data/Chan/strategies --timerange=20251008-
# freqtrade download-data -c ./user_data/Chan/config/HeikinAshi_BTC.json -t 1m 1m 1h 1d 1M --pairs BTC/USDT:USDT --timerange=20250405-
# freqtrade download-data -c ./user_data/Chan/config/HeikinAshi_BTC.json -t 1m 1h 1d 1M --pairs BTC/USDT --timerange=20170101-
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi --strategy HeikinAshi_BTC --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/HeikinAshi_BTC.json -e 200 --timerange=20250201-20250901
# freqtrade edge -c ./user_data/Chan/config/HeikinAshi_BTC.json --strategy HeikinAshi_BTC --strategy-path ./user_data/Chan/strategies --timerange 20250721-20250901
# freqtrade plot-dataframe -c ./user_data/Chan/config/HeikinAshi_BTC.json --strategy HeikinAshi_BTC --strategy-path ./user_data/Chan/strategies --timerange 20250721-20250901
# sudo docker compose run --rm heikinashi_btc backtesting -c ./user_data/Chan/config/HeikinAshi_BTC.json --strategy HeikinAshi_BTC --strategy-path ./user_data/Chan/strategies --timerange=20250721-
# sudo docker compose run --rm heikinashi_btc download-data -c ./user_data/Chan/config/HeikinAshi_BTC.json --pairs BTC/USDT:USDT -t 1m --timerange 20240101-
# sudo docker compose run --rm heikinashi_btc trade -c ./user_data/Chan/config/HeikinAshi_BTC.json --strategy HeikinAshi_BTC --strategy-path ./user_data/Chan/strategies
class HeikinAshi_BTC(IStrategy):
"""
使用 Heikin Ashi 蜡烛的双均线趋势策略,支持多/空。
逻辑摘要:
- 指标:ha_open/ha_close/ha_high/ha_low + ha_close 的快/慢 EMA
- 做多:ha_close > ha_open 且 ha_ema_fast > ha_ema_slow
- 做空:ha_close < ha_open 且 ha_ema_fast < ha_ema_slow
- 退出:相反信号或均线反转
"""
INTERFACE_VERSION: int = 3
time1h = 1440
can_short: bool = True
timeframe: str = "1h"
process_only_new_candles: bool = True
# ROI 与止损可根据需要在配置中覆盖
minimal_roi = {
"120": 0.01,
"60": 0.02,
"0": 0.03,
}
stoploss: float = -0.30
use_exit_signal: bool = True
exit_profit_only: bool = False
ignore_roi_if_entry_signal: bool = False
# 需要的历史K线数量(包含EMA等指标预热)
startup_candle_count: int = 200
plot_config = {
"main_plot": {
"ha_close": {"color": "orange"},
"ha_ema_fast": {"color": "green"},
"ha_ema_slow": {"color": "red"},
},
"subplots": {},
}
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""计算 Heikin Ashi 相关指标与均线。"""
if dataframe is None or dataframe.empty:
return dataframe
dataframe_1h = resample_to_interval(dataframe, self.get_ticker_indicator() * self.time1h)
ha = qtpylib.heikinashi(dataframe_1h)
dataframe_1h["ha_open"] = ha["open"]
dataframe_1h["ha_close"] = ha["close"]
dataframe_1h["ha_high"] = ha["high"]
dataframe_1h["ha_low"] = ha["low"]
# 对 Heikin Ashi close 做 EMA 平滑,减少噪声
dataframe_1h["ha_ema_fast"] = ta.EMA(dataframe_1h["ha_close"], timeperiod=24)
dataframe_1h["ha_ema_slow"] = ta.EMA(dataframe_1h["ha_close"], timeperiod=52)
dataframe = resampled_merge(dataframe, dataframe_1h)
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""根据 HA 趋势定义进场信号(多/空)。"""
if dataframe is None or dataframe.empty:
return dataframe
ha_close = 'resample_{}_ha_close'.format(self.get_ticker_indicator()*self.time1h)
ha_open = 'resample_{}_ha_open'.format(self.get_ticker_indicator()*self.time1h)
ha_ema_fast = 'resample_{}_ha_ema_fast'.format(self.get_ticker_indicator()*self.time1h)
ha_ema_slow = 'resample_{}_ha_ema_slow'.format(self.get_ticker_indicator()*self.time1h)
volume = 'resample_{}_volume'.format(self.get_ticker_indicator()*self.time1h)
# 做多:HA 实体向上 且 快线上穿慢线(金叉)
dataframe.loc[
(
(dataframe[ha_close] > dataframe[ha_open]) &
(qtpylib.crossed_above(dataframe[ha_ema_fast], dataframe[ha_ema_slow])) &
(dataframe["volume"] > 0)
),
["enter_long", "enter_tag"],
] = (1, "ha_trend_long")
# 做空:HA 实体向下 且 快线下穿慢线(死叉)
dataframe.loc[
(
(dataframe[ha_close] < dataframe[ha_open]) &
(qtpylib.crossed_below(dataframe[ha_ema_fast], dataframe[ha_ema_slow])) &
(dataframe["volume"] > 0)
),
["enter_short", "enter_tag"],
] = (1, "ha_trend_short")
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""根据 HA 反转或均线反转定义出场信号(多/空)。"""
if dataframe is None or dataframe.empty:
return dataframe
ha_close = 'resample_{}_ha_close'.format(self.get_ticker_indicator()*self.time1h)
ha_open = 'resample_{}_ha_open'.format(self.get_ticker_indicator()*self.time1h)
ha_ema_fast = 'resample_{}_ha_ema_fast'.format(self.get_ticker_indicator()*self.time1h)
ha_ema_slow = 'resample_{}_ha_ema_slow'.format(self.get_ticker_indicator()*self.time1h)
volume = 'resample_{}_volume'.format(self.get_ticker_indicator()*self.time1h)
# 多单退出:HA 变为阴 或 快线下穿慢线(死叉)
dataframe.loc[
(
(
(dataframe[ha_close] < dataframe[ha_open]) |
(qtpylib.crossed_below(dataframe[ha_ema_fast], dataframe[ha_ema_slow]))
)
& (dataframe["volume"] > 0)
),
["exit_long", "exit_tag"],
] = (1, "ha_long_exit")
# 空单退出:HA 变为阳 或 快线上穿慢线(金叉)
dataframe.loc[
(
(
(dataframe[ha_close] > dataframe[ha_open]) |
(qtpylib.crossed_above(dataframe[ha_ema_fast], dataframe[ha_ema_slow]))
)
& (dataframe["volume"] > 0)
),
["exit_short", "exit_tag"],
] = (1, "ha_short_exit")
return dataframe
def get_ticker_indicator(self):
return int(self.timeframe[:-1])