十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AutoHedge实战:用自动对冲系统把风险敞口变成可量化参数

AutoHedge实战:用自动对冲系统把风险敞口变成可量化参数 做过交易的人应该都有这种感觉手里拿着现货仓位明明中长期逻辑没坏行情却天天来回扇耳光。止损吧容易被洗下车扛着吧真来一根大阴线又让人整晚睡不着。我写 AutoHedge 就是想解决这个矛盾。它不是一个理财App也不是什么收费信号群而是一套我自己搭的自动化对冲引擎——不预测涨跌只负责在你账面出现风险敞口的时候自动开一张反向单把波动压到你晚上能安心睡觉的水平。这套东西适合谁手里长期持有现货的人、做趋势但不想被回撤打乱节奏的人、以及像我一样“手贱”总想手动操作结果越操作越亏的人。如果你是纯短线日内玩家那直接划走对冲不是你的菜。AutoHedge 的核心逻辑很朴素在不放弃投资收益的前提下用一小部分资金买“保险”让组合的净值曲线尽量平滑。这篇文章我会把整套系统的设计思路、策略参数、关键代码、踩坑记录都写出来代码可以直接拿去改策略逻辑也能复制到自己的账户上做验证。重点不是给你一个“必赚”方案而是把自动化对冲从“玄学”变成“数学”。1. 为什么我非要搞一套自动对冲系统1.1 对冲的本质不是预测是买保险很多人一听“对冲”就以为是判断要跌了所以做空其实完全不是一回事。对冲的核心是把你不愿意承担的那部分风险转移出去让它在数学上和你手里的多头敞口互相抵消。举个最直白的例子你有 1 个比特币的现货怕短期下跌于是开 0.5 个比特币的空单。如果币价跌了 10%现货亏 10%空单却赚了大约 10% —— 注意这里空单的仓位是对冲比例所决定的部分在保证金杠杆下收益会被放大所以实际上是用 0.5 倍的空头杠杆刚好把 50% 的现货敞口保护住。这个思路就像一个买了房子的家庭明明觉得小区地段不错但还是会买一份家财险。买保险不是因为预测家里一定会失火而是因为“万一失火”这个结果你承受不起。AutoHedge 做的就是这个保险经纪人的工作它不管市场涨还是跌只管你当前的风险敞口有多大然后按你设定的规则自动把敞口压回安全线内。1.2 手动对冲最大的坑你不是输给市场是输给人性可能有人会说就这么点事我每天看一眼行情跌了就开空涨了就平掉手动操作不就行了说这种话的人大概率没有经历过连续暴跌时的心理状态。我自己就踩过这个坑。2023 年有一次波动率突然拉升市场半小时内跌了 4%。我当时盯着屏幕手指悬在空单按钮上脑子里想的不是“该开仓”而是“再等等可能有反弹”。然后价格继续跌我又想“已经跌这么多了现在开空追不上万一反弹就两头挨打”。结果等了一晚上现货亏了近 8%对冲单一张都没开出去。后来我就想明白了——人在面对浮亏时的决策能力和坐在电脑前复盘时的决策能力完全不是同一个水平。手动对冲的问题不只是慢而是你会给自己找无数个“再等等”的理由。AutoHedge 把这件事从“临场决策”变成了“事前编程”。你在冷静的时候把规则写死执行的时候机器不管你的情绪跌到阈值就开空涨回安全区就平空没有任何犹豫和借口。这对个人交易者来说可能是自动化最大的价值——不是赚更多钱而是防止你在关键时刻做出蠢事。1.3 AutoHedge 到底解决了哪几个具体问题简单梳理一下我最初的目标就四条第一把巨额回撤控制在预设范围内比如组合最大回撤不超过 15%第二在趋势仍然存在的前提下不用因为短期波动剧烈而交出筹码第三把对冲的仓位计算、下单执行、再平衡调仓全部自动化不需要人盯盘第四把每一笔对冲的成本和收益记录清楚能复盘、能优化而不是凭感觉。这四个目标决定了系统需要具备三个能力持仓数据和行情数据的实时计算能力、对冲比例和触发条件的策略决策能力、以及 API 自动下单的执行能力。2. AutoHedge 的整体设计思路与核心模块2.1 系统架构三层各管各的不搞耦合这套系统我前前后后重构过三次最后稳定运行的架构是标准的三层模型如果你自己写交易机器人也建议至少照这个思路来分不要图省事把所有逻辑塞在一个脚本里。第一层是数据层。负责定时拉取现货持仓、期货账户余额、行情 ticker、资金费率、K线等数据。我用的 Python CCXT 库统一封装了交易所 API很方便后面代码部分会细说。第二层是策略层。拿到数据之后算出当前敞口、波动率、需要开多少仓、什么时候调仓这一层只做计算和决策不直接碰交易所。第三层是执行层。根据策略层的结果调用下单接口、管理挂单、处理重试和异常。三层之间用简单的函数调用或者消息队列衔接我本地的 MVP 版本是直接函数调用后来加了 Redis 队列用于异步告警但核心逻辑不变。这个结构的最大好处是可以单独调试。比如我发现下单有问题只在执行层改不需要动策略逻辑想换一个对冲比例的计算方法只改策略层不影响数据获取和下单。如果全混在一个脚本里改一个功能经常搞得另外两个功能也跑不了。2.2 敞口计算先搞清楚你手里到底压了多少风险很多人以为“敞口”就是现货市值这是最大的误区。敞口应该把杠杆、保证金、未实现盈亏全部算进去而且不同账户之间如果不打通还要做汇总。我的计算方法比较简单把所有现货持仓按当前市场价格折成法币或本位币价值再减去期货账户里所有反向头寸的“名义价值”。公式可以写成总敞口 现货市值 - 期货空单名义价值举个例子你持有 1 BTC 现货价格 60000 USDT同时在合约账户开了 50000 USDT 名义价值的 BTC/USDT 永续空单。那么总敞口就是 60000 - 50000 10000 USDT也就是说市场如果涨跌 1%你整体资金大约会波动 100 USDT。这个数字才是你真正暴露在风险中的金额。但这里有个坑——跨账户统计时你的现货账户和合约账户往往是分开的。如果只盯着合约账户看你看到的是空单浮盈完全不知道自己现货那边亏了多少。我在第一版就因为这个吃了大亏只接了合约 API算出来的敞口是负数以为自己在对冲实际上整体还是满仓多头。所以 AutoHedge 第一步就是把两个账户的数据拉到一起做计算少一个都不行。2.3 对冲比例决策一把梭还是留部分敞口对冲比例是整套系统里最有讲究的参数直接决定了策略是偏防守还是偏进攻。最常见的做法有三种我根据自己的经验都试过。第一种是固定比例比如永远对冲 50% 的头寸。优点是非常简单几乎不会有策略错误缺点是太死板趋势行情里白白砍掉一半利润。第二种是区间动态比例根据波动率调整——波动率越高对冲比例越大波动率低的时候对冲比例就调低甚至归零。第三种是目标敞口法不管市场怎么走始终把总敞口控制在一个固定范围内比如始终让总敞口不超过总资产的 20%。我最推荐第三种思路因为它最符合“对冲是买保险”的定位。你问自己一个问题我现在能接受组合最多亏多少如果总资产 10 万你能接受一天最多亏 5000那总敞口控制在 5 万以内就够了剩下的头寸全部拿空单保掉。这样无论市场涨跌你的实际波动都是有限的。AutoHedge 里我默认启用的就是目标敞口法并且把目标敞口设置成一个区间比如 10%-20% 之间避免频繁触发调仓产生太多摩擦成本。2.4 执行与再平衡逻辑宁可慢一点也不要频繁乱动执行层最容易犯的毛病就是过度交易。我见过很多人做动态对冲波动率一改变就立刻加仓减仓结果手续费和滑点把整个策略的收益都吃光了。这里要用到一个做市商和机构经常挂在嘴边的词——再平衡缓冲带。简单说就是只有当敞口偏离目标值超过一定阈值时才动手。比如目标敞口区间是 10%-20%如果实际敞口是 18%虽然离 20% 很近但还没越线不操作一旦突破 20%就减仓到 15%这个“减仓到 15%”不是回到目标上限而是回到区间中下部留出足够缓冲避免刚操作完又得反向操作。这个逻辑和恒温器有点像房间温度低于设定下限才开暖气而且一次性加热到比下限略高的位置而不是一到下限就开关反复跳。AutoHedge 默认的参数是区间为 10%-20%越线后恢复到 12%。这样通常一周最多触发两三次调仓手续费成本非常可控。3. 策略逻辑与关键参数设置3.1 波动率怎么算别用直觉用数字说话波动率是这个系统里最核心的输入信号之一。我建议不要用单根 K 线的涨跌幅来判断“行情很波动”那太滞后了。更好的做法是用历史波动率的年度化数值或者更直接一点用 ATRAverage True Range平均真实波幅来衡量最近的日内波动水平。ATR 的计算逻辑并不难它衡量的是最近 N 根 K 线的真实波动范围的平均值。真实波动范围 TR max(最高价 - 最低价, 最高价 - 昨收价, 昨收价 - 最低价)。我通常用 14 周期 ATR并且把它换算成“日波动率百分比”日波动率 ATR(14) / 当前价格 × 100%为什么用 ATR 而不是标准差因为 ATR 对跳空和极限行情更敏感而且计算简单直观做对冲的本质是防极端情况不是算正态分布里的那种平均波动。我在系统里设了一个参数hedge_vol_threshold默认值是 2%。当持仓标的的 14 日 ATR 折算出来的日波动率大于 2% 时系统会把目标敞口上限从 20% 下压到 10%小于 2% 时则维持正常区间。3.2 对冲触发条件不预测方向只管理偏离策略层最核心的函数就是判断当前到底该不该动。我把触发条件分成三类敞口越界、时间定期再平衡、极端行情熔断。敞口越界已经说过了就是当总敞口超出目标区间时触发。时间定期再平衡是兜底机制因为市场可能在短时间内没触发越界条件但缓慢变化的持仓成本结构也需要定期检查我设置为每 30 分钟跑一次全量检查每 4 小时强制做一次微调。极端行情熔断是最后一道保险——如果单根 5 分钟 K 线波动超过 5%系统会立刻把敞口压到 0等市场恢复平静再重新评估。这套触发逻辑的好处是正常行情下无人打扰极端行情下反应迅速整体交易频率不高手续费压力小。3.3 参数调优的朴素方法先暴力搜索再人工判断很多人一上来就想用机器学习调参我建议先别。AutoHedge 的可调参数其实不多核心就四个目标敞口区间上下限、越线上限后的回落目标、波动率阈值、再平衡检查周期。对这四个参数我采用的是最笨但最有效的办法——用历史数据做网格搜索然后人工盯关键行情区间看表现。比如我会固定其他三个参数只把目标敞口上限从 15%、20%、25% 各跑一遍回测比较最大回撤、年化收益、调仓次数这三个指标。表格里列出我的部分回测结果模拟数据仅展示方法目标敞口上限年化收益率最大回撤月度调仓次数总手续费占比15%18.2%-8.4%111.8%20%22.7%-12.1%81.4%25%24.9%-17.6%61.2%30%26.3%-23.5%51.0%从表格可以明显看出敞口上限越高赚得越多回撤也越大。这不是玄学是金融里最基础的风险收益交换。我最后选了 20%因为在回撤 12% 和收益 22% 的组合里心理压力比较能承受而且趋势行情里也不至于过早掉队。3.4 回测要覆盖的三种行情趋势、震荡、黑天鹅回测最容易犯的错误是只看“平均表现”我见过有人拿一年数据跑一遍年化 30%兴冲冲上实盘结果接下来一个月就遇到极端行情直接打回原形。真正靠谱的回测至少要切成三个场景分别验证单边上涨趋势、宽幅震荡、突然暴跌。单边上涨趋势里对冲系统应该做到的是“少赚一点但别踏空”如果回测出来连普通现货都没跑赢说明对冲比例设得太保守。宽幅震荡行情里系统最大的敌人是频繁调仓手续费会吃人这时候要看调仓次数是不是控制得住。暴跌行情里回撤必须在预设范围内如果系统在大跌里没能把敞口压下来说明波动率触发逻辑有致命缺陷。我建议至少在 BTC 上一次完整的牛熊周期数据上做回测至少覆盖 2019-2022 年这种包含大涨大跌的时间段。不要拿过去三个月的行情自我安慰那什么都说明不了。4. 实操搭建从零部署一套可运行的 AutoHedge4.1 技术栈选择Python 就是个人开发者的最优解个人搭量化对冲系统不建议一上来就整 C 或 Rust。你要的不是微秒级延迟而是开发效率和可维护性。我用的是 Python 3.10 CCXT pandas numpy数据库用的是 SQLite部署在云服务器上通过 systemd 守护进程保证常驻运行。CCXT 是个开源库统一封装了上百家交易所的 API也就是说你不需要为每个交易平台写单独的请求封装改一个配置参数就能切换交易所。这对做对冲非常重要因为通常你需要同时在现货和合约两个市场操作如果两个市场还不在同一个交易所CCXT 能做到一套代码多处复用省掉一大半工作量。4.2 核心代码一数据拉取与敞口计算下面这段是 AutoHedge 里最核心的敞口计算代码精简掉了异常处理保留主干逻辑。注意这里我假设你的现货和合约在同一个交易所如果跨交易所需要额外处理汇率换算我后面会在避坑部分详细说。import ccxt exchange_spot ccxt.binance({ apiKey: YOUR_SPOT_API_KEY, secret: YOUR_SPOT_API_SECRET, }) exchange_futures ccxt.binance({ apiKey: YOUR_FUTURES_API_KEY, secret: YOUR_FUTURES_API_SECRET, options: {defaultType: future}, }) SYMBOL BTC/USDT def get_total_exposure(): # 现货持仓市值 spot_balance exchange_spot.fetch_balance() spot_qty spot_balance[BTC][free] spot_balance[BTC][used] ticker exchange_spot.fetch_ticker(SYMBOL) price ticker[last] spot_value spot_qty * price # 合约持仓名义价值 futures_positions exchange_futures.fetch_positions([SYMBOL]) futures_notional 0.0 for pos in futures_positions: if pos[side] short: futures_notional pos[notional] elif pos[side] long: futures_notional - pos[notional] total_exposure spot_value - futures_notional return spot_value, futures_notional, total_exposure要特别注意的是fetch_positions返回的notional在不同交易所和不同合约类型下可能是正值也可能是负值我上面做了正负归一化处理。实际使用前一定要拿交易所的测试网跑一下数据确认字段含义否则一个正负号搞反策略方向就全反了。4.3 核心代码二策略决策与下单执行下面这段是策略决策和执行的简化版本。策略层读取敞口数据判断是否越界然后计算出目标开仓量交给执行层下单。TARGET_LOW 0.10 # 目标敞口下限 10% TARGET_HIGH 0.20 # 目标敞口上限 20% REBALANCE_TO 0.12 # 越界后回落到 12% def compute_target_hedge_ratio(total_assets, total_exposure): exposure_ratio total_exposure / total_assets if exposure_ratio TARGET_HIGH: # 需要增加空单把敞口压到 REBALANCE_TO target_exposure total_assets * REBALANCE_TO delta_notional total_exposure - target_exposure return increase_short, delta_notional elif exposure_ratio TARGET_LOW: # 敞口太低可能空单开多了需要减少空单 target_exposure total_assets * ((TARGET_LOW TARGET_HIGH) / 2) delta_notional target_exposure - total_exposure return decrease_short, delta_notional return hold, 0.0 def rebalance(): spot_value, futures_notional, total_exposure get_total_exposure() total_assets spot_value # 简化处理不统计合约账户的保证金余额 action, delta compute_target_hedge_ratio(total_assets, total_exposure) if action hold: return # 假设合约面值每张 0.001 BTC需要按价格换算下单张数 price ccxt.binance().fetch_ticker(SYMBOL)[last] amount_contracts round(delta / price / 0.001) if action increase_short: exchange_futures.create_order(SYMBOL, market, sell, amount_contracts) elif action decrease_short: exchange_futures.create_order(SYMBOL, market, buy, amount_contracts)这段代码最大的隐患是“假设合约面值每张 0.001 BTC”这个数值每个交易所、每个合约都不一样千万别抄作业抄死。正确的做法是先调exchange_futures.load_markets()然后读取合约规格里的contractSize字段再根据这个字段结算下单数量。4.4 上线前必须做的三件事第一件事用模拟盘或者交易所测试网跑至少两周。不要觉得模拟盘没意思它是唯一能让你放心验证 API 权限、字段格式、下单逻辑的地方。我第一次上线前在测试网发现币安合约的create_order参数里type必须写成market但price参数不能传None否则会报参数错误——这种坑只有跑起来才会暴露。第二件事把资金费率算进成本里。永续合约每隔几个小时就会结算一次资金费率如果你长期持有空单资金费率的方向不适合你可能每天都要付出一笔费用。AutoHedge 里我加入了一个funding_rate检查任务当资金费率绝对值高于万分之一时会触发一个提示甚至可以考虑是否提前平掉对冲仓位来避开结算时间点。第三件事设置进程守护和异常告警。我用的 systemd 服务文件启动 Python 进程配合钉钉机器人 webhook 发送告警。一旦进程退出或者 API 报错消息会直接推送到手机而不是等你上线了才发现服务器已经挂了三天。5. 常见问题与避坑记录5.1 数据不同步导致的对冲失效我在前面提到过第一版最大的坑就是没把现货和合约两个账户的数据合并导致算出来的敞口是错的。还有一个更隐蔽的问题很多交易所的行情接口有缓存ticker 价格可能延迟几秒甚至几十秒。做高频对冲的时候这种延迟是致命的但对 AutoHedge 这种“半小时检查一次”的系统延迟几秒完全不是问题。真正需要小心的是“持仓数据”和“行情数据”的时间戳不一致。有一次我发现系统开了空单之后明明敞口已经降到目标区间但下一次检查又显示敞口过高又开了一单。查了半天发现是因为合约持仓接口返回的数据是上一个区块时间点的现货数据却是实时的两个数据相差了将近一分钟导致计算出来的敞口虚高。解决办法很简单——对所有数据源只在拿到完整快照后再做计算不做边拉边算的流式处理。5.2 手续费和滑点对冲成本的隐藏杀手很多新手会对冲一次、回测一次觉得成本低到可以忽略。实际跑起来就发现如果每次调仓都用市价单手续费加滑点可能占到名义本金的 0.1% 到 0.3%。看起来不多但如果一周调仓三次一年就是 150 次累积下来接近 20% 的额外损耗直接吞掉策略的大半利润。我的解决方法有三个一是尽量用限价单把单子挂在盘口附近吃被动成交手续费能打折。二是给调仓加上“最小触发金额”门槛如果计算出需要调整的名义价值小于总资产的 1%就不动攒到下一次再一起调。三是在策略参数里加上一个“成本预估函数”每次决策时先把预估手续费和滑点算进净收益只有当净收益为正时才执行调仓。5.3 API 断连、限频与重试机制交易所 API 不是 100% 稳定的尤其在行情剧烈波动的时候限频和断连是家常便饭。AutoHedge 里我写了一个简单的重试装饰器遇到网络错误和 5xx 错误时最多重试三次每次间隔递增。但要注意对于下单这种非幂等操作重试务必谨慎——第一笔单可能已经成交了只是返回超时你再重试就会重复下单。所以我在下单前会为每个订单生成一个唯一的clientOrderId重试时带同一个 ID。交易所通常会根据这个 ID 做幂等处理避免重复成交。这个细节是我第一次实盘跑的时候吃了一次双倍空单的亏才学乖的现在写进代码里了。5.4 策略失效没有万能的对冲系统最后必须说一句实话任何对冲系统都有失效的时候。最典型的情况是市场出现急涨行情现货疯涨你的空单虽然亏损有限但整体收益被对冲掉一部分导致你跑输大盘。另一个情况是震荡行情里频繁触发再平衡手续费越烧越多回撤却没减多少。AutoHedge 不能帮你避免这些问题它只能帮你把问题量化、把执行纪律化。我自己的处理方式是给系统加了一个“策略开关”面板隔一段时间就看看近一个月的实际调仓记录和收益归因如果发现对冲成本明显超过它带来的回撤保护价值就果断先把策略暂停重新调参或者换一套逻辑。对冲是手段不是目的目的是你晚上能睡好长期能留在市场里。我用这套系统跑了小半年最大的体会不是“赚了多少钱”而是终于治好了自己盯盘和乱操作的毛病。现在我的持仓在大部分时候都是“开了 AutoHedge 就去做别的事”只在每天固定的时间点看一眼系统日志和告警。最后再分享一个小技巧对冲系统上线后别急着追求参数最优先用一个自己最舒服的保守参数跑一两个月慢慢观察真实的手续费、滑点和触发频率然后根据实际数据再一点点放松参数。市场不会因为你参数跑得快就多给你钱但一定会因为你撑过了低谷而奖励你。
返回列表