Compare commits

...

65 Commits

Author SHA1 Message Date
zhangkun9038@dingtalk.com
28fd0a80c9 stable1 14 2025-04-28 21:08:33 +08:00
zhangkun9038@dingtalk.com
9bb14377ed stable1 14 2025-04-28 21:07:51 +08:00
zhangkun9038@dingtalk.com
eceb04d22d stable1 13 2025-04-28 21:01:58 +08:00
zhangkun9038@dingtalk.com
a46895526c stable1 11 2025-04-28 20:51:06 +08:00
zhangkun9038@dingtalk.com
bba64d33cb stable1 11 2025-04-28 20:49:31 +08:00
zhangkun9038@dingtalk.com
ec8447e116 stable1 10 2025-04-28 20:38:42 +08:00
zhangkun9038@dingtalk.com
bc39868c5e stable1 10 2025-04-28 20:37:16 +08:00
zhangkun9038@dingtalk.com
42781656b4 stable1 7 2025-04-28 18:30:02 +08:00
zhangkun9038@dingtalk.com
d6fe9f8573 stable1 6 2025-04-28 18:26:32 +08:00
zhangkun9038@dingtalk.com
ebd83c9374 stable1 6 2025-04-28 18:25:54 +08:00
zhangkun9038@dingtalk.com
ae6330a89e stable1 6 2025-04-28 18:24:44 +08:00
zhangkun9038@dingtalk.com
51d4de29ae stable1 6 2025-04-28 18:22:50 +08:00
zhangkun9038@dingtalk.com
7564bac37a stable1 6 2025-04-28 18:18:53 +08:00
zhangkun9038@dingtalk.com
9f681ae122 stable1 6 2025-04-28 18:17:27 +08:00
zhangkun9038@dingtalk.com
2b6f010cd5 stable1 6 2025-04-28 18:15:53 +08:00
zhangkun9038@dingtalk.com
a416b4347d stable1 6 2025-04-28 18:14:42 +08:00
zhangkun9038@dingtalk.com
36a7563923 stable1 6 2025-04-28 18:13:34 +08:00
zhangkun9038@dingtalk.com
6c634f9137 stable1 6 2025-04-28 18:02:40 +08:00
zhangkun9038@dingtalk.com
de13e2e144 stable1 6 2025-04-28 18:02:14 +08:00
zhangkun9038@dingtalk.com
e10821a4fc stable1 6 2025-04-28 17:58:03 +08:00
zhangkun9038@dingtalk.com
bb7a4af33a stable1 5 2025-04-28 17:55:14 +08:00
zhangkun9038@dingtalk.com
4b2fd054b5 stable1 4 2025-04-28 17:54:40 +08:00
zhangkun9038@dingtalk.com
5ef9dd3d61 stable1 4 2025-04-28 17:54:01 +08:00
zhangkun9038@dingtalk.com
9acebf9952 stable1 3 2025-04-28 17:53:27 +08:00
zhangkun9038@dingtalk.com
1a9a0932c5 stable1 1 2025-04-28 17:51:42 +08:00
zhangkun9038@dingtalk.com
8f4c374e6b 检查 MACD 列是否存在
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 17:01:30 +08:00
zhangkun9038@dingtalk.com
ff7aff8ee7 检查 MACD 列是否存在
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 16:51:18 +08:00
zhangkun9038@dingtalk.com
e5cc226c01 检查 MACD 列是否存在
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 16:48:53 +08:00
zhangkun9038@dingtalk.com
63c1f07f06 确保是二维数组
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 16:46:26 +08:00
zhangkun9038@dingtalk.com
b20684b5b1 up3
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 16:12:02 +08:00
zhangkun9038@dingtalk.com
96b76ffcc0 up3
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 16:09:50 +08:00
zhangkun9038@dingtalk.com
0fa0866370 up3
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 16:09:02 +08:00
zhangkun9038@dingtalk.com
887c4778b4 up3
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 16:07:03 +08:00
zhangkun9038@dingtalk.com
8777726441 up
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 15:49:02 +08:00
zhangkun9038@dingtalk.com
b6b9a62c35 up
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 15:47:10 +08:00
zhangkun9038@dingtalk.com
3fafbff8c3 up
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 15:44:22 +08:00
zhangkun9038@dingtalk.com
619de5cede 跟特征数量没关系
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 15:32:01 +08:00
zhangkun9038@dingtalk.com
8535b10cea 跟特征数量没关系
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 15:15:37 +08:00
zhangkun9038@dingtalk.com
7da603fd08 跟特征数量没关系
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 15:14:09 +08:00
zhangkun9038@dingtalk.com
144615b7a8 跟特征数量没关系
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 14:13:16 +08:00
zhangkun9038@dingtalk.com
8448ab40f5 跟特征数量没关系
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 14:11:36 +08:00
zhangkun9038@dingtalk.com
6916e49479 跟特征数量没关系
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 14:10:54 +08:00
zhangkun9038@dingtalk.com
29fd0941a0 跟特征数量没关系
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 14:08:34 +08:00
zhangkun9038@dingtalk.com
ec7d7c2842 跟特征数量没关系
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 14:07:32 +08:00
zhangkun9038@dingtalk.com
ea707fe104 跟特征数量没关系
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 14:06:49 +08:00
zhangkun9038@dingtalk.com
2f4a06d505 跟特征数量没关系
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 13:59:16 +08:00
zhangkun9038@dingtalk.com
65116ab7b4 跟特征数量没关系
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 13:49:35 +08:00
zhangkun9038@dingtalk.com
fbd72745cb 跟特征数量没关系
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 13:34:56 +08:00
zhangkun9038@dingtalk.com
8477bf07c7 增加特征
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 13:23:49 +08:00
zhangkun9038@dingtalk.com
03a54a5b0a 添加更多技术指标
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 13:13:19 +08:00
zhangkun9038@dingtalk.com
e3ffdd92e0 :生成的特征可能不够稳定,导致新数据与训练数据差异过大
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 13:01:27 +08:00
zhangkun9038@dingtalk.com
c3e4a73eb3 缩进乱了
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 12:49:02 +08:00
zhangkun9038@dingtalk.com
21e4c2f2ea 数据清理
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 12:45:19 +08:00
zhangkun9038@dingtalk.com
86e9a2ab61 确保 &-buy_rsi 列的值计算正确
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 12:42:12 +08:00
zhangkun9038@dingtalk.com
05b65162a1 Additional check to ensure no NaN values remain
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 12:34:47 +08:00
zhangkun9038@dingtalk.com
64e2edfa4e 计算 buy_rsi_pred 并清理 NaN 值
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 12:31:10 +08:00
zhangkun9038@dingtalk.com
328769e0e1 up
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 12:25:20 +08:00
zhangkun9038@dingtalk.com
82ed0e90e9 up
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 12:21:53 +08:00
zhangkun9038@dingtalk.com
244b91ebd3 up
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 12:20:59 +08:00
zhangkun9038@dingtalk.com
e0884d4349 up
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 12:16:22 +08:00
zhangkun9038@dingtalk.com
456ae1fde0 up
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 12:10:17 +08:00
zhangkun9038@dingtalk.com
b72587ed6a up 2025-04-28 11:54:42 +08:00
zhangkun9038@dingtalk.com
c145d5d452 up
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 11:52:45 +08:00
zhangkun9038@dingtalk.com
872bfe4078 up
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 11:38:27 +08:00
zhangkun9038@dingtalk.com
6e4e54b9c8 去掉看底牌代码
Some checks are pending
Update Docker Hub Description / dockerHubDescription (push) Waiting to run
2025-04-28 11:34:17 +08:00
9 changed files with 538 additions and 266 deletions

