second
This commit is contained in:
parent
17199e9a44
commit
b816a42eab
1
.gitignore
vendored
1
.gitignore
vendored
@ -114,6 +114,7 @@ target/
|
|||||||
!config_examples/config_full.example.json
|
!config_examples/config_full.example.json
|
||||||
!config_examples/config_kraken.example.json
|
!config_examples/config_kraken.example.json
|
||||||
!config_examples/config_freqai.example.json
|
!config_examples/config_freqai.example.json
|
||||||
|
!config_examples/*.json
|
||||||
|
|
||||||
docker-compose-*.yml
|
docker-compose-*.yml
|
||||||
data/
|
data/
|
||||||
|
|||||||
130
config_examples/config_freqai.okx.json
Normal file
130
config_examples/config_freqai.okx.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -22,6 +22,7 @@ services:
|
|||||||
- "./user_data:/freqtrade/user_data"
|
- "./user_data:/freqtrade/user_data"
|
||||||
- "./config_examples:/freqtrade/config_examples"
|
- "./config_examples:/freqtrade/config_examples"
|
||||||
- "./freqtrade/templates:/freqtrade/templates"
|
- "./freqtrade/templates:/freqtrade/templates"
|
||||||
|
- "./freqtrade/exchange/:/freqtrade/exchange"
|
||||||
# Expose api on port 8080 (localhost only)
|
# Expose api on port 8080 (localhost only)
|
||||||
# Please read the https://www.freqtrade.io/en/stable/rest-api/ documentation
|
# Please read the https://www.freqtrade.io/en/stable/rest-api/ documentation
|
||||||
# for more information.
|
# for more information.
|
||||||
@ -30,12 +31,23 @@ services:
|
|||||||
# Default command used when running `docker compose up`
|
# Default command used when running `docker compose up`
|
||||||
|
|
||||||
# --freqaimodel XGBoostRegressor
|
# --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: >
|
command: >
|
||||||
trade
|
backtesting
|
||||||
--logfile /freqtrade/user_data/logs/freqtrade.log
|
--logfile /freqtrade/user_data/logs/freqtrade.log
|
||||||
--db-url sqlite:////freqtrade/user_data/tradesv3.sqlite
|
--freqaimodel XGBoostRegressor
|
||||||
--freqaimodel LightGBMRegressor
|
--config /freqtrade/config_examples/config_freqai.okx.json
|
||||||
--config /freqtrade/config_examples/config_freqai.okx.json
|
--strategy-path /freqtrade/templates
|
||||||
--strategy FreqaiExampleStrategy
|
--strategy FreqaiExampleStrategy
|
||||||
--strategy-path /freqtrade/templates
|
--timerange 20250320-20250420
|
||||||
|
--export trades
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -88,13 +88,11 @@ class FreqaiExampleHybridStrategy(IStrategy):
|
|||||||
stoploss = -0.05
|
stoploss = -0.05
|
||||||
use_exit_signal = True
|
use_exit_signal = True
|
||||||
startup_candle_count: int = 30
|
startup_candle_count: int = 30
|
||||||
can_short = True
|
can_short = False
|
||||||
|
|
||||||
# Hyperoptable parameters
|
# Hyperoptable parameters
|
||||||
buy_rsi = IntParameter(low=1, high=50, default=30, space="buy", optimize=True, load=True)
|
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)
|
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(
|
def feature_engineering_expand_all(
|
||||||
self, dataframe: DataFrame, period: int, metadata: dict, **kwargs
|
self, dataframe: DataFrame, period: int, metadata: dict, **kwargs
|
||||||
@ -210,35 +208,65 @@ class FreqaiExampleHybridStrategy(IStrategy):
|
|||||||
dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek
|
dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek
|
||||||
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:
|
||||||
"""
|
logger.info(f"Setting FreqAI targets for pair: {metadata['pair']}")
|
||||||
*Only functional with FreqAI enabled strategies*
|
logger.info(f"DataFrame shape: {dataframe.shape}")
|
||||||
Required function to set the targets for the model.
|
logger.info(f"Available columns: {list(dataframe.columns)}")
|
||||||
All targets must be prepended with `&` to be recognized by the FreqAI internals.
|
logger.info(f"First few rows:\n{dataframe[['date', 'close']].head().to_string()}")
|
||||||
|
|
||||||
More details about feature engineering available:
|
if "close" not in dataframe.columns:
|
||||||
|
logger.error("Required 'close' column missing in dataframe")
|
||||||
https://www.freqtrade.io/en/latest/freqai-feature-engineering
|
raise ValueError("Required 'close' column missing in dataframe")
|
||||||
|
|
||||||
:param dataframe: strategy dataframe which will receive the targets
|
if len(dataframe) < 50:
|
||||||
:param metadata: metadata of current pair
|
logger.error(f"Insufficient data: {len(dataframe)} rows, need at least 50 for shift(-50)")
|
||||||
usage example: dataframe["&-target"] = dataframe["close"].shift(-1) / dataframe["close"]
|
raise ValueError("Insufficient data for target calculation")
|
||||||
"""
|
|
||||||
self.freqai.class_names = ["down", "up"]
|
try:
|
||||||
dataframe["&s-up_or_down"] = np.where(
|
# 生成数值型标签:1 表示上涨,0 表示下跌
|
||||||
dataframe["close"].shift(-50) > dataframe["close"], "up", "down"
|
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
|
return dataframe
|
||||||
|
|
||||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # noqa: C901
|
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
# User creates their own custom strat here. Present example is a supertrend
|
logger.info(f"Processing pair: {metadata['pair']}")
|
||||||
# based strategy.
|
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
|
# RSI
|
||||||
dataframe["rsi"] = ta.RSI(dataframe)
|
dataframe["rsi"] = ta.RSI(dataframe)
|
||||||
|
|
||||||
@ -254,67 +282,30 @@ class FreqaiExampleHybridStrategy(IStrategy):
|
|||||||
"bb_middleband"
|
"bb_middleband"
|
||||||
]
|
]
|
||||||
|
|
||||||
# TEMA - Triple Exponential Moving Average
|
# TEMA
|
||||||
dataframe["tema"] = ta.TEMA(dataframe, timeperiod=9)
|
dataframe["tema"] = ta.TEMA(dataframe, timeperiod=9)
|
||||||
|
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
||||||
df.loc[
|
df.loc[
|
||||||
(
|
(
|
||||||
# Signal: RSI crosses above 30
|
|
||||||
(qtpylib.crossed_above(df["rsi"], self.buy_rsi.value))
|
(qtpylib.crossed_above(df["rsi"], self.buy_rsi.value))
|
||||||
& (df["tema"] <= df["bb_middleband"]) # Guard: tema below BB middle
|
& (df["tema"] <= df["bb_middleband"])
|
||||||
& (df["tema"] > df["tema"].shift(1)) # Guard: tema is raising
|
& (df["tema"] > df["tema"].shift(1))
|
||||||
& (df["volume"] > 0) # Make sure Volume is not 0
|
& (df["volume"] > 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")
|
|
||||||
),
|
),
|
||||||
"enter_long",
|
"enter_long",
|
||||||
] = 1
|
] = 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
|
return df
|
||||||
|
|
||||||
def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
||||||
df.loc[
|
df.loc[
|
||||||
(
|
(
|
||||||
# Signal: RSI crosses above 70
|
|
||||||
(qtpylib.crossed_above(df["rsi"], self.sell_rsi.value))
|
(qtpylib.crossed_above(df["rsi"], self.sell_rsi.value))
|
||||||
& (df["tema"] > df["bb_middleband"]) # Guard: tema above BB middle
|
& (df["tema"] > df["bb_middleband"])
|
||||||
& (df["tema"] < df["tema"].shift(1)) # Guard: tema is falling
|
& (df["tema"] < df["tema"].shift(1))
|
||||||
& (df["volume"] > 0) # Make sure Volume is not 0
|
& (df["volume"] > 0)
|
||||||
),
|
),
|
||||||
"exit_long",
|
"exit_long",
|
||||||
] = 1
|
] = 1
|
||||||
|
return df
|
||||||
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
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user