From e2e4ac0aa43cac2427d2726071f20bb4a2cf3ae1 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Mon, 10 Aug 2026 13:56:23 +0800
Subject: [PATCH] feat(gate): 添加加仓止损配置功能

---
 src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java         |  229 +++++++++----------------------------
 src/main/resources/static/gate-config.html                                       |   27 ++++
 src/main/java/com/xcong/excoin/modules/gateApi/GateConfigDTO.java                |   12 ++
 src/main/java/com/xcong/excoin/modules/gateApi/GateConfigPersistenceService.java |    4 
 src/main/java/com/xcong/excoin/modules/gateApi/GateConfig.java                   |   36 ++++++
 5 files changed, 133 insertions(+), 175 deletions(-)

diff --git a/src/main/java/com/xcong/excoin/modules/gateApi/GateConfig.java b/src/main/java/com/xcong/excoin/modules/gateApi/GateConfig.java
index 31cc1f8..5f9ab69 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateConfig.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateConfig.java
@@ -111,6 +111,14 @@
     private final boolean priceDriveEnabled;
     /** 策略运行轮数:达到盈利后重启算一轮,达到上限后不再重启。0=不限轮数 */
     private final int rounds;
+    /** 止损次数统计方式:"single"=单向分别统计 / "dual"=双向统一统计,默认 "dual" */
+    private final String stopLossCountMode;
+    /** 加仓间隔:每隔多少次止损触发一次加仓,默认 3 */
+    private final int addPositionInterval;
+    /** 加仓数量:每次加仓追加的张数,默认 1 */
+    private final int addPositionQuantity;
+    /** 单边最大仓位量:单向持仓张数上限,超出则按上限挂单,0=不限制 */
+    private final int maxPositionPerSide;
     /** 网格绝对步长(shortBaseEntryPrice × gridRate),运行时由队列生成逻辑设置 */
     private BigDecimal step;
     /** 网格元素列表,由队列初始化时同步填充,包含完整的多空仓挂单状态 */
@@ -146,6 +154,10 @@
         this.takeProfitGridSpan = builder.takeProfitGridSpan;
         this.priceDriveEnabled = builder.priceDriveEnabled;
         this.rounds = builder.rounds;
+        this.stopLossCountMode = builder.stopLossCountMode;
+        this.addPositionInterval = builder.addPositionInterval;
+        this.addPositionQuantity = builder.addPositionQuantity;
+        this.maxPositionPerSide = builder.maxPositionPerSide;
     }
 
     // ==================== REST/WS 地址 ====================
@@ -241,6 +253,14 @@
     public boolean isPriceDriveEnabled() { return priceDriveEnabled; }
     /** @return 策略运行轮数上限,0=不限 */
     public int getRounds() { return rounds; }
+    /** @return 止损次数统计方式:"single"=单向分别统计 / "dual"=双向统一统计 */
+    public String getStopLossCountMode() { return stopLossCountMode; }
+    /** @return 加仓间隔:每隔多少次止损触发一次加仓 */
+    public int getAddPositionInterval() { return addPositionInterval; }
+    /** @return 加仓数量:每次加仓追加的张数 */
+    public int getAddPositionQuantity() { return addPositionQuantity; }
+    /** @return 单边最大仓位量:单向持仓张数上限,0=不限制 */
+    public int getMaxPositionPerSide() { return maxPositionPerSide; }
 
     // ==================== 运行时参数 ====================
 
@@ -341,6 +361,14 @@
         private boolean priceDriveEnabled = true;
         /** 策略运行轮数上限,0=不限轮数。默认 0 */
         private int rounds = 0;
+        /** 止损次数统计方式:"single"=单向分别统计 / "dual"=双向统一统计,默认 "dual" */
+        private String stopLossCountMode = "dual";
+        /** 加仓间隔:每隔多少次止损触发一次加仓,默认 3 */
+        private int addPositionInterval = 3;
+        /** 加仓数量:每次加仓追加的张数,默认 1 */
+        private int addPositionQuantity = 1;
+        /** 单边最大仓位量:单向持仓张数上限,超出则按上限挂单,0=不限制 */
+        private int maxPositionPerSide = 0;
 
         /** 设置 API Key */
         public Builder apiKey(String apiKey) { this.apiKey = apiKey; return this; }
