From 04a1828f4cca58545725aef8fb6eb6312d3d1e83 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Thu, 08 Oct 2026 11:57:19 +0800
Subject: [PATCH] feat(stations): 添加每轮止损次数统计功能

---
 src/main/java/com/xcong/excoin/modules/station/service/impl/StationServiceImpl.java |   89 ++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 88 insertions(+), 1 deletions(-)

diff --git a/src/main/java/com/xcong/excoin/modules/station/service/impl/StationServiceImpl.java b/src/main/java/com/xcong/excoin/modules/station/service/impl/StationServiceImpl.java
index 8bc76bd..6b29015 100644
--- a/src/main/java/com/xcong/excoin/modules/station/service/impl/StationServiceImpl.java
+++ b/src/main/java/com/xcong/excoin/modules/station/service/impl/StationServiceImpl.java
@@ -18,7 +18,10 @@
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -65,6 +68,38 @@
         persistConfig(apiKeyMd5, params);
         // 2. 发送 MQ 命令,通知 JAR(需手动点击「启动」才生效)
         cmdProducer.sendCommand(apiKeyMd5, "UPDATE_CONFIG", JSON.toJSONString(params));
+    }
+
+    // ==================== 手动控制指令 ====================
+
+    @Override
+    public void cancelLongTpSl(String apiKeyMd5) {
+        cmdProducer.sendCommand(apiKeyMd5, "CANCEL_LONG_TP_SL");
+    }
+
+    @Override
+    public void cancelShortTpSl(String apiKeyMd5) {
+        cmdProducer.sendCommand(apiKeyMd5, "CANCEL_SHORT_TP_SL");
+    }
+
+    @Override
+    public void rebuildLongTpSl(String apiKeyMd5) {
+        cmdProducer.sendCommand(apiKeyMd5, "REBUILD_LONG_TP_SL");
+    }
+
+    @Override
+    public void rebuildShortTpSl(String apiKeyMd5) {
+        cmdProducer.sendCommand(apiKeyMd5, "REBUILD_SHORT_TP_SL");
+    }
+
+    @Override
+    public void closeLong(String apiKeyMd5) {
+        cmdProducer.sendCommand(apiKeyMd5, "CLOSE_LONG");
+    }
+
+    @Override
+    public void closeShort(String apiKeyMd5) {
+        cmdProducer.sendCommand(apiKeyMd5, "CLOSE_SHORT");
     }
 
     // ==================== 别名 ====================
@@ -131,6 +166,51 @@
     @Override
     public List<StrategyEventLog> getEvents(String apiKeyMd5) {
         return strategyEventLogDao.selectByApiKeyMd5(apiKeyMd5);
+    }
+
+    @Override
+    public List<Map<String, Object>> getRoundStopLossCount(String apiKeyMd5) {
+        // selectByApiKeyMd5 按 event_time DESC 返回(新→旧),反转为升序(旧→新)
+        List<StrategyEventLog> events = strategyEventLogDao.selectByApiKeyMd5(apiKeyMd5);
+        List<StrategyEventLog> asc = new ArrayList<>(events);
+        Collections.reverse(asc);
+
+        List<Map<String, Object>> rounds = new ArrayList<>();
+        int longCount = 0;
+        int shortCount = 0;
+
+        for (StrategyEventLog e : asc) {
+            String type = e.getEventType();
+            if ("STOP_LOSS_TRIGGERED".equals(type)) {
+                JSONObject p = JSON.parseObject(e.getPayloadJson());
+                String direction = p.getString("direction");
+                if ("long".equals(direction)) {
+                    longCount++;
+                } else if ("short".equals(direction)) {
+                    shortCount++;
+                }
+            } else if ("ROUND_COMPLETE".equals(type)) {
+                // 轮次结束(重置前):快照当前累计的止损次数,然后清零进入下一轮
+                JSONObject p = JSON.parseObject(e.getPayloadJson());
+                Map<String, Object> row = new LinkedHashMap<>();
+                row.put("roundNum", p.getIntValue("roundNum"));
+                row.put("longLossCount", longCount);
+                row.put("shortLossCount", shortCount);
+                rounds.add(row);
+                longCount = 0;
+                shortCount = 0;
+            }
+        }
+
+        // 当前进行中的轮次(尚未 ROUND_COMPLETE):若已有止损则追加
+        if (longCount > 0 || shortCount > 0) {
+            Map<String, Object> row = new LinkedHashMap<>();
+            row.put("roundNum", null);
+            row.put("longLossCount", longCount);
+            row.put("shortLossCount", shortCount);
+            rounds.add(row);
+        }
+        return rounds;
     }
 
     @Override
@@ -209,8 +289,15 @@
         if (params.containsKey("addPositionQuantity")) status.setAddPositionQuantity(intVal(params.get("addPositionQuantity")));
         if (params.containsKey("maxPositionPerSide")) status.setMaxPositionPerSide(intVal(params.get("maxPositionPerSide")));
         if (params.containsKey("addPositionStartThreshold")) status.setAddPositionStartThreshold(intVal(params.get("addPositionStartThreshold")));
-        if (params.containsKey("placeExcessTakeProfit")) status.setPlaceExcessTakeProfit(boolVal(params.get("placeExcessTakeProfit")));
+        if (params.containsKey("placeExcessTakeProfit")) status.setPlaceExcessTakeProfit(str(params.get("placeExcessTakeProfit")));
         if (params.containsKey("priceDriveEnabled")) status.setPriceDriveEnabled(boolVal(params.get("priceDriveEnabled")));
+        if (params.containsKey("dualStopLossEnabled")) status.setDualStopLossEnabled(boolVal(params.get("dualStopLossEnabled")));
+        if (params.containsKey("longStopLossPrice")) status.setLongStopLossPrice(str(params.get("longStopLossPrice")));
+        if (params.containsKey("shortStopLossPrice")) status.setShortStopLossPrice(str(params.get("shortStopLossPrice")));
+        if (params.containsKey("tpTriggerOppositeAddEnabled")) status.setTpTriggerOppositeAddEnabled(boolVal(params.get("tpTriggerOppositeAddEnabled")));
+        if (params.containsKey("tpTriggerOppositeAddQuantity")) status.setTpTriggerOppositeAddQuantity(intVal(params.get("tpTriggerOppositeAddQuantity")));
+        if (params.containsKey("baseTakeProfitPerGrid")) status.setBaseTakeProfitPerGrid(str(params.get("baseTakeProfitPerGrid")));
+        if (params.containsKey("baseTakeProfitGridCount")) status.setBaseTakeProfitGridCount(intVal(params.get("baseTakeProfitGridCount")));
         if (params.containsKey("rounds")) status.setTotalRounds(intVal(params.get("rounds")));
         if (params.containsKey("principal")) status.setPrincipal(str(params.get("principal")));
     }

--
Gitblit v1.9.1