2
关注
19
关注者

币本位做空资费套利策略的量化实现

创建于: 2025-01-21 17:06:52, 更新于: 2025-02-17 11:25:01
comments   0
hits   447

币本位做空资费套利策略的量化实现

想象一下,有一种策略,能够在数字货币市场的剧烈波动中稳如泰山,无论牛市还是熊市,都能为你带来持续的正收益。听起来像是天方夜谭?其实不然。币本位一倍做空资费套利策略,正是这样一个“躺赚”神器。

通过巧妙利用币本位合约的独特性质与资金费率机制,这一策略不仅能够在牛市中获取稳定收益,还能在熊市中有效规避风险,实现跨越市场周期的稳健回报。币本位合约的“保现金”特性,使得即使市场价格大幅波动,策略的总价值也能保持稳定。而资金费率作为永续合约的核心机制,为策略提供了额外的收益来源。本文将深入解析币本位做空策略的核心逻辑、实现过程及其优缺点,帮助投资者理解如何在数字货币市场中,通过这一策略实现“躺赚”收益。无论你是量化交易新手,还是经验丰富的投资者,这一策略都值得你深入了解与尝试。

币本位做空

币本位的特点

币本位合约的单位以张为计量,每张的价值固定(例如 BTC 的 1 张合约价值为 100 USD,ETH 为 10 USD)。这种特点使得在 1 倍杠杆的情况下,即使市场价格大幅波动,理论上不会爆仓。

盈亏结算分析

假设当前价格为 100 USD,开 10 张币本位做空合约,其价值等于 100 USD,换算为币数量为 1 个单位:

  1. 价格下跌

    • 价格跌至 50 USD,收益率为: [ \text{收益率} = \frac{100 - 50}{50} = 100\% ] 持币数量变为 2 个,总价值仍为: [ 2 \times 50 = 100 \, \text{USD} ]
  2. 价格上涨

    • 价格涨至 200 USD,收益率为: [ \text{收益率} = \frac{100 - 200}{200} = -50\% ] 持币数量变为 0.5 个,总价值仍为: [ 0.5 \times 200 = 100 \, \text{USD} ]

因此在一定程度上来说,币本位做空的总价值保持稳定,是一种保现金的操作。那么该策略的收益来源来自于哪里呢,我们介绍下一个概念:资金费率

资金费率

在币本位做空策略中,资金费率是策略收益的核心驱动力之一,其特性和变化直接影响策略的稳定性和盈利能力。通过合理利用资金费率的特点,策略可以在不同市场环境下保持较好的表现,但也需要注意其潜在的局限性。

资金费率的定义与特点

资金费率是永续合约市场中的一种调节机制,旨在平衡多空双方的持仓成本,避免永续合约价格长期偏离现货价格。其实有学过CFA的朋友,会对永续合约的原名 swap 记忆犹新。永续合约的资金费率机制,其设计灵感很大程度上来源于传统金融中的利率互换(Interest Rate Swap)。在利率互换中,双方根据约定的利率(如固定利率与浮动利率)定期交换利息支付,以对冲利率波动的风险。类似地,永续合约的资金费率机制也是通过定期支付或收取费用,来平衡多空双方的持仓成本,确保合约价格与现货价格之间的差距不会过大。

币本位做空资费套利策略的量化实现

资金费率的引入,本质上是为了解决永续合约与现货价格之间的基差问题。当永续合约价格高于现货价格时,资金费率为正,多头支付费用给空头;当永续合约价格低于现货价格时,资金费率为负,空头支付费用给多头。这种机制不仅借鉴了利率互换的核心理念,还结合了数字货币市场的特点,形成了一种独特的市场调节工具。在数字货币领域,其主要特点包括:

  1. 市场供需决定:资金费率由市场中多头与空头的供需关系决定,是多空力量博弈的结果。
  2. 周期性支付:资金费率按一定周期(如8小时)支付,费用在多头和空头之间相互转移。
  3. 情绪反映:正资金费率通常表明市场情绪偏多,永续合约价格高于现货价格;负资金费率则表明市场情绪偏空,永续合约价格低于现货价格。

