c4ac3a8fe0
- 创建 OnChainWsService,支持通过 Polygon RPC eth_subscribe 实时监听链上交易 - 实现并行监控策略:链上 WS 和轮询同时运行,哪个数据先返回用哪个 - 支持通过 eth_unsubscribe 取消单个 Leader 的订阅 - 在 CopyOrderTrackingService 中添加 Mutex 保证线程安全 - 实现链上交易解析逻辑(USDC Transfer + ERC1155 Transfer) - 实现 Gamma API 元数据补齐逻辑 - 优化 WebSocket 连接管理:只创建一个连接,没有跟单配置时自动取消 - 跟单配置生效/失效时及时更新 WebSocket 订阅 - 使用 Gson 替换所有 JSON 解析,JsonRpcResponse.result 使用 JsonElement 类型 - 添加相关文档:copy-trading-logic-summary.md 和 copy-trading-monitor-strategy.md
8.2 KiB
8.2 KiB
跟单买卖逻辑简易文档
一、跟单信号检测
1.1 监控方式
系统使用轮询方式监控 Leader 的交易活动:
- 数据源:Polymarket Data API 的
/activity接口 - 轮询间隔:默认 2 秒(可配置)
- 查询参数:
user: Leader 钱包地址type:["TRADE"](只查询交易类型)limit: 100(每次最多查询 100 条)sortBy:TIMESTAMPsortDirection:DESC(按时间戳降序,最新的在前)
1.2 增量检测机制
通过 diff 算法检测新增交易:
-
首次轮询:
- 查询最近 100 条交易
- 缓存所有交易 ID(不处理)
- 标记首次轮询完成
-
后续轮询:
- 查询最近 100 条交易
- 与缓存的交易 ID 集合进行 diff
- 找出新增的交易 ID
- 处理新增交易
- 更新缓存(添加新增的交易 ID)
-
去重机制:
- 使用
leaderId + tradeId作为唯一标识 - 在
processed_trade表中记录已处理的交易 - 避免重复处理同一笔交易
- 使用
1.3 交易数据转换
从 UserActivityResponse 转换为 TradeResponse:
TradeResponse(
id = activity.transactionHash, // 交易ID(用于去重)
market = activity.conditionId, // 市场ID
side = activity.side, // "BUY" 或 "SELL"
price = activity.price, // 交易价格
size = activity.size, // 交易数量
timestamp = activity.timestamp, // 时间戳(秒)
user = activity.proxyWallet, // 用户钱包地址
outcomeIndex = activity.outcomeIndex, // 结果索引(0=第一个outcome,1=第二个outcome)
outcome = activity.outcome // 结果名称
)
1.4 信号触发流程
轮询任务启动
↓
定期轮询所有 Leader(每 2 秒)
↓
查询 Leader 活动(/activity 接口)
↓
转换为 TradeResponse
↓
diff 检测新增交易
↓
调用 processTrade() 处理交易
↓
根据 side 字段判断:
- "BUY" → processBuyTrade()
- "SELL" → processSellTrade()
二、订单构建
2.1 买入订单构建流程
2.1.1 前置检查
-
查找跟单关系:
- 查询所有启用且支持该 Leader 的跟单配置
- 验证账户 API 凭证是否配置
- 验证账户是否启用
-
获取 Token ID:
- 使用
outcomeIndex和market获取 tokenId - 支持多元市场(不限于 YES/NO)
- 使用
-
计算买入数量:
- RATIO 模式:
买入数量 = Leader 数量 × 跟单比例 - FIXED 模式:
买入数量 = 固定金额 / 买入价格
- RATIO 模式:
-
过滤条件检查:
- 价格区间检查
- 仓位限制检查
- 市场分类检查
- 订单簿检查(获取最佳卖单价格)
-
价格调整:
- 应用价格容忍度(
priceTolerance) - 买入价格 = Leader 价格 × (1 + 容忍度)
- 确保调整后的价格不低于最佳卖单价格
- 应用价格容忍度(
2.1.2 订单签名
使用 OrderSigningService.createAndSignOrder() 创建并签名订单:
-
计算订单金额:
- BUY 订单:
makerAmount = price × size(USDC 金额,最多 2 位小数)takerAmount = size(shares 数量,最多 4 位小数)
- 转换为 wei(6 位小数)
- BUY 订单:
-
生成订单参数:
salt: 时间戳(毫秒)maker: 代理钱包地址(proxyAddress)signer: 从私钥推导的签名地址taker: 零地址(0x0000...)tokenId: 从 outcomeIndex 获取makerAmount: 计算出的 maker 金额(wei)takerAmount: 计算出的 taker 金额(wei)expiration: "0"(永不过期)nonce: "0"feeRateBps: "0"side: "BUY"signatureType: 2(Browser Wallet)
-
EIP-712 签名:
- 编码域分隔符(Exchange Contract + Chain ID)
- 编码订单消息哈希
- 计算结构化数据哈希
- 使用私钥签名(r + s + v)
2.1.3 创建订单请求
构建 NewOrderRequest:
NewOrderRequest(
order = signedOrder, // 签名的订单对象
owner = account.apiKey, // API Key
orderType = "FAK", // Fill-And-Kill(允许部分成交,未成交部分立即取消)
deferExec = false // 立即执行
)
2.1.4 提交订单
-
创建 CLOB API 客户端(带认证):
- 使用账户的 API Key、Secret、Passphrase
- 解密 API 凭证
-
调用 API 创建订单:
POST /orders- 带重试机制(最多重试 2 次)
- 每次重试都重新生成 salt 并重新签名
-
记录订单跟踪:
- 保存到
copy_order_tracking表 - 记录买入订单 ID、数量、价格等信息
- 状态:
filled
- 保存到
2.2 卖出订单构建流程
2.2.1 前置检查
-
查找跟单关系:
- 查询所有启用且支持该 Leader 的跟单配置
- 验证是否支持卖出(
supportSell = true)
-
计算需要匹配的数量:
需要匹配数量 = Leader 卖出数量 × 跟单比例
-
查找未匹配的买入订单:
- 使用
outcomeIndex匹配(支持多元市场) - 按 FIFO 顺序(先进先出)
- 查询
copy_order_tracking表中未匹配的订单
- 使用
-
计算实际可卖出数量:
- 按 FIFO 顺序匹配
- 支持部分匹配(一个买入订单可以被多次卖出匹配)
2.2.2 价格计算
-
优先使用订单簿 bestBid:
- 查询订单簿(
getOrderbookByTokenId) - 获取最佳买单价格(bestBid)
- 使用 bestBid 作为卖出价格
- 查询订单簿(
-
备选方案:
- 如果获取订单簿失败,使用 Leader 价格
- 卖出价格 = Leader 价格 × 0.9(固定按 90% 计算)
2.2.3 订单签名
与买入订单类似,但:
side: "SELL"- SELL 订单金额计算:
makerAmount = size(shares 数量,最多 4 位小数)takerAmount = price × size(USDC 金额,使用原始价格计算)
2.2.4 创建订单请求
与买入订单相同:
orderType: "FAK"deferExec: false
2.2.5 提交订单
-
调用 API 创建卖出订单(带重试机制)
-
更新买入订单状态:
- 更新
copy_order_tracking表中的remainingQuantity - 如果完全匹配,状态更新为
fully_matched - 如果部分匹配,状态更新为
partially_matched
- 更新
-
记录匹配关系:
- 保存到
sell_match_record表(卖出匹配记录) - 保存到
sell_match_detail表(匹配明细,包含盈亏计算)
- 保存到
三、关键配置
3.1 跟单配置参数
copyMode: 跟单模式("RATIO" 或 "FIXED")copyRatio: 跟单比例(RATIO 模式)fixedAmount: 固定金额(FIXED 模式)priceTolerance: 价格容忍度(买入时使用)supportSell: 是否支持卖出enabled: 是否启用
3.2 订单类型
- FAK (Fill-And-Kill):
- 允许部分成交
- 未成交部分立即取消
- 快速响应 Leader 交易,避免订单长期挂单导致价格不匹配
3.3 重试机制
- 最多重试次数:2 次(首次 + 1 次重试)
- 重试延迟:3 秒
- 重试策略:每次重试都重新生成 salt 并重新签名,确保签名唯一性
四、数据流向
Leader 交易(链上)
↓
Polymarket Data API (/activity)
↓
轮询服务(CopyTradingPollingService)
↓
交易处理服务(CopyOrderTrackingService)
↓
订单签名服务(OrderSigningService)
↓
CLOB API (POST /orders)
↓
订单跟踪表(copy_order_tracking)
五、注意事项
-
去重机制:
- 使用
leaderId + tradeId作为唯一标识 - 在
processed_trade表中记录已处理的交易 - 避免重复处理同一笔交易
- 使用
-
价格调整:
- 买入时应用价格容忍度(提高买入价格)
- 卖出时优先使用订单簿 bestBid,失败则使用 Leader 价格的 90%
-
订单簿检查:
- 买入前检查订单簿中是否有可匹配的卖单
- 确保调整后的买入价格不低于最佳卖单价格
-
FIFO 匹配:
- 卖出时按买入时间顺序匹配(先进先出)
- 支持部分匹配
-
错误处理:
- 订单创建失败时记录到
failed_trade表 - 支持重试机制(最多 2 次)
- 发送失败通知(如果配置了
pushFailedOrders)
- 订单创建失败时记录到