@@ -392,6 +420,14 @@
         public Builder priceDriveEnabled(boolean priceDriveEnabled) { this.priceDriveEnabled = priceDriveEnabled; return this; }
         /** 设置策略运行轮数上限:达到盈利后重启计数,达到此值后不再重启。0=不限 */
         public Builder rounds(int rounds) { this.rounds = rounds; return this; }
+        /** 设置止损次数统计方式:"single"=单向分别统计 / "dual"=双向统一统计 */
+        public Builder stopLossCountMode(String stopLossCountMode) { this.stopLossCountMode = stopLossCountMode; return this; }
+        /** 设置加仓间隔:每隔多少次止损触发一次加仓 */
+        public Builder addPositionInterval(int addPositionInterval) { this.addPositionInterval = addPositionInterval; return this; }
+        /** 设置加仓数量:每次加仓追加的张数 */
+        public Builder addPositionQuantity(int addPositionQuantity) { this.addPositionQuantity = addPositionQuantity; return this; }
+        /** 设置单边最大仓位量:单向持仓张数上限,0=不限制 */
+        public Builder maxPositionPerSide(int maxPositionPerSide) { this.maxPositionPerSide = maxPositionPerSide; return this; }
 
         public GateConfig build() {
             return new GateConfig(this);
diff --git a/src/main/java/com/xcong/excoin/modules/gateApi/GateConfigDTO.java b/src/main/java/com/xcong/excoin/modules/gateApi/GateConfigDTO.java
index bd054b4..7a897dd 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateConfigDTO.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateConfigDTO.java
@@ -57,6 +57,14 @@
     private Boolean priceDriveEnabled;
     /** 策略运行轮数上限:达到盈利后重启计数,达到此值后不再重启。0=不限 */
     private int rounds;
+    /** 止损次数统计方式:"single"=单向分别统计 / "dual"=双向统一统计,默认 "dual" */
+    private String stopLossCountMode;
+    /** 加仓间隔:每隔多少次止损触发一次加仓,默认 3 */
+    private int addPositionInterval;
+    /** 加仓数量:每次加仓追加的张数,默认 1 */
+    private int addPositionQuantity;
+    /** 单边最大仓位量:单向持仓张数上限,超出则按上限挂单,0=不限制 */
+    private int maxPositionPerSide;
 
     /**
      * 从 GateConfig 构建 DTO(不暴露 apiSecret)。
@@ -84,6 +92,10 @@
                 .isProduction(config.isProduction())
                 .priceDriveEnabled(config.isPriceDriveEnabled())
                 .rounds(config.getRounds())
+                .stopLossCountMode(config.getStopLossCountMode())
+                .addPositionInterval(config.getAddPositionInterval())
+                .addPositionQuantity(config.getAddPositionQuantity())
+                .maxPositionPerSide(config.getMaxPositionPerSide())
                 .build();
     }
 }
diff --git a/src/main/java/com/xcong/excoin/modules/gateApi/GateConfigPersistenceService.java b/src/main/java/com/xcong/excoin/modules/gateApi/GateConfigPersistenceService.java
index f6e7000..abc0bc7 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateConfigPersistenceService.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateConfigPersistenceService.java
@@ -106,6 +106,10 @@
                 .takeProfitGridSpan(dto.getTakeProfitGridSpan())
                 .priceDriveEnabled(nvl(dto.getPriceDriveEnabled(), true))
                 .rounds(dto.getRounds())
+                .stopLossCountMode(nvl(dto.getStopLossCountMode(), "dual"))
+                .addPositionInterval(dto.getAddPositionInterval() > 0 ? dto.getAddPositionInterval() : 3)
+                .addPositionQuantity(dto.getAddPositionQuantity() > 0 ? dto.getAddPositionQuantity() : 1)
+                .maxPositionPerSide(dto.getMaxPositionPerSide())
                 .build();
     }
 
diff --git a/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java b/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
index 28b49de..11b3e00 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
@@ -676,7 +676,6 @@
                 log.info("[Gate] 空单成交 gridId:{}, 当前持仓:{}张", filledQty, posSize);
 
 
-                checkShortStopLongProfit(accumulatedShortLossCount, shortGridElement.getId() + 2);
 
             }
         }
@@ -706,8 +705,6 @@
                 // [Gate] 止盈挂单:超出基础仓位的部分,挂在空仓第一止损位
 //                placeExcessTakeProfit(posSize, true);
                 log.info("[Gate] 多单成交 gridId:{}, 当前持仓:{}张", filledQty, posSize);
-
-                checkLongStopShortProfit(accumulatedLongLossCount, longGridElement.getId() -2);
 
             }
         }
@@ -1274,6 +1271,56 @@
         }
     }
 
+    // ========== 加仓计算 ==========
+
+    /**
+     * 根据 {@code stopLossCountMode} 计算当前有效的止损次数。
+     * <ul>
+     *   <li>{@code "single"}(单向):返回该方向的累计止损次数</li>
+     *   <li>{@code "dual"}(双向):返回多空双向累计总次数</li>
+     * </ul>
+     *
+     * @param isLong {@code true}=多仓方向,{@code false}=空仓方向
+     * @return 有效止损次数
+     */
+    private int getEffectiveStopLossCount(boolean isLong) {
+        if ("single".equals(config.getStopLossCountMode())) {
+            return isLong ? accumulatedLongLossCount : accumulatedShortLossCount;
+        }
+        // "dual" — 双向总次数
+        return accumulatedLongLossCount + accumulatedShortLossCount;
+    }
+
+    /**
+     * 根据加仓配置计算止损追单时的实际下单量。
+     * <p>公式:</p>
+     * <pre>
+     * addMultiplier = floor(effectiveStopLossCount / addPositionInterval)
+     * addQty = addMultiplier × addPositionQuantity
+     * finalQty = min(baseQuantity + addQty, maxPositionPerSide > 0 ? maxPositionPerSide : ∞)
+     * </pre>
+     *
+     * @param isLong {@code true}=多仓方向,{@code false}=空仓方向
+     * @return 实际下单张数(字符串)
+     */
+    private String calculateEntryQuantity(boolean isLong) {
+        int baseQty = Integer.parseInt(config.getBaseQuantity());
+        int interval = config.getAddPositionInterval();
+        int addQtyPerUnit = config.getAddPositionQuantity();
+        int maxPerSide = config.getMaxPositionPerSide();
+
+        int effectiveCount = getEffectiveStopLossCount(isLong);
+        int addMultiplier = interval > 0 ? effectiveCount / interval : 0;
+        int addQty = addMultiplier * addQtyPerUnit;
+        int totalQty = baseQty + addQty;
+
+        if (maxPerSide > 0 && totalQty > maxPerSide) {
+            totalQty = maxPerSide;
+        }
+
+        return String.valueOf(totalQty);
+    }
+
     private void handleLongStopLossTriggered(GridElement gridElement, String orderId) {
         gridElement.removeLongStopLossOrderId(orderId);
 
@@ -1300,8 +1347,9 @@
 
         // 止损追单:同一网格可有多笔挂单,不判断 isHasLongOrder,直接挂单
         BigDecimal triggerPrice = newEntryGrid.getGridPrice();
-//        String size = String.valueOf(Math.max(queryPositionSize(Position.ModeEnum.DUAL_SHORT), shortPositionSize.intValue()) + Integer.parseInt(config.getQuantity()));
-        String size = String.valueOf(config.getBaseQuantity());
+        String size = calculateEntryQuantity(true);
+        log.info("[Gate] 多仓止损追单 有效次数:{}, 基础:{}张 → 实际:{}张, 模式:{}",
+                getEffectiveStopLossCount(true), config.getBaseQuantity(), size, config.getStopLossCountMode());
         newEntryGrid.getLongTraderParam().setQuantity(size);
         placeEntryOrderWithPreFlag(newEntryGrid, true, triggerPrice,
                 FuturesPriceTrigger.RuleEnum.NUMBER_1, size);
@@ -1317,172 +1365,6 @@
                 });
             }
         }