正资金费率(Funding Rate > 0)

  • 表现:在正资金费率环境下,多头需支付费用给空头。
  • 收益逻辑:随着资金费率的稳定支付,策略通过持有空头头寸持续收取资金费用,即使市场价格上涨,也不会对收益产生过大负面影响。例如,当资金费率为0.02%/8小时时,持有$10,000价值的合约可获得每日收益: [ 每日收益 = 10,000 \times 0.02\% \times 3 = 6 \, \text{USDT} ] 这种收益来源相对稳定且与价格波动关系较小。

负资金费率(Funding Rate < 0)

  • 表现:当资金费率为负时,空头需支付费用给多头,策略收益可能受到压缩,甚至出现亏损。
  • 风险点:在负资金费率持续时间较长或波动较大的情况下,如长期熊市,策略可能因支付资金费用而面临较高成本,导致整体收益无法覆盖运营成本。

策略盈利来源

正如子楠大神所言,策略在构建初始阶段需要了解策略的收益来源和风险来源。在上面的基础上,我们了解了币本位做空的总价值是保持稳定,那么收益的来源是资金费率套利。有些同学可能好奇,资金费率会持续带来稳定的正向收益吗?我们在【数据探索】模块进行一下数据分析工作:

币本位做空资费套利策略的量化实现

SELECT 
    Exchange,
    Symbol,
    toDateTime(Time / 1000) AS Time,  -- 将毫秒时间戳转换为时间戳
    Rate,
    SUM(Rate) OVER (PARTITION BY Exchange, Symbol ORDER BY Time) AS CumulativeRate  -- 计算累加的 Rate
FROM 
    klines.funding
WHERE 
    Exchange = '{{exchange}}' 
    AND Symbol = '{{symbol}}'
ORDER BY 
    Time;

动态参数使用币安交易所,合约选择比特币币本位(btc_usd)合约,进行累计资金费率的展示。可以看到,在2020至今,资金费率收益呈现稳步上涨的水平,累计5年收益高达50%。虽然和动辄成百上千的收益不能媲美,但是胜在比较稳定,因此我们可以理清该策略的盈利来源。

在币本位做空策略中,资金费率是盈利的关键来源。由于币本位合约的单位是固定的,即使市场价格波动,做空者的合约价值变化通常是可控的,因此资金费率成为策略的主要盈利来源。以下是资金费率对策略的具体影响:

  1. 牛市阶段(资金费率为正)

    • 市场现象:在牛市中,由于市场情绪高涨,投资者普遍倾向于做多,导致多头持仓增加,从而推动资金费率变为正值。
    • 收益逻辑:在这种情况下,做空者将从多头支付的资金费率中获得收益,成为策略的主要盈利来源。由于资金费率为正,空头头寸可以持续获利,且通过利用获得的资金费率可以继续开仓空单,获取更多的资金费率收益。
    • 具体影响:如果资金费率为正且稳定,空头将定期收取资金费率,且不受价格波动太大影响。即使市场价格上涨,空头在合约上可能会面临较大的明面损失,但从整体来看,资金的净值保持相对稳定。这是因为策略的盈利来源主要依赖于资金费率的收取,而非价格波动。尽管价格波动可能对合约的明面亏损产生影响,但由于空头头寸的合约价值变化可控,资金的主要盈利还是来自于资金费率的定期收取,而非市场价格的波动。这使得策略能够在一定程度上抵消价格波动的风险,维持资金的稳定增值。
  2. 熊市阶段(资金费率为负)

    • 市场现象:在熊市中,市场价格下跌较快,做空头寸通常激增,导致资金费率变为负值,即空头需要向多头支付费用。然而,由于近年来数字货币市场的蓬勃发展,熊市阶段相对较少。
    • 收益逻辑:尽管资金费率为负,且空头需要支付费用,但由于做空合约的单位固定,空头头寸的合约价值变化较小,因此策略的保值特性依然有效。价格的剧烈波动不会导致大幅亏损,同时资金费率的负担相对可控,策略仍能在一定程度上保持稳定。

通过这两种市场环境的分析,可以看出,币本位做空策略的盈利和风险主要受资金费率的影响。牛市阶段资金费率为正时,做空者能够从中获益;而熊市阶段虽然资金费率为负,但由于合约价值变化可控,策略仍具备较强的保值能力。

