ネットワーク取引戦略

作者: リン・ハーン優しさ作成日: 2018-08-23 13:45:27,更新日:

ネットワーク取引戦略 (www.fmz.com) 格子取引の基本考え方は非常に単純です. 1つの取引を行う代わりに,複数の取引を格子パターンを形成して行う.通常,これらは現在の価格レベルの周りにストップまたは制限オーダーとして入力されますが,必ずしもそうではありません.私は下記の詳細に説明しますが,それが基本的なアイデアです.

グリッド・トレーディングとは何か? どのように機能するのか? 格子取引は市場変動を操作するものです.トレーダーが好む理由は2つあります.第一に,市場の方向性について明確な予測が必要ないということです.

2つ目は 変動が激しい市場ではうまく機能し 明確な傾向がないことです

グリッド取引は,特定のグリッドパターン内の動きに基づいた技術分析取引の一種である. グリッド取引は外為取引で人気がある. 全体的に,この技術は,既定のベース価格の上下で一定の定期的な間隔で購入・販売オーダーを置き,市場の通常の価格変動から利益を得ることを目指している.そのような購入・販売オーダーは,一般的に10〜15ユニットの間隔で隔離され,取引グリッドを作成する.

グリッドは方向をカスタマイズできます

基本的な取引:まず買い,それから売る.

グリッドは,最初の価格を下回る価格で購入注文を送信し始めます.これは最初の価格が続く価格です (第2の最新の購入価格,第3の最新の購入価格...など).各購入注文は"価格間隔"パラメータで区切られています.待機中の注文の数は"単一の量"で,"総量"が満たされるまで注文総数を送信します.

購入オーダーが完了した後,プログラムは購入価格に基づいて,注文が販売された後,販売価格に価格の"価格差"パラメータを追加し,その後,このグリッド戦略の進行を再開します (チェック,注文を配置,実行されるまで待って,販売)

まずショートセール,その後カバーで購入: 操作は正反対です

この戦略の最大のリスクは 市場の動向が一方的で 価格変動が格子を超えている場合です

次のコードは,自動ストップ損失と動きの機能を持つグリッドを作りました.

コメント:

この戦略は,仮想待機注文デザインを使用し,待機注文数を制限するために取引所に多くの処理を提供し,問題を柔軟に解決します.

格子論理は柔軟な設計で 巧みな構造です

利益と損失の計算,各数値統計アルゴリズムは参照として使用することができ,各条件検出設計は厳格です. (BUGの可能性を最小限に抑えるため)

ソースコードは学ぶ価値がある.

詳細については,以下を参照してください.

https://www.fmz.com/strategy/112633

ソースコード:

// Grid can customize direction
// Basic trading operation: buy first and then sell.
// The grid will start to send buying order at the price that below the first price, which is the price follow 
// by the first price (second latest buying price, third latest buying price…and so on). Each buying order is separated by
// the "price interval" parameter. The number of pending orders is "single quantity", and will send the order total until
// the "total quantity" is filled.
// After any buying order is completed, the program will be on the basis of the buying price, add the price of the "price
// difference" parameter to the sell price, after the order has been sold, and then re-start the progress of this 
// grid strategy (checking, place order, wait until it executed, sell)
// Selling short first and then buy to cover: the operation is just the opposite
// The biggest risk of this strategy is when the market trend is unilateral moving, and the price fluctuations are exceeding the grid.
// The following code has made the grid with automatic stop loss and movement function.
// Comments:
// The strategy uses a virtual pending order design, which provides a great deal of processing for the exchange to limit the number
// of pending orders, and solves the problem flexibly.
// The grid logic is flexible in design and clever in structure.
// Profit and loss calculation, each numerical statistical algorithm can be used for reference, and each condition detection design 
// is rigorous. (for minimize the possibility of BUG)
// The source code is very worth learning.