-
-    }
-
-    // ========== 止损触发操作表 ==========
-    /**
-     * value = int[2]: {对手满仓止盈%, 基础仓位阈值%}
-     * <ul>
-     *   <li>[0] 对手满仓:挂对手仓位此%做止盈;0=不挂</li>
-     *   <li>[1] 基础仓位阈值(占maxPos的%):对手/己方仓位超出此阈值部分做止盈;0=不挂</li>
-     * </ul>
-     */
-    private static final Map<Integer, int[]> STOP_LOSS_RULES = new LinkedHashMap<>();
-    private static final int DEFAULT_STOP_LOSS_PERCENT = 5;
-
-    static {
-        // times  {满仓%, 阈值%}
-        put(1,  new int[]{0,  0});
-        put(2,  new int[]{0,  0});
-        put(3,  new int[]{15, 0});
-        put(4,  new int[]{0, 0});
-        put(5,  new int[]{0, 0});
-        put(6,  new int[]{30, 40});
-        put(7,  new int[]{0, 0});
-        put(8,  new int[]{0, 0});
-        put(9,  new int[]{40, 50});
-        put(10, new int[]{0, 0});
-        put(11, new int[]{0, 0});
-        put(12, new int[]{50, 60});
-        put(13, new int[]{0, 0});
-        put(14, new int[]{0, 0});
-        put(15, new int[]{60, 60});
-        put(16, new int[]{0, 0});
-        put(17, new int[]{0, 0});
-        put(18, new int[]{65, 65});
-        put(19, new int[]{0, 0});
-        put(20, new int[]{0, 0});
-        put(21, new int[]{70, 70});
-        put(22, new int[]{0, 0});
-        put(23, new int[]{0, 0});
-        put(24, new int[]{75, 75});
-        put(25, new int[]{0, 0});
-        put(26, new int[]{0, 0});
-        put(27, new int[]{80, 80});
-        put(28, new int[]{0, 0});
-        put(29, new int[]{0, 0});
-        put(30, new int[]{85, 85});
-        put(31, new int[]{0, 0});
-        put(32, new int[]{0, 0});
-    }
-
-    private static void put(int times, int[] rule) { STOP_LOSS_RULES.put(times, rule); }
-
-    private static int[] ruleOf(int times) {
-        int[] r = STOP_LOSS_RULES.get(times);
-        return r != null ? r : STOP_LOSS_RULES.get(32);
-    }
-
-    /**
-     * 多仓止损触发后,查表处理对手盘(空仓)盈止 + 己方盈止 + 激活。
-     */
-    private void checkLongStopShortProfit(int times, int gridId) {
-        int[] r = ruleOf(times);
-        int maxPos = Integer.parseInt(config.getBaseQuantity());
-        if (maxPos <= 0) {
-            return;
-        }
-
-        int fullTpPct = r[0];     // 对手满仓 → 止盈%
-        int fullTpPctNum = maxPos * (100- fullTpPct)/ 100;
-        int thresholdPct = r[1];  // 基础仓位阈值%
-        int thresholdPosNum = maxPos * (100- thresholdPct)/ 100;
-
-        // ---------- 对手盘(空仓)盈止 ----------
-        int oppPos = Math.max(queryPositionSize(Position.ModeEnum.DUAL_SHORT), shortPositionSize.intValue());
-        boolean isFullTp = oppPos > fullTpPctNum;
-        if (isFullTp) {
-            // 对手仓位超出阈值:超额整个挂一笔止盈,位置在触发格 - 1
-            int excess = oppPos - fullTpPctNum;
-            GridElement tpElem = GridElement.findById(gridId);
-            if (tpElem != null) {
-                placeTakeProfitAtGrid(tpElem, false, excess, times);
-            }
-        }
-
-        // ---------- 己方(多仓)盈止(≥6次) ----------
-//        if (times >= 6 && thresholdPct > 0) {
-//            int selfPos = Math.max(queryPositionSize(Position.ModeEnum.DUAL_LONG), longPositionSize.intValue());
-//            int excess = selfPos - thresholdPosNum;
-//            if (excess > 0) {
-//                int perOrderQty = maxPos * DEFAULT_STOP_LOSS_PERCENT / 100;
-//                if (perOrderQty <= 0) {
-//                    perOrderQty = 1;
-//                }
-//                int totalOrders = excess / perOrderQty;
-//                if (totalOrders <= 0) {
-//                    totalOrders = 1;
-//                }
-//                int entryGridId = 0; // 多仓建仓格
-//                for (int i = 0; i < totalOrders; i++) {
-//                    int tpGridId = entryGridId + 2 * (i + 1); // 朝盈利方向(向上)间隔2格
-//                    GridElement tpElem = GridElement.findById(tpGridId);
-//                    if (tpElem == null) {
-//                        break;
-//                    }
-//                    placeTakeProfitAtGrid(tpElem, true, perOrderQty, times);
-//                }
-//                log.info("[Gate] 止损{}→己方多仓止盈, excess:{}, 挂{}单, 每单{}张",
-//                        times, excess, totalOrders, perOrderQty);
-//            }
-//        }
-
-    }
-
-    /**
-     * 空仓止损触发后,查表处理对手盘(多仓)盈止 + 己方盈止。
-     */
-    private void checkShortStopLongProfit(int times, int gridId) {
-        int[] r = ruleOf(times);
-        int maxPos = Integer.parseInt(config.getBaseQuantity());
-        if (maxPos <= 0) {
-            return;
-        }
-
-        int fullTpPct = r[0];     // 对手满仓 → 止盈%
-        int fullTpPctNum = maxPos * (100 - fullTpPct) / 100;
-        int thresholdPct = r[1];  // 基础仓位阈值%
-        int thresholdPosNum = maxPos * (100 - thresholdPct) / 100;
-
-        // ---------- 对手盘(多仓)盈止 ----------
-        int oppPos = Math.max(queryPositionSize(Position.ModeEnum.DUAL_LONG), longPositionSize.intValue());
-        boolean isFullTp = oppPos > fullTpPctNum;
-        if (isFullTp) {
-            // 对手仓位超出阈值:超额整个挂一笔止盈,位置在触发格
-            int excess = oppPos - fullTpPctNum;
-            GridElement tpElem = GridElement.findById(gridId);
-            if (tpElem != null) {
-                placeTakeProfitAtGrid(tpElem, true, excess, times);
-            }
-        }
-
-        // ---------- 己方(空仓)盈止(≥6次) ----------
-//        if (times >= 6 && thresholdPct > 0) {
-//            int selfPos = Math.max(queryPositionSize(Position.ModeEnum.DUAL_SHORT), shortPositionSize.intValue());
-//            int excess = selfPos - thresholdPosNum;
-//            if (excess > 0) {
-//                int perOrderQty = maxPos * DEFAULT_STOP_LOSS_PERCENT / 100;
-//                if (perOrderQty <= 0) {
-//                    perOrderQty = 1;
-//                }
-//                int totalOrders = excess / perOrderQty;
-//                if (totalOrders <= 0) {
-//                    totalOrders = 1;
-//                }
-//                int entryGridId = 0; // 空仓建仓格
-//                for (int i = 0; i < totalOrders; i++) {
-//                    int tpGridId = entryGridId - 2 * (i + 1); // 朝盈利方向(向下)间隔2格
-//                    GridElement tpElem = GridElement.findById(tpGridId);
-//                    if (tpElem == null) {
-//                        break;
-//                    }
-//                    placeTakeProfitAtGrid(tpElem, false, perOrderQty, times);
-//                }
-//                log.info("[Gate] 止损{}→己方空仓止盈, excess:{}, 挂{}单, 每单{}张",
-//                        times, excess, totalOrders, perOrderQty);
-//            }
-//        }
 
     }
 