策略逻辑架构

一个稳健的策略架构是策略成功实施的关键,它提供了清晰的执行框架,确保策略能够在各种市场条件下稳定运作,并有效应对风险和波动。这里我们参考小草大神的U本位多币种合约策略架构,针对币本位,做了一定程度的修改和完善。

'''backtest
start: 2024-11-20 00:00:00
end: 2024-11-20 01:00:00
period: 1d
basePeriod: 1m
exchanges: [{"eid":"Futures_OKX","currency":"BTC_USD"}]
'''

import time
import json 

# 全局变量,储存数据
SYMBOLS = 'BTC'     # SYMBOLS代表要交易的币种,格式如"BTC,ETH,LTC"
QUOTO = 'USD'       # QUOTO为基础货币, 币本位为USD
INTERVAL = 5        # INTERVAL代表循环的间隔
ICEMONEY = 2000     # 冰山下单金额
ROLLINGNUM = 1      # 滚动盈利加仓数量


Info = {
    'trade_symbols': SYMBOLS.split(','),  # 交易币种
    'base_coin': QUOTO,                   # 基础货币
    'ticker': {},                         # 行情数据
    'order': {},                          # 订单信息
    'account': {},                        # 账户信息
    'precision': {},                      # 精度信息
    'position': {},                       # 仓位信息
    'time': {},                           # 时间相关数据
    'count': {},                          # 计数器
    'interval': INTERVAL                  # 循环的间隔时间
}

# 初始化策略
def InitInfo():

    # 初始化时间数据,控制更新的时间
    Info['time'] = {
        'update_ticker_time': 0,    # 更新行情的时间
        'update_pos_time': 0,       # 更新仓位的时间
        'update_profit_time': 0,    # 更新利润的时间
        'update_account_time': 0,   # 更新账户信息的时间
        'update_status_time': 0,    # 更新状态的时间
        'last_loop_time': 0,        # 上一次主循环的时间
        'loop_delay': 0,            # 循环延迟
        'start_time': time.time()   # 起始时间
    }

    # 初始化每个交易币种的数据
    for symbol in Info['trade_symbols']:

        Info['account'][symbol] = {
            'init_balance': 0,  # 初始余额
            'margin_balance': 0,  # 保证金余额
            'margin_used': 0,  # 已用保证金
            'margin_free': 0,  # 可用保证金
            'profit': 0,  # 总收益
            'profit_rate': 0,  # 收益率
            'unrealised_profit': 0,  # 未实现收益
        } # 初始化账户信息,初始余额为0

        Info['ticker'][symbol] = {'last': 0, 'ask': 0, 'bid': 0}  # 行情
        Info['order'][symbol] = {                                 # 订单信息
            'buy': {'id': 0, 'price': 0, 'amount': 0},
            'sell': {'id': 0, 'price': 0, 'amount': 0}
        }
        Info['position'][symbol] = {                              # 仓位信息
            'amount': 0, 'hold_price': 0, 'unrealised_profit': 0, 'open_time': 0, 'value': 0
        }
        Info['precision'][symbol] = {}  # 精度信息初始化为空
        Info['position'][symbol] = {                              # 仓位信息
            'amount': 0, 'hold_price': 0, 'unrealised_profit': 0, 'open_time': 0, 'value': 0
        }