View File

@ -0,0 +1,5 @@
<<<<<<< HEAD
=======
freqtrade backtesting --strategy FreqaiExampleStrategy --strategy-path freqtrade/templates --config config_examples/config_freqai.example.json --timerange 20230101-20230401
>>>>>>> Snippet

5
4. **清理缓存**: Normal file
View File

@ -0,0 +1,5 @@
<<<<<<< HEAD
=======
rm -rf /freqtrade/user_data/models/test62/
>>>>>>> Snippet

5
5. **重新训练**: Normal file
View File

@ -0,0 +1,5 @@
<<<<<<< HEAD
=======
freqtrade trade --config config_examples/config_freqai.okx.json --strategy FreqaiExampleStrategy
>>>>>>> Snippet

0
asdf Normal file
View File

View File

@ -2,16 +2,16 @@
"$schema": "https://schema.freqtrade.io/schema.json", "$schema": "https://schema.freqtrade.io/schema.json",
"trading_mode": "spot", "trading_mode": "spot",
"margin_mode": "isolated", "margin_mode": "isolated",
"max_open_trades": 4, "max_open_trades": 3,
"stake_currency": "USDT", "stake_currency": "USDT",
"stake_amount": 150, "stake_amount": 100,
"tradable_balance_ratio": 1, "tradable_balance_ratio": 1,
"fiat_display_currency": "USD", "fiat_display_currency": "USD",
"dry_run": true, "dry_run": true,
"timeframe": "3m", "timeframe": "15m",
"dry_run_wallet": 1000, "dry_run_wallet": 1000,
"cancel_open_orders_on_exit": true, "cancel_open_orders_on_exit": true,
"stoploss": -0.05, "stoploss": -0.1,
"unfilledtimeout": { "unfilledtimeout": {
"entry": 5, "entry": 5,
"exit": 15 "exit": 15
@ -30,12 +30,13 @@
}, },
"ccxt_async_config": { "ccxt_async_config": {
"enableRateLimit": true, "enableRateLimit": true,
"rateLimit": 500, "rateLimit": 500,
"timeout": 20000 "timeout": 20000
}, },
"pair_whitelist": [ "pair_whitelist": [
"BTC/USDT", "BTC/USDT",
"SOL/USDT" "SOL/USDT",
"ETH/USDT"
], ],
"pair_blacklist": [] "pair_blacklist": []
}, },
@ -61,50 +62,45 @@
], ],
"freqai": { "freqai": {
"enabled": true, "enabled": true,
"model_path": "/freqtrade/user_data/models",
"data_kitchen": { "data_kitchen": {
"fillna": "ffill" "fillna": "ffill"
}, },
"freqaimodel": "CatboostClassifier", "freqaimodel": "XGBoostRegressor",
"purge_old_models": 2, "model_training_parameters": {
"train_period_days": 15, "n_estimators": 300,
"identifier": "test62", "learning_rate": 0.03,
"train_period_days": 30, "max_depth": 6,
"backtest_period_days": 10, "subsample": 0.8,
"colsample_bytree": 0.8,
"reg_lambda": 1.0,
"objective": "reg:squarederror",
"eval_metric": "rmse",
"early_stopping_rounds": 20
},
"train_period_days": 730,
"backtest_period_days": 90,
"live_retrain_hours": 0, "live_retrain_hours": 0,
"feature_selection": { "feature_selection": {
"method": "recursive_elimination" "method": "recursive_elimination"
}, },
"feature_parameters": { "feature_parameters": {
"include_timeframes": [ "include_timeframes": ["15m", "1h", "4h"],
"3m", "include_corr_pairlist": ["BTC/USDT", "SOL/USDT", "ETH/USDT"],
"15m", "label_period_candles": 60,
"1h"
],
"include_corr_pairlist": [
"BTC/USDT",
"SOL/USDT"
],
"label_period_candles": 20,
"include_shifted_candles": 2, "include_shifted_candles": 2,
"DI_threshold": 0.9,
"weight_factor": 0.9, "weight_factor": 0.9,
"principal_component_analysis": false, "principal_component_analysis": false,
"use_SVM_to_remove_outliers": false, "use_SVM_to_remove_outliers": true,
"indicator_periods_candles": [ "SVM_parameters": {
10, "nu": 0.1
20, },
50 "DI_threshold": 0,
], "indicator_periods_candles": [14, 20]
"plot_feature_importances": 0
}, },
"data_split_parameters": { "data_split_parameters": {
"test_size": 0.2 "test_size": 0.2,
}, "shuffle": true
"model_training_parameters": {
"n_estimators": 100,
"learning_rate": 0.05,
"max_depth": 5,
"num_leaves": 31
} }
}, },
"api_server": { "api_server": {
@ -128,3 +124,4 @@
"loglevel": "DEBUG" "loglevel": "DEBUG"
} }
} }

View File

@ -64,9 +64,10 @@ services:
command: > command: >
backtesting backtesting
--logfile /freqtrade/user_data/logs/freqtrade.log --logfile /freqtrade/user_data/logs/freqtrade.log
--freqaimodel LightGBMRegressor --freqaimodel XGBoostRegressor
--config /freqtrade/config_examples/config_freqai.okx.json --config /freqtrade/config_examples/config_freqai.okx.json
--config /freqtrade/templates/FreqaiExampleStrategy.json --config /freqtrade/templates/FreqaiExampleStrategy.json
--strategy-path /freqtrade/templates --strategy-path /freqtrade/templates
--strategy FreqaiExampleStrategy --strategy FreqaiExampleStrategy
--timerange 20240920-20250420 --timerange 20250320-20250420
--cache none