// Source Code:
/* Interface parameters (shown as global variables in the code)
OpType                              Grid Direction                              Drop-down box (selected)          Buy first then sell | Sell first then buy
FirstPriceAuto                      initial price automatic                     boolean (true/false)              true
FirstPrice@!FirstPriceAuto          initial price                               numerical (number)                100
AllNum                              total number                                numerical (number)                10
PriceGrid                           Price Interval                              numerical (number)                1
PriceDiff                           spread                                      numerical (number)                2
AmountType                          order size                                  drop-down box (selected)          buy and sell the same amount | custom amount
AmountOnce@AmountType==0            Single transaction quantity                 numerical (number)                0.1
BAmountOnce@AmountType==1           Buying Order Size                           numerical (number)                0.1
SAmountOnce@AmountType==1           Selling order size                          numerical (number)                0.1
AmountCoefficient@AmountType==0     Quantity difference                         String    (string)                *1
AmountDot                           The decimal point                           numerical (number)                3
EnableProtectDiff                   Turn on spread protection                   Boolean   (true/false)            false
ProtectDiff@EnableProtectDiff       Entry spread Price Protection               numerical (number)                20
CancelAllWS                         stop cancels all pending orders             Boolean   (true/false)            true
CheckInterval                       polling interval number                     numerical (number)                2000
Interval                            failure retry interval                      numerical (number)                1300
RestoreProfit                       restores last profit                        Boolean   (true/false)            false
LastProfit@RestoreProfit            Last Profit                                 numerical (number)                0
ProfitAsOrg@RestoreProfit           Last profit counted as average price        Boolean (true/false)              false
EnableAccountCheck                  enable balance verification                 Boolean (true/false)              true
EnableStopLoss@EnableAccountCheck   open Stop Loss                              Boolean (true/false)              false
StopLoss@EnableStopLoss             maximum floating loss                       numerical (number)                100
StopLossMode@EnableStopLoss         Post-stop loss operation                    Drop-down box (selected)          Recycle and exit | Recycle and re-cast
EnableStopWin@EnableAccountCheck    Turn on Take Profit                         Boolean (true/false)              false
StopWin@EnableStopWin               Maximum floating profit                     Number type (number)              120
StopWinMode@EnableStopWin           post-take profit operation                  drop-down box (selected)          Recycle and exit | Recycle and re-cast
AutoMove@EnableAccountCheck         auto Move                                   Boolean (true/false)              false
MaxDistance@AutoMove                maximum distance                            numerical (number)                20
MaxIdle@AutoMove                    maximum idle (seconds)                      numerical (number)                7200
EnableDynamic                       Turns on dynamic pending orders             Boolean (true/false)              false
DynamicMax@EnableDynamic            order expiration distance                   Number (number)                   30
ResetData                           clears all data at startup                  Boolean (true/false)              true
Precision                           price decimal length                        numerical (number)                5
*/

function hasOrder(orders, orderId) {                           // Check if there is an order with order ID in parameter orders
    for (var i = 0; i < orders.length; i++) {                  // Traverse orders to check if there are same ids, if there are then return true
        if (orders[i].Id == orderId) {
            return true;
        }
    }
    return false;                                              // All traversed, no trigger if means haven't found the order with the ID orderId, return false
}


function cancelPending() {                                     // Cancel all pending order functions
    var ret = false;                                           // Set return success tag variable
    while (true) {                                             // while loop
        if (ret) {                                             // If ret is true then Sleep for a certain time
            Sleep(Interval);
        }
        var orders = _C(exchange.GetOrders);                   // Call the API to get the order information that the exchange did not executed.
        if (orders.length == 0) {                              // If an empty array is returned, the exchange has no unexecuted orders.
            break;                                             // Jump out of the while loop
        }

        for (var j = 0; j < orders.length; j++) {              // Traverse the unfinished order array and use orders[j].Id one by one to cancel the order based on the index j.
            exchange.CancelOrder(orders[j].Id, orders[j]);
            ret = true;                                        // Once there is a cancel operation, ret is assigned a value of true. Used to trigger above Sleep, wait for re-exchange.GetOrders detection 
        }
    }
    return ret;                                                // return ret
}

function valuesToString(values, pos) {                      // Value converted to a string
    var result = '';                                        // Declare an empty string result for return
    if (typeof(pos) === 'undefined') {                      // If the pos parameter is not passed, assign pos a value of 0.
        pos = 0;
    }
    for (var i = pos; i < values.length; i++) {             // Process values array according to the passed pos
        if (i > pos) {                                      // In addition to the first loop, add ' ' a space after the result string
            result += ' ';
        }
        if (values[i] === null) {                           // If values (function argument list array) the current index's element is null then result adds 'null' string
            result += 'null';
        } else if (typeof(values[i]) == 'undefined') {      // If it is undefined, add 'undefined'
            result += 'undefined';
        } else {                                            // Remaining type do switch detection separately
            switch (values[i].constructor.name) {           // Check the name property of the constructor of values[i], which is the type name
                case 'Date':
                case 'Number':
                case 'String':
                case 'Function':
                    result += values[i].toString();         // If it is a date type, a numeric type, a string type, or a function type, call its toString function and convert it to a string, then add
                    break;
                default:
                    result += JSON.stringify(values[i]);    // In other cases, use the JSON.stringify function to convert to a JSON string. Add to result 
                    break;
            }
        }
    }
    return result;                                          // return result
}

