附录B 核心策略Python代码片段

192页 · 预计6

使用说明

本附录提供了壹信量化套利策略的核心Python代码片段,涵盖期现套利、资金费率套利、跨所套利、配对统计套利、网格套利五大核心策略。所有代码均为简化版,重点展示策略逻辑和API调用方式,适合作为策略开发的参考模板。

环境要求

  • Python 3.8+
  • 依赖库:requests, pandas, numpy, ccxt, python-dotenv
  • 安装命令:pip install requests pandas numpy ccxt python-dotenv

壹信量化API说明

壹信量化平台提供两类API:

  1. 数据API(a-sig.com):用于获取行情数据、套利指标、回测数据等
    • 基础URL:https://api.a-sig.com/v1
    • 认证方式:API Key(请求头 X-API-Key
  2. 交易API(intoquant.com):用于策略部署、订单管理、账户管理等
    • 基础URL:https://api.intoquant.com/v1
    • 认证方式:API Key + Secret(HMAC签名)

注意:本代码中的API端点和参数为示例,实际使用时请参考壹信量化平台的最新API文档。你可以在 a-sig.com 和 intoquant.com 的开发者中心获取完整的API文档和密钥。

代码结构

每个策略代码包含以下模块:

  1. 配置和初始化
  2. 数据获取
  3. 信号计算
  4. 下单执行
  5. 风控检查
  6. 主循环

所有代码都有详细的中文注释,便于理解和修改。

免责声明

本代码仅供学习和参考,不构成投资建议。实盘交易前请充分回测和模拟盘验证,并自行承担交易风险。


一、通用工具模块

在具体策略之前,先提供一个通用的工具模块,包含API请求封装、指标计算、日志记录等通用功能,后续策略都会引用这个模块。

"""
文件:yixin_common.py
功能:壹信量化通用工具模块
"""

import time
import hmac
import hashlib
import requests
import pandas as pd
import numpy as np
from datetime import datetime
from dotenv import load_dotenv
import os

# 加载环境变量(API密钥等敏感信息放在.env文件中,不要硬编码在代码里)
load_dotenv()

# ==================== 配置 ====================
A_SIG_BASE_URL = "https://api.a-sig.com/v1"      # 壹信数据API基础URL
INTOQUANT_BASE_URL = "https://api.intoquant.com/v1"  # 壹信交易API基础URL
A_SIG_API_KEY = os.getenv("A_SIG_API_KEY", "")    # 壹信数据API密钥
INTOQUANT_API_KEY = os.getenv("INTOQUANT_API_KEY", "")  # 壹信交易API Key
INTOQUANT_API_SECRET = os.getenv("INTOQUANT_API_SECRET", "")  # 壹信交易API Secret

# ==================== 日志工具 ====================
def log(level, message):
    """
    简单的日志函数,打印带时间戳和级别的日志
    实际项目中建议使用logging模块,写入文件并做日志轮转
    """
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"[{timestamp}] [{level}] {message}")

# ==================== API请求工具 ====================
def a_sig_request(endpoint, params=None):
    """
    向壹信数据API(a-sig.com)发送GET请求
    :param endpoint: API端点,如 "/market/klines"
    :param params: 请求参数字典
    :return: 返回JSON数据
    """
    url = f"{A_SIG_BASE_URL}{endpoint}"
    headers = {
        "X-API-Key": A_SIG_API_KEY,
        "Content-Type": "application/json"
    }
    try:
        response = requests.get(url, headers=headers, params=params, timeout=10)
        response.raise_for_status()  # 抛出HTTP错误
        return response.json()
    except requests.exceptions.RequestException as e:
        log("ERROR", f"a-sig API请求失败: {endpoint}, 错误: {e}")
        return None

def intoquant_sign(params, secret):
    """
    生成壹信交易API的HMAC-SHA256签名
    :param params: 请求参数字典(包含timestamp)
    :param secret: API Secret
    :return: 签名字符串
    """
    # 将参数按key排序,拼接成查询字符串
    sorted_params = sorted(params.items())
    query_string = "&".join([f"{k}={v}" for k, v in sorted_params])
    # HMAC-SHA256签名
    signature = hmac.new(
        secret.encode(),
        query_string.encode(),
        hashlib.sha256
    ).hexdigest()
    return signature

def intoquant_request(method, endpoint, params=None):
    """
    向壹信交易API(intoquant.com)发送签名请求
    :param method: "GET" 或 "POST"
    :param endpoint: API端点,如 "/order/create"
    :param params: 请求参数字典
    :return: 返回JSON数据
    """
    url = f"{INTOQUANT_BASE_URL}{endpoint}"
    timestamp = int(time.time() * 1000)  # 毫秒时间戳
    
    # 构造参数字典,加入时间戳
    if params is None:
        params = {}
    params["timestamp"] = timestamp
    
    # 生成签名
    signature = intoquant_sign(params, INTOQUANT_API_SECRET)
    params["sign"] = signature
    
    headers = {
        "X-API-Key": INTOQUANT_API_KEY,
        "Content-Type": "application/json"
    }
    
    try:
        if method.upper() == "GET":
            response = requests.get(url, headers=headers, params=params, timeout=10)
        else:
            response = requests.post(url, headers=headers, json=params, timeout=10)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        log("ERROR", f"intoquant API请求失败: {method} {endpoint}, 错误: {e}")
        return None

# ==================== 技术指标计算工具 ====================
def calculate_sma(series, window):
    """
    计算简单移动平均线(SMA)
    :param series: 价格序列(pandas.Series)
    :param window: 窗口期
    :return: SMA序列
    """
    return series.rolling(window=window).mean()

def calculate_std(series, window):
    """
    计算滚动标准差
    :param series: 价格序列
    :param window: 窗口期
    :return: 标准差序列
    """
    return series.rolling(window=window).std()

def calculate_zscore(series, window):
    """
    计算滚动Z-Score
    :param series: 数据序列(如价差)
    :param window: 滚动窗口
    :return: Z-Score序列
    """
    mean = series.rolling(window=window).mean()
    std = series.rolling(window=window).std()
    zscore = (series - mean) / std
    return zscore

def calculate_atr(high, low, close, window=14):
    """
    计算平均真实波幅(ATR)
    :param high: 最高价序列
    :param low: 最低价序列
    :param close: 收盘价序列
    :param window: 窗口期
    :return: ATR序列
    """
    # 计算真实波幅TR
    tr1 = high - low
    tr2 = abs(high - close.shift(1))
    tr3 = abs(low - close.shift(1))
    tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
    # 计算ATR(简单移动平均)
    atr = tr.rolling(window=window).mean()
    return atr

def calculate_rsi(close, window=14):
    """
    计算相对强弱指数(RSI)
    :param close: 收盘价序列
    :param window: 窗口期
    :return: RSI序列
    """
    delta = close.diff()
    gain = delta.where(delta > 0, 0)
    loss = (-delta).where(delta < 0, 0)
    avg_gain = gain.rolling(window=window).mean()
    avg_loss = loss.rolling(window=window).mean()
    rs = avg_gain / avg_loss
    rsi = 100 - (100 / (1 + rs))
    return rsi

# ==================== 风控工具 ====================
def check_drawdown(current_value, peak_value, max_drawdown_limit):
    """
    检查当前回撤是否超过限制
    :param current_value: 当前资产总值
    :param peak_value: 历史最高资产总值
    :param max_drawdown_limit: 最大回撤限制(如0.1表示10%)
    :return: (是否超限, 当前回撤)
    """
    if peak_value == 0:
        return False, 0
    drawdown = (peak_value - current_value) / peak_value
    is_exceeded = drawdown > max_drawdown_limit
    return is_exceeded, drawdown

def check_daily_loss(today_start_value, current_value, daily_loss_limit):
    """
    检查当日亏损是否超过限制
    :param today_start_value: 今日初始资产
    :param current_value: 当前资产
    :param daily_loss_limit: 单日亏损限制(如0.03表示3%)
    :return: (是否超限, 当日亏损率)
    """
    if today_start_value == 0:
        return False, 0
    daily_loss = (today_start_value - current_value) / today_start_value
    is_exceeded = daily_loss > daily_loss_limit
    return is_exceeded, daily_loss

# ==================== 数据获取工具 ====================
def get_klines(symbol, interval, limit=100):
    """
    从壹信数据API获取K线数据
    :param symbol: 交易对,如 "BTC/USDT"
    :param interval: K线周期,如 "1m", "5m", "1h", "1d"
    :param limit: 获取数量
    :return: DataFrame,包含 open, high, low, close, volume
    """
    params = {
        "symbol": symbol,
        "interval": interval,
        "limit": limit
    }
    data = a_sig_request("/market/klines", params)
    if data and "data" in data:
        df = pd.DataFrame(data["data"])
        # 确保列名和数据类型正确
        df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms")
        for col in ["open", "high", "low", "close", "volume"]:
            df[col] = df[col].astype(float)
        df.set_index("timestamp", inplace=True)
        return df
    else:
        log("ERROR", f"获取K线数据失败: {symbol} {interval}")
        return None

def get_funding_rate(symbol, exchange="binance"):
    """
    获取当前资金费率
    :param symbol: 交易对,如 "BTC/USDT"
    :param exchange: 交易所,如 "binance", "okx"
    :return: 当前资金费率(如0.0001表示0.01%)
    """
    params = {
        "symbol": symbol,
        "exchange": exchange
    }
    data = a_sig_request("/futures/funding-rate", params)
    if data and "data" in data:
        return float(data["data"]["fundingRate"])
    else:
        log("ERROR", f"获取资金费率失败: {symbol} {exchange}")
        return None

