diff --git a/.gitignore b/.gitignore index 3d5dd445..4a0c14e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ # Freqtrade rules -config*.json *.sqlite *.sqlite-shm *.sqlite-wal diff --git a/config_examples/confg_base_hyperopt.json b/config_examples/confg_base_hyperopt.json new file mode 100644 index 00000000..f72f2766 --- /dev/null +++ b/config_examples/confg_base_hyperopt.json @@ -0,0 +1,59 @@ +{ + "exchange": { + "name": "okx", + "key": "your_api_key", + "secret": "your_api_secret", + "ccxt_config": { + "enableRateLimit": true, + "defaultType": "spot", + "rateLimit": 2000, + "timeout": 30000 + }, + "pair_whitelist": ["OKB/USDT", "SOL/USDT"] + }, + "pairlists": [ + { + "method": "StaticPairList" + } + ], + "timeframe": "3m", + "strategy": "", + "order_types": { + "entry": "market", + "exit": "market", + "stoploss": "limit", + "stoploss_on_exchange": false + }, + "order_time_in_force": { + "entry": "gtc", + "exit": "gtc" + }, + "entry_pricing": { + "price_side": "other", + "use_order_book": false, + "price_last_balance": 0.0 + }, + "exit_pricing": { + "price_side": "other", + "use_order_book": false + }, + "use_exit_signal": true, + "exit_profit_only": false, + "ignore_roi_if_entry_signal": false, + "stoploss": -0.05, + "minimal_roi": { + "0": 0.06, + "30": 0.04, + "60": 0.02, + "120": 0 + }, + "stake_currency": "USDT", + "stake_amount": 150, + "max_open_trades": 4, + "unfilledtimeout": { + "entry": 5, + "exit": 15, + "unit": "minutes" + }, + "fee": 0.0008 +} diff --git a/config_examples/config.json b/config_examples/config.json new file mode 100644 index 00000000..4b836a8b --- /dev/null +++ b/config_examples/config.json @@ -0,0 +1,84 @@ +{ + "exchange": { + "name": "okx", + "key": "your_api_key", + "secret": "your_api_secret", + "ccxt_config": { + "enableRateLimit": true, + "defaultType": "spot", + "rateLimit": 2000, + "timeout": 30000 + }, + "pair_whitelist": ["OKB/USDT", "SOL/USDT"] + }, + "pairlists": [ + { + "method": "StaticPairList" + } + ], + "timeframe": "15m", + "strategy": "NostalgiaForInfinityV8", + "freqai": { + "enabled": true, + "freqaimodel": "LightGBMRegressor", + "train_period_days": 7, + "backtest_period_days": 0.2, + "feature_parameters": { + "include_timeframes": ["15m"], + "include_corr_pairlist": ["OKB/USDT", "SOL/USDT"], + "label_period_candles": 4, + "include_shifted_candles": 0, + "indicator_periods_candles": [5, 10, 20], + "include_default_features": ["close", "open", "high", "low", "volume", "rsi", "ema", "sma"], + "DI_threshold": 0.3 + }, + "data_split_parameters": { + "test_size": 0.2, + "shuffle": false + }, + "model_training_parameters": { + "n_estimators": 100, + "learning_rate": 0.1, + "num_leaves": 15, + "verbose": -1 + } + }, + "order_types": { + "entry": "market", + "exit": "market", + "stoploss": "limit", + "stoploss_on_exchange": false + }, + "order_time_in_force": { + "entry": "gtc", + "exit": "gtc" + }, + "entry_pricing": { + "price_side": "other", + "use_order_book": false, + "price_last_balance": 0.0 + }, + "exit_pricing": { + "price_side": "other", + "use_order_book": false + }, + "use_exit_signal": true, + "exit_profit_only": false, + "ignore_roi_if_entry_signal": false, + "stoploss": -0.05, + "minimal_roi": { + "0": 0.06, + "30": 0.04, + "60": 0.02, + "120": 0 + }, + "stake_currency": "USDT", + "stake_amount": 150, + "max_open_trades": 4, + "unfilledtimeout": { + "entry": 5, + "exit": 15, + "unit": "minutes" + }, + "fee": 0.0008 +} diff --git a/config_examples/config_adsf.json b/config_examples/config_adsf.json new file mode 100644 index 00000000..9bec863f --- /dev/null +++ b/config_examples/config_adsf.json @@ -0,0 +1,127 @@ +{ + "$schema": "https://schema.freqtrade.io/schema.json", + "trading_mode": "spot", + "margin_mode": "isolated", + "max_open_trades": 4, + "stake_currency": "USDT", + "stake_amount": 150, + "startup_candle_count": 30, + "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.05, + "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": [ + "OKB/USDT" + ], + "pair_blacklist": [] + }, + "entry_pricing": { + "price_side": "other", + "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, + "identifier": "test175", + "freqaimodel": "XGBoostRegressor", + "model_path": "/freqtrade/user_data/models", + "save_backtesting_prediction": true, + "purge_old_models": true, + "load_trained_model": true, + "train_period_days": 90, + "backtest_period_days": 10, + "live_retrain_hours": 0, + "include_predictions_in_final_dataframe": true, + "data_kitchen": { + "fillna": "ffill", + "feature_parameters": { + "DI_threshold": 0.5, + "weight_factor": 0.9 + } + }, + "feature_parameters": { + "include_timeframes": ["5m", "15m", "1h"], + "include_corr_pairlist": ["BTC/USDT", "ETH/USDT"], + "label_period_candles": 12, + "include_shifted_candles": 3, + "indicator_periods_candles": [10, 20, 50], + "plot_feature_importances": 1, + "feature_selection": { + "method": "none" + } + }, + "data_split_parameters": { + "test_size": 0.2, + "shuffle": false, + "random_state": 42 + }, + "model_training_parameters": { + "n_estimators": 200, + "learning_rate": 0.05, + "max_depth": 6, + "subsample": 0.8, + "colsample_bytree": 0.8, + "reg_alpha": 0.1, + "reg_lambda": 1.0 + }, + }, + "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 c2428fca..4186c4f2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,12 +30,22 @@ services: # Default command used when running `docker compose up` # --freqaimodel XGBoostRegressor - 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 + # 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 + command: > + backtesting + --logfile /freqtrade/user_data/logs/freqtrade.log + --config /freqtrade/config_examples/config_adsf.json + --strategy-path /freqtrade/templates + --freqaimodel LightGBMRegressor + --strategy NostalgiaForInfinityV8 + --timerange 20250401-20250415 + --fee 0.0008 + --cache none diff --git a/filter.py b/filter.py new file mode 100644 index 00000000..cbec0976 --- /dev/null +++ b/filter.py @@ -0,0 +1,7 @@ +with open('output.log', 'r') as input_file, open('output_filted.log', 'w') as output_file: + for line in input_file: + if "validation_0" in line: + if "[99]" in line: + output_file.write(line) + else: + output_file.write(line) diff --git a/freqtrade/templates/NostalgiaV8.py b/freqtrade/templates/NostalgiaV8.py new file mode 100644 index 00000000..bf4c1a14 --- /dev/null +++ b/freqtrade/templates/NostalgiaV8.py @@ -0,0 +1,248 @@ +import numpy as np +import pandas as pd +pd.options.mode.chained_assignment = None +from pandas import DataFrame +from freqtrade.strategy import IStrategy +from freqtrade.strategy import IntParameter +import logging +from functools import reduce +import datetime + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + +logger = logging.getLogger(__name__) + +class NostalgiaForInfinityV8(IStrategy): + INTERFACE_VERSION = 3 + + minimal_roi = { + "0": 0.06, + "30": 0.04, + "60": 0.02, + "120": 0 + } + + stoploss = -0.05 + use_custom_stoploss = True + trailing_stop = False + timeframe = '15m' + process_only_new_candles = True + use_exit_signal = True + exit_profit_only = False + ignore_roi_if_entry_signal = False + startup_candle_count: int = 146 + + can_short = False + + order_types = { + 'entry': 'market', + 'exit': 'market', + 'stoploss': 'limit', + 'stoploss_on_exchange': False + } + + order_time_in_force = { + 'entry': 'gtc', + 'exit': 'gtc' + } + + buy_rsi = IntParameter(low=20, high=60, default=50, space="buy", optimize=True) + sell_rsi = IntParameter(low=50, high=80, default=55, space="sell", optimize=True) + buy_volume_factor = IntParameter(low=1, high=5, default=2, space="buy", optimize=True) + + freqai_info = { + "model": "XGBoostRegressor", + "feature_parameters": { + "include_timeframes": ["15m"], + "include_corr_pairlist": ["OKB/USDT", "SOL/USDT"], + "label_period_candles": 4, + "include_shifted_candles": 0, + "indicator_periods_candles": [5, 10, 20], + "include_default_features": ["close", "open", "high", "low", "volume", "rsi", "ema", "sma"] + }, + "data_split_parameters": { + "test_size": 0.2, + "shuffle": False + }, + "model_training_parameters": { + "n_estimators": 100, + "learning_rate": 0.1, + "num_leaves": 15, + "n_jobs": 4, + "verbosity": -1 + } + } + + plot_config = { + 'main_plot': { + 'ema6': {'color': 'blue'}, + 'ema24': {'color': 'red'}, + 'sma32': {'color': 'green'}, + 'sma64': {'color': 'purple'}, + }, + 'subplots': { + "RSI": {'rsi': {'color': 'blue'}, 'buy_rsi_pred': {'color': 'orange'}} + } + } + + def detect_pivot_low(self, series: pd.Series, left: int, right: int) -> pd.Series: + pivots = pd.Series(False, index=series.index) + for i in range(left, len(series) - right): + if (series[i] < series[i - left:i]).all() and (series[i] < series[i + 1:i + right + 1]).all(): + pivots.iloc[i] = True + return pivots + + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + if dataframe.empty: + logger.warning(f"Empty dataframe for {metadata['pair']}, skipping indicators") + return dataframe + + informative = dataframe.copy() + + # Momentum Indicators + informative['rsi'] = ta.RSI(informative, timeperiod=14) + + # EMA and SMA + informative['ema6'] = ta.EMA(informative, timeperiod=6) + informative['ema24'] = ta.EMA(informative, timeperiod=24) + informative['sma32'] = ta.SMA(informative, timeperiod=32) + informative['sma64'] = ta.SMA(informative, timeperiod=64) + + # ATR for dynamic stoploss + informative['atr'] = ta.ATR(informative, timeperiod=14) + + # Volume + informative['volume_mean'] = informative['volume'].rolling(20).mean() + + # RSI Divergence using custom pivot detection + informative['price_pivot_low'] = self.detect_pivot_low(informative['close'], left=3, right=3) + informative['rsi_pivot_low'] = self.detect_pivot_low(informative['rsi'], left=3, right=3) + informative['bullish_divergence'] = (informative['price_pivot_low'] == True) & (informative['rsi_pivot_low'] != True) + + # 设置 FreqAI 标签 + informative = self.set_freqai_targets(informative) + + # 启动 FreqAI + try: + informative = self.freqai.start(informative, metadata, self) + logger.info(f"FreqAI features for {metadata['pair']}: {informative.columns.tolist()}") + except Exception as e: + logger.error(f"FreqAI failed for {metadata['pair']}: {str(e)}") + informative['buy_rsi_pred'] = informative['rsi'] + informative['do_predict'] = 0 + + logger.info(f"Indicators for {metadata['pair']} at {informative['date'].iloc[-1]}: " + f"RSI={informative['rsi'].iloc[-1]:.2f}, " + f"buy_rsi_pred={informative['buy_rsi_pred'].iloc[-1] if 'buy_rsi_pred' in informative else 'N/A'}, " + f"do_predict={informative['do_predict'].iloc[-1]}, " + f"EMA6={informative['ema6'].iloc[-1]:.2f}, " + f"EMA24={informative['ema24'].iloc[-1]:.2f}, " + f"Bullish_Divergence={informative['bullish_divergence'].iloc[-1]}") + + return informative + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + if dataframe.empty: + logger.warning(f"Empty dataframe for {metadata['pair']}, skipping indicators") + return dataframe + + informative = dataframe.copy() + + # Momentum Indicators + informative['rsi'] = ta.RSI(informative, timeperiod=14) + + # EMA and SMA + informative['ema6'] = ta.EMA(informative, timeperiod=6) + informative['ema24'] = ta.EMA(informative, timeperiod=24) + informative['sma32'] = ta.SMA(informative, timeperiod=32) + informative['sma64'] = ta.SMA(informative, timeperiod=64) + + # ATR for dynamic stoploss + informative['atr'] = ta.ATR(informative, timeperiod=14) + + # Volume + informative['volume_mean'] = informative['volume'].rolling(20).mean() + + # RSI Divergence using custom pivot detection + informative['price_pivot_low'] = self.detect_pivot_low(informative['close'], left=3, right=3) + informative['rsi_pivot_low'] = self.detect_pivot_low(informative['rsi'], left=3, right=3) + informative['bullish_divergence'] = (informative['price_pivot_low'] == True) & (informative['rsi_pivot_low'] != True) + + # 设置 FreqAI 标签(✅ 已修复:删除了非法标记) + informative = self.set_freqai_targets(informative, metadata) + + # 启动 FreqAI + try: + informative = self.freqai.start(informative, metadata, self) + logger.info(f"FreqAI features for {metadata['pair']}: {informative.columns.tolist()}") + except Exception as e: + logger.error(f"FreqAI failed for {metadata['pair']}: {str(e)}") + informative['buy_rsi_pred'] = informative['rsi'] + informative['do_predict'] = 0 + + logger.info(f"Indicators for {metadata['pair']} at {informative['date'].iloc[-1]}: " + f"RSI={informative['rsi'].iloc[-1]:.2f}, " + f"buy_rsi_pred={informative['buy_rsi_pred'].iloc[-1] if 'buy_rsi_pred' in informative else 'N/A'}, " + f"do_predict={informative['do_predict'].iloc[-1]}, " + f"EMA6={informative['ema6'].iloc[-1]:.2f}, " + f"EMA24={informative['ema24'].iloc[-1]:.2f}, " + f"Bullish_Divergence={informative['bullish_divergence'].iloc[-1]}") + + return informative + def set_freqai_targets(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + label_period = self.freqai_info["feature_parameters"]["label_period_candles"] + dataframe['label'] = ( + (dataframe['close'].shift(-label_period) / dataframe['close']) - 1 + ) * 100 # 百分比变化 + return dataframe + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + if dataframe.empty: + logger.warning(f"Empty dataframe for {metadata['pair']}, skipping exit trend") + return dataframe + + exit_long_conditions = [ + dataframe['rsi'] > self.sell_rsi.value, + dataframe['ema6'] < dataframe['ema24'], + dataframe['volume'] > 0 + ] + + dataframe.loc[ + reduce(lambda x, y: x & y, exit_long_conditions), + 'exit_long' + ] = 1 + return dataframe + + def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, + current_rate: float, current_profit: float, **kwargs) -> float: + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + if dataframe.empty: + logger.warning(f"Empty dataframe for {pair} in custom_stoploss") + return self.stoploss + + last_candle = dataframe.iloc[-1].squeeze() + atr_stoploss = last_candle['close'] - 1.5 * last_candle['atr'] + if atr_stoploss < current_rate: + return (atr_stoploss / current_rate) - 1 + return self.stoploss + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + if dataframe.empty: + logger.warning(f"Empty dataframe for {metadata['pair']}, skipping entry trend") + return dataframe + + # 示例买入条件: + conditions = [ + dataframe['rsi'] < self.buy_rsi.value, + dataframe['ema6'] > dataframe['ema24'], + dataframe['volume'] > 0, + dataframe['bullish_divergence'] == True, + dataframe['do_predict'] == 1, + dataframe['rsi'] < dataframe['buy_rsi_pred'] + ] + + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'enter_long' + ] = 1 + + return dataframe diff --git a/freqtrade/templates/Supertrend.py b/freqtrade/templates/Supertrend.py new file mode 100644 index 00000000..0797974e --- /dev/null +++ b/freqtrade/templates/Supertrend.py @@ -0,0 +1,69 @@ +from functools import reduce +from pandas import DataFrame +from freqtrade.strategy import IStrategy + +import talib.abstract as ta + +from freqtrade.strategy.interface import IStrategy + +class TrendFollowingStrategy(IStrategy): + + INTERFACE_VERSION: int = 3 + # ROI table: + minimal_roi = {"0": 0.15, "30": 0.1, "60": 0.05} + # minimal_roi = {"0": 1} + + # Stoploss: + stoploss = -0.265 + + # Trailing stop: + trailing_stop = True + trailing_stop_positive = 0.05 + trailing_stop_positive_offset = 0.1 + trailing_only_offset_is_reached = False + can_short=False + + timeframe = "5m" + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Calculate OBV + dataframe['obv'] = ta.OBV(dataframe['close'], dataframe['volume']) + + # Add your trend following indicators here + dataframe['trend'] = dataframe['close'].ewm(span=20, adjust=False).mean() + + return dataframe + + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add your trend following buy signals here + dataframe.loc[ + (dataframe['close'] > dataframe['trend']) & + (dataframe['close'].shift(1) <= dataframe['trend'].shift(1)) & + (dataframe['obv'] > dataframe['obv'].shift(1)), + 'enter_long'] = 1 + + # Add your trend following sell signals here + dataframe.loc[ + (dataframe['close'] < dataframe['trend']) & + (dataframe['close'].shift(1) >= dataframe['trend'].shift(1)) & + (dataframe['obv'] < dataframe['obv'].shift(1)), + 'enter_short'] = 1 + + return dataframe + + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add your trend following exit signals for long positions here + dataframe.loc[ + (dataframe['close'] < dataframe['trend']) & + (dataframe['close'].shift(1) >= dataframe['trend'].shift(1)) & + (dataframe['obv'] > dataframe['obv'].shift(1)), + 'exit_long'] = 1 + + # Add your trend following exit signals for short positions here + dataframe.loc[ + (dataframe['close'] > dataframe['trend']) & + (dataframe['close'].shift(1) <= dataframe['trend'].shift(1)) & + (dataframe['obv'] < dataframe['obv'].shift(1)), + 'exit_short'] = 1 + + return dataframe diff --git a/freqtrade/templates/chat.md b/freqtrade/templates/chat.md new file mode 100644 index 00000000..6b0707d0 --- /dev/null +++ b/freqtrade/templates/chat.md @@ -0,0 +1,85 @@ +> 请把当前策略的运行逻辑进行梳理和讲解 + +我来为您梳理和讲解这个策略的运行逻辑。这是一个基于 FreqAI 的机器学习交易策略,主要使用 RSI 指标和机器学习模型进行交易决策。以下是策略的主要逻辑: + +### 1. 策略配置 +```python +minimal_roi = { + "0": 0.02, + "7": 0.01, + "13": 0.005, + "60": 0 +} +stoploss = 0.0 +trailing_stop = True +``` +- 设置了静态的 ROI 目标 +- 启用了追踪止损 + +### 2. 特征工程 +策略通过多个方法构建特征: +- `feature_engineering_expand_all`: 计算 RSI 和布林带宽度 +- `feature_engineering_expand_basic`: 计算价格变化百分比 +- `feature_engineering_standard`: 计算波动率和时间特征 + +### 3. 目标变量设置 +在 `set_freqai_targets` 方法中: +- 使用未来 `label_period_candles` 周期的 RSI 值作为目标变量 +- 计算并标准化波动率特征 + +### 4. 指标计算 +`populate_indicators` 方法中计算了多个技术指标: +- RSI +- 波动率 +- TEMA(三重指数移动平均线) +- 布林带下轨 +- 未来价格方向(up_or_down) + +### 5. 机器学习模型 +使用 LightGBM 回归模型: +```python +freqai_info = { + "model": "LightGBMRegressor", + "feature_parameters": {...}, + "data_split_parameters": {...}, + "model_training_parameters": {...} +} +``` +- 模型预测买入和卖出的 RSI 值 +- 使用 SOL/USDT 作为相关交易对 + +### 6. 入场逻辑 +`populate_entry_trend` 方法中: +- 当实际 RSI 上穿预测的买入 RSI 时 +- TEMA 指标向上 +- 交易量大于 0 +- 模型预测结果为 1 +- 未来价格方向为上涨 + +### 7. 出场逻辑 +`populate_exit_trend` 方法中: +- 当实际 RSI 上穿预测的卖出 RSI 时 +- 或价格下跌超过 2% +- 或价格跌破布林带下轨 +- 或持有时间超过 1 天 + +### 8. 交易确认 +`confirm_trade_entry` 方法: +- 检查当前价格是否在可接受范围内 +- 记录交易确认信息 + +### 策略特点 +1. 机器学习驱动:使用 LightGBM 模型预测 RSI 值 +2. 多特征融合:结合技术指标、波动率和时间特征 +3. 风险控制:设置了 ROI 目标和追踪止损 +4. 动态调整:通过机器学习模型动态调整交易信号 + +### 运行流程 +1. 数据准备:获取历史数据,计算技术指标 +2. 特征工程:构建机器学习特征 +3. 模型训练:使用历史数据训练 LightGBM 模型 +4. 信号生成:根据模型预测结果生成交易信号 +5. 交易执行:根据入场和出场逻辑执行交易 +6. 风险控制:根据 ROI 和止损规则管理仓位 + +这个策略结合了传统技术分析和机器学习方法,通过模型预测来增强交易信号的准确性,同时保持了严格的风险控制机制。 diff --git a/freqtrade/templates/harmonicDivergence.py b/freqtrade/templates/harmonicDivergence.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/freqtrade/templates/harmonicDivergence.py @@ -0,0 +1 @@ + diff --git a/freqtrade/templates/hyper.py b/freqtrade/templates/hyper.py new file mode 100644 index 00000000..e69de29b diff --git a/freqtrade/templates/sma.py b/freqtrade/templates/sma.py new file mode 100644 index 00000000..6f35a0fb --- /dev/null +++ b/freqtrade/templates/sma.py @@ -0,0 +1,130 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# flake8: noqa: F401 +# isort: skip_file +# --- Do not remove these libs --- +from functools import reduce +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy import ( + BooleanParameter, + CategoricalParameter, + DecimalParameter, + IStrategy, + IntParameter, +) + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class FAdxSmaStrategy(IStrategy): + + INTERFACE_VERSION = 3 + timeframe = "1h" + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi". + minimal_roi = {"60": 0.075, "30": 0.1, "0": 0.05} + # minimal_roi = {"0": 1} + + stoploss = -0.05 + can_short = False + + # Trailing stoploss + trailing_stop = False + # trailing_only_offset_is_reached = False + # trailing_stop_positive = 0.01 + # trailing_stop_positive_offset = 0.0 # Disabled / not configured + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = True + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 14 + + # Hyperoptable parameters + + # Define the guards spaces + pos_entry_adx = DecimalParameter(15, 40, decimals=1, default=30.0, space="buy") + pos_exit_adx = DecimalParameter(15, 40, decimals=1, default=30.0, space="sell") + + # Define the parameter spaces + adx_period = IntParameter(4, 24, default=14) + sma_short_period = IntParameter(4, 24, default=12) + sma_long_period = IntParameter(12, 175, default=48) + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # Calculate all adx values + for val in self.adx_period.range: + dataframe[f"adx_{val}"] = ta.ADX(dataframe, timeperiod=val) + + # Calculate all sma_short values + for val in self.sma_short_period.range: + dataframe[f"sma_short_{val}"] = ta.SMA(dataframe, timeperiod=val) + + # Calculate all sma_long values + for val in self.sma_long_period.range: + dataframe[f"sma_long_{val}"] = ta.SMA(dataframe, timeperiod=val) + + return dataframe + + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions_long = [] + conditions_short = [] + + # GUARDS AND TRIGGERS + conditions_long.append( + dataframe[f"adx_{self.adx_period.value}"] > self.pos_entry_adx.value + ) + conditions_short.append( + dataframe[f"adx_{self.adx_period.value}"] > self.pos_entry_adx.value + ) + + conditions_long.append( + qtpylib.crossed_above( + dataframe[f"sma_short_{self.sma_short_period.value}"], + dataframe[f"sma_long_{self.sma_long_period.value}"], + ) + ) + conditions_short.append( + qtpylib.crossed_below( + dataframe[f"sma_short_{self.sma_short_period.value}"], + dataframe[f"sma_long_{self.sma_long_period.value}"], + ) + ) + + dataframe.loc[ + reduce(lambda x, y: x & y, conditions_long), + "enter_long", + ] = 1 + + dataframe.loc[ + reduce(lambda x, y: x & y, conditions_short), + "enter_short", + ] = 1 + + return dataframe + + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions_close = [] + conditions_close.append( + dataframe[f"adx_{self.adx_period.value}"] < self.pos_entry_adx.value + ) + + dataframe.loc[ + reduce(lambda x, y: x & y, conditions_close), + "exit_long", + ] = 1 + + dataframe.loc[ + reduce(lambda x, y: x & y, conditions_close), + "exit_short", + ] = 1 + + return dataframe diff --git a/run.sh b/run.sh new file mode 100755 index 00000000..d2188480 --- /dev/null +++ b/run.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +rm -rf user_data/models/* +rm -rf ./freqtrade/user_data/data/backtest_results/* + +docker-compose run --rm freqtrade >output.log 2>&1 +sed -i 's/\x1B\[[0-9;]*m//g' output.log +python3 filter.py + +rm ./result/* -fr +mv ./user_data/backtest_results/* ./result/ + +cd ./result +# 查找当前目录下的所有 zip 文件 +zip_files=(*.zip) + +# 检查是否只有一个 zip 文件 +if [ ${#zip_files[@]} -eq 1 ]; then + # 解压缩该 zip 文件到当前目录 + unzip "${zip_files[0]}" + rm *.zip + rm *.feather +else + echo "当前目录下没有 zip 文件或者有多个 zip 文件,无法操作。" +fi + +cd - +sed -i 's/\x1B\[[0-9;]*m//g' output.log +python3 filter.py +cp output_filted.log result/ -f diff --git a/tests/config_test_comments.json b/tests/config_test_comments.json new file mode 100644 index 00000000..19d82c45 --- /dev/null +++ b/tests/config_test_comments.json @@ -0,0 +1,129 @@ +{ + /* Single-line C-style comment */ + "max_open_trades": 3, + /* + * Multi-line C-style comment + */ + "stake_currency": "BTC", + "stake_amount": 0.05, + "fiat_display_currency": "USD", // C++-style comment + "amount_reserve_percent": 0.05, // And more, tabs before this comment + "dry_run": false, + "timeframe": "5m", + "trailing_stop": false, + "trailing_stop_positive": 0.005, + "trailing_stop_positive_offset": 0.0051, + "trailing_only_offset_is_reached": false, + "minimal_roi": { + "40": 0.0, + "30": 0.01, + "20": 0.02, + "0": 0.04 + }, + "stoploss": -0.10, + "unfilledtimeout": { + "buy": 10, + "sell": 30, // Trailing comma should also be accepted now + }, + "bid_strategy": { + "use_order_book": false, + "ask_last_balance": 0.0, + "order_book_top": 1, + "check_depth_of_market": { + "enabled": false, + "bids_to_ask_delta": 1 + } + }, + "ask_strategy": { + "use_order_book": false, + "order_book_min": 1, + "order_book_max": 9 + }, + "order_types": { + "buy": "limit", + "sell": "limit", + "stoploss": "market", + "stoploss_on_exchange": false, + "stoploss_on_exchange_interval": 60 + }, + "order_time_in_force": { + "buy": "gtc", + "sell": "gtc" + }, + "pairlist": { + "method": "VolumePairList", + "config": { + "number_assets": 20, + "sort_key": "quoteVolume", + "precision_filter": false + } + }, + "exchange": { + "name": "binance", + "sandbox": false, + "key": "your_exchange_key", + "secret": "your_exchange_secret", + "password": "", + "ccxt_config": { + "enableRateLimit": true + }, + "ccxt_async_config": { + "enableRateLimit": false, + "rateLimit": 500, + "aiohttp_trust_env": false + }, + "pair_whitelist": [ + "ETH/BTC", + "LTC/BTC", + "ETC/BTC", + "DASH/BTC", + "ZEC/BTC", + "XLM/BTC", + "NXT/BTC", + "TRX/BTC", + "ADA/BTC", + "XMR/BTC" + ], + "pair_blacklist": [ + "DOGE/BTC" + ], + "outdated_offset": 5, + "markets_refresh_interval": 60 + }, + "edge": { + "enabled": false, + "process_throttle_secs": 3600, + "calculate_since_number_of_days": 7, + "allowed_risk": 0.01, + "stoploss_range_min": -0.01, + "stoploss_range_max": -0.1, + "stoploss_range_step": -0.01, + "minimum_winrate": 0.60, + "minimum_expectancy": 0.20, + "min_trade_number": 10, + "max_trade_duration_minute": 1440, + "remove_pumps": false + }, + "telegram": { + // We can now comment out some settings + // "enabled": true, + "enabled": false, + "token": "your_telegram_token", + "chat_id": "your_telegram_chat_id" + }, + "api_server": { + "enabled": false, + "listen_ip_address": "127.0.0.1", + "listen_port": 8080, + "username": "freqtrader", + "password": "SuperSecurePassword" + }, + "db_url": "sqlite:///tradesv3.sqlite", + "initial_state": "running", + "forcebuy_enable": false, + "internals": { + "process_throttle_secs": 5 + }, + "strategy": "DefaultStrategy", + "strategy_path": "user_data/strategies/" +} \ No newline at end of file