@@ -1510,8 +1392,9 @@
 
         // 止损追单:同一网格可有多笔挂单,不判断 isHasShortOrder,直接挂单
         BigDecimal triggerPrice = newEntryGrid.getGridPrice();
-//        String size = String.valueOf(Math.max(queryPositionSize(Position.ModeEnum.DUAL_LONG), longPositionSize.intValue()) + Integer.parseInt(config.getQuantity()));
-        String size = String.valueOf(config.getBaseQuantity());
+        String size = calculateEntryQuantity(false);
+        log.info("[Gate] 空仓止损追单 有效次数:{}, 基础:{}张 → 实际:{}张, 模式:{}",
+                getEffectiveStopLossCount(false), config.getBaseQuantity(), size, config.getStopLossCountMode());
         newEntryGrid.getShortTraderParam().setQuantity(size);
         placeEntryOrderWithPreFlag(newEntryGrid, false, triggerPrice,
                 FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(size));
diff --git a/src/main/resources/static/gate-config.html b/src/main/resources/static/gate-config.html
index 1b41299..d3582e4 100644
--- a/src/main/resources/static/gate-config.html
+++ b/src/main/resources/static/gate-config.html
@@ -117,6 +117,20 @@
             <div class="form-group"><label>止盈网格跨度</label><input id="takeProfitGridSpan" placeholder="2"></div>
             <div class="form-group"><label>价格驱动开关</label><select id="priceDriveEnabled"><option value="true" selected>开启</option><option value="false">关闭</option></select></div>
             <div class="form-group"><label>运行轮数(0=不限)</label><input id="rounds" type="number" placeholder="0"></div>