def get_spread(symbol, exchange_a, exchange_b):
    """
    获取两个交易所之间的价差
    :param symbol: 交易对
    :param exchange_a: 交易所A
    :param exchange_b: 交易所B
    :return: 价差(绝对)和价差率
    """
    params = {
        "symbol": symbol,
        "exchange_a": exchange_a,
        "exchange_b": exchange_b
    }
    data = a_sig_request("/arbitrage/spread", params)
    if data and "data" in data:
        return float(data["data"]["spread"]), float(data["data"]["spreadRate"])
    else:
        log("ERROR", f"获取价差失败: {symbol} {exchange_a}-{exchange_b}")
        return None, None

二、期现套利策略

期现套利是最基础、最稳健的套利策略。当期货价格与现货价格的基差扩大到一定程度时,做多现货+做空期货,等基差收敛后平仓获利。

"""
文件:strategy_cash_futures_arbitrage.py
功能:BTC期现套利策略(简化版)
策略逻辑:
  1. 实时计算BTC现货和当季期货的基差率
  2. 当基差率 > 开仓阈值(如1.5%)时,做多现货+做空期货
  3. 当基差率 < 平仓阈值(如0.3%)时,平仓获利
  4. 严格的风控:最大回撤、单日亏损、仓位限制
"""

import time
import pandas as pd
from yixin_common import (
    log, get_klines, intoquant_request,
    check_drawdown, check_daily_loss, calculate_sma
)

# ==================== 策略参数 ====================
SYMBOL = "BTC/USDT"           # 交易对
SPOT_EXCHANGE = "binance"      # 现货交易所
FUTURES_EXCHANGE = "binance"   # 期货交易所
FUTURES_TYPE = "quarterly"     # 期货类型:quarterly(当季), perpetual(永续)

BASIS_OPEN_THRESHOLD = 0.015   # 基差率开仓阈值:1.5%
BASIS_CLOSE_THRESHOLD = 0.003  # 基差率平仓阈值:0.3%
POSITION_SIZE_RATIO = 0.8      # 仓位比例:使用80%的可用资金
MAX_DRAWDOWN_LIMIT = 0.08      # 最大回撤限制:8%
DAILY_LOSS_LIMIT = 0.03        # 单日亏损限制:3%
CHECK_INTERVAL = 60             # 检查间隔:60秒

# ==================== 策略状态 ====================
class StrategyState:
    def __init__(self):
        self.position = 0           # 当前持仓状态:0=空仓, 1=持有期现头寸
        self.spot_qty = 0           # 现货持仓数量
        self.futures_qty = 0        # 期货持仓数量
        self.entry_basis = 0        # 开仓时的基差率
        self.peak_value = 0         # 历史最高资产总值
        self.today_start_value = 0  # 今日初始资产
        self.last_check_date = None # 上次检查日期(用于重置今日初始值)

state = StrategyState()

# ==================== 核心函数 ====================
def get_account_value():
    """
    获取账户总资产(现货+期货权益合计)
    """
    result = intoquant_request("GET", "/account/total-value")
    if result and result.get("code") == 0:
        return float(result["data"]["totalValue"])
    else:
        log("ERROR", "获取账户总资产失败")
        return None

def get_current_basis():
    """
    获取当前基差率
    基差率 = (期货价格 - 现货价格) / 现货价格
    """
    # 获取现货价格
    spot_data = intoquant_request("GET", "/market/ticker", 
                                   {"symbol": SYMBOL, "exchange": SPOT_EXCHANGE, "type": "spot"})
    # 获取期货价格
    futures_data = intoquant_request("GET", "/market/ticker",
                                      {"symbol": SYMBOL, "exchange": FUTURES_EXCHANGE, "type": "futures", "futuresType": FUTURES_TYPE})
    
    if spot_data and futures_data and spot_data.get("code") == 0 and futures_data.get("code") == 0:
        spot_price = float(spot_data["data"]["last"])
        futures_price = float(futures_data["data"]["last"])
        basis_rate = (futures_price - spot_price) / spot_price
        return basis_rate, spot_price, futures_price
    else:
        log("ERROR", "获取当前价格失败")
        return None, None, None

def open_position(basis_rate, spot_price, futures_price, account_value):
    """
    开仓:做多现货 + 做空期货
    """
    log("INFO", f"触发开仓信号,当前基差率: {basis_rate:.4%},开始开仓...")
    
    # 计算仓位金额
    position_value = account_value * POSITION_SIZE_RATIO
    # 现货买入数量(用一半资金买现货)
    spot_budget = position_value / 2
    spot_qty = spot_budget / spot_price
    # 期货做空数量(跟现货数量对应,1倍杠杆)
    futures_qty = spot_qty
    
    # 买入现货
    spot_order = intoquant_request("POST", "/order/create", {
        "exchange": SPOT_EXCHANGE,
        "symbol": SYMBOL,
        "type": "spot",
        "side": "buy",
        "orderType": "market",  # 市价单,实际建议用限价单降低滑点
        "quantity": round(spot_qty, 6)
    })
    
    # 做空期货
    futures_order = intoquant_request("POST", "/order/create", {
        "exchange": FUTURES_EXCHANGE,
        "symbol": SYMBOL,
        "type": "futures",
        "futuresType": FUTURES_TYPE,
        "side": "sell",
        "orderType": "market",
        "quantity": round(futures_qty, 6),
        "leverage": 1  # 1倍杠杆
    })
    
    if spot_order and futures_order and spot_order.get("code") == 0 and futures_order.get("code") == 0:
        log("INFO", f"开仓成功!现货买入: {spot_qty:.6f} BTC,期货做空: {futures_qty:.6f} BTC")
        state.position = 1
        state.spot_qty = spot_qty
        state.futures_qty = futures_qty
        state.entry_basis = basis_rate
        return True
    else:
        log("ERROR", "开仓失败,请检查订单状态")
        return False

def close_position(basis_rate):
    """
    平仓:卖出现货 + 平掉期货空头
    """
    log("INFO", f"触发平仓信号,当前基差率: {basis_rate:.4%},开仓基差率: {state.entry_basis:.4%},开始平仓...")
    
    # 卖出现货
    spot_order = intoquant_request("POST", "/order/create", {
        "exchange": SPOT_EXCHANGE,
        "symbol": SYMBOL,
        "type": "spot",
        "side": "sell",
        "orderType": "market",
        "quantity": round(state.spot_qty, 6)
    })
    
    # 平期货空头(买入平仓)
    futures_order = intoquant_request("POST", "/order/create", {
        "exchange": FUTURES_EXCHANGE,
        "symbol": SYMBOL,
        "type": "futures",
        "futuresType": FUTURES_TYPE,
        "side": "buy",
        "orderType": "market",
        "quantity": round(state.futures_qty, 6),
        "reduceOnly": True  # 只减仓
    })
    
    if spot_order and futures_order and spot_order.get("code") == 0 and futures_order.get("code") == 0:
        profit = (state.entry_basis - basis_rate) * state.spot_qty * 100  # 粗略估算收益
        log("INFO", f"平仓成功!本次套利预估收益: {profit:.2f} USDT")
        state.position = 0
        state.spot_qty = 0
        state.futures_qty = 0
        state.entry_basis = 0
        return True
    else:
        log("ERROR", "平仓失败,请检查订单状态")
        return False

def risk_check(account_value):
    """
    风控检查:最大回撤、单日亏损
    返回True表示可以继续交易,False表示需要暂停交易
    """
    # 更新历史最高值
    if account_value > state.peak_value:
        state.peak_value = account_value
    
    # 检查最大回撤
    dd_exceeded, drawdown = check_drawdown(account_value, state.peak_value, MAX_DRAWDOWN_LIMIT)
    if dd_exceeded:
        log("WARNING", f"最大回撤超限!当前回撤: {drawdown:.2%},限制: {MAX_DRAWDOWN_LIMIT:.2%},暂停开仓")
        if state.position == 1:
            log("WARNING", "当前有持仓,触发回撤止损,强制平仓")
            basis_rate, _, _ = get_current_basis()
            if basis_rate is not None:
                close_position(basis_rate)
        return False
    
    # 检查单日亏损
    today = time.strftime("%Y-%m-%d")
    if state.last_check_date != today:
        state.today_start_value = account_value
        state.last_check_date = today
    
    loss_exceeded, daily_loss = check_daily_loss(state.today_start_value, account_value, DAILY_LOSS_LIMIT)
    if loss_exceeded:
        log("WARNING", f"单日亏损超限!当日亏损: {daily_loss:.2%},限制: {DAILY_LOSS_LIMIT:.2%},今日暂停交易")
        if state.position == 1:
            log("WARNING", "当前有持仓,触发单日亏损止损,强制平仓")
            basis_rate, _, _ = get_current_basis()
            if basis_rate is not None:
                close_position(basis_rate)
        return False
    
    return True

# ==================== 主循环 ====================
def main():
    log("INFO", "=" * 50)
    log("INFO", "BTC期现套利策略启动")
    log("INFO", f"开仓基差阈值: {BASIS_OPEN_THRESHOLD:.2%}")
    log("INFO", f"平仓基差阈值: {BASIS_CLOSE_THRESHOLD:.2%}")
    log("INFO", "=" * 50)
    
    while True:
        try:
            # 1. 获取账户资产
            account_value = get_account_value()
            if account_value is None:
                time.sleep(CHECK_INTERVAL)
                continue
            
            # 2. 风控检查
            if not risk_check(account_value):
                time.sleep(CHECK_INTERVAL)
                continue
            
            # 3. 获取当前基差
            basis_rate, spot_price, futures_price = get_current_basis()
            if basis_rate is None:
                time.sleep(CHECK_INTERVAL)
                continue
            
            log("INFO", f"当前基差率: {basis_rate:.4%},持仓状态: {state.position}")
            
            # 4. 信号判断和交易执行
            if state.position == 0:
                # 空仓状态:检查开仓信号
                if basis_rate > BASIS_OPEN_THRESHOLD:
                    open_position(basis_rate, spot_price, futures_price, account_value)
            else:
                # 持仓状态:检查平仓信号
                if basis_rate < BASIS_CLOSE_THRESHOLD:
                    close_position(basis_rate)
            
            # 5. 等待下一次检查
            time.sleep(CHECK_INTERVAL)
            
        except Exception as e:
            log("ERROR", f"主循环异常: {e}")
            time.sleep(CHECK_INTERVAL)

