This commit is contained in:
zhangkun9038@dingtalk.com 2025-05-19 10:45:49 +00:00
commit 4e512554ba
16 changed files with 509 additions and 2626 deletions

1
.gitignore vendored
View File

@ -1,5 +1,4 @@
# Freqtrade rules
config*.json
*.sqlite
*.sqlite-shm
*.sqlite-wal

2
aaa
View File

@ -1,2 +0,0 @@
https://www.freqtrade.io/en/stable/strategy/custom-stakeamount/

View File

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

View File

@ -1,128 +0,0 @@
{
"$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,
"use_exit_signal": true,
"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": "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,
"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"
}
}

View File

@ -31,24 +31,36 @@ 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
# command: >
# backtesting
# --logfile /freqtrade/user_data/logs/freqtrade.log
# --freqaimodel XGBoostRegressor
# --freqaimodel LightGBMRegressor
# --config /freqtrade/config_examples/config_freqai.okx.json
# --strategy-path /freqtrade/templates
# --strategy FreqaiExampleStrategy
# --breakdown week month
# --timerange 20250310-20250410
# --export trades
# command: >
# hyperopt
# --logfile /freqtrade/user_data/logs/freqtrade.log
# --freqaimodel LightGBMRegressor
# --config /freqtrade/config_examples/config_freqai.okx.json
# --strategy-path /freqtrade/templates
# --strategy FreqaiExampleStrategy
# --timerange 20250301-20250420
# --hyperopt-loss SharpeHyperOptLoss
# --spaces roi stoploss trailing
# -e 200
# command: >
# hyperopt
# --logfile /freqtrade/user_data/logs/freqtrade.log

7
filter.py Normal file
View File

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

View File

@ -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 和止损规则管理仓位
这个策略结合了传统技术分析和机器学习方法,通过模型预测来增强交易信号的准确性,同时保持了严格的风险控制机制。

View File