+            <!-- ===== 加仓止损配置 ===== -->
+            <div class="form-group full" style="margin-top:6px;border-top:1px solid var(--border);padding-top:8px">
+                <label style="font-size:13px;font-weight:600;color:var(--text)">加仓 & 止损统计</label>
+            </div>
+            <div class="form-group">
+                <label>止损次数统计方式</label>
+                <select id="stopLossCountMode">
+                    <option value="dual" selected>双向统一统计</option>
+                    <option value="single">单向分别统计</option>
+                </select>
+            </div>
+            <div class="form-group"><label>加仓间隔(次)</label><input id="addPositionInterval" type="number" placeholder="3"></div>
+            <div class="form-group"><label>加仓数量(张)</label><input id="addPositionQuantity" type="number" placeholder="1"></div>
+            <div class="form-group"><label>单边最大仓位量(张)</label><input id="maxPositionPerSide" type="number" placeholder="0(不限制)"></div>
         </form>
     </div>
 
@@ -192,7 +206,8 @@
     } catch(e) {
         // 文件不存在 → 用默认值填充,用户可编辑后保存
         fillForm({ gridRate:0.005, expectedProfit:0.15, maxLoss:1.5,
-            baseQuantity:'2', quantity:'2', maxPositionSize:4, stopLossCount:0, takeProfitGridSpan:2, priceDriveEnabled: true, rounds: 0 });
+            baseQuantity:'2', quantity:'2', maxPositionSize:4, stopLossCount:0, takeProfitGridSpan:2, priceDriveEnabled: true, rounds: 0,
+            stopLossCountMode:'dual', addPositionInterval:3, addPositionQuantity:1, maxPositionPerSide:0 });
         toast('未找到配置,已加载默认值,编辑后请保存', 'success');
     }
     currentApiKey = key;
