2026-02-13 16:42:47 +08:00

375 lines
12 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
set -e # 出错立即停止
### 函数定义区 ###
# 定义 remove_existing_containers 函数
remove_existing_containers() {
local mode="$1" # dryrun 或 live
echo "正在查找并移除所有 'freqtrade-${mode}-*' 的容器..." >&2
# 查找所有名称以 freqtrade-${mode}- 开头的容器
CONTAINERS=$(docker ps -aq --filter "name=freqtrade-${mode}-")
if [ -z "$CONTAINERS" ]; then
echo "✅ 未找到任何 'freqtrade-${mode}-*' 的容器" >&2
return
fi
echo "找到以下容器:" >&2
docker ps -a --filter "name=freqtrade-${mode}-" --format "table {{.ID}}\t{{.Names}}\t{{.Status}}" >&2
echo "正在自动移除所有符合条件的容器..." >&2
# 停止并移除容器
for CONTAINER in $CONTAINERS; do
CONTAINER_NAME=$(docker ps -a --filter "id=$CONTAINER" --format "{{.Names}}")
echo "正在停止容器 $CONTAINER_NAME..." >&2
docker stop "$CONTAINER" >/dev/null 2>&1
echo "正在移除容器 $CONTAINER_NAME..." >&2
if docker rm "$CONTAINER" >/dev/null 2>&1; then
echo "✅ 已成功移除容器 $CONTAINER_NAME" >&2
else
echo "⚠️ 移除容器 $CONTAINER_NAME 失败" >&2
fi
done
echo "✅ 容器清理完成" >&2
}
# 1. 从SQLite数据库获取当前开放交易的币对列表
get_open_trades_pairs() {
local db_path="$1"
local pairs=()
if [ ! -f "$db_path" ]; then
echo "⚠️ 交易数据库文件 $db_path 不存在,开放交易对列表为空" >&2
echo ""
return
fi
local query_result=$(sqlite3 -header -csv "$db_path" "SELECT id, pair, open_date, amount, open_rate, max_rate, close_profit, stake_amount FROM trades WHERE is_open = 1 ORDER BY open_date DESC LIMIT 10;")
pairs=($(echo "$query_result" | awk -F ',' 'NR > 1 {print $2}' | tr -d '"'))
echo "从数据库获取到开放交易对: ${pairs[*]}" >&2
echo "${pairs[@]}"
}
# 2. 验证JSON文件格式是否有效
validate_json_file() {
local json_path="$1"
if ! command -v jq &>/dev/null; then
echo "❌ 未安装jq工具请先安装jq" >&2
exit 1
fi
if [ ! -f "$json_path" ]; then
echo "❌ JSON配置文件 $json_path 不存在" >&2
exit 1
fi
if ! jq . "$json_path" >/dev/null 2>&1; then
echo "❌ JSON文件格式错误$json_path" >&2
exit 1
fi
}
# 3. 从远程URL获取币对列表带重试机制
get_remote_pairs() {
local remote_url="$1"
local pairs=()
local max_retries=5
local retry_delay=3
local attempt=1
if [ -z "$remote_url" ]; then
echo "⚠️ 未提供远程币对列表URL远程币对列表为空" >&2
echo ""
return
fi
# 重试循环
while [ $attempt -le $max_retries ]; do
echo "正在从远程URL获取币对列表 (尝试 $attempt/$max_retries): $remote_url" >&2
local pairs_json=$(curl -s "$remote_url")
if [ $? -eq 0 ] && [ -n "$pairs_json" ]; then
# curl 成功且有返回内容,尝试解析
local parsed_pairs=$(echo "$pairs_json" | python3 -c "
import sys, json
try:
data = json.load(sys.stdin)
pairs = [pair.replace('-', '/') for pair in data.get('pairlist', [])]
print(' '.join(pairs) if pairs else '')
except:
pass
")
if [ -n "$parsed_pairs" ]; then
pairs=($parsed_pairs)
echo "✓ 从远程成功获取到币对: ${pairs[*]}" >&2
echo "${pairs[*]}"
return 0
fi
fi
# 如果不是最后一次尝试,等待后重试
if [ $attempt -lt $max_retries ]; then
echo "⚠️ 获取失败,${retry_delay}秒后重试..." >&2
sleep $retry_delay
fi
attempt=$((attempt + 1))
done
# 所有重试都失败
echo "✗ 远程币对列表获取失败(已重试${max_retries}次),使用空列表" >&2
echo ""
return 1
}
# 4. 合并并去重多个币对列表
merge_and_deduplicate_pairs() {
local -a db_pairs=($1)
local -a remote_pairs=($2)
local -a default_pairs=($3)
local -a merged=()
# 合并三个来源的币对并去重
merged=($(printf "%s\n" "${db_pairs[@]}" "${remote_pairs[@]}" "${default_pairs[@]}" | sort -u | tr '\n' ' '))
echo "合并去重后的币对列表 (${#merged[@]}个): ${merged[*]}" >&2
echo "${merged[@]}"
}
# 5. 更新live.json中的pair_whitelist
update_live_json_pair_whitelist() {
local json_path="$1"
local -a pairlist=($2)
validate_json_file "$json_path"
local json_array=$(printf '%s\n' "${pairlist[@]}" | jq -R . | jq -s .)
echo "正在更新 $json_path 中的exchange.pair_whitelist..." >&2
jq --argjson pairs "$json_array" '.exchange.pair_whitelist = $pairs' "$json_path" >"$json_path.tmp" && mv "$json_path.tmp" "$json_path"
if [ $? -eq 0 ]; then
echo "✅ 成功更新 $json_path新的pair_whitelist包含 ${#pairlist[@]} 个币对" >&2
else
echo "❌ 更新 $json_path 失败" >&2
exit 1
fi
}
### 主逻辑区 ###
# 检查 .env 文件
if [ ! -f ".env" ]; then
echo "⚠️ 本地缺少 .env 文件,请创建并配置" >&2
exit 1
fi
# 加载 .env 变量
export $(grep -v '^#' .env | xargs)
# 设置默认远程币对列表URL支持命令行参数和环境变量覆盖
DEFAULT_PAIR_REMOTE_URL="http://pairlist.xl.home/api/pairlist?mute=true&count=30"
# 优先级:环境变量 > 默认值
PAIR_REMOTE_LIST_URL="${PAIR_REMOTE_LIST_URL:-$DEFAULT_PAIR_REMOTE_URL}"
# 检查必要环境变量
if [ -z "$TEST_BRANCH" ] || [ -z "$DRYRUN_BRANCH" ]; then
echo "⚠️ .env 文件缺少 TEST_BRANCH 或 DRYRUN_BRANCH 配置" >&2
exit 1
fi
# 检查当前分支
current_branch=$(git rev-parse --abbrev-ref HEAD)
if [[ "$current_branch" != "$TEST_BRANCH" && "$current_branch" != "$DRYRUN_BRANCH" ]]; then
echo "⚠️ 错误:当前分支 '$current_branch' 不符合配置要求" >&2
exit 1
else
echo "✅ 当前分支 '$current_branch' 符合配置要求" >&2
fi
# dryrun分支清理
if [[ "$current_branch" == *"dryrun"* ]]; then
echo "当前为dryrun分支执行git reset --hard清理工作区..." >&2
git reset --hard || {
echo "⚠️ git reset失败"
exit 1
}
echo "✅ Git工作区已清理" >&2
fi
# 解析命令行参数:获取手动指定的币对列表、远程 URL 和模型
MANUAL_PAIRS=""
while [[ $# -gt 0 ]]; do
case "$1" in
--pairs=*)
MANUAL_PAIRS="${1#*=}"
shift
;;
--pairs)
if [[ -n "$2" && "$2" != -* ]]; then
MANUAL_PAIRS="$2"
shift 2
else
echo "错误:--pairs需要指定值" >&2
exit 1
fi
;;
--pairRemoteList=*)
PAIR_REMOTE_LIST_URL="${1#*=}"
shift
;;
--pairRemoteList)
if [[ -n "$2" && "$2" != -* ]]; then
PAIR_REMOTE_LIST_URL="$2"
shift 2
else
echo "错误:--pairRemoteList需要指定值" >&2
exit 1
fi
;;
--freqaimodel=*)
FREQAI_MODEL="${1#*=}"
shift
;;
--freqaimodel)
if [[ -n "$2" && "$2" != -* ]]; then
FREQAI_MODEL="$2"
shift 2
else
echo "错误:--freqaimodel需要指定值" >&2
exit 1
fi
;;
*)
shift
;;
esac
done
# 加载策略配置
STRATEGY_NAME=${STRATEGY_NAME:-TheForceV7}
CONFIG_FILE=${CONFIG_FILE:-freqaiprimer.json} # 修改:使用 freqaiprimer.json 作为基础配置
FREQAI_MODEL=${FREQAI_MODEL:-XGBoostRegressorMultiTarget}
PARAMS_NAME=$(echo "$STRATEGY_NAME" | tr '[:upper:]' '[:lower:]')
echo "使用策略: $STRATEGY_NAME" >&2
echo "使用配置: $CONFIG_FILE" >&2
echo "参数文件: $PARAMS_NAME.json" >&2
echo "配置加载顺序:" >&2
echo " 1. /freqtrade/config_examples/$CONFIG_FILE" >&2
echo " 2. /freqtrade/templates/${PARAMS_NAME}.json" >&2
echo " 3. /freqtrade/config_examples/live.json" >&2
echo "测试分支: $TEST_BRANCH" >&2
echo "远程币对列表URL: $PAIR_REMOTE_LIST_URL" >&2
echo "使用模型: $FREQAI_MODEL" >&2
### 核心:处理币对列表 ###
# 定义写死的币对白名单
DEFAULT_PAIRS="BTC/USDT ETH/USDT XRP/USDT XAUT/USDT SOL/USDT BNB/USDT TRUMP/USDT PEPE/USDT TRB/USDT DOGE/USDT AVAX/USDT BCH/USDT SUI/USDT FIL/USDT LTC/USDT IP/USDT"
# 1. 获取数据库币对(使用绝对路径)
db_path="/home/ubuntu/freqtrade/user_data/tradesv3.sqlite"
db_pairs=$(get_open_trades_pairs "$db_path")
# 2. 不再使用远程币对,已注释
# remote_pairs=$(get_remote_pairs "$PAIR_REMOTE_LIST_URL")
# 3. 处理手动指定的币对或使用合并逻辑
if [ -n "$MANUAL_PAIRS" ]; then
# 如果手动指定了币对,直接使用手动指定的币对
final_pairs=$(echo "$MANUAL_PAIRS" | tr ',' ' ')
echo "使用手动指定的币对: $final_pairs" >&2
else
# 只合并两个来源:数据库 + 默认白名单(不再使用远程)
final_pairs=$(merge_and_deduplicate_pairs "$db_pairs" "" "$DEFAULT_PAIRS")
echo "使用合并后的币对列表作为最终列表 (数据库 + 默认白名单)" >&2
fi
# 4. 更新配置文件
update_live_json_pair_whitelist "../config_examples/live.json" "$final_pairs"
# 编辑 ../freqtrade/templates/${PARAMS_NAME}.json 文件,删除包含 "protection": {}, 的行
echo "正在编辑 ../freqtrade/templates/${PARAMS_NAME}.json 文件,删除 'protection': {} 行..." >&2
sed -i '/"protection": {},/d' "../freqtrade/templates/${PARAMS_NAME}.json"
if [ $? -eq 0 ]; then
echo "✅ 成功编辑 ../freqtrade/templates/${PARAMS_NAME}.json 文件" >&2
else
echo "❌ 编辑 ../freqtrade/templates/${PARAMS_NAME}.json 文件失败" >&2
exit 1
fi
### 启动容器 ###
# 第1步先清理所有旧容器
echo "正在清理所有 live 旧容器..." >&2
remove_existing_containers "live"
# 第2步生成新容器名
GIT_COMMIT_SHORT=$(git rev-parse HEAD | cut -c 1-8)
TIMESTAMP=$(date +%Y%m%d%H%M%S)
CONTAINER_NAME="freqtrade-live-${GIT_COMMIT_SHORT}-${TIMESTAMP}"
echo "准备启动容器: $CONTAINER_NAME" >&2
# 清理临时文件放置交叉感染
cd ../
source .venv/bin/activate
# 注释掉删除模型命令,保留已训练的模型以便复用
# rm -rf user_data/models/* # ❌ 不删除:保留训练好的模型
rm -rf ./freqtrade/user_data/data/backtest_results/* # ✅ 仅清理回测结果
rm -fr ./user_data/dryrun_results/* # ✅ 仅清理 dryrun 结果
cd -
# 第3步启动新容器
echo "正在启动新容器..." >&2
# 临时禁用 set -e以便捕获 docker run 的错误
set +e
docker run -d --restart=always \
--name "${CONTAINER_NAME}" \
-p 8080:8080 \
--add-host "www.okx.com:104.18.43.174" \
--add-host "api.okx.com:104.18.43.174" \
-v "$(pwd)/../user_data:/freqtrade/user_data" \
-v "$(pwd)/../config_examples:/freqtrade/config_examples" \
-v "$(pwd)/../freqtrade/templates:/freqtrade/templates" \
-v "$(pwd)/../freqtrade/exchange:/freqtrade/exchange" \
-v "$(pwd)/../freqtrade/plugins/protections:/freqtrade/freqtrade/plugins/protections" \
-v "$(pwd)/../ccxt/async_support/okx.py:/home/ftuser/.local/lib/python3.12/site-packages/ccxt/async_support/okx.py" \
-v "$(pwd)/../freqtrade/freqai/data_kitchen.py:/freqtrade/freqai/data_kitchen.py" \
-v "$(pwd)/../freqtrade/freqai/data_drawer.py:/freqtrade/freqai/data_drawer.py" \
-v "$(pwd)/../freqtrade/freqai/freqai_interface.py:/freqtrade/freqai/freqai_interface.py" \
freqtradeorg/freqtrade:develop_freqai_withredis \
trade \
--logfile /freqtrade/user_data/logs/freqtrade.log \
--db-url sqlite:////freqtrade/user_data/tradesv3.sqlite \
--freqaimodel $FREQAI_MODEL \
--fee 0.0008 \
--config /freqtrade/config_examples/$CONFIG_FILE \
--config /freqtrade/templates/${PARAMS_NAME}.json \
--config /freqtrade/config_examples/live_${FREQAI_MODEL}.json \
--strategy $STRATEGY_NAME \
--strategy-path /freqtrade/templates
RUN_RESULT=$?
set -e # 恢复 set -e
if [ $RUN_RESULT -eq 0 ]; then
echo "✅ 容器 $CONTAINER_NAME 启动成功" >&2
echo "⚡ 查看日志: docker logs -f $CONTAINER_NAME" >&2
else
echo "❌ 容器启动失败 (退出码: $RUN_RESULT),请查看上方错误信息" >&2
exit 1
fi