| | |
| | | * onPositionUpdate() → 基底成交 → baseLongOpened && baseShortOpened |
| | | * ↓ |
| | | * tryGenerateQueues() |
| | | * ├── generateShortQueue() ← 空仓价格队列(降序,从 shortBaseEntryPrice-step 向下) |
| | | * ├── generateLongQueue() ← 多仓价格队列(升序,从 shortBaseEntryPrice+step 向上) |
| | | * ├── generateShortQueue() ← 空仓价格队列(降序,基价×(1−gridRate) 等比向下) |
| | | * ├── generateLongQueue() ← 多仓价格队列(升序,基价×(1+gridRate) 等比向上) |
| | | * ├── updateGridElements() ← 构建 GridElement 列表 + TraderParam + 全局索引 |
| | | * ├── 挂基座止盈单(ID=0 的 long/short takeProfit) |
| | | * └── 挂初始条件单(up=-1 多单, down=1 空单) |
| | |
| | | * |
| | | * <h3>关键公式</h3> |
| | | * <pre> |
| | | * step = shortBaseEntryPrice × gridRate ← 网格绝对步长 |
| | | * step = shortBaseEntryPrice × gridRate ← 首档间距(0档与±1档的距离度量) |
| | | * minTick = 10^(-priceScale) ← 交易所最小价格单位 |
| | | * 多止盈 = gridPrice + (step - minTick) ← 多仓止盈价 |
| | | * 空止盈 = gridPrice - (step - minTick) ← 空仓止盈价 |
| | | * 单笔盈利 = (step - minTick) × contractMultiplier × quantity ← USDT |
| | | * 等比网格: 下一档 = 本档 × (1 ± gridRate) ← 向下×(1−r),向上×(1+r) |
| | | * 止盈价 = 本档价 × (1 ± gridRate)(与相邻网格价联动) ← 多仓向上,空仓向下 |
| | | * 单笔盈利 ≈ 档价 × gridRate × contractMultiplier × quantity(随网格位置浮动) |
| | | * </pre> |
| | | * |
| | | * <h3>线程模型</h3> |
| | |
| | | } |
| | | |
| | | /** |
| | | * 生成空仓价格队列。 |
| | | * 以 shortBaseEntryPrice × gridRate 作为绝对步长 step,存到 config。 |
| | | * 第1个元素 = shortBaseEntryPrice − step,后续依次递减 step,共 gridQueueSize 个。 |
| | | * 队列降序排列(大→小),方便 processShortGrid 中从头遍历。 |
| | | * 生成空仓价格队列(等比 / 复合百分比网格)。 |
| | | * |
| | | * <p>每个网格价 = 上一档 × (1 − gridRate),即自 shortBaseEntryPrice 向下等比递减: |
| | | * <pre> |
| | | * 第1档 = shortBaseEntryPrice × (1 − gridRate) |
| | | * 第2档 = 第1档 × (1 − gridRate) |
| | | * ... |
| | | * </pre> |
| | | * 档间距 = 该档价格 × gridRate,随价格下降而收窄。 |
| | | * |
| | | * <p>同时将 step(= shortBaseEntryPrice × gridRate,即 0档 与 ±1档 的间距)注入 config, |
| | | * 供跨度重启等需要固定距离度量的功能使用。 |
| | | * |
| | | * <p>队列降序排列(大→小),方便 processShortGrid 中从头遍历。 |
| | | */ |
| | | private void generateShortQueue() { |
| | | shortPriceQueue.clear(); |
| | | totalShortPriceQueue.clear(); |
| | | totalLongPriceQueue.clear(); |
| | | int prec = config.getPriceScale(); |
| | | BigDecimal step = shortBaseEntryPrice.multiply(config.getGridRate()).setScale(prec, RoundingMode.HALF_UP); |
| | | BigDecimal gridRate = config.getGridRate(); |
| | | // 首档间距 = 基价 × gridRate(0档与±1档的间距),保留作跨度等固定距离度量 |
| | | BigDecimal step = shortBaseEntryPrice.multiply(gridRate).setScale(prec, RoundingMode.HALF_UP); |
| | | config.setStep(step); |
| | | BigDecimal elem = shortBaseEntryPrice.subtract(step).setScale(prec, RoundingMode.HALF_UP); |
| | | for (int i = 0; i < config.getGridQueueSize(); i++) { |
| | | shortPriceQueue.add(elem); |
| | | totalLongPriceQueue.add( elem); |
| | | totalShortPriceQueue.add( elem); |
| | | BigDecimal factor = BigDecimal.ONE.subtract(gridRate); |
| | | BigDecimal elem = shortBaseEntryPrice.multiply(factor).setScale(prec, RoundingMode.HALF_UP); |
| | | for (int i = 0; i < config.getGridQueueSize(); i++) { |
| | | shortPriceQueue.add(elem); |
| | | totalLongPriceQueue.add(elem); |
| | | totalShortPriceQueue.add(elem); |
| | | |
| | | elem = elem.subtract(step).setScale(prec, RoundingMode.HALF_UP); |
| | | if (elem.compareTo(BigDecimal.ZERO) <= 0) { |
| | | break; |
| | | } |
| | | elem = elem.multiply(factor).setScale(prec, RoundingMode.HALF_UP); |
| | | if (elem.compareTo(BigDecimal.ZERO) <= 0) { |
| | | break; |
| | | } |
| | | } |
| | | shortPriceQueue.sort((a, b) -> b.compareTo(a)); |
| | | log.info("[Gate] 空队列:{}", shortPriceQueue); |
| | | log.info("[Gate] 空队列(等比):{}", shortPriceQueue); |
| | | } |
| | | |
| | | /** |
| | | * 生成多仓价格队列。 |
| | | * 以 shortBaseEntryPrice + step 为首元素,后续依次递增 step,共 gridQueueSize 个。 |
| | | * 队列升序排列(小→大),方便 processLongGrid 中从头遍历。 |
| | | * 生成多仓价格队列(等比 / 复合百分比网格)。 |
| | | * |
| | | * <p>每个网格价 = 上一档 × (1 + gridRate),即自 shortBaseEntryPrice 向上等比递增: |
| | | * <pre> |
| | | * 第1档 = shortBaseEntryPrice × (1 + gridRate) |
| | | * 第2档 = 第1档 × (1 + gridRate) |
| | | * ... |
| | | * </pre> |
| | | * 档间距 = 该档价格 × gridRate,随价格上升而放大; |
| | | * 与空仓队列(下方 ×(1−gridRate))关于基价对称,保证上下网格连续一致。 |
| | | * |
| | | * <p>队列升序排列(小→大),方便 processLongGrid 中从头遍历。 |
| | | */ |
| | | private void generateLongQueue() { |
| | | longPriceQueue.clear(); |
| | | int prec = config.getPriceScale(); |
| | | BigDecimal step = config.getStep(); |
| | | BigDecimal elem = shortBaseEntryPrice.add(step).setScale(prec, RoundingMode.HALF_UP); |
| | | BigDecimal factor = BigDecimal.ONE.add(config.getGridRate()); |
| | | BigDecimal elem = shortBaseEntryPrice.multiply(factor).setScale(prec, RoundingMode.HALF_UP); |
| | | for (int i = 0; i < config.getGridQueueSize(); i++) { |
| | | longPriceQueue.add(elem); |
| | | totalLongPriceQueue.add( elem); |
| | | totalShortPriceQueue.add( elem); |
| | | elem = elem.add(step).setScale(prec, RoundingMode.HALF_UP); |
| | | totalLongPriceQueue.add(elem); |
| | | totalShortPriceQueue.add(elem); |
| | | elem = elem.multiply(factor).setScale(prec, RoundingMode.HALF_UP); |
| | | } |
| | | longPriceQueue.sort(BigDecimal::compareTo); |
| | | log.info("[Gate] 多队列:{}", longPriceQueue); |
| | | log.info("[Gate] 多队列(等比):{}", longPriceQueue); |
| | | totalShortPriceQueue.sort((a, b) -> b.compareTo(a)); |
| | | log.info("[Gate] 队列从大到小:{}", totalShortPriceQueue); |
| | | totalLongPriceQueue.sort(BigDecimal::compareTo); |
| | |
| | | int longSize = longPriceQueue.size(); |
| | | //根据精度转换成小数 |
| | | int prec = config.getPriceScale(); |
| | | BigDecimal step = config.getStep(); |
| | | // 等比网格:止盈价与本档相邻网格价联动 = 本档价 × (1 ± gridRate) |
| | | BigDecimal tpUpFactor = BigDecimal.ONE.add(config.getGridRate()); |
| | | BigDecimal tpDownFactor = BigDecimal.ONE.subtract(config.getGridRate()); |
| | | // String qty = config.getBaseQuantity(); |
| | | String qty = config.getQuantity(); |
| | | |
| | |
| | | TraderParam longParam = TraderParam.builder() |
| | | .direction(TraderParam.Direction.LONG) |
| | | .entryPrice(price) |
| | | .takeProfitPrice(price.add(step).setScale(prec, RoundingMode.HALF_UP)) |
| | | .takeProfitPrice(price.multiply(tpUpFactor).setScale(prec, RoundingMode.HALF_UP)) |
| | | .quantity(qty) |
| | | .build(); |
| | | TraderParam shortParam = TraderParam.builder() |
| | | .direction(TraderParam.Direction.SHORT) |
| | | .entryPrice(price) |
| | | .takeProfitPrice(price.subtract(step).setScale(prec, RoundingMode.HALF_UP)) |
| | | .takeProfitPrice(price.multiply(tpDownFactor).setScale(prec, RoundingMode.HALF_UP)) |
| | | .quantity(qty) |
| | | .build(); |
| | | elements.add(GridElement.builder() |
| | |
| | | TraderParam longParam = TraderParam.builder() |
| | | .direction(TraderParam.Direction.LONG) |
| | | .entryPrice(price) |
| | | .takeProfitPrice(price.add(step).setScale(prec, RoundingMode.HALF_UP)) |
| | | .takeProfitPrice(price.multiply(tpUpFactor).setScale(prec, RoundingMode.HALF_UP)) |
| | | .quantity(qty) |
| | | .build(); |
| | | TraderParam shortParam = TraderParam.builder() |
| | | .direction(TraderParam.Direction.SHORT) |
| | | .entryPrice(price) |
| | | .takeProfitPrice(price.subtract(step).setScale(prec, RoundingMode.HALF_UP)) |
| | | .takeProfitPrice(price.multiply(tpDownFactor).setScale(prec, RoundingMode.HALF_UP)) |
| | | .quantity(qty) |
| | | .build(); |
| | | elements.add(GridElement.builder() |
| | |
| | | TraderParam longParam = TraderParam.builder() |
| | | .direction(TraderParam.Direction.LONG) |
| | | .entryPrice(price) |
| | | .takeProfitPrice(price.add(step).setScale(prec, RoundingMode.HALF_UP)) |
| | | .takeProfitPrice(price.multiply(tpUpFactor).setScale(prec, RoundingMode.HALF_UP)) |
| | | .quantity(qty) |
| | | .build(); |
| | | TraderParam shortParam = TraderParam.builder() |
| | | .direction(TraderParam.Direction.SHORT) |
| | | .entryPrice(price) |
| | | .takeProfitPrice(price.subtract(step).setScale(prec, RoundingMode.HALF_UP)) |
| | | .takeProfitPrice(price.multiply(tpDownFactor).setScale(prec, RoundingMode.HALF_UP)) |
| | | .quantity(qty) |
| | | .build(); |
| | | elements.add(GridElement.builder() |
| | |
| | | if (newEntryGrid != null) { |
| | | |
| | | // String quantity = String.valueOf(Math.max(queryPositionSize(Position.ModeEnum.DUAL_SHORT), shortPositionSize.intValue()) + Integer.parseInt(config.getQuantity())); |
| | | String quantity = String.valueOf(config.getBaseQuantity()); |
| | | // String quantity = String.valueOf(config.getBaseQuantity()); |
| | | |
| | | |
| | | String quantity = calculateEntryQuantity(true); |
| | | // 向下检查是否已有多单挂在更低价格网格,有则跳过(防止价格回升后重复挂单) |
| | | boolean hasLongOrderBelow = false; |
| | | GridElement checkDownCursor = GridElement.findById(newEntryGrid.getDownId()); |
| | |
| | | |
| | | // String quantity = String.valueOf((accumulatedShortLossCount + 1) * Integer.parseInt(config.getQuantity())); |
| | | // String quantity = String.valueOf(Math.max(queryPositionSize(Position.ModeEnum.DUAL_LONG), longPositionSize.intValue()) + Integer.parseInt(config.getQuantity())); |
| | | String quantity = String.valueOf(config.getBaseQuantity()); |
| | | // String quantity = String.valueOf(config.getBaseQuantity()); |
| | | |
| | | String quantity = calculateEntryQuantity(false); |
| | | // 向上检查是否已有空单挂在更高价格网格,有则跳过(防止价格回落后重复挂单) |
| | | boolean hasShortOrderAbove = false; |
| | | GridElement checkUpCursor = GridElement.findById(newEntryGrid.getUpId()); |