@@ -212,6 +227,10 @@
     byId('takeProfitGridSpan').value = d.takeProfitGridSpan ?? '';
     byId('priceDriveEnabled').value = (d.priceDriveEnabled === true || d.priceDriveEnabled === 'true') ? 'true' : 'false';
     byId('rounds').value = d.rounds ?? 0;
+    byId('stopLossCountMode').value = d.stopLossCountMode || 'dual';
+    byId('addPositionInterval').value = d.addPositionInterval ?? 3;
+    byId('addPositionQuantity').value = d.addPositionQuantity ?? 1;
+    byId('maxPositionPerSide').value = d.maxPositionPerSide ?? 0;
 }
 
 function collectForm() {
@@ -226,7 +245,11 @@
         stopLossCount: parseInt(byId('stopLossCount').value) || 0,
         takeProfitGridSpan: parseInt(byId('takeProfitGridSpan').value) || 2,
         priceDriveEnabled: byId('priceDriveEnabled').value === 'true',
-        rounds: parseInt(byId('rounds').value) || 0
+        rounds: parseInt(byId('rounds').value) || 0,
+        stopLossCountMode: byId('stopLossCountMode').value,
+        addPositionInterval: parseInt(byId('addPositionInterval').value) || 3,
+        addPositionQuantity: parseInt(byId('addPositionQuantity').value) || 1,
+        maxPositionPerSide: parseInt(byId('maxPositionPerSide').value) || 0
     };
 }
 

--
Gitblit v1.9.1