@ -1,277 +0,0 @@
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 FreqaiPrimer(IStrategy):
minimal_roi = {
0: 0.135,
9: 0.052,
15: 0.007,
60: 0
}
stoploss = -0.263
trailing_stop = True
trailing_stop_positive = 0.324
trailing_stop_positive_offset = 0.411
trailing_only_offset_is_reached = False
max_open_trades = 4
process_only_new_candles = True
use_exit_signal = True
startup_candle_count: int = 40
can_short = False
buy_rsi = IntParameter(low=10, high=50, default=30, space="buy", optimize=False, load=True)
sell_rsi = IntParameter(low=50, high=90, default=70, space="sell", optimize=False, load=True)
roi_0 = DecimalParameter(low=0.01, high=0.2, default=0.135, space="roi", optimize=True, load=True)
roi_15 = DecimalParameter(low=0.005, high=0.1, default=0.052, space="roi", optimize=True, load=True)
roi_30 = DecimalParameter(low=0.001, high=0.05, default=0.007, space="roi", optimize=True, load=True)
stoploss_param = DecimalParameter(low=-0.35, high=-0.1, default=-0.263, space="stoploss", optimize=True, load=True)
trailing_stop_positive_param = DecimalParameter(low=0.1, high=0.5, default=0.324, space="trailing", optimize=True, load=True)
trailing_stop_positive_offset_param = DecimalParameter(low=0.2, high=0.6, default=0.411, space="trailing", optimize=True, load=True)
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": 200,
"learning_rate": 0.05,
"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 = dataframe.replace([np.inf, -np.inf], 0)
dataframe = dataframe.ffill()
dataframe = dataframe.fillna(0)
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 = dataframe.replace([np.inf, -np.inf], 0)
dataframe = dataframe.ffill()
dataframe = dataframe.fillna(0)
return dataframe
def feature_engineering_standard(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
if len(dataframe["close"]) < 20:
logger.warning(f"数据不足 {len(dataframe)} 根 K 线,%-volatility 可能不完整")
dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek
dataframe["%-hour_of_day"] = dataframe["date"].dt.hour
dataframe["%-volatility"] = dataframe["close"].pct_change().rolling(20, min_periods=1).std()
dataframe["%-volatility"] = dataframe["%-volatility"].replace([np.inf, -np.inf], 0)
dataframe["%-volatility"] = dataframe["%-volatility"].ffill()
dataframe["%-volatility"] = dataframe["%-volatility"].fillna(0)
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"]
if "%-volatility" not in dataframe.columns:
logger.warning("缺少 %-volatility 列,强制重新生成")
dataframe["%-volatility"] = dataframe["close"].pct_change().rolling(20, min_periods=1).std()
dataframe["%-volatility"] = dataframe["%-volatility"].replace([np.inf, -np.inf], 0)
dataframe["%-volatility"] = dataframe["%-volatility"].ffill()
dataframe["%-volatility"] = dataframe["%-volatility"].fillna(0)
dataframe["&-buy_rsi"] = ta.RSI(dataframe, timeperiod=14)
dataframe["&-buy_rsi"] = dataframe["&-buy_rsi"].shift(-label_period).ffill().bfill()
for col in ["&-buy_rsi", "%-volatility"]:
dataframe[col] = dataframe[col].replace([np.inf, -np.inf], 0)
dataframe[col] = dataframe[col].ffill()
dataframe[col] = dataframe[col].fillna(0)
if dataframe[col].isna().any():
logger.warning(f"目标列 {col} 仍包含 NaN数据预览\n{dataframe[col].tail(10)}")
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']}")
logger.debug(f"输入特征列:{list(dataframe.columns)}")
dataframe = self.freqai.start(dataframe, metadata, self)
logger.debug(f"FreqAI 输出特征列:{list(dataframe.columns)}")
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)
label_period = self.freqai_info["feature_parameters"]["label_period_candles"]
dataframe["up_or_down"] = np.where(
dataframe["close"].shift(-label_period) > dataframe["close"], 1, 0
)
if "&-buy_rsi" in dataframe.columns:
if "%-volatility" not in dataframe.columns:
logger.warning("缺少 %-volatility 列,强制重新生成")
dataframe["%-volatility"] = dataframe["close"].pct_change().rolling(20, min_periods=1).std()
dataframe["%-volatility"] = dataframe["%-volatility"].replace([np.inf, -np.inf], 0)
dataframe["%-volatility"] = dataframe["%-volatility"].ffill()
dataframe["%-volatility"] = dataframe["%-volatility"].fillna(0)
dataframe["&-sell_rsi"] = dataframe["&-buy_rsi"] + 30
dataframe["&-stoploss"] = self.stoploss - (dataframe["%-volatility"] * 5).clip(-0.05, 0.05)
dataframe["&-roi_0"] = (dataframe["close"].shift(-label_period) / dataframe["close"] - 1).clip(0, 0.2)
for col in ["&-buy_rsi", "&-sell_rsi", "&-stoploss", "&-roi_0"]:
dataframe[col] = dataframe[col].replace([np.inf, -np.inf], 0)
dataframe[col] = dataframe[col].ffill()
dataframe[col] = dataframe[col].fillna(0)
dataframe["buy_rsi_pred"] = dataframe["&-buy_rsi"].rolling(5).mean().clip(10, 50)
dataframe["sell_rsi_pred"] = dataframe["&-sell_rsi"].rolling(5).mean().clip(50, 90)
dataframe["stoploss_pred"] = dataframe["&-stoploss"].clip(-0.35, -0.1)
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"]:
if dataframe[col].isna().any():
logger.warning(f"{col} 包含 NaN填充为默认值")
dataframe[col] = dataframe[col].ffill()
dataframe[col] = dataframe[col].fillna(dataframe[col].mean())
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(self.trailing_stop_positive_param.value)
self.trailing_stop_positive_offset = float(self.trailing_stop_positive_offset_param.value)
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}")
else:
logger.warning(f"&-buy_rsi 列缺失,跳过 FreqAI 预测逻辑,检查 freqai.start 输出")
dataframe = dataframe.replace([np.inf, -np.inf], 0)
dataframe = dataframe.ffill()
dataframe = dataframe.fillna(0)
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()}")
logger.debug(f"最终特征列:{list(dataframe.columns)}")
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["tema"] > df["tema"].shift(1),
df["volume"] > 0,
df["do_predict"] == 1,
df["up_or_down"] == 1
]
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["close"] < df["close"].shift(1) * 0.97),
df["volume"] > 0,
df["do_predict"] == 1,
df["up_or_down"] == 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:
try:
df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if df is None or df.empty:
logger.warning(f"无法获取 {pair} 的分析数据,拒绝交易")
return False
last_candle = df.iloc[-1].squeeze()
if "close" not in last_candle or np.isnan(last_candle["close"]):
logger.warning(f"{pair} 的最新 K 线缺少有效 close 价格,拒绝交易")
return False
if side == "long":
max_rate = last_candle["close"] * (1 + 0.0025) # 0.25% 滑点阈值
if rate > max_rate:
logger.debug(f"拒绝 {pair} 的买入,价格 {rate} 超过最大允许价格 {max_rate}")
return False
elif side == "short":
logger.warning(f"{pair} 尝试做空,但策略不支持做空 (can_short={self.can_short})")
return False
logger.debug(f"确认 {pair} 的交易side={side}, rate={rate}, close={last_candle['close']}")
return True
except Exception as e:
logger.error(f"确认 {pair} 交易时出错:{str(e)}")
return False