# 获取精度信息
def GetPrecision():
    # 获取交易所的市场信息

    # 回测系统需要获取行情信息后, 才能获取市场信息,实盘系统可忽略
    for symbol in Info['trade_symbols']:
         curcontract = symbol + '_' + QUOTO + '.swap'
         exchange.GetTicker(curcontract) 
        
    exchange_info = exchange.GetMarkets()
    
    # 遍历市场中的所有交易对
    for pair, data in exchange_info.items():
        symbol = pair.split('_')[0]  # 永续合约交易对的格式为 BTC_USDT.swap
        
        # 检查该交易对是否为我们要交易的币种,基础货币是否匹配,且是永续合约
        if symbol in Info['trade_symbols'] and pair.split('.')[0].endswith(Info['base_coin']) and pair.endswith('swap'):
            # 获取该交易对的精度信息
            Info['precision'][symbol] = {
                'tick_size': data['TickSize'],                  # 价格精度
                'amount_size': data['AmountSize'],              # 数量精度
                'price_precision': data['PricePrecision'],      # 价格小数位精度
                'amount_precision': data['AmountPrecision'],    # 数量小数位精度
                'min_qty': data['MinQty'],                      # 最小下单数量
                'max_qty': data['MaxQty'],                      # 最大下单数量
                'min_notional': data['MinNotional'],            # 最小交易额
                'ctVal': data['CtVal']                          # 合约价值,如1张代表100USD
            }

            Log(f"初始化币种信息: {symbol}, "
                 f"价格精度: {Info['precision'][symbol]['tick_size']}, "
                 f"数量精度: {Info['precision'][symbol]['amount_size']}, "
                 f"价格小数位精度: {Info['precision'][symbol]['price_precision']}, "
                 f"数量小数位精度: {Info['precision'][symbol]['amount_precision']}, "
                 f"最小下单数量: {Info['precision'][symbol]['min_qty']}, "
                 f"最大下单数量: {Info['precision'][symbol]['max_qty']}, "
                 f"最小交易额: {Info['precision'][symbol]['min_notional']}, "
                 f"合约价值(张): {Info['precision'][symbol]['ctVal']}")


# 更新行情信息
def UpdateTicker():
    
    # 记录当前更新时间戳
    Info['time']['update_ticker_time'] = time.time() * 1000 # 使用time.time()获取当前时间的时间戳
    
    # 遍历获取到的行情数据
    for symbol in Info['trade_symbols']:
        
        curcontract = symbol + '_' + QUOTO + '.swap'
        data = exchange.GetTicker(curcontract)
        
        if not data:
            Log("获取行情失败", GetLastError())
            return
        
        # 更新行情的卖出价、买入价和最后成交价
        Info['ticker'][symbol]['ask'] = float(data['Sell'])  # 卖出价
        Info['ticker'][symbol]['bid'] = float(data['Buy'])   # 买入价
        Info['ticker'][symbol]['last'] = float(data['Last']) # 最后成交价


# 更新账户信息
def UpdateAccount():
    # 遍历所有交易币种,更新账户信息
    for symbol in Info['trade_symbols']:    
        curcontract = symbol + '_' + QUOTO  # 拼接币种和报价货币,形成合约标识
        exchange.SetCurrency(curcontract)  # 设置当前合约
        
        # 获取账户信息
        account = exchange.GetAccount()
        
        # 如果账户信息获取失败,记录日志并结束函数
        if account is None:
            Log(curcontract, ": 更新账户失败")
            return
        
        # 计算账户的关键信息
        # 'Stocks': 可用余额, 'FrozenStocks': 冻结余额, 'Equity': 总权益, 'UPnL': 未实现盈亏, 区别U本位(Balance)
        Info['account'][symbol]['margin_used'] = account['Equity'] - account['Stocks']  # 已用保证金
        Info['account'][symbol]['margin_balance'] = account['Equity']  # 当前账户总权益
        Info['account'][symbol]['margin_free'] = account['Stocks']  # 当前可用余额
        Info['account'][symbol]['unrealised_profit'] = account['UPnL']  # 未实现盈亏

        # 从全局存储中读取账户初始余额字典
        accDict = _G("init_balance")
        if accDict is None:
            accDict = {}  # 如果全局存储为空,则初始化为空字典

        # 初始化账户初始余额(仅在第一次运行时设置)
        if not Info['account'][symbol]['init_balance']:
            # 如果全局存储有记录且余额大于0,则使用该记录作为初始余额
            if accDict and accDict.get(symbol) and accDict[symbol] > 0:
                Info['account'][symbol]['init_balance'] = round(accDict[symbol], 2)
            else:
                # 否则,将当前保证金余额作为初始余额
                Info['account'][symbol]['init_balance'] = Info['account'][symbol]['margin_balance'] * Info['ticker'][symbol]['last']
                accDict[symbol] = Info['account'][symbol]['init_balance']  # 更新全局存储
                _G("init_balance", accDict)

        # 计算账户利润及利润率
        Info['account'][symbol]['profit'] = round(Info['account'][symbol]['margin_balance'] * Info['ticker'][symbol]['last'] - Info['account'][symbol]['init_balance'], 2)  # 当前利润
        Info['account'][symbol]['profit_rate'] = round((100 * Info['account'][symbol]['profit']) / Info['account'][symbol]['init_balance'], 2)  # 当前利润率(百分比)

