美文网首页程序员的量化世界财经投资理财
数字货币--网格变形策略之单边网格 (注释版)

数字货币--网格变形策略之单边网格 (注释版)

作者: 发明者量化FMZ | 来源:发表于2017-09-09 13:06 被阅读114次

    网格变形策略之单边网格 (注释版)

    策略介绍

    • 网格可以自定义方向

    • 先买后卖:
      网格会从首价格开始向下挂买单, 每个买单间隔 "价格间隔" 这个参数, 挂单数量为"单笔数量", 挂够 "总数量" 个买单, 有任意买单成交以后, 程序会在买价基础上加 "价差(元)" 这个参数的的值的价格挂出卖单, 卖出, 卖出以后,重新按原来这个网格的价格挂买入单

    • 先卖后买:
      操作刚好相反

    • 策略最大的风险就是单边行情, 价格波动超出网格范围.

    • 网格带有自动止损和移动功能

    • 注释心得:

      策略使用了 虚拟挂单设计, 对于交易所 限制挂单数量,做出了很棒的 处理,灵活的解决了该问题。

      网格逻辑设计灵活,结构巧妙。

      盈亏计算,各个数值统计 算法可借鉴,各个条件检测设计严谨。(能尽量 减少BUG 出现可能)

      源代码 非常值得学习。

    • 注释代码

    /*  界面参数 (代码中体现为全局变量)
    变量                                  描述                  类型                       默认值         
    OpType                              网格方向                下拉框(selected)           先买后卖|先卖后买
    FirstPriceAuto                      首价格自动               布尔型(true/false)         true
    FirstPrice@!FirstPriceAuto          首价格                  数字型(number)             100
    AllNum                              总数量                  数字型(number)             10
    PriceGrid                           价格间隔                数字型(number)              1
    PriceDiff                           价差(元)                数字型(number)              2
    AmountType                          订单大小                下拉框(selected)           买卖同量|自定义量
    AmountOnce@AmountType==0            单笔数量                数字型(number)             0.1
    BAmountOnce@AmountType==1           买单大小                数字型(number)             0.1
    SAmountOnce@AmountType==1           卖单大小                数字型(number)             0.1
    AmountCoefficient@AmountType==0     量差                    字符串(string)             *1
    AmountDot                           量小数点最长位数          数字型(number)             3
    EnableProtectDiff                   开启价差保护             布尔型(true/false)         false
    ProtectDiff@EnableProtectDiff       入市价差保护             数字型(number)              20
    CancelAllWS                         停止时取消所有挂单        布尔型(true/false)         true
    CheckInterval                       轮询间隔                数字型(number)              2000
    Interval                            失败重试间隔             数字型(number)              1300
    RestoreProfit                       恢复上次盈利             布尔型(true/false)          false
    LastProfit@RestoreProfit            上次盈利                数字型(number)               0
    ProfitAsOrg@RestoreProfit           上次盈利算入均价          布尔型(true/false)          false
    EnableAccountCheck                  启用资金检验             布尔型(true/false)          true
    EnableStopLoss@EnableAccountCheck   开启止损                布尔型(true/false)          false
    StopLoss@EnableStopLoss             最大浮动亏损(元)         数字型(number)              100
    StopLossMode@EnableStopLoss         止损后操作              下拉框(selected)             回收并退出|回收再撒网
    EnableStopWin@EnableAccountCheck    开启止盈                布尔型(true/false)           false
    StopWin@EnableStopWin               最大浮动盈利(元)         数字型(number)               120
    StopWinMode@EnableStopWin           止盈后操作               下拉框(selected)            回收并退出|回收再撒网
    AutoMove@EnableAccountCheck         自动移动                 布尔型(true/false)          false
    MaxDistance@AutoMove                最大距离(元)             数字型(number)               20
    MaxIdle@AutoMove                    最大空闲(秒)             数字型(number)               7200
    EnableDynamic                       开启动态挂单              布尔型(true/false)          false
    DynamicMax@EnableDynamic            订单失效距离(元)          数字型(number)               30
    ResetData                           启动时清空所有数据         布尔型(true/false)           true
    Precision                           价格小数位长度            数字型(number)                5
    */
    
    function hasOrder(orders, orderId) {                           // 检测 参数 orders 中 是否有  ID 为 orderId 的订单
        for (var i = 0; i < orders.length; i++) {                  // 遍历 orders 检测 是否 有相同的 id , 找到 返回 true
            if (orders[i].Id == orderId) {
                return true;
            }
        }
        return false;                                              // 全部遍历完 ,没有触发 if 则 没有找到 ID 为 orderId 的订单, 返回 false
    }
    
    
    function cancelPending() {                                      // 取消所有挂单 函数
        var ret = false;                                            // 设置 返回成功  标记变量
        while (true) {                                              // while 循环
            if (ret) {                                              // 如果 ret 为 true 则 Sleep 一定时间
                Sleep(Interval);
            }
            var orders = _C(exchange.GetOrders);                    // 调用  API 获取 交易所 未完成的订单信息
            if (orders.length == 0) {                               // 如果返回的是  空数组, 即 交易所 没有未完成的订单。
                break;                                              // 跳出 while 循环
            }
    
            for (var j = 0; j < orders.length; j++) {               // 遍历 未完成的 订单数组, 并根据索引j 逐个使用  orders[j].Id 去 取消订单。
                exchange.CancelOrder(orders[j].Id, orders[j]);
                ret = true;                                         // 一旦有取消操作, ret 赋值 为 true 。用于触发 以上 Sleep , 等待后重新 exchange.GetOrders 检测 
            }
        }
        return ret;                                                 // 返回 ret
    }
    
    function valuesToString(values, pos) {                      // 值 转换 为字符串
        var result = '';                                        // 声明一个用于返回的  空字符串  result
        if (typeof(pos) === 'undefined') {                      // 如果 没有传入  pos 这个参数 ,给 pos 赋值 0
            pos = 0;
        }
        for (var i = pos; i < values.length; i++) {             // 根据 传入的 pos 处理 values 数组
            if (i > pos) {                                      // 除了第一次 循环 之后 在result 字符串后 加上 ' ' 一个空格
                result += ' ';
            }
            if (values[i] === null) {                           // 如果 values (函数 参数列表 数组) 当前的索引的 元素 为 null 则 result 添加 'null'字符串
                result += 'null';
            } else if (typeof(values[i]) == 'undefined') {      // 如果 是 未定义的, 则添加 'undefined'
                result += 'undefined';
            } else {                                            // 剩余类型 做 switch 检测 分别处理
                switch (values[i].constructor.name) {           // 检查 values[i] 的 constructor 的 name 属性, 即类型 名称
                    case 'Date':
                    case 'Number':
                    case 'String':
                    case 'Function':
                        result += values[i].toString();         // 如果是 日期类型、数值类型、字符串类型、函数类型 ,调用其 toString 函数 转换为字符串 后,添加
                        break;
                    default:
                        result += JSON.stringify(values[i]);    // 其他情况 则 使用JSON.stringify 函数 转换为 JSON 字符串 添加到 result 
                        break;
                }
            }
        }
        return result;                                          // 返回 result
    }
    
    function Trader() {                                                 // Trader 函数 ,使用闭包。
        var vId = 0;                                                    // 订单递增ID
        var orderBooks = [];                                            // 订单薄
        var hisBooks = [];                                              // 历史订单薄
        var orderBooksLen = 0;                                          // 订单薄长度
        this.Buy = function(price, amount, extra) {                     // 买函数, 参数: 价格 、数量、扩展信息
            if (typeof(extra) === 'undefined') {                        // 如果参数 extra 未传入,即 typeof 返回 undefined 
                extra = '';                                             // 给 extra 赋值空字符串
            } else {
                extra = valuesToString(arguments, 2);                   // 调用 this.Buy 函数 时传入的参数 arguments ,传入 valuesToString函数中
            }
            vId++;                                                      // 
            var orderId = "V" + vId;                                    //
            orderBooks[orderId] = {                                     // 向订单薄 数组中添加 属性 orderId, 用构造的 对象 对其初始化。
                Type: ORDER_TYPE_BUY,                                   // 构造的对象  Type 属性:  类型    买单
                Status: ORDER_STATE_PENDING,                            //                       状态    挂起
                Id: 0,                                                  //                       订单ID  0
                Price: price,                                           //                       价格    参数 price
                Amount: amount,                                         //                       订单量   参数 amount 
                Extra: extra                                            //                       扩展信息  经valuesToString 处理过的字符串。
            };
            orderBooksLen++;                                            // 订单薄的长度 累计加1
            return orderId;                                             // 返回 本次构造的订单的  orderId (非交易所订单ID ,别混淆。)
        };
        this.Sell = function(price, amount, extra) {                    // 和 thie.Buy 基本类似, 构造卖单。
            if (typeof(extra) === 'undefined') {
                extra = '';
            } else {
                extra = valuesToString(arguments, 2);
            }
            vId++;
            var orderId = "V" + vId;
            orderBooks[orderId] = {
                Type: ORDER_TYPE_SELL,
                Status: ORDER_STATE_PENDING,
                Id: 0,
                Price: price,
                Amount: amount,
                Extra: extra
            };
            orderBooksLen++;
            return orderId;
        };
        this.GetOrders = function() {                                   // 获取未完成的订单信息
            var orders = _C(exchange.GetOrders);                        // 调用 API GetOrders 获取 未完成的订单信息 赋值给 orders
            for (orderId in orderBooks) {                               // 遍历 Trader 对象中的 orderBooks 
                var order = orderBooks[orderId];                        // 根据 orderId 取出 订单
                if (order.Status !== ORDER_STATE_PENDING) {             // 如果 order 的状态不等于  挂起状态 ,就跳过本次循环
                    continue;
                }
                var found = false;                                      // 初始化 found 变量(标记 是否找到) 为 true
                for (var i = 0; i < orders.length; i++) {               // 遍历 API 返回的未完成的订单的数据  
                    if (orders[i].Id == order.Id) {                     // 找到 和 orderBooks 中 未完成订单 , id 相同的订单时,给found 赋值 true,代表找到。 
                        found = true;                                   
                        break;                                          // 跳出当前循环
                    }
                }
                if (!found) {                                           // 如果 没有找到,则 向 orders push  orderBooks[orderId]。
                    orders.push(orderBooks[orderId]);                   // 为何要这样 push ?
                }
            }
            return orders;                                              // 返回 orders
        }
        this.GetOrder = function(orderId) {                             // 获取订单
            if (typeof(orderId) === 'number') {                         // 如果传入的 参数 orderId 是数值类型 
                return exchange.GetOrder(orderId);                      // 调用 API GetOrder 根据 orderId 获取 订单信息并返回。
            }
            if (typeof(hisBooks[orderId]) !== 'undefined') {            // typeof(hisBooks[orderId]) 如果不等于 未定义的
                return hisBooks[orderId];                               // 返回 hisBooks 中 属性为 orderId 的数据
            }
            if (typeof(orderBooks[orderId]) !== 'undefined') {          // 同上, orderBooks 中如果有 属性为 orderId的值存在, 返回这个数据。
                return orderBooks[orderId];
            }
            return null;                                                // 如果不符合上述条件触发, 返回 null
        };
        this.Len = function() {                                         // 返回 Trader 的 orderBookLen 变量, 即返回订单薄长度。
            return orderBooksLen;
        };
        this.RealLen = function() {                                     // 返回 订单薄中 激活订单数量。
            var n = 0;                                                  // 初始计数 为 0
            for (orderId in orderBooks) {                               // 遍历 订单薄
                if (orderBooks[orderId].Id > 0) {                       // 如果 在遍历中 当前 的订单的 Id 大于0 ,即 非初始时的0, 表明订单已下单,该订单已经激活。
                    n++;                                                // 累计 已经激活的订单
                }
            }
            return n;                                                   // 返回 n值, 即返回 真实 订单薄长度。(已激活订单数量)
        };
        this.Poll = function(ticker, priceDiff) {                       // 
            var orders = _C(exchange.GetOrders);                        // 获取 所有未完成的订单
            for (orderId in orderBooks) {                               // 遍历 订单薄
                var order = orderBooks[orderId];                        // 取出当前 的订单 赋值给 order
                if (order.Id > 0) {                                     // 如果订单 为 激活状态,即 order.Id 不为0(已经下过单)
                    var found = false;                                  // 变量 found(标记找到) 为 false
                    for (var i = 0; i < orders.length; i++) {           // 在交易所返回的 未完成订单信息中 查找 相同的订单号
                        if (order.Id == orders[i].Id) {                 // 如果查找到, 给found 赋值 true ,代表已找到。
                            found = true;
                        }
                    }
                    if (!found) {                                       // 如果当前的 orderId 代表的订单 没有在 交易所返回的未完成订单数组orders中找到对应的。
                        order.Status = ORDER_STATE_CLOSED;              // 给 orderBooks 中对应 orderId 的订单(即当前的order变量)更新,Status 属性更新为 ORDER_STATE_CLOSED (即 已关闭) 
                        hisBooks[orderId] = order;                      // 完成的订单 记录在 历史订单薄里,即 hisBooks ,统一,且唯一的订单号 orderId
                        delete(orderBooks[orderId]);                    // 删除 订单薄的 名为 orderId值的 属性。(完成的订单 从中 删除)
                        orderBooksLen--;                                // 订单薄 长度自减
                        continue;                                       // 以下代码 跳过继续循环。
                    }
                }
                var diff = _N(order.Type == ORDER_TYPE_BUY ? (ticker.Buy - order.Price) : (order.Price - ticker.Sell));
                // diff 为 当前订单薄 中 订单的  计划开仓价和 当前实时开仓价格的差值。
    
                var pfn = order.Type == ORDER_TYPE_BUY ? exchange.Buy : exchange.Sell;   // 根据订单的类型,给 pfn 赋值相应的  API 函数 引用。
                // 即 如果 order 的类型是买单 , pfn 就是  exchange.Buy 函数的引用, 卖单同理。
    
                if (order.Id == 0 && diff <= priceDiff) {                                // 如果 订单薄中的订单 order 没有激活(即Id 等于0 ) 并且 当前价格距离 订单计划价格 小于等于 参数传入的 priceDiff
                    var realId = pfn(order.Price, order.Amount, order.Extra + "(距离: " + diff + (order.Type == ORDER_TYPE_BUY ? (" 买一: " + ticker.Buy) : (" 卖一: " + ticker.Sell))+")");
                    // 执行下单函数 ,参数传入 价格、数量、 订单扩展信息 + 挂单距离 + 行情数据(买一 或者 卖一),返回 交易所 订单id
    
                    if (typeof(realId) === 'number') {    // 如果 返回的  realId 是数值类型
                        order.Id = realId;                // 赋值给 订单薄 当前的订单  order的 Id 属性。
                    }
                } else if (order.Id > 0 && diff > (priceDiff + 1)) {  // 如果订单 处于激活状态, 并且 当前距离 大于 参数传入的 距离
                    var ok = true;                                    // 声明一个 用于标记的变量   初始 true 
                    do {                                              // 先执行 do 再判断 while
                        ok = true;                                    // ok 赋值 true
                        exchange.CancelOrder(order.Id, "不必要的" + (order.Type == ORDER_TYPE_BUY ? "买单" : "卖单"), "委托价:", order.Price, "量:", order.Amount, ", 距离:", diff, order.Type == ORDER_TYPE_BUY ? ("买一: " + ticker.Buy) : ("卖一: " + ticker.Sell));
                        // 取消 当前 超出 范围的挂单, 在取消订单这条日志后 打印 当前订单的信息、当前 距离 diff。
    
                        Sleep(200);                                   // 等待 200 毫秒
                        orders = _C(exchange.GetOrders);              // 调用 API 获取 交易所 中 未完成的订单。
                        for (var i = 0; i < orders.length; i++) {     // 遍历 这些未完成的订单。
                            if (orders[i].Id == order.Id) {           // 如果找到 取消的订单 还在 交易所未完成的订单数组中
                                ok = false;                           // 给 ok 这个变量赋值 false , 即没有 取消成功
                            }
                        }
                    } while (!ok);                                    // 如果 ok 为 false,则 !ok 为 true ,while 就会继续重复循环,继续取消这个订单,并检测是否取消成功
                    order.Id = 0;                                     // 给 order.Id 赋值 0 , 代表 当前这个订单 是未激活的。
                }
            }
        };
    }
    
    function balanceAccount(orgAccount, initAccount) {               // 平衡账户 函数   参数 策略启动时最初始的账户信息 , 本次撒网前初始账户信息
        cancelPending();                                             // 调用自定义函数  cancelPending()  取消所有挂单。
        var nowAccount = _C(exchange.GetAccount);                    // 声明一个  变量  nowAccount 用来记录 此刻 账户的最新信息。
        var slidePrice = 0.2;                                        // 设置下单时 的滑价 为 0.2
        var ok = true;                                               // 标记变量 初始  true
        while (true) {                                               // while 循环
            var diff = _N(nowAccount.Stocks - initAccount.Stocks);   // 计算出 当前 账户 和  初始账户 的币差 diff
            if (Math.abs(diff) < exchange.GetMinStock()) {           // 如果 币差的绝对值 小于 交易所 的最小交易量,break 跳出循环,不进行平衡操作。
                break;
            }
            var depth = _C(exchange.GetDepth);                       // 获取 交易所深度信息  赋值给 声明的 depth 变量 
            var books = diff > 0 ? depth.Bids : depth.Asks;          // 根据 币差 的大于0 或者 小于 0 ,提取 depth 中的 买单数组 或者 卖单数组(等于0 不会处理,在判断小于GetMinStock 的时候已经break)
                                                                     // 币差大于0 要卖出平衡,所以看买单数组,  币差小于0 相反。
            var n = 0;                                               // 声明 n 初始为 0
            var price = 0;                                           // 声明 price 初始 0
            for (var i = 0; i < books.length; i++) {                 // 遍历 买单 或者 卖单 数组
                n += books[i].Amount;                                // 根据 遍历的索引 i , 累计每次的 订单的Amount (订单量)
                if (n >= Math.abs(diff)) {                           // 如果 累计的 订单量 n 大于等于 币差,则:
                    price = books[i].Price;                          // 获取 当前索引的订单的 价格,赋值给 price
                    break;                                           // 跳出 当前 for 遍历循环
                }
            }
            var pfn = diff > 0 ? exchange.Sell : exchange.Buy;       // 根据 币差 大于0 或者 小于 0 , 将 下卖单 API(exchange.Sell) 或者 下买单 API(exchange.Buy) 引用传递给 声明的 pfn
            var amount = Math.abs(diff);                             // 将要平衡操作的 下单量 为 diff 即 币差,   赋值给 声明的 amount 变量
            var price = diff > 0 ? (price - slidePrice) : (price + slidePrice);    // 根据币差 决定的 买卖方向 ,在 price 的基础上 增加 或者 减去 滑价(滑价是为了更容易成交),再赋值给 price
            Log("开始平衡", (diff > 0 ? "卖出" : "买入"), amount, "个币");            // 输出 日志 平衡的 币数。
            if (diff > 0) {                                                        // 根据币差 决定的 买卖方向 , 检测账户币数 或者 钱数是否足够。
                amount = Math.min(nowAccount.Stocks, amount);                      // 确保下单量 amount 不会超过 当前 账户 的可用币数。
            } else {
                amount = Math.min(nowAccount.Balance / price, amount);             // 确保下单量 amount 不会超过 当前 账户 的可用钱数。
            }
            if (amount < exchange.GetMinStock()) {                                 // 检测 最终下单数量 是否 小于 交易所 允许的最小下单量
                Log("资金不足, 无法平衡到初始状态");                                    // 如果 下单量过小,则打印 信息。
                ok = false;                                                         // 标记 平衡失败
                break;                                                              // 跳出 while 循环
            }
            pfn(price, amount);                                                     // 执行 下单 API (pfn 引用)
            Sleep(1000);                                                            // 暂停 1 秒
            cancelPending();                                                        // 取消所有挂单。
            nowAccount = _C(exchange.GetAccount);                                   // 获取当前 最新账户信息
        }
        if (ok) {                                                                   // 当 ok 为 true (平衡成功) 时执行 花括号内代码
            LogProfit(_N(nowAccount.Balance - orgAccount.Balance));                 // 用传入的参数 orgAccount (平衡前的账户信息)的Balance 属性 减去当前的 账户信息的 Balance 属性,即 钱数之差, 
                                                                                    // 也就是 盈亏 (因币数不变,略有误差 因为有些很小的 量不能平衡)
            Log("平衡完成", nowAccount);                                              // 输出日志 平衡完成。
        }
    }
    
    var STATE_WAIT_OPEN = 0;                                                        // 用于 fishTable 中每个 节点的  状态
    var STATE_WAIT_COVER = 1;                                                       // ...
    var STATE_WAIT_CLOSE = 2;                                                       // ...
    var ProfitCount = 0;                                                            // 盈亏次数 记录
    var BuyFirst = true;                                                            // 初始 界面参数
    var IsSupportGetOrder = true;                                                   // 交易所 是否支持  GetOrder API 函数, 全局变量, 用于 main 函数开始的判断
    var LastBusy = 0;                                                               // 记录上次 处理的时间对象
    
    function setBusy() {                            // 设置 Busy 时间
        LastBusy = new Date();                      // 给 LastBusy 赋值当前的时间对象
    }
    
    function isTimeout() {                                                          // 判断是否超时
        if (MaxIdle <= 0) {                                                         // 最大空闲时间(基于 是否自动 移动网格), 如果 最大空闲时间 MaxIdle 设置小于等于0
            return false;                                                           // 返回 false, 不判断 超时。即 总是返回false 未超时。
        }
        var now = new Date();                                                       // 获取当前时间对象
        if (((now.getTime() - LastBusy.getTime()) / 1000) >= MaxIdle) {             // 使用当前时间对象的 getTime 函数 获取时间戳 与 LastBusy 的时间戳 计算差值,
                                                                                    // 除以1000 算出 两个时间对象间 相差的秒数。 判断是否大于 最大空闲时间MaxIdle
            LastBusy = now;                                                         // 如果是大于,  更新 LastBusy 为当前时间对象 now
            return true;                                                            // 返回 true ,即超时。
        }
        return false;                                                               // 返回 false 未超时
    }
    
    function onexit() {                             // 程序 退出 时的收尾函数。
        if (CancelAllWS) {                          // 设置了 停止时取消所有挂单,则 调用 cancelPending() 函数 取消所有挂单
            Log("正在退出, 尝试取消所有挂单");
            cancelPending();
        }
        Log("策略成功停止");
        Log(_C(exchange.GetAccount));               // 打印退出程序时的  账户持仓信息。
    }
    
    
    function fishing(orgAccount, fishCount) {    // 撒网  参数 : 账户信息 ,撒网次数
        setBusy();                               // 设置 LastBuys 为当前 时间戳
        var account = _C(exchange.GetAccount);   // 声明一个  account  变量 , 获取当前 账户信息  并 赋值。
        Log(account);                            // 输出  本次调用 fishing 函数 开始 时的账户信息。
        var InitAccount = account;               // 声明一个 变量 InitAccount 并用 account 赋值。 此处是 记录 本次 撒网 前的 初始账户资金,用于计算 浮动盈亏。
        var ticker = _C(exchange.GetTicker);     // 获取 行情 赋值给 声明的 ticker 变量
        var amount = _N(AmountOnce);             // 根据 界面参数 单笔数量,使用 _N 处理小数位(_N 默认 保留2位),赋值给 amount 。
        var amountB = [amount];                  // 声明一个 变量 叫  amountB  是一个数组,用 amount 初始化 一个元素
        var amountS = [amount];                  // 声明一个 变量 叫  amountS  ...
        if (typeof(AmountType) !== 'undefined' && AmountType == 1) {     // 按自定义量 ,订单大小类型 这个界面参数如果不是未定义的,
                                                                         //并且 AmountType 在界面上设定为 自定义量,即AmountType 值为 1 (下拉框的索引) 
            for (var idx = 0; idx < AllNum; idx++) {      // AllNum 总数量。 如果是设置自定义量, 根据总数量 循环一定次数 给amountB/amountS 即买卖单量数组赋值
                amountB[idx] = BAmountOnce;               // 使用界面参数  给买单量数组 赋值
                amountS[idx] = SAmountOnce;               // ...         给卖单...
            }
        } else {                                          // 其它
            for (var idx = 1; idx < AllNum; idx++) {      // 根据网格总数量 循环。
                switch (AmountCoefficient[0]) {           // 根据界面参数 差量 这个字符串的 第一个 字符,即 AmountCoefficient[0] 是 '+'、'-'、'*'、'/'
                    case '+':                             // 根据 界面参数 进行 构造 下单量加法递增的网格。
                        amountB[idx] = amountB[idx - 1] + parseFloat(AmountCoefficient.substring(1));
                        break;
                    case '-':                             // ... 
                        amountB[idx] = amountB[idx - 1] - parseFloat(AmountCoefficient.substring(1));
                        break;
                    case '*':
                        amountB[idx] = amountB[idx - 1] * parseFloat(AmountCoefficient.substring(1));
                        break;
                    case '/':
                        amountB[idx] = amountB[idx - 1] / parseFloat(AmountCoefficient.substring(1));
                        break;
                }
                amountB[idx] = _N(amountB[idx], AmountDot);   // 买单 、买单 量相同,处理好数据小数位。
                amountS[idx] = amountB[idx];                  // 赋值。
            }
        }
        if (FirstPriceAuto) {                                 // 如果界面参数设置了 首价格自动 为 true ,执行 if 花括号内代码。
            FirstPrice = BuyFirst ? _N(ticker.Buy - PriceGrid, Precision) : _N(ticker.Sell + PriceGrid, Precision);
            // 界面参数  FirstPrice 根据 BuyFirst全局变量(声明初始为true,在main开始已经根据OpType赋值)设定第一个价格,用此刻行情 ticker 和 界面参数 PriceGrid 价格间距去设定。 
        }
        // Initialize fish table    初始化网格
        var fishTable = {};                         // 声明一个 网格对象
        var uuidTable = {};                         // 识别码 表格对象
        var needStocks = 0;                         // 所需币数 变量
        var needMoney = 0;                          // 所需 钱  变量
        var actualNeedMoney = 0;                    // 实际需要的 钱
        var actualNeedStocks = 0;                   // 实际需要的 币
        var notEnough = false;                      // 资金不足 标记变量, 初始设置为false
        var canNum = 0;                             // 可用 网格
        for (var idx = 0; idx < AllNum; idx++) {    // 根据 网格数 AllNum 去遍历 构造。
            var price = _N((BuyFirst ? FirstPrice - (idx * PriceGrid) : FirstPrice + (idx * PriceGrid)), Precision);
            // 遍历构造时,当前的索引idx 的 价格 设置 根据 BuyFirst 去设置。 每个索引价格 之间的间距 为 PriceGrid .
            needStocks += amountS[idx];                      // 卖出所需 币数 随着 循环逐步 累计。(由 卖单量数组逐个累计到 needStocks)
            needMoney += price * amountB[idx];               // 买入所需 钱数 随着 循环逐步 累计。(.... 买单量数组逐个累计...)
            if (BuyFirst) {                                  // 处理 先买 
                if (_N(needMoney) <= _N(account.Balance)) {  // 如果 网格所需的钱 小于 账户上的可用钱数
                    actualNeedMondy = needMoney;             // 赋值给 实际所需要的钱数
                    actualNeedStocks = needStocks;           // 赋值给 实际所需要的币数    该出有些问题?
                    canNum++;                                // 累计 可用网格数
                } else {                                     // _N(needMoney) <= _N(account.Balance) 该条件不满足,则设置 资金不足标记变量 为 true
                    notEnough = true;
                }
            } else {                                         // 处理 先卖
                if (_N(needStocks) <= _N(account.Stocks)) {  // 检测 所需币数 是不是 小于 账户 可用币数
                    actualNeedMondy = needMoney;             // 赋值
                    actualNeedStocks = needStocks;
                    canNum++;                                // 累计可用网格数
                } else {
                    notEnough = true;                        // 不满足资金条件 ,就设置  true
                }
            }
            fishTable[idx] = STATE_WAIT_OPEN;                // 根据当前索引idx,设置网格对象的idx成员(网格结点)的状态,初始为STATE_WAIT_OPEN(等待开仓)
            uuidTable[idx] = -1;                             // 编号对象 也根据当前 idx 初始化 自己的idx 值(对应 fishTable 的节点)为 -1
        }
        if (!EnableAccountCheck && (canNum < AllNum)) {      // 如果不启用资金检验, 并且 可开 节点 小于 界面参数设置的网格数量(节点总数)时。
            Log("警告, 当前资金只可做", canNum, "个网格, 全网共需", (BuyFirst ? needMoney : needStocks), "请保持资金充足");   // Log 输出 警告信息。
            canNum = AllNum;                                                                                          // 更新可开数量 为界面参数的设置
        }
        if (BuyFirst) {                                                                         // 先买
            if (EnableProtectDiff && (FirstPrice - ticker.Sell) > ProtectDiff) {                // 开启差价保护 并且 入市价格 减去 此刻卖一 大于 入市差价保护
                throw "首次买入价比市场卖1价高" + _N(FirstPrice - ticker.Sell, Precision) + ' 元';  // 抛出错误 信息。
            } else if (EnableAccountCheck && account.Balance < _N(needMoney)) {                 // 如果启用资金检验  并且 账户 可用钱数 小于 网格所需资金钱数。
                if (fishCount == 1) {                                                           // 如果是第一次撒网
                    throw "资金不足, 需要" + _N(needMoney) + "元";                                // 抛出错误 资金不足
                } else {
                    Log("资金不足, 需要", _N(needMoney), "元, 程序只做", canNum, "个网格 #ff0000");  // 如果不是 第一次 撒网, 输出提示信息。
                }
            } else {                                                                            // 其他情况, 没有开启 资金检验 、差价保护 等
                Log('预计动用资金: ', _N(needMoney), "元");                                        // 输出 预计动用资金。
            }
        } else {                                                                                // 先卖, 一下类似 先买
            if (EnableProtectDiff && (ticker.Buy - FirstPrice) > ProtectDiff) {
                throw "首次卖出价比市场买1价高 " + _N(ticker.Buy - FirstPrice, Precision) + ' 元';
            } else if (EnableAccountCheck && account.Stocks < _N(needStocks)) {
                if (fishCount == 1) {
                    throw "币数不足, 需要 " + _N(needStocks) + " 个币";
                } else {
                    Log("资金不足, 需要", _N(needStocks), "个币, 程序只做", canNum, "个网格 #ff0000");
                }
            } else {
                Log('预计动用币数: ', _N(needStocks), "个, 约", _N(needMoney), "元");
            }
        }
    
        var trader = new Trader();                                          // 构造一个 Trader 对象, 赋值给 此处声明的 trader 变量。
        var OpenFunc = BuyFirst ? exchange.Buy : exchange.Sell;             // 根据 是否先买后卖 ,设定开仓函数OpenFunc 是 引用 exchange.Buy 还是 exchange.Sell
        var CoverFunc = BuyFirst ? exchange.Sell : exchange.Buy;            // 同上
        if (EnableDynamic) {                                                // 根据界面参数 EnableDynamic (是否动态挂单) 是否开启, 去再次 设定 OpenFunc/CoverFunc
            OpenFunc = BuyFirst ? trader.Buy : trader.Sell;                 // 引用 trader 对象的 成员函数 Buy 用于 动态挂单 (主要是由于一些交易所 限制挂单数量,所以就需要虚拟动态挂单)
            CoverFunc = BuyFirst ? trader.Sell : trader.Buy;                // 同上
        }
        var ts = new Date();                                                // 创建此刻时间对象(赋值给ts),用于记录此刻时间。
        var preMsg = "";                                                    // 声明一个 变量 用于记录 上次信息, 初始 空字符串
        var profitMax = 0;                                                  // 最大收益 
        while (true) {                                                      // 网格 撒网后的 主要 逻辑
            var now = new Date();                                           // 记录 当前循环 开始的时的时间
            var table = null;                                               // 声明一个 变量
            if (now.getTime() - ts.getTime() > 5000) {                      // 计算当前 时间 now 和 记录的时间 ts 之间的差值 是否大于 5000 毫秒
                if (typeof(GetCommand) == 'function' && GetCommand() == "收网") {         // 检测是否 接收到 策略 交互控件 命令  “收网”,停止并平衡到初始状态
                    Log("开始执行命令进行收网操作");                                          // 输出 信息 
                    balanceAccount(orgAccount, InitAccount);                              // 执行平衡函数 ,平衡币数 到初始状态
                    return false;                                                         // 本次 撒网函数  fishing 返回 false
                }
                ts = now;                                                                 // 用当前时间 now 更新 ts,用于下次比对时间
                var nowAccount = _C(exchange.GetAccount);                                 // 声明 nowAccount 变量  并初始为当前最新 账户信息。
                var ticker = _C(exchange.GetTicker);                                      // 声明 ticker 变量 并初始为当前行情信息
                if (EnableDynamic) {                                                      // 如果开启动态挂单
                    trader.Poll(ticker, DynamicMax);                                      // 调用 trader 对象的 Poll 函数 ,根据 当前 ticker 行情,和 界面参数 DynamicMax(订单失效距离)检测 并 处理 所有订单。
                }
                var amount_diff = (nowAccount.Stocks + nowAccount.FrozenStocks) - (InitAccount.Stocks + InitAccount.FrozenStocks);  // 计算当前的 币差
                var money_diff = (nowAccount.Balance + nowAccount.FrozenBalance) - (InitAccount.Balance + InitAccount.FrozenBalance); // 计算当前的 钱差
                var floatProfit = _N(money_diff + (amount_diff * ticker.Last));           // 计算 当前 本次撒网 的浮动盈亏
                var floatProfitAll = _N((nowAccount.Balance + nowAccount.FrozenBalance - orgAccount.Balance - orgAccount.FrozenBalance) + ((nowAccount.Stocks + nowAccount.FrozenStocks - orgAccount.Stocks - orgAccount.FrozenStocks) * ticker.Last));
                // 计算 总体的浮动盈亏
    
                var isHold = Math.abs(amount_diff) >= exchange.GetMinStock();             // 如果 此刻 币差 绝对值 大于 交易所最小 交易量 ,代表已经持仓
                if (isHold) {                                                             // 已经持仓 则执行  setBusy() 函数,该函数会给 LastBusy 更新时间。
                    setBusy();                                                            // 即  开仓后开始  启动开仓机制。
                }
    
                profitMax = Math.max(floatProfit, profitMax);                             // 刷新 最大浮动盈亏
                if (EnableAccountCheck && EnableStopLoss) {                               // 如果启动账户检测 并且 启动 止损
                    if ((profitMax - floatProfit) >= StopLoss) {                          // 如果 最大浮动盈亏 减去 当前浮动盈亏 大于等于 最大浮动亏损值,则执行 花括号内代码
                        Log("当前浮动盈亏", floatProfit, "利润最高点: ", profitMax, "开始止损");   // 输出信息
                        balanceAccount(orgAccount, InitAccount);                          // 平衡账户
                        if (StopLossMode == 0) {                                          // 根据 止损模式 处理, 如果 StopLossMode 等于 0 ,即 止损后退出程序。
                            throw "止损退出";                                               // 抛出错误 “止损退出”  策略停止。
                        } else {
                            return true;                                                   // 除了 止损后退出模式,  即 : 止损后重新撒网。
                        }
                    }
                }
                if (EnableAccountCheck && EnableStopWin) {                                 // 如果开启了 检测账户 并且 开启了 止盈
                    if (floatProfit > StopWin) {                                           // 如果 浮动盈亏 大于 止盈
                        Log("当前浮动盈亏", floatProfit, "开始止盈");                         // 输出日志
                        balanceAccount(orgAccount, InitAccount);                           // 平衡账户 恢复初始 (止盈)
                        if (StopWinMode == 0) {                                            // 根据止盈模式 处理。
                            throw "止盈退出";                                                // 止盈后退出
                        } else {
                            return true;                                                    // 止盈后 返回 true , 继续撒网
                        }
                    }
                }
                var distance = 0;                                                           // 声明 一个 变量 用来 记录 距离
                if (EnableAccountCheck && AutoMove) {                                       // 如果开启 账户检测 并且 网格自动移动
                    if (BuyFirst) {                                                         // 如果是 先买后卖 
                        distance = ticker.Last - FirstPrice;                                // 给 distance 赋值 : 当前的价格 减去 首价格,算出距离 
                    } else {                                                                // 其他情况 : 先卖后买
                        distance = FirstPrice - ticker.Last;                                // 给 distance 赋值 : 首价格 减去 当前价格,算出距离
                    }
                    var refish = false;                                                     // 是否重新撒网 标记变量
                    if (!isHold && isTimeout()) {                                           // 如果没有持仓(isHold 为 false) 并且 超时(isTimeout 返回 true)
                        Log("空仓过久, 开始移动网格");                                         
                        refish = true;                                                      // 标记 重新撒网 
                    }
                    if (distance > MaxDistance) {                                           // 如果 当前 的距离 大于 界面参数设定的最大距离, 标记 重新撒网
                        Log("价格超出网格区间过多, 开始移动网格, 当前距离: ", _N(distance, Precision), "当前价格:", ticker.Last);
                        refish = true;
                    }
                    if (refish) {                                                           // 如果 refish 是 true ,则执行 平衡函数 
                        balanceAccount(orgAccount, InitAccount);
                        return true;                                                        // 本次 撒网函数 返回 true
                    }
                }
    
                var holdDirection, holdAmount = "--",                                       // 声明 三个 变量,持仓方向、持仓数量、持仓价格
                    holdPrice = "--";
                if (isHold) {                                                               // 持仓时
                    if (RestoreProfit && ProfitAsOrg) {                                     // 如果 开启 恢复上次盈利 并且 上次盈利算入均价
                        if (BuyFirst) {                                                     // 如果是先买后卖 
                            money_diff += LastProfit;                                       // 把上次盈利 加入 money_diff ,即 上次收益 折合入 钱差(在先买的情况,钱差为负值,即花费的),折合入开仓成本。
                        } else {                                                            // 如果是先卖后买
                            money_diff -= LastProfit;                                       // 先卖后买 钱差 为 正值 , why - ?
                        }
                    }
    
                    // 处理先买后卖
                    holdAmount = amount_diff;                                               // 币差 赋值 给持仓数量 (此刻币差 即 持仓)
                    holdPrice = (-money_diff) / amount_diff;                                // 用 钱差 除以 币差 算出 持仓均价, 
                                                                                            // 注意 : 如果 money_diff 为 负值 ,则amount_diff 一定为正值,所以一定要在 money_diff 前加 负号,这样算出的价格才是 正数
                    // 处理先卖后买
                    if (!BuyFirst) {                                                        // 如果是 先卖后买 则触发 更新 持仓量 和 持仓均价
                        holdAmount = -amount_diff;                                          // 币差为负数  ,所以取反
                        holdPrice = (money_diff) / -amount_diff;                            // 计算持仓均价。
                    }
                    holdAmount = _N(holdAmount, 4);                                         // 持仓量,保留4位小数。
                    holdPrice = _N(holdPrice, Precision);                                   // 持仓均价, 保留 Precision 位小数。
                    holdDirection = BuyFirst ? "多" : "空";                                  // 根据 先买后卖 或者 先卖后买 给 holdDirection 赋值 多 或者 空
                } else {                                                                    // 如果 isHold 为false ,给holdDirection 赋值 "--"
                    holdDirection = "--";
                }
                table = {                                                                   // 给声明 的 table 变量 赋值一个 对象,用于在 BotVS 机器人 状态栏上显示 表格信息
                    type: 'table',                                                          // 详见 API 文档 LogStatus 函数, 这里给 type 属性 初始化 'table' 用于在状态栏显示成表格
                    title: '运行状态',                                                        // 表格的 标题
                    cols: ['动用资金', '持有仓位', '持仓大小', '持仓均价', '总浮动盈亏', '当前网格盈亏', '撒网次数', '网格偏移', '真实委托', '最新币价'],   // 表格的 列名
                    rows: [                                                                                                                   // 表格的 逐行的数据
                        [_N(actualNeedMondy, 4), holdDirection, holdAmount, holdPrice, _N(floatProfitAll, 4) + ' ( ' + _N(floatProfitAll * 100 / actualNeedMondy, 4) + ' % )', floatProfit, fishCount, (AutoMove && distance > 0) ? ((BuyFirst ? "向上" : "向下") + "偏离: " + _N(distance) + " 元") : "--", trader.RealLen(), ticker.Last]
                        // 一行数据
                    ]
                };
                
            }                                                                               // 每间隔 5 秒处理 一些任务, 并更新 机器人状态栏 表格对象 table 
            
            var orders = _C(trader.GetOrders);                                              // 获取 所有未完成的订单
            if (table) {                                                                    // 如果 table 已经被 赋值表格对象
                if (!EnableDynamic) {                                                       // 如果没有开启动态挂单
                    table.rows[0][8] = orders.length;                                       // 在状态栏 表格 第一行 第9列 位置 更新 挂单数组的长度
                }
                LogStatus('`' + JSON.stringify(table) + '`');                               // 调用 BotVS 平台 API LogStatus 显示 设置的状态栏表格
            }
            for (var idx = 0; idx < canNum; idx++) {                                        // 遍历 可用的 网格节点数量。
                var openPrice = _N((BuyFirst ? FirstPrice - (idx * PriceGrid) : FirstPrice + (idx * PriceGrid)), Precision);        // 随着 节点 索引 idx 遍历,构造每个节点的 开仓价 (方向由 先买后卖,或者先卖后买 决定) 
                var coverPrice = _N((BuyFirst ? openPrice + PriceDiff : openPrice - PriceDiff), Precision);     // 开仓平仓价差,即 每个节点的盈利空间
                var state = fishTable[idx];                                                                     // 赋值 渔网 节点的状态
                var fishId = uuidTable[idx];                                                                    // 编号
                
                // 此处判断作用为: 过滤 未完成的订单
                if (hasOrder(orders, fishId)) {                                                                 // 如果 所有未完成订单,即挂单数组 中有ID为 fishId 的订单
                    continue;                                                                                   // 跳过本次循环  继续循环
                }
    
                if (fishId != -1 && IsSupportGetOrder) {                                                        // 网格 节点 id 不等于 初始值,即下过订单,并且 交易所支持 GetOrder
                    var order = trader.GetOrder(fishId);                                                        // 获取 该 fishId 号 的订单
                    // 此处判断作用为: 过滤 没有找到订单 的 网格节点,以下判断(state == STATE_WAIT_COVER) 等等 的逻辑不会触发
                    if (!order) {                                                                               // 如果 !order 为真 即获取订单失败 
                        Log("获取订单信息失败, ID: ", fishId);                                                     // 输出日志
                        continue;                                                                               // 跳过本次循环 继续循环
                    }
                    // 此处判断作用为: 过滤 处于挂起状态,未成交,或者 未完全成交的 网格节点, 以下判断(state == STATE_WAIT_COVER) 等等 的逻辑不会触发
                    if (order.Status == ORDER_STATE_PENDING) {                                                  // 如果订单状态 是在交易所 挂起状态
                        //Log("订单状态为未完成, ID: ", fishId);
                        continue;                                                                               // 跳过本次循环 继续循环
                    }
                }
    
                if (state == STATE_WAIT_COVER) {                                                                // 如果 当前节点 状态是 等待平仓
                    var coverId = CoverFunc(coverPrice, (BuyFirst ? amountS[idx] : amountB[idx]), (BuyFirst ? '完成买单:' : '完成卖单:'), openPrice, '量:', (BuyFirst ? amountB[idx] : amountS[idx]));
                    // 调用 平仓 函数 CoverFunc 挂出 平仓单
    
                    if (typeof(coverId) === 'number' || typeof(coverId) === 'string') {        // 判断 如果 平仓函数 返回的 Id 为 数值(由BotVS API 直接返回) 或者 字符串(由 trader 对象的 Buy/Sell函数返回)
                        fishTable[idx] = STATE_WAIT_CLOSE;                                     // 已经挂出  平仓单, 更新状态为 :  STATE_WAIT_CLOSE 即等待 节点任务完成
                        uuidTable[idx] = coverId;                                              // 把 订单号 储存在 uuidTable 对应的 idx 位置上。
                    }
                } else if (state == STATE_WAIT_OPEN || state == STATE_WAIT_CLOSE) {            // 如果状态是 等待开仓 或者 等待完成
                    var openId = OpenFunc(openPrice, BuyFirst ? amountB[idx] : amountS[idx]);  // 下开仓单。
                    if (typeof(openId) === 'number' || typeof(openId) === 'string') {          // 判断是否下单成功
                        fishTable[idx] = STATE_WAIT_COVER;                                     // 更新状态 为等待平仓
                        uuidTable[idx] = openId;                                               // 记录当前 节点 订单ID
                        if (state == STATE_WAIT_CLOSE) {                                       // 如果是等待完成 (开仓订单下了后 才会触发)
                            ProfitCount++;                                                     // 累计盈利次数
                            var account = _C(exchange.GetAccount);                             // 获取当前账户信息
                            var ticker = _C(exchange.GetTicker);                               // 获取当前行情信息
                            var initNet = _N(((InitAccount.Stocks + InitAccount.FrozenStocks) * ticker.Buy) + InitAccount.Balance + InitAccount.FrozenBalance, 8);
                            // 计算 初始 资产 净值
                            var nowNet = _N(((account.Stocks + account.FrozenStocks) * ticker.Buy) + account.Balance + account.FrozenBalance, 8);
                            // 计算 当前 资产 净值
                            var actualProfit = _N(((nowNet - initNet)) * 100 / initNet, 8);    // 计算 收益率
                            if (AmountType == 0) {                                             // 根据 买卖同量  , 自定义量  不同的处理。
                                var profit = _N((ProfitCount * amount * PriceDiff) + LastProfit, 8);      // 计算: 所有盈利节点的 盈亏 和 上次撒网盈亏 之和 即 总盈亏
                                Log((BuyFirst ? '完成卖单:' : '完成买单:'), coverPrice, '量:', (BuyFirst ? amountS[idx] : amountB[idx]), '平仓收益', profit);
                                // 输出 订单完成信息
                            } else {
                                Log((BuyFirst ? '完成卖单:' : '完成买单:'), coverPrice, '量:', (BuyFirst ? amountS[idx] : amountB[idx]));
                            }
                        }
                    }
                }
            }
            Sleep(CheckInterval);                        // 网格逻辑 主要 while 循环检测, 每次 暂停一定时间 CheckInterval 即:检测间隔
        }
        return true;                                     // 本次撒网完成 返回 true
    }
    
    function main() {                                    // 策略主函数,程序从这里开始执行。
        if (ResetData) {                                 // RestData 为界面参数, 默认 true , 控制 启动时 是否清空所有数据。默认全部清空。
            LogProfitReset();                            // 执行 API LogProfitReset 函数,清空 所有收益。
            LogReset();                                  // 执行 API LogReset 函数, 清空 所有日志。
        }
        // exchange.SetMaxDigits(Precision)              // 已废弃,使用 exchange.SetPrecision 代替。
        exchange.SetPrecision(Precision, 3)              // exchange.SetPrecision(2, 3); // 设置价格小数位精度为2位, 品种下单量小数位精度为3位
                                                         // Precision 为界面参数。
    
        if (typeof(AmountType) === 'undefined') {        // 订单 数量类型, 0:“买卖同量” ,  1:“自定义量”  , 检测 如果该参数是 未定义的,默认设置 0 。
            AmountType = 0;                              // typeof 会 检测 AmountType 的类型, 如果是 undefined  即 “未定义” ,则给 AmountType 赋值 0。
        }
        if (typeof(AmountDot) === 'undefined') {         // 订单量 小数点 最长位数 AmountDot 如果是 未定义的, 设置 AmountDot 为 3 。
            AmountDot = 3;                               // 其实已经由 exchange.SetPrecision(Precision, 3) 设置过了,在底层会截断处理。
        }
        if (typeof(EnableDynamic) === 'undefined') {     // 检测 是否 开启动态挂单 参数,  如果 EnableDynamic 是未定义的, 设置 为 false 即 不开启。
            EnableDynamic = false;
        }
        if (typeof(AmountCoefficient) === 'undefined') { // 如果未定义, 默认设置   "*1"
            AmountCoefficient = "*1";
        }
        if (typeof(EnableAccountCheck) === 'undefined') {// 如果未定义, 启用资金检验 参数 设置为 true ,即 开启。
            EnableAccountCheck = true;
        }
        BuyFirst = (OpType == 0);                        // 根据 OpType 的设置 去 给BuyFirst 赋值, OpType 设置网格类型, 0: 先买后卖, 1: 先卖后买
        IsSupportGetOrder = exchange.GetName().indexOf('itstamp') == -1;    // 检测 交易所 名称, 如果是  Bitstamp  则提醒
        if (!IsSupportGetOrder) {
            Log(exchange.GetName(), "不支持GetOrder, 可能影响策略稳定性.");
        }
    
        SetErrorFilter("502:|503:|S_U_001|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|refused|EOF|When");
        // SetErrorFilter  过滤错误信息  
    
        exchange.SetRate(1); 
        Log('已经禁用汇率转换, 当前货币为', exchange.GetBaseCurrency());    // 禁用汇率转换
    
        if (!RestoreProfit) {     //  恢复上次盈利 若果是  false 则 给 LastProfit 赋值 0 , 即不恢复。
            LastProfit = 0;
        }
    
        var orgAccount = _C(exchange.GetAccount);     // 获取账户信息,    此处记录 策略开始运行时的 初始账户信息 ,用于 计算一些收益,如: 总体浮动盈亏 等。本策略有几个参数 都是 该变量传入。
        var fishCount = 1;                            // 撒网次数 初始1
        while (true) {                                // 策略 主循环
            if (!fishing(orgAccount, fishCount)) {    // 撒网函数 fishing
                break;
            }
            fishCount++;                              // 撒网次数 累计
            Log("第", fishCount, "次重新撒网...");      // 输出 撒网信息。
            FirstPriceAuto = true;                    // 重置 首价格自动 为true
            Sleep(1000);                              // 轮询间隔 1000毫秒
        }
    }
    

    如有注释错误,欢迎指正 ^^

    相关文章

      网友评论

        本文标题:数字货币--网格变形策略之单边网格 (注释版)

        本文链接:https://www.haomeiwen.com/subject/jtwojxtx.html