if __name__ == "__main__":
    main()

期现套利策略使用说明

  1. 参数调整:根据市场环境调整BASIS_OPEN_THRESHOLDBASIS_CLOSE_THRESHOLD。牛市中基差通常较高,可以把开仓阈值设高一些(如2%);熊市中基差较低甚至为负,可以设低一些(如0.8%)。建议用a-sig.com的基差历史数据统计合理的阈值。

  2. 下单方式:示例中用的是市价单,实盘中建议改用限价单(orderType: "limit",指定price参数),可以显著降低滑点。可以配合"限价单+超时撤销+市价单兜底"的智能下单算法。

  3. 期货类型:示例中用的是当季期货(quarterly),你也可以用永续合约(perpetual)。用永续合约时,需要考虑资金费率的影响——如果资金费率为正,做空期货可以额外收取资金费,增加收益;如果为负,则需要支付资金费,减少收益。

  4. 基差计算优化:示例中用最新成交价计算基差,实盘中建议用订单簿的中间价((买一价+卖一价)/2)计算基差,更准确地反映可成交的基差。可以调用/market/orderbook接口获取订单簿数据。

  5. 对冲比例:示例中用1:1的对冲比例(现货数量=期货数量),这是简化处理。实际上,因为期货有杠杆和保证金,更精确的对冲需要考虑delta对冲、保证金成本等。对于1倍杠杆的期现套利,1:1基本够用。


三、资金费率套利策略

资金费率套利利用永续合约的资金费率机制,当资金费率为正时,做空永续合约+做多现货,收取资金费;当资金费率为负时反向操作。

"""
文件:strategy_funding_rate_arbitrage.py
功能:资金费率套利策略(简化版)
策略逻辑:
  1. 实时监控永续合约的资金费率
  2. 当资金费率 > 正费率阈值(如0.05%每8小时)时,做多现货+做空永续,收取资金费
  3. 当资金费率 < 负费率阈值(如-0.05%每8小时)时,做空现货+做多永续,收取资金费
  4. 当资金费率回归中性区间时,平仓
  5. 同时监控价格波动风险,设置止损
"""

import time
from yixin_common import (
    log, get_funding_rate, intoquant_request,
    check_drawdown, check_daily_loss
)

# ==================== 策略参数 ====================
SYMBOL = "BTC/USDT"
EXCHANGE = "binance"

FUNDING_LONG_THRESHOLD = 0.0005   # 正费率开仓阈值:0.05%(每8小时),做空永续+做多现货
FUNDING_SHORT_THRESHOLD = -0.0005  # 负费率开仓阈值:-0.05%(每8小时),做多永续+做空现货
FUNDING_CLOSE_THRESHOLD = 0.0001   # 费率平仓阈值:绝对值<0.01%时平仓

POSITION_SIZE_RATIO = 0.7      # 仓位比例
MAX_DRAWDOWN_LIMIT = 0.10      # 最大回撤限制:10%
DAILY_LOSS_LIMIT = 0.04        # 单日亏损限制:4%
PRICE_STOP_LOSS = 0.05         # 价格波动止损:对冲头寸的价格偏离超过5%时止损
CHECK_INTERVAL = 300            # 检查间隔:5分钟(资金费率变化慢,不需要太频繁)

# ==================== 策略状态 ====================
class StrategyState:
    def __init__(self):
        self.position = 0           # 0=空仓, 1=正费率套利(多现货+空永续), -1=负费率套利(空现货+多永续)
        self.spot_qty = 0
        self.futures_qty = 0
        self.entry_funding = 0      # 开仓时的资金费率
        self.entry_spot_price = 0   # 开仓时的现货价格
        self.peak_value = 0
        self.today_start_value = 0
        self.last_check_date = None

state = StrategyState()

# ==================== 核心函数 ====================
def get_account_value():
    result = intoquant_request("GET", "/account/total-value")
    if result and result.get("code") == 0:
        return float(result["data"]["totalValue"])
    return None

def get_current_prices():
    """获取当前现货和永续价格"""
    spot = intoquant_request("GET", "/market/ticker", 
                              {"symbol": SYMBOL, "exchange": EXCHANGE, "type": "spot"})
    futures = intoquant_request("GET", "/market/ticker",
                                 {"symbol": SYMBOL, "exchange": EXCHANGE, "type": "futures", "futuresType": "perpetual"})
    if spot and futures and spot.get("code") == 0 and futures.get("code") == 0:
        return float(spot["data"]["last"]), float(futures["data"]["last"])
    return None, None

def open_position(funding_rate, direction, account_value):
    """
    开仓
    direction=1: 正费率套利 -> 做多现货 + 做空永续
    direction=-1: 负费率套利 -> 做空现货(或借币卖空) + 做多永续
    """
    log("INFO", f"触发开仓信号,资金费率: {funding_rate:.4%},方向: {'正费率套利' if direction==1 else '负费率套利'}")
    
    position_value = account_value * POSITION_SIZE_RATIO
    spot_price, futures_price = get_current_prices()
    if spot_price is None:
        return False
    
    qty = (position_value / 2) / spot_price  # 现货和期货各用一半资金
    
    if direction == 1:
        # 做多现货
        spot_order = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL, "type": "spot",
            "side": "buy", "orderType": "market", "quantity": round(qty, 6)
        })
        # 做空永续
        futures_order = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL, "type": "futures",
            "futuresType": "perpetual", "side": "sell", "orderType": "market",
            "quantity": round(qty, 6), "leverage": 1
        })
    else:
        # 负费率套利:做空现货(需要在现货市场借币,或用现货杠杆)
        # 简化处理:这里假设支持现货做空,实际需要确认交易所是否支持
        spot_order = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL, "type": "margin",
            "side": "sell", "orderType": "market", "quantity": round(qty, 6)
        })
        # 做多永续
        futures_order = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL, "type": "futures",
            "futuresType": "perpetual", "side": "buy", "orderType": "market",
            "quantity": round(qty, 6), "leverage": 1
        })
    
    if spot_order and futures_order and spot_order.get("code") == 0 and futures_order.get("code") == 0:
        log("INFO", f"开仓成功!数量: {qty:.6f} BTC")
        state.position = direction
        state.spot_qty = qty
        state.futures_qty = qty
        state.entry_funding = funding_rate
        state.entry_spot_price = spot_price
        return True
    return False

def close_position():
    """平仓"""
    log("INFO", f"触发平仓信号,当前持仓: {state.position},开始平仓...")
    
    spot_price, _ = get_current_prices()
    
    if state.position == 1:
        # 平正费率套利:卖出现货 + 平永续空头
        spot_order = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL, "type": "spot",
            "side": "sell", "orderType": "market", "quantity": round(state.spot_qty, 6)
        })
        futures_order = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL, "type": "futures",
            "futuresType": "perpetual", "side": "buy", "orderType": "market",
            "quantity": round(state.futures_qty, 6), "reduceOnly": True
        })
    else:
        # 平负费率套利:买入还券 + 平永续多头
        spot_order = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL, "type": "margin",
            "side": "buy", "orderType": "market", "quantity": round(state.spot_qty, 6)
        })
        futures_order = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL, "type": "futures",
            "futuresType": "perpetual", "side": "sell", "orderType": "market",
            "quantity": round(state.futures_qty, 6), "reduceOnly": True
        })
    
    if spot_order and futures_order and spot_order.get("code") == 0 and futures_order.get("code") == 0:
        log("INFO", "平仓成功!")
        state.position = 0
        state.spot_qty = 0
        state.futures_qty = 0
        return True
    return False

def risk_check(account_value):
    """风控检查"""
    if account_value > state.peak_value:
        state.peak_value = account_value
    
    dd_exceeded, drawdown = check_drawdown(account_value, state.peak_value, MAX_DRAWDOWN_LIMIT)
    if dd_exceeded:
        log("WARNING", f"最大回撤超限: {drawdown:.2%},强制平仓")
        if state.position != 0:
            close_position()
        return False
    
    today = time.strftime("%Y-%m-%d")
    if state.last_check_date != today:
        state.today_start_value = account_value
        state.last_check_date = today
    
    loss_exceeded, daily_loss = check_daily_loss(state.today_start_value, account_value, DAILY_LOSS_LIMIT)
    if loss_exceeded:
        log("WARNING", f"单日亏损超限: {daily_loss:.2%},今日暂停交易")
        if state.position != 0:
            close_position()
        return False
    
    # 价格波动止损:检查对冲头寸的价格偏离
    if state.position != 0:
        spot_price, futures_price = get_current_prices()
        if spot_price and state.entry_spot_price > 0:
            price_change = abs(spot_price - state.entry_spot_price) / state.entry_spot_price
            if price_change > PRICE_STOP_LOSS:
                log("WARNING", f"价格波动止损!现货价格变动: {price_change:.2%},超过阈值: {PRICE_STOP_LOSS:.2%}")
                close_position()
                return False
    
    return True

# ==================== 主循环 ====================
def main():
    log("INFO", "=" * 50)
    log("INFO", "资金费率套利策略启动")
    log("INFO", f"正费率开仓阈值: {FUNDING_LONG_THRESHOLD:.4%}")
    log("INFO", f"负费率开仓阈值: {FUNDING_SHORT_THRESHOLD:.4%}")
    log("INFO", "=" * 50)
    
    while True:
        try:
            account_value = get_account_value()
            if account_value is None:
                time.sleep(CHECK_INTERVAL)
                continue
            
            if not risk_check(account_value):
                time.sleep(CHECK_INTERVAL)
                continue
            
            # 获取当前资金费率
            funding_rate = get_funding_rate(SYMBOL, EXCHANGE)
            if funding_rate is None:
                time.sleep(CHECK_INTERVAL)
                continue
            
            log("INFO", f"当前资金费率: {funding_rate:.4%},持仓: {state.position}")
            
            if state.position == 0:
                # 空仓:检查开仓信号
                if funding_rate > FUNDING_LONG_THRESHOLD:
                    open_position(funding_rate, 1, account_value)
                elif funding_rate < FUNDING_SHORT_THRESHOLD:
                    open_position(funding_rate, -1, account_value)
            else:
                # 持仓:检查平仓信号
                if abs(funding_rate) < abs(FUNDING_CLOSE_THRESHOLD):
                    close_position()
            
            time.sleep(CHECK_INTERVAL)
            
        except Exception as e:
            log("ERROR", f"主循环异常: {e}")
            time.sleep(CHECK_INTERVAL)