# 更新仓位信息
def UpdatePosition():
    
    # 获取永续合约的仓位信息
    # 调用接口获取指定币种的仓位信息
    pos = exchange.GetPositions(Info['base_coin'] + ".swap")
    
    # 记录仓位信息的更新时间(单位:毫秒)
    Info['time']['update_pos_time'] = time.time() * 1000

    # 初始化仓位信息
    # 为每个交易币种创建默认的仓位信息
    position_info = {symbol: {'amount': 0, 'hold_price': 0, 'unrealised_profit': 0} for symbol in Info['trade_symbols']}

    # 遍历获取到的仓位信息,更新对应币种的仓位数据
    for data in pos:
        # 提取币种名称(Symbol)的一部分作为标识
        symbol = data['Symbol'].split("_")[0]
        
        # 过滤掉不属于指定基准币种或不在交易列表中的币种
        if not data['Symbol'].split(".")[0].endswith(Info['base_coin']) or symbol not in Info['trade_symbols']:
            continue
        
        # 更新仓位信息,区分多头和空头(通过 Type 字段)
        position_info[symbol] = {
            'amount': data['Amount'] if data['Type'] == 0 else -data['Amount'],  # 多头为正,空头为负
            'hold_price': data['Price'],  # 仓位持有价格
            'unrealised_profit': data['Profit']  # 未实现盈亏
        }

    # 初始化每个币种的仓位统计数据
    # long: 多头价值, short: 空头价值, total: 总仓位价值, leverage: 杠杆率
    for symbol in Info['trade_symbols']:
        Info['count'][symbol] = {'long': 0, 'short': 0, 'total': 0, 'leverage': 0}

    # 遍历更新后的仓位信息
    for symbol, info in position_info.items():
        # 计算仓位变化量
        deal_volume = abs(info['amount'] - Info['position'][symbol]['amount'])
        # 计算变化方向(正数为增仓,负数为减仓)
        direction = 1 if info['amount'] - Info['position'][symbol]['amount'] > 0 else -1
        
        # 如果仓位发生变化,记录成交信息
        if deal_volume:
            # 获取成交价格(买入或卖出)
            deal_price = Info['order'][symbol]['buy']['price'] if direction == 1 else Info['order'][symbol]['sell']['price']
            # 打印仓位更新日志
            Log(
                symbol,
                "仓位更新:",
                round(Info['position'][symbol]['amount'], 1),
                " -> ",
                round(info['amount'], 1),
                ", 买" if direction == 1 else ", 卖",
                ", 成交价:",
                deal_price,
                ", 成本价:",
                round(Info['position'][symbol]['hold_price'], Info['precision'][symbol]['price_precision']),
            )

        # 更新仓位信息
        Info['position'][symbol]['amount'] = info['amount']  # 更新仓位数量
        Info['position'][symbol]['hold_price'] = info['hold_price']  # 更新持仓价格
        Info['position'][symbol]['value'] = round(Info['position'][symbol]['amount'] * Info['precision'][symbol]['ctVal'], 2)  # 仓位价值
        Info['position'][symbol]['unrealised_profit'] = info['unrealised_profit']  # 更新未实现盈亏

        # 统计多头和空头仓位价值
        if Info['position'][symbol]['amount'] > 0:
            # 如果为多头,增加多头仓位价值
            Info['count'][symbol]['long'] += abs(Info['position'][symbol]['value'])
        else:
            # 如果为空头,增加空头仓位价值
            Info['count'][symbol]['short'] += abs(Info['position'][symbol]['value'])

        # 计算仓位总价值(按最新价格计算)
        Info['count'][symbol]['total'] = round((Info['count'][symbol]['long'] + Info['count'][symbol]['short']) / Info['ticker'][symbol]['last'], 2)
        # 计算杠杆率(总仓位价值与账户保证金的比值)
        Info['count'][symbol]['leverage'] = round(Info['count'][symbol]['total'] / Info['account'][symbol]['margin_balance'], 2)