View File

@ -52,7 +52,9 @@ class FreqaiExampleHybridStrategy(IStrategy):
"random_state": 1 "random_state": 1
}, },
"model_training_parameters": { "model_training_parameters": {
"n_estimators": 800 "n_estimators": 200,
"max_depth": 5,
"learning_rate": 0.05
} }
}, },
@ -122,13 +124,11 @@ class FreqaiExampleHybridStrategy(IStrategy):
""" """
dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period) dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period)
dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period)
dataframe["%-adx-period"] = ta.ADX(dataframe, timeperiod=period)
dataframe["%-sma-period"] = ta.SMA(dataframe, timeperiod=period)
dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period) dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period)
bollinger = qtpylib.bollinger_bands( bollinger = qtpylib.bollinger_bands(
qtpylib.typical_price(dataframe), window=period, stds=2.2 qtpylib.typical_price(dataframe), window=period, stds=2
) )
dataframe["bb_lowerband-period"] = bollinger["lower"] dataframe["bb_lowerband-period"] = bollinger["lower"]
dataframe["bb_middleband-period"] = bollinger["mid"] dataframe["bb_middleband-period"] = bollinger["mid"]
@ -137,13 +137,6 @@ class FreqaiExampleHybridStrategy(IStrategy):
dataframe["%-bb_width-period"] = ( dataframe["%-bb_width-period"] = (
dataframe["bb_upperband-period"] - dataframe["bb_lowerband-period"] dataframe["bb_upperband-period"] - dataframe["bb_lowerband-period"]
) / dataframe["bb_middleband-period"] ) / dataframe["bb_middleband-period"]
dataframe["%-close-bb_lower-period"] = dataframe["close"] / dataframe["bb_lowerband-period"]
dataframe["%-roc-period"] = ta.ROC(dataframe, timeperiod=period)
dataframe["%-relative_volume-period"] = (
dataframe["volume"] / dataframe["volume"].rolling(period).mean()
)
return dataframe return dataframe
@ -177,8 +170,7 @@ class FreqaiExampleHybridStrategy(IStrategy):
dataframe["%-ema-200"] = ta.EMA(dataframe, timeperiod=200) dataframe["%-ema-200"] = ta.EMA(dataframe, timeperiod=200)
""" """
dataframe["%-pct-change"] = dataframe["close"].pct_change() dataframe["%-pct-change"] = dataframe["close"].pct_change()
dataframe["%-raw_volume"] = dataframe["volume"]
dataframe["%-raw_price"] = dataframe["close"]
return dataframe return dataframe
def feature_engineering_standard( def feature_engineering_standard(
@ -209,10 +201,10 @@ class FreqaiExampleHybridStrategy(IStrategy):
dataframe["%-hour_of_day"] = dataframe["date"].dt.hour dataframe["%-hour_of_day"] = dataframe["date"].dt.hour
return dataframe return dataframe
def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame: def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
"""
Redefined target variable to predict whether the price will increase or decrease in the future.
"""
logger.info(f"Setting FreqAI targets for pair: {metadata['pair']}") 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: if "close" not in dataframe.columns:
logger.error("Required 'close' column missing in dataframe") logger.error("Required 'close' column missing in dataframe")
@ -223,22 +215,16 @@ class FreqaiExampleHybridStrategy(IStrategy):
raise ValueError("Insufficient data for target calculation") raise ValueError("Insufficient data for target calculation")
try: try:
# 生成数值型标签1 表示上涨0 表示下跌 # Define target variable: 1 for price increase, 0 for price decrease
dataframe["&-up_or_down"] = np.where( dataframe["&-up_or_down"] = np.where(
dataframe["close"].shift(-50) > dataframe["close"], dataframe["close"].shift(-50) > dataframe["close"], 1, 0
1.0, # 数值型标签
0.0
) )
# Ensure target variable is a 2D array
dataframe["&-up_or_down"] = dataframe["&-up_or_down"].values.reshape(-1, 1)
except Exception as e: except Exception as e:
logger.error(f"Failed to create &-up_or_down column: {str(e)}") logger.error(f"Failed to create &-up_or_down column: {str(e)}")
raise 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") logger.info("FreqAI targets set successfully")
return dataframe return dataframe

View File

@ -1,5 +1,6 @@
import logging import logging
import numpy as np import numpy as np
import pandas as pd
from functools import reduce from functools import reduce
import talib.abstract as ta import talib.abstract as ta
from pandas import DataFrame from pandas import DataFrame
@ -9,241 +10,280 @@ from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class FreqaiExampleStrategy(IStrategy): class FreqaiExampleStrategy(IStrategy):
# 移除硬编码的 minimal_roi 和 stoploss改为动态适配 """
minimal_roi = {} # 将在 populate_indicators 中动态生成 FreqAI-based trading strategy using XGBoostRegressor for regression-based price movement prediction.
stoploss = 0.0 # 将在 populate_indicators 中动态设置 Optimized for short-term trading on spot markets (BTC/USDT, ETH/USDT, SOL/USDT).
Key improvements:
- Fixed KeyError in populate_entry_trend by using pd.concat for conditions
- Dynamic ATR-based stop-loss and ROI
- Enhanced feature engineering with cross-timeframe indicators
- Standardized and transformed target values to amplify signals
- Disabled DI filtering to resolve datasieve warnings
"""
# Strategy parameters
minimal_roi = {}
stoploss = 0.0
trailing_stop = True trailing_stop = True
process_only_new_candles = True process_only_new_candles = True
use_exit_signal = True use_exit_signal = True
startup_candle_count: int = 40 startup_candle_count: int = 40
can_short = False can_short = False
timeframe = "15m"
# 参数定义FreqAI 动态适配 buy_rsi 和 sell_rsi禁用 Hyperopt 优化 # Hyperopt parameters
buy_rsi = IntParameter(low=10, high=50, default=27, space="buy", optimize=False, load=True) buy_rsi = IntParameter(low=10, high=50, default=30, space="buy", optimize=True, load=True)
sell_rsi = IntParameter(low=50, high=90, default=59, space="sell", optimize=False, load=True) sell_rsi = IntParameter(low=50, high=90, default=70, space="sell", optimize=True, load=True)
roi_0 = DecimalParameter(low=0.01, high=0.1, default=0.05, space="roi", optimize=True, load=True)
roi_15 = DecimalParameter(low=0.005, high=0.05, default=0.03, space="roi", optimize=True, load=True)
roi_30 = DecimalParameter(low=0.001, high=0.03, default=0.01, space="roi", optimize=True, load=True)
stoploss_param = DecimalParameter(low=-0.2, high=-0.05, default=-0.1, space="stoploss", optimize=True, load=True)
# 为 Hyperopt 优化添加 ROI 和 stoploss 参数 # FreqAI configuration
roi_0 = DecimalParameter(low=0.01, high=0.2, default=0.038, space="roi", optimize=True, load=True)
roi_15 = DecimalParameter(low=0.005, high=0.1, default=0.027, space="roi", optimize=True, load=True)
roi_30 = DecimalParameter(low=0.001, high=0.05, default=0.009, space="roi", optimize=True, load=True)
stoploss_param = DecimalParameter(low=-0.35, high=-0.1, default=-0.182, space="stoploss", optimize=True, load=True)
# FreqAI 配置
freqai_info = { freqai_info = {
"model": "LightGBMRegressor", "model": "XGBoostRegressor",
"return_type": "raw", # Use raw regression predictions
"feature_parameters": { "feature_parameters": {
"include_timeframes": ["5m", "15m", "1h"], "include_timeframes": ["15m", "1h", "4h"],
"include_corr_pairlist": [], "include_corr_pairlist": ["BTC/USDT", "SOL/USDT", "ETH/USDT"],
"label_period_candles": 12, "label_period_candles": 60, # Extended prediction horizon
"include_shifted_candles": 3, "include_shifted_candles": 2,
"weight_factor": 0.9,
"principal_component_analysis": False,
"use_SVM_to_remove_outliers": True,
"SVM_parameters": {"nu": 0.1},
"DI_threshold": 0, # Disable DI filtering
"indicator_periods_candles": [14, 20]
}, },
"data_split_parameters": { "data_split_parameters": {
"test_size": 0.2, "test_size": 0.2,
"shuffle": False, "shuffle": True,
}, },
"model_training_parameters": { "model_training_parameters": {
"n_estimators": 100, "n_estimators": 300,
"learning_rate": 0.1, "learning_rate": 0.03,
"num_leaves": 31, "max_depth": 6,
"verbose": -1, "subsample": 0.8,
"colsample_bytree": 0.8,
"reg_lambda": 1.0,
"objective": "reg:squarederror",
"eval_metric": "rmse",
"early_stopping_rounds": 20,
"verbose": 0,
}, },
"data_kitchen": {
"feature_parameters": {
"DI_threshold": 0 # Ensure DI filtering is disabled
}
}
} }
plot_config = { def calculate_macd(self, dataframe: DataFrame) -> DataFrame:
"main_plot": {}, """Calculate MACD indicators and handle exceptions."""
"subplots": { try:
"&-buy_rsi": {"&-buy_rsi": {"color": "green"}}, macd = ta.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9)
"&-sell_rsi": {"&-sell_rsi": {"color": "red"}}, dataframe["macd"] = macd["macd"].replace([np.inf, -np.inf], np.nan).ffill().fillna(0)
"&-stoploss": {"&-stoploss": {"color": "purple"}}, dataframe["macdsignal"] = macd["macdsignal"].replace([np.inf, -np.inf], np.nan).ffill().fillna(0)
"&-roi_0": {"&-roi_0": {"color": "orange"}}, logger.info("MACD calculated successfully.")
"do_predict": {"do_predict": {"color": "brown"}}, except Exception as e:
}, logger.error(f"Error calculating MACD: {str(e)}")
} dataframe["macd"] = np.nan
dataframe["macdsignal"] = np.nan
return dataframe
def feature_engineering_expand_all(self, dataframe: DataFrame, period: int, metadata: dict, **kwargs) -> DataFrame: def feature_engineering_expand_all(self, dataframe: DataFrame, period: int, metadata: dict, **kwargs) -> DataFrame:
dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period) """Enhanced feature engineering with cross-timeframe and momentum indicators."""
dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period) # Standard technical indicators
dataframe["%-sma-period"] = ta.SMA(dataframe, timeperiod=period)
dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period)
dataframe["%-adx-period"] = ta.ADX(dataframe, timeperiod=period)
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=period, stds=2.2)
dataframe["bb_lowerband-period"] = bollinger["lower"]
dataframe["bb_middleband-period"] = bollinger["mid"]
dataframe["bb_upperband-period"] = bollinger["upper"]
dataframe["%-bb_width-period"] = (
dataframe["bb_upperband-period"] - dataframe["bb_lowerband-period"]
) / dataframe["bb_middleband-period"]
dataframe["%-close-bb_lower-period"] = dataframe["close"] / dataframe["bb_lowerband-period"]
dataframe["%-roc-period"] = ta.ROC(dataframe, timeperiod=period)
dataframe["%-relative_volume-period"] = (
dataframe["volume"] / dataframe["volume"].rolling(period).mean()
)
dataframe.replace([np.inf, -np.inf], 0, inplace=True)
dataframe.ffill(inplace=True)
dataframe.fillna(0, inplace=True)
return dataframe
def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
dataframe["%-pct-change"] = dataframe["close"].pct_change()
dataframe["%-raw_volume"] = dataframe["volume"]
dataframe["%-raw_price"] = dataframe["close"]
dataframe.replace([np.inf, -np.inf], 0, inplace=True)
dataframe.fillna(method='ffill', inplace=True)
dataframe.fillna(0, inplace=True)
return dataframe
def feature_engineering_standard(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek
dataframe["%-hour_of_day"] = dataframe["date"].dt.hour
dataframe.replace([np.inf, -np.inf], 0, inplace=True)
dataframe.fillna(method='ffill', inplace=True)
dataframe.fillna(0, inplace=True)
return dataframe
def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
logger.info(f"设置 FreqAI 目标,交易对:{metadata['pair']}")
if "close" not in dataframe.columns:
logger.error("数据框缺少必要的 'close'")
raise ValueError("数据框缺少必要的 'close'")
try:
label_period = self.freqai_info["feature_parameters"]["label_period_candles"]
# 生成 %-volatility 特征
dataframe["%-volatility"] = dataframe["close"].pct_change().rolling(20).std()
# 单一回归目标
dataframe["&-buy_rsi"] = ta.RSI(dataframe, timeperiod=14).shift(-label_period)
# 数据清理
for col in ["&-buy_rsi", "%-volatility"]:
dataframe[col].replace([np.inf, -np.inf], 0, inplace=True)
dataframe[col].fillna(method='ffill', inplace=True)
dataframe[col].fillna(0, inplace=True)
if dataframe[col].isna().any():
logger.warning(f"目标列 {col} 仍包含 NaN检查数据生成逻辑")
except Exception as e:
logger.error(f"创建 FreqAI 目标失败:{str(e)}")
raise
logger.info(f"目标列预览:\n{dataframe[['&-buy_rsi']].head().to_string()}")
return dataframe
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
logger.info(f"处理交易对:{metadata['pair']}")
dataframe = self.freqai.start(dataframe, metadata, self)
# 计算传统指标
dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14) dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
dataframe["sma20"] = ta.SMA(dataframe["close"], timeperiod=20)
dataframe["ema50"] = ta.EMA(dataframe["close"], timeperiod=50)
dataframe["atr"] = ta.ATR(dataframe["high"], dataframe["low"], dataframe["close"], timeperiod=14)
dataframe["obv"] = ta.OBV(dataframe["close"], dataframe["volume"])
dataframe["adx"] = ta.ADX(dataframe["high"], dataframe["low"], dataframe["close"], timeperiod=14)
dataframe["momentum"] = ta.MOM(dataframe["close"], timeperiod=14)
dataframe["price_sma_diff"] = (dataframe["close"] - dataframe["sma20"]) / dataframe["sma20"]
# Bollinger Bands
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
dataframe["bb_lowerband"] = bollinger["lower"] dataframe["bb_lowerband"] = bollinger["lower"]
dataframe["bb_middleband"] = bollinger["mid"] dataframe["bb_middleband"] = bollinger["mid"]
dataframe["bb_upperband"] = bollinger["upper"] dataframe["bb_upperband"] = bollinger["upper"]
dataframe["tema"] = ta.TEMA(dataframe, timeperiod=9) dataframe["bb_width"] = (bollinger["upper"] - bollinger["lower"]) / bollinger["mid"]
# 生成 up_or_down 信号(非 FreqAI 目标) # Cross-timeframe features
label_period = self.freqai_info["feature_parameters"]["label_period_candles"] for tf in ["1h", "4h"]:
dataframe["up_or_down"] = np.where( tf_data = self.dp.get_pair_dataframe(pair=metadata["pair"], timeframe=tf)
dataframe["close"].shift(-label_period) > dataframe["close"], 1, 0 if not tf_data.empty:
) dataframe[f"rsi_{tf}"] = ta.RSI(tf_data, timeperiod=14)
bollinger_tf = qtpylib.bollinger_bands(qtpylib.typical_price(tf_data), window=20, stds=2)
# 动态设置参数 dataframe[f"bb_width_{tf}"] = (bollinger_tf["upper"] - bollinger_tf["lower"]) / bollinger_tf["mid"]
if "&-buy_rsi" in dataframe.columns:
# 派生其他目标 # Correlated pair features
dataframe["&-sell_rsi"] = dataframe["&-buy_rsi"] + 30 if metadata["pair"] == "SOL/USDT":
dataframe["%-volatility"] = dataframe["close"].pct_change().rolling(20).std() btc_data = self.dp.get_pair_dataframe(pair="BTC/USDT", timeframe=self.timeframe)
dataframe["&-stoploss"] = -0.1 - (dataframe["%-volatility"] * 10).clip(0, 0.25) if not btc_data.empty:
dataframe["&-roi_0"] = (dataframe["close"].shift(-label_period) / dataframe["close"] - 1).clip(0, 0.2) dataframe["btc_rsi"] = ta.RSI(btc_data, timeperiod=14)
dataframe["btc_price_change"] = btc_data["close"].pct_change()
# 限制预测值,添加平滑 dataframe["btc_volatility"] = btc_data["close"].pct_change().rolling(20).std()
dataframe["buy_rsi_pred"] = dataframe["&-buy_rsi"].rolling(5).mean().clip(10, 50)
dataframe["buy_rsi_pred"].fillna(dataframe["buy_rsi_pred"].mean(), inplace=True) # Data cleaning
if dataframe["buy_rsi_pred"].isna().any(): for col in dataframe.columns:
logger.warning("buy_rsi_pred 列包含 NaN已填充为默认值") if dataframe[col].dtype in ["float64", "int64"]:
dataframe["sell_rsi_pred"] = dataframe["&-sell_rsi"].rolling(5).mean().clip(50, 90) dataframe[col] = dataframe[col].replace([np.inf, -np.inf], np.nan).ffill().fillna(dataframe[col].median())
dataframe["sell_rsi_pred"].fillna(dataframe["sell_rsi_pred"].mean(), inplace=True) logger.info(f"Feature engineering completed, features: {len(dataframe.columns)}")
if dataframe["sell_rsi_pred"].isna().any():
logger.warning("sell_rsi_pred 列包含 NaN已填充为默认值")
dataframe["stoploss_pred"] = dataframe["&-stoploss"].clip(-0.35, -0.1)
dataframe["stoploss_pred"].fillna(dataframe["stoploss_pred"].mean(), inplace=True)
if dataframe["stoploss_pred"].isna().any():
logger.warning("stoploss_pred 列包含 NaN已填充为默认值")
dataframe["roi_0_pred"] = dataframe["&-roi_0"].clip(0.01, 0.2)
dataframe["roi_0_pred"].fillna(dataframe["roi_0_pred"].mean(), inplace=True)
if dataframe["roi_0_pred"].isna().any():
logger.warning("roi_0_pred 列包含 NaN已填充为默认值")
# 检查预测值
for col in ["buy_rsi_pred", "sell_rsi_pred", "stoploss_pred", "roi_0_pred", "&-sell_rsi", "&-stoploss", "&-roi_0"]:
if dataframe[col].isna().any():
logger.warning(f"{col} 包含 NaN填充为默认值")
dataframe[col].fillna(dataframe[col].mean(), inplace=True)
# 动态追踪止盈
dataframe["trailing_stop_positive"] = (dataframe["roi_0_pred"] * 0.5).clip(0.01, 0.3)
dataframe["trailing_stop_positive_offset"] = (dataframe["roi_0_pred"] * 0.75).clip(0.02, 0.4)
# 设置策略级参数
self.buy_rsi.value = float(dataframe["buy_rsi_pred"].iloc[-1])
self.sell_rsi.value = float(dataframe["sell_rsi_pred"].iloc[-1])
self.stoploss = float(self.stoploss_param.value)
self.minimal_roi = {
0: float(self.roi_0.value),
15: float(self.roi_15.value),
30: float(self.roi_30.value),
60: 0
}
self.trailing_stop_positive = float(dataframe["trailing_stop_positive"].iloc[-1])
self.trailing_stop_positive_offset = float(dataframe["trailing_stop_positive_offset"].iloc[-1])
logger.info(f"动态参数buy_rsi={self.buy_rsi.value}, sell_rsi={self.sell_rsi.value}, "
f"stoploss={self.stoploss}, trailing_stop_positive={self.trailing_stop_positive}")
dataframe.replace([np.inf, -np.inf], 0, inplace=True)
dataframe.fillna(method='ffill', inplace=True)
dataframe.fillna(0, inplace=True)
logger.info(f"up_or_down 值统计:\n{dataframe['up_or_down'].value_counts().to_string()}")
logger.info(f"do_predict 值统计:\n{dataframe['do_predict'].value_counts().to_string()}")
return dataframe return dataframe
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame: def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
enter_long_conditions = [ """Basic feature engineering for FreqAI."""
qtpylib.crossed_above(df["rsi"], df["buy_rsi_pred"]), dataframe["%-pct-change"] = dataframe["close"].pct_change()
df["tema"] > df["tema"].shift(1), dataframe["%-raw_volume"] = dataframe["volume"]
df["volume"] > 0, dataframe["%-raw_price"] = dataframe["close"]
df["do_predict"] == 1, for col in dataframe.columns:
df["up_or_down"] == 1 if dataframe[col].dtype in ["float64", "int64"]:
] dataframe[col] = dataframe[col].replace([np.inf, -np.inf], np.nan).ffill().fillna(dataframe[col].median())
if enter_long_conditions: return dataframe
df.loc[
reduce(lambda x, y: x & y, enter_long_conditions),
["enter_long", "enter_tag"]
] = (1, "long")
return df
def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame: def feature_engineering_standard(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
exit_long_conditions = [ """Standard feature engineering for temporal features."""
qtpylib.crossed_above(df["rsi"], df["sell_rsi_pred"]), dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek
(df["close"] < df["close"].shift(1) * 0.97), dataframe["%-hour_of_day"] = dataframe["date"].dt.hour
df["volume"] > 0, for col in dataframe.columns:
df["do_predict"] == 1, if dataframe[col].dtype in ["float64", "int64"]:
df["up_or_down"] == 0 dataframe[col] = dataframe[col].replace([np.inf, -np.inf], np.nan).ffill().fillna(dataframe[col].median())
return dataframe
def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
"""Set FreqAI prediction targets with standardized and transformed values."""
logger.info(f"Setting FreqAI targets for pair: {metadata['pair']}")
if "close" not in dataframe.columns:
logger.error("DataFrame missing 'close' column")
raise ValueError("DataFrame missing 'close' column")
try:
label_period = self.freqai_info["feature_parameters"]["label_period_candles"]
dataframe["&-up_or_down"] = (
dataframe["close"].shift(-label_period) - dataframe["close"]
) / dataframe["close"]
# Standardize target values
dataframe["&-up_or_down"] = (
dataframe["&-up_or_down"] - dataframe["&-up_or_down"].mean()
) / dataframe["&-up_or_down"].std()
# Apply logarithmic transformation to amplify signals
dataframe["&-up_or_down"] = np.log1p(dataframe["&-up_or_down"].abs()) * np.sign(dataframe["&-up_or_down"])
dataframe["&-up_or_down"] = dataframe["&-up_or_down"].replace([np.inf, -np.inf], np.nan).ffill().fillna(0)
dataframe["&-buy_rsi"] = ta.RSI(dataframe, timeperiod=14)
dataframe["%-volatility"] = dataframe["close"].pct_change().rolling(20).std()
for col in ["&-buy_rsi", "&-up_or_down", "%-volatility"]:
dataframe[col] = dataframe[col].replace([np.inf, -np.inf], np.nan).ffill().fillna(dataframe[col].median())
except Exception as e:
logger.error(f"Failed to create FreqAI targets: {str(e)}")
raise
logger.info(f"Target column shape: {dataframe['&-up_or_down'].shape}")
logger.info(f"Target preview:\n{dataframe[['&-up_or_down', '&-buy_rsi']].head().to_string()}")
return dataframe
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""Populate indicators and dynamic strategy parameters."""
logger.info(f"Processing pair: {metadata['pair']}")
dataframe = self.freqai.start(dataframe, metadata, self)
# Calculate technical indicators
dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
dataframe["atr"] = ta.ATR(dataframe["high"], dataframe["low"], dataframe["close"], timeperiod=14)
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 = self.calculate_macd(dataframe)
# Dynamic parameter settings
if "&-buy_rsi" in dataframe.columns:
dataframe["&-sell_rsi"] = dataframe["&-buy_rsi"] + 20
dataframe["%-volatility"] = dataframe["close"].pct_change().rolling(20).std()
dataframe["&-stoploss"] = -2 * dataframe["atr"] / dataframe["close"]
dataframe["&-roi_0"] = (dataframe["close"] / dataframe["close"].shift(10) - 1).clip(0, 0.1).fillna(0)
# Dynamic predictions
dataframe["buy_rsi_pred"] = dataframe["rsi"].rolling(window=10).mean().clip(20, 50).fillna(dataframe["rsi"].median())
dataframe["sell_rsi_pred"] = dataframe["buy_rsi_pred"] + 20
dataframe["stoploss_pred"] = -2 * dataframe["atr"] / dataframe["close"].clip(-0.2, -0.05)
dataframe["roi_0_pred"] = dataframe["&-roi_0"].clip(0.01, 0.1).fillna(dataframe["&-roi_0"].mean())
for col in ["&-stoploss", "&-roi_0", "buy_rsi_pred", "sell_rsi_pred", "stoploss_pred", "roi_0_pred"]:
dataframe[col] = dataframe[col].replace([np.inf, -np.inf], np.nan).ffill().fillna(dataframe[col].mean())
# Set strategy parameters
self.buy_rsi.value = float(dataframe["buy_rsi_pred"].iloc[-1])
self.sell_rsi.value = float(dataframe["sell_rsi_pred"].iloc[-1])
self.stoploss = float(dataframe["stoploss_pred"].iloc[-1])
self.minimal_roi = {
0: float(self.roi_0.value) + (dataframe["atr"].iloc[-1] / dataframe["close"].iloc[-1]),
15: float(self.roi_15.value) * 0.8,
30: float(self.roi_30.value) * 0.5,
60: 0
}
# Dynamic trailing stop
self.trailing_stop_positive = float(1.5 * dataframe["atr"].iloc[-1] / dataframe["close"].iloc[-1])
self.trailing_stop_positive_offset = float(2.5 * dataframe["atr"].iloc[-1] / dataframe["close"].iloc[-1])
# Data cleaning
dataframe.ffill(inplace=True)
dataframe.fillna(dataframe.mean(numeric_only=True), inplace=True)
logger.info(f"&-up_or_down stats:\n{dataframe['&-up_or_down'].describe().to_string()}")
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""Generate entry signals with relaxed conditions."""
# Validate required columns
required_cols = ["rsi", "buy_rsi_pred", "volume", "bb_middleband", "macd", "macdsignal", "&-up_or_down"]
if not all(col in dataframe.columns for col in required_cols):
logger.error(f"Missing required columns: {set(required_cols) - set(dataframe.columns)}")
return dataframe
dataframe = self.calculate_macd(dataframe)
enter_long_conditions = [
dataframe["rsi"] < dataframe["buy_rsi_pred"],
dataframe["volume"] > dataframe["volume"].rolling(window=10).mean() * 1.0,
dataframe["close"] > dataframe["bb_middleband"],
dataframe["macd"] > dataframe["macdsignal"],
dataframe["&-up_or_down"] > (0.001 if metadata["pair"] == "BTC/USDT" else 0.0015) # Lower threshold for BTC
] ]
if exit_long_conditions: # Combine conditions into a DataFrame
df.loc[ conditions_df = pd.concat(enter_long_conditions, axis=1)
reduce(lambda x, y: x & y, exit_long_conditions), dataframe.loc[conditions_df.sum(axis=1) >= 3, ["enter_long", "enter_tag"]] = (1, "long")
"exit_long" return dataframe
] = 1
return df def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""Generate exit signals with confirmation."""
# Validate required columns
required_cols = ["rsi", "sell_rsi_pred", "bb_middleband", "macd", "macdsignal", "&-up_or_down"]
if not all(col in dataframe.columns for col in required_cols):
logger.error(f"Missing required columns: {set(required_cols) - set(dataframe.columns)}")
return dataframe
exit_long_conditions = [
dataframe["rsi"] > dataframe["sell_rsi_pred"],
dataframe["close"] < dataframe["bb_middleband"],
dataframe["macd"] < dataframe["macdsignal"],
dataframe["&-up_or_down"] < -0.005
]
# Combine conditions into a DataFrame
conditions_df = pd.concat(exit_long_conditions, axis=1)
# Require confirmation: at least 3 conditions met for two consecutive candles
exit_signal = (conditions_df.sum(axis=1) >= 3) & (conditions_df.shift(1).sum(axis=1) >= 3)
dataframe.loc[exit_signal, "exit_long"] = 1
return dataframe
def confirm_trade_entry( def confirm_trade_entry(
self, pair: str, order_type: str, amount: float, rate: float, self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time, entry_tag, side: str, **kwargs time_in_force: str, current_time, entry_tag, side: str, **kwargs
) -> bool: ) -> bool:
"""Confirm trade entry to avoid slippage."""
df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = df.iloc[-1].squeeze() last_candle = df.iloc[-1].squeeze()
if side == "long": if side == "long":
if rate > (last_candle["close"] * (1 + 0.0025)): if rate > (last_candle["close"] * (1 + 0.0025)):
return False return False
return True return True

233
请啊! Normal file
View File

@ -0,0 +1,233 @@
import logging
import numpy as np
from functools import reduce
import talib.abstract as ta
from pandas import DataFrame
from technical import qtpylib
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
logger = logging.getLogger(__name__)
class FreqaiExampleStrategy(IStrategy):
# 移除硬编码的 minimal_roi 和 stoploss改为动态适配
minimal_roi = {} # 将在 populate_indicators 中动态生成
stoploss = 0.0 # 将在 populate_indicators 中动态设置
trailing_stop = True
process_only_new_candles = True
use_exit_signal = True
startup_candle_count: int = 40
can_short = False
# 参数定义FreqAI 动态适配 buy_rsi 和 sell_rsi禁用 Hyperopt 优化
buy_rsi = IntParameter(low=10, high=50, default=27, space="buy", optimize=False, load=True)
sell_rsi = IntParameter(low=50, high=90, default=59, space="sell", optimize=False, load=True)
# 为 Hyperopt 优化添加 ROI 和 stoploss 参数
roi_0 = DecimalParameter(low=0.01, high=0.2, default=0.038, space="roi", optimize=True, load=True)
roi_15 = DecimalParameter(low=0.005, high=0.1, default=0.027, space="roi", optimize=True, load=True)
roi_30 = DecimalParameter(low=0.001, high=0.05, default=0.009, space="roi", optimize=True, load=True)
stoploss_param = DecimalParameter(low=-0.35, high=-0.1, default=-0.182, space="stoploss", optimize=True, load=True)
# FreqAI 配置
freqai_info = {
"model": "LightGBMRegressor",
"feature_parameters": {
"include_timeframes": ["5m", "15m", "1h"],
"include_corr_pairlist": [],
"label_period_candles": 12,
"include_shifted_candles": 3,
},
"data_split_parameters": {
"test_size": 0.2,
"shuffle": False,
},
"model_training_parameters": {
"n_estimators": 100,
"learning_rate": 0.1,
"num_leaves": 31,
"verbose": -1,
},
}
plot_config = {
"main_plot": {},
"subplots": {
"&-buy_rsi": {"&-buy_rsi": {"color": "green"}},
"&-sell_rsi": {"&-sell_rsi": {"color": "red"}},
"&-stoploss": {"&-stoploss": {"color": "purple"}},
"&-roi_0": {"&-roi_0": {"color": "orange"}},
"do_predict": {"do_predict": {"color": "brown"}},
},
}
def feature_engineering_expand_all(self, dataframe: DataFrame, period: int, metadata: dict, **kwargs) -> DataFrame:
dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period)
dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period)
dataframe["%-sma-period"] = ta.SMA(dataframe, timeperiod=period)
dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period)
dataframe["%-adx-period"] = ta.ADX(dataframe, timeperiod=period)
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=period, stds=2.2)
dataframe["bb_lowerband-period"] = bollinger["lower"]
dataframe["bb_middleband-period"] = bollinger["mid"]
dataframe["bb_upperband-period"] = bollinger["upper"]
dataframe["%-bb_width-period"] = (
dataframe["bb_upperband-period"] - dataframe["bb_lowerband-period"]
) / dataframe["bb_middleband-period"]
dataframe["%-close-bb_lower-period"] = dataframe["close"] / dataframe["bb_lowerband-period"]
dataframe["%-roc-period"] = ta.ROC(dataframe, timeperiod=period)
dataframe["%-relative_volume-period"] = (
dataframe["volume"] / dataframe["volume"].rolling(period).mean()
)
dataframe.replace([np.inf, -np.inf], 0, inplace=True)
dataframe.ffill(inplace=True)
dataframe.fillna(0, inplace=True)
return dataframe
def feature_engineering_expand_basic(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
dataframe["%-pct-change"] = dataframe["close"].pct_change()
dataframe["%-raw_volume"] = dataframe["volume"]
dataframe["%-raw_price"] = dataframe["close"]
dataframe.replace([np.inf, -np.inf], 0, inplace=True)
dataframe.fillna(method='ffill', inplace=True)
dataframe.fillna(0, inplace=True)
return dataframe
def feature_engineering_standard(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek
dataframe["%-hour_of_day"] = dataframe["date"].dt.hour
dataframe.replace([np.inf, -np.inf], 0, inplace=True)
dataframe.fillna(method='ffill', inplace=True)
dataframe.fillna(0, inplace=True)
return dataframe
def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
logger.info(f"设置 FreqAI 目标,交易对:{metadata['pair']}")
if "close" not in dataframe.columns:
logger.error("数据框缺少必要的 'close' 列")
raise ValueError("数据框缺少必要的 'close' 列")
try:
label_period = self.freqai_info["feature_parameters"]["label_period_candles"]
# 生成 %-volatility 特征
dataframe["%-volatility"] = dataframe["close"].pct_change().rolling(20).std()
# 单一回归目标
# 移除对未来的数据依赖
dataframe["&-buy_rsi"] = ta.RSI(dataframe, timeperiod=14)
# 数据清理
for col in ["&-buy_rsi", "%-volatility"]:
dataframe[col].replace([np.inf, -np.inf], 0, inplace=True)
dataframe[col].fillna(method='ffill', inplace=True)
dataframe[col].fillna(0, inplace=True)
if dataframe[col].isna().any():
logger.warning(f"目标列 {col} 仍包含 NaN检查数据生成逻辑")
except Exception as e:
logger.error(f"创建 FreqAI 目标失败:{str(e)}")
raise
logger.info(f"目标列预览:\n{dataframe[['&-buy_rsi']].head().to_string()}")
return dataframe
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
logger.info(f"处理交易对:{metadata['pair']}")
dataframe = self.freqai.start(dataframe, metadata, self)
# 计算传统指标
dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
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["tema"] = ta.TEMA(dataframe, timeperiod=9)
# 生成 up_or_down 信号(非 FreqAI 目标)
label_period = self.freqai_info["feature_parameters"]["label_period_candles"]
# 使用历史数据生成 up_or_down 信号
dataframe["up_or_down"] = np.where(
dataframe["close"] > dataframe["close"].shift(1), 1, 0
)
# 动态设置参数
if "&-buy_rsi" in dataframe.columns:
# 派生其他目标
dataframe["&-sell_rsi"] = dataframe["&-buy_rsi"] + 30
dataframe["%-volatility"] = dataframe["close"].pct_change().rolling(20).std()
dataframe["&-stoploss"] = -0.1 - (dataframe["%-volatility"] * 10).clip(0, 0.25)
dataframe["&-roi_0"] = (dataframe["close"] / dataframe["close"].shift(label_period) - 1).clip(0, 0.2)
# 简化动态参数生成逻辑
dataframe["buy_rsi_pred"] = dataframe["&-buy_rsi"].clip(10, 50)
dataframe["sell_rsi_pred"] = dataframe["&-buy_rsi"] + 30
dataframe["stoploss_pred"] = -0.1 - (dataframe["%-volatility"] * 10).clip(0, 0.25)
dataframe["roi_0_pred"] = dataframe["&-roi_0"].clip(0.01, 0.2)
# 检查预测值
for col in ["buy_rsi_pred", "sell_rsi_pred", "stoploss_pred", "roi_0_pred", "&-sell_rsi", "&-stoploss", "&-roi_0"]:
if dataframe[col].isna().any():
logger.warning(f"列 {col} 包含 NaN填充为默认值")
dataframe[col].fillna(dataframe[col].mean(), inplace=True)
# 更保守的止损和止盈设置
dataframe["trailing_stop_positive"] = (dataframe["roi_0_pred"] * 0.3).clip(0.01, 0.2)
dataframe["trailing_stop_positive_offset"] = (dataframe["roi_0_pred"] * 0.5).clip(0.01, 0.3)
# 设置策略级参数
self.buy_rsi.value = float(dataframe["buy_rsi_pred"].iloc[-1])
self.sell_rsi.value = float(dataframe["sell_rsi_pred"].iloc[-1])
self.stoploss = float(self.stoploss_param.value)
self.minimal_roi = {
0: float(self.roi_0.value),
15: float(self.roi_15.value),
30: float(self.roi_30.value),
60: 0
}
self.trailing_stop_positive = float(dataframe["trailing_stop_positive"].iloc[-1])
self.trailing_stop_positive_offset = float(dataframe["trailing_stop_positive_offset"].iloc[-1])
logger.info(f"动态参数buy_rsi={self.buy_rsi.value}, sell_rsi={self.sell_rsi.value}, "
f"stoploss={self.stoploss}, trailing_stop_positive={self.trailing_stop_positive}")
dataframe.replace([np.inf, -np.inf], 0, inplace=True)
dataframe.fillna(method='ffill', inplace=True)
dataframe.fillna(0, inplace=True)
logger.info(f"up_or_down 值统计:\n{dataframe['up_or_down'].value_counts().to_string()}")
logger.info(f"do_predict 值统计:\n{dataframe['do_predict'].value_counts().to_string()}")
return dataframe
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
enter_long_conditions = [
qtpylib.crossed_above(df["rsi"], df["buy_rsi_pred"]),
df["volume"] > 0
]
if enter_long_conditions:
df.loc[
reduce(lambda x, y: x & y, enter_long_conditions),
["enter_long", "enter_tag"]
] = (1, "long")
return df
def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
exit_long_conditions = [
qtpylib.crossed_above(df["rsi"], df["sell_rsi_pred"]),
df["volume"] > 0
]
if exit_long_conditions:
df.loc[
reduce(lambda x, y: x & y, exit_long_conditions),
"exit_long"
] = 1
return df
def confirm_trade_entry(
self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time, entry_tag, side: str, **kwargs
) -> bool:
df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = df.iloc[-1].squeeze()
if side == "long":
if rate > (last_candle["close"] * (1 + 0.0025)):
return False
return True