if __name__ == "__main__":
    main()

资金费率套利策略使用说明

  1. 资金费率的预测:资金费率每8小时结算一次,但费率是提前计算的(通常在结算前1小时左右确定)。你可以在a-sig.com上获取"预测资金费率"(predicted funding rate),在结算前就布局,而不是等结算后才反应。

  2. 持仓时间:资金费率套利不需要长期持仓。通常在费率高的时候开仓,等费率回归中性后平仓,持仓时间从几小时到几天不等。不要为了等下一次结算而长期持有,因为价格波动风险会累积。

  3. 价格波动风险:虽然是对冲头寸,但现货和永续的价格不会完全同步,特别是在剧烈波动时,可能出现"基差扩大"导致对冲头寸浮亏。示例中设置了5%的价格波动止损,实盘中可以根据币种的波动率调整(BTC可以设3%-5%,山寨币需要设更高或避免交易)。

  4. 负费率套利的实现:负费率套利需要做空现货,这需要交易所支持现货杠杆(margin trading)或借币。不是所有交易所都支持,也不是所有币种都能借到。在开仓前要确认可借币数量和借币利率。如果借币利率太高(如年化>10%),可能会侵蚀资金费收益。

  5. 多币种布局:单一币种的资金费率机会有限,可以同时监控BTC、ETH、SOL等多个主流币种,哪个币种费率高就做哪个。a-sig.com提供了全市场资金费率排行榜,可以快速发现高费率机会。


四、跨所套利策略

跨所套利利用同一币种在不同交易所的价格差异,在低价交易所买入,同时在高价交易所卖出,等价差收敛后平仓。

"""
文件:strategy_cross_exchange_arbitrage.py
功能:跨交易所套利策略(简化版)
策略逻辑:
  1. 实时监控同一币种在两个交易所的价差
  2. 当价差率 > 开仓阈值(覆盖手续费+滑点+缓冲)时,在低价所买入,高价所卖出
  3. 当价差率 < 平仓阈值时,平仓获利
  4. 管理两个交易所的资金,确保两边都有足够的保证金
"""

import time
from yixin_common import (
    log, get_spread, intoquant_request,
    check_drawdown, check_daily_loss
)

# ==================== 策略参数 ====================
SYMBOL = "BTC/USDT"
EXCHANGE_A = "binance"     # 交易所A
EXCHANGE_B = "okx"         # 交易所B

SPREAD_OPEN_THRESHOLD = 0.004   # 价差率开仓阈值:0.4%(需要覆盖两边手续费约0.2%+滑点0.1%+缓冲0.1%)
SPREAD_CLOSE_THRESHOLD = 0.001  # 价差率平仓阈值:0.1%
POSITION_SIZE_RATIO = 0.6       # 仓位比例(跨所需要两边都有资金,所以比例低一些)
MAX_DRAWDOWN_LIMIT = 0.06       # 最大回撤:6%
DAILY_LOSS_LIMIT = 0.03         # 单日亏损:3%
CHECK_INTERVAL = 10              # 检查间隔:10秒(跨所价差变化快,需要高频监控)

# ==================== 策略状态 ====================
class StrategyState:
    def __init__(self):
        self.position = 0           # 0=空仓, 1=A买B卖, -1=B买A卖
        self.qty_a = 0              # 交易所A的持仓数量
        self.qty_b = 0              # 交易所B的持仓数量
        self.entry_spread = 0       # 开仓时的价差率
        self.peak_value = 0
        self.today_start_value = 0
        self.last_check_date = None

state = StrategyState()

# ==================== 核心函数 ====================
def get_total_account_value():
    """获取两个交易所的总资产合计"""
    total = 0
    for ex in [EXCHANGE_A, EXCHANGE_B]:
        result = intoquant_request("GET", "/account/total-value", {"exchange": ex})
        if result and result.get("code") == 0:
            total += float(result["data"]["totalValue"])
    return total if total > 0 else None

def get_exchange_price(exchange):
    """获取指定交易所的最新价格"""
    result = intoquant_request("GET", "/market/ticker", 
                                {"symbol": SYMBOL, "exchange": exchange, "type": "spot"})
    if result and result.get("code") == 0:
        return float(result["data"]["last"])
    return None

def check_balance(exchange, side, qty, price):
    """
    检查交易所是否有足够的余额
    side=buy: 检查USDT余额是否足够买入qty
    side=sell: 检查币的余额是否足够卖出qty
    """
    result = intoquant_request("GET", "/account/balances", {"exchange": exchange})
    if result and result.get("code") == 0:
        balances = result["data"]
        if side == "buy":
            usdt_balance = float(balances.get("USDT", {}).get("available", 0))
            return usdt_balance >= qty * price * 1.01  # 留1%缓冲
        else:
            coin = SYMBOL.split("/")[0]
            coin_balance = float(balances.get(coin, {}).get("available", 0))
            return coin_balance >= qty * 1.01
    return False

def open_position(spread_rate, direction, account_value):
    """
    开仓
    direction=1: A价格低,B价格高 -> A买入,B卖出
    direction=-1: B价格低,A价格高 -> B买入,A卖出
    """
    log("INFO", f"触发开仓信号,价差率: {spread_rate:.4%},方向: {'A买B卖' if direction==1 else 'B买A卖'}")
    
    price_a = get_exchange_price(EXCHANGE_A)
    price_b = get_exchange_price(EXCHANGE_B)
    if price_a is None or price_b is None:
        return False
    
    # 计算交易数量(用总资金的一部分,两边各一半)
    position_value = account_value * POSITION_SIZE_RATIO
    buy_price = price_a if direction == 1 else price_b
    qty = (position_value / 2) / buy_price
    
    # 确定买卖方向
    if direction == 1:
        buy_ex, sell_ex = EXCHANGE_A, EXCHANGE_B
    else:
        buy_ex, sell_ex = EXCHANGE_B, EXCHANGE_A
    
    # 检查余额
    if not check_balance(buy_ex, "buy", qty, buy_price):
        log("WARNING", f"{buy_ex} USDT余额不足,无法开仓")
        return False
    if not check_balance(sell_ex, "sell", qty, buy_price):
        log("WARNING", f"{sell_ex} 币余额不足,无法开仓")
        return False
    
    # 买入
    buy_order = intoquant_request("POST", "/order/create", {
        "exchange": buy_ex, "symbol": SYMBOL, "type": "spot",
        "side": "buy", "orderType": "market", "quantity": round(qty, 6)
    })
    # 卖出
    sell_order = intoquant_request("POST", "/order/create", {
        "exchange": sell_ex, "symbol": SYMBOL, "type": "spot",
        "side": "sell", "orderType": "market", "quantity": round(qty, 6)
    })
    
    if buy_order and sell_order and buy_order.get("code") == 0 and sell_order.get("code") == 0:
        log("INFO", f"开仓成功!买入: {buy_ex} {qty:.6f},卖出: {sell_ex} {qty:.6f}")
        state.position = direction
        state.qty_a = qty if direction == 1 else -qty  # 正数表示多头,负数表示空头
        state.qty_b = -qty if direction == 1 else qty
        state.entry_spread = spread_rate
        return True
    return False

def close_position():
    """平仓:反向操作"""
    log("INFO", f"触发平仓信号,当前持仓: {state.position},开始平仓...")
    
    if state.position == 1:
        # A买B卖 -> 平仓:A卖出,B买入
        sell_ex, buy_ex = EXCHANGE_A, EXCHANGE_B
        sell_qty = state.qty_a
        buy_qty = abs(state.qty_b)
    else:
        # B买A卖 -> 平仓:B卖出,A买入
        sell_ex, buy_ex = EXCHANGE_B, EXCHANGE_A
        sell_qty = state.qty_b
        buy_qty = abs(state.qty_a)
    
    # 卖出
    sell_order = intoquant_request("POST", "/order/create", {
        "exchange": sell_ex, "symbol": SYMBOL, "type": "spot",
        "side": "sell", "orderType": "market", "quantity": round(sell_qty, 6)
    })
    # 买入
    buy_order = intoquant_request("POST", "/order/create", {
        "exchange": buy_ex, "symbol": SYMBOL, "type": "spot",
        "side": "buy", "orderType": "market", "quantity": round(buy_qty, 6)
    })
    
    if sell_order and buy_order and sell_order.get("code") == 0 and buy_order.get("code") == 0:
        log("INFO", "平仓成功!")
        state.position = 0
        state.qty_a = 0
        state.qty_b = 0
        return True
    return False

def rebalance_funds():
    """
    资金再平衡:当两个交易所的资金不平衡时,划转资金
    这是跨所套利的重要环节,确保两边都有足够的资金
    简化版:实际中需要考虑转账时间、手续费、链上拥堵等
    """
    # 获取两个交易所的USDT余额
    balances = {}
    for ex in [EXCHANGE_A, EXCHANGE_B]:
        result = intoquant_request("GET", "/account/balances", {"exchange": ex})
        if result and result.get("code") == 0:
            balances[ex] = float(result["data"].get("USDT", {}).get("available", 0))
    
    if len(balances) == 2:
        total = sum(balances.values())
        target = total / 2
        # 如果某个交易所余额低于目标的70%,触发再平衡
        for ex in [EXCHANGE_A, EXCHANGE_B]:
            if balances[ex] < target * 0.7:
                other = EXCHANGE_B if ex == EXCHANGE_A else EXCHANGE_A
                transfer_amount = (balances[other] - target) * 0.8  # 转80%的超额部分
                if transfer_amount > 100:  # 至少转100U
                    log("INFO", f"触发资金再平衡:从{other}划转{transfer_amount:.2f} USDT到{ex}")
                    # 调用转账接口(实际中需要确认链上地址、网络等)
                    # intoquant_request("POST", "/account/transfer", {...})
                    # 注意:跨所转账需要时间,期间可能无法交易,需要暂停策略