# 订单函数
def Order(symbol, direction, price, amount, msg):
    pair = f"{symbol}_{Info['base_coin']}.swap"  # 构造交易对名称
    ret = exchange.CreateOrder(pair, direction, price, amount, msg)  # 执行下单操作
    
    # 判断是否下单成功
    if ret:
        Info['order'][symbol][direction]['id'] = ret  # 记录订单ID
        Info['order'][symbol][direction]['price'] = price  # 记录下单价格
        Log('记录下单价格:', price, direction, Info['order'][symbol][direction]['price'], )
    else:
        Log(f"{symbol} {direction} {price} {amount} 下单异常")  # 输出异常信息

# 交易函数
def Trade(symbol, direction, price, amount, msg):
    
    # 根据最小价格变动调整价格
    price = round(price - (price % Info['precision'][symbol]['tick_size']), Info['precision'][symbol]['price_precision'])
    
    # 计算调整后的交易数量
    amount = amount / Info['precision'][symbol]['ctVal']  # 计算真实合约数量
    amount = round(amount - (amount % Info['precision'][symbol]['amount_size']), Info['precision'][symbol]['amount_precision'])

    # 限制最大交易数量
    if Info['precision'][symbol]['max_qty'] > 0:
        amount = min(amount, Info['precision'][symbol]['max_qty'])
    
    new_order = False
    
    # 如果新价格与之前的订单价格差异大于0.0001,则重新下单

    if Info['order'][symbol][direction]['price'] > 0 and abs(price - Info['order'][symbol][direction]['price']) / price > 0.0001:
        Log('已持订单,订单价格发生变化')
        new_order = True
    
    # 如果交易数量为0或者当前订单ID为0,则撤单
    if amount <= 0 or Info['order'][symbol][direction]['id'] == 0:
        Log('新订单产生')
        new_order = True
    
    # 如果需要新订单
    if new_order:
        # 如果有原有订单,撤销它
        if Info['order'][symbol][direction]['id'] != 0:
            exchange.CancelOrder(Info['order'][symbol][direction]['id'])
            Info['order'][symbol][direction]['id'] = 0
            Info['order'][symbol][direction]['price'] = 0
            Log('撤单成功:', symbol)
        
        
        # 如果更新仓位或ticker的延迟太高,则不下单
        #if (time.time() * 1000 - Info['time']['update_pos_time'] > 2 * Info['interval'] * 1000 or 
        #    time.time() * 1000 - Info['time']['update_ticker_time'] > 2 * Info['interval'] * 1000):
        #    Log('仓位更新时间', time.time() * 1000, Info['time']['update_pos_time'], time.time() * 1000 - Info['time']['update_pos_time'])
        #    Log('Ticker更新时间', time.time() * 1000, Info['time']['update_ticker_time'], time.time() * 1000 - Info['time']['update_ticker_time'])
        #    Log('延迟过高')
        #    return
        
        # 如果订单金额或数量过低,不执行下单操作
        if price * amount <= Info['precision'][symbol]['min_notional'] or amount < Info['precision'][symbol]['min_qty']:
            Log(f"{symbol} 下单量太低", price * amount)
            return
        
        # 执行下单操作
        Log('order下单:', symbol)
        Order(symbol, direction, price, amount, msg)


