From b5d174384309a023d3e623d41b7602078e64ae40 Mon Sep 17 00:00:00 2001 From: "zhangkun9038@dingtalk.com" Date: Wed, 7 May 2025 13:41:13 +0800 Subject: [PATCH] first --- config_examples/config_base_hyperopt.json | 68 +++++++ docker-compose.yml | 4 +- freqtrade/templates/BaseHyperOptStrategy.py | 190 ++++++++++++++++++++ 3 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 config_examples/config_base_hyperopt.json create mode 100644 freqtrade/templates/BaseHyperOptStrategy.py diff --git a/config_examples/config_base_hyperopt.json b/config_examples/config_base_hyperopt.json new file mode 100644 index 00000000..25aa73c7 --- /dev/null +++ b/config_examples/config_base_hyperopt.json @@ -0,0 +1,68 @@ +{ + "strategy": "MyStrategy", + "timeframe": "5m", + "dry_run": true, + "stake_currency": "USDT", + "stake_amount": 10.0, + "minimal_roi": { + "40": 0.0, + "30": 0.01, + "20": 0.02, + "0": 0.04 + }, + "stoploss": -0.10, + "trailing_stop": false, + "trailing_stop_positive": 0.0, + "trailing_stop_positive_offset": 0.0, + "trailing_only_offset_is_reached": 0.0, + "unfilledtimeout": { + "buy": 300, + "sell": 300 + }, + "exchange": { + "name": "binance", + "key": "", + "secret": "", + "ccxt_config": { + "enableRateLimit": true + }, + "pair_whitelist": [ + "BTC/USDT", + "ETH/USDT", + "SOL/USDT" + ], + "pair_blacklist": [] + }, + "experimental": { + "use_sell_signal": true, + "ignore_roi_if_buy_signal": false + }, + "telegram": { + "enabled": false, + "token": "", + "chat_id": "" + }, + "api_server": { + "enabled": false, + "listen_ip_address": "127.0.0.1", + "listen_port": 8080, + "verbosity": "error", + "enable_openapi": false + }, + "hyperopt": { + "enabled": false, + "epochs": 100, + "spaces": [ + "buy", + "sell", + "roi", + "stoploss", + "trailing" + ], + "hyperopt_path": "", + "hyperopt_loss": "SharpeHyperOptLossDaily", + "print_all": false, + "mongodb_url": "", + "mongodb_database": "" + } +} diff --git a/docker-compose.yml b/docker-compose.yml index ecd2a7df..cb3261eb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -67,9 +67,9 @@ services: backtesting --logfile /freqtrade/user_data/logs/freqtrade.log --freqaimodel XGBoostRegressor - --config /freqtrade/config_examples/config_freqai.okx.json + --config /freqtrade/config_examples/config_base_hyperopt.json --strategy-path /freqtrade/templates - --strategy OKXRegressionStrategy + --strategy SharpeHyperOptStrategy --timerange 20250401-20250415 --fee 0.0008 --cache none diff --git a/freqtrade/templates/BaseHyperOptStrategy.py b/freqtrade/templates/BaseHyperOptStrategy.py new file mode 100644 index 00000000..c4a0de91 --- /dev/null +++ b/freqtrade/templates/BaseHyperOptStrategy.py @@ -0,0 +1,190 @@ +from freqtrade.strategy import IStrategy, IntParameter, RealParameter, DecimalParameter, BooleanParameter +from pandas import DataFrame +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from datetime import datetime + +class BsseHyperOptStrategy(IStrategy): + # 策略的基本配置 + timeframe = '5m' + minimal_roi = {"40": 0.0, "30": 0.01, "20": 0.02, "0": 0.04} + stoploss = -0.10 + startup_candle_count = 20 + + # 可选的订单类型映射 + order_types = { + "entry": "limit", + "exit": "limit", + "stoploss": "limit", + "stoploss_on_exchange": False, + } + + # 可选的订单有效时间 + order_time_in_force = { + "entry": "gtc", + "exit": "gtc", + } + + # 可优化的参数 + buy_rsi = IntParameter(30, 50, default=35, space='buy') + buy_plusdi = RealParameter(low=0, high=1, default=0.5, space='buy') + sell_rsi = IntParameter(low=50, high=100, default=70, space='sell') + sell_minusdi = DecimalParameter( + low=0, high=1, default=0.5001, decimals=3, space='sell', load=False + ) + protection_enabled = BooleanParameter(default=True) + protection_cooldown_lookback = IntParameter([0, 50], default=30) + + @property + def protections(self): + prot = [] + if self.protection_enabled.value: + prot.append( + { + "method": "CooldownPeriod", + "stop_duration_candles": self.protection_cooldown_lookback.value, + } + ) + return prot + + def bot_start(self): + pass + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # 动量指标 + # ADX + dataframe["adx"] = ta.ADX(dataframe) + + # MACD + macd = ta.MACD(dataframe) + dataframe["macd"] = macd["macd"] + dataframe["macdsignal"] = macd["macdsignal"] + dataframe["macdhist"] = macd["macdhist"] + + # 负向指标 + dataframe["minus_di"] = ta.MINUS_DI(dataframe) + + # 正向指标 + dataframe["plus_di"] = ta.PLUS_DI(dataframe) + + # RSI + dataframe["rsi"] = ta.RSI(dataframe) + + # 快速随机指标 + stoch_fast = ta.STOCHF(dataframe) + dataframe["fastd"] = stoch_fast["fastd"] + dataframe["fastk"] = stoch_fast["fastk"] + + # 布林带 + 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["ema10"] = ta.EMA(dataframe, timeperiod=10) + + return dataframe + + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe["rsi"] < self.buy_rsi.value) + & (dataframe["fastd"] < 35) + & (dataframe["adx"] > 30) + & (dataframe["plus_di"] > self.buy_plusdi.value) + ) + | ((dataframe["adx"] > 65) & (dataframe["plus_di"] > self.buy_plusdi.value)), + "enter_long", + ] = 1 + + return dataframe + + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + ( + (qtpylib.crossed_above(dataframe["rsi"], self.sell_rsi.value)) + | (qtpylib.crossed_above(dataframe["fastd"], 70)) + ) + & (dataframe["adx"] > 10) + & (dataframe["minus_di"] > 0) + ) + | ((dataframe["adx"] > 70) & (dataframe["minus_di"] > self.sell_minusdi.value)), + "exit_long", + ] = 1 + + return dataframe + + def leverage( + self, + pair: str, + current_time: datetime, + current_rate: float, + proposed_leverage: float, + max_leverage: float, + entry_tag: str | None, + side: str, + **kwargs, + ) -> float: + return 3.0 + + def adjust_trade_position( + self, + trade, + current_time: datetime, + current_rate: float, + current_profit: float, + min_stake: float | None, + max_stake: float, + current_entry_rate: float, + current_exit_rate: float, + current_entry_profit: float, + current_exit_profit: float, + **kwargs, + ) -> float | None: + if current_profit < -0.0075: + orders = trade.select_filled_orders(trade.entry_side) + return round(orders[0].stake_amount, 0) + return None + + class HyperOpt: + def buy_indicator_space(self): + return [ + self.strategy.buy_rsi.get_space('buy_rsi'), + self.strategy.buy_plusdi.get_space('buy_plusdi') + ] + + def sell_indicator_space(self): + return [ + self.strategy.sell_rsi.get_space('sell_rsi'), + self.strategy.sell_minusdi.get_space('sell_minusdi') + ] + + def roi_space(self): + from skopt.space import Integer + return [ + Integer(10, 120, name='roi_t1'), + Integer(10, 60, name='roi_t2'), + Integer(10, 40, name='roi_t3'), + ] + + def stoploss_space(self): + from skopt.space import Real + return [Real(-0.5, -0.01, name='stoploss')] + + def trailing_space(self): + from skopt.space import Real, Categorical + return [ + Categorical([True, False], name='trailing_stop'), + Real(0.01, 0.1, name='trailing_stop_positive'), + Real(0.0, 0.1, name='trailing_stop_positive_offset'), + Real(0.0, 0.1, name='trailing_only_offset_is_reached') + ] + + def max_open_trades_space(self): + from skopt.space import Integer + return [Integer(1, 10, name='max_open_trades')]``