Rebrand to 比特骆驼自动化对冲系统; clear login autofill and sim subtitle.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
# eth_hedge_sim
|
# 比特骆驼自动化对冲系统(eth_hedge_sim)
|
||||||
|
|
||||||
独立自动对冲**模拟盘**:OKX 实盘只读行情 + 本地虚拟资金撮合。
|
**产品名:比特骆驼自动化对冲系统**
|
||||||
|
工程仓库 / 目录名:`eth_hedge_sim`(与产品名分离,部署路径不变)。
|
||||||
|
|
||||||
|
独立自动对冲系统:OKX / 币安行情 + 本地 SIM 撮合,可选 LIVE 实盘。
|
||||||
与现网 `crypto_monitor` **无代码、进程、密钥共用**。
|
与现网 `crypto_monitor` **无代码、进程、密钥共用**。
|
||||||
|
|
||||||
仓库:<https://git.bz121.com/dekun/eth_hedge_sim.git>
|
仓库:<https://git.bz121.com/dekun/eth_hedge_sim.git>
|
||||||
@@ -11,12 +14,14 @@
|
|||||||
- [代码结构](docs/代码结构.md)
|
- [代码结构](docs/代码结构.md)
|
||||||
- [商业化与授权方案](docs/商业化与授权方案.md)
|
- [商业化与授权方案](docs/商业化与授权方案.md)
|
||||||
- [策略说明](docs/策略说明.md)
|
- [策略说明](docs/策略说明.md)
|
||||||
|
- [实盘策略说明](docs/实盘策略说明.md)
|
||||||
|
|
||||||
## 访问(测试机)
|
## 访问(测试机)
|
||||||
|
|
||||||
- 反代:[`https://dc.hyf2.cc`](https://dc.hyf2.cc) → 本机 `5155`(PM2: `eth-hedge-api`)
|
- 反代:[`https://dc.hyf2.cc`](https://dc.hyf2.cc) → 本机 `5155`(PM2: `eth-hedge-api`)
|
||||||
- 默认登录:见服务器 `/opt/eth_hedge_sim/.env` 的 `AUTH_USERNAME` / `AUTH_PASSWORD`(示例 `admin` / `admin123`)
|
- 登录账号:见服务器 `/opt/eth_hedge_sim/.env` 的 `AUTH_USERNAME` / `AUTH_PASSWORD`
|
||||||
- 可在「系统设置」修改用户名/密码;前端固定同源 API,无 API 地址配置项
|
- 可在「系统设置」修改用户名/密码;前端固定同源 API,无 API 地址配置项
|
||||||
|
- 支持电脑 / 平板安装为独立应用(PWA)
|
||||||
|
|
||||||
## 一键部署 / 更新(禁止 scp 传代码)
|
## 一键部署 / 更新(禁止 scp 传代码)
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -73,9 +73,9 @@ async def lifespan(app: FastAPI):
|
|||||||
|
|
||||||
|
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="eth_hedge_sim",
|
title="比特骆驼自动化对冲系统",
|
||||||
version="0.3.0",
|
version="0.3.0",
|
||||||
description="ETH 自动对冲模拟盘 P1/P2",
|
description="比特骆驼自动化对冲系统(eth_hedge_sim)",
|
||||||
lifespan=lifespan,
|
lifespan=lifespan,
|
||||||
)
|
)
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
|
|||||||
+155
-155
@@ -1,155 +1,155 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# deploy/lib/common.sh — eth_hedge_sim 部署公共函数
|
# deploy/lib/common.sh — 比特骆驼自动化对冲系统 部署公共函数
|
||||||
set -e
|
set -e
|
||||||
set -u
|
set -u
|
||||||
if [ -n "${BASH_VERSION:-}" ]; then
|
if [ -n "${BASH_VERSION:-}" ]; then
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
fi
|
fi
|
||||||
|
|
||||||
INSTALL_ROOT="${INSTALL_ROOT:-/opt/eth_hedge_sim}"
|
INSTALL_ROOT="${INSTALL_ROOT:-/opt/eth_hedge_sim}"
|
||||||
GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/eth_hedge_sim.git}"
|
GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/eth_hedge_sim.git}"
|
||||||
GIT_BRANCH="${GIT_BRANCH:-main}"
|
GIT_BRANCH="${GIT_BRANCH:-main}"
|
||||||
BACKUP_ROOT="${BACKUP_ROOT:-/root/backups/eth_hedge_sim}"
|
BACKUP_ROOT="${BACKUP_ROOT:-/root/backups/eth_hedge_sim}"
|
||||||
TZ_NAME="${EHS_TZ:-Asia/Shanghai}"
|
TZ_NAME="${EHS_TZ:-Asia/Shanghai}"
|
||||||
NODE_MAJOR="${NODE_MAJOR:-20}"
|
NODE_MAJOR="${NODE_MAJOR:-20}"
|
||||||
PM2_APP_NAME="${PM2_APP_NAME:-eth-hedge-api}"
|
PM2_APP_NAME="${PM2_APP_NAME:-eth-hedge-api}"
|
||||||
API_PORT="${API_PORT:-5155}"
|
API_PORT="${API_PORT:-5155}"
|
||||||
|
|
||||||
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
DEPLOY_DIR="$(cd "${LIB_DIR}/.." && pwd)"
|
DEPLOY_DIR="$(cd "${LIB_DIR}/.." && pwd)"
|
||||||
REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)"
|
REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)"
|
||||||
|
|
||||||
log() { printf '[%s] %s\n' "$(TZ="${TZ_NAME}" date '+%Y-%m-%d %H:%M:%S')" "$*"; }
|
log() { printf '[%s] %s\n' "$(TZ="${TZ_NAME}" date '+%Y-%m-%d %H:%M:%S')" "$*"; }
|
||||||
step() { echo ""; log "==> $*"; }
|
step() { echo ""; log "==> $*"; }
|
||||||
|
|
||||||
die() {
|
die() {
|
||||||
echo "错误: $*" >&2
|
echo "错误: $*" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
require_root() {
|
require_root() {
|
||||||
if [[ "$(id -u)" -ne 0 ]]; then
|
if [[ "$(id -u)" -ne 0 ]]; then
|
||||||
die "请使用 root 执行(推荐: sudo -i 后运行)"
|
die "请使用 root 执行(推荐: sudo -i 后运行)"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
detect_server_ip() {
|
detect_server_ip() {
|
||||||
local ip=""
|
local ip=""
|
||||||
if command -v hostname >/dev/null 2>&1; then
|
if command -v hostname >/dev/null 2>&1; then
|
||||||
ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
||||||
fi
|
fi
|
||||||
if [[ -z "${ip}" ]]; then
|
if [[ -z "${ip}" ]]; then
|
||||||
ip="127.0.0.1"
|
ip="127.0.0.1"
|
||||||
fi
|
fi
|
||||||
echo "${ip}"
|
echo "${ip}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# curl|bash 时 stdin 可能是管道,交互从 /dev/tty 读
|
# curl|bash 时 stdin 可能是管道,交互从 /dev/tty 读
|
||||||
cm_read() {
|
cm_read() {
|
||||||
local __var="$1"
|
local __var="$1"
|
||||||
local __prompt="${2:-}"
|
local __prompt="${2:-}"
|
||||||
local __line=""
|
local __line=""
|
||||||
if [[ -n "${__prompt}" ]]; then
|
if [[ -n "${__prompt}" ]]; then
|
||||||
printf '%s' "${__prompt}" >/dev/tty 2>/dev/null || printf '%s' "${__prompt}"
|
printf '%s' "${__prompt}" >/dev/tty 2>/dev/null || printf '%s' "${__prompt}"
|
||||||
fi
|
fi
|
||||||
if [[ -r /dev/tty ]]; then
|
if [[ -r /dev/tty ]]; then
|
||||||
IFS= read -r __line </dev/tty || true
|
IFS= read -r __line </dev/tty || true
|
||||||
else
|
else
|
||||||
IFS= read -r __line || true
|
IFS= read -r __line || true
|
||||||
fi
|
fi
|
||||||
printf -v "${__var}" '%s' "${__line}"
|
printf -v "${__var}" '%s' "${__line}"
|
||||||
}
|
}
|
||||||
|
|
||||||
confirm_yes() {
|
confirm_yes() {
|
||||||
local msg="$1"
|
local msg="$1"
|
||||||
local ans=""
|
local ans=""
|
||||||
cm_read ans "${msg} [y/N] "
|
cm_read ans "${msg} [y/N] "
|
||||||
[[ "${ans}" == [yY] || "${ans}" == [yY][eE][sS] ]]
|
[[ "${ans}" == [yY] || "${ans}" == [yY][eE][sS] ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
repo_ready() {
|
repo_ready() {
|
||||||
[[ -f "${1}/deploy/manage.sh" && -f "${1}/deploy/pull_and_restart.sh" ]]
|
[[ -f "${1}/deploy/manage.sh" && -f "${1}/deploy/pull_and_restart.sh" ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve_repo_root() {
|
resolve_repo_root() {
|
||||||
if repo_ready "${INSTALL_ROOT}"; then
|
if repo_ready "${INSTALL_ROOT}"; then
|
||||||
echo "${INSTALL_ROOT}"
|
echo "${INSTALL_ROOT}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if [[ -n "${REPO_ROOT:-}" ]] && repo_ready "${REPO_ROOT}"; then
|
if [[ -n "${REPO_ROOT:-}" ]] && repo_ready "${REPO_ROOT}"; then
|
||||||
echo "${REPO_ROOT}"
|
echo "${REPO_ROOT}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_system_deps() {
|
ensure_system_deps() {
|
||||||
step "检查系统依赖"
|
step "检查系统依赖"
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
if command -v apt-get >/dev/null 2>&1; then
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
apt-get update -qq
|
apt-get update -qq
|
||||||
apt-get install -y git ca-certificates curl python3 python3-venv python3-pip
|
apt-get install -y git ca-certificates curl python3 python3-venv python3-pip
|
||||||
fi
|
fi
|
||||||
if ! command -v node >/dev/null 2>&1; then
|
if ! command -v node >/dev/null 2>&1; then
|
||||||
step "安装 Node.js ${NODE_MAJOR}"
|
step "安装 Node.js ${NODE_MAJOR}"
|
||||||
curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash -
|
curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash -
|
||||||
apt-get install -y nodejs
|
apt-get install -y nodejs
|
||||||
fi
|
fi
|
||||||
if ! command -v pm2 >/dev/null 2>&1; then
|
if ! command -v pm2 >/dev/null 2>&1; then
|
||||||
step "安装 PM2"
|
step "安装 PM2"
|
||||||
npm install -g pm2
|
npm install -g pm2
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
pm2_stop_project() {
|
pm2_stop_project() {
|
||||||
if command -v pm2 >/dev/null 2>&1; then
|
if command -v pm2 >/dev/null 2>&1; then
|
||||||
pm2 stop "${PM2_APP_NAME}" 2>/dev/null || true
|
pm2 stop "${PM2_APP_NAME}" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
pm2_delete_project() {
|
pm2_delete_project() {
|
||||||
if command -v pm2 >/dev/null 2>&1; then
|
if command -v pm2 >/dev/null 2>&1; then
|
||||||
pm2 delete "${PM2_APP_NAME}" 2>/dev/null || true
|
pm2 delete "${PM2_APP_NAME}" 2>/dev/null || true
|
||||||
pm2 save 2>/dev/null || true
|
pm2 save 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
pm2_save_startup() {
|
pm2_save_startup() {
|
||||||
if command -v pm2 >/dev/null 2>&1; then
|
if command -v pm2 >/dev/null 2>&1; then
|
||||||
pm2 save 2>/dev/null || true
|
pm2 save 2>/dev/null || true
|
||||||
pm2 startup systemd -u root --hp /root 2>/dev/null || true
|
pm2 startup systemd -u root --hp /root 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
verify_health() {
|
verify_health() {
|
||||||
local port="${1:-${API_PORT}}"
|
local port="${1:-${API_PORT}}"
|
||||||
local url="http://127.0.0.1:${port}/health"
|
local url="http://127.0.0.1:${port}/health"
|
||||||
step "健康检查 ${url}"
|
step "健康检查 ${url}"
|
||||||
local i
|
local i
|
||||||
for i in 1 2 3 4 5 6 7 8 9 10; do
|
for i in 1 2 3 4 5 6 7 8 9 10; do
|
||||||
if curl -fsS "${url}" >/dev/null 2>&1; then
|
if curl -fsS "${url}" >/dev/null 2>&1; then
|
||||||
log "health OK"
|
log "health OK"
|
||||||
curl -fsS "${url}" || true
|
curl -fsS "${url}" || true
|
||||||
echo ""
|
echo ""
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
log "警告: health 暂未就绪,请稍后检查 pm2 logs ${PM2_APP_NAME}"
|
log "警告: health 暂未就绪,请稍后检查 pm2 logs ${PM2_APP_NAME}"
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
print_post_install_guide() {
|
print_post_install_guide() {
|
||||||
local ip
|
local ip
|
||||||
ip="$(detect_server_ip)"
|
ip="$(detect_server_ip)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "══════════════════════════════════════"
|
echo "══════════════════════════════════════"
|
||||||
echo " eth_hedge_sim 部署完成"
|
echo " 比特骆驼自动化对冲系统 部署完成"
|
||||||
echo " 目录: ${INSTALL_ROOT}"
|
echo " 目录: ${INSTALL_ROOT}"
|
||||||
echo " 本机: http://${ip}:${API_PORT}/health"
|
echo " 本机: http://${ip}:${API_PORT}/health"
|
||||||
echo " 进程: pm2 status → ${PM2_APP_NAME}"
|
echo " 进程: pm2 status → ${PM2_APP_NAME}"
|
||||||
echo " 管理: bash ${INSTALL_ROOT}/deploy/manage.sh"
|
echo " 管理: bash ${INSTALL_ROOT}/deploy/manage.sh"
|
||||||
echo " 配置: ${INSTALL_ROOT}/.env"
|
echo " 配置: ${INSTALL_ROOT}/.env"
|
||||||
echo "══════════════════════════════════════"
|
echo "══════════════════════════════════════"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|||||||
+80
-80
@@ -1,80 +1,80 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# deploy/lib/uninstall.sh — 一键卸载(仅本项目 PM2,不动其它进程)
|
# deploy/lib/uninstall.sh — 一键卸载(仅本项目 PM2,不动其它进程)
|
||||||
set -e
|
set -e
|
||||||
set -u
|
set -u
|
||||||
if [ -n "${BASH_VERSION:-}" ]; then
|
if [ -n "${BASH_VERSION:-}" ]; then
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
fi
|
fi
|
||||||
|
|
||||||
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
# shellcheck source=common.sh
|
# shellcheck source=common.sh
|
||||||
source "${LIB_DIR}/common.sh"
|
source "${LIB_DIR}/common.sh"
|
||||||
|
|
||||||
main_uninstall() {
|
main_uninstall() {
|
||||||
require_root
|
require_root
|
||||||
|
|
||||||
local root=""
|
local root=""
|
||||||
if ! root="$(resolve_repo_root)"; then
|
if ! root="$(resolve_repo_root)"; then
|
||||||
if [[ -d "${INSTALL_ROOT}" ]]; then
|
if [[ -d "${INSTALL_ROOT}" ]]; then
|
||||||
root="${INSTALL_ROOT}"
|
root="${INSTALL_ROOT}"
|
||||||
else
|
else
|
||||||
die "未找到安装目录 ${INSTALL_ROOT}"
|
die "未找到安装目录 ${INSTALL_ROOT}"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
REPO_ROOT="${root}"
|
REPO_ROOT="${root}"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "将卸载 eth_hedge_sim:"
|
echo "将卸载 比特骆驼自动化对冲系统 (eth_hedge_sim):"
|
||||||
echo " - 停止并删除 PM2 进程: ${PM2_APP_NAME}"
|
echo " - 停止并删除 PM2 进程: ${PM2_APP_NAME}"
|
||||||
echo " - 移走目录: ${INSTALL_ROOT}"
|
echo " - 移走目录: ${INSTALL_ROOT}"
|
||||||
echo " - 备份 .env 到 ${BACKUP_ROOT}"
|
echo " - 备份 .env 到 ${BACKUP_ROOT}"
|
||||||
echo " - 不影响其它 PM2 应用"
|
echo " - 不影响其它 PM2 应用"
|
||||||
echo ""
|
echo ""
|
||||||
if ! confirm_yes "确认卸载?"; then
|
if ! confirm_yes "确认卸载?"; then
|
||||||
log "已取消卸载"
|
log "已取消卸载"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local stamp backup_dir removed_dir
|
local stamp backup_dir removed_dir
|
||||||
stamp="$(TZ="${TZ_NAME}" date +%Y%m%d-%H%M%S)"
|
stamp="$(TZ="${TZ_NAME}" date +%Y%m%d-%H%M%S)"
|
||||||
backup_dir="${BACKUP_ROOT}/pre-uninstall-${stamp}"
|
backup_dir="${BACKUP_ROOT}/pre-uninstall-${stamp}"
|
||||||
removed_dir="${INSTALL_ROOT}.removed.${stamp}"
|
removed_dir="${INSTALL_ROOT}.removed.${stamp}"
|
||||||
mkdir -p "${backup_dir}"
|
mkdir -p "${backup_dir}"
|
||||||
|
|
||||||
step "备份配置"
|
step "备份配置"
|
||||||
if [[ -f "${REPO_ROOT}/.env" ]]; then
|
if [[ -f "${REPO_ROOT}/.env" ]]; then
|
||||||
cp -a "${REPO_ROOT}/.env" "${backup_dir}/.env"
|
cp -a "${REPO_ROOT}/.env" "${backup_dir}/.env"
|
||||||
fi
|
fi
|
||||||
if [[ -d "${REPO_ROOT}/backend/data" ]]; then
|
if [[ -d "${REPO_ROOT}/backend/data" ]]; then
|
||||||
cp -a "${REPO_ROOT}/backend/data" "${backup_dir}/data" 2>/dev/null || true
|
cp -a "${REPO_ROOT}/backend/data" "${backup_dir}/data" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
{
|
{
|
||||||
echo "created_at=${stamp}"
|
echo "created_at=${stamp}"
|
||||||
echo "install_root=${INSTALL_ROOT}"
|
echo "install_root=${INSTALL_ROOT}"
|
||||||
echo "removed_dir=${removed_dir}"
|
echo "removed_dir=${removed_dir}"
|
||||||
echo "pm2_app=${PM2_APP_NAME}"
|
echo "pm2_app=${PM2_APP_NAME}"
|
||||||
} >"${backup_dir}/uninstall.manifest"
|
} >"${backup_dir}/uninstall.manifest"
|
||||||
|
|
||||||
step "停止并移除 PM2 进程 ${PM2_APP_NAME}"
|
step "停止并移除 PM2 进程 ${PM2_APP_NAME}"
|
||||||
pm2_stop_project
|
pm2_stop_project
|
||||||
pm2_delete_project
|
pm2_delete_project
|
||||||
|
|
||||||
step "移走安装目录"
|
step "移走安装目录"
|
||||||
if [[ -d "${INSTALL_ROOT}" ]]; then
|
if [[ -d "${INSTALL_ROOT}" ]]; then
|
||||||
mv "${INSTALL_ROOT}" "${removed_dir}"
|
mv "${INSTALL_ROOT}" "${removed_dir}"
|
||||||
log "已移动: ${INSTALL_ROOT} -> ${removed_dir}"
|
log "已移动: ${INSTALL_ROOT} -> ${removed_dir}"
|
||||||
else
|
else
|
||||||
log "安装目录不存在,跳过"
|
log "安装目录不存在,跳过"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "卸载完成."
|
echo "卸载完成."
|
||||||
echo " 配置备份: ${backup_dir}"
|
echo " 配置备份: ${backup_dir}"
|
||||||
echo " 旧目录: ${removed_dir} (确认无误后可手动 rm -rf)"
|
echo " 旧目录: ${removed_dir} (确认无误后可手动 rm -rf)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "回滚示例:"
|
echo "回滚示例:"
|
||||||
echo " mv ${removed_dir} ${INSTALL_ROOT}"
|
echo " mv ${removed_dir} ${INSTALL_ROOT}"
|
||||||
echo " bash ${INSTALL_ROOT}/deploy/manage.sh # 选 1 修复环境"
|
echo " bash ${INSTALL_ROOT}/deploy/manage.sh # 选 1 修复环境"
|
||||||
}
|
}
|
||||||
|
|
||||||
main_uninstall "$@"
|
main_uninstall "$@"
|
||||||
|
|||||||
+29
-29
@@ -1,29 +1,29 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# deploy/lib/update.sh — 更新(仅 git pull,禁止 scp)
|
# deploy/lib/update.sh — 更新(仅 git pull,禁止 scp)
|
||||||
set -e
|
set -e
|
||||||
set -u
|
set -u
|
||||||
if [ -n "${BASH_VERSION:-}" ]; then
|
if [ -n "${BASH_VERSION:-}" ]; then
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
fi
|
fi
|
||||||
|
|
||||||
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
# shellcheck source=common.sh
|
# shellcheck source=common.sh
|
||||||
source "${LIB_DIR}/common.sh"
|
source "${LIB_DIR}/common.sh"
|
||||||
|
|
||||||
main_update() {
|
main_update() {
|
||||||
require_root
|
require_root
|
||||||
if ! REPO_ROOT="$(resolve_repo_root)"; then
|
if ! REPO_ROOT="$(resolve_repo_root)"; then
|
||||||
die "未找到安装目录 ${INSTALL_ROOT},请先执行「1) 一键部署」"
|
die "未找到安装目录 ${INSTALL_ROOT},请先执行「1) 一键部署」"
|
||||||
fi
|
fi
|
||||||
if ! repo_ready "${REPO_ROOT}"; then
|
if ! repo_ready "${REPO_ROOT}"; then
|
||||||
die "安装不完整,请先执行「1) 一键部署」"
|
die "安装不完整,请先执行「1) 一键部署」"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
step "更新 eth_hedge_sim (git pull + 构建 + pm2 reload)"
|
step "更新 比特骆驼自动化对冲系统 (git pull + 构建 + pm2 reload)"
|
||||||
bash "${REPO_ROOT}/deploy/pull_and_restart.sh"
|
bash "${REPO_ROOT}/deploy/pull_and_restart.sh"
|
||||||
verify_health || true
|
verify_health || true
|
||||||
echo ""
|
echo ""
|
||||||
log "更新完成"
|
log "更新完成"
|
||||||
}
|
}
|
||||||
|
|
||||||
main_update "$@"
|
main_update "$@"
|
||||||
|
|||||||
+224
-224
@@ -1,224 +1,224 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# eth_hedge_sim 部署管理器 — 一键部署 / 卸载 / 更新
|
# 比特骆驼自动化对冲系统 部署管理器(工程 eth_hedge_sim)
|
||||||
#
|
#
|
||||||
# 新服务器(免克隆):
|
# 新服务器(免克隆):
|
||||||
# curl -fsSL https://git.bz121.com/dekun/eth_hedge_sim/raw/branch/main/deploy/manage.sh | bash
|
# curl -fsSL https://git.bz121.com/dekun/eth_hedge_sim/raw/branch/main/deploy/manage.sh | bash
|
||||||
#
|
#
|
||||||
# 已安装:
|
# 已安装:
|
||||||
# bash /opt/eth_hedge_sim/deploy/manage.sh
|
# bash /opt/eth_hedge_sim/deploy/manage.sh
|
||||||
#
|
#
|
||||||
set -e
|
set -e
|
||||||
if [ -n "${BASH_VERSION:-}" ]; then
|
if [ -n "${BASH_VERSION:-}" ]; then
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
fi
|
fi
|
||||||
|
|
||||||
INSTALL_ROOT="${INSTALL_ROOT:-/opt/eth_hedge_sim}"
|
INSTALL_ROOT="${INSTALL_ROOT:-/opt/eth_hedge_sim}"
|
||||||
GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/eth_hedge_sim.git}"
|
GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/eth_hedge_sim.git}"
|
||||||
GIT_BRANCH="${GIT_BRANCH:-main}"
|
GIT_BRANCH="${GIT_BRANCH:-main}"
|
||||||
|
|
||||||
# curl | bash 时脚本从 stdin 执行,BASH_SOURCE[0] 为空;须先安全探测再 set -u
|
# curl | bash 时脚本从 stdin 执行,BASH_SOURCE[0] 为空;须先安全探测再 set -u
|
||||||
_script_src="${BASH_SOURCE[0]:-}"
|
_script_src="${BASH_SOURCE[0]:-}"
|
||||||
if [[ -n "${_script_src}" && -f "${_script_src}" ]]; then
|
if [[ -n "${_script_src}" && -f "${_script_src}" ]]; then
|
||||||
DEPLOY_DIR="$(cd "$(dirname "${_script_src}")" && pwd)"
|
DEPLOY_DIR="$(cd "$(dirname "${_script_src}")" && pwd)"
|
||||||
REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)"
|
REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)"
|
||||||
LIB_DIR="${DEPLOY_DIR}/lib"
|
LIB_DIR="${DEPLOY_DIR}/lib"
|
||||||
else
|
else
|
||||||
DEPLOY_DIR=""
|
DEPLOY_DIR=""
|
||||||
REPO_ROOT=""
|
REPO_ROOT=""
|
||||||
LIB_DIR=""
|
LIB_DIR=""
|
||||||
fi
|
fi
|
||||||
unset _script_src
|
unset _script_src
|
||||||
|
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
repo_ready() {
|
repo_ready() {
|
||||||
[[ -f "${1}/deploy/manage.sh" && -f "${1}/deploy/pull_and_restart.sh" && -d "${1}/deploy/lib" ]]
|
[[ -f "${1}/deploy/manage.sh" && -f "${1}/deploy/pull_and_restart.sh" && -d "${1}/deploy/lib" ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_git_cli() {
|
ensure_git_cli() {
|
||||||
if command -v git >/dev/null 2>&1; then
|
if command -v git >/dev/null 2>&1; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if command -v apt-get >/dev/null 2>&1; then
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
apt-get update -qq
|
apt-get update -qq
|
||||||
apt-get install -y git ca-certificates curl
|
apt-get install -y git ca-certificates curl
|
||||||
else
|
else
|
||||||
echo "错误: 未找到 git" >&2
|
echo "错误: 未找到 git" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
sync_repo_if_present() {
|
sync_repo_if_present() {
|
||||||
local root="$1"
|
local root="$1"
|
||||||
if [[ -d "${root}/.git" ]] && command -v git >/dev/null 2>&1; then
|
if [[ -d "${root}/.git" ]] && command -v git >/dev/null 2>&1; then
|
||||||
git -C "${root}" fetch --all --prune 2>/dev/null || true
|
git -C "${root}" fetch --all --prune 2>/dev/null || true
|
||||||
git -C "${root}" checkout "${GIT_BRANCH}" 2>/dev/null || true
|
git -C "${root}" checkout "${GIT_BRANCH}" 2>/dev/null || true
|
||||||
git -C "${root}" pull --ff-only origin "${GIT_BRANCH}" 2>/dev/null \
|
git -C "${root}" pull --ff-only origin "${GIT_BRANCH}" 2>/dev/null \
|
||||||
|| git -C "${root}" pull --ff-only 2>/dev/null \
|
|| git -C "${root}" pull --ff-only 2>/dev/null \
|
||||||
|| true
|
|| true
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# 旧目录已存在但缺 manage.sh:尽量 git pull / 修复后切到仓库内脚本
|
# 旧目录已存在但缺 manage.sh:尽量 git pull / 修复后切到仓库内脚本
|
||||||
heal_existing_install() {
|
heal_existing_install() {
|
||||||
local root="$1"
|
local root="$1"
|
||||||
echo "检测到已有目录但缺少管理脚本: ${root}"
|
echo "检测到已有目录但缺少管理脚本: ${root}"
|
||||||
echo "尝试同步最新代码…"
|
echo "尝试同步最新代码…"
|
||||||
ensure_git_cli
|
ensure_git_cli
|
||||||
|
|
||||||
if [[ -d "${root}/.git" ]]; then
|
if [[ -d "${root}/.git" ]]; then
|
||||||
sync_repo_if_present "${root}"
|
sync_repo_if_present "${root}"
|
||||||
if repo_ready "${root}"; then
|
if repo_ready "${root}"; then
|
||||||
echo "同步成功,切换到仓库内 manage.sh"
|
echo "同步成功,切换到仓库内 manage.sh"
|
||||||
exec bash "${root}/deploy/manage.sh" "$@" </dev/tty
|
exec bash "${root}/deploy/manage.sh" "$@" </dev/tty
|
||||||
fi
|
fi
|
||||||
echo "git pull 后仍不完整,尝试强制对齐 origin/${GIT_BRANCH} …"
|
echo "git pull 后仍不完整,尝试强制对齐 origin/${GIT_BRANCH} …"
|
||||||
git -C "${root}" fetch origin "${GIT_BRANCH}" || true
|
git -C "${root}" fetch origin "${GIT_BRANCH}" || true
|
||||||
git -C "${root}" reset --hard "origin/${GIT_BRANCH}" || true
|
git -C "${root}" reset --hard "origin/${GIT_BRANCH}" || true
|
||||||
if repo_ready "${root}"; then
|
if repo_ready "${root}"; then
|
||||||
echo "对齐成功,切换到仓库内 manage.sh"
|
echo "对齐成功,切换到仓库内 manage.sh"
|
||||||
exec bash "${root}/deploy/manage.sh" "$@" </dev/tty
|
exec bash "${root}/deploy/manage.sh" "$@" </dev/tty
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 无 .git 或仍不可用:备份 .env/data 后重新 clone
|
# 无 .git 或仍不可用:备份 .env/data 后重新 clone
|
||||||
echo "将备份配置并重新克隆到 ${root}"
|
echo "将备份配置并重新克隆到 ${root}"
|
||||||
local stamp backup_dir
|
local stamp backup_dir
|
||||||
stamp="$(date +%Y%m%d-%H%M%S)"
|
stamp="$(date +%Y%m%d-%H%M%S)"
|
||||||
backup_dir="/root/backups/eth_hedge_sim/heal-${stamp}"
|
backup_dir="/root/backups/eth_hedge_sim/heal-${stamp}"
|
||||||
mkdir -p "${backup_dir}"
|
mkdir -p "${backup_dir}"
|
||||||
[[ -f "${root}/.env" ]] && cp -a "${root}/.env" "${backup_dir}/.env"
|
[[ -f "${root}/.env" ]] && cp -a "${root}/.env" "${backup_dir}/.env"
|
||||||
[[ -d "${root}/backend/data" ]] && cp -a "${root}/backend/data" "${backup_dir}/data" || true
|
[[ -d "${root}/backend/data" ]] && cp -a "${root}/backend/data" "${backup_dir}/data" || true
|
||||||
echo "备份目录: ${backup_dir}"
|
echo "备份目录: ${backup_dir}"
|
||||||
|
|
||||||
local removed="${root}.old.${stamp}"
|
local removed="${root}.old.${stamp}"
|
||||||
mv "${root}" "${removed}"
|
mv "${root}" "${removed}"
|
||||||
echo "旧目录已移至: ${removed}"
|
echo "旧目录已移至: ${removed}"
|
||||||
mkdir -p "$(dirname "${root}")"
|
mkdir -p "$(dirname "${root}")"
|
||||||
git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${root}"
|
git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${root}"
|
||||||
if [[ -f "${backup_dir}/.env" ]]; then
|
if [[ -f "${backup_dir}/.env" ]]; then
|
||||||
cp -a "${backup_dir}/.env" "${root}/.env"
|
cp -a "${backup_dir}/.env" "${root}/.env"
|
||||||
echo "已恢复 .env"
|
echo "已恢复 .env"
|
||||||
fi
|
fi
|
||||||
if [[ -d "${backup_dir}/data" ]]; then
|
if [[ -d "${backup_dir}/data" ]]; then
|
||||||
mkdir -p "${root}/backend"
|
mkdir -p "${root}/backend"
|
||||||
cp -a "${backup_dir}/data" "${root}/backend/data"
|
cp -a "${backup_dir}/data" "${root}/backend/data"
|
||||||
echo "已恢复 backend/data"
|
echo "已恢复 backend/data"
|
||||||
fi
|
fi
|
||||||
exec bash "${root}/deploy/manage.sh" "$@" </dev/tty
|
exec bash "${root}/deploy/manage.sh" "$@" </dev/tty
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrap_repo() {
|
bootstrap_repo() {
|
||||||
if repo_ready "${INSTALL_ROOT}"; then
|
if repo_ready "${INSTALL_ROOT}"; then
|
||||||
REPO_ROOT="${INSTALL_ROOT}"
|
REPO_ROOT="${INSTALL_ROOT}"
|
||||||
DEPLOY_DIR="${REPO_ROOT}/deploy"
|
DEPLOY_DIR="${REPO_ROOT}/deploy"
|
||||||
LIB_DIR="${DEPLOY_DIR}/lib"
|
LIB_DIR="${DEPLOY_DIR}/lib"
|
||||||
sync_repo_if_present "${REPO_ROOT}"
|
sync_repo_if_present "${REPO_ROOT}"
|
||||||
# pull 后可能才有 lib,再校验一次
|
# pull 后可能才有 lib,再校验一次
|
||||||
if ! repo_ready "${REPO_ROOT}"; then
|
if ! repo_ready "${REPO_ROOT}"; then
|
||||||
heal_existing_install "${INSTALL_ROOT}" "$@"
|
heal_existing_install "${INSTALL_ROOT}" "$@"
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if [[ -n "${REPO_ROOT}" ]] && repo_ready "${REPO_ROOT}"; then
|
if [[ -n "${REPO_ROOT}" ]] && repo_ready "${REPO_ROOT}"; then
|
||||||
DEPLOY_DIR="${REPO_ROOT}/deploy"
|
DEPLOY_DIR="${REPO_ROOT}/deploy"
|
||||||
LIB_DIR="${DEPLOY_DIR}/lib"
|
LIB_DIR="${DEPLOY_DIR}/lib"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 目录在,但还是旧版(你当前的情况)
|
# 目录在,但还是旧版(你当前的情况)
|
||||||
if [[ -d "${INSTALL_ROOT}" ]]; then
|
if [[ -d "${INSTALL_ROOT}" ]]; then
|
||||||
if [[ "$(id -u)" -ne 0 ]]; then
|
if [[ "$(id -u)" -ne 0 ]]; then
|
||||||
echo "错误: 请使用 root 执行" >&2
|
echo "错误: 请使用 root 执行" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
heal_existing_install "${INSTALL_ROOT}" "$@"
|
heal_existing_install "${INSTALL_ROOT}" "$@"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "eth_hedge_sim 部署管理器 — 首次自举"
|
echo "比特骆驼自动化对冲系统 部署管理器 — 首次自举"
|
||||||
echo "将克隆到: ${INSTALL_ROOT}"
|
echo "将克隆到: ${INSTALL_ROOT}"
|
||||||
if [[ "$(id -u)" -ne 0 ]]; then
|
if [[ "$(id -u)" -ne 0 ]]; then
|
||||||
echo "错误: 请使用 root 执行" >&2
|
echo "错误: 请使用 root 执行" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
ensure_git_cli
|
ensure_git_cli
|
||||||
mkdir -p "$(dirname "${INSTALL_ROOT}")"
|
mkdir -p "$(dirname "${INSTALL_ROOT}")"
|
||||||
git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${INSTALL_ROOT}"
|
git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${INSTALL_ROOT}"
|
||||||
# clone 后用仓库内脚本 + 终端 stdin(管道已 EOF)
|
# clone 后用仓库内脚本 + 终端 stdin(管道已 EOF)
|
||||||
exec bash "${INSTALL_ROOT}/deploy/manage.sh" "$@" </dev/tty
|
exec bash "${INSTALL_ROOT}/deploy/manage.sh" "$@" </dev/tty
|
||||||
}
|
}
|
||||||
|
|
||||||
show_banner() {
|
show_banner() {
|
||||||
local path
|
local path
|
||||||
path="${INSTALL_ROOT}"
|
path="${INSTALL_ROOT}"
|
||||||
if [[ -n "${REPO_ROOT}" ]] && repo_ready "${REPO_ROOT}"; then
|
if [[ -n "${REPO_ROOT}" ]] && repo_ready "${REPO_ROOT}"; then
|
||||||
path="${REPO_ROOT}"
|
path="${REPO_ROOT}"
|
||||||
fi
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
echo "╔══════════════════════════════════════╗"
|
echo "╔══════════════════════════════════════╗"
|
||||||
echo "║ eth_hedge_sim 部署管理器 ║"
|
echo "║ 比特骆驼自动化对冲系统 部署管理器 ║"
|
||||||
echo "║ 路径: ${path}"
|
echo "║ 路径: ${path}"
|
||||||
echo "╚══════════════════════════════════════╝"
|
echo "╚══════════════════════════════════════╝"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
show_menu() {
|
show_menu() {
|
||||||
echo " 1) 一键部署"
|
echo " 1) 一键部署"
|
||||||
echo " 2) 一键卸载"
|
echo " 2) 一键卸载"
|
||||||
echo " 3) 更新"
|
echo " 3) 更新"
|
||||||
echo " 0) 退出"
|
echo " 0) 退出"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# 交互读入(兼容 curl|bash)
|
# 交互读入(兼容 curl|bash)
|
||||||
cm_read() {
|
cm_read() {
|
||||||
local __var="$1"
|
local __var="$1"
|
||||||
local __prompt="${2:-}"
|
local __prompt="${2:-}"
|
||||||
local __line=""
|
local __line=""
|
||||||
if [[ -n "${__prompt}" ]]; then
|
if [[ -n "${__prompt}" ]]; then
|
||||||
printf '%s' "${__prompt}" >/dev/tty 2>/dev/null || printf '%s' "${__prompt}"
|
printf '%s' "${__prompt}" >/dev/tty 2>/dev/null || printf '%s' "${__prompt}"
|
||||||
fi
|
fi
|
||||||
if [[ -r /dev/tty ]]; then
|
if [[ -r /dev/tty ]]; then
|
||||||
IFS= read -r __line </dev/tty || true
|
IFS= read -r __line </dev/tty || true
|
||||||
else
|
else
|
||||||
IFS= read -r __line || true
|
IFS= read -r __line || true
|
||||||
fi
|
fi
|
||||||
printf -v "${__var}" '%s' "${__line}"
|
printf -v "${__var}" '%s' "${__line}"
|
||||||
}
|
}
|
||||||
|
|
||||||
main_menu() {
|
main_menu() {
|
||||||
bootstrap_repo
|
bootstrap_repo
|
||||||
# shellcheck source=lib/common.sh
|
# shellcheck source=lib/common.sh
|
||||||
source "${LIB_DIR}/common.sh"
|
source "${LIB_DIR}/common.sh"
|
||||||
REPO_ROOT="$(resolve_repo_root || echo "${INSTALL_ROOT}")"
|
REPO_ROOT="$(resolve_repo_root || echo "${INSTALL_ROOT}")"
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
show_banner
|
show_banner
|
||||||
show_menu
|
show_menu
|
||||||
local choice=""
|
local choice=""
|
||||||
cm_read choice "请选择 [0-3]: "
|
cm_read choice "请选择 [0-3]: "
|
||||||
case "${choice}" in
|
case "${choice}" in
|
||||||
1)
|
1)
|
||||||
bash "${LIB_DIR}/install.sh"
|
bash "${LIB_DIR}/install.sh"
|
||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
bash "${LIB_DIR}/uninstall.sh"
|
bash "${LIB_DIR}/uninstall.sh"
|
||||||
;;
|
;;
|
||||||
3)
|
3)
|
||||||
bash "${LIB_DIR}/update.sh"
|
bash "${LIB_DIR}/update.sh"
|
||||||
;;
|
;;
|
||||||
0)
|
0)
|
||||||
echo "再见."
|
echo "再见."
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "无效选项,请输入 0-3"
|
echo "无效选项,请输入 0-3"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
echo ""
|
echo ""
|
||||||
cm_read _ "按 Enter 返回菜单..."
|
cm_read _ "按 Enter 返回菜单..."
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
main_menu "$@"
|
main_menu "$@"
|
||||||
|
|||||||
+55
-55
@@ -1,55 +1,55 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# 一键更新/部署:仅 git pull,禁止 scp 传代码。
|
# 一键更新/部署:仅 git pull,禁止 scp 传代码。
|
||||||
# 用法:
|
# 用法:
|
||||||
# bash /opt/eth_hedge_sim/deploy/pull_and_restart.sh
|
# bash /opt/eth_hedge_sim/deploy/pull_and_restart.sh
|
||||||
# 或通过 manage.sh 选 1/3
|
# 或通过 manage.sh 选 1/3
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
cd "$ROOT"
|
cd "$ROOT"
|
||||||
|
|
||||||
echo "[eth_hedge_sim] pull @ $ROOT"
|
echo "[比特骆驼] pull @ $ROOT"
|
||||||
if [[ -d "$ROOT/.git" ]]; then
|
if [[ -d "$ROOT/.git" ]]; then
|
||||||
git fetch --all --prune
|
git fetch --all --prune
|
||||||
git pull --ff-only
|
git pull --ff-only
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ! -f "$ROOT/.env" ]]; then
|
if [[ ! -f "$ROOT/.env" ]]; then
|
||||||
echo "[eth_hedge_sim] missing .env — copy from example"
|
echo "[比特骆驼] missing .env — copy from example"
|
||||||
cp "$ROOT/.env.example" "$ROOT/.env"
|
cp "$ROOT/.env.example" "$ROOT/.env"
|
||||||
# 云上测试机默认直连 OKX
|
# 云上测试机默认直连 OKX
|
||||||
sed -i 's/^OKX_HTTP_PROXY=.*/OKX_HTTP_PROXY=/' "$ROOT/.env" || true
|
sed -i 's/^OKX_HTTP_PROXY=.*/OKX_HTTP_PROXY=/' "$ROOT/.env" || true
|
||||||
sed -i 's/^API_PORT=.*/API_PORT=5155/' "$ROOT/.env" || true
|
sed -i 's/^API_PORT=.*/API_PORT=5155/' "$ROOT/.env" || true
|
||||||
sed -i 's/^ENV_NAME=.*/ENV_NAME=test/' "$ROOT/.env" || true
|
sed -i 's/^ENV_NAME=.*/ENV_NAME=test/' "$ROOT/.env" || true
|
||||||
sed -i 's/^EXCHANGE=.*/EXCHANGE=okx/' "$ROOT/.env" || true
|
sed -i 's/^EXCHANGE=.*/EXCHANGE=okx/' "$ROOT/.env" || true
|
||||||
echo "[eth_hedge_sim] wrote .env — please review AUTH_* secrets"
|
echo "[比特骆驼] wrote .env — please review AUTH_* secrets"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ! -d "$ROOT/.venv" ]]; then
|
if [[ ! -d "$ROOT/.venv" ]]; then
|
||||||
python3 -m venv "$ROOT/.venv"
|
python3 -m venv "$ROOT/.venv"
|
||||||
fi
|
fi
|
||||||
# shellcheck disable=SC1091
|
# shellcheck disable=SC1091
|
||||||
source "$ROOT/.venv/bin/activate"
|
source "$ROOT/.venv/bin/activate"
|
||||||
pip install -U pip
|
pip install -U pip
|
||||||
pip install -r "$ROOT/requirements.txt"
|
pip install -r "$ROOT/requirements.txt"
|
||||||
|
|
||||||
if ! command -v npm >/dev/null 2>&1; then
|
if ! command -v npm >/dev/null 2>&1; then
|
||||||
echo "ERROR: npm not found. Install Node.js 18+ first (manage.sh 一键部署会自动装)."
|
echo "ERROR: npm not found. Install Node.js 18+ first (manage.sh 一键部署会自动装)."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
(
|
(
|
||||||
cd "$ROOT/frontend"
|
cd "$ROOT/frontend"
|
||||||
if [[ -f package-lock.json ]]; then npm ci; else npm install; fi
|
if [[ -f package-lock.json ]]; then npm ci; else npm install; fi
|
||||||
npm run build
|
npm run build
|
||||||
)
|
)
|
||||||
|
|
||||||
if ! command -v pm2 >/dev/null 2>&1; then
|
if ! command -v pm2 >/dev/null 2>&1; then
|
||||||
npm install -g pm2
|
npm install -g pm2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 仅 reload 本项目进程,禁止 pm2 restart all
|
# 仅 reload 本项目进程,禁止 pm2 restart all
|
||||||
pm2 startOrReload "$ROOT/deploy/ecosystem.config.cjs" --update-env
|
pm2 startOrReload "$ROOT/deploy/ecosystem.config.cjs" --update-env
|
||||||
pm2 save
|
pm2 save
|
||||||
|
|
||||||
echo "[eth_hedge_sim] done. health: http://127.0.0.1:5155/health"
|
echo "[比特骆驼] done. health: http://127.0.0.1:5155/health"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# OKX 实盘策略说明
|
# OKX 实盘策略说明
|
||||||
|
|
||||||
> 专用于 **OKX** LIVE。通用对冲逻辑见 [策略说明](./策略说明.md);币安见 [币安实盘策略说明](./币安实盘策略说明.md)。
|
> 产品:**比特骆驼自动化对冲系统** · 专用于 **OKX** LIVE。通用对冲逻辑见 [策略说明](./策略说明.md);币安见 [币安实盘策略说明](./币安实盘策略说明.md)。
|
||||||
> **标准仓**:永续 **1 ETH** + 期权 **2 ETH** 名义(倍数 `k` 可缩放)。
|
> **标准仓**:永续 **1 ETH** + 期权 **2 ETH** 名义(倍数 `k` 可缩放)。
|
||||||
> 更新:2026-07-26
|
> 更新:2026-07-26
|
||||||
|
|
||||||
|
|||||||
+284
-283
@@ -1,283 +1,284 @@
|
|||||||
# eth_hedge_sim — 代码结构
|
# 比特骆驼自动化对冲系统 — 代码结构
|
||||||
|
|
||||||
> 本文描述目标仓库目录与模块职责。当前阶段以文档为准;业务代码按阶段逐步添加。
|
> **产品名**:比特骆驼自动化对冲系统 · **工程目录**:`eth_hedge_sim`
|
||||||
> 可从 `crypto_monitor` **复制** 有用片段到本仓库后改造;**禁止修改** 现网仓库内文件。
|
> 本文描述目标仓库目录与模块职责。当前阶段以文档为准;业务代码按阶段逐步添加。
|
||||||
|
> 可从 `crypto_monitor` **复制** 有用片段到本仓库后改造;**禁止修改** 现网仓库内文件。
|
||||||
---
|
|
||||||
|
---
|
||||||
## 1. 根目录总览
|
|
||||||
|
## 1. 根目录总览
|
||||||
```text
|
|
||||||
eth_hedge_sim/
|
```text
|
||||||
├── README.md
|
eth_hedge_sim/ # 产品:比特骆驼自动化对冲系统
|
||||||
├── .gitignore
|
├── README.md
|
||||||
├── .env.example # 无密钥的示例;真 .env 不上库
|
├── .gitignore
|
||||||
├── package.json # 若前端/Node;或改用 Python 则 requirements.txt
|
├── .env.example # 无密钥的示例;真 .env 不上库
|
||||||
├── requirements.txt # 后端若用 Python
|
├── package.json # 若前端/Node;或改用 Python 则 requirements.txt
|
||||||
├── docs/
|
├── requirements.txt # 后端若用 Python
|
||||||
│ ├── 开发方案.md # 业务与部署方案(本文档姐妹篇)
|
├── docs/
|
||||||
│ └── 代码结构.md # 本文件
|
│ ├── 开发方案.md # 业务与部署方案(本文档姐妹篇)
|
||||||
├── deploy/
|
│ └── 代码结构.md # 本文件
|
||||||
│ ├── ecosystem.config.cjs # PM2:仅本项目进程
|
├── deploy/
|
||||||
│ └── pull_and_restart.sh # git pull + 构建 + pm2 reload(不碰现网)
|
│ ├── ecosystem.config.cjs # PM2:仅本项目进程
|
||||||
├── backend/ # API + 行情 + 策略 + 本地撮合
|
│ └── pull_and_restart.sh # git pull + 构建 + pm2 reload(不碰现网)
|
||||||
│ ├── app/
|
├── backend/ # API + 行情 + 策略 + 本地撮合
|
||||||
│ │ ├── main.py # 或 index.ts:进程入口
|
│ ├── app/
|
||||||
│ │ ├── config.py
|
│ │ ├── main.py # 或 index.ts:进程入口
|
||||||
│ │ ├── api/ # HTTP 路由
|
│ │ ├── config.py
|
||||||
│ │ ├── ws/ # 向前端推送
|
│ │ ├── api/ # HTTP 路由
|
||||||
│ │ ├── market/ # OKX 只读行情
|
│ │ ├── ws/ # 向前端推送
|
||||||
│ │ ├── strategy/ # 自动对冲状态机
|
│ │ ├── market/ # OKX 只读行情
|
||||||
│ │ ├── sim/ # 本地撮合与账本
|
│ │ ├── strategy/ # 自动对冲状态机
|
||||||
│ │ ├── live/ # 实盘适配器(后期,默认关闭)
|
│ │ ├── sim/ # 本地撮合与账本
|
||||||
│ │ ├── models/ # DB 模型 / schema
|
│ │ ├── live/ # 实盘适配器(后期,默认关闭)
|
||||||
│ │ └── services/ # 组、统计、设置等应用服务
|
│ │ ├── models/ # DB 模型 / schema
|
||||||
│ ├── data/ # 本地 SQLite 等(gitignore 数据文件)
|
│ │ └── services/ # 组、统计、设置等应用服务
|
||||||
│ └── tests/
|
│ ├── data/ # 本地 SQLite 等(gitignore 数据文件)
|
||||||
├── frontend/ # OKX 风格 Web
|
│ └── tests/
|
||||||
│ ├── index.html
|
├── frontend/ # Web 控制台(比特骆驼)
|
||||||
│ ├── package.json
|
│ ├── index.html
|
||||||
│ ├── src/
|
│ ├── package.json
|
||||||
│ │ ├── main.tsx
|
│ ├── src/
|
||||||
│ │ ├── App.tsx
|
│ │ ├── main.tsx
|
||||||
│ │ ├── layouts/ # 导航壳
|
│ │ ├── App.tsx
|
||||||
│ │ ├── pages/
|
│ │ ├── layouts/ # 导航壳
|
||||||
│ │ │ ├── Plan.tsx # 自动对冲计划
|
│ │ ├── pages/
|
||||||
│ │ │ ├── Trades.tsx # 交易记录
|
│ │ │ ├── Plan.tsx # 自动对冲计划
|
||||||
│ │ │ ├── Stats.tsx # 统计
|
│ │ │ ├── Trades.tsx # 交易记录
|
||||||
│ │ │ └── Settings.tsx # 系统设置
|
│ │ │ ├── Stats.tsx # 统计
|
||||||
│ │ ├── components/ # 盘口、组标识、持仓条等
|
│ │ │ └── Settings.tsx # 系统设置
|
||||||
│ │ ├── api/ # 调后端
|
│ │ ├── components/ # 盘口、组标识、持仓条等
|
||||||
│ │ └── styles/ # OKX 深色交易台风
|
│ │ ├── api/ # 调后端
|
||||||
│ └── dist/ # 构建产物(可部署由 API 托管或 nginx)
|
│ │ └── styles/ # 深色交易台风
|
||||||
└── scripts/ # 运维/一次性工具(可选)
|
│ └── dist/ # 构建产物(可部署由 API 托管或 nginx)
|
||||||
└── smoke_market.py # 只读行情连通性检查
|
└── scripts/ # 运维/一次性工具(可选)
|
||||||
```
|
└── smoke_market.py # 只读行情连通性检查
|
||||||
|
```
|
||||||
技术栈可在开工时二选一(建议尽快定一种,避免双栈):
|
|
||||||
|
技术栈可在开工时二选一(建议尽快定一种,避免双栈):
|
||||||
- **推荐 A**:后端 Python(FastAPI)+ 前端 React/Vite(与现网 Python 生态接近,便于复制行情代码)。
|
|
||||||
- **推荐 B**:全 Node(Nest/Express + React)。
|
- **推荐 A**:后端 Python(FastAPI)+ 前端 React/Vite(与现网 Python 生态接近,便于复制行情代码)。
|
||||||
|
- **推荐 B**:全 Node(Nest/Express + React)。
|
||||||
下文按 **推荐 A** 描述模块;若选 B,目录名对应平移即可。
|
|
||||||
|
下文按 **推荐 A** 描述模块;若选 B,目录名对应平移即可。
|
||||||
---
|
|
||||||
|
---
|
||||||
## 2. 后端模块职责
|
|
||||||
|
## 2. 后端模块职责
|
||||||
### 2.1 `backend/app/market/` — 行情(OKX 实盘只读)
|
|
||||||
|
### 2.1 `backend/app/market/` — 行情(OKX 实盘只读)
|
||||||
```text
|
|
||||||
market/
|
```text
|
||||||
├── okx_rest.py # 合约列表、到期、启动对齐
|
market/
|
||||||
├── okx_ws.py # 永续 + 期权盘口订阅
|
├── okx_rest.py # 合约列表、到期、启动对齐
|
||||||
├── instruments.py # 解析「次日 16:00」到期、ATM 行权价
|
├── okx_ws.py # 永续 + 期权盘口订阅
|
||||||
├── book_cache.py # 买一/卖一/深度内存缓存
|
├── instruments.py # 解析「次日 16:00」到期、ATM 行权价
|
||||||
└── types.py # Quote / Depth 结构
|
├── book_cache.py # 买一/卖一/深度内存缓存
|
||||||
```
|
└── types.py # Quote / Depth 结构
|
||||||
|
```
|
||||||
职责:
|
|
||||||
|
职责:
|
||||||
- 只拉行情,**不调用交易接口**。
|
|
||||||
- 对外提供:永续买卖一、Call/Put 买卖一与深度、标记价。
|
- 只拉行情,**不调用交易接口**。
|
||||||
- 可选:行情快照落盘,供 P4 回放。
|
- 对外提供:永续买卖一、Call/Put 买卖一与深度、标记价。
|
||||||
|
- 可选:行情快照落盘,供 P4 回放。
|
||||||
可参考现网 OKX WS/REST 实现,复制后改成本模块 API。
|
|
||||||
|
可参考现网 OKX WS/REST 实现,复制后改成本模块 API。
|
||||||
### 2.2 `backend/app/sim/` — 本地模拟
|
|
||||||
|
### 2.2 `backend/app/sim/` — 本地模拟
|
||||||
```text
|
|
||||||
sim/
|
```text
|
||||||
├── matcher.py # 撮合:永续市价;期权吃买卖一;滑点=1×f;双边手续费
|
sim/
|
||||||
├── ledger.py # 虚拟资金、占用、流水
|
├── matcher.py # 撮合:永续市价;期权吃买卖一;滑点=1×f;双边手续费
|
||||||
├── liquidity.py # 买一深度是否覆盖 2 ETH
|
├── ledger.py # 虚拟资金、占用、流水
|
||||||
└── pricing.py # 成交价公式(含 f)
|
├── liquidity.py # 买一深度是否覆盖 2 ETH
|
||||||
```
|
└── pricing.py # 成交价公式(含 f)
|
||||||
|
```
|
||||||
成交价口径(与开发方案一致):
|
|
||||||
|
成交价口径(与开发方案一致):
|
||||||
| 腿 | 动作 | 基准 | 成交价 |
|
|
||||||
|----|------|------|--------|
|
| 腿 | 动作 | 基准 | 成交价 |
|
||||||
| 永续 | 开多 / 平空 | 卖一 | 基准 × (1+f) |
|
|----|------|------|--------|
|
||||||
| 永续 | 开空 / 平多 | 买一 | 基准 × (1-f) |
|
| 永续 | 开多 / 平空 | 卖一 | 基准 × (1+f) |
|
||||||
| 期权 | 买入 | 卖一 | 基准 × (1+f) |
|
| 永续 | 开空 / 平多 | 买一 | 基准 × (1-f) |
|
||||||
| 期权 | 卖出 | 买一 | 基准 × (1-f) |
|
| 期权 | 买入 | 卖一 | 基准 × (1+f) |
|
||||||
|
| 期权 | 卖出 | 买一 | 基准 × (1-f) |
|
||||||
另扣:`手续费 = 名义 × f`。
|
|
||||||
|
另扣:`手续费 = 名义 × f`。
|
||||||
### 2.3 `backend/app/strategy/` — 自动对冲
|
|
||||||
|
### 2.3 `backend/app/strategy/` — 自动对冲
|
||||||
```text
|
|
||||||
strategy/
|
```text
|
||||||
├── clock.py # 业务窗:D 16:00~D+1 08:00;次数≤3
|
strategy/
|
||||||
├── signal.py # Call 卖一 vs Put 卖一 → 方向
|
├── clock.py # 业务窗:D 16:00~D+1 08:00;次数≤3
|
||||||
├── exits.py # 权利金覆盖 / 30 点
|
├── signal.py # Call 卖一 vs Put 卖一 → 方向
|
||||||
├── sizing.py # 永续 1 ETH、期权 2 ETH(写死)
|
├── exits.py # 权利金覆盖 / 30 点
|
||||||
├── group.py # 组 ID:G-YYYYMMDD-NN
|
├── sizing.py # 永续 1 ETH、期权 2 ETH(写死)
|
||||||
└── engine.py # 状态机:空仓→开仓→盯盘→全平→下一组
|
├── group.py # 组 ID:G-YYYYMMDD-NN
|
||||||
```
|
└── engine.py # 状态机:空仓→开仓→盯盘→全平→下一组
|
||||||
|
```
|
||||||
状态机要点:
|
|
||||||
|
状态机要点:
|
||||||
- 同时最多 1 组。
|
|
||||||
- 平完才允许下一组;达 3 次或过 08:00 则停开。
|
- 同时最多 1 组。
|
||||||
- 每组锁定 `initial_premium`。
|
- 平完才允许下一组;达 3 次或过 08:00 则停开。
|
||||||
|
- 每组锁定 `initial_premium`。
|
||||||
### 2.4 `backend/app/live/` — 实盘(P5)
|
|
||||||
|
### 2.4 `backend/app/live/` — 实盘(P5)
|
||||||
```text
|
|
||||||
live/
|
```text
|
||||||
├── okx_trade.py # 永续市价单;期权吃一对应下单
|
live/
|
||||||
└── adapter.py # 与 sim.matcher 相同接口,便于切换
|
├── okx_trade.py # 永续市价单;期权吃一对应下单
|
||||||
```
|
└── adapter.py # 与 sim.matcher 相同接口,便于切换
|
||||||
|
```
|
||||||
默认关闭;仅 `MODE=LIVE` 且设置页确认后启用。
|
|
||||||
|
默认关闭;仅 `MODE=LIVE` 且设置页确认后启用。
|
||||||
### 2.5 `backend/app/api/` + `ws/`
|
|
||||||
|
### 2.5 `backend/app/api/` + `ws/`
|
||||||
```text
|
|
||||||
api/
|
```text
|
||||||
├── plan.py # 当前组、策略启停、紧急全平
|
api/
|
||||||
├── trades.py # 组列表、组成交明细
|
├── plan.py # 当前组、策略启停、紧急全平
|
||||||
├── stats.py # 汇总统计
|
├── trades.py # 组列表、组成交明细
|
||||||
└── settings.py # 读写配置(脱敏)
|
├── stats.py # 汇总统计
|
||||||
|
└── settings.py # 读写配置(脱敏)
|
||||||
ws/
|
|
||||||
└── push.py # market.snapshot / group.updated / fill.created / strategy.state
|
ws/
|
||||||
```
|
└── push.py # market.snapshot / group.updated / fill.created / strategy.state
|
||||||
|
```
|
||||||
### 2.6 `backend/app/models/` — 数据
|
|
||||||
|
### 2.6 `backend/app/models/` — 数据
|
||||||
建议表:
|
|
||||||
|
建议表:
|
||||||
| 表 | 用途 |
|
|
||||||
|----|------|
|
| 表 | 用途 |
|
||||||
| `groups` | 组头:方向、开平时间、初始权利金、平仓原因、盈亏 |
|
|----|------|
|
||||||
| `fills` | 成交:腿、价、量、滑点、手续费 |
|
| `groups` | 组头:方向、开平时间、初始权利金、平仓原因、盈亏 |
|
||||||
| `positions` | 当前仓(最多一组两腿) |
|
| `fills` | 成交:腿、价、量、滑点、手续费 |
|
||||||
| `ledger_entries` | 资金流水 |
|
| `positions` | 当前仓(最多一组两腿) |
|
||||||
| `settings` | 配置 KV |
|
| `ledger_entries` | 资金流水 |
|
||||||
| `market_ticks` | 可选行情快照 |
|
| `settings` | 配置 KV |
|
||||||
|
| `market_ticks` | 可选行情快照 |
|
||||||
---
|
|
||||||
|
---
|
||||||
## 3. 前端结构
|
|
||||||
|
## 3. 前端结构
|
||||||
```text
|
|
||||||
frontend/src/pages/
|
```text
|
||||||
├── Plan.tsx # 自动对冲计划(主盘)
|
frontend/src/pages/
|
||||||
├── Trades.tsx # 交易记录(按组)
|
├── Plan.tsx # 自动对冲计划(主盘)
|
||||||
├── Stats.tsx # 统计
|
├── Trades.tsx # 交易记录(按组)
|
||||||
└── Settings.tsx # 系统设置
|
├── Stats.tsx # 统计
|
||||||
|
└── Settings.tsx # 系统设置
|
||||||
frontend/src/components/
|
|
||||||
├── Nav.tsx # 四项导航
|
frontend/src/components/
|
||||||
├── GroupBadge.tsx # 组 ID + 状态色
|
├── Nav.tsx # 四项导航
|
||||||
├── PerpOrderBook.tsx # 永续盘口
|
├── GroupBadge.tsx # 组 ID + 状态色
|
||||||
├── OptionOrderBook.tsx # Call/Put;卖一选向高亮、买一平仓
|
├── PerpOrderBook.tsx # 永续盘口
|
||||||
├── PositionBar.tsx # 1 ETH + 2 ETH、浮盈、触发进度
|
├── OptionOrderBook.tsx # Call/Put;卖一选向高亮、买一平仓
|
||||||
└── EquityChart.tsx # 统计页曲线
|
├── PositionBar.tsx # 1 ETH + 2 ETH、浮盈、触发进度
|
||||||
```
|
└── EquityChart.tsx # 统计页曲线
|
||||||
|
```
|
||||||
导航文案固定:
|
|
||||||
|
导航文案固定:
|
||||||
1. 自动对冲计划
|
|
||||||
2. 交易记录
|
1. 自动对冲计划
|
||||||
3. 统计
|
2. 交易记录
|
||||||
4. 系统设置
|
3. 统计
|
||||||
|
4. 系统设置
|
||||||
---
|
|
||||||
|
---
|
||||||
## 4. 部署相关文件
|
|
||||||
|
## 4. 部署相关文件
|
||||||
### 4.1 `deploy/ecosystem.config.cjs`(示意)
|
|
||||||
|
### 4.1 `deploy/ecosystem.config.cjs`(示意)
|
||||||
```js
|
|
||||||
module.exports = {
|
```js
|
||||||
apps: [
|
module.exports = {
|
||||||
{
|
apps: [
|
||||||
name: 'eth-hedge-api',
|
{
|
||||||
cwd: '/opt/eth_hedge_sim/backend',
|
name: 'eth-hedge-api',
|
||||||
script: 'uvicorn',
|
cwd: '/opt/eth_hedge_sim/backend',
|
||||||
args: 'app.main:app --host 0.0.0.0 --port 8100',
|
script: 'uvicorn',
|
||||||
interpreter: 'python3',
|
args: 'app.main:app --host 0.0.0.0 --port 8100',
|
||||||
env: { MODE: 'SIM' },
|
interpreter: 'python3',
|
||||||
},
|
env: { MODE: 'SIM' },
|
||||||
// 若前端独立静态服务可再加 eth-hedge-web;也可由 nginx 指到 frontend/dist
|
},
|
||||||
],
|
// 若前端独立静态服务可再加 eth-hedge-web;也可由 nginx 指到 frontend/dist
|
||||||
};
|
],
|
||||||
```
|
};
|
||||||
|
```
|
||||||
### 4.2 `deploy/pull_and_restart.sh`(原则)
|
|
||||||
|
### 4.2 `deploy/pull_and_restart.sh`(原则)
|
||||||
- 仅 `cd /opt/eth_hedge_sim && git pull`
|
|
||||||
- 安装依赖 / `frontend` build
|
- 仅 `cd /opt/eth_hedge_sim && git pull`
|
||||||
- `pm2 startOrReload deploy/ecosystem.config.cjs --update-env`
|
- 安装依赖 / `frontend` build
|
||||||
- **禁止** `pm2 restart all`,**禁止** 调用现网 `crypto_monitor` 的部署脚本
|
- `pm2 startOrReload deploy/ecosystem.config.cjs --update-env`
|
||||||
|
- **禁止** `pm2 restart all`,**禁止** 调用现网 `crypto_monitor` 的部署脚本
|
||||||
---
|
|
||||||
|
---
|
||||||
## 5. 进程与端口(建议,可改)
|
|
||||||
|
## 5. 进程与端口(建议,可改)
|
||||||
| 服务 | 端口 | PM2 名 |
|
|
||||||
|------|------|--------|
|
| 服务 | 端口 | PM2 名 |
|
||||||
| API + WS | `8100` | `eth-hedge-api` |
|
|------|------|--------|
|
||||||
| 前端(若独立) | `8101` 或 nginx 反代 | `eth-hedge-web` |
|
| API + WS | `8100` | `eth-hedge-api` |
|
||||||
|
| 前端(若独立) | `8101` 或 nginx 反代 | `eth-hedge-web` |
|
||||||
与现网端口、进程名全部错开。
|
|
||||||
|
与现网端口、进程名全部错开。
|
||||||
---
|
|
||||||
|
---
|
||||||
## 6. 配置与密钥
|
|
||||||
|
## 6. 配置与密钥
|
||||||
```text
|
|
||||||
.env.example # 提交到 git
|
```text
|
||||||
.env # 仅服务器 / 本机,gitignore
|
.env.example # 提交到 git
|
||||||
```
|
.env # 仅服务器 / 本机,gitignore
|
||||||
|
```
|
||||||
关键项:
|
|
||||||
|
关键项:
|
||||||
- `MODE=SIM|LIVE`
|
|
||||||
- `OKX_API_KEY / SECRET / PASSPHRASE`(SIM 阶段只读权限即可)
|
- `MODE=SIM|LIVE`
|
||||||
- `FEE_RATE`(滑点自动 = 1 × FEE_RATE)
|
- `OKX_API_KEY / SECRET / PASSPHRASE`(SIM 阶段只读权限即可)
|
||||||
- `INITIAL_EQUITY`
|
- `FEE_RATE`(滑点自动 = 1 × FEE_RATE)
|
||||||
- `MAX_ROUNDS=3`
|
- `INITIAL_EQUITY`
|
||||||
- `OPEN_HHMM=16:00` / `STOP_OPEN_HHMM=08:00`
|
- `MAX_ROUNDS=3`
|
||||||
- `TZ=Asia/Shanghai`
|
- `OPEN_HHMM=16:00` / `STOP_OPEN_HHMM=08:00`
|
||||||
|
- `TZ=Asia/Shanghai`
|
||||||
---
|
|
||||||
|
---
|
||||||
## 7. 从现网复制代码时的规则
|
|
||||||
|
## 7. 从现网复制代码时的规则
|
||||||
| 允许 | 禁止 |
|
|
||||||
|------|------|
|
| 允许 | 禁止 |
|
||||||
| 复制 OKX 行情订阅、签名、深度解析到本仓库 | 修改 `C:\Users\dekun\Desktop\crypto_monitor` 下任何文件 |
|
|------|------|
|
||||||
| 复制「买一流动性检查」思路后重写 | 把本项目塞进现网 monorepo 一起 PM2 |
|
| 复制 OKX 行情订阅、签名、深度解析到本仓库 | 修改 `C:\Users\dekun\Desktop\crypto_monitor` 下任何文件 |
|
||||||
| 新建本仓库的 `.env` | 默认使用现网交易 Key 做模拟(模拟不需要交易权限) |
|
| 复制「买一流动性检查」思路后重写 | 把本项目塞进现网 monorepo 一起 PM2 |
|
||||||
|
| 新建本仓库的 `.env` | 默认使用现网交易 Key 做模拟(模拟不需要交易权限) |
|
||||||
复制后在本仓库内自由修改;现网保持原样。
|
|
||||||
|
复制后在本仓库内自由修改;现网保持原样。
|
||||||
---
|
|
||||||
|
---
|
||||||
## 8. 建议落地顺序(与开发方案分期对应)
|
|
||||||
|
## 8. 建议落地顺序(与开发方案分期对应)
|
||||||
1. 建 git 远程 → clone 到本机该目录与服务器 `/opt/eth_hedge_sim`
|
|
||||||
2. P0:`market/` 通行情
|
1. 建 git 远程 → clone 到本机该目录与服务器 `/opt/eth_hedge_sim`
|
||||||
3. P1:`sim/` 手动开平
|
2. P0:`market/` 通行情
|
||||||
4. P2:`strategy/` 自动化
|
3. P1:`sim/` 手动开平
|
||||||
5. P3:`frontend/` 四页
|
4. P2:`strategy/` 自动化
|
||||||
6. `deploy/` PM2 上独立机
|
5. P3:`frontend/` 四页
|
||||||
7. P5:`live/` 实盘开关
|
6. `deploy/` PM2 上独立机
|
||||||
|
7. P5:`live/` 实盘开关
|
||||||
---
|
|
||||||
|
---
|
||||||
## 9. 当前仓库已有文件
|
|
||||||
|
## 9. 当前仓库已有文件
|
||||||
```text
|
|
||||||
eth_hedge_sim/
|
```text
|
||||||
└── docs/
|
eth_hedge_sim/
|
||||||
├── 开发方案.md
|
└── docs/
|
||||||
└── 代码结构.md
|
├── 开发方案.md
|
||||||
```
|
└── 代码结构.md
|
||||||
|
```
|
||||||
负责人创建远程仓库后,将本目录作为首批提交即可;业务代码按上表目录逐步添加。
|
|
||||||
|
负责人创建远程仓库后,将本目录作为首批提交即可;业务代码按上表目录逐步添加。
|
||||||
|
|||||||
+4
-2
@@ -1,5 +1,6 @@
|
|||||||
# eth_hedge_sim — 商业化与授权方案(初步)
|
# 比特骆驼自动化对冲系统 — 商业化与授权方案(初步)
|
||||||
|
|
||||||
|
> **产品名**:比特骆驼自动化对冲系统 · **工程**:`eth_hedge_sim`
|
||||||
> 状态:草案,指导后续开发,细节可迭代。
|
> 状态:草案,指导后续开发,细节可迭代。
|
||||||
> 商业模式结论:**客户侧部署 + 软件授权**(不做中心化多租户托管)。
|
> 商业模式结论:**客户侧部署 + 软件授权**(不做中心化多租户托管)。
|
||||||
> 关联文档:[开发方案](./开发方案.md)、[代码结构](./代码结构.md)
|
> 关联文档:[开发方案](./开发方案.md)、[代码结构](./代码结构.md)
|
||||||
@@ -12,6 +13,7 @@
|
|||||||
|
|
||||||
| 项 | 约定 |
|
| 项 | 约定 |
|
||||||
|----|------|
|
|----|------|
|
||||||
|
| 产品名 | **比特骆驼自动化对冲系统** |
|
||||||
| 产品形态 | 可独立部署的对冲交易系统(SIM + 可选实盘) |
|
| 产品形态 | 可独立部署的对冲交易系统(SIM + 可选实盘) |
|
||||||
| 交付方式 | 安装包 / 一键脚本 / Docker(后续择一为主) |
|
| 交付方式 | 安装包 / 一键脚本 / Docker(后续择一为主) |
|
||||||
| 收费方式 | **授权(License)**:按实例、按期限、按功能档 |
|
| 收费方式 | **授权(License)**:按实例、按期限、按功能档 |
|
||||||
@@ -30,7 +32,7 @@
|
|||||||
|
|
||||||
### 1.3 产品定位一句话
|
### 1.3 产品定位一句话
|
||||||
|
|
||||||
> 客户在自己的服务器上部署;API Key 不出客户环境;你提供软件、更新与授权。
|
> **比特骆驼自动化对冲系统**:客户在自己的服务器上部署;API Key 不出客户环境;你提供软件、更新与授权。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
# ETH 永续 + 期权对冲 · 实盘说明索引
|
# 比特骆驼自动化对冲系统 · 实盘说明索引
|
||||||
|
|
||||||
|
> 产品:**比特骆驼自动化对冲系统**
|
||||||
> 通用规则见 [策略说明](./策略说明.md)。**开平仓、资金币种、API 按交易所分开写**,请直接打开对应文档。
|
> 通用规则见 [策略说明](./策略说明.md)。**开平仓、资金币种、API 按交易所分开写**,请直接打开对应文档。
|
||||||
> 更新:2026-07-26
|
> 更新:2026-07-26
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# 币安实盘策略说明
|
# 币安实盘策略说明
|
||||||
|
|
||||||
> 专用于 **币安** LIVE。通用对冲逻辑见 [策略说明](./策略说明.md);OKX 见 [OKX实盘策略说明](./OKX实盘策略说明.md)。
|
> 产品:**比特骆驼自动化对冲系统** · 专用于 **币安** LIVE。通用对冲逻辑见 [策略说明](./策略说明.md);OKX 见 [OKX实盘策略说明](./OKX实盘策略说明.md)。
|
||||||
> **标准仓**:永续 **1 ETH** + 期权 **2 ETH** 名义(倍数 `k` 可缩放)。
|
> **标准仓**:永续 **1 ETH** + 期权 **2 ETH** 名义(倍数 `k` 可缩放)。
|
||||||
> 更新:2026-07-26
|
> 更新:2026-07-26
|
||||||
|
|
||||||
|
|||||||
+6
-3
@@ -1,5 +1,7 @@
|
|||||||
# eth_hedge_sim — 自动对冲模拟盘开发方案
|
# 比特骆驼自动化对冲系统 — 开发方案
|
||||||
|
|
||||||
|
> **产品名**:比特骆驼自动化对冲系统
|
||||||
|
> **工程名 / 目录**:`eth_hedge_sim`
|
||||||
> 独立项目,与现网 `crypto_monitor` / `zk.hyf2.cc` **无部署、无进程、无密钥共用关系**。
|
> 独立项目,与现网 `crypto_monitor` / `zk.hyf2.cc` **无部署、无进程、无密钥共用关系**。
|
||||||
> 可参考现有仓库的实现思路或复制片段到本仓库,但 **禁止修改** `crypto_monitor` 内任何文件。
|
> 可参考现有仓库的实现思路或复制片段到本仓库,但 **禁止修改** `crypto_monitor` 内任何文件。
|
||||||
> Git 仓库由负责人自行创建;本目录为本地工程骨架与文档。
|
> Git 仓库由负责人自行创建;本目录为本地工程骨架与文档。
|
||||||
@@ -10,7 +12,8 @@
|
|||||||
|
|
||||||
| 项 | 约定 |
|
| 项 | 约定 |
|
||||||
|----|------|
|
|----|------|
|
||||||
| 项目名 / 目录名 | `eth_hedge_sim` |
|
| 产品名 | **比特骆驼自动化对冲系统** |
|
||||||
|
| 工程名 / 目录名 | `eth_hedge_sim` |
|
||||||
| 本机路径 | `C:\Users\dekun\Desktop\新建文件夹\eth_hedge_sim` |
|
| 本机路径 | `C:\Users\dekun\Desktop\新建文件夹\eth_hedge_sim` |
|
||||||
| 云服务器 | **单独一台 Ubuntu**(或同机不同目录/不同 PM2 进程名,且与现网隔离) |
|
| 云服务器 | **单独一台 Ubuntu**(或同机不同目录/不同 PM2 进程名,且与现网隔离) |
|
||||||
| 部署 | PM2 |
|
| 部署 | PM2 |
|
||||||
@@ -218,4 +221,4 @@ TZ=Asia/Shanghai
|
|||||||
|
|
||||||
## 9. 一句话
|
## 9. 一句话
|
||||||
|
|
||||||
**独立仓 `eth_hedge_sim`:OKX 真行情只读 + 本地虚拟资金撮合(永续市价、期权只买吃买卖一、滑点=1×手续费)+ OKX 风四页前端 + Ubuntu/PM2 单独部署;可抄现网思路,但不改现网代码、不共用现网进程。**
|
**独立仓 `eth_hedge_sim`(产品:比特骆驼自动化对冲系统):OKX/币安真行情只读 + 本地虚拟资金撮合(永续市价、期权只买吃买卖一、滑点=1×手续费)+ 四页前端 + Ubuntu/PM2 单独部署;可抄现网思路,但不改现网代码、不共用现网进程。**
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
# ETH 永续 + 期权对冲策略说明
|
# 比特骆驼自动化对冲系统 — 策略说明
|
||||||
|
|
||||||
|
> 产品:**比特骆驼自动化对冲系统**(工程 `eth_hedge_sim`)
|
||||||
> 依据当前代码逻辑整理(SIM 默认真值参数)。
|
> 依据当前代码逻辑整理(SIM 默认真值参数)。
|
||||||
> 关联:[开发方案](./开发方案.md)、[商业化与授权方案](./商业化与授权方案.md)、[实盘索引](./实盘策略说明.md)、[OKX实盘](./OKX实盘策略说明.md)、[币安实盘](./币安实盘策略说明.md)
|
> 关联:[开发方案](./开发方案.md)、[商业化与授权方案](./商业化与授权方案.md)、[实盘索引](./实盘策略说明.md)、[OKX实盘](./OKX实盘策略说明.md)、[币安实盘](./币安实盘策略说明.md)
|
||||||
> 更新:2026-07-26
|
> 更新:2026-07-26
|
||||||
|
|||||||
+4
-4
@@ -11,16 +11,16 @@
|
|||||||
<meta name="mobile-web-app-capable" content="yes" />
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="对冲系统" />
|
<meta name="apple-mobile-web-app-title" content="比特骆驼" />
|
||||||
<meta name="application-name" content="对冲系统" />
|
<meta name="application-name" content="比特骆驼" />
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="ETH 永续+期权对冲监控与策略控制台,支持电脑与平板安装为独立应用。"
|
content="比特骆驼自动化对冲系统:ETH 永续+期权对冲监控与策略控制台,支持电脑与平板安装为独立应用。"
|
||||||
/>
|
/>
|
||||||
<link rel="manifest" href="/manifest.webmanifest" />
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32.png" />
|
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32.png" />
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png" />
|
||||||
<title>OKX 自动化对冲系统</title>
|
<title>比特骆驼自动化对冲系统</title>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link
|
<link
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "eth-hedge-sim-web",
|
"name": "bitcamel-hedge-web",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
|
"description": "比特骆驼自动化对冲系统 Web",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "OKX 自动化对冲系统",
|
"name": "比特骆驼自动化对冲系统",
|
||||||
"short_name": "对冲系统",
|
"short_name": "比特骆驼",
|
||||||
"description": "ETH 永续+期权对冲监控与策略控制台",
|
"description": "比特骆驼自动化对冲系统:ETH 永续+期权对冲监控与策略控制台",
|
||||||
"start_url": "/plan",
|
"start_url": "/plan",
|
||||||
"scope": "/",
|
"scope": "/",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ function Shell({ children }: { children: ReactNode }) {
|
|||||||
<div className="app-shell">
|
<div className="app-shell">
|
||||||
<header className="topnav">
|
<header className="topnav">
|
||||||
<div className="topnav-side left">
|
<div className="topnav-side left">
|
||||||
<div className="brand">OKX 自动化对冲系统</div>
|
<div className="brand">比特骆驼自动化对冲系统</div>
|
||||||
<InstallBadge />
|
<InstallBadge />
|
||||||
</div>
|
</div>
|
||||||
<nav className="topnav-center">
|
<nav className="topnav-center">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import InstallBadge from "../components/InstallBadge";
|
|||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
const nav = useNavigate();
|
const nav = useNavigate();
|
||||||
const [username, setUsername] = useState("admin");
|
const [username, setUsername] = useState("");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [err, setErr] = useState("");
|
const [err, setErr] = useState("");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -29,10 +29,9 @@ export default function LoginPage() {
|
|||||||
<div className="login-wrap">
|
<div className="login-wrap">
|
||||||
<form className="login-box" onSubmit={onSubmit}>
|
<form className="login-box" onSubmit={onSubmit}>
|
||||||
<div className="login-title-row">
|
<div className="login-title-row">
|
||||||
<h1>OKX 自动化对冲系统</h1>
|
<h1>比特骆驼自动化对冲系统</h1>
|
||||||
<InstallBadge />
|
<InstallBadge />
|
||||||
</div>
|
</div>
|
||||||
<p>模拟盘登录 · 支持电脑/平板安装为应用</p>
|
|
||||||
{err ? <div className="err">{err}</div> : null}
|
{err ? <div className="err">{err}</div> : null}
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<label htmlFor="user">用户名</label>
|
<label htmlFor="user">用户名</label>
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ input {
|
|||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
.topnav-side.left .brand {
|
.topnav-side.left .brand {
|
||||||
max-width: 9em;
|
max-width: 11em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,7 +400,8 @@ input {
|
|||||||
|
|
||||||
.login-box h1 {
|
.login-box h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 22px;
|
font-size: 20px;
|
||||||
|
line-height: 1.35;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-title-row {
|
.login-title-row {
|
||||||
@@ -408,7 +409,7 @@ input {
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-box p {
|
.login-box p {
|
||||||
@@ -417,6 +418,10 @@ input {
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-box .err {
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.field {
|
.field {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
|||||||
Reference in New Issue
Block a user