# 更新状态函数
def UpdateStatus():
    
    # 更新状态时间
    Info['time']['update_status_time'] = time.time() * 1000

    # 账户信息表格
    table1 = {
        "type": "table",
        "title": "账户信息",
        "cols": [
            "币种", "初始权益", "实时权益", "可用余额", 
            "总收益", "收益率"
        ],
        "rows": [],
    }

    # 遍历每个交易对,填充交易对信息
    for symbol in Info['trade_symbols']:
        table1['rows'].append([
            symbol,  # 币种
            round(Info['account'][symbol]['init_balance'], 2),  # 初始权益
            round(Info['account'][symbol]['margin_balance'] *  Info['ticker'][symbol]['last'], 2),  # 实时权益
            round(Info['account'][symbol]['margin_free'] *  Info['ticker'][symbol]['last'], 2), # 可用余额
            round(Info['account'][symbol]['profit'], 2),  # 总收益
            str(Info['account'][symbol]['profit_rate']) + "%"  # 收益率
        ])

    # 交易对信息表格
    table2 = {
        "type": "table",
        "title": "交易对信息",
        "cols": [
            "币种", "方向", "数量", "持仓价格", "持仓价值", 
            "现价"
        ],
        "rows": [],
    }

    # 遍历每个交易对,填充交易对信息
    for symbol in Info['trade_symbols']:
        table2['rows'].append([
            symbol,  # 币种
            "LONG" if Info['position'][symbol]['amount'] > 0 else "SHORT",  # 方向
            Info['position'][symbol]['amount'],  # 数量
            round(Info['position'][symbol]['hold_price'], Info['precision'][symbol]['price_precision']),  # 持仓价格
            round(Info['position'][symbol]['value'], 2),  # 持仓价值
            round(Info['ticker'][symbol]['last'], Info['precision'][symbol]['price_precision'])  # 现价
        ])

    # 输出状态日志
    LogStatus(
        f"初始化时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(Info['time']['start_time']))}\n",
        f"`{json.dumps(table1)}`\n" + f"`{json.dumps(table2)}`\n",
        f"循环延时: {str(Info['time']['loop_delay']) + 'ms'}\n",
        f"最后执行时间: {_D()}\n"
    )

    totalprofit = 0
    for symbol in Info['trade_symbols']:
        totalprofit += Info['account'][symbol]['profit']  

    # 每10秒钟更新一次账户信息
    if time.time() * 1000 - Info['time']['update_profit_time'] > 10 * 1000:
        LogProfit(round(totalprofit, 3), '&')  # 输出收益日志
        Info['time']['update_profit_time'] = time.time() * 1000  # 更新收益时间


def MakeOrder():
    
    # 遍历所有交易对
    for symbol in Info['trade_symbols']:
        
        availBal = Info['account'][symbol]['margin_free'] *  Info['ticker'][symbol]['last'] #可用金额
        sell_price = Info['ticker'][symbol]['ask'] #卖出价格

        if availBal > ICEMONEY: #可用金额大于冰山挂单数量,起始建仓
            Trade(symbol, "sell", sell_price, ICEMONEY, symbol)  # 执行买入操作
        elif availBal > ROLLINGNUM * Info['precision'][symbol]['ctVal']: #可用盈利大于滚动加仓数量,逐步下单
            Trade(symbol, "sell", sell_price, ROLLINGNUM * Info['precision'][symbol]['ctVal'], symbol)  # 执行买入操作

def OnTick():
         
    # 更新市场行情信息
    UpdateTicker()

    # 更新账户信息
    UpdateAccount()
     
    # 更新持仓信息
    UpdatePosition()

    # 执行下单操作
    MakeOrder()
    
    # 更新状态信息
    UpdateStatus()

def main():

    LogReset(0)                # 日志重置
    Log('策略起始#0000ff')
    # 初始化信息
    InitInfo()                 # 初始化币种信息
    GetPrecision()             # 获取精度信息
    exchange.SetMarginLevel(1) # 设置杠杆倍数
    
    while True:  # 无限循环
        
        loop_start_time = time.time() * 1000  # 获取当前时间(毫秒)
        
        # 检查上次循环时间与设定的间隔时间是否已过
        if time.time() * 1000 - Info['time']['last_loop_time'] > Info['interval'] * 1000:
            
            OnTick()  # 调用 OnTick 函数

            # 更新最后一次循环时间
            Info['time']['last_loop_time'] = time.time() * 1000
            # 计算当前循环的延迟时间
            Info['time']['loop_delay'] = time.time() * 1000 - loop_start_time
        
        # 暂停5毫秒,避免过度消耗CPU资源
        Sleep(5)

1. 全局变量与功能描述

策略以简单变量定义为基础,通过模块化功能实现滚动加仓与状态监控。

  • SYMBOLS:交易币种(如”BTC”)。
  • QUOTO:基础货币,币本位通常为USD。
  • INTERVAL:策略循环的时间间隔。
  • ICEMONEY:初始下单金额,用于冰山订单。
  • ROLLINGNUM:滚动盈利后的加仓数量。

2. 核心功能模块

