This commit is contained in:
Ubuntu 2025-04-22 15:43:04 +08:00
parent 17199e9a44
commit b816a42eab
4 changed files with 214 additions and 80 deletions

1
.gitignore vendored
View File

@ -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/

View File

@ -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"
}
}

View File

@ -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

View File

@ -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