def risk_check(account_value):
    if account_value > state.peak_value:
        state.peak_value = account_value
    
    dd_exceeded, drawdown = check_drawdown(account_value, state.peak_value, MAX_DRAWDOWN_LIMIT)
    if dd_exceeded:
        log("WARNING", f"最大回撤超限: {drawdown:.2%},强制平仓")
        if state.position != 0:
            close_position()
        return False
    
    today = time.strftime("%Y-%m-%d")
    if state.last_check_date != today:
        state.today_start_value = account_value
        state.last_check_date = today
    
    loss_exceeded, daily_loss = check_daily_loss(state.today_start_value, account_value, DAILY_LOSS_LIMIT)
    if loss_exceeded:
        log("WARNING", f"单日亏损超限: {daily_loss:.2%},今日暂停交易")
        if state.position != 0:
            close_position()
        return False
    
    return True

# ==================== 主循环 ====================
def main():
    log("INFO", "=" * 50)
    log("INFO", f"跨所套利策略启动: {EXCHANGE_A} <-> {EXCHANGE_B}")
    log("INFO", f"开仓价差阈值: {SPREAD_OPEN_THRESHOLD:.2%}")
    log("INFO", "=" * 50)
    
    while True:
        try:
            account_value = get_total_account_value()
            if account_value is None:
                time.sleep(CHECK_INTERVAL)
                continue
            
            if not risk_check(account_value):
                time.sleep(CHECK_INTERVAL)
                continue
            
            # 资金再平衡检查(每小时检查一次,简化为每360次循环)
            # 实际中可以用时间判断
            
            # 获取当前价差
            spread, spread_rate = get_spread(SYMBOL, EXCHANGE_A, EXCHANGE_B)
            if spread_rate is None:
                time.sleep(CHECK_INTERVAL)
                continue
            
            log("INFO", f"当前价差率: {spread_rate:.4%},持仓: {state.position}")
            
            if state.position == 0:
                # 空仓:检查开仓信号
                if spread_rate > SPREAD_OPEN_THRESHOLD:
                    # A价格低,B价格高
                    open_position(spread_rate, 1, account_value)
                elif spread_rate < -SPREAD_OPEN_THRESHOLD:
                    # B价格低,A价格高
                    open_position(spread_rate, -1, account_value)
            else:
                # 持仓:检查平仓信号
                if state.position == 1 and spread_rate < SPREAD_CLOSE_THRESHOLD:
                    close_position()
                elif state.position == -1 and spread_rate > -SPREAD_CLOSE_THRESHOLD:
                    close_position()
            
            time.sleep(CHECK_INTERVAL)
            
        except Exception as e:
            log("ERROR", f"主循环异常: {e}")
            time.sleep(CHECK_INTERVAL)

if __name__ == "__main__":
    main()

跨所套利策略使用说明

  1. 价差阈值的设定:开仓阈值必须覆盖所有成本:

    • 两边的交易手续费:约0.1%-0.2%
    • 两边的滑点:约0.05%-0.15%
    • 资金划转成本(如果需要划转):约0.01%-0.1%
    • 安全缓冲:约0.1%
      合计通常需要0.3%-0.5%。低于这个阈值的价差没有套利价值。建议用a-sig.com的历史价差数据,统计不同阈值下的机会频率和收益,找到最优阈值。
  2. 资金管理是核心:跨所套利需要在两个交易所都有资金。如果一个交易所的资金用完了,就无法开仓。需要:

    • 初始时两个交易所各放一半资金
    • 定期(如每天)做资金再平衡,把盈利的交易所的资金转到亏损的交易所
    • 预留一定的"转账缓冲资金",应对临时的资金不平衡
    • 注意跨所转账需要时间(链上转账几分钟到几小时),转账期间资金不可用
  3. 执行速度很重要:跨所价差往往在几秒到几十秒内就会消失。需要:

    • 低延迟的服务器(建议放在交易所服务器附近,如AWS东京/新加坡)
    • 高效的代码(避免不必要的计算和等待)
    • 同时下单(买入和卖出几乎同时进行,避免单边风险)
      示例中用的是顺序下单,实盘中建议用多线程或异步同时下单。
  4. 单边风险:如果一边下单成功另一边失败(如网络问题、余额不足),就会产生单边敞口,面临价格波动风险。需要:

    • 下单后立即检查两边的订单状态
    • 如果一边失败,立即对冲另一边(如买入成功但卖出失败,立即在同一交易所卖出对冲)
    • 设置单边持仓的时间限制(如超过30秒未对冲,强制平仓)
  5. 交易所选择:不是所有交易所组合都适合跨所套利。需要考虑:

    • 两个交易所的流动性都要好(深度大、滑点小)
    • 两个交易所之间的转账要顺畅(支持相同的链和币种)
    • 两个交易所的API都要稳定
    • 常见的组合:Binance-OKX、Binance-Bybit、OKX-Bybit等

五、配对统计套利策略

配对统计套利利用两个高度相关、存在协整关系的币种之间的价差均值回归特性,当价差偏离均值时做收敛交易。

"""
文件:strategy_pairs_trading.py
功能:配对统计套利策略(简化版)
策略逻辑:
  1. 选择一对存在协整关系的币种(如ETH和BTC)
  2. 实时计算配对价差和Z-Score
  3. 当Z-Score > 开仓阈值(如2.0)时,做空价差(做空A+做多B)
  4. 当Z-Score < -开仓阈值时,做多价差(做多A+做空B)
  5. 当Z-Score回归0附近时平仓
  6. 协整关系检验和止损
"""

import time
import numpy as np
import pandas as pd
from yixin_common import (
    log, get_klines, intoquant_request, calculate_zscore,
    check_drawdown, check_daily_loss
)
from statsmodels.tsa.stattools import coint  # 需要安装:pip install statsmodels

# ==================== 策略参数 ====================
SYMBOL_A = "ETH/USDT"    # 配对币种A
SYMBOL_B = "BTC/USDT"    # 配对币种B
EXCHANGE = "binance"
INTERVAL = "1h"           # K线周期:1小时
LOOKBACK_WINDOW = 60      # 滚动窗口:60根K线(60小时)

Z_OPEN_THRESHOLD = 2.0    # Z-Score开仓阈值
Z_CLOSE_THRESHOLD = 0.2   # Z-Score平仓阈值(接近0时平仓)
Z_STOP_LOSS = 3.5         # Z-Score止损阈值(超过3.5说明可能协整断裂)

POSITION_SIZE_RATIO = 0.7
MAX_DRAWDOWN_LIMIT = 0.10
DAILY_LOSS_LIMIT = 0.04
COINTEST_PVALUE = 0.05    # 协整检验p值阈值
CHECK_INTERVAL = 300       # 检查间隔:5分钟(跟K线周期匹配)

# ==================== 策略状态 ====================
class StrategyState:
    def __init__(self):
        self.position = 0           # 0=空仓, 1=做多价差(多A空B), -1=做空价差(空A多B)
        self.qty_a = 0
        self.qty_b = 0
        self.entry_zscore = 0
        self.hedge_ratio = 1.0      # 对冲比例β
        self.peak_value = 0
        self.today_start_value = 0
        self.last_check_date = None
        self.last_calc_time = None   # 上次计算Z-Score的时间

state = StrategyState()

# ==================== 核心函数 ====================
def get_account_value():
    result = intoquant_request("GET", "/account/total-value")
    if result and result.get("code") == 0:
        return float(result["data"]["totalValue"])
    return None

def calculate_hedge_ratio_and_spread(price_a, price_b):
    """
    计算对冲比例和配对价差
    用线性回归:price_a = α + β * price_b + ε
    β就是对冲比例,残差ε就是价差
    """
    # 确保两个序列长度相同
    min_len = min(len(price_a), len(price_b))
    price_a = price_a[-min_len:]
    price_b = price_b[-min_len:]
    
    # 线性回归计算对冲比例
    # 简化版:用numpy的polyfit
    coeffs = np.polyfit(price_b, price_a, 1)
    beta = coeffs[0]  # 斜率就是对冲比例
    
    # 计算价差
    spread = price_a - beta * price_b
    
    return beta, spread

def check_cointegration(price_a, price_b):
    """
    检验两个价格序列是否存在协整关系
    返回:(是否协整, p值)
    """
    min_len = min(len(price_a), len(price_b))
    price_a = price_a[-min_len:]
    price_b = price_b[-min_len:]
    
    try:
        score, pvalue, _ = coint(price_a, price_b)
        return pvalue < COINTEST_PVALUE, pvalue
    except Exception as e:
        log("ERROR", f"协整检验失败: {e}")
        return False, 1.0