以下为各功能模块的简要说明:

  1. 初始化函数 (InitInfo)
    用于加载交易币种信息和初始账户状态。

  2. 精度信息获取 (GetPrecision)
    通过交易所 API 获取每个交易对的最小下单单位和合约价值。

  3. 行情更新 (UpdateTicker)
    定期获取当前市场行情,包括买一价、卖一价和最新价。

  4. 账户与持仓更新 (UpdateAccount, UpdatePosition)
    实时同步账户余额、持仓信息,为交易决策提供依据。

  5. 交易与订单管理 (Order, Trade)
    统一下单与执行功能,支持限价单与市价单操作。

  6. 状态监控 (UpdateStatus)
    跟踪策略运行状态,包括当前盈亏和未成交订单。

3. 策略下单逻辑 (MakeOrder)

策略的核心在于初始建仓(冰山建仓)和滚动加仓(受滚动加仓参数 ROLLINGNUM 控制)。以下是具体逻辑的详细说明:

  1. 初始建仓检查:验证账户余额是否大于初始冰山建仓金额(ICEMONEY)。若满足条件,逐步分批开仓,以减少因一次性建仓而产生的过大价格滑点。
  2. 滚动加仓策略:根据账户的盈利情况(资金费率收益),合理增加仓位,通过滚动加仓优化收益,从而提升策略的总体盈利能力。

币本位做空资费套利策略的量化实现

根据策略回测结果,可以看到策略在多个市场周期中获取了稳定的收益,尤其是在资金费率长期为正的牛市中表现尤为突出。因此,这一策略在一定程度上非常适合大资金管理。其低波动性、稳定收益的特点,能够满足大资金对风险控制和收益稳定性的双重需求。同时,策略采用1倍杠杆,避免了高杠杆带来的爆仓风险,进一步增强了其安全性。

策略优缺点分析

优点

  1. 策略简单易执行
    逻辑清晰,无需复杂计算,适合对冲基金或个人投资者的大资金管理。

  2. 抗风险能力强
    一倍杠杆规避爆仓风险,且币本位的特性保证了美元价值恒定,无惧市场大幅波动。

  3. 收益稳定
    牛市中资金费率收益较高,延展性强,可利用所得币种进行其他投资(如质押)。

缺点

  1. 对币种和行情依赖较强
    这点是硬伤,当一个币种资金费率长期为正时(空头支付多头),比如BNB合约,使用【数据探索】模块选择BNB币本位合约,可以看到由于交易所的设置,资金费率累计为负值。另外当大周期长期处于熊市(2022年),资金费率为负时,收益可能无法覆盖策略运行成本。

  1. 策略刚性不足
    当前策略未包含动态调整逻辑,如牛熊市切换时缺乏应对措施(如止损或止盈策略)。

  2. 盈利空间有限
    适合“躺平”收取资费,无法捕捉大波动行情中的超额收益,某些情况下收益率不如传统债券基金。

小结

币本位一倍做空资费套利策略充分利用了币本位合约的特殊性质,为资金保值与稳定增值提供了一个低风险的方案。然而,面对不同的市场阶段,策略需要具备一定的灵活性,以应对资金费率的波动和潜在风险。未来的优化方向可以包括:

  1. 增加牛熊市场切换的动态管理措施;
  2. 根据资金费率的变化情况,调整加仓节奏。

但从本质上讲,该策略存在一定的矛盾。策略的初始假设是数字货币市场长期处于牛市状态,通过做空币本位合约来保持稳定现金流并获取资金费率收益。然而,如果我们假设市场处于长期牛市,为什么不直接选择囤币(hlod),静待比特币的稳步上涨呢?当然,对于追求稳定收益的策略而言,该策略依然是可靠的。本文的目标主要是介绍这一设计思路,并提供一种量化投资的策略框架,大家可以在此基础上进行进一步完善和优化。希望该策略能够成为数字货币量化投资者的有效工具。

参考来源

FMZ升级后,如何快捷构建通用多币种交易策略

算法交易实战日记(十九) —— 套利策略详解系列(1):币本位做空资费套利

温馨提示:投资有风险,入市需谨慎。建议投资者在充分了解策略逻辑与风险的基础上,根据自身风险承受能力合理配置资金,并严格控制仓位与杠杆。本文内容仅供参考,不构成任何投资建议。

更多内容