30
run.sh Executable file
View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -60,11 +60,14 @@ if [[ "$@" == *"--timerange"* ]] && [[ "$@" == *"--days"* ]]; then
fi
# Get timerange or days from parameters
timerange=""
days=""
if [[ "$@" == *"--timerange"* ]]; then
timerange=$(get_param_value "--timerange" "$@")
elif [[ "$@" == *"--days"* ]]; then
days=$(get_param_value "--days" "$@")
fi
# Get pairs and timeframe from parameters or use defaults
pairs=$(get_csv_param_value "--pairs" "$@")
timeframe=$(get_csv_param_value "--timeframe" "$@")
@ -75,18 +78,22 @@ if [[ -z "$pairs" ]]; then
fi
if [[ -z "$timeframe" ]]; then
timeframe="3m,5m,15m,30m,1h,4h,6h,12h,1d"
timeframe="5m,15m,30m,1h,4h,6h,12h,1d"
fi
# Convert timeframe string to array
IFS=',' read -r -a timeframe_array <<<"$timeframe"
timeframe_array_str=$(printf " '%s'" "${timeframe_array[@]}")
# Initialize the base command
cmd="docker-compose run --rm freqtrade download-data --config /freqtrade/config_examples/basic.json --pairs $pairs --timeframe $timeframe"
cmd="docker-compose run --rm freqtrade download-data --config /freqtrade/config_examples/basic.json --pairs $pairs --timeframe$timeframe_array_str"
# Add timerange or days if provided
if [[ -n "$timerange" ]]; then
cmd+=" --timerange $timerange"
cmd+=" --timerange='$timerange'"
elif [[ -n "$days" ]]; then
cmd+=" --days $days"
cmd+=" --days=$days"
fi
# Execute the command
eval $cmd
eval "$cmd"

View File

@ -1,4 +1,26 @@
#!/bin/bash
# 加载 .env 文件中的变量
if [ -f ".env" ]; then
export $(grep -v '^#' .env | xargs)
else
echo "⚠️ 本地缺少 .env 文件,请创建并配置。"
exit 1
fi
# 获取当前分支名称
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
# 检查当前分支是否匹配 TEST_BRANCH 或 DRYRUN_BRANCH
if [[ "$CURRENT_BRANCH" != "$TEST_BRANCH" && "$CURRENT_BRANCH" != "$DRYRUN_BRANCH" ]]; then
echo "⚠️ 错误:当前分支 '$CURRENT_BRANCH' 不符合环境变量配置的预期。"
echo "请确保当前分支与 .env 文件中的 TEST_BRANCH 或 DRYRUN_BRANCH 配置一致。"
echo "TEST_BRANCH=$TEST_BRANCH"
echo "DRYRUN_BRANCH=$DRYRUN_BRANCH"
exit 1
else
echo "✅ 当前分支 '$CURRENT_BRANCH' 符合环境变量配置要求。"
fi
# 检查当前分支名称是否包含 "dryrun"
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [[ "$CURRENT_BRANCH" != *"dryrun"* ]]; then
@ -33,7 +55,6 @@ if [ ! -f ".env" ]; then
echo ""
exit 1
fi
# 加载 .env 文件中的变量
export $(grep -v '^#' .env | xargs)
# 设置默认值

