Einseitige Gitter für die Verformungsstrategie (Anmerkung)

Schriftsteller:Kleine Träume, Erstellt: 2017-09-06 18:39:54, Aktualisiert: 2019-07-31 18:19:33

Einseitige Gitter für die Verformungsstrategie (Anmerkung)

Einführung in die Strategie

  • Das Gitter kann sich anpassen.

  • Erst kaufen und dann verkaufen: Das Gerät hängt die Rechnung ab dem Anfangspreis ab, mit jedem Zahlungsintervall. Der Parameter für die Anzahl der Aufschlüsse ist "Einzelzahl", ausreichend. Die Gesamtzahl der Aufschlüsse ist eine Rechnung. Nach der Abwicklung beliebiger Aufschlüsse addiert der Programm auf der Grundlage des Kaufpreises.

  • Erst verkaufen und dann kaufen: Das ist genau das Gegenteil.

  • Das größte Risiko für die Strategie ist ein einseitiger Markt, bei dem die Preise außerhalb des Gitterbereichs schwanken.

  • Gitter mit Autostopp- und Bewegungsfunktionen

  • Der Kommentar lautet:

    Die Strategie, die mit einem virtuellen Hanging List-Design für die Börsen, die die Anzahl der Hanging-Orders begrenzen, hervorragend verarbeitet wurde, löste das Problem flexibel.

    Die logische Gestaltung des Gitterplatzes ist flexibel und geschickt aufgebaut.

    Profit-Loss-Computation, verschiedene Zahlenstatistiken Algorithmen können abgeleitet werden, verschiedene Konditionen sind streng in der Entwurfsdetektion.

    Der Quellcode ist sehr lernwert.

  • Kommentarcode

/*  界面参数 (代码中体现为全局变量)
变量                                  描述                  类型                       默认值         
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 变量 赋值一个 对象,用于在 发明者量化 机器人 状态栏上显示 表格信息
                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) + '`');                               // 调用 发明者量化 平台 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 - 

Verwandt

Mehr

LebendKannst du mir einen Kontakt link hinterlassen?

LebendDiese Strategie kann die maximale Grenze der günstigen Position automatisch verschieben, oder sie kann nur mit einem Stop-Loss neu verbreitet werden, aber die Wiederverbreitung des Netzes scheint problematisch zu sein und kann nicht zum ursprünglichen Stand der Strategie ausgeglichen werden. Ich habe mehrmals ausgeglichen, aber es fehlt an Münzen, um nur zwei oder drei Gitter zu schaffen.

18180828122Wer unterstützt den Vertrag, der den Vertrag wiederholt, schlägt vor, dass das Abonnement fehlschlägt

KkkmsMarkierung

JjkkWenn das Netzwerk größer ist, ist die Anruffrequenz der Anrufe höher als die der APIs.

Sie ist ein junger Mann.Kann der Eigentümer eine Kontaktdaten hinterlassen?

nxtplayer666, lernen Sie die Code-Ideen zu verstehen.

Kleine TräumeV: DoMyBestForeverAgo

Kleine TräumeDie Strategie sollte eine sofortige Strategie sein.

JjkkWo genau? Ich kann es nicht finden.

Kleine TräumeSie können die Intervallzeit für die Abfrage oder die Intervallzeit für die Bestellung kontrollieren.

Kleine TräumeBotVS QQ-Gruppe kann mich QQ ^^: Kleine Träume