def get_current_zscore():
    """
    获取当前的Z-Score,同时更新对冲比例
    """
    # 获取两个币种的K线数据
    df_a = get_klines(SYMBOL_A, INTERVAL, LOOKBACK_WINDOW + 10)
    df_b = get_klines(SYMBOL_B, INTERVAL, LOOKBACK_WINDOW + 10)
    
    if df_a is None or df_b is None:
        return None, None, None
    
    # 对齐时间索引
    df_a, df_b = df_a.align(df_b, join="inner", axis=0)
    
    if len(df_a) < LOOKBACK_WINDOW:
        log("WARNING", f"K线数据不足,当前{len(df_a)}根,需要{LOOKBACK_WINDOW}根")
        return None, None, None
    
    close_a = df_a["close"].values
    close_b = df_b["close"].values
    
    # 协整检验
    is_cointegrated, pvalue = check_cointegration(close_a, close_b)
    if not is_cointegrated:
        log("WARNING", f"配对不存在协整关系,p值: {pvalue:.4f},暂停交易")
        return None, None, None
    
    # 计算对冲比例和价差
    beta, spread = calculate_hedge_ratio_and_spread(close_a, close_b)
    
    # 计算Z-Score
    spread_series = pd.Series(spread)
    zscore_series = calculate_zscore(spread_series, LOOKBACK_WINDOW)
    current_zscore = zscore_series.iloc[-1]
    
    log("INFO", f"对冲比例β: {beta:.4f},当前Z-Score: {current_zscore:.4f},协整p值: {pvalue:.4f}")
    
    return current_zscore, beta, (close_a[-1], close_b[-1])

def open_position(zscore, beta, prices, account_value):
    """
    开仓
    zscore>0: 价差偏高 -> 做空价差(做空A + 做多B)
    zscore<0: 价差偏低 -> 做多价差(做多A + 做空B)
    """
    direction = -1 if zscore > 0 else 1
    log("INFO", f"触发开仓信号,Z-Score: {zscore:.4f},方向: {'做空价差' if direction==-1 else '做多价差'}")
    
    price_a, price_b = prices
    
    # 计算仓位(用总资金的一部分)
    position_value = account_value * POSITION_SIZE_RATIO
    
    # 按对冲比例分配资金
    # A的价值 = beta * B的价值(确保组合是市场中性的)
    value_b = position_value / (1 + beta)
    value_a = position_value - value_b
    
    qty_a = value_a / price_a
    qty_b = value_b / price_b
    
    if direction == 1:
        # 做多价差:做多A + 做空B
        order_a = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL_A, "type": "spot",
            "side": "buy", "orderType": "market", "quantity": round(qty_a, 6)
        })
        order_b = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL_B, "type": "futures",
            "futuresType": "perpetual", "side": "sell", "orderType": "market",
            "quantity": round(qty_b, 6), "leverage": 1
        })
    else:
        # 做空价差:做空A(需要现货杠杆或借币)+ 做多B
        order_a = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL_A, "type": "margin",
            "side": "sell", "orderType": "market", "quantity": round(qty_a, 6)
        })
        order_b = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL_B, "type": "futures",
            "futuresType": "perpetual", "side": "buy", "orderType": "market",
            "quantity": round(qty_b, 6), "leverage": 1
        })
    
    if order_a and order_b and order_a.get("code") == 0 and order_b.get("code") == 0:
        log("INFO", f"开仓成功!A: {qty_a:.6f}, B: {qty_b:.6f}")
        state.position = direction
        state.qty_a = qty_a if direction == 1 else -qty_a
        state.qty_b = -qty_b if direction == 1 else qty_b
        state.entry_zscore = zscore
        state.hedge_ratio = beta
        return True
    return False

def close_position():
    """平仓"""
    log("INFO", f"触发平仓信号,当前持仓: {state.position},开始平仓...")
    
    if state.position == 1:
        # 平多价差:卖出A + 平B空头
        order_a = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL_A, "type": "spot",
            "side": "sell", "orderType": "market", "quantity": round(abs(state.qty_a), 6)
        })
        order_b = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL_B, "type": "futures",
            "futuresType": "perpetual", "side": "buy", "orderType": "market",
            "quantity": round(abs(state.qty_b), 6), "reduceOnly": True
        })
    else:
        # 平空价差:买入还A + 平B多头
        order_a = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL_A, "type": "margin",
            "side": "buy", "orderType": "market", "quantity": round(abs(state.qty_a), 6)
        })
        order_b = intoquant_request("POST", "/order/create", {
            "exchange": EXCHANGE, "symbol": SYMBOL_B, "type": "futures",
            "futuresType": "perpetual", "side": "sell", "orderType": "market",
            "quantity": round(abs(state.qty_b), 6), "reduceOnly": True
        })
    
    if order_a and order_b and order_a.get("code") == 0 and order_b.get("code") == 0:
        log("INFO", "平仓成功!")
        state.position = 0
        state.qty_a = 0
        state.qty_b = 0
        return True
    return False

def risk_check(account_value, current_zscore):
    """风控检查"""
    if account_value > state.peak_value:
        state.peak_value = account_value
    
    dd_exceeded, drawdown = check_drawdown(account_value, state.peak_value, MAX_DRAWDOWN_LIMIT)
    if dd_exceeded:
        log("WARNING", f"最大回撤超限: {drawdown:.2%},强制平仓")
        if state.position != 0:
            close_position()
        return False
    
    today = time.strftime("%Y-%m-%d")
    if state.last_check_date != today:
        state.today_start_value = account_value
        state.last_check_date = today
    
    loss_exceeded, daily_loss = check_daily_loss(state.today_start_value, account_value, DAILY_LOSS_LIMIT)
    if loss_exceeded:
        log("WARNING", f"单日亏损超限: {daily_loss:.2%},今日暂停交易")
        if state.position != 0:
            close_position()
        return False
    
    # Z-Score止损:如果Z-Score超过止损阈值,说明可能协整断裂,强制平仓
    if state.position != 0 and current_zscore is not None:
        if abs(current_zscore) > Z_STOP_LOSS:
            log("WARNING", f"Z-Score止损!当前Z-Score: {current_zscore:.4f},超过阈值: {Z_STOP_LOSS},可能协整断裂")
            close_position()
            return False
    
    return True

# ==================== 主循环 ====================
def main():
    log("INFO", "=" * 50)
    log("INFO", f"配对统计套利策略启动: {SYMBOL_A} - {SYMBOL_B}")
    log("INFO", f"Z-Score开仓阈值: {Z_OPEN_THRESHOLD}")
    log("INFO", f"Z-Score平仓阈值: {Z_CLOSE_THRESHOLD}")
    log("INFO", "=" * 50)
    
    while True:
        try:
            account_value = get_account_value()
            if account_value is None:
                time.sleep(CHECK_INTERVAL)
                continue
            
            # 获取当前Z-Score(每根K线计算一次,简化为每次循环都算)
            current_zscore, beta, prices = get_current_zscore()
            if current_zscore is None:
                time.sleep(CHECK_INTERVAL)
                continue
            
            if not risk_check(account_value, current_zscore):
                time.sleep(CHECK_INTERVAL)
                continue
            
            if state.position == 0:
                # 空仓:检查开仓信号
                if current_zscore > Z_OPEN_THRESHOLD:
                    open_position(current_zscore, beta, prices, account_value)
                elif current_zscore < -Z_OPEN_THRESHOLD:
                    open_position(current_zscore, beta, prices, account_value)
            else:
                # 持仓:检查平仓信号
                if abs(current_zscore) < Z_CLOSE_THRESHOLD:
                    close_position()
            
            time.sleep(CHECK_INTERVAL)
            
        except Exception as e:
            log("ERROR", f"主循环异常: {e}")
            time.sleep(CHECK_INTERVAL)

if __name__ == "__main__":
    main()

配对统计套利策略使用说明

  1. 配对选择是关键:不是任意两个币种都能做配对套利。需要满足:

    • 高相关性(相关系数>0.7)
    • 存在协整关系(p值<0.05)
    • 价差有均值回归特性(半衰期适中,如3-10天)
    • 两个币种都有足够的流动性(能做空、深度好)
      常见的配对:ETH-BTC、SOL-ETH、BNB-BTC、LINK-ETH等同板块币种。建议用a-sig.com的配对筛选工具,批量扫描全市场的配对机会。
  2. 对冲比例的动态调整:示例中每次都重新计算对冲比例β,这是正确的做法。因为两个币种的价格关系会随时间变化,固定的对冲比例会导致对冲不精确,产生额外的风险。更高级的方法是用卡尔曼滤波动态估计时变的对冲比例,效果更好。

  3. 协整关系的监控:配对套利最大的风险是协整断裂——两个币种的长期均衡关系被打破(如其中一个币种出现黑天鹅事件),价差不回归反而持续扩大。需要:

    • 每次计算Z-Score时都重新做协整检验
    • 如果p值>0.1,说明协整关系减弱,暂停开新仓
    • 如果持仓中协整关系断裂(Z-Score超过3.5),强制止损平仓
    • 定期(如每周)重新评估配对是否仍然有效
  4. 做空的实现:配对套利通常需要做空其中一个币种。可以用:

    • 永续合约做空(最常用,流动性好,支持高杠杆)
    • 现货杠杆做空(需要借币,有借币利息)
    • 示例中A用现货/杠杆,B用永续合约,你可以根据实际情况调整。建议两个都用永续合约,这样不需要借币,操作更简单。
  5. 参数优化

    • K线周期:1小时适合中短线,4小时适合中长线。周期越长,信号越少但越可靠。
    • 滚动窗口:60根K线(1小时周期=2.5天)适合短线,240根(10天)适合中长线。窗口太短Z-Score波动大,太长反应慢。
    • 开仓阈值:2.0是常用值,对应约95%分位。可以用1.5(更多机会但假信号多)或2.5(更少机会但更可靠)。
      建议用a-sig.com的回测工具,在历史数据上优化这些参数,找到最优组合。

六、网格套利策略

网格套利在设定的价格区间内,设置一系列价格档位,价格每下跌一个档位买入,每上涨一个档位卖出,通过区间内的高抛低吸获利。

"""
文件:strategy_grid_trading.py
功能:网格套利策略(简化版,现货网格)
策略逻辑:
  1. 设定价格区间(上界、下界)和网格数量
  2. 在区间内等间距(或等比例)设置网格线
  3. 价格每下跌一格买入固定数量,每上涨一格卖出固定数量
  4. 价格突破区间上界或下界时,暂停网格或调整区间
  5. 严格的资金管理和风控
"""