function Trader() {                                                 // Trader function, using closures.
    var vId = 0;                                                    // Order increment ID
    var orderBooks = [];                                            // Order book
    var hisBooks = [];                                              // Historical order book
    var orderBooksLen = 0;                                          // Order book length
    this.Buy = function(price, amount, extra) {                     // Buying function, parameters: price, quantity, extended information
        if (typeof(extra) === 'undefined') {                        // If the parameter extra is not passed in, ie typeof returns undefined 
            extra = '';                                             // Assign an empty string to extra
        } else {
            extra = valuesToString(arguments, 2);                   // The argument arguments passed when calling this.Buy function is passed to the valuesToString function.
        }
        vId++;                                                      // 
        var orderId = "V" + vId;                                    //
        orderBooks[orderId] = {                                     // Add the attribute orderId to the order book array and initialize it with the constructed object.
            Type: ORDER_TYPE_BUY,                                   // Constructed Object Type Property: Type buy
            Status: ORDER_STATE_PENDING,                            //                       State     on hold
            Id: 0,                                                  //                       orderID    0
            Price: price,                                           //                       price   paramter  price
            Amount: amount,                                         //                       Order quantity parameter amount 
            Extra: extra                                            //                       Extended information A string processed by valuesToString
        };
        orderBooksLen++;                                            // The length of the order book is increased by 1
        return orderId;                                             // Returns the orderId of the order constructed this time (non-exchange order ID, don't confuse.)
    };
    this.Sell = function(price, amount, extra) {                    // Basically similar to this.Buy, construct a sell order.
        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() {                                   // Get unfinished order information
        var orders = _C(exchange.GetOrders);                        // Call API GetOrders to get unfinished order information Assigned to orders
        for (orderId in orderBooks) {                               // Traversing the orderBooks in the Trader object 
            var order = orderBooks[orderId];                        // Take out the order based on orderId
            if (order.Status !== ORDER_STATE_PENDING) {             // If the state of order is not equal to the suspended state, skip this loop
                continue;
            }
            var found = false;                                      // Initialize the found variable (marked if found) to true
            for (var i = 0; i < orders.length; i++) {               // Traversing data for unexecuted orders returned by the API  
                if (orders[i].Id == order.Id) {                     // When you find an order with the same order id in orderBooks, assign a value of true to find, which means find. 
                    found = true;                                   
                    break;                                          // Jump out of the current loop
                }
            }
            if (!found) {                                           // If not found, push orderBooks[orderId] to orders.
                orders.push(orderBooks[orderId]);                   // Why do you want to push like this?
            }
        }
        return orders;                                              // return orders
    }
    this.GetOrder = function(orderId) {                             // Get order
        if (typeof(orderId) === 'number') {                         // If the passed argument orderId is a numeric type 
            return exchange.GetOrder(orderId);                      // Call the API GetOrder to get the order information based on the orderId and return.
        }
        if (typeof(hisBooks[orderId]) !== 'undefined') {            // Typeof(hisBooks[orderId]) if not equal to undefined
            return hisBooks[orderId];                               // Return data in hisBooks with attribute as orderId
        }
        if (typeof(orderBooks[orderId]) !== 'undefined') {          // As above, if there is a value of orderId in the orderBooks, this data is returned.
            return orderBooks[orderId];
        }
        return null;                                                // Return null if the above conditions are not met
    };
    this.Len = function() {                                         // Returns the Trader's orderBookLen variable, which returns the order book length.
        return orderBooksLen;
    };
    this.RealLen = function() {                                     // Back In the order book Activate the order quantity.
        var n = 0;                                                  // Initial count is 0
        for (orderId in orderBooks) {                               // Traversing the order book
            if (orderBooks[orderId].Id > 0) {                       // If the Id of the current order in the traversal is greater than 0, that is, 0 other than the initial time, 
                                                                    // indicating that the order has been placed, the order has been activated.
                n++;                                                // Cumulatively activated order
            }
        }
        return n;                                                   // Returns the value of n, which returns the true order book length. (number of orders activated)
    };
    this.Poll = function(ticker, priceDiff) {                       // 
        var orders = _C(exchange.GetOrders);                        // Get all unexecuted orders
        for (orderId in orderBooks) {                               // Traversing the order book
            var order = orderBooks[orderId];                        // Take the current order assign to order
            if (order.Id > 0) {                                     // If the order is active, ie order.Id is not 0 (already placed)
                var found = false;                                  // Variable found (mark found) is false
                for (var i = 0; i < orders.length; i++) {           // Find the same order number in the executed order information returned by the exchange
                    if (order.Id == orders[i].Id) {                 // If found, assign a value of true to find, which means it has been found.
                        found = true;
                    }
                }
                if (!found) {                                       // If the current orderId represents an order that is not found in the order of the uncompleted order returned by the exchange.
                    order.Status = ORDER_STATE_CLOSED;              // Updates the order corresponding to orderId in orderBooks (ie the current order variable) and updates
                                                                    // the Status property to ORDER_STATE_CLOSED (ie closed) 
                    hisBooks[orderId] = order;                      // The completed order is recorded in the historical order book, ie hisBooks, unified, and the unique order number orderId
                    delete(orderBooks[orderId]);                    // Delete the attribute of the order book named orderId value. (The completed order is deleted from it)
                    orderBooksLen--;                                // Order book length reduction
                    continue;                                       // The following code skips the loop.
                }
            }
            var diff = _N(order.Type == ORDER_TYPE_BUY ? (ticker.Buy - order.Price) : (order.Price - ticker.Sell));
            // Diff is the difference between the planned opening price of the order in the current order book and the current real-time opening price.

            var pfn = order.Type == ORDER_TYPE_BUY ? exchange.Buy : exchange.Sell;   // Assign the corresponding API function reference to pfn according to the type of the order.
            // That is, if the order type is a buy order, pfn is a reference to the exchange.Buy function, the same as the sell order.

            if (order.Id == 0 && diff <= priceDiff) {                                // If the order order in the order book is not activated (ie Id is equal to 0) and the current price is less than or 
                                                                                     // equal to the order plan price, the priceDiff passed in the parameter.   
                var realId = pfn(order.Price, order.Amount, order.Extra + "(distance: " + diff + (order.Type == ORDER_TYPE_BUY ? (" ask price: " + ticker.Buy) : (" bid price: " + ticker.Sell))+")");
                // Execute order function, parameter passing price, quantity, order extension information + pending order distance + market data (ask price or bid price), return exchange order id

                if (typeof(realId) === 'number') {    // If the returned realId is a numeric type
                    order.Id = realId;                // Assign the Id attribute of the current order order to the order book.
                }
            } else if (order.Id > 0 && diff > (priceDiff + 1)) {  // If the order is active and the current distance is greater than the distance passed in by the parameter
                var ok = true;                                    // Declare a variable for tagging       Initially set true 
                do {                                              // Execute "do" first and then judge while    
                    ok = true;                                    // Ok assign true
                    exchange.CancelOrder(order.Id, "unnecessary" + (order.Type == ORDER_TYPE_BUY ? "buying" : "selling"), "placed order price:", order.Price, "volume:", order.Amount, ", distance:", 
                                                 diff, order.Type == ORDER_TYPE_BUY ? ("ask price: " + ticker.Buy) : ("bid price: " + ticker.Sell));
                    // Cancel the pending order that is out of range. After canceling the order, print the current order information and the current distance diff.

                    Sleep(200);                                   // Wait 200 milliseconds
                    orders = _C(exchange.GetOrders);              // Call the API to get an uncompleted order in the exchange.
                    for (var i = 0; i < orders.length; i++) {     // Traverse these unfinished orders.
                        if (orders[i].Id == order.Id) {           // If the cancelled order is found in the list of orders that have not been completed by the exchange
                            ok = false;                           // Assign ok this variable to false, that is, no cancellation is successful.
                        }
                    }
                } while (!ok);                                    // If ok is false, then !ok is true and while will continue to repeat the loop, continue to cancel the order,
                                                                  // and check if the cancellation is successful.
                order.Id = 0;                                     // Assigning a value of 0 to order.Id means that the current order is inactive.
            }
        }
    };
}

function balanceAccount(orgAccount, initAccount) {               // Balance Account Function Parameter Initial account information when the strategy is started
    cancelPending();                                             // Call the custom function cancelPending() to cancel all pending orders.
    var nowAccount = _C(exchange.GetAccount);                    // Declare a variable nowAccount to record the latest information about the account at the moment.
    var slidePrice = 0.2;                                        // Set the slip price when placing the order as 0.2
    var ok = true;                                               // Tag variable initially set true
    while (true) {                                               // while loop
        var diff = _N(nowAccount.Stocks - initAccount.Stocks);   // Calculate the difference between the current account and the initial account diff
        if (Math.abs(diff) < exchange.GetMinStock()) {           // If the absolute value of the currency difference is less than the minimum transaction volume of the exchange,
                                                                 // the break jumps out of the loop and does not perform balancing operations.
            break;
        }
        var depth = _C(exchange.GetDepth);                       // Get the exchange depth information Assign to the declared depth variable 
        var books = diff > 0 ? depth.Bids : depth.Asks;          // According to the difference of the currency is greater than 0 or less than 0, extract the buy order array or 
                                                                 // sell order array in depth (equal to 0 will not be processed, it is break when it is judged to be less than GetMinStock)
                                                                 // The difference between the coins is greater than 0 to sell the balance, so look at the buy order array, 
                                                                 // the difference between the coins is less than 0 is the opposite.
        var n = 0;                                               // Statement n initial is 0
        var price = 0;                                           // Statement price initial 0
        for (var i = 0; i < books.length; i++) {                 // Traversing the buy or sell order array
            n += books[i].Amount;                                // Accumulate Amount (order quantity) for each order based on the index i traversed
            if (n >= Math.abs(diff)) {                           // If the cumulative order quantity n is greater than or equal to the currency difference, then:
                price = books[i].Price;                          // Get the price of the current indexed order, assign it to price
                break;                                           // Jump out of the current for traversal cycle
            }
        }
        var pfn = diff > 0 ? exchange.Sell : exchange.Buy;       // Pass the sell order API (exchange.Sell) or the next buy order API (exchange.Buy) reference to the declared pfn
                                                                 // based on the currency difference greater than 0 or less than 0
        var amount = Math.abs(diff);                             // The amount of the order to be balanced is diff, the difference in the currency, assigned to the declared amount variable.
        var price = diff > 0 ? (price - slidePrice) : (price + slidePrice);    // The direction of buying and selling according to the difference in the currency, increase or decrease the
                                                                               // slip price based on the price (slip price is to make it easier to trade), and then assign it to price
        Log("start the balance", (diff > 0 ? "sell" : "buy"), amount, "of coins");           // The number of coins that the output log balances.
        if (diff > 0) {                                                        // According to the direction of the buying and selling, determine whether the account currency or the amount of coins is sufficient.
            amount = Math.min(nowAccount.Stocks, amount);                      // Make sure that the order amount will not exceed the available coins of the current account.
        } else {
            amount = Math.min(nowAccount.Balance / price, amount);             // Make sure that the amount of order placed does not exceed the amount of money available in the current account.
        }
        if (amount < exchange.GetMinStock()) {                                 // Check if the final order quantity is less than the minimum order quantity allowed by the exchange
            Log("Insufficient funds, unable to balance to the initial state");                  // If the order quantity is too small, the information is printed.
            ok = false;                                                        // Tag balance failed
            break;                                                             // Jump out of the while loop
        }
        pfn(price, amount);                                                     // Execute order API (pfn reference)
        Sleep(1000);                                                            // Pause for 1 second
        cancelPending();                                                        // Cancel all pending orders.
        nowAccount = _C(exchange.GetAccount);                                   // Get current account information
    }
    if (ok) {                                                                   // Execute the code inside curly braces when ok is true (balance is successful)
        LogProfit(_N(nowAccount.Balance - orgAccount.Balance));                 // Use the Balance property of the incoming parameter orgAccount (account information before balancing) 
                                                                                // to subtract the Balance property of the current account information, that is, the difference in the amount of money. 
                                                                                // That is, profit and loss (because the number of coins does not change, there is a slight error because some small
                                                                                // amounts cannot be balanced)
        Log("平衡完成", nowAccount);                                             // The output log is balanced.
    }
}

var STATE_WAIT_OPEN = 0;                                                        // Used for the state of each node in the fishTable
var STATE_WAIT_COVER = 1;                                                       // ...
var STATE_WAIT_CLOSE = 2;                                                       // ...
var ProfitCount = 0;                                                            // Profit and loss record
var BuyFirst = true;                                                            // Initial interface parameters
var IsSupportGetOrder = true;                                                   // determine the exchange support the GetOrder API function, a global variable, used to determine the start of the main function
var LastBusy = 0;                                                               // Record the last processed time object

function setBusy() {                            // Set Busy time
    LastBusy = new Date();                      // Assign LastBusy to the current time object
}

function isTimeout() {                                                          // Determine if it times out
    if (MaxIdle <= 0) {                                                         // Maximum idle time (based on whether the grid is automatically moved), 
                                                                                // if the maximum idle time MaxIdle is set less than or equal to 0
        return false;                                                           // Returns false, does not judge the timeout. That is, always return false without timeout.
    }
    var now = new Date();                                                       // Get current time object
    if (((now.getTime() - LastBusy.getTime()) / 1000) >= MaxIdle) {             // Use the getTime function of the current time object to get the timestamp and the timestamp of LastBusy to calculate the difference,
                                                                                // Divide by 1000 to calculate the number of seconds between the two time objects. 
                                                                                // Determine if it is greater than the maximum idle time MaxIdle
        LastBusy = now;                                                         // If it is greater than, update LastBusy to the current time object now
        return true;                                                            // Returns true, which is a timeout.
    }
    return false;                                                               // Return false no timeout
}

function onexit() {                             // The closing function when the program exits.
    if (CancelAllWS) {                          // To cancel all pending orders when stop, call cancelPending() to cancel all pending orders.
        Log("Exiting, try to cancel all pending orders");
        cancelPending();
    }
    Log("Strategy successfully stopped");
    Log(_C(exchange.GetAccount));               // Print the account position information when you exit the program.
}


function fishing(orgAccount, fishCount) {    // Casting parameters: account information, number of casting
    setBusy();                               // Set LastBuys to the current timestamp
    var account = _C(exchange.GetAccount);   // Declare an account variable to get the current account information and assign it.
    Log(account);                            // Output the account information at the start of the call to the fishing function.
    var InitAccount = account;               // Declare a variable InitAccount and assign it with account. Here is the initial account funds recorded before 
                                             // this casting, used to calculate floating profit and loss.   
    var ticker = _C(exchange.GetTicker);     // Get the quote value assigned to the declared ticker variable 
    var amount = _N(AmountOnce);             // According to the number of interface parameters, use _N to process the decimal places (_N defaults to 2 bits) and assign them to amount.
    var amountB = [amount];                  // Declare a variable called amountB is an array, initialize an element with amount
    var amountS = [amount];                  // Declare a variable called amountS ...
    if (typeof(AmountType) !== 'undefined' && AmountType == 1) {     // According to the custom amount, the order size type, if this interface parameter is not undefined,
                                                                     // And AmountType is set to a custom amount on the interface, that is, the AmountType value is 1 (the index of the drop-down box) 
        for (var idx = 0; idx < AllNum; idx++) {      // The total number of AllNum. If you set a custom amount, cycle amountB/amountS to the order quantity array according to the total number of cycles.
            amountB[idx] = BAmountOnce;               // Assign value to the buy order array using interface parameters
            amountS[idx] = SAmountOnce;               // ...          to the sell order...
        }
    } else {                                          // else
        for (var idx = 1; idx < AllNum; idx++) {      // Cycles based on the total number of grids.
            switch (AmountCoefficient[0]) {           // According to the interface parameter difference, the first character of the string, AmountCoefficient[0] is '+', '-', '*', '/'
                case '+':                             // According to the interface parameters, a grid with a single addition and increment is constructed.
                    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);   // buying order, buying amount, and process the data decimal places.
            amountS[idx] = amountB[idx];                  // Assignment
        }
    }
    if (FirstPriceAuto) {                                 // If the first parameter is automatically set to true if the interface parameter is set, the code inside the if curly brackets is executed.
        FirstPrice = BuyFirst ? _N(ticker.Buy - PriceGrid, Precision) : _N(ticker.Sell + PriceGrid, Precision);
        // The interface parameter FirstPrice sets the first price according to the BuyFirst global variable (the initial statement is true, 
        // and has been assigned according to the OpType at the beginning of the main). 
        // The price is set by the price ticker and the price parameter PriceGrid price interval.  
    }
    // Initialize fish table   
    var fishTable = {};                         // Declare a grid object
    var uuidTable = {};                         // Identification code table object
    var needStocks = 0;                         // Required coins variable
    var needMoney = 0;                          // Required money variable
    var actualNeedMoney = 0;                    // Actually needed money
    var actualNeedStocks = 0;                   // Actually needed coins
    var notEnough = false;                      // Underfunded tag variable, initially set to false
    var canNum = 0;                             // Available grid
    for (var idx = 0; idx < AllNum; idx++) {    // The structure is traversed according to the number of grid AllNum.
        var price = _N((BuyFirst ? FirstPrice - (idx * PriceGrid) : FirstPrice + (idx * PriceGrid)), Precision);
        // When traversing the construct, the current index idx price setting is set according to BuyFirst. The spacing between each index price is PriceGrid.
        needStocks += amountS[idx];                      // The number of coins sold is gradually accumulated with the cycle. (accumulated by the sell order quantity array to needStocks one by one)
        needMoney += price * amountB[idx];               // The amount of money required to buy is gradually accumulated with the cycle. (....buy the order quantity array one by one...)
        if (BuyFirst) {                                  // Handling buy first 
            if (_N(needMoney) <= _N(account.Balance)) {  // If the grid requires less money than the amount of money available on the account
                actualNeedMondy = needMoney;             // Assigned to the actual amount of money required
                actualNeedStocks = needStocks;           // Assigning to the actual number of coins required. Is there something wrong with this?
                canNum++;                                // Cumulative number of available grids
            } else {                                     // _N(needMoney) <= _N(account.Balance) If this condition is not met, set the underfunded tag variable to true
                notEnough = true;
            }
        } else {                                         // Handling sell first
            if (_N(needStocks) <= _N(account.Stocks)) {  // Check if the required number of coins is less than the number of coins available in the account
                actualNeedMondy = needMoney;             // Assignment
                actualNeedStocks = needStocks;
                canNum++;                                // Cumulative number of available grids
            } else {
                notEnough = true;                        // Set true if the funding conditions are not met
            }
        }
        fishTable[idx] = STATE_WAIT_OPEN;                // According to the current index idx, set the state of the idx member (grid node) of the grid object,
                                                         // initially STATE_WAIT_OPEN (waiting to open the position)
        uuidTable[idx] = -1;                             // The numbered object also initializes its own idx value (the node corresponding to the fishTable) to -1 based on the current idx.
    }
    if (!EnableAccountCheck && (canNum < AllNum)) {      // If the funds check is not enabled, and the number of grids (the total number of nodes) where the node is smaller 
                                                         // than the interface parameter setting can be opened.
        Log("Warning, current funds can only be made", canNum, "of Grids, total grid needs", (BuyFirst ? needMoney : needStocks), "Please keep sufficient funds");   // Log outputs a warning message.
        canNum = AllNum;                                                                                          // Update the number of openable settings for the interface parameters
    }
    if (BuyFirst) {                                                                         // buy first
        if (EnableProtectDiff && (FirstPrice - ticker.Sell) > ProtectDiff) {                // Open spread protection and enter the market price minus the current bid price more than
                                                                                            // the market entry price protection         
            throw "The first buying price is higher than the market selling price" + _N(FirstPrice - ticker.Sell, Precision) + ' dollar';  // Throw an error message.
        } else if (EnableAccountCheck && account.Balance < _N(needMoney)) {                 // If the funds check is enabled and the amount of money available for the account is less than
                                                                                            // the amount of money required for the grid.
            if (fishCount == 1) {                                                           // If it is the first time to cast the grid
                throw "Insufficient funds, need" + _N(needMoney) + "dollar";                // Throw an error, insufficient funds
            } else {
                Log("Insufficient funds, need", _N(needMoney), "dollar, the program only make", canNum, "of grids #ff0000");  // If it is not the first time to cast a grid, output a message.
            }
        } else {                                                                            // In other cases, there is no capital inspection, price protection, etc.
            Log('Estimated use of funds: ', _N(needMoney), "dollar");                            // The output is expected to use funds.
        }
    } else {                                                                                // sell first, The following is similar to "buy first"
        if (EnableProtectDiff && (ticker.Buy - FirstPrice) > ProtectDiff) {
            throw "The first selling price is higher than the market buying price" + _N(ticker.Buy - FirstPrice, Precision) + ' dollar';
        } else if (EnableAccountCheck && account.Stocks < _N(needStocks)) {
            if (fishCount == 1) {
                throw "Insufficient funds, need" + _N(needStocks) + " of coins";
            } else {
                Log("Insufficient funds, need", _N(needStocks), "of coins, program only make", canNum, "of grids #ff0000");
            }
        } else {
            Log('Estimated use of funds: ', _N(needStocks), "coins, approximately", _N(needMoney), "dollar");
        }
    }

    var trader = new Trader();                                          // Constructs a Trader object, assigning it to the trader variable declared here.
    var OpenFunc = BuyFirst ? exchange.Buy : exchange.Sell;             // According to whether to buy and sell first, set the open function OpenFunc to refer to exchange.Buy or exchange.Sell
    var CoverFunc = BuyFirst ? exchange.Sell : exchange.Buy;            // same as above
    if (EnableDynamic) {                                                // Set OpenFunc/CoverFunc again according to whether the interface parameter EnableDynamic is enabled.
        OpenFunc = BuyFirst ? trader.Buy : trader.Sell;                 // The member function Buy that references the trader object is used for dynamic pending orders (mainly because 
                                                                        // some exchanges limit the number of pending orders, so virtual dynamic pending orders are required)
        CoverFunc = BuyFirst ? trader.Sell : trader.Buy;                // same as above
    }
    var ts = new Date();                                                // Create a time object at this time (assigned to ts) to record the time at the moment.
    var preMsg = "";                                                    // Declare a variable to record the last message, the initial set to empty string
    var profitMax = 0;                                                  // Maximum return 
    while (true) {                                                      // The main logic after the grid is casted
        var now = new Date();                                           // Record the time when the current cycle started
        var table = null;                                               // Declare a variable
        if (now.getTime() - ts.getTime() > 5000) {                      // Calculate whether the difference between the current time now and the recorded time ts is greater than 5000 milliseconds
            if (typeof(GetCommand) == 'function' && GetCommand() == "Receiving grid") {         // Check if the strategy interaction control command "receives the grid" is received, 
                                                                                                // stops and balances to the initial state.
                Log("Start executing commands to perform grid operations");                                          // Output information 
                balanceAccount(orgAccount, InitAccount);                              // Perform a balancing function to balance the number of coins to the initial state
                return false;                                                         // This time the grid function is fishing and return false
            }
            ts = now;                                                                 // Update ts with current time now for next comparison time
            var nowAccount = _C(exchange.GetAccount);                                 // Declare the nowAccount variable and initially been set as the current account information. 
            var ticker = _C(exchange.GetTicker);                                      // Declare the ticker variable and initially been set as the current market information.
            if (EnableDynamic) {                                                      // If you enable dynamic pending orders
                trader.Poll(ticker, DynamicMax);                                      // Call the Poll function of the trader object to detect and process all orders based on the 
                                                                                      // current ticker market and the interface parameter DynamicMax.
            }
            var amount_diff = (nowAccount.Stocks + nowAccount.FrozenStocks) - (InitAccount.Stocks + InitAccount.FrozenStocks);  // Calculate the current coin difference
            var money_diff = (nowAccount.Balance + nowAccount.FrozenBalance) - (InitAccount.Balance + InitAccount.FrozenBalance); // Calculate the current money difference
            var floatProfit = _N(money_diff + (amount_diff * ticker.Last));           // Calculate the current floating profit and loss of this time of casting grid
            var floatProfitAll = _N((nowAccount.Balance + nowAccount.FrozenBalance - orgAccount.Balance - orgAccount.FrozenBalance) + ((nowAccount.Stocks + nowAccount.FrozenStocks 
                                     - orgAccount.Stocks - orgAccount.FrozenStocks) * ticker.Last));
            // Calculate the overall floating profit and loss

            var isHold = Math.abs(amount_diff) >= exchange.GetMinStock();             // If the absolute value of the coin difference at this moment is greater than the minimum trading 
                                                                                      // volume of the exchange, it means that the position has been held.
            if (isHold) {                                                             // If you have already held a position, execute the setBusy() function, which will update the LastBusy time.
                setBusy();                                                            // That is, after opening the position, the opening of the opening mechanism is started.
            }

            profitMax = Math.max(floatProfit, profitMax);                             // Refresh the maximum floating profit and loss
            if (EnableAccountCheck && EnableStopLoss) {                               // If you initiate account detection and start a stop loss
                if ((profitMax - floatProfit) >= StopLoss) {                          // If the maximum floating profit or loss minus the current floating profit or loss is greater than or equal to 
                                                                                      // the maximum floating loss value, execute the code inside the curly braces
                    Log("Current floating profit a

もっと