Playing JavaScript with old people - creating a buyer/seller tool (7) Useful tool to know why!

Author: The Little Dream, Created: 2017-03-16 12:29:51, Updated: 2017-10-11 10:37:54

Useful tools must know why they are useful!

It's fun to write a strategy on a weekday, and all sorts of wild ideas pop up in the editor, and maybe it's your next holy grail, and it's exciting to think about! The only thing that affects this passion is the handling of the buying and selling logic of the trading strategy system, which is indeed crucial for the handling of this trading module, but somewhat boring and the logic is more complex.

  • It's nice to have a well-written module that you can use directly (I've used it in previous posts), but in addition to using it and understanding how it works, I've annotated the code:

/* Interval Failed to retest interval ((milliseconds)) Numeric type ((number)) 500 SlideTick number of sliding price points ((integer)) numerical type ((number)) 1 RiskControl Turn on wind control Bull (true/false) false MaxTrade@RiskControl Maximum number of trades in a business day MaxTradeAmount@RiskControl Maximum number of items to be added to a single account */

var __orderCount = 0 // records the next single order count for the current business day var __orderDay = 0 // records the date of the current business day

function CanTrade ((tradeAmount) { // Risk control module, parameters: number of deals if (!RiskControl) { // By default, do not turn on the wind control module, if not, the CanTrade function returns true return true I'm not sure. if (typeof(tradeAmount) == number && tradeAmount > MaxTradeAmount) { // Enter the parameter tradeAmount as a numeric type, and the amount of the submission is greater than the maximum amount of submission set by the template parameter Log ((the limit of the hurricane control module, which exceeds the maximum download volume, MaxTradeAmount, #ff0000 @); // output prompt, interrupt execution. Throw button interrupts the execution button return false; I'm not sure. var nowDay = new Date (().getDate ((); // Get the current date if (nowDay!= __orderDay) { // getDate() Returns some day in a month (1 ~ 31) from the Date object. __orderDay = nowDay; // __orderDay The global variable records the trigger date of the first entry into the wind control module. __orderCount = 0; // update the __orderDay variable and reset the __orderCount variable I'm not sure. __orderCount++; // global variable __orderCount The next single number, self-added and cumulative. if (__orderCount > MaxTrade) { // Determines whether the maximum number of trades per day is greater than the maximum number of trades per day set by the parameter Log ((Hurricane control module limit, cannot trade, exceeds maximum downward single number of pins, MaxTrade, #ff0000 @); // exceeded, output prompt message, interrupted execution. Throw button interrupts the execution button return false; I'm not sure. return true; // returns true, i.e. can be traded. I'm not sure.

function init ((() { // Template initialization function, which is executed first when the template is loaded. if (typeof(SlideTick) === undefined) { // Checks if SlideTick is undefined。 SlideTick = 1; // setting The default is 1 } else { // Parse string converted to a numeric value, however if a string with a non-numeric character beginning will return NaN, it may cause an error SlideTick = parseInt ((SlideTick); the name of the file is SlideTick. I'm not sure. Log (Logging the commodity trading library successfully loaded); I'm not sure.

function GetPosition ((e, contractType, direction, positions) { // Combine a contract with the same direction in the past and present positions, with the following parameters: Exchange object, type of contract, direction, API returns holding data ((blank)
var allCost = 0; // contractType Contract in the direction of total spending, without multiplying by the number of contracts (because the whole can be arranged) var allAmount = 0; // total number of contract hands var allProfit = 0; // total profit and loss var allFrozen = 0; // Total number of freezes var pos Margin = 0; // Holding contracts Leverage if (typeof(positions) === undefined!positions) { // If the parameter does not return the holding information passed to the API positions = _C ((e.GetPosition); // where the API is called to obtain the holding information. I'm not sure. for (var i = 0; i < positions.length; i++) { // runs through this array of stored information。 if (positions[i].ContractType == contractType && // Contract code of the holding information of the current index == Contract code specified by the parameter ((contractType)) and the direction is equal to the direction of the parameter passing ((direction)) of the current position or the previous position (i.e., the position of the object is the same as the position of the other object) ) { // Conditional execution of the if block posMargin = positions[i].MarginLevel; // Obtain leverage assigned to posMargin allCost += (positions[i].Price * positions[i].Amount); // Total cost (number of contracts already signed, current index holding price * holding volume) cumulative allAmount += positions[i].Amount; // the total number of contract hands allProfit += positions[i].Profit; // Contract floating profit and loss cumulative allFrozen += positions[i].FrozenAmount; // The total number of contract hands that are frozen I'm not sure. I'm not sure. if (allAmount === 0) { // If the total number of eligible contract hands accumulated after the traversal is completed is 0, returns null, i.e. no conditionally limited contract holding return null; and I'm not sure. return { // allAmount is non-zero, returns the holding information of an object after merging. MarginLevel: PostMargin, which is the most recent version of MarginLevel. FrozenAmount: allFrozen, the first game in the Frozen series. Price: _N ((allCost / allAmount), Amount: allAmount, Profit: allProfit, Type: direction, ContractType: type of the contract I'm not sure. I'm not sure.

function Open ((e, contractType, direction, opAmount) { // operation of single-variety contracts Opening function, parameters: exchange object, contract code, direction, number of operations var initPosition = GetPosition ((e, contractType, direction); // calls the GetPosition function at the top to get the holding information after the merger. var isFirst = true; // Set the isFirst tag (indicates that the following while is the first true loop) var initAmount = initPosition? initPosition.Amount : 0; // If initPosition is null, initAmount is assigned 0, otherwise it is assigned initPosition.Amount var positionNow = initPosition; // Declares that a variable positionNow indicates the current holding information while (true) { // while loop var needOpen = opAmount; // Declares the temporary variable needOpen and assigns it a value with the parameter amount of transactions required if (isFirst) { // If it is the first time executed, only update isFirst. This is marked as false, since the update is false. The next loop will execute else block until then. isFirst = false; } else { positionNow = GetPosition ((e, contractType, direction); // updates positionNow, the current information held in the store. if (positionNow) { // If there is a holding information, the number of positions needed to open next needOpen is equal to the number of operations required by the parameter minus the difference between the holding information obtained this time and the last time (i.e. how many new hands were opened) needOpen = opAmount - (positionNow.Amount - initAmount); I'm not sure. I'm not sure. var insDetail = _C ((e. SetContractType, contractType); // Set the type of the contract. // Log (for initial hold, initAmount, for current hold, positionNow, for need to hold, needOpen); if (needOpen < insDetail.MinLimitOrderVolume) { // If the next number of hands to open is less than the minimum number of hands to open on the contract's limit list break; // jump out of the loop I'm not sure. if (!CanTrade(opAmount)) { // Wind control module Detects, if returned false, jumps out of the loop without trading. I'm going to break. I'm not sure. var depth = _C ((e.GetDepth); // get the depth of the market. var amount = Math.min ((insDetail.MaxLimitOrderVolume, needOpen); // Restrict order quantity to be no larger than the maximum order quantity of the contracted order e.SetDirection ((direction == PD_LONG? buy button: sell button); // Set the direction according to the parameter direction. the orderId of var; if (direction == PD_LONG) { // Calls different APIs to transact ((open or empty) depending on the direction of the parameter direction) orderId = e.Buy ((depth.Asks[0].Price + (insDetail.PriceTick * SlideTick), Math.min ((amount, depth.Asks[0].Amount), contractType, Ask, depth.Asks[0]); // See the API documentation, CTP commodity futures price slippage for insDetail.PriceTick, which must be an integer multiple of this value // The actual volume of the call API is not greater than the volume of the array } else { orderId = e.Sell ((depth.Bids[0].Price - (insDetail.PriceTick * SlideTick), Math.min ((amount, depth.Bids[0].Amount), contractType, Bid width, depth.Bids[0]); I'm not sure. // Cancel Pending Orders while (true) { // After placing an order An interval of time, cancelling unfinished orders. Sleep (interval); var orders = _C ((e.GetOrders); // Retrieves all unfinished orders if (orders.length === 0) { // If orders are empty arrays, skip the current while I'm going to break. I'm not sure. for (var j = 0; j < orders.length; j++) { // traverses an array of unfinished orders e.CancelOrder ((orders[j].Id); // Cancel an order based on the ID in the order information in the current index. if (j < (orders.length - 1)) { // travels through the interval for a certain amount of time, on the one hand the frequency is too high. Sleep ((Interval); // Sleep is paused Interval milliseconds I'm not sure. I'm not sure. I'm not sure. } // If the main loop while exits var ret = { // Declares an object to be returned price: 0, // the average price of the transaction amount: 0, // the number of transactions position: positionNow // Recently acquired information on the holding of this variety I'm not sure. if (!positionNow) { // Returns the initialized ret directly if no holding information is available return ret; I'm not sure. if (!initPosition) { // If there is no holding information of this kind when the current function is started to execute}. ret.price = positionNow.Price; // The price in the current holding information positionNow is the average holding price at which the transaction was completed ret.amount = positionNow.Amount; // also on } else { // if there is already information about the holding of the variety at the beginning. ret.amount = positionNow.Amount - initPosition.Amount; // The difference is the number of new positions opened ret.price = _N(((positionNow.Price * positionNow.Amount) - (initPosition.Price * initPosition.Amount)) /ret.amount); // The newly added cost of this transaction divided by the newly opened position is the average price of this transaction I'm not sure. return ret; // Returned ret I'm not sure.

function Cover ((e, contractType) { // Single-variety placement function, parameters: exchange object, contract code var insDetail = _C ((e.SetContractType, contractType); // Set the type of the contract while (true) { // main while loop var n = 0; // counting of flatbed operations var opAmount = 0; // declaration The operation The variable var positions = _C ((e.GetPosition); // Calls the API to get hold information, distinguishing the above from the get hold function. See the API documentation for more details. for (var i = 0; i < positions.length; i++) { // Browse through Stored information if (positions[i].ContractType!= contractType) { // If the holding information of the current index is not equal to the contract to be operated i.e.: contractType Continue; // skipped I'm not sure. var amount = Math.min ((insDetail.MaxLimitOrderVolume, positions[i].Amount); // Controls the maximum amount of transactions that are not higher than the invoice var depth; if (positions[i].Type == PD_LONG の の positions[i].Type == PD_LONG_YD) { // handles many positions depth = _C(e.GetDepth); // Calls the API to get current Invoice data opAmount = Math.min ((amount, depth.Bids[0].Amount); // limit The amount of operation is not greater than the amount of one array if (!CanTrade ((opAmount)) { // Wind control module detection return; I'm not sure. e.SetDirection ((positions[i].Type == PD_LONG? closebuy_today button : closebuy button); // Set the transaction direction, see the API documentation for more details

            e.Sell(depth.Bids[0].Price - (insDetail.PriceTick * SlideTick), opAmount, contractType, positions[i].Type == PD_LONG ? "平今" : "平昨", 'Bid', depth.Bids[0]);
                                                                                           // 执行平仓 API ,详细参见 API文档。
            n++;                                                                           // 操作计数累加
        } else if (positions[i].Type == PD_SHORT || positions[i].Type == PD_SHORT_YD) {    // 处理 空仓 类似多仓处理
            depth = _C(e.GetDepth);
            opAmount = Math.min(amount, depth.Asks[0].Amount);
            if (!CanTrade(opAmount)) {
                return;
            }
            e.SetDirection(positions[i].Type == PD_SHORT ? "closesell_today" : "closesell");
            e.Buy(depth.Asks[0].Price + (insDetail.PriceTick * SlideTick), opAmount, contractType, positions[i].Type == PD_SHORT ? "平今" : "平昨", 'Ask', depth.Asks[0]);
            n++;
        }
    }
    if (n === 0) {                                                                         // 如果n 等于 0 ,即初始为0 ,在遍历时没有累加,没有可平的仓位。
        break;                                                                             // 跳出主while循环
    }
    while (true) {                                                                         // 间隔一定时间后, 取消所有挂单。类似Open函数的  CancelPendingOrders
        Sleep(Interval);
        var orders = _C(e.GetOrders);
        if (orders.length === 0) {
            break;
        }
        for (var j = 0; j < orders.length; j++) {
            e.CancelOrder(orders[j].Id);
            if (j < (orders.length - 1)) {
                Sleep(Interval);
            }
        }
    }
}

}

var trans = { // used to display detailed account information in the status bar, Chinese translation, dictionary The account ID tag: The investor account tag, the account ID tag, the account ID tag, the account ID tag, and the account ID tag. In addition to the above, the project is also aimed at the development of a digital currency. In addition, the Bank of Japan has also been involved in the development of a number of new financial instruments, such as the Bank of Japan's New York Stock Exchange. In addition to the above, the company has also been involved in a number of other projects, such as the development of a mobile app for mobile phones. In addition, the number of people who have access to the Internet has been increasing. In addition, the company has also been involved in the development of a number of new products and services. In addition to the above, the Commission has also issued a statement on the implementation of the new rules. The credit rating of the bank has increased, and it is expected that the bank will continue to increase its credit rating. Curr Margin Curr: Curr's current total collateral is up, and the current total collateral is up, and the current total collateral is up. The currency ID tag: The code tag for the currency tag. In addition, the government has announced that it will release the gold in the next few months, which will be used to finance the project. Deposit: Put a sum of money in the deposit box. The exchange's delivery margin is the amount of gold it delivers to the public. The exchange's primary objective is to promote the exchange's growth and attractiveness. In the meantime, we're going to have to find a way to get rid of the frozen cash. The freezing of the frozen Commission: Freezing of the procedure fee, freezing the procedure fee, freezing the procedure fee, freezing the procedure fee, freezing the procedure fee. In addition to the freeze-dried gold, the frozen margin is also used for the production of gold bars. In addition to the above, the Bank of Greece has also announced that it will be offering loans for the purchase of mortgages. In addition, the bank has also been involved in a number of high-profile financial scandals, such as the sale of mortgage-backed securities. In addition, the bank has also been involved in a number of other financial scandals, such as the sale of mortgage loans to the poor. Interest rates: Interest income is rising, interest rates are rising. InterestBase: InterestBase is the base rate of interest, which is the rate of interest. In addition, the government has announced that it will be closing down all of its stores. In addition to the above, you can also buy a mortgage for a fixed amount of money. In addition to the above, the company has also been involved in a number of other projects, such as: The last time I checked the reserve currency, I was in the middle of a crisis, and I was in the middle of a crisis. The last time you had a credit card, you had a credit card, you had a credit card. The last deposit limit was set by the bank. The last time I put money into a mortgage, I had to pay a mortgage. The last time you put money into a mortgage, you put money into a mortgage, you put money in a mortgage, you put money in a mortgage, you put money in a mortgage, you put money in a mortgage, you put money in a mortgage. The last time the bank held a gold deposit, it was in the form of a pre-margin bond. The last mortgage you paid was for the first time, and the last mortgage you paid was for the second time. In addition, the government has announced that it will release the remainder of the reserves in the next few months. Reserve Balance Bond: A reserve bond that holds the underlying futures for settlement. The settlement ID tag: settlement number tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement ID tag, settlement number, settlement ID, settlement ID, settlement number, settlement number, settlement number, settlement ID, settlement number, settlement number, settlement number, settlement number, SpecProductCloseProfit: SpecProduct holdings gain or lose, while the remaining stores lose or lose. Specific Product Commission: Specific Product Commission: Specific Product Commission: Specific Product Commission: Specific Product Commission: Specific Product Commission: Specific Product Commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: Specific product commission: SpecProductExchange Margin: SpecProductExchange guarantees the gold margin of the exchange, which is used for the exchange of products. SpecProductFrozenCommission: SpecProductFrozenCommission is a freezing and freezing service for specialty products. SpecProduct FrozenMargin rubber: Rubber is a special product that is frozen to guarantee gold rubber. SpecProduct Margin: SpecProduct Margin is the percentage of the market that is covered by a specific product. SpecProductPositionProfitProfit: SpecProductPositionProfit is the profit or loss on the stock of a specific product. SpecProductPositionProfitByAlg: SpecProductPositionProfit byAlg: SpecProductPositionProfit byAlg: SpecProductPositionProfit byAlg: SpecProductPositionProfit byAlg: SpecProductPositionProfit byAlg: SpecProductPositionProfit byAlg: SpecProductPositionProfit byAlg: SpecProductPositionProfit byAlg: In addition to the above, the company has also been involved in a number of other projects, such as: Withdrawing the coin: Withdrawing the amount of gold, withdrawing the amount of gold, withdrawing the amount of gold, withdrawing the amount of gold, withdrawing the amount of gold, withdrawing the amount of gold. WithdrawQuota: You can withdraw money from your bank account, but you can't withdraw money from your bank account. It's not that simple.

function AccountToTable ((jsStr, title) { // Function function for exporting account information to a status bar form, parameters: JSON structural string to be displayed, title if (typeof(title) === undefined) { // If the title parameter is not entered, initialize to: Account information The title = "Account information bar"; I'm not sure. var tbl = { // Declares a table object to be passed to the LogStatus function, displayed in the status bar type: table, // type Specified as table title: title, // parameter title Assigns a value to tbl's title field cols: [column field column, column description column, column threshold column], // table column header rows: [] // Array fields in which table data is stored per row, starting as empty arrays. I'm not sure. try { // detects anomalies var fields = JSON.parse ((jsStr); // Parse the jsStr string for (var k in fields) { // Attributes of fields objects, where k is the property value, not clear. See JS tutorial. if (k == AccountID k == BrokerID ) { // If the current traversed property is either of these properties, skip it. Continue I'm not sure. var desc = trans[k]; // get to the Chinese description of desc based on the trans dictionary attribute name var v = fields[k]; // Retrieves the value of the current attribute name if (typeof(v) === number) { // If the property value is of the numeric type, a 5-bit decimal is retained. v = _N ((v, 5); I'm not sure. tbl.rows.push (([k, typeof(desc) === undefined? : desc, v]); // Compresses the one-dimensional array of the current combination of attributes, property descriptions, property values into the rows property of the table object tbl. I'm not sure. } catch (e) {} // Captures anomalies, but does not process them return tbl; // Returns the object tbl I'm not sure.

var PositionManager = (function() { // Declares a variable PositionManager accepts the return value of an anonymous function, which returns the value of an object constructed function PositionManager(e) { // Declares that a function PositionManager is inside an anonymous function. if (typeof(e) === undefined) { // If the parameter e is not passed, the global variable exchange is assigned to e by default e = exchange; I'm not sure. if (e.GetName()!== Futures_CTP) { // Detects whether the main exchange object e is a commodity futures exchange, or if it is not throwing an exception. throw Only support CTP; // Only support CTP I'm not sure. this.e = e; // add an attribute e to the current function (which is also an object) and assign the argument e to it this.account = null; // Add an account Variable initially null I'm not sure. // Get the cache PositionManager.prototype.Account = function (() { // Add method to the above-stated PositionManager function Account if (!this.account) { // If the account property of PositionManager is null this.account = _C ((this.e.GetAccount); // Calls the GetAccount function (i.e. the Exchange Object API) of this.e. exchange object to get account information. I'm not sure. return this.account; // This method returns this PositionManager.account account information. I'm not sure. PositionManager.prototype.GetAccount = function ((getTable) { // Add method This method obtains the most recent account information This.account = _C ((this.e.GetAccount); if (typeof(getTable)!== undefined string && getTable) { // If you want to return the details of the most recently retrieved account information as an object, getTable must be true return AccountToTable ((this.e.GetRawJSON))) // GetRawJSON function See the API documentation for more details I'm not sure. return this.account; // Returns the account information after the update. I'm not sure.

PositionManager.prototype.GetPosition = function(contractType, direction, positions) { // 给 PositionManager 添加方法 用于在主策略中调用该模板的 函数
    return GetPosition(this.e, contractType, direction, positions);
};

PositionManager.prototype.OpenLong = function(contractType, shares) {                  // 添加 开多仓 方法
    if (!this.account) {
        this.account = _C(this.e.GetAccount);
    }
    return Open(this.e, contractType, PD_LONG, shares);
};

PositionManager.prototype.OpenShort = function(contractType, shares) {                 // 添加 开空仓 方法
    if (!this.account) {
        this.account = _C(this.e.GetAccount);
    }
    return Open(this.e, contractType, PD_SHORT, shares);
};

PositionManager.prototype.Cover = function(contractType) {                             // 添加 平仓 方法
    if (!this.account) {
        this.account = _C(this.e.GetAccount);
    }
    return Cover(this.e, contractType);
};
PositionManager.prototype.CoverAll = function() {                                      // 添加 所有仓位全平方法
    if (!this.account) {
        this.account = _C(this.e.GetAccount);
    }
    while (true) {
        var positions = _C(this.e.GetPosition)
        if (positions.length == 0) {
            break
        }
        for (var i = 0; i < positions.length; i++) {                                   // 首先平掉 对冲合约 对冲合约 举例 MA709&MA705
            // Cover Hedge Position First
            if (positions[i].ContractType.indexOf('&') != -1) {
                Cover(this.e, positions[i].ContractType)
                Sleep(1000)
            }
        }
        for (var i = 0; i < positions.length; i++) {
            if (positions[i].ContractType.indexOf('&') == -1) {
                Cover(this.e, positions[i].ContractType)
                Sleep(1000)
            }
        }
    }
};
PositionManager.prototype.Profit = function(contractType) {                            // 添加计算收益的方法
    var accountNow = _C(this.e.GetAccount);
    return _N(accountNow.Balance - this.account.Balance);
};

return PositionManager;                                                                // 匿名函数返回 在自身内声明的 PositionManager 函数(对象)。

})();

$.NewPositionManager = function(e) { // Exports a function to construct a PositionManager object return new PositionManager ((e); I'm not sure.

// Via: http://mt.sohu.com/20160429/n446860150.shtml$.IsTrading = function ((symbol) { // Determines whether a contract is in the trade Within the time frame, the parameter symbol Contract code var now = new Date ((); // Retrieves the current time object var day = now.getDay(); // Retrieves the current time on a specific day of the week. var hour = now.getHours(); // Retrieves the hour of the 24 hours var minute = now.getMinutes ((); // Get which minute in a minute

if (day === 0 || (day === 6 && (hour > 2 || hour == 2 && minute > 30))) {              // 第一个过滤, day == 0 星期天  或者  day == 6 星期六并且
    return false;                                                                      // 2点30以后 。 星期五 夜盘结束。  返回 false  即所有品种不在交易时间
}
symbol = symbol.replace('SPD ', '').replace('SP ', '');                                // 正则表达式 匹配其交易系统用“SPD”表示跨期套利交易,若指令买进“SPD CF1609&CF17...
                                                                                       // 过滤掉 跨期套利的 合约编码
var p, i, shortName = "";
for (i = 0; i < symbol.length; i++) {                                                  // 遍历合约代码字符串,取出 代码(排除数字的部分)赋值给shortName 并且转换为大写
    var ch = symbol.charCodeAt(i);
    if (ch >= 48 && ch <= 57) {
        break;
    }
    shortName += symbol[i].toUpperCase();
}

var period = [                                                                         // 通常交易时间  9:00 - 10:15,
    [9, 0, 10, 15],                                                                    //             10:30 - 11:30
    [10, 30, 11, 30],                                                                  //              13:30 - 15:00
    [13, 30, 15, 0]
];
if (shortName === "IH" || shortName === "IF" || shortName === "IC") {                  // 如果是这些 品种,交易时间 period 调整
    period = [
        [9, 30, 11, 30],
        [13, 0, 15, 0]
    ];
} else if (shortName === "TF" || shortName === "T") {                                  // 国债品种  时间调整
    period = [
        [9, 15, 11, 30],
        [13, 0, 15, 15]
    ];
}


if (day >= 1 && day <= 5) {                                                            // 如果是 周一 到周五, 不考虑夜盘。 判断当前时间是否符合 period 设定的时间表
    for (i = 0; i < period.length; i++) {
        p = period[i];
        if ((hour > p[0] || (hour == p[0] && minute >= p[1])) && (hour < p[2] || (hour == p[2] && minute < p[3]))) {
            return true;                                                               // 符合遍历出的  时间表 中的 时间段,  即该品种在交易时间内。
        }
    }
}

var nperiod = [                                                                        // 额外判断 夜盘品种  nperiod[n][0] 是夜盘时间相同的一类
                                                                                       // 品种汇总,nperiod[n][1] 就是该类品种的夜盘交易时间
    [
        ['AU', 'AG'],
        [21, 0, 02, 30]
    ],
    [
        ['CU', 'AL', 'ZN', 'PB', 'SN', 'NI'],
        [21, 0, 01, 0]
    ],
    [
        ['RU', 'RB', 'HC', 'BU'],
        [21, 0, 23, 0]
    ],
    [
        ['P', 'J', 'M', 'Y', 'A', 'B', 'JM', 'I'],
        [21, 0, 23, 30]
    ],
    [
        ['SR', 'CF', 'RM', 'MA', 'TA', 'ZC', 'FG', 'IO'],
        [21, 0, 23, 30]
    ],
];
for (i = 0; i < nperiod.length; i++) {                                                // 遍历所有夜盘品种 交易时间段,对比当前时间。
    for (var j = 0; j < nperiod[i][0].length; j++) {
        if (nperiod[i][0][j] === shortName) {
            p = nperiod[i][1];
            var condA = hour > p[0] || (hour == p[0] && minute >= p[1]);
            var condB = hour < p[2] || (hour == p[2] && minute < p[3]);
            // in one day
            if (p[2] >= p[0]) {
                if ((day >= 1 && day <= 5) && condA && condB) {
                    return true;
                }
            } else {
                if (((day >= 1 && day <= 5) && condA) || ((day >= 2 && day <= 6) && condB)) {
                    return true;
                }
            }
            return false;
        }
    }
}
return false;

};

$.NewTaskQueue = function ((onTaskFinish) { // A queue object constructor function used to perform a variety of transactions. Parameter: a function that returns when a task is completed. var self = {} // declares an empty object self.ERR_SUCCESS = 0 // Define the return message Successfully self.ERR_SET_SYMBOL = 1 // Error in setting the contract self.ERR_GET_RECORDS = 2 // Error in the acquisition of the K line self.ERR_GET_ORDERS = 3 // Obtained an unfinished order error self.ERR_GET_POS = 4 // Error in obtaining the holding information self.ERR_TRADE = 5 // The transaction was incorrect self.ERR_GET_DEPTH = 6 // Gain depth field error self.ERR_NOT_TRADING = 7 // not at the time of trading self.ERR_BUSY = 8 // blocked

self.onTaskFinish = typeof(onTaskFinish) === 'undefined' ? null : onTaskFinish  // 如果在 初始化队列对象时没有 传入需要回调的匿名函数,该属性赋值为null,否则赋值回调函数
self.retryInterval = 300                                                        // 重试间隔 毫秒数
self.tasks = []                                                                 // 这个是一个重要的属性,队列中储存任务的数组。
self.pushTask = function(e, symbol, action, amount, arg, onFinish) {            // 给空对象添加函数,该函数是压入 新任务 到任务数组中。参数分别为:
                                                                                // 交易所对象、合约代码、执行动作、数量、回调函数参数、回调函数
    var task = {                                                                // 构造一个任务对象
        e: e,                                                                   // 交易所对象
        action: action,                                                         // 执行的动作
        symbol: symbol,                                                         // 合约代码
        amount: amount,                                                         // 操作数量
        init: false,                                                            // 是否初始化
        finished: false,                                                        // 是否任务完成
        dealAmount: 0,                                                          // 已处理的 量
        preAmount: 0,                                                           // 上一次的 量
        preCost: 0,                                                             // 上一次的 花费
        retry: 0,                                                               // 重试次数
        maxRetry: 10,                                                           // 最大重试次数
        arg: typeof(onFinish) !== 'undefined' ? arg : undefined,                // 如果没有传入 回调函数,此项 设置为 undefined
        onFinish: typeof(onFinish) == 'undefined' ? arg : onFinish              // 如果没有传入回调函数,把 arg 复制给 onFinish(实际上是 arg没传入,中间隔过去了)
    }
    
    switch (task.action) {                                                      // 根据执行的动作初始化描述信息
        case "buy":
            task.desc = task.symbol + " 开多仓, 数量 " + task.amount
            break
        case "sell":
            task.desc = task.symbol + " 开空仓, 数量 " + task.amount
            break
        case "closebuy":
            task.desc = task.symbol + " 平多仓, 数量 " + task.amount
            break
        case "closesell":
            task.desc = task.symbol + " 平空仓, 数量 " + task.amount
            break
        default:
            task.desc = task.symbol + " " + task.action + ", 数量 " + task.amount
    }

    self.tasks.push(task)                                                       // 压入任务数组中
    Log("接收到任务", task.desc)                                                  // 输出日志 显示 接收到任务。
}

self.cancelAll = function(e) {                                                  // 添加函数,取消所有,参数: 交易所对象
    while (true) {                                                              // 遍历未完成的所有订单,逐个取消。
        var orders = e.GetOrders();
        if (!orders) {                                                          // 所有API 调用都不重试,如果API调用失败,立即返回。
            return self.ERR_GET_ORDERS;
        }
        if (orders.length == 0) {
            break;
        }
        for (var i = 0; i < orders.length; i++) {
            e.CancelOrder(orders[i].Id);
            Sleep(self.retryInterval);
        }
    }
    return self.ERR_SUCCESS                                                      // 返回 完成标记
}

self.pollTask = function(task) {                                                 // 执行数组中弹出的任务
    var insDetail = task.e.SetContractType(task.symbol);                         // 切换到当前 任务 task 对象保存的合约类型
    if (!insDetail) {                                                            // 切换失败 立即返回
        return self.ERR_SET_SYMBOL;
    }
    var ret = null;
    var isCover = task.action != "buy" && task.action != "sell";                 // 根据执行的动作,设置 是否是平仓的 标记
    do {                                                                         // do while 循环,先执行 do 以内
        if (!$.IsTrading(task.symbol)) {                                         // 判断是否在交易时间
            return self.ERR_NOT_TRADING;                                         // 不在交易时间立即返回
        }
        if (self.cancelAll(task.e) != self.ERR_SUCCESS) {                        // 调用全部取消函数 ,如果不等于 完成状态
            return self.ERR_TRADE;                                               // 返回交易失败
        }
        if (!CanTrade(task.amount)) {                                            // 风控模块检测。
            ret = null
            break
        }
        var positions = task.e.GetPosition();                                    // 获取持仓信息
        // Error
        if (!positions) {
            return self.ERR_GET_POS;                                             // 如果调用获取持仓 API 错误,立即返回
        }
        // search position
        var pos = null;
        for (var i = 0; i < positions.length; i++) {                             // 遍历持仓信息,查找持仓合并持仓,类似 上面的 GetPosition 函数
            if (positions[i].ContractType == task.symbol && (((positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD) && (task.action == "buy" || task.action == "closebuy")) || ((positions[i].Type == PD_SHORT || positions[i].Type == PD_SHORT_YD) && (task.action == "sell" || task.action == "closesell")))) {
                if (!pos) {
                    pos = positions[i];
                    pos.Cost = positions[i].Price * positions[i].Amount;
                } else {
                    pos.Amount += positions[i].Amount;
                    pos.Profit += positions[i].Profit;
                    pos.Cost += positions[i].Price * positions[i].Amount;
                }
            }
        }
        // record pre position
        if (!task.init) {                                                        // 如果任务没有初始化,执行以下
            task.init = true;                                                    // 更新为已初始化
            if (pos) {                                                           // 如果查找到之前的持仓,把之前的持仓数量、 花费 复制给task 的相应变量保存
                task.preAmount = pos.Amount;
                task.preCost = pos.Cost;
            } else {                                                             // 如果执行这个任务 时没有 ,同样的方向  同样合约的持仓,把task相关变量赋值0
                task.preAmount = 0;
                task.preCost = 0;
                if (isCover) {                                                   // 如果是 平仓动作,输出日志 : 找不到仓位,跳出循环。
                    Log("找不到仓位", task.symbol, task.action);
                    ret = null;
                    break;
                }
            }
        }
        var remain = task.amount;                                                // 声明一个局部变量,用 任务的属性 amount(任务设定的交易量) 初始化
        if (isCover && !pos) {                                                   // 如果 第二次循环中 , 该任务动作是平仓,并且 没有持仓了,给pos 赋值
            pos = {Amount:0, Cost: 0, Price: 0}
        }
        if (pos) {                                                               // 如果 pos 不为null 
            task.dealAmount = pos.Amount - task.preAmount;                       // 已经处理的任务量 等于 每次获取的持仓信息持仓量 与最初开始循环的初始持仓信息持仓量的差值
            if (isCover) {                                                       // 如果是 平仓动作, dealAmount 是负值, 这里取反操作
                task.dealAmount = -task.dealAmount;
            }
            remain = parseInt(task.amount - task.dealAmount);                    // 任务的 交易量 减去 已经处理的交易量  得出 剩余需要处理的交易量
            if (remain <= 0 || task.retry >= task.maxRetry) {                    // 如果剩余需要的交易量小于等于0(此处分析应该是不会小于0,有兴趣的可以分析下。) 或者重试次数大于最大重试上限.
                ret = {                                                          // 更新ret 对象,  更新为已经成交的信息,和 当前仓位信息。
                    price: (pos.Cost - task.preCost) / (pos.Amount - task.preAmount),
                    amount: (pos.Amount - task.preAmount),
                    position: pos
                };
                if (isCover) {                                                   // 如果是 平仓动作
                    ret.amount = -ret.amount;                                    // 平仓时计算出的是负值  ,取反操作
                    if (pos.Amount == 0) {                                       // 如果持仓量为0了, 把ret 的持仓信息 赋值为 null
                        ret.position = null;
                    }
                }
                break;                                                           // remain <= 0 || task.retry >= task.maxRetry 符合这个条件,跳出while循环
            }
        } else if (task.retry >= task.maxRetry) {                                // 如果不是 平仓操作。pos 为null 没有持仓(平仓操作 pos 此处不会是null)
            ret = null;                                                          // 并且 该任务重试测试 大于最大重试次数。跳出循环。
            break;                                                               // 即此时  , 超过最大重试次数,并且 没有增加持仓(开仓 每次都失败了。),跳出循环
        }

        var depth = task.e.GetDepth();                                           // 获取 深度数据
        if (!depth) {
            return self.ERR_GET_DEPTH;                                           // 获取失败立即返回
        }
        var orderId = null;                                                      // 订单ID
        var slidePrice = insDetail.PriceTick * SlideTick;                        // 计算具体滑价值
        if (isCover) {                                                           // 如果是平仓操作
            for (var i = 0; i < positions.length; i++) {                         // 遍历本轮的  API 返回的持仓信息。
                if (positions[i].ContractType !== task.symbol) {                 // 不是当前任务 品种的  跳过。
                    continue;
                }
                if (parseInt(remain) < 1) {                                      // 需要处理的 交易的量 如果小于1,跳出 while
                    break
                }
                var amount = Math.min(insDetail.MaxLimitOrderVolume, positions[i].Amount, remain);  // 在合约规定的最大下单量、持仓量、需要处理的量中取最小值。 
                if (task.action == "closebuy" && (positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD)) {   // 如果是平多仓, 持仓信息 为 今日多仓  或者 昨日多仓
                    task.e.SetDirection(positions[i].Type == PD_LONG ? "closebuy_today" : "closebuy");                  // 设置方向
                    amount = Math.min(amount, depth.Bids[0].Amount)                                                     // 根据盘口量 和 下单量 再取一个最小值。
                    orderId = task.e.Sell(_N(depth.Bids[0].Price - slidePrice, 2), amount, task.symbol, positions[i].Type == PD_LONG ? "平今" : "平昨", 'Bid', depth.Bids[0]);
                                                                                                                        // 执行具体的 API 操作,以下平空类似
                } else if (task.action == "closesell" && (positions[i].Type == PD_SHORT || positions[i].Type == PD_SHORT_YD)) {
                    task.e.SetDirection(positions[i].Type == PD_SHORT ? "closesell_today" : "closesell");
                    amount = Math.min(amount, depth.Asks[0].Amount)
                    orderId = task.e.Buy(_N(depth.Asks[0].Price + slidePrice, 2), amount, task.symbol, positions[i].Type == PD_SHORT ? "平今" : "平昨", 'Ask', depth.Asks[0]);
                }
                // assume order is success insert
                remain -= amount;                                                // 假设是成功执行, 需要处理的交易量 减去 此次交易的量。
            }
        } else {                                                                 // 开仓
            if (task.action == "buy") {
                task.e.SetDirection("buy");
                orderId = task.e.Buy(_N(depth.Asks[0].Price + slidePrice, 2), Math.min(remain, depth.Asks[0].Amount), task.symbol, 'Ask', depth.Asks[0]);
            } else {
                task.e.SetDirection("sell");
                orderId = task.e.Sell(_N(depth.Bids[0].Price - slidePrice, 2), Math.min(remain, depth.Bids[0].Amount), task.symbol, 'Bid', depth.Bids[0]);
            }
        }
        // symbol not in trading or other else happend
        if (!orderId) {                                                          // 没有返回具体的ID ,可能是 交易不在交易队列,或者其他错误。
            task.retry++;                                                        // 累计重试次数
            return self.ERR_TRADE;                                               // 返回错误信息。即使不成功, 重新 执行该任务的时候 会重新一次流程。除了task对象的数据 所有数据都会刷新
        }
    } while (true);                                                              // 循环判断 恒为真
    task.finished = true                                                         // 如果在 while 循环中没有直接 return  顺序执行到此,则任务完成                                                      

    if (self.onTaskFinish) {                                                     // 如果队列控制对象的 回调函数 设置 不为null(即 self.onTaskFinish 存在)
        self.onTaskFinish(task, ret)                                             // 执行回调函数。把 task 任务 对象  和 交易的结果  ret 对象 传入回调函数。 
    }

    if (task.onFinish) {                                                         // 处理 任务的回调函数
        task.onFinish(task, ret);
    }
    return self.ERR_SUCCESS;
}

self.poll = function() {                                                         // 迭代执行 弹出 tasks 中的任务 ,并调用 pollTask 执行任务。
    var processed = 0                                                            // 未执行完成的任务计数 ,每次初始0
    _.each(self.tasks, function(task) {                                          // 迭代  可以搜索 _.each 的用法
        if (!task.finished) {                                                    // 如果 任务不是完成状态,
            processed++                                                          // 未完成任务 计数 累计
            self.pollTask(task)                                                  // 执行弹出的任务
        }
    })
    if (processed == 0) {                                                        // 如果没有未完成的任务,即 所有任务队列内的任务完成 ,执行清空 队列对象中 tasks 数组.
        self.tasks = []
    }
}

self.size = function() {                                                         // 给队列对象添加 函数 size 获取 任务队列 中 任务个数
    return self.tasks.length
}

return self                                                                      // 返回构造好的队列对象

}

$.AccountToTable = AccountToTable;


More