| | |
| | | * @param closePrice K 线收盘价(即当前最新成交价) |
| | | */ |
| | | public void onKline(BigDecimal closePrice) { |
| | | |
| | | lastKlinePrice = closePrice; |
| | | |
| | | //初始化0位置的开仓,并且用空的开仓价格,作为价格基准来划分网格 |
| | |
| | | return; |
| | | } |
| | | |
| | | |
| | | // checkProfitAndReset(); |
| | | |
| | | checkProfitAndReset(); |
| | | |
| | | if (state == StrategyState.ACTIVE && |
| | | longActive == false && |
| | |
| | | BigDecimal target = initialPrincipal.add(config.getExpectedProfit()); |
| | | |
| | | FuturesAccount account = futuresApi.listFuturesAccounts(SETTLE); |
| | | BigDecimal unrealisedPnl = new BigDecimal(account.getCrossUnrealisedPnl()); |
| | | BigDecimal available = new BigDecimal(account.getCrossAvailable()); |
| | | BigDecimal totalEquity = unrealisedPnl.add(available); |
| | | BigDecimal totalEquity = new BigDecimal(account.getTotal()); |
| | | |
| | | // 估算平仓手续费:(多仓张数+空仓张数) × 合约面值 × 当前价 × taker费率 |
| | | BigDecimal totalSize = longPositionSize.abs().add(shortPositionSize.abs()); |
| | | BigDecimal closeContractValue = |
| | | totalSize.multiply(config.getContractMultiplier()).multiply(lastKlinePrice != null ? lastKlinePrice : BigDecimal.ZERO); |
| | | BigDecimal estimatedFee = closeContractValue.multiply(TAKER_FEE_RATE); |
| | | BigDecimal netEquity = totalEquity.subtract(estimatedFee); |
| | | log.info("[Gate] 盈亏检查,总张数:{}, upl:{}, avail:{}, 合计:{}, 估手续费:{}, 净权益:{}, 目标:{}", |
| | | totalSize,unrealisedPnl, available, totalEquity, estimatedFee, netEquity, target); |
| | | if (netEquity.compareTo(target) > 0) { |
| | | log.info("[Gate] 盈亏达标(净权益{}>目标{}),重置策略", netEquity, target); |
| | | if (totalEquity.compareTo(target) > 0) { |
| | | log.info("[Gate] 盈亏达标(净权益{}>目标{}),重置策略", totalEquity, target); |
| | | state = StrategyState.STOPPED; |
| | | try { |
| | | futuresApi.cancelPriceTriggeredOrderList(SETTLE, config.getContract()); |
| | |
| | | if (!newEntryGrid.isHasLongOrder()) { |
| | | BigDecimal triggerPrice = newEntryGrid.getGridPrice(); |
| | | |
| | | // 累计止损张数 + 当前止损量作为追单size,不再依赖positionSize(避免WS竞态) |
| | | accumulatedLongLossCount += Integer.parseInt(config.getQuantity()); |
| | | String size = String.valueOf(accumulatedLongLossCount + Integer.parseInt(config.getQuantity())); |
| | | log.info("[Gate] 多仓止损触发 gridId:{}, 在gridId:{}挂{}基础张多单", |
| | | gridId, newEntryGridId, size); |
| | | |
| | | newEntryGrid.getLongTraderParam().setQuantity(size); |
| | | placeEntryOrderWithPreFlag(newEntryGrid, true, triggerPrice, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, size); |
| | | // 止损触发后持仓在减少,取REST和WS缓存中较小值更准确 |
| | | int posSize = Math.min(queryPositionSize(Position.ModeEnum.DUAL_LONG), longPositionSize.intValue()); |
| | | int maxPos = config.getMaxPositionSize(); |
| | | int targetAmount = Integer.parseInt(config.getQuantity()) * 2; // quantity + 本次止损量 |
| | | int addSize; |
| | | if (maxPos > 0) { |
| | | int remainingRoom = maxPos - posSize; |
| | | if (remainingRoom <= 0) { |
| | | log.warn("[Gate] 多仓止损触发 gridId:{}, 当前持仓{}/{}已达上限,跳过追单", |
| | | gridId, posSize, maxPos); |
| | | addSize = 0; |
| | | } else { |
| | | addSize = Math.min(remainingRoom, targetAmount); |
| | | } |
| | | } else { |
| | | addSize = targetAmount; |
| | | } |
| | | if (addSize > 0) { |
| | | String size = String.valueOf(addSize); |
| | | log.info("[Gate] 多仓止损触发 gridId:{}, 在gridId:{}补{}张多单(当前{}/上限{})", |
| | | gridId, newEntryGridId, size, posSize, maxPos > 0 ? maxPos : "无"); |
| | | newEntryGrid.getLongTraderParam().setQuantity(size); |
| | | placeEntryOrderWithPreFlag(newEntryGrid, true, triggerPrice, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, size); |
| | | } |
| | | }else{ |
| | | log.warn("[Gate] 多仓止损触发 gridId:{}, 目标gridId:{}已有挂单,跳过重复下单", gridId, newEntryGridId); |
| | | } |
| | |
| | | if (!newEntryGrid.isHasShortOrder()) { |
| | | BigDecimal triggerPrice = newEntryGrid.getGridPrice(); |
| | | |
| | | // 累计止损张数 + 当前止损量作为追单size,不再依赖positionSize(避免WS竞态) |
| | | accumulatedShortLossCount += Integer.parseInt(config.getQuantity()); |
| | | String size = String.valueOf(accumulatedShortLossCount + Integer.parseInt(config.getQuantity())); |
| | | log.info("[Gate] 空仓止损触发 gridId:{}, 在gridId:{}挂{}基础张空单", |
| | | gridId, newEntryGridId, size); |
| | | newEntryGrid.getShortTraderParam().setQuantity(size); |
| | | placeEntryOrderWithPreFlag(newEntryGrid, false, triggerPrice, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(size)); |
| | | // 止损触发后持仓在减少,取REST和WS缓存中较小值更准确 |
| | | int posSize = Math.min(queryPositionSize(Position.ModeEnum.DUAL_SHORT), shortPositionSize.intValue()); |
| | | int maxPos = config.getMaxPositionSize(); |
| | | int targetAmount = Integer.parseInt(config.getQuantity()) * 2; // quantity + 本次止损量 |
| | | int addSize; |
| | | if (maxPos > 0) { |
| | | int remainingRoom = maxPos - posSize; |
| | | if (remainingRoom <= 0) { |
| | | log.warn("[Gate] 空仓止损触发 gridId:{}, 当前持仓{}/{}已达上限,跳过追单", |
| | | gridId, posSize, maxPos); |
| | | addSize = 0; |
| | | } else { |
| | | addSize = Math.min(remainingRoom, targetAmount); |
| | | } |
| | | } else { |
| | | addSize = targetAmount; |
| | | } |
| | | if (addSize > 0) { |
| | | String size = String.valueOf(addSize); |
| | | log.info("[Gate] 空仓止损触发 gridId:{}, 在gridId:{}补{}张空单(当前{}/上限{})", |
| | | gridId, newEntryGridId, size, posSize, maxPos > 0 ? maxPos : "无"); |
| | | newEntryGrid.getShortTraderParam().setQuantity(size); |
| | | placeEntryOrderWithPreFlag(newEntryGrid, false, triggerPrice, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(size)); |
| | | } |
| | | }else{ |
| | | log.warn("[Gate] 空仓止损触发 gridId:{}, 目标gridId:{}已有挂单,跳过重复下单", gridId, newEntryGridId); |
| | | } |