Hãy chơi JavaScript với những người già - tạo ra một đối tác bán hàng và mua hàng.

Tác giả:Giấc mơ nhỏ, Tạo: 2017-03-16 12:29:51, Cập nhật: 2017-10-11 10:37:54

Một công cụ hữu ích phải biết lý do tại sao nó hữu ích!

Một điều rất thú vị khi viết chiến lược trong ngày làm việc, những ý tưởng vô nghĩa xuất hiện trên biên tập viên, có lẽ đó là chiếc cốc linh thiêng tiếp theo của bạn. Điều duy nhất ảnh hưởng đến niềm đam mê này là việc xử lý logic mua bán cho hệ thống chiến lược giao dịch, điều này thực sự rất quan trọng, nhưng nó hơi nhàm chán và logic phức tạp.

  • Rất may là có một mô-đun được viết tốt và có thể sử dụng trực tiếp (một số bài viết trước đã sử dụng nó), nhưng ngoài việc sử dụng và hiểu cách nó hoạt động, tôi đã chú thích mã:

/* Interval Trục trặc thử lại khoảng thời gian (millisecond) Số (number) 500 SlideTick Số điểm giá trượt (tổng số) Định dạng số (số) 1 RiskControl bật kiểm soát gió dạng Bull ((true/false) false MaxTrade@RiskControl Số lần giao dịch tối đa trong ngày làm việc MaxTradeAmount@RiskControl Tối đa số đơn đặt hàng */

var __orderCount = 0 // ghi lại số lần đơn tiếp theo trong ngày làm việc hiện tại var __orderDay = 0 // ghi lại ngày của ngày làm việc hiện tại

function CanTrade ((tradeAmount) { // Mô-đun kiểm soát rủi ro, tham số: Số lượng giao dịch if (!RiskControl) { // mặc định không khởi động mô-đun kiểm soát gió, nếu không khởi động, hàm CanTrade trả về true return true ♪ if (typeof ((tradeAmount) == number && tradeAmount > MaxTradeAmount) { // Nhập tham số tradeAmount là loại số và số đơn đặt hàng lớn hơn số đơn đặt hàng tối đa được đặt trong tham số mẫu Log ((định hạn của mô-đun điều khiển bão, vượt quá khối lượng đơn hàng tối đa, MaxTradeAmount, #ff0000 @); // xuất thông báo gợi ý, ngưng thực hiện. Throw quạt ngắt quạt thực hiện return false; ♪ var nowDay = new Date (().getDate ((); // lấy ngày hiện tại if (nowDay!= __orderDay) { // getDate() Trả về ngày nào đó trong tháng từ đối tượng Date (1 ~ 31);; bắt đầu bằng 0 _orderDay lần đầu tiên không bằng nowDay __orderDay = nowDay; // __orderDay là một biến toàn cầu ghi lại ngày khởi động lần đầu tiên vào mô-đun điều khiển gió. __orderCount = 0; // Cập nhật biến __orderDay, đặt lại biến __orderCount ♪ __orderCount++; // biến toàn cầu __orderCount Số đơn tiếp theo, tự cộng cộng. if (__orderCount > MaxTrade) { // Xác định số lần giao dịch tối đa trong một ngày vượt quá số lần giao dịch tối đa trong một ngày Log ((Hạn chế module điều khiển bão, không thể giao dịch, vượt quá số lần đơn nhất tối đa, MaxTrade, #ff0000 @); // vượt quá, xuất thông báo gợi ý, ngưng thực hiện. Throw quạt ngắt quạt thực hiện return false; ♪ return true; // không kích hoạt bất kỳ điều kiện nào trên, trả về true, nghĩa là có thể giao dịch. ♪

function init ((() { // Chức năng khởi tạo mẫu, sẽ được thực hiện trước khi mẫu được tải. if (typeof ((SlideTick) === undefined) { // Kiểm tra xem SlideTick chưa được định nghĩa. SlideTick = 1; // đặt mặc định là 1 } else { // phân tích chuỗi chuyển đổi thành số, tuy nhiên nếu là một chuỗi bắt đầu từ các ký tự phi số sẽ trả về NaN, có thể gây ra lỗi SlideTick = parseInt ((SlideTick); Log (đóng thư mục giao dịch hàng hóa thành công);

function GetPosition ((e, contractType, direction, positions) { // Kết hợp một hợp đồng với vị trí ngày hôm qua và vị trí hiện tại cùng một hướng, các tham số như sau: đối tượng giao dịch, loại hợp đồng, hướng, dữ liệu nắm giữ được trả về bởi API ((không có chỗ)
var allCost = 0; // contractType Thỏa thuận theo hướng tổng số tiền chi tiêu, không tính theo số lượng hợp đồng (vì tổng thể có thể được thỏa thuận) var allAmount = 0; // Tổng số hợp đồng var allProfit = 0; // tổng lợi nhuận và lỗ var allFrozen = 0; // Tổng số lượng đông lạnh var pos Margin = 0; // Hợp đồng nắm giữ Đòn bẩy if (typeof ((positions) === undefined ᅢ!positions) { // Nếu tham số không truyền thông tin lưu trữ được trả về API positions = _C ((e.GetPosition); // gọi API để lấy thông tin lưu trữ. ♪ for (var i = 0; i < positions.length; i++) { // đi qua mảng thông tin lưu trữ này。 if (positions[i].ContractType == contractType && // Mã hợp đồng của thông tin nắm giữ trong chỉ mục hiện tại == Mã hợp đồng được chỉ định bởi tham số (contractType) và hướng tương đương với hướng (direction) của tham số được truyền (current position) hoặc vị trí trước đó (((positions[i].Type == PD_LONG bởi positions[i].Type == PD_LONG_YD) && direction == PD_LONG) bởi ((positions[i].Type == PD_SHORT bởi positions[i].Type == PD_SHORT_YD) && direction == PD_SHORT)) ) { // thực hiện if block theo điều kiện posMargin = positions[i].MarginLevel; // lấy giá trị đòn bẩy Đặt giá trị cho posMargin allCost += (positions[i].Price * positions[i].Amount); // Tổng chi phí ((Số lượng hợp đồng đã được giao, giá nắm giữ chỉ số hiện tại * Số lượng nắm giữ) tổng cộng allAmount += positions[i].Amount; // Tổng số người ký hợp đồng allProfit += positions[i].Profit; // Hợp đồng lơ lửng lợi nhuận lỗ tích lũy allFrozen += positions[i].FrozenAmount; // Tổng số bàn giao dịch bị đóng băng ♪ ♪ if (allAmount === 0) { // Nếu số lượng hợp đồng đủ điều kiện tích lũy sau khi đi qua là 0, trả về null, tức là không có hợp đồng hạn chế có điều kiện return null; ♪ return { // allAmount không bằng 0, trả về thông tin lưu trữ của một đối tượng sau khi hợp nhất. MarginLevel: postMargin, FrozenAmount: allFrozen, Giá: _N ((allCost / allAmount), Amount: allAmount, Profit: allProfit, Type: direction, ContractType: contractType }; ♪

Open function ((e, contractType, direction, opAmount) { // Chức năng mở giao dịch, tham số: đối tượng sàn giao dịch, mã hợp đồng, hướng, số lượng giao dịch var initPosition = GetPosition ((e, contractType, direction); // gọi hàm GetPosition ở trên để lấy thông tin lưu trữ sau khi hợp nhất. var isFirst = true; // thiết lập thẻ isFirst (cho thấy dưới đây while là vòng lặp đầu tiên là true) var initAmount = initPosition? initPosition.Amount : 0; // Nếu initPosition là null, initAmount sẽ được gán 0, nếu không thì initPosition.Amount var positionNow = initPosition; // tuyên bố một biến positionNow cho thông tin lưu trữ hiện tại while (true) { // while vòng lặp var needOpen = opAmount; // tuyên bố biến tạm thời needOpen và gán giá trị cho nó bằng tham số số lượng cần giao dịch if (isFirst) { // Nếu là lần đầu tiên thực hiện, chỉ cập nhật isFirst. Được đánh dấu là false, vì cập nhật là false. isFirst = false; } else { positionNow = GetPosition ((e, contractType, direction); // cập nhật positionNow, thông tin lưu trữ hiện tại. if (positionNow) { // Nếu có thông tin nắm giữ, số lượng giao dịch cần mở tiếp theo needOpen bằng số lượng giao dịch yêu cầu bởi tham số trừ thông tin nắm giữ được lấy lần này so với lần trước (tức là bao nhiêu bàn tay mới được mở) needOpen = opAmount - (positionNow.Amount - initAmount); ♪ ♪ ♪ ♪ var insDetail = _C ((e.SetContractType, contractType); // thiết lập loại hợp đồng. // Log (( Initial hold, initAmount, Current hold, positionNow, Need to hold, needOpen); if (needOpen < insDetail.MinLimitOrderVolume) { // Nếu số lượng giao dịch tiếp theo nhỏ hơn số lượng giao dịch tối thiểu trong danh sách giới hạn của hợp đồng break; // thoát khỏi vòng lặp if (!CanTrade(opAmount)) { // Wind Control Module Chẩn đoán, nếu trả về false, thoát khỏi vòng không giao dịch. break; var depth = _C ((e.GetDepth); // thu thập thông tin về độ sâu thị trường. var amount = Math.min ((insDetail.MaxLimitOrderVolume, needOpen); // Hạn chế số lượng đơn hàng không thể lớn hơn số lượng đơn hàng tối đa của danh sách hạn chế hợp đồng e.SetDirection ((direction == PD_LONG? buy: sell); // Đặt chỉ định theo tham số direction. var orderId; if (direction == PD_LONG) { // Gọi các API khác nhau để giao dịch ((mở nhiều hoặc trống) tùy theo hướng của tham số direction) orderId = e.Buy ((depth.Asks[0].Price + (insDetail.PriceTick * SlideTick), Math.min ((amount, depth.Asks[0].Amount), contractType, Ask, depth.Asks[0]); // Xem tài liệu API cụ thể, CTP giá kỳ hạn hàng hóa di chuyển một lần để insDetail.PriceTick, phải là một số nguyên lần của giá trị này // Số lượng đơn thực tế của cuộc gọi API không lớn hơn số lượng một hàng } else { orderId = e.Sell ((depth.Bids[0].Price - (insDetail.PriceTick * SlideTick), Math.min ((amount, depth.Bids[0].Amount), contractType, Bid, depth.Bids[0]); // Hủy PendingOrders while (true) { // Sau khi đặt hàng, bỏ qua các đơn đặt hàng chưa hoàn thành. Ngủ (Interval); var orders = _C ((e.GetOrders); // lấy tất cả các đơn đặt hàng chưa hoàn thành if (orders.length === 0) { // Nếu orders là một tập hợp không, hãy bỏ trong khi hiện tại break; for (var j = 0; j < orders.length; j++) { // Đi qua mảng lệnh chưa hoàn thành e.CancelOrder ((orders[j].Id); // hủy đặt hàng theo ID trong thông tin đặt hàng trong chỉ mục hiện tại. if (j < (orders.length - 1)) { // đi qua khoảng thời gian nhất định, một mặt tần số quá cao. Sleep ((Interval); // Sleep tạm dừng Interval millisecond } // Nếu vòng tròn chính while thoát ra var ret = { // tuyên bố một đối tượng được sử dụng để trả về giá: 0, // giá trung bình giao dịch số lượng: 0, // số lượng giao dịch position: positionNow // Thông tin gần đây về sản phẩm }; if (!positionNow) { // Nếu không có thông tin lưu trữ, trực tiếp trả về ret khởi tạo return ret; if (!initPosition) { // Nếu không có bất kỳ thông tin lưu trữ nào của loại này khi bắt đầu thực hiện hàm hiện tại. ret.price = positionNow.Price; // thông tin nắm giữ hiện tại. ret.amount = positionNow.Amount; // cùng với } else { // nếu đã có thông tin lưu trữ của giống đó khi bắt đầu. ret.amount = positionNow.Amount - initPosition.Amount; // Sự khác biệt là số lần mở mới ret.price = _N(((positionNow.Price * positionNow.Amount) - (initPosition.Price * initPosition.Amount)) / ret.amount); // Chi phí mới tăng trong giao dịch này được tính bằng giá trung bình của giao dịch này return ret; // Trả về ret

function Cover ((e, contractType) { // Phương thức giao dịch đơn, tham số: đối tượng sàn giao dịch, mã hợp đồng var insDetail = _C ((e.SetContractType, contractType); // Thiết lập loại hợp đồng while (true) { // vòng tròn chính while var n = 0; // Đếm các hoạt động bình đẳng var opAmount = 0; // tuyên bố Hoạt động var positions = _C ((e.GetPosition); // Gọi API lấy thông tin lưu trữ, phân biệt với hàm lấy lưu trữ trên. Xem tài liệu API chi tiết. for (var i = 0; i < positions.length; i++) { // truy cập thông tin lưu trữ if (positions[i].ContractType!= contractType) { // Nếu thông tin nắm giữ trong chỉ mục hiện tại hợp đồng không bằng hợp đồng để vận hành: contractType Continue; // Bỏ qua var amount = Math.min ((insDetail.MaxLimitOrderVolume, positions[i].Amount); // Kiểm soát số lượng giao dịch tối đa không quá đơn hàng var depth; if (positions[i].Type == PD_LONG の の positions[i].Type == PD_LONG_YD) { // xử lý nhiều vị trí depth = _C ((e.GetDepth); // Gọi API để lấy dữ liệu hiện tại opAmount = Math.min ((amount, depth.Bids[0].Amount); // giới hạn if (!CanTrade ((opAmount)) { // Phát hiện mô-đun điều khiển gió return; e.SetDirection ((positions[i].Type == PD_LONG? closebuy_today : closebuy ); // thiết lập hướng giao dịch, xem tài liệu API chi tiết

            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 = { // được sử dụng để hiển thị thông tin chi tiết tài khoản trong thanh trạng thái AccountID : Tài khoản nhà đầu tư Available: có thể sử dụng để kiếm tiền, có thể sử dụng để kiếm tiền, có thể sử dụng để kiếm tiền. Theo đó, các nhà đầu tư sẽ có thể sử dụng các dịch vụ giao dịch tương lai để thanh toán các khoản tiền dự trữ trong tương lai. Trong khi đó, một số công ty khác cũng đang tìm kiếm các ứng dụng khác để cung cấp dịch vụ giao dịch. Những người tham gia vào cuộc họp này nói rằng họ không thể làm được điều đó. Theo đó, các nhà đầu tư sẽ có thể làm việc với các nhà đầu tư khác trong tương lai. Trong khi đó, các nhà lãnh đạo của các nước khác cũng có thể tham gia vào các cuộc biểu tình. Theo đó, số lượng tín dụng của các công ty có thể tăng lên từ 10 tỷ USD/năm. Trong khi đó, các nhà đầu tư khác cũng cho biết rằng họ sẽ phải trả cho các khoản đầu tư của họ. CurrencyID token: mã token của đồng coin. Các nhà đầu tư đã giao các đồng vàng đảm bảo, trong khi các nhà đầu tư đã giao các đồng vàng đảm bảo, trong khi các nhà đầu tư đã giao các đồng vàng đảm bảo. Deposit: Đặt số tiền vào . Trong khi đó, một số nhà đầu tư khác cũng cho biết rằng họ sẽ bán đồng xu bằng tiền mặt. Trong khi đó, các nhà đầu tư khác cũng cho biết họ sẽ tiếp tục sử dụng các loại tiền tệ như tiền bạc, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử, tiền điện tử. Trong khi đó, một số người cho rằng tiền mặt đã bị đóng băng. Theo một số nguồn tin trên, các nhà báo đã đưa ra những tuyên bố về việc việc đóng băng các máy tính của các công ty. Có thể bạn sẽ thấy một số hình ảnh của những người đã làm việc tại các trang web của chúng tôi, trong đó có những hình ảnh của những người đã làm việc tại các trang web của chúng tôi. FundMortgageAvailable: Số dư thế chấp tiền tệ Trong khi đó, các nhà đầu tư khác cũng có thể sử dụng các dịch vụ này. Trong khi đó, các nhà đầu tư khác cũng có thể tham gia vào các hoạt động này. Những người tham gia vào cuộc họp này đã nói rằng: "Chúng tôi không thể làm điều đó. Lợi nhuận cơ sở: Lợi nhuận cơ sở: Lợi nhuận cơ sở: Những người tham gia vào cuộc họp này nói rằng: "Chúng tôi không thể làm điều đó". Các quỹ đầu tư có thể ký quỹ một số tiền, trong đó có các quỹ đầu tư có thể ký quỹ một số khoản tiền. Trong khi đó, các nhà đầu tư khác cũng có thể làm điều này. Trong khi đó, các nhà đầu tư khác cũng cho biết họ sẽ không trả tiền cho các khoản đầu tư của họ. Trước đó, các nhà đầu tư đã cho biết họ sẽ tiếp tục sử dụng các khoản tín dụng của họ để tăng giá trị tài chính. PreDeposit: Số tiền đặt cọc cuối cùng đã được đặt cọc. Trong khi đó, các nhà đầu tư khác cũng có thể sử dụng các khoản tiền này. Trong khi đó, các nhà đầu tư khác cũng cho biết họ sẽ không trả tiền cho các khoản nợ này. Theo đó, các nhà đầu tư của các công ty có thể tham gia vào các hoạt động này. Theo đó, các nhà đầu tư sẽ phải trả tiền cho các khoản vay trước khi có thể trả tiền cho các khoản vay trước khi có thể trả tiền cho các khoản vay trước khi có thể trả tiền cho các khoản vay trước khi có thể trả tiền cho các khoản vay trước khi có thể trả tiền cho các khoản vay trước khi có thể trả tiền cho các khoản vay trước khi có thể trả tiền. Các nhà nghiên cứu cho biết: "Điều quan trọng là các nhà nghiên cứu đã tìm ra một số nguyên nhân gây ra sự thay đổi này. Khối dự trữ: Khối dự trữ dự trữ: Khối dự trữ dự trữ: Khối dự trữ dự trữ: Khối dự trữ dự trữ: Khối dự trữ dự trữ: Khối dự trữ dự trữ: Khối dự trữ dự trữ: Khối dự trữ dự trữ dự trữ dự trữ dự trữ SettlementID: SettlementID: SettlementID: SettlementID: SettlementID: SettlementID: SettlementID Các sản phẩm đặc biệt có lợi nhuận hoặc lỗ khi được lưu trữ, trong khi sản phẩm đặc biệt có lợi nhuận hoặc lỗ khi được lưu trữ. Theo đó, các nhà sản xuất sản phẩm đặc biệt sẽ phải trả tiền cho các sản phẩm đặc biệt. Các sàn giao dịch sản phẩm đặc biệt đảm bảo đồng xu bằng đồng xu, và các sàn giao dịch sản phẩm đặc biệt đảm bảo đồng xu bằng đồng xu. Sản phẩm đặc biệt: Sản phẩm đặc biệt: Sản phẩm đặc biệt: Sản phẩm đặc biệt: Sản phẩm đặc biệt: Sản phẩm đặc biệt: Sản phẩm đặc biệt: Sản phẩm đặc biệt: Các sản phẩm đặc biệt của nhôm chiếm tỷ lệ bảo lãnh của nhôm, trong khi các sản phẩm đặc biệt của nhôm chiếm tỷ lệ bảo lãnh của nhôm. Các sản phẩm đặc biệt được lưu trữ có lợi nhuận hoặc thua lỗ, trong khi các sản phẩm đặc biệt có lợi nhuận hoặc thua lỗ. Phương pháp này được sử dụng để xác định mức lợi nhuận của một sản phẩm đặc biệt theo thuật toán Lợi nhuận và Lợi nhuận của sản phẩm đặc biệt. Những người tham gia vào cuộc họp này đã được mời đến tham dự các cuộc họp của các tổ chức khác trên thế giới. Người dùng có thể sử dụng các công cụ như: Người dùng có thể lấy tiền từ các khoản tiền mà họ không cần. };

function AccountToTable ((jsStr, title) { // chức năng để xuất thông tin tài khoản vào biểu mẫu thanh trạng thái, tham số: chuỗi cấu trúc JSON để hiển thị, tiêu đề if (typeof(title) === undefined) { // Nếu không nhập tham số title, khởi tạo là: thông tin tài khoản title = Áp dụng thông tin tài khoản; var tbl = { // tuyên bố một đối tượng bảng, được sử dụng để nhập vào hàm LogStatus, hiển thị trong thanh trạng thái type: table, // type Định dạng là table title: title, // tham số title Đặt giá trị cho tbl cols: [Công thức, mục tiêu, mục tiêu, mục tiêu, mục tiêu, mục tiêu] // rows: [] // Một trường phân loại trong bảng lưu trữ dữ liệu mỗi dòng, bắt đầu bằng một mảng trống. }; try { // phát hiện ngoại lệ var fields = JSON.parse ((jsStr); // phân tích chuỗi jsStr for (var k in fields) { // qua thuộc tính của đối tượng fields, k là giá trị thuộc tính, không hiểu có thể xem hướng dẫn JS. If (k == AccountID k == BrokerID) { // Nếu thuộc tính đang truy cập là cả hai thuộc tính, hãy bỏ qua. tiếp tục var desc = trans[k]; // theo tên thuộc tính của từ điển trans var v = fields[k]; // lấy giá trị của tên thuộc tính hiện tại if (typeof(v) === number) { // Nếu giá trị thuộc tính là số, giữ số nhỏ 5 bit. v = _N ((v, 5); tbl.rows.push (([k, typeof(desc) === undefined? : desc, v]); // Đẩy một mảng một chiều hiện tại của các thuộc tính, mô tả thuộc tính, giá trị thuộc tính vào trong các thuộc tính rows của đối tượng bảng tbl. } catch (e) {} // Bắt bất thường nhưng không xử lý return tbl; // Trả về đối tượng tbl

var PositionManager = (function() { // tuyên bố một biến PositionManager chấp nhận giá trị trả về của một hàm ẩn danh, mà giá trị trả về là một đối tượng được cấu trúc function PositionManager ((e) { // tuyên bố một hàm PositionManager là bên trong một hàm ẩn danh. if (typeof(e) === undefined) { // Nếu không nhập tham số e, mặc định gán giá trị của biến toàn cầu đối tượng exchange cho e e = exchange; ♪ if (e.GetName()!== Futures_CTP) { // Kiểm tra đối tượng của sàn giao dịch chính e là sàn giao dịch tương lai hàng hóa hay không, nếu không thì ném ngoại lệ. Throw Only support CTP; // Chỉ hỗ trợ CTP ♪ this.e = e; // thêm một thuộc tính e vào hàm hiện tại (thực ra cũng là một đối tượng) và gán cho nó một tham số e this.account = null; // Thêm một account ♪ // Get Cache PositionManager.prototype.Account = function (() { // Thêm phương thức hàm Account vào PositionManager được tuyên bố ở trên if (!this.account) { // Nếu tài khoản của PositionManager là null this.account = _C ((this.e.GetAccount); // Gọi hàm GetAccount của đối tượng giao dịch this.e (tức là API đối tượng giao dịch) để lấy thông tin tài khoản. ♪ return this.account; // Cách này trả về thông tin tài khoản PositionManager.account này. }; PositionManager.prototype.GetAccount = function ((getTable) { // Thêm phương pháp Cách này lấy thông tin tài khoản mới nhất this.account = _C ((this.e.getAccount); if (typeof ((getTable)!== undefined string && getTable) { // Nếu muốn trả về chi tiết thông tin tài khoản gần đây nhất như một đối tượng, getTable phải là true return AccountToTable ((this.e.GetRawJSON))) // GetRawJSON chức năng Xem thêm tài liệu API ♪ return this.account; // trả về thông tin tài khoản sau khi cập nhật. };

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) { // Xuất ra hàm, tạo ra một đối tượng PositionManager return new PositionManager ((e); };

// Thông qua:http://mt.sohu.com/20160429/n446860150.shtml$.IsTrading = function ((symbol) { // Xác định liệu hợp đồng có trong giao dịch không Trong khoảng thời gian, tham số symbol Mã hợp đồng var now = new Date ((); // lấy đối tượng thời gian hiện tại var day = now.getDay ((); // lấy thời gian hiện tại vào ngày nào trong tuần. var hour = now.getHours ((); // lấy giờ trong 24 giờ var minute = now.getMinutes ((); // lấy phút trong một phút

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) { // được sử dụng để xây dựng các đối tượng hàng rào để thực hiện nhiều loại giao dịch. Parameter: hàm gọi lại khi nhiệm vụ hoàn thành. var self = {} // tuyên bố một đối tượng trống self.ERR_SUCCESS = 0 // Định nghĩa trả về thông tin thành công self.ERR_SET_SYMBOL = 1 // Lỗi thiết lập hợp đồng self.ERR_GET_RECORDS = 2 // Nhận lỗi dòng K self.ERR_GET_ORDERS = 3 // Nhận lỗi đặt hàng chưa hoàn thành self.ERR_GET_POS = 4 // Nhận thông tin lưu trữ sai self.ERR_TRADE = 5 // Lỗi giao dịch self.ERR_GET_DEPTH = 6 // Nhận lỗi đường sâu self.ERR_NOT_TRADING = 7 self.ERR_BUSY = 8 // chặn

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;


Thêm nữa