From b816a42eab019f3f395cac21daa4c403a59ef2ba Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 22 Apr 2025 15:43:04 +0800 Subject: [PATCH] second --- .gitignore | 1 + config_examples/config_freqai.okx.json | 130 +++++++++++++++++ docker-compose.yml | 26 +++- .../templates/FreqaiExampleHybridStrategy.py | 137 ++++++++---------- 4 files changed, 214 insertions(+), 80 deletions(-) create mode 100644 config_examples/config_freqai.okx.json diff --git a/.gitignore b/.gitignore index 3d5dd44..f50ce36 100644 --- a/.gitignore +++ b/.gitignore @@ -114,6 +114,7 @@ target/ !config_examples/config_full.example.json !config_examples/config_kraken.example.json !config_examples/config_freqai.example.json +!config_examples/*.json docker-compose-*.yml data/ diff --git a/config_examples/config_freqai.okx.json b/config_examples/config_freqai.okx.json new file mode 100644 index 0000000..7298d71 --- /dev/null +++ b/config_examples/config_freqai.okx.json @@ -0,0 +1,130 @@ +{ + "$schema": "https://schema.freqtrade.io/schema.json", + "trading_mode": "spot", + "margin_mode": "isolated", + "max_open_trades": 4, + "stake_currency": "USDT", + "stake_amount": 150, + "tradable_balance_ratio": 1, + "fiat_display_currency": "USD", + "dry_run": true, + "timeframe": "3m", + "dry_run_wallet": 1000, + "cancel_open_orders_on_exit": true, + "stoploss": -0.09, + "unfilledtimeout": { + "entry": 5, + "exit": 15 + }, + "exchange": { + "name": "okx", + "key": "eca767d4-fda5-4a1b-bb28-49ae18093307", + "secret": "8CA3628A556ED137977DB298D37BC7F3", + "enable_ws": false, + "ccxt_config": { + "enableRateLimit": true, + "rateLimit": 500, + "options": { + "defaultType": "spot" + } + }, + "ccxt_async_config": { + "enableRateLimit": true, + "rateLimit": 500, + "timeout": 20000 + }, + "pair_whitelist": [ + "BTC/USDT", + "SOL/USDT" + ], + "pair_blacklist": [] + }, + "entry_pricing": { + "price_side": "same", + "use_order_book": true, + "order_book_top": 1, + "price_last_balance": 0.0, + "check_depth_of_market": { + "enabled": false, + "bids_to_ask_delta": 1 + } + }, + "exit_pricing": { + "price_side": "other", + "use_order_book": true, + "order_book_top": 1 + }, + "pairlists": [ + { + "method": "StaticPairList" + } + ], + "freqai": { + "enabled": true, + "data_kitchen": { + "fillna": "ffill" + }, + "freqaimodel": "CatboostClassifier", + "purge_old_models": 2, + "train_period_days": 15, + "identifier": "test3", + "train_period_days": 30, + "backtest_period_days": 10, + "live_retrain_hours": 0, + "feature_selection": { + "method": "recursive_elimination" + }, + "feature_parameters": { + "include_timeframes": [ + "3m", + "15m", + "1h" + ], + "include_corr_pairlist": [ + "BTC/USDT", + "SOL/USDT" + ], + "label_period_candles": 20, + "include_shifted_candles": 2, + "DI_threshold": 0.9, + "weight_factor": 0.9, + "principal_component_analysis": false, + "use_SVM_to_remove_outliers": false, + "indicator_periods_candles": [ + 10, + 20, + 50 + ], + "plot_feature_importances": 0 + }, + "data_split_parameters": { + "test_size": 0.2 + }, + "model_training_parameters": { + "n_estimators": 100, + "learning_rate": 0.05, + "max_depth": 5, + "num_leaves": 31 + } + }, + "api_server": { + "enabled": true, + "listen_ip_address": "0.0.0.0", + "listen_port": 8080, + "verbosity": "error", + "enable_openapi": false, + "jwt_secret_key": "6a599ab046dbb419014807dffd7b8823bfa7e5df56b17d545485deb87331b4ca", + "ws_token": "6O5pBDiRigiZrmIsofaE2rkKMJtf9h8zVQ", + "CORS_origins": [], + "username": "freqAdmin", + "password": "admin" + }, + "bot_name": "freqtrade", + "initial_state": "running", + "force_entry_enable": false, + "internals": { + "process_throttle_secs": 5, + "heartbeat_interval": 20, + "loglevel": "DEBUG" + } +} diff --git a/docker-compose.yml b/docker-compose.yml index c2428fc..0dfc5a0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,6 +22,7 @@ services: - "./user_data:/freqtrade/user_data" - "./config_examples:/freqtrade/config_examples" - "./freqtrade/templates:/freqtrade/templates" + - "./freqtrade/exchange/:/freqtrade/exchange" # Expose api on port 8080 (localhost only) # Please read the https://www.freqtrade.io/en/stable/rest-api/ documentation # for more information. @@ -30,12 +31,23 @@ services: # Default command used when running `docker compose up` # --freqaimodel XGBoostRegressor + # commangd: > + # # trade + # --logfile /freqtrade/user_data/logs/freqtrade.log + # --db-url sqlite:////freqtrade/user_data/tradesv3.sqlite + # --freqaimodel LightGBMRegressor + # --config /freqtrade/config_examples/config_freqai.okx.json + # --strategy FreqaiExampleStrategy + # --strategy FreqaiExampleHybridStrategy + # --strategy-path /freqtrade/templates command: > - trade - --logfile /freqtrade/user_data/logs/freqtrade.log - --db-url sqlite:////freqtrade/user_data/tradesv3.sqlite - --freqaimodel LightGBMRegressor - --config /freqtrade/config_examples/config_freqai.okx.json - --strategy FreqaiExampleStrategy - --strategy-path /freqtrade/templates + backtesting + --logfile /freqtrade/user_data/logs/freqtrade.log + --freqaimodel XGBoostRegressor + --config /freqtrade/config_examples/config_freqai.okx.json + --strategy-path /freqtrade/templates + --strategy FreqaiExampleStrategy + --timerange 20250320-20250420 + --export trades + diff --git a/freqtrade/templates/FreqaiExampleHybridStrategy.py b/freqtrade/templates/FreqaiExampleHybridStrategy.py index 908c4b1..83f8846 100644 --- a/freqtrade/templates/FreqaiExampleHybridStrategy.py +++ b/freqtrade/templates/FreqaiExampleHybridStrategy.py @@ -88,13 +88,11 @@ class FreqaiExampleHybridStrategy(IStrategy): stoploss = -0.05 use_exit_signal = True startup_candle_count: int = 30 - can_short = True + can_short = 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) def feature_engineering_expand_all( self, dataframe: DataFrame, period: int, metadata: dict, **kwargs @@ -210,35 +208,65 @@ class FreqaiExampleHybridStrategy(IStrategy): dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek dataframe["%-hour_of_day"] = dataframe["date"].dt.hour return dataframe - def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame: - """ - *Only functional with FreqAI enabled strategies* - Required function to set the targets for the model. - All targets must be prepended with `&` to be recognized by the FreqAI internals. - - More details about feature engineering available: - - https://www.freqtrade.io/en/latest/freqai-feature-engineering - - :param dataframe: strategy dataframe which will receive the targets - :param metadata: metadata of current pair - usage example: dataframe["&-target"] = dataframe["close"].shift(-1) / dataframe["close"] - """ - self.freqai.class_names = ["down", "up"] - dataframe["&s-up_or_down"] = np.where( - dataframe["close"].shift(-50) > dataframe["close"], "up", "down" - ) - + logger.info(f"Setting FreqAI targets for pair: {metadata['pair']}") + logger.info(f"DataFrame shape: {dataframe.shape}") + logger.info(f"Available columns: {list(dataframe.columns)}") + logger.info(f"First few rows:\n{dataframe[['date', 'close']].head().to_string()}") + + if "close" not in dataframe.columns: + logger.error("Required 'close' column missing in dataframe") + raise ValueError("Required 'close' column missing in dataframe") + + if len(dataframe) < 50: + logger.error(f"Insufficient data: {len(dataframe)} rows, need at least 50 for shift(-50)") + raise ValueError("Insufficient data for target calculation") + + try: + # 生成数值型标签:1 表示上涨,0 表示下跌 + dataframe["&-up_or_down"] = np.where( + dataframe["close"].shift(-50) > dataframe["close"], + 1.0, # 数值型标签 + 0.0 + ) + except Exception as e: + logger.error(f"Failed to create &-up_or_down column: {str(e)}") + raise + + logger.info(f"Target column head:\n{dataframe[['&-up_or_down']].head().to_string()}") + + if "&-up_or_down" not in dataframe.columns: + logger.error("FreqAI failed to generate the &-up_or_down column") + raise KeyError("FreqAI failed to generate the &-up_or_down column") + + logger.info("FreqAI targets set successfully") return dataframe - def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # noqa: C901 - # User creates their own custom strat here. Present example is a supertrend - # based strategy. + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + logger.info(f"Processing pair: {metadata['pair']}") + logger.info(f"Input DataFrame shape: {dataframe.shape}") + logger.info(f"Input DataFrame columns: {list(dataframe.columns)}") + logger.info(f"Input DataFrame head:\n{dataframe[['date', 'close', 'volume']].head().to_string()}") + + # Ensure FreqAI processing + logger.info("Calling self.freqai.start") + try: + dataframe = self.freqai.start(dataframe, metadata, self) + except Exception as e: + logger.error(f"self.freqai.start failed: {str(e)}") + raise + logger.info("self.freqai.start completed") + + logger.info(f"Output DataFrame shape: {dataframe.shape}") + logger.info(f"Output DataFrame columns: {list(dataframe.columns)}") + # Safely log columns that exist + available_columns = [col for col in ['date', 'close', '&-up_or_down'] if col in dataframe.columns] + logger.info(f"Output DataFrame head:\n{dataframe[available_columns].head().to_string()}") + + if "&-up_or_down" not in dataframe.columns: + logger.error("FreqAI did not generate the required &-up_or_down column") + raise KeyError("FreqAI did not generate the required &-up_or_down column") - dataframe = self.freqai.start(dataframe, metadata, self) - - # TA indicators to combine with the Freqai targets # RSI dataframe["rsi"] = ta.RSI(dataframe) @@ -254,67 +282,30 @@ class FreqaiExampleHybridStrategy(IStrategy): "bb_middleband" ] - # TEMA - Triple Exponential Moving Average + # TEMA dataframe["tema"] = ta.TEMA(dataframe, timeperiod=9) return dataframe - def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame: df.loc[ ( - # Signal: RSI crosses above 30 (qtpylib.crossed_above(df["rsi"], self.buy_rsi.value)) - & (df["tema"] <= df["bb_middleband"]) # Guard: tema below BB middle - & (df["tema"] > df["tema"].shift(1)) # Guard: tema is raising - & (df["volume"] > 0) # Make sure Volume is not 0 - & (df["do_predict"] == 1) # Make sure Freqai is confident in the prediction - & - # Only enter trade if Freqai thinks the trend is in this direction - (df["&s-up_or_down"] == "up") + & (df["tema"] <= df["bb_middleband"]) + & (df["tema"] > df["tema"].shift(1)) + & (df["volume"] > 0) ), "enter_long", ] = 1 - - df.loc[ - ( - # Signal: RSI crosses above 70 - (qtpylib.crossed_above(df["rsi"], self.short_rsi.value)) - & (df["tema"] > df["bb_middleband"]) # Guard: tema above BB middle - & (df["tema"] < df["tema"].shift(1)) # Guard: tema is falling - & (df["volume"] > 0) # Make sure Volume is not 0 - & (df["do_predict"] == 1) # Make sure Freqai is confident in the prediction - & - # Only enter trade if Freqai thinks the trend is in this direction - (df["&s-up_or_down"] == "down") - ), - "enter_short", - ] = 1 - return df def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame: df.loc[ ( - # Signal: RSI crosses above 70 (qtpylib.crossed_above(df["rsi"], self.sell_rsi.value)) - & (df["tema"] > df["bb_middleband"]) # Guard: tema above BB middle - & (df["tema"] < df["tema"].shift(1)) # Guard: tema is falling - & (df["volume"] > 0) # Make sure Volume is not 0 + & (df["tema"] > df["bb_middleband"]) + & (df["tema"] < df["tema"].shift(1)) + & (df["volume"] > 0) ), "exit_long", ] = 1 - - df.loc[ - ( - # Signal: RSI crosses above 30 - (qtpylib.crossed_above(df["rsi"], self.exit_short_rsi.value)) - & - # Guard: tema below BB middle - (df["tema"] <= df["bb_middleband"]) - & (df["tema"] > df["tema"].shift(1)) # Guard: tema is raising - & (df["volume"] > 0) # Make sure Volume is not 0 - ), - "exit_short", - ] = 1 - - return df + return df