entry = it.next();
+ GridSellOrder order = entry.getValue();
+ if (normalized.compareTo(order.sellPrice) == 0) {
+ it.remove();
+ return order;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 从 clientOid(SELL_{symbol}_{entryPrice}_{ts})解析入场价。
+ */
+ private BigDecimal parseEntryPriceFromClientOid(String clientOid) {
+ if (clientOid == null || !clientOid.startsWith("SELL_")) {
+ return null;
+ }
+ String plainSymbol = getPlainSymbol();
+ String prefix = "SELL_" + plainSymbol + "_";
+ if (!clientOid.startsWith(prefix)) {
+ return null;
+ }
+ String remainder = clientOid.substring(prefix.length());
+ int lastUnderscore = remainder.lastIndexOf('_');
+ if (lastUnderscore <= 0) {
+ return null;
+ }
+ try {
+ return normalizePrice(new BigDecimal(remainder.substring(0, lastUnderscore)));
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ }
+
// ======================== 交易所同步 ========================
private void syncOpenOrdersFromExchange() {
@@ -889,29 +987,63 @@ public class AutoGridStrategy extends BaseStrategy {
if (openOrders == null) {
return;
}
+ int buyCount = 0;
+ int sellCount = 0;
for (int i = 0; i < openOrders.size(); i++) {
JSONObject order = openOrders.getJSONObject(i);
- if (!"buy".equals(order.getString("side"))) {
- continue;
- }
- if (!"open".equals(order.getString("tradeSide"))) {
- continue;
- }
+ String side = order.getString("side");
+ String tradeSide = order.getString("tradeSide");
BigDecimal price = normalizePrice(order.getBigDecimal("price"));
String clientOid = order.getString("clientOid");
- if (clientOid == null) {
- clientOid = "SYNC_" + price.toPlainString();
+ String exchangeOrderId = order.getString("orderId");
+ BigDecimal quantity = firstPositive(
+ order.getBigDecimal("size"),
+ order.getBigDecimal("baseVolume"));
+
+ if ("buy".equals(side) && "open".equals(tradeSide)) {
+ if (clientOid == null) {
+ clientOid = "SYNC_BUY_" + price.toPlainString();
+ }
+ GridBuyOrder gridBuyOrder = new GridBuyOrder(clientOid, price);
+ gridBuyOrder.exchangeOrderId = exchangeOrderId;
+ pendingBuyOrders.put(priceKey(price), gridBuyOrder);
+ buyCount++;
+ } else if ("sell".equals(side) && "close".equals(tradeSide)) {
+ if (clientOid == null) {
+ clientOid = "SYNC_SELL_" + price.toPlainString();
+ }
+ BigDecimal entryPrice = parseEntryPriceFromClientOid(clientOid);
+ String clientPositionId = entryPrice != null
+ ? "POS_" + entryPrice.toPlainString()
+ : "SYNC_POS_" + price.toPlainString();
+ if (quantity == null) {
+ quantity = baseQuantity;
+ }
+ GridSellOrder sellOrder = new GridSellOrder(clientOid, clientPositionId, price, quantity);
+ sellOrder.exchangeOrderId = exchangeOrderId;
+ pendingSellOrders.put(clientPositionId, sellOrder);
+ sellCount++;
+
+ if (entryPrice != null && !hasOpenPosition(clientPositionId)) {
+ openPositions.add(new GridPosition(clientPositionId, entryPrice, quantity, price));
+ }
}
- GridBuyOrder gridBuyOrder = new GridBuyOrder(clientOid, price);
- gridBuyOrder.exchangeOrderId = order.getString("orderId");
- pendingBuyOrders.put(priceKey(price), gridBuyOrder);
}
- logger.info("同步挂单 {} 笔", pendingBuyOrders.size());
+ logger.info("同步挂单:买单 {} 笔,卖单 {} 笔", buyCount, sellCount);
} catch (Exception e) {
logger.warn("同步挂单失败:{}", e.getMessage());
}
}
+ private boolean hasOpenPosition(String clientPositionId) {
+ for (GridPosition position : openPositions) {
+ if (position.clientPositionId.equals(clientPositionId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* 若交易所上残留多笔买单,只保留最靠近现价(价格最高)的一笔,撤销其余。
*/
@@ -1039,6 +1171,7 @@ public class AutoGridStrategy extends BaseStrategy {
sb.append("ATR:").append(formatPrice(currentAtr)).append("
");
sb.append("网格间距:").append(formatPrice(currentGridGap)).append("
");
sb.append("待成交买单:").append(pendingBuyOrders.size()).append(" / 1 笔
");
+ sb.append("待成交卖单:").append(pendingSellOrders.size()).append(" 笔
");
sb.append("持仓待止盈:").append(openPositions.size())
.append(" / ").append(gridCount).append(" 层
");
sb.append("上限暂停:").append(upperBoundPaused ? "是" : "否").append("
");
@@ -1069,6 +1202,17 @@ public class AutoGridStrategy extends BaseStrategy {
rows.add(row);
}
+ List pendingSells = new ArrayList<>(pendingSellOrders.values());
+ pendingSells.sort(Comparator.comparing(o -> o.sellPrice, Comparator.reverseOrder()));
+ for (GridSellOrder order : pendingSells) {
+ JSONObject row = new JSONObject();
+ row.put("type", "待成交卖单");
+ row.put("price", formatPrice(order.sellPrice));
+ row.put("quantity", order.quantity.toPlainString());
+ row.put("takeProfit", formatPrice(order.sellPrice));
+ rows.add(row);
+ }
+
List positions = new ArrayList<>(openPositions);
positions.sort(Comparator.comparing(p -> p.entryPrice, Comparator.reverseOrder()));
for (GridPosition position : positions) {
@@ -1076,7 +1220,7 @@ public class AutoGridStrategy extends BaseStrategy {
row.put("type", "持仓");
row.put("price", formatPrice(position.entryPrice));
row.put("quantity", position.quantity.toPlainString());
- row.put("takeProfit", formatPrice(position.takeProfitPrice(currentGridGap)));
+ row.put("takeProfit", formatPrice(position.takeProfitPrice));
rows.add(row);
}