42
tools/mergeme.sh Executable file
View File

@ -0,0 +1,42 @@
#!/bin/bash
set -e # 出错立即停止
# 获取参数
source_branch=$1
target_branch=$2
# 确保传入了两个参数
if [ -z "$source_branch" ] || [ -z "$target_branch" ]; then
echo "❌ 使用方法: $0 <源分支> <目标分支>"
echo "示例: $0 theforce-noai-test base"
exit 1
fi
# 定位到 Git 仓库根目录
cd "$(git rev-parse --show-toplevel)"
# 获取当前分支名
current_branch=$(git rev-parse --abbrev-ref HEAD)
# 如果当前不在 source_branch 上,则切换过去
if [ "$current_branch" != "$source_branch" ]; then
echo "🔄 当前在分支 '$current_branch',正在切换到 '$source_branch'"
git checkout "$source_branch"
fi
echo "📥 正在拉取 '$source_branch' 的最新代码..."
git pull origin "$source_branch"
echo "📥 正在拉取目标分支 '$target_branch' 的最新代码..."
git fetch origin "$target_branch"
echo "🔄 正在合并 '$target_branch' 到 '$source_branch'"
git merge "origin/$target_branch" -m "Merge branch '$target_branch' into $source_branch"
# 如果有冲突,需要手动解决,脚本会在此处中断
echo "📤 正在推送合并后的代码到远程 '$source_branch'"
git push origin "$source_branch"
echo "✅ 合并完成并已推送到远程分支 '$source_branch'"

11
tools/mergetemp.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
source_branch=$0
dest_branch=$1
path=$3
cd $path
git reset --hard
git checkout $dest_branch
git pull
git checkout $source_branch
git merge $dest_branch

65
tools/view_feather.py Normal file
View File

@ -0,0 +1,65 @@
import argparse
import pandas as pd
def analyze_candlestick_data(file_path):
# 读取feather文件
df = pd.read_feather(file_path)
# 查看数据集行数和列数
rows, columns = df.shape
if rows < 500:
# 短表数据行数少于500查看全量数据信息
print('数据全部内容信息:')
print(df.to_csv(sep='\t', na_rep='nan'))
else:
# 长表数据查看数据前几行信息
print('数据前几行内容信息:')
print(df.head().to_csv(sep='\t', na_rep='nan'))
# 查看数据的基本信息
print('数据基本信息:')
df.info()
# 查看数据集行数和列数
rows, columns = df.shape
if columns < 10 and rows < 500:
# 短表窄数据列少于10且行数少于500查看全量统计信息
print('数据全部内容描述性统计信息:')
print(df.describe(include='all', percentiles=[.25, .5, .75]).to_csv(sep='\t', na_rep='nan'))
else:
# 长表数据查看数据前几行统计信息
print('数据前几行描述性统计信息:')
print(df.head().describe(include='all', percentiles=[.25, .5, .75]).to_csv(sep='\t', na_rep='nan'))
# 计算时间跨度
min_date = df['date'].min()
max_date = df['date'].max()
time_span = max_date - min_date
# 检查时间序列完整性
df = df.sort_values('date') # 确保数据按时间排序
df['time_diff'] = df['date'].diff().dt.total_seconds() # 计算相邻时间点的差值(秒)
expected_freq = df['time_diff'].mode()[0] # 使用最常见的间隔作为预期频率
missing_intervals = df[df['time_diff'] > expected_freq] # 找出间隔大于预期的位置
print(f"\n数据时间跨度:{time_span}")
print(f"开始时间:{min_date}")
print(f"结束时间:{max_date}")
if missing_intervals.empty:
print("数据完整性:完整,未发现缺失的蜡烛图数据")
else:
print(f"数据完整性:不完整,发现 {len(missing_intervals)} 处可能的缺失")
print("缺失位置示例:")
for _, row in missing_intervals.head(5).iterrows(): # 显示前5个缺失示例
gap_duration = pd.Timedelta(seconds=row['time_diff'])
print(f" - 在 {row['date']} 之前缺失了 {gap_duration}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='分析Freqtrade蜡烛图Feather文件')
parser.add_argument('--path', required=True, help='Feather文件路径')
args = parser.parse_args()
analyze_candlestick_data(args.path)