资源加载中... loading...

The grid trading strategy

Author: , Created: 2018-08-23 13:45:27, Updated:

The grid trading strategy (www.fmz.com) The basic idea of grid trading is very straightforward. Instead of placing one trade, we place multiple trades forming a grid pattern. Usually these are entered as “stop” or “limit” orders around the current price level – but not always. I’ll explain this in more detail below, but that’s the basic idea.

What is grid trading and how does it work? Grid trading is a play on market volatility. There are two reasons why it’s favored by traders. The first is that it doesn’t “require” you to have a definitive prediction on the market direction.

The second is that it works well in volatile markets, where there isn’t a clear trend – these conditions are very common in the currency markets

Grid trading is a type of technical analysis trading that is based on movement within specific grid patterns. Grid trading is popular in foreign exchange trading. Overall the technique seeks to capitalize on normal price volatility in markets by placing buy and sell orders at certain regular intervals above and below a predefined base price. Such buy and sell orders, generally spaced at 10 or 15 units of intervals, create a trading grid.

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.

For more information,please see:

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

Source Code:

”` // 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

更多内容