import time
import numpy as np
from yixin_common import (
    log, get_klines, intoquant_request, calculate_atr,
    check_drawdown, check_daily_loss
)

# ==================== 策略参数 ====================
SYMBOL = "BTC/USDT"
EXCHANGE = "binance"

# 网格参数(也可以用ATR自动计算,见下方函数)
GRID_UPPER = 32000.0     # 网格上界
GRID_LOWER = 28000.0     # 网格下界
GRID_COUNT = 20           # 网格数量(20格 = 21条网格线)
GRID_TYPE = "arithmetic"  # 网格类型:arithmetic(等差), geometric(等比)

POSITION_SIZE_RATIO = 0.8  # 总仓位比例
MAX_DRAWDOWN_LIMIT = 0.15   # 最大回撤:15%(网格策略回撤可能较大)
DAILY_LOSS_LIMIT = 0.05     # 单日亏损:5%
CHECK_INTERVAL = 30          # 检查间隔:30秒

# ==================== 策略状态 ====================
class StrategyState:
    def __init__(self):
        self.grid_lines = []        # 网格线价格列表
        self.grid_qty = 0           # 每格交易数量
        self.holdings = {}          # 持仓记录:{网格线索引: 买入数量}
        self.total_cost = 0         # 总买入成本
        self.total_holdings = 0     # 总持仓数量
        self.peak_value = 0
        self.today_start_value = 0
        self.last_check_date = None
        self.initialized = False    # 是否已初始化网格

state = StrategyState()

# ==================== 核心函数 ====================
def get_account_value():
    result = intoquant_request("GET", "/account/total-value")
    if result and result.get("code") == 0:
        return float(result["data"]["totalValue"])
    return None

def get_current_price():
    result = intoquant_request("GET", "/market/ticker",
                                {"symbol": SYMBOL, "exchange": EXCHANGE, "type": "spot"})
    if result and result.get("code") == 0:
        return float(result["data"]["last"])
    return None

def calculate_grid_lines():
    """
    计算网格线价格
    等差网格:每格价格差相等
    等比网格:每格价格比例相等(适合价格波动大的标的)
    """
    lines = []
    if GRID_TYPE == "arithmetic":
        step = (GRID_UPPER - GRID_LOWER) / GRID_COUNT
        for i in range(GRID_COUNT + 1):
            lines.append(GRID_LOWER + i * step)
    else:  # geometric
        ratio = (GRID_UPPER / GRID_LOWER) ** (1 / GRID_COUNT)
        for i in range(GRID_COUNT + 1):
            lines.append(GRID_LOWER * (ratio ** i))
    return lines

def calculate_grid_qty(account_value):
    """
    计算每格的交易数量
    假设所有网格都被触发买入,总资金不超过仓位限制
    """
    total_budget = account_value * POSITION_SIZE_RATIO
    # 平均价格
    avg_price = (GRID_UPPER + GRID_LOWER) / 2
    # 总买入数量 = 总预算 / 平均价格
    total_qty = total_budget / avg_price
    # 每格数量 = 总数量 / 网格数
    grid_qty = total_qty / GRID_COUNT
    return grid_qty

def initialize_grid(account_value):
    """初始化网格:计算网格线、每格数量、初始底仓"""
    log("INFO", "初始化网格...")
    state.grid_lines = calculate_grid_lines()
    state.grid_qty = calculate_grid_qty(account_value)
    
    current_price = get_current_price()
    if current_price is None:
        return False
    
    # 初始底仓:如果当前价格在区间内,买入当前价格以下所有网格的底仓
    # 简化处理:先买入50%的底仓
    initial_qty = state.grid_qty * GRID_COUNT * 0.5
    initial_cost = initial_qty * current_price
    
    if initial_cost > account_value * POSITION_SIZE_RATIO:
        initial_qty = (account_value * POSITION_SIZE_RATIO) / current_price
    
    log("INFO", f"买入初始底仓: {initial_qty:.6f} {SYMBOL.split('/')[0]},价格: {current_price:.2f}")
    
    order = intoquant_request("POST", "/order/create", {
        "exchange": EXCHANGE, "symbol": SYMBOL, "type": "spot",
        "side": "buy", "orderType": "market", "quantity": round(initial_qty, 6)
    })
    
    if order and order.get("code") == 0:
        state.total_holdings = initial_qty
        state.total_cost = initial_qty * current_price
        state.initialized = True
        log("INFO", f"网格初始化完成!网格数: {GRID_COUNT},每格数量: {state.grid_qty:.6f}")
        log("INFO", f"网格区间: [{GRID_LOWER:.2f}, {GRID_UPPER:.2f}]")
        return True
    else:
        log("ERROR", "初始底仓买入失败")
        return False

def find_grid_index(price):
    """找到价格所在的网格线索引"""
    for i in range(len(state.grid_lines) - 1):
        if state.grid_lines[i] <= price < state.grid_lines[i + 1]:
            return i
    if price >= state.grid_lines[-1]:
        return len(state.grid_lines) - 1
    return 0

def check_grid_trades(prev_price, current_price):
    """
    检查从上一次检查到现在,价格穿越了哪些网格线,执行相应的买卖
    """
    if prev_price == current_price:
        return
    
    # 确定价格移动方向
    if current_price > prev_price:
        # 价格上涨:检查穿越了哪些网格线,执行卖出
        for i in range(len(state.grid_lines)):
            line_price = state.grid_lines[i]
            if prev_price < line_price <= current_price:
                # 向上穿越了这条网格线,执行卖出
                if i in state.holdings and state.holdings[i] > 0:
                    sell_qty = state.holdings[i]
                    log("INFO", f"网格卖出!价格穿越网格线{i}: {line_price:.2f},卖出: {sell_qty:.6f}")
                    order = intoquant_request("POST", "/order/create", {
                        "exchange": EXCHANGE, "symbol": SYMBOL, "type": "spot",
                        "side": "sell", "orderType": "market", "quantity": round(sell_qty, 6)
                    })
                    if order and order.get("code") == 0:
                        state.holdings[i] = 0
                        state.total_holdings -= sell_qty
                        # 计算利润:卖出价 - 买入价
                        # 简化处理,实际需要记录每笔买入成本
    else:
        # 价格下跌:检查穿越了哪些网格线,执行买入
        for i in range(len(state.grid_lines) - 1, -1, -1):
            line_price = state.grid_lines[i]
            if prev_price > line_price >= current_price:
                # 向下穿越了这条网格线,执行买入
                if i not in state.holdings or state.holdings[i] == 0:
                    buy_qty = state.grid_qty
                    log("INFO", f"网格买入!价格穿越网格线{i}: {line_price:.2f},买入: {buy_qty:.6f}")
                    order = intoquant_request("POST", "/order/create", {
                        "exchange": EXCHANGE, "symbol": SYMBOL, "type": "spot",
                        "side": "buy", "orderType": "market", "quantity": round(buy_qty, 6)
                    })
                    if order and order.get("code") == 0:
                        state.holdings[i] = buy_qty
                        state.total_holdings += buy_qty
                        state.total_cost += buy_qty * line_price

def check_range_breakout(current_price):
    """
    检查价格是否突破网格区间
    突破上界:全部卖出,暂停网格
    突破下界:暂停买入,等待价格回归或调整区间
    """
    if current_price > GRID_UPPER:
        log("WARNING", f"价格突破网格上界!当前: {current_price:.2f},上界: {GRID_UPPER:.2f}")
        # 全部卖出
        if state.total_holdings > 0:
            log("INFO", f"全部卖出持仓: {state.total_holdings:.6f}")
            order = intoquant_request("POST", "/order/create", {
                "exchange": EXCHANGE, "symbol": SYMBOL, "type": "spot",
                "side": "sell", "orderType": "market", "quantity": round(state.total_holdings, 6)
            })
            if order and order.get("code") == 0:
                state.holdings = {}
                state.total_holdings = 0
        # 暂停策略,等待人工调整区间
        log("WARNING", "网格暂停,请调整区间后重新启动")
        return True  # 返回True表示需要暂停
    
    elif current_price < GRID_LOWER:
        log("WARNING", f"价格突破网格下界!当前: {current_price:.2f},下界: {GRID_LOWER:.2f}")
        # 下界突破不强制卖出(可能继续持有等反弹),但暂停新的买入
        log("WARNING", "价格低于网格下界,暂停买入,持有现有仓位等待反弹")
        # 实际中可以设置一个止损线,跌破下界一定比例后止损
        return False  # 继续运行但不买入
    
    return False

def risk_check(account_value):
    if account_value > state.peak_value:
        state.peak_value = account_value
    
    dd_exceeded, drawdown = check_drawdown(account_value, state.peak_value, MAX_DRAWDOWN_LIMIT)
    if dd_exceeded:
        log("WARNING", f"最大回撤超限: {drawdown:.2%},全部卖出止损")
        if state.total_holdings > 0:
            order = intoquant_request("POST", "/order/create", {
                "exchange": EXCHANGE, "symbol": SYMBOL, "type": "spot",
                "side": "sell", "orderType": "market", "quantity": round(state.total_holdings, 6)
            })
            if order and order.get("code") == 0:
                state.holdings = {}
                state.total_holdings = 0
        return False
    
    today = time.strftime("%Y-%m-%d")
    if state.last_check_date != today:
        state.today_start_value = account_value
        state.last_check_date = today
    
    loss_exceeded, daily_loss = check_daily_loss(state.today_start_value, account_value, DAILY_LOSS_LIMIT)
    if loss_exceeded:
        log("WARNING", f"单日亏损超限: {daily_loss:.2%},今日暂停交易")
        return False
    
    return True

# ==================== 主循环 ====================
def main():
    log("INFO", "=" * 50)
    log("INFO", "网格套利策略启动")
    log("INFO", f"网格区间: [{GRID_LOWER:.2f}, {GRID_UPPER:.2f}]")
    log("INFO", f"网格数量: {GRID_COUNT},类型: {GRID_TYPE}")
    log("INFO", "=" * 50)
    
    prev_price = None
    
    while True:
        try:
            account_value = get_account_value()
            if account_value is None:
                time.sleep(CHECK_INTERVAL)
                continue
            
            # 初始化网格
            if not state.initialized:
                if not initialize_grid(account_value):
                    time.sleep(CHECK_INTERVAL)
                    continue
            
            if not risk_check(account_value):
                time.sleep(CHECK_INTERVAL)
                continue
            
            current_price = get_current_price()
            if current_price is None:
                time.sleep(CHECK_INTERVAL)
                continue
            
            log("INFO", f"当前价格: {current_price:.2f},持仓: {state.total_holdings:.6f}")
            
            # 检查区间突破
            should_pause = check_range_breakout(current_price)
            if should_pause:
                break  # 暂停策略
            
            # 检查网格交易
            if prev_price is not None:
                check_grid_trades(prev_price, current_price)
            
            prev_price = current_price
            time.sleep(CHECK_INTERVAL)
            
        except Exception as e:
            log("ERROR", f"主循环异常: {e}")
            time.sleep(CHECK_INTERVAL)

if __name__ == "__main__":
    main()

网格套利策略使用说明

  1. 网格区间的设定:这是网格策略最关键的参数。区间太窄,价格容易突破,频繁暂停;区间太宽,网格密度低,交易机会少,资金利用率低。建议:

    • 用近期的支撑位和阻力位作为区间上下界
    • 用ATR设定区间宽度:区间宽度 = 3-5倍ATR(14日)
    • 回测不同区间在历史数据上的表现,选择最优区间
    • 可以用a-sig.com的网格回测工具,快速测试不同参数的效果
  2. 网格数量的设定:网格数量决定了每格的间距和每格的资金量。

    • 数量太少(如<10):间距大,交易机会少,但每格利润高
    • 数量太多(如>50):间距小,交易频繁,但每格利润低,手续费占比高
    • 常用范围:20-40格
    • 每格利润要大于手续费+滑点(通常每格间距>0.2%才有意义)
  3. 等差vs等比网格

    • 等差网格:每格价格差相等,适合价格波动小、区间窄的标的(如BTC在震荡市)
    • 等比网格:每格价格比例相等,适合价格波动大、区间宽的标的(如山寨币),因为高价区每格绝对值大、低价区每格绝对值小,更合理
    • 建议BTC/ETH用等差,山寨币用等比
  4. 初始底仓:示例中买入了50%的底仓。实际中可以根据当前价格位置调整:

    • 当前价格在区间下沿:多买底仓(如70%)
    • 当前价格在区间上沿:少买底仓(如30%)
    • 当前价格在中间:50%
      底仓太多,下跌时买入空间小;底仓太少,上涨时卖出利润少。需要平衡。
  5. 区间突破的处理

    • 向上突破:通常全部卖出获利,因为价格可能进入新的上涨区间,继续持有网格可能错过上涨。可以等价格稳定后重新设定更高的区间。
    • 向下突破:比较棘手。可以选择:
      a. 继续持有,等价格反弹回区间(但可能越套越深)
      b. 设置止损(如跌破下界5%全部卖出),避免更大亏损
      c. 动态调整区间下界,继续网格(但需要更多资金)
      建议设置止损,不要死扛。网格策略在单边下跌市中会持续接飞刀,亏损可能很大。
  6. 合约网格vs现货网格:示例是现货网格,你也可以用合约网格:

    • 合约网格可以用杠杆,资金效率更高,但风险也更大
    • 双向网格(同时挂多空单)可以在上涨和下跌中都获利
    • 合约网格需要特别注意保证金管理,避免强平
      建议新手先用现货网格,熟悉后再尝试合约网格。

七、代码部署和运维建议

最后,提供一些代码部署和运维的建议,帮助你把这些策略从"能运行"变成"稳定盈利"。

7.1 服务器部署

# 1. 准备云服务器(推荐配置:2核4G以上,Linux系统)
# 推荐选择交易所服务器所在地区的节点(如AWS东京、阿里云新加坡),降低延迟

# 2. 安装Python环境
sudo apt update
sudo apt install python3 python3-pip python3-venv

# 3. 创建项目目录和虚拟环境
mkdir -p /opt/yixin_strategy
cd /opt/yixin_strategy
python3 -m venv venv
source venv/bin/activate

# 4. 安装依赖
pip install requests pandas numpy ccxt python-dotenv statsmodels

# 5. 上传代码和.env文件
# .env文件内容示例:
# A_SIG_API_KEY=your_a_sig_api_key
# INTOQUANT_API_KEY=your_intoquant_api_key
# INTOQUANT_API_SECRET=your_intoquant_api_secret
# 注意:.env文件包含敏感信息,不要上传到Git,设置文件权限600

# 6. 用systemd管理进程(开机自启、崩溃自动重启)
# 创建 /etc/systemd/system/grid-strategy.service
# [Unit]
# Description=Yixin Grid Trading Strategy
# After=network.target
#
# [Service]
# Type=simple
# User=root
# WorkingDirectory=/opt/yixin_strategy
# ExecStart=/opt/yixin_strategy/venv/bin/python /opt/yixin_strategy/strategy_grid_trading.py
# Restart=always
# RestartSec=10
#
# [Install]
# WantedBy=multi-user.target

# 启用和启动
sudo systemctl daemon-reload
sudo systemctl enable grid-strategy
sudo systemctl start grid-strategy

# 查看日志
sudo journalctl -u grid-strategy -f

7.2 日志和监控

# 建议使用Python的logging模块,而不是简单的print
# 日志同时输出到控制台和文件,文件按天轮转,保留30天

import logging
from logging.handlers import TimedRotatingFileHandler

def setup_logger(name, log_file, level=logging.INFO):
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    
    handler = TimedRotatingFileHandler(log_file, when="midnight", interval=1, backupCount=30)
    handler.setFormatter(formatter)
    
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(formatter)
    
    logger = logging.getLogger(name)
    logger.setLevel(level)
    logger.addHandler(handler)
    logger.addHandler(console_handler)
    
    return logger

# 使用
logger = setup_logger("grid_strategy", "/var/log/yixin/grid.log")
logger.info("策略启动")
logger.warning("风险告警")
logger.error("交易失败")

7.3 告警通知

# 关键事件(开仓、平仓、风控触发、系统异常)需要实时通知
# 可以用Telegram Bot、企业微信、钉钉、邮件等方式

import requests

def send_telegram_alert(bot_token, chat_id, message):
    """发送Telegram告警"""
    url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
    params = {
        "chat_id": chat_id,
        "text": message,
        "parse_mode": "Markdown"
    }
    try:
        requests.post(url, params=params, timeout=10)
    except Exception as e:
        print(f"发送告警失败: {e}")

# 使用示例
# send_telegram_alert(BOT_TOKEN, CHAT_ID, "⚠️ *风控告警*\n最大回撤超限: 8.5%,已强制平仓")

7.4 数据备份和回滚

# 1. 策略代码用Git管理,每次修改都提交,方便回滚
git init
git add .
git commit -m "初始版本"
# 修改后
git commit -am "优化网格参数"
# 回滚到上一个版本
git revert HEAD

# 2. 配置文件和参数单独管理,不要硬编码在代码里
# 用config.yaml或.env管理配置,修改配置不需要改代码

# 3. 定期备份交易记录和账户数据
# 可以每天导出交易历史,保存到数据库或文件
# 用于后续的绩效分析和策略优化

7.5 实盘前的检查清单

在把任何策略投入实盘前,请确认以下事项:

  • 策略逻辑经过充分回测,样本外测试通过
  • 模拟盘运行至少2周,表现跟回测一致
  • 小资金实盘运行至少1个月,表现符合预期
  • 代码经过充分测试,没有已知bug
  • 风控规则完善(最大回撤、单日亏损、止损、异常处理)
  • 服务器稳定,有崩溃自动重启机制
  • 日志和告警系统正常工作
  • 有应急预案(系统故障、交易所宕机、极端行情)
  • 资金量在策略的舒适容量范围内
  • 你理解策略的收益来源和风险,能承受最坏情况

八、总结

本附录提供了五大核心套利策略的简化版Python代码,涵盖期现套利、资金费率套利、跨所套利、配对统计套利、网格套利。每个策略都包含完整的策略逻辑、信号计算、下单执行、风控检查和主循环,并有详细的中文注释。

这些代码是"简化版",重点是展示策略逻辑和API调用方式。实盘使用时,你还需要:

  1. 优化执行:把市价单改为限价单或智能下单算法,降低滑点
  2. 完善风控:增加更多的风控规则(如单笔止损、仓位限制、相关性风控)
  3. 异常处理:完善各种异常情况的处理(API错误、网络中断、订单异常)
  4. 参数优化:用历史数据回测优化参数,找到适合当前市场的参数
  5. 监控告警:完善日志和告警系统,及时发现问题
  6. 资金管理:完善多策略、多账户的资金管理和再平衡

壹信量化平台提供了完整的策略开发和部署工具链:

  • a-sig.com:提供高质量的行情数据、套利指标、回测工具,是策略研发的数据基础
  • intoquant.com:提供策略定制开发、系统部署、API对接、运维监控等服务,帮助你把策略快速落地

如果你在策略开发或部署过程中遇到问题,可以访问intoquant.com获取专业的技术支持和定制服务。

最后再次提醒:量化交易有风险,实盘前请充分回测和模拟盘验证,控制仓位,做好风控。本代码仅供学习参考,不构成投资建议。

祝你的量化套利之路顺利!

⚠️ 风险提示:本书内容仅为量化研究与知识分享,不构成任何投资建议。套利交易存在基差、费率、流动性、平台与极端行情等风险,历史表现不代表未来收益。投资有风险,入市需谨慎,请自主决策、量力而行。

💡 键盘 ←/→ 翻章 · T 键切换目录