myTestFreqAI/tools/hyperopt_org.sh
2025-11-21 09:41:18 +08:00

506 lines
16 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# 环境检查 - 只支持Linux
if [[ "$(uname)" != "Linux" ]]; then
echo "错误此脚本仅支持Linux环境不支持macOS或其他操作系统。"
echo "请在Linux系统上运行此脚本。"
exit 1
fi
# Function to extract the value of a parameter
get_param_value() {
local param="$1"
shift
local args=($@)
local i=0
while [ $i -lt ${#args[@]} ]; do
case "${args[$i]}" in
$param=*)
echo "${args[$i]#*=}"
return 0
;;
$param)
# Check if the next argument exists and does not start with a dash
if [ $((i + 1)) -lt ${#args[@]} ] && [[ "${args[$((i + 1))]}" != -* ]]; then
echo "${args[$((i + 1))]}"
return 0
fi
# 如果参数存在但没有值,返回特殊标识"PARAM_WITHOUT_VALUE"
echo "PARAM_WITHOUT_VALUE"
return 0
;;
esac
i=$((i + 1))
done
# 如果参数不存在,返回空字符串
echo ""
return 0
}
# 检查 .env 文件
if [ ! -f ".env" ]; then
echo "⚠️ 本地缺少 .env 文件,请创建并配置。示例内容如下:"
echo ""
echo "STRATEGY_NAME=TheForceV7"
echo "CONFIG_FILE=basic.json"
echo "TEST_BRANCH=theforce-noai-test"
echo "DRYRUN_BRANCH=theforce-noai-dryrun"
echo ""
exit 1
fi
# 加载 .env 文件中的变量
if [ -f .env ]; then
set -a
source .env
set +a
fi
# 设置默认值
STRATEGY_NAME=${STRATEGY_NAME:-TheForceV7}
CONFIG_FILE=${CONFIG_FILE:-basic.json}
TEST_BRANCH=${TEST_BRANCH:-develop}
# --- 新增:解析多个 --spaces 参数 ---
# 提取所有 --spaces 后的值(支持多个 --spaces 参数)
SPACES_VALUES=()
for arg in "$@"; do
if [[ "$prev_arg" == "--spaces" ]] || [[ "$prev_arg" == "--space" ]]; then
if [[ ! "$arg" =~ ^-- ]]; then
SPACES_VALUES+=("$arg")
fi
fi
prev_arg="$arg"
done
# 如果有多个 --spaces 值,合并为空格分隔的字符串
if [ ${#SPACES_VALUES[@]} -gt 0 ]; then
SPACES="${SPACES_VALUES[@]}"
else
# 使用get_param_value作为备选方案兼容旧格式
SPACES_ARG=$(get_param_value "--spaces" "$@")
if [ -z "$SPACES_ARG" ]; then
SPACES_ARG=$(get_param_value "--space" "$@")
if [ -z "$SPACES_ARG" ]; then
SPACES="buy"
else
SPACES="$SPACES_ARG"
fi
else
SPACES="$SPACES_ARG"
fi
fi
echo "Using hyperopt spaces: $SPACES"
EPOCHS_ARG=$(get_param_value "--epochs" "$@")
if [ -z "$EPOCHS_ARG" ]; then
EPOCHS=100
else
EPOCHS="$EPOCHS_ARG"
fi
echo "Using epochs: $EPOCHS"
RANDOM_STATE_ARG=$(get_param_value "--random-state" "$@")
if [ -z "$RANDOM_STATE_ARG" ]; then
RANDOM_STATE=19
else
RANDOM_STATE="$RANDOM_STATE_ARG"
fi
echo "Using random-state: $RANDOM_STATE"
# --- 新增:解析 --jobs 或 -j 参数 ---
JOBS_ARG=$(get_param_value "--jobs" "$@")
if [ -z "$JOBS_ARG" ]; then
JOBS_ARG=$(get_param_value "-j" "$@")
if [ -z "$JOBS_ARG" ]; then
JOBS=4
else
JOBS="$JOBS_ARG"
fi
else
JOBS="$JOBS_ARG"
fi
echo "Using jobs: $JOBS"
# Initialize variables
START_DATE_RAW=""
END_DATE_RAW=""
PAIRS_ARG=""
PAIR_REMOTE_LIST_URL=""
STRATEGY_ARG=""
CONFIG_ARG=""
# 首先尝试从位置参数获取日期(无论是否有命名参数)
# 保存原始参数列表
ORIG_ARGS=("$@")
# 检查前两个参数是否是日期格式8位数字
if [ $# -gt 0 ] && [[ "$1" =~ ^[0-9]{8}$ ]]; then
START_DATE_RAW="$1"
if [ $# -gt 1 ] && [[ "$2" =~ ^[0-9]{8}$ ]]; then
END_DATE_RAW="$2"
# 移除已处理的日期参数
shift 2
fi
fi
# 处理所有参数,包括命名参数
while [ $# -gt 0 ]; do
case "$1" in
--start-date)
if [ $# -gt 1 ] && [[ "$2" != -* ]]; then
START_DATE_RAW="$2"
shift 2
else
shift
fi
;;
--end-date)
if [ $# -gt 1 ] && [[ "$2" != -* ]]; then
END_DATE_RAW="$2"
shift 2
else
shift
fi
;;
--pairs)
if [ $# -gt 1 ] && [[ "$2" != -* ]]; then
PAIRS_ARG="$2"
shift 2
else
shift
fi
;;
--pairRemoteList)
if [ $# -gt 1 ] && [[ "$2" != -* ]]; then
PAIR_REMOTE_LIST_URL="$2"
shift 2
else
shift
fi
;;
--strategy | -t)
if [ $# -gt 1 ] && [[ "$2" != -* ]]; then
STRATEGY_ARG="$2"
shift 2
else
shift
fi
;;
--config)
if [ $# -gt 1 ] && [[ "$2" != -* ]]; then
CONFIG_ARG="$2"
shift 2
else
shift
fi
;;
--spaces | --space | --epochs | --random-state | --jobs | -j)
# 这些参数已经在前面处理过了,跳过参数及其值
shift
if [[ $# -gt 0 ]] && [[ ! "$1" =~ ^-- ]]; then
shift
fi
;;
*)
shift
;;
esac
done
# 恢复原始参数列表以便后续处理
set -- "${ORIG_ARGS[@]}"
# 参数顺序说明
# 支持的调用方式:
# 1. 位置参数: $0 [开始日期] [结束日期] [其他参数]
# 2. 命名参数: $0 --start-date=YYYYMMDD --end-date=YYYYMMDD [其他参数]
# 3. 混合模式: $0 [开始日期] [结束日期] --space sell [其他参数]
# 处理策略名和交易对的位置参数(如果有的话)
# 这里需要重新分析参数,因为前两个位置参数可能是日期
POSITIONAL_ARGS=()
while [ $# -gt 0 ]; do
case "$1" in
--*) # 跳过命名参数
shift
if [[ "$1" != -* && $# -gt 0 ]]; then
shift
fi
;;
*)
POSITIONAL_ARGS+=("$1")
shift
;;
esac
done
# 现在POSITIONAL_ARGS中只包含位置参数非命名参数
# 前两个参数可能是日期,但我们已经在上面处理了
# 检查是否有剩余的位置参数用于策略名和交易对
if [ ${#POSITIONAL_ARGS[@]} -gt 2 ]; then
# 第3个位置参数可能是策略名
if [[ "${POSITIONAL_ARGS[2]}" != */* && ! "${POSITIONAL_ARGS[2]}" =~ ^[0-9]{8}$ ]]; then
STRATEGY_ARG="${POSITIONAL_ARGS[2]}"
echo "检测到策略参数: $STRATEGY_ARG"
fi
fi
if [ ${#POSITIONAL_ARGS[@]} -gt 3 ]; then
# 第4个位置参数可能是交易对或URL
FOURTH_PARAM="${POSITIONAL_ARGS[3]}"
if [[ "$FOURTH_PARAM" =~ ^[0-9]{8}$ ]]; then
echo "错误第4个参数应该是交易对或URL而不是日期"
exit 1
elif [[ "$FOURTH_PARAM" == http://* ]] || [[ "$FOURTH_PARAM" == https://* ]]; then
PAIR_REMOTE_LIST_URL="$FOURTH_PARAM"
echo "检测到远程交易对列表URL: $PAIR_REMOTE_LIST_URL"
elif [[ "$FOURTH_PARAM" == */* ]]; then
PAIRS_ARG="$FOURTH_PARAM"
echo "检测到交易对参数: $PAIRS_ARG"
fi
fi
# Set default values if parameters not provided
# Linux 专用的date命令 (UTC时间)
echo "[DEBUG] 开始日期原始值: '$START_DATE_RAW'"
echo "[DEBUG] 结束日期原始值: '$END_DATE_RAW'"
if [ -z "$START_DATE_RAW" ]; then
START_DATE_RAW=$(date -u +"%Y-%m-%d %H:%M:%S")
echo "[DEBUG] 使用默认开始日期: $START_DATE_RAW"
fi
if [ -z "$END_DATE_RAW" ]; then
END_DATE_RAW=$(date -u +"%Y-%m-%d %H:%M:%S")
echo "[DEBUG] 使用默认结束日期: $END_DATE_RAW"
fi
# Parse dates - Linux 专用实现
# 支持的格式:
# 1. 20250501 (YYYYMMDD)
# 2. 2025-10-01 13:48 (YYYY-MM-DD HH:MM)
# 3. 2025-10-01 13:48:30 (YYYY-MM-DD HH:MM:SS)
# 解析起始日期
if [[ "$START_DATE_RAW" =~ ^[0-9]{8}$ ]]; then
# 处理YYYYMMDD格式
YEAR=${START_DATE_RAW:0:4}
MONTH=${START_DATE_RAW:4:2}
DAY=${START_DATE_RAW:6:2}
# Linux/GNU date 命令语法
START_DATE=$(date -u -d "$YEAR-$MONTH-$DAY" +"%s")
echo "[DEBUG] 解析起始日期: $START_DATE_RAW -> $YEAR-$MONTH-$DAY -> 时间戳: $START_DATE"
if [ -z "$START_DATE" ]; then
echo "错误:无效的起始日期格式 $START_DATE_RAW,请使用 YYYYMMDD 或 YYYY-MM-DD HH:MM"
exit 1
fi
elif [[ "$START_DATE_RAW" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2} ]]; then
# 处理YYYY-MM-DD HH:MM 或 YYYY-MM-DD HH:MM:SS格式
START_DATE=$(date -u -d "$START_DATE_RAW" +"%s")
echo "[DEBUG] 解析起始日期: $START_DATE_RAW -> 时间戳: $START_DATE"
if [ -z "$START_DATE" ]; then
echo "错误:无效的起始日期格式 $START_DATE_RAW,请使用 YYYYMMDD 或 YYYY-MM-DD HH:MM"
exit 1
fi
else
echo "错误:不支持的起始日期格式 $START_DATE_RAW,请使用 YYYYMMDD 或 YYYY-MM-DD HH:MM"
exit 1
fi
# 解析结束日期
if [[ "$END_DATE_RAW" =~ ^[0-9]{8}$ ]]; then
# 处理YYYYMMDD格式
YEAR=${END_DATE_RAW:0:4}
MONTH=${END_DATE_RAW:4:2}
DAY=${END_DATE_RAW:6:2}
# Linux/GNU date 命令语法
END_DATE=$(date -u -d "$YEAR-$MONTH-$DAY" +"%s")
echo "[DEBUG] 解析结束日期: $END_DATE_RAW -> $YEAR-$MONTH-$DAY -> 时间戳: $END_DATE"
if [ -z "$END_DATE" ]; then
echo "错误:无效的结束日期格式 $END_DATE_RAW,请使用 YYYYMMDD 或 YYYY-MM-DD HH:MM"
exit 1
fi
elif [[ "$END_DATE_RAW" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2} ]]; then
# 处理YYYY-MM-DD HH:MM 或 YYYY-MM-DD HH:MM:SS格式
END_DATE=$(date -u -d "$END_DATE_RAW" +"%s")
echo "[DEBUG] 解析结束日期: $END_DATE_RAW -> 时间戳: $END_DATE"
if [ -z "$END_DATE" ]; then
echo "错误:无效的结束日期格式 $END_DATE_RAW,请使用 YYYYMMDD 或 YYYY-MM-DD HH:MM"
exit 1
fi
else
echo "错误:不支持的结束日期格式 $END_DATE_RAW,请使用 YYYYMMDD 或 YYYY-MM-DD HH:MM"
exit 1
fi
# 处理交易对参数:优先级为 --pairRemoteList > --pairs > 默认值
if [ -n "$PAIR_REMOTE_LIST_URL" ]; then
# 从远程API获取交易对列表
echo "Fetching pairs from remote URL: $PAIR_REMOTE_LIST_URL"
pairs_json=$(curl -s "$PAIR_REMOTE_LIST_URL")
# 检查API响应是否成功
if [[ $? -ne 0 || -z "$pairs_json" ]]; then
echo "Error: Failed to fetch pairs from remote URL, using --pairs parameter or default"
if [ -n "$PAIRS_ARG" ]; then
PAIRS_FLAG="--pairs $PAIRS_ARG"
echo "Using pairs from --pairs parameter: $PAIRS_ARG"
else
# 使用默认的交易对列表
DEFAULT_PAIRS="BTC/USDT TON/USDT DOT/USDT XRP/USDT OKB/USDT SOL/USDT DOGE/USDT RIO/USDT LTC/USDT SUI/USDT PEPE/USDT TRB/USDT FIL/USDT UNI/USDT KAITO/USDT"
PAIRS_FLAG="--pairs $DEFAULT_PAIRS"
echo "Using default pairs: $DEFAULT_PAIRS"
fi
else
# 解析JSON并提取交易对将连字符替换为斜杠
remote_pairs=$(echo "$pairs_json" | python3 -c "
import sys, json
data = json.load(sys.stdin)
pairs = [pair.replace('-', '/') for pair in data.get('pairlist', [])]
print(' '.join(pairs) if pairs else '')")
# 如果解析成功且有交易对
if [[ -n "$remote_pairs" ]]; then
PAIRS_FLAG="--pairs $remote_pairs"
echo "Successfully fetched $(echo "$remote_pairs" | wc -w) pairs from remote URL"
echo "Pairs: $remote_pairs"
else
echo "Error: Failed to parse or empty pairlist from remote URL, using --pairs parameter or default"
if [ -n "$PAIRS_ARG" ]; then
PAIRS_FLAG="--pairs $PAIRS_ARG"
echo "Using pairs from --pairs parameter: $PAIRS_ARG"
else
# 使用默认的交易对列表
DEFAULT_PAIRS="BTC/USDT TON/USDT DOT/USDT XRP/USDT OKB/USDT SOL/USDT DOGE/USDT RIO/USDT LTC/USDT SUI/USDT PEPE/USDT TRB/USDT FIL/USDT UNI/USDT KAITO/USDT"
PAIRS_FLAG="--pairs $DEFAULT_PAIRS"
echo "Using default pairs: $DEFAULT_PAIRS"
fi
fi
fi
elif [ -n "$PAIRS_ARG" ]; then
# 使用 --pairs 参数提供的交易对
PAIRS_FLAG="--pairs $PAIRS_ARG"
echo "Using pairs from --pairs parameter: $PAIRS_ARG"
else
# 没有提供任何交易对参数,使用默认值
DEFAULT_PAIRS="BTC/USDT TON/USDT DOT/USDT XRP/USDT OKB/USDT SOL/USDT DOGE/USDT RIO/USDT LTC/USDT SUI/USDT PEPE/USDT TRB/USDT FIL/USDT UNI/USDT KAITO/USDT"
PAIRS_FLAG="--pairs $DEFAULT_PAIRS"
echo "No pairs parameter provided, using default pairs: $DEFAULT_PAIRS"
fi
# 如果命令行提供了有效的策略参数,覆盖.env文件设置
if [ -n "$STRATEGY_ARG" ] && [ "$STRATEGY_ARG" != "PARAM_WITHOUT_VALUE" ]; then
# 策略类名保持不变如MyStrategy但文件名使用小写
STRATEGY_CLASS_NAME="$STRATEGY_ARG"
STRATEGY_FILE_NAME="$(echo "$STRATEGY_ARG" | tr '[:upper:]' '[:lower:]').py"
STRATEGY_NAME="$STRATEGY_FILE_NAME"
echo "Overriding strategy with command line parameter: $STRATEGY_CLASS_NAME"
echo "Strategy class name: $STRATEGY_CLASS_NAME"
echo "Strategy file name: $STRATEGY_FILE_NAME"
# 自动匹配策略对应的配置文件(使用小写)
STRATEGY_CONFIG_LOWER="$(echo "$STRATEGY_ARG" | tr '[:upper:]' '[:lower:]').json"
echo "Checking auto-matched config file: freqtrade/templates/$STRATEGY_CONFIG_LOWER"
# 检查小写配置文件
if [ -f "../freqtrade/templates/$STRATEGY_CONFIG_LOWER" ]; then
CONFIG_FILE="$STRATEGY_CONFIG_LOWER"
echo "Auto-matched config file for strategy: $CONFIG_FILE"
else
echo "Warning: Auto-matched config file '$STRATEGY_CONFIG_LOWER' not found in templates directory"
echo "Available config files in templates directory:"
ls ../freqtrade/templates/*.json 2>/dev/null | sed 's|.*/||' || echo "No JSON config files found"
echo "Using current config: $CONFIG_FILE"
fi
fi
# 如果命令行提供了配置参数,覆盖.env文件设置
if [ -n "$CONFIG_ARG" ]; then
CONFIG_FILE="$CONFIG_ARG"
echo "Overriding config with command line parameter: $CONFIG_FILE"
fi
# 检查指定的配置文件是否存在,如果不存在则使用默认配置
echo "Checking config file: freqtrade/templates/$CONFIG_FILE"
if [ ! -f "../freqtrade/templates/$CONFIG_FILE" ]; then
echo "Warning: Config file freqtrade/templates/$CONFIG_FILE not found, using default configuration"
CONFIG_FILE="basic.json"
else
echo "Config file found: freqtrade/templates/$CONFIG_FILE"
fi
# 最后确认STRATEGY_NAME有值
if [ -z "$STRATEGY_NAME" ]; then
echo "Warning: STRATEGY_NAME is empty, using default TheForceV7"
STRATEGY_NAME="TheForceV7"
fi
# 确保STRATEGY_CLASS_NAME有值
if [ -z "$STRATEGY_CLASS_NAME" ]; then
echo "Warning: STRATEGY_CLASS_NAME is empty, setting to STRATEGY_NAME value"
STRATEGY_CLASS_NAME="$STRATEGY_NAME"
fi
# 在参数处理完成后输出最终使用的值
# 显示完整使用帮助
echo "正确用法示例:"
echo "位置参数模式:"
echo " $0 20230901 20231001 MartinGale BTC/USDT"
echo "命名参数模式:"
echo " $0 --start-date=20230901 --end-date=20231001 -t MartinGale --spaces \"buy sell\" --epochs 200 --random-state 42"
echo "Using strategy: $STRATEGY_NAME"
echo "Using config: $CONFIG_FILE"
echo "Using testBranch: $TEST_BRANCH"
echo "Using hyperopt spaces: $SPACES"
echo "Using epochs: $EPOCHS"
echo "Using random-state: $RANDOM_STATE"
# 直接在当前目录工作,不使用不存在的.venv
cd ../
# 安全地删除文件,只删除存在的文件
find user_data/models -type f -delete 2>/dev/null
touch user_data/models/.gitkeep
find ./freqtrade/user_data/data/backtest_results -type f -delete 2>/dev/null
touch ./freqtrade/user_data/data/backtest_results/.gitkeep
find ./user_data/dryrun_results -type f -delete 2>/dev/null
touch ./user_data/dryrun_results/.gitkeep
find result -type f -delete 2>/dev/null
touch result/.gitkeep
# 🔧 新增:清空 hyperopt_results 目录中的所有结果文件(执行 hyperopt 前清理旧结果)
echo "清空旧的 hyperopt 结果文件..."
find user_data/hyperopt_results -type f \( -name "*.fthypt" -o -name "*.pkl" -o -name ".last_result.json" \) -delete 2>/dev/null
echo "✓ hyperopt_results 目录已清空"
PARAMS_NAME=freqaiprimer
echo "docker-compose run --rm freqtrade hyperopt $PAIRS_FLAG \
--logfile /freqtrade/user_data/logs/freqtrade.log \
--freqaimodel LightGBMRegressorMultiTarget \
--strategy $STRATEGY_CLASS_NAME \
--config /freqtrade/config_examples/$CONFIG_FILE \
--config /freqtrade/templates/${PARAMS_NAME}.json \
--enable-protections \
--strategy-path /freqtrade/templates \
--timerange ${START_DATE}-${END_DATE} \
--random-state $RANDOM_STATE \
--epochs $EPOCHS \
-j $JOBS \
--hyperopt-loss CalmarHyperOptLoss \
--spaces $SPACES \
--fee 0.001"
docker-compose run --rm freqtrade hyperopt $PAIRS_FLAG \
--logfile /freqtrade/user_data/logs/freqtrade.log \
--freqaimodel LightGBMRegressorMultiTarget \
--strategy $STRATEGY_CLASS_NAME \
--config /freqtrade/config_examples/$CONFIG_FILE \
--config /freqtrade/templates/${PARAMS_NAME}.json \
--enable-protections \
--strategy-path /freqtrade/templates \
--timerange ${START_DATE}-${END_DATE} \
--random-state $RANDOM_STATE \
--epochs $EPOCHS \
-j $JOBS \
--hyperopt-loss CalmarHyperOptLoss \
--spaces $SPACES \
--fee 0.001