| | |
| | | package com.xcong.excoin.modules.gateApi; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import com.xcong.excoin.utils.dingtalk.DingTalkUtils; |
| | | import io.gate.gateapi.ApiClient; |
| | | import io.gate.gateapi.ApiException; |
| | | import io.gate.gateapi.GateApiException; |
| | |
| | | import java.math.RoundingMode; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Iterator; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | |
| | | lastKlinePrice = closePrice; |
| | | updateUnrealizedPnl(); |
| | | if (state == StrategyState.STOPPED) { |
| | | try { |
| | | futuresApi.cancelPriceTriggeredOrderList(SETTLE, config.getContract()); |
| | | } catch (ApiException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | closeExistingPositions(); |
| | | |
| | | BigDecimal totalPnl = cumulativePnl.add(unrealizedPnl); |
| | | log.info("[Gate] 已实现:{}, 未实现:{}, 合计:{}", |
| | | cumulativePnl, unrealizedPnl, totalPnl); |
| | | return; |
| | | } |
| | | |
| | |
| | | tryGenerateQueues(); |
| | | }else { |
| | | longPositionSize = size; |
| | | //取消多仓位线以上的开空仓挂单 |
| | | List<GridElement> allShortOrders = GridElement.findAllShortOrders(longEntryPrice); |
| | | List<GridElement> allShortOrders = GridElement.findAllLongOrders(longEntryPrice); |
| | | if (CollUtil.isNotEmpty(allShortOrders)){ |
| | | GridElement keep = allShortOrders.stream() |
| | | .max((a, b) -> a.getGridPrice().compareTo(b.getGridPrice())) |
| | | .orElse(null); |
| | | for (GridElement e : allShortOrders) { |
| | | if (e == keep) { |
| | | continue; |
| | | } |
| | | executor.cancelConditionalOrder( |
| | | e.getShortOrderId(), |
| | | e.getLongOrderId(), |
| | | orderId -> { |
| | | shortEntryTraderIdParam( |
| | | longEntryTraderIdParam( |
| | | e, |
| | | null, |
| | | false |
| | |
| | | } |
| | | ); |
| | | |
| | | if (e.getShortTakeProfitOrderId() != null){ |
| | | if (e.getLongTakeProfitOrderId() != null){ |
| | | executor.cancelConditionalOrder( |
| | | e.getShortTakeProfitOrderId(), |
| | | e.getLongTakeProfitOrderId(), |
| | | orderId -> { |
| | | shortTakeProfitTraderIdParam( |
| | | longTakeProfitTraderIdParam( |
| | | e, |
| | | null, |
| | | false |
| | |
| | | tryGenerateQueues(); |
| | | }else { |
| | | shortPositionSize = size.abs(); |
| | | //取消空仓仓位线以下的开多仓挂单 |
| | | List<GridElement> allLongOrders = GridElement.findAllLongOrders(shortEntryPrice); |
| | | List<GridElement> allLongOrders = GridElement.findAllShortOrders(shortEntryPrice); |
| | | if (CollUtil.isNotEmpty(allLongOrders)){ |
| | | GridElement keep = allLongOrders.stream() |
| | | .min((a, b) -> a.getGridPrice().compareTo(b.getGridPrice())) |
| | | .orElse(null); |
| | | for (GridElement e : allLongOrders) { |
| | | if (e == keep) { |
| | | continue; |
| | | } |
| | | executor.cancelConditionalOrder( |
| | | e.getLongOrderId(), |
| | | e.getShortOrderId(), |
| | | orderId -> { |
| | | longEntryTraderIdParam( |
| | | shortEntryTraderIdParam( |
| | | e, |
| | | null, |
| | | false |
| | | ); |
| | | } |
| | | ); |
| | | if (e.getLongTakeProfitOrderId() != null){ |
| | | if (e.getShortTakeProfitOrderId() != null){ |
| | | executor.cancelConditionalOrder( |
| | | e.getLongTakeProfitOrderId(), |
| | | e.getShortTakeProfitOrderId(), |
| | | orderId -> { |
| | | longTakeProfitTraderIdParam( |
| | | shortTakeProfitTraderIdParam( |
| | | e, |
| | | null, |
| | | false |
| | |
| | | return; |
| | | } |
| | | cumulativePnl = cumulativePnl.add(pnl); |
| | | log.info("[Gate] 盈亏累加:{}, 方向:{}, 累计:{}", pnl, side, cumulativePnl); |
| | | updateUnrealizedPnl(); |
| | | BigDecimal totalPnl = cumulativePnl.add(unrealizedPnl); |
| | | log.info("[Gate] 已实现:{}, 未实现:{}, 合计:{}", |
| | | cumulativePnl, unrealizedPnl, totalPnl); |
| | | |
| | | if (cumulativePnl.compareTo(config.getOverallTp()) >= 0) { |
| | | log.info("[Gate] 已达止盈目标 {}→已停止", cumulativePnl); |
| | | if (totalPnl.compareTo(config.getOverallTp()) >= 0) { |
| | | log.info("[Gate] 已达止盈目标(合计{})→已停止, 已实现:{}, 未实现:{}", |
| | | totalPnl, cumulativePnl, unrealizedPnl); |
| | | state = StrategyState.STOPPED; |
| | | } else if (cumulativePnl.compareTo(config.getMaxLoss().negate()) <= 0) { |
| | | log.info("[Gate] 已达亏损上限 {}→已停止", cumulativePnl); |
| | | state = StrategyState.STOPPED; |
| | | } else if (totalPnl.compareTo(config.getMaxLoss().negate()) <= 0) { |
| | | String logMessage = StrUtil.format("[Gate] 已达亏损风险值(合计{}), 已实现:{}, 未实现:{}", |
| | | totalPnl, cumulativePnl, unrealizedPnl); |
| | | log.info(logMessage); |
| | | |
| | | |
| | | DingTalkUtils.getDefault().sendActionCard("风险提醒", logMessage, config.getApiKey(), ""); |
| | | // state = StrategyState.STOPPED; |
| | | } |
| | | } |
| | | |
| | |
| | | null, |
| | | false |
| | | ); |
| | | longEntryTraderIdParam( |
| | | byLongTakeProfitOrderId, |
| | | null, |
| | | false |
| | | ); |
| | | // longEntryTraderIdParam( |
| | | // byLongTakeProfitOrderId, |
| | | // null, |
| | | // false |
| | | // ); |
| | | } |
| | | GridElement byShortTakeProfitOrderId = GridElement.findByShortTakeProfitOrderId(orderId); |
| | | if (byShortTakeProfitOrderId != null){ |
| | |
| | | null, |
| | | false |
| | | ); |
| | | shortEntryTraderIdParam( |
| | | byShortTakeProfitOrderId, |
| | | null, |
| | | false |
| | | ); |
| | | // shortEntryTraderIdParam( |
| | | // byShortTakeProfitOrderId, |
| | | // null, |
| | | // false |
| | | // ); |
| | | } |
| | | |
| | | /** |
| | |
| | | GridElement longGridElement = GridElement.findByLongOrderId(orderId); |
| | | if (longGridElement != null) { |
| | | if (longGridElement.isHasLongOrder()){ |
| | | longEntryTraderIdParam( |
| | | longGridElement, |
| | | null, |
| | | false |
| | | ); |
| | | if (longGridElement.getLongTakeProfitOrderId() == null){ |
| | | BigDecimal longTp = longGridElement.getLongTraderParam().getTakeProfitPrice(); |
| | | if (longTp != null) { |
| | |
| | | GridElement shortGridElement = GridElement.findByShortOrderId(orderId); |
| | | if (shortGridElement != null) { |
| | | if (shortGridElement.isHasShortOrder()){ |
| | | shortEntryTraderIdParam( |
| | | shortGridElement, |
| | | null, |
| | | false |
| | | ); |
| | | if (shortGridElement.getShortTakeProfitOrderId() == null){ |
| | | BigDecimal shortTp = shortGridElement.getShortTraderParam().getTakeProfitPrice(); |
| | | if (shortTp != null) { |
| | |
| | | /** |
| | | * 匹配止盈单止盈 |
| | | */ |
| | | GridElement byLongTakeProfitOrderId = GridElement.findByLongTakeProfitOrderId(orderId); |
| | | if (byLongTakeProfitOrderId != null){ |
| | | longTakeProfitTraderIdParam( |
| | | byLongTakeProfitOrderId, |
| | | null, |
| | | false |
| | | ); |
| | | longEntryTraderIdParam( |
| | | byLongTakeProfitOrderId, |
| | | null, |
| | | false |
| | | ); |
| | | } |
| | | GridElement byShortTakeProfitOrderId = GridElement.findByShortTakeProfitOrderId(orderId); |
| | | if (byShortTakeProfitOrderId != null){ |
| | | shortTakeProfitTraderIdParam( |
| | |
| | | null, |
| | | false |
| | | ); |
| | | // TPonUserTradeShortEntry(byShortTakeProfitOrderId); |
| | | } |
| | | GridElement byLongTakeProfitOrderId = GridElement.findByLongTakeProfitOrderId(orderId); |
| | | if (byLongTakeProfitOrderId != null){ |
| | | longTakeProfitTraderIdParam( |
| | | byLongTakeProfitOrderId, |
| | | null, |
| | | false |
| | | ); |
| | | longEntryTraderIdParam( |
| | | byLongTakeProfitOrderId, |
| | | null, |
| | | false |
| | | ); |
| | | // TPonUserTradeLongEntry(byLongTakeProfitOrderId); |
| | | } |
| | | |
| | | /** |
| | | * 匹配挂单 |
| | | */ |
| | | GridElement longGridElement = GridElement.findByLongOrderId(orderId); |
| | | if (longGridElement != null) { |
| | | if (longGridElement.isHasLongOrder() && !tradeId.equals("0")){ |
| | | if (longGridElement.getLongTakeProfitOrderId() == null){ |
| | | BigDecimal longTp = longGridElement.getLongTraderParam().getTakeProfitPrice(); |
| | | if (longTp != null) { |
| | | executor.placeTakeProfit(longTp, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | ORDER_TYPE_CLOSE_LONG, |
| | | negate(config.getQuantity()), |
| | | (profitId) -> { |
| | | longTakeProfitTraderIdParam( |
| | | longGridElement, |
| | | profitId, |
| | | true |
| | | ); |
| | | }); |
| | | log.info("[Gate] 多单成交匹配止盈, orderId:{}, 止盈价:{}, size:{}", orderId, longTp, negate(config.getQuantity())); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | GridElement shortGridElement = GridElement.findByShortOrderId(orderId); |
| | | if (shortGridElement != null) { |
| | | if (shortGridElement.isHasShortOrder() && !tradeId.equals("0")){ |
| | | |
| | | onUserTradeShortEntry(shortGridElement); |
| | | if (shortGridElement.getShortTakeProfitOrderId() == null){ |
| | | BigDecimal shortTp = shortGridElement.getShortTraderParam().getTakeProfitPrice(); |
| | | if (shortTp != null) { |
| | |
| | | }); |
| | | log.info("[Gate] 空单成交匹配止盈, orderId:{}, 止盈价:{}, size:{}", orderId, shortTp, config.getQuantity()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | GridElement longGridElement = GridElement.findByLongOrderId(orderId); |
| | | if (longGridElement != null) { |
| | | if (longGridElement.isHasLongOrder() && !tradeId.equals("0")){ |
| | | |
| | | onUserTradeLongEntry(longGridElement); |
| | | if (longGridElement.getLongTakeProfitOrderId() == null){ |
| | | BigDecimal longTp = longGridElement.getLongTraderParam().getTakeProfitPrice(); |
| | | if (longTp != null) { |
| | | executor.placeTakeProfit(longTp, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | ORDER_TYPE_CLOSE_LONG, |
| | | negate(config.getQuantity()), |
| | | (profitId) -> { |
| | | longTakeProfitTraderIdParam( |
| | | longGridElement, |
| | | profitId, |
| | | true |
| | | ); |
| | | }); |
| | | log.info("[Gate] 多单成交匹配止盈, orderId:{}, 止盈价:{}, size:{}", orderId, longTp, negate(config.getQuantity())); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void TPonUserTradeShortEntry(GridElement gridElement) { |
| | | if (!isMarginSafe()) { |
| | | log.warn("[Gate] 保证金超限,跳过挂条件单"); |
| | | } else { |
| | | |
| | | /** |
| | | * 下一个开仓位置 |
| | | * 获取队列第一个元素的价格对应的网格 |
| | | * 判断网格是否能开空仓,如果不能则跳过 |
| | | * 前进方向挂空仓条件单 |
| | | * 后置方向挂多空条件单 |
| | | */ |
| | | //下一个开仓位置 |
| | | BigDecimal gridPrice = gridElement.getGridPrice(); |
| | | |
| | | // 判断网格是否能开空仓,如果不能则跳过 |
| | | if (gridElement != null) { |
| | | TraderParam downShortTraderParam = gridElement.getShortTraderParam(); |
| | | if ( |
| | | !gridElement.isHasShortOrder() && |
| | | gridPrice.compareTo(longEntryPrice) <= 0 && |
| | | gridPrice.compareTo(shortEntryPrice) >= 0 |
| | | ){ |
| | | placeEntryOrderWithPreFlag(gridElement, false, |
| | | downShortTraderParam.getEntryPrice(), |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | negate(downShortTraderParam.getQuantity())); |
| | | |
| | | } |
| | | |
| | | TraderParam downLongTraderParam = gridElement.getLongTraderParam(); |
| | | if ( |
| | | !gridElement.isHasLongOrder() && |
| | | gridPrice.compareTo(longEntryPrice) <= 0 |
| | | ){ |
| | | placeEntryOrderWithPreFlag(gridElement, true, |
| | | downLongTraderParam.getEntryPrice(), |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | downLongTraderParam.getQuantity()); |
| | | } |
| | | |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void TPonUserTradeLongEntry(GridElement gridElement) { |
| | | if (!isMarginSafe()) { |
| | | log.warn("[Gate] 保证金超限,跳过挂条件单"); |
| | | } else { |
| | | BigDecimal newLongFirst = gridElement.getGridPrice() ; |
| | | |
| | | // 判断网格是否能开多空仓,如果不能则跳过 |
| | | if (gridElement != null) { |
| | | |
| | | // TraderParam downLongTraderParam = gridElement.getLongTraderParam(); |
| | | // if ( |
| | | // !gridElement.isHasLongOrder() && |
| | | // newLongFirst.compareTo(shortEntryPrice) >= 0 && |
| | | // newLongFirst.compareTo(longEntryPrice) <= 0 |
| | | // ){ |
| | | // placeEntryOrderWithPreFlag(gridElement, true, |
| | | // downLongTraderParam.getEntryPrice(), |
| | | // FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | // config.getQuantity()); |
| | | // |
| | | // } |
| | | |
| | | TraderParam shortTraderParam = gridElement.getShortTraderParam(); |
| | | if ( |
| | | !gridElement.isHasShortOrder() && |
| | | newLongFirst.compareTo(shortEntryPrice) >= 0 |
| | | ){ |
| | | |
| | | placeEntryOrderWithPreFlag(gridElement, false, |
| | | shortTraderParam.getEntryPrice(), |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | negate(config.getQuantity())); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void onUserTradeShortEntry(GridElement gridElement) { |
| | | if (!isMarginSafe()) { |
| | | log.warn("[Gate] 保证金超限,跳过挂条件单"); |
| | | } else { |
| | | //下一个开仓位置 |
| | | GridElement UpGridElement = GridElement.findById(gridElement.getDownId()); |
| | | BigDecimal newLongFirst = UpGridElement.getGridPrice(); |
| | | |
| | | // 判断网格是否能开空仓,如果不能则跳过 |
| | | if (UpGridElement != null) { |
| | | |
| | | if (!UpGridElement.isHasShortOrder() && shortEntryPrice.compareTo(newLongFirst) > 0) { |
| | | |
| | | TraderParam upShortTraderParam = UpGridElement.getShortTraderParam(); |
| | | placeEntryOrderWithPreFlag(UpGridElement, false, |
| | | upShortTraderParam.getEntryPrice(), |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | negate(upShortTraderParam.getQuantity())); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void onUserTradeLongEntry(GridElement gridElement) { |
| | | if (!isMarginSafe()) { |
| | | log.warn("[Gate] 保证金超限,跳过挂条件单"); |
| | | } else { |
| | | //下一个开仓位置 |
| | | GridElement UpGridElement = GridElement.findById(gridElement.getUpId()); |
| | | BigDecimal newLongFirst = UpGridElement.getGridPrice() ; |
| | | |
| | | // 判断网格是否能开多仓,如果不能则跳过 |
| | | if (UpGridElement != null) { |
| | | |
| | | if (!UpGridElement.isHasLongOrder() && longEntryPrice.compareTo(newLongFirst) < 0) { |
| | | TraderParam upLongTraderParam = UpGridElement.getLongTraderParam(); |
| | | placeEntryOrderWithPreFlag(UpGridElement, true, |
| | | upLongTraderParam.getEntryPrice(), |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | config.getQuantity()); |
| | | } |
| | | } |
| | | } |
| | |
| | | Integer upId = baseGridElement.getUpId(); |
| | | GridElement upGridElementOne = GridElement.findById(upId); |
| | | BigDecimal longTp = upGridElementOne.getGridPrice(); |
| | | executor.placeConditionalEntryOrder( |
| | | placeEntryOrderWithPreFlag(upGridElementOne, true, |
| | | longTp, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | config.getQuantity(), |
| | | orderId -> { |
| | | longEntryTraderIdParam( |
| | | upGridElementOne, |
| | | orderId, |
| | | true |
| | | ); |
| | | }, |
| | | null); |
| | | config.getQuantity()); |
| | | Integer downId = baseGridElement.getDownId(); |
| | | GridElement downGridElementOne = GridElement.findById(downId); |
| | | BigDecimal shortTp = downGridElementOne.getGridPrice(); |
| | | executor.placeConditionalEntryOrder( |
| | | placeEntryOrderWithPreFlag(downGridElementOne, false, |
| | | shortTp, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | negate(config.getQuantity()), |
| | | orderId -> { |
| | | shortEntryTraderIdParam( |
| | | downGridElementOne, |
| | | orderId, |
| | | true |
| | | ); |
| | | }, |
| | | null); |
| | | negate(config.getQuantity())); |
| | | |
| | | state = StrategyState.ACTIVE; |
| | | } |
| | |
| | | int longSize = longPriceQueue.size(); |
| | | //根据精度转换成小数 |
| | | int prec = config.getPriceScale(); |
| | | BigDecimal minTick = BigDecimal.ONE.scaleByPowerOfTen(-prec); |
| | | BigDecimal step = config.getStep().subtract(minTick); |
| | | // BigDecimal minTick = BigDecimal.ONE.scaleByPowerOfTen(-prec); |
| | | // BigDecimal step = config.getStep().subtract(minTick); |
| | | BigDecimal step = config.getStep(); |
| | | String qty = config.getQuantity(); |
| | | |
| | | // 空仓队列:id 从 -1 自减, shortPriceQueue[i] → id=-(i+1) |
| | |
| | | // 判断网格是否能开空仓,如果不能则跳过 |
| | | if (UpGridElement != null) { |
| | | |
| | | if (!UpGridElement.isHasShortOrder()) { |
| | | |
| | | //挂空仓条件单 |
| | | TraderParam upShortTraderParam = UpGridElement.getShortTraderParam(); |
| | | executor.placeConditionalEntryOrder( |
| | | upShortTraderParam.getEntryPrice(), |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | negate(upShortTraderParam.getQuantity()), |
| | | orderId -> |
| | | { |
| | | shortEntryTraderIdParam( |
| | | UpGridElement, |
| | | orderId, |
| | | true |
| | | ); |
| | | }, |
| | | null |
| | | ); |
| | | } |
| | | // if (!UpGridElement.isHasShortOrder() && shortEntryPrice.compareTo(newLongFirst) > 0) { |
| | | // |
| | | // TraderParam upShortTraderParam = UpGridElement.getShortTraderParam(); |
| | | // placeEntryOrderWithPreFlag(UpGridElement, false, |
| | | // upShortTraderParam.getEntryPrice(), |
| | | // FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | // negate(upShortTraderParam.getQuantity())); |
| | | // } |
| | | int i = UpGridElement.getId() + 2; |
| | | GridElement downGridElement = GridElement.findById(i); |
| | | if (downGridElement != null){ |
| | | |
| | | BigDecimal downGridPrice = downGridElement.getGridPrice(); |
| | | |
| | | TraderParam downShortTraderParam = downGridElement.getShortTraderParam(); |
| | | if ( |
| | | !downGridElement.isHasShortOrder() && |
| | | downGridPrice.compareTo(longEntryPrice) <= 0 && |
| | | downGridPrice.compareTo(shortEntryPrice) >= 0 |
| | | ){ |
| | | executor.placeConditionalEntryOrder( |
| | | downShortTraderParam.getEntryPrice(), |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | negate(downShortTraderParam.getQuantity()), |
| | | orderId -> |
| | | { |
| | | shortEntryTraderIdParam( |
| | | downGridElement, |
| | | orderId, |
| | | true |
| | | ); |
| | | }, |
| | | null |
| | | ); |
| | | |
| | | } |
| | | // TraderParam downShortTraderParam = downGridElement.getShortTraderParam(); |
| | | // if ( |
| | | // !downGridElement.isHasShortOrder() && |
| | | // downGridPrice.compareTo(longEntryPrice) <= 0 && |
| | | // downGridPrice.compareTo(shortEntryPrice) >= 0 |
| | | // ){ |
| | | // placeEntryOrderWithPreFlag(downGridElement, false, |
| | | // downShortTraderParam.getEntryPrice(), |
| | | // FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | // negate(downShortTraderParam.getQuantity())); |
| | | // |
| | | // } |
| | | |
| | | TraderParam downLongTraderParam = downGridElement.getLongTraderParam(); |
| | | if ( |
| | | !downGridElement.isHasLongOrder() && |
| | | downGridPrice.compareTo(longEntryPrice) <= 0 |
| | | ){ |
| | | executor.placeConditionalEntryOrder( |
| | | placeEntryOrderWithPreFlag(downGridElement, true, |
| | | downLongTraderParam.getEntryPrice(), |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | downLongTraderParam.getQuantity(), |
| | | orderId -> |
| | | { |
| | | longEntryTraderIdParam( |
| | | downGridElement, |
| | | orderId, |
| | | true |
| | | ); |
| | | }, |
| | | null |
| | | ); |
| | | downLongTraderParam.getQuantity()); |
| | | } |
| | | } |
| | | } |
| | |
| | | // 判断网格是否能开多仓,如果不能则跳过 |
| | | if (UpGridElement != null) { |
| | | |
| | | if (!UpGridElement.isHasLongOrder()) { |
| | | //挂多仓条件单 |
| | | TraderParam upLongTraderParam = UpGridElement.getLongTraderParam(); |
| | | executor.placeConditionalEntryOrder( |
| | | upLongTraderParam.getEntryPrice(), |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | config.getQuantity(), |
| | | orderId -> |
| | | { |
| | | longEntryTraderIdParam( |
| | | UpGridElement, |
| | | orderId, |
| | | true |
| | | ); |
| | | }, |
| | | null |
| | | ); |
| | | } |
| | | // if (!UpGridElement.isHasLongOrder() && longEntryPrice.compareTo(newLongFirst) < 0) { |
| | | // TraderParam upLongTraderParam = UpGridElement.getLongTraderParam(); |
| | | // placeEntryOrderWithPreFlag(UpGridElement, true, |
| | | // upLongTraderParam.getEntryPrice(), |
| | | // FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | // config.getQuantity()); |
| | | // } |
| | | |
| | | int i = UpGridElement.getId() - 2; |
| | | GridElement downGridElement = GridElement.findById(i); |
| | |
| | | |
| | | BigDecimal downGridPrice = downGridElement.getGridPrice(); |
| | | |
| | | TraderParam downLongTraderParam = downGridElement.getLongTraderParam(); |
| | | if ( |
| | | !downGridElement.isHasLongOrder() && |
| | | downGridPrice.compareTo(shortEntryPrice) >= 0 && |
| | | downGridPrice.compareTo(longEntryPrice) <= 0 |
| | | ){ |
| | | executor.placeConditionalEntryOrder( |
| | | downLongTraderParam.getEntryPrice(), |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | config.getQuantity(), |
| | | orderId -> |
| | | { |
| | | longEntryTraderIdParam( |
| | | downGridElement, |
| | | orderId, |
| | | true |
| | | ); |
| | | }, |
| | | null |
| | | ); |
| | | |
| | | } |
| | | // TraderParam downLongTraderParam = downGridElement.getLongTraderParam(); |
| | | // if ( |
| | | // !downGridElement.isHasLongOrder() && |
| | | // downGridPrice.compareTo(shortEntryPrice) >= 0 && |
| | | // downGridPrice.compareTo(longEntryPrice) <= 0 |
| | | // ){ |
| | | // placeEntryOrderWithPreFlag(downGridElement, true, |
| | | // downLongTraderParam.getEntryPrice(), |
| | | // FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | // config.getQuantity()); |
| | | // |
| | | // } |
| | | |
| | | TraderParam shortTraderParam = downGridElement.getShortTraderParam(); |
| | | if ( |
| | |
| | | downGridPrice.compareTo(shortEntryPrice) >= 0 |
| | | ){ |
| | | |
| | | executor.placeConditionalEntryOrder( |
| | | placeEntryOrderWithPreFlag(downGridElement, false, |
| | | shortTraderParam.getEntryPrice(), |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | negate(config.getQuantity()), |
| | | orderId -> |
| | | { |
| | | shortEntryTraderIdParam( |
| | | downGridElement, |
| | | orderId, |
| | | true |
| | | ); |
| | | }, |
| | | null |
| | | ); |
| | | negate(config.getQuantity())); |
| | | } |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * 预设标志位后提交条件开仓单,防止异步回调导致的竞态重复挂单。 |
| | | * |
| | | * <p>在调用 {@link GateTradeExecutor#placeConditionalEntryOrder} 之前同步设置 |
| | | * {@code isHasLongOrder / isHasShortOrder},关闭 WS 线程与 Executor 线程之间的 |
| | | * 检查-下单时间窗口。API 失败时自动回滚标志位。 |
| | | * |
| | | * @param gridElement 目标网格元素 |
| | | * @param isLong true=多仓下单,false=空仓下单 |
| | | * @param triggerPrice 触发价 |
| | | * @param rule 触发规则 |
| | | * @param size 开仓张数 |
| | | */ |
| | | private void placeEntryOrderWithPreFlag(GridElement gridElement, boolean isLong, |
| | | BigDecimal triggerPrice, |
| | | FuturesPriceTrigger.RuleEnum rule, |
| | | String size) { |
| | | if (isLong) { |
| | | gridElement.setHasLongOrder(true); |
| | | } else { |
| | | gridElement.setHasShortOrder(true); |
| | | } |
| | | executor.placeConditionalEntryOrder(triggerPrice, rule, size, |
| | | orderId -> { |
| | | if (isLong) { |
| | | longEntryTraderIdParam(gridElement, orderId, true); |
| | | } else { |
| | | shortEntryTraderIdParam(gridElement, orderId, true); |
| | | } |
| | | }, |
| | | () -> { |
| | | if (isLong) { |
| | | gridElement.setHasLongOrder(false); |
| | | gridElement.setLongOrderId(null); |
| | | } else { |
| | | gridElement.setHasShortOrder(false); |
| | | gridElement.setShortOrderId(null); |
| | | } |
| | | GridElement.refreshIndices(); |
| | | log.warn("[Gate] 条件单创建失败,回滚标志位 gridId:{}, isLong:{}", gridElement.getId(), isLong); |
| | | } |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * 根据持仓和当前价格计算未实现盈亏。 |
| | | * |
| | | * <h3>正向合约公式</h3> |