Играть с братом в JavaScript - создать партнёра, который будет делать покупки и продажи (7) полезный инструмент, чтобы знать, почему он полезен!

Автор:Маленькие мечты, Создано: 2017-03-16 12:29:51, Обновлено: 2017-10-11 10:37:54

Полезные инструменты должны знать, зачем они нужны!

Например, если вы хотите написать стратегию в рабочее время, то вам приятно, когда в редакторе появляются разные идеи, которые могут стать вашим следующим кубком, и это волнует! Единственное, что влияет на эту страсть, - это обработка логики покупки и продажи для системы торговых стратегий, которая действительно важна, но немного скучна и имеет более сложную логику.

  • К счастью, есть хорошо написанный модуль, который можно использовать непосредственно (в предыдущих статьях он был использован), но, помимо использования и понимания того, как он работает, я комментирую код:

/* Interval Неудачный повторный тест интервал (миллисекунды) Цифровой тип (число) 500 SlideTick Количество скользящих точек ((полные числа)) Цифровой тип ((номер) 1) RiskControl включить ветроуправление Бул ((true/false) false MaxTrade@RiskControl Наибольшее количество торговых операций в рабочий день Цифровой тип (number) 50 MaxTradeAmount@RiskControl Максимальное количество заказа на одну купюру Цифровой тип (number) 1000 */

var __orderCount = 0 // Записывает следующее число единиц текущего рабочего дня var __orderDay = 0 // Записывает дату текущего рабочего дня

function CanTrade ((tradeAmount) { // Модуль управления рисками, параметры: количество сделок if (!RiskControl) { // По умолчанию не включать модуль ветроконтроля, если он не включен, функция CanTrade возвращает true return true - вернуть истинное {y:bi} if (typeof(tradeAmount) == number && tradeAmount > MaxTradeAmount) { // Введите параметр tradeAmount с числовым типом, и количество подписных записей больше, чем количество подписных записей, установленное параметром шаблона Log ((ограничение модуля управления ураганом, превышение максимального объема загрузки, MaxTradeAmount, #ff0000 @); // вывод подсказки, прерывание выполнения. бросить крем прерывает выполнение крем return false; {y:bi} var nowDay = new Date (().getDate ((); // получает текущую дату if (nowDay!= __orderDay) { // getDate() возвращает день месяца (1 - 31) из объекта Date. __orderDay = nowDay; // __orderDay Глобальная переменная записывает дату запуска, когда в первый раз входил в модуль ветроуправления. __orderCount = 0; // Обновление переменной __orderDay, перестановка переменной __orderCount {y:bi} __orderCount++; // Глобальная переменная __orderCount Следующее однократное число, самодобавление. if (__orderCount > MaxTrade) { // определяет, превышает ли параметр максимальное количество операций в день Log ((ограничение модуля управления ураганом, не подлежит торговле, превышение максимального нижеследующего однократного числа кнопок, MaxTrade, #ff0000 @); // превышение, выпуск подсказки, прерывание выполнения. бросить крем прерывает выполнение крем return false; {y:bi} return true; // ни одно из вышеперечисленных условий не задействовано, возвращает true, то есть можно торговать. {y:bi}

function init ((() { // Функция инициализации шаблона, которая выполняется сначала при загрузке шаблона. if (typeof ((SlideTick) === undefined) { // проверяет, не определено ли SlideTick. SlideTick = 1; // настройки По умолчанию 1 } else { // парализует строку. Преобразуется в числовое значение, однако может вызвать ошибку, если строка, начинающаяся с нецифровых символов, возвращает NaN SlideTick = parseInt ((СлайдТик); {y:bi} Log (загрузка торговой базы с успешным загрузкой); {y:bi}

function GetPosition ((e, contractType, direction, positions) { // объединяет контракт с одинаковыми позициями в прошлом и настоящем, параметры следующие: объект биржи, тип контракта, направление, API возвращенные позиции (англ.) русск.
var allCost = 0; // contractType Контракт в направлении Общий расход средств, не умноженный на один контракт ((поскольку в целом можно договориться) var allAmount = 0; // Общее количество контрактов var allProfit = 0; // сумма прибыли и убытка var allFrozen = 0; // Общее количество заморозков var pos Margin = 0; // Хеджирование контрактов if (typeof(positions) === undefined!positions) { // Если параметры не передаются API, возвращается информация о хранении positions = _C ((e.GetPosition); // при этом вызывается API для получения информации о хранении. {y:bi} for (var i = 0; i < positions.length; i++) { // проходит через данный массив хранения информации. if (positions[i].ContractType == contractType && // Контрактный код информации о хранении текущего индекса == Контрактный код, указанный параметром ((contractType) и направление, равное направлению, в котором был передан параметр ((direction) на текущей или прошлой позиции (((positions[i].Type == PD_LONG для позиций[i].Type == PD_LONG_YD) && direction == PD_LONG для позиций[i].Type == PD_SHORT для позиций[i].Type == PD_SHORT_YD) && direction == PD_SHORT)) ) { // Условное выполнение if блока posMargin = positions[i].MarginLevel; // Получить уровень левериджера Назначить значение posMargin allCost += (positions[i].Price * positions[i].Amount); // Общие затраты ((Количество сделанных контрактов, текущая цена индекса * объем хранения) allAmount += positions[i].Amount; // суммарное количество контрактов allProfit += positions[i].Profit; // Контрактный плавающий прирост и убыток allFrozen += positions[i].FrozenAmount; // суммарное количество замороженных контрактов {y:bi} {y:bi} if (allAmount === 0) { // Если после завершения просмотра суммарное количество квалифицированных контрактов составляет 0, возвращается null, то есть безограниченное количество контрактов. return null; {y:bi} return { // allAmount не 0, возвращает информацию о хранении объекта после объединения. MarginLevel: постмаржинальный, FrozenAmount: все замороженные, Цена: _N ((allCost / allAmount), Amount: allАмоунт, Профит: allProfit, Тип: направление ContractType: ContractType (тип контракта) }; {y:bi}

function Open ((e, contractType, direction, opAmount) { // Операция с одним вариантом контракта Функция открытия, параметры: объект биржи, код контракта, направление, количество операций var initPosition = GetPosition ((e, contractType, direction); // вызывает функцию GetPosition в верхней части, чтобы получить информацию о хранении после объединения. var isFirst = true; // настройка маркировки isFirst (означает, что нижеwhile является первым циклом true) var initAmount = initPosition? initPosition.Amount : 0; // Если initPosition null, то initAmount присваивается 0, иначе присваивается initPosition.Amount var positionNow = initPosition; // Заявляет, что переменная positionNow указывает на текущую информацию while (true) { // while цикл var needOpen = opAmount; // объявляет временную переменную needOpen и присваивает ей значение с помощью параметров if (isFirst) { // Если это первый раз выполняется, то только обновление isFirst помечено как false, так как обновление False. isFirst = false; } else { positionNow = GetPosition ((e, contractType, direction); // обновляет positionNow, текущую информацию о хранении. if (positionNow) { // Если есть информация о состоянии, то количество необходимых позиций, которые необходимо открыть в следующий раз needOpen, равно количеству операций, требуемых параметром минус разница между полученной информацией о состоянии и предыдущей (т.е. количество новых открытых рук) needOpen = opAmount - (positionNow.Amount - initAmount); {y:bi} {y:bi} var insDetail = _C ((e.SetContractType, contractType); // Установка типа контракта. // Log (приложения для первоначального хранения, initAmount, currentHold, positionNow, needOpen); if (needOpen < insDetail.MinLimitOrderVolume) { // Если следующее количество открытых позиций меньше минимального количества открытых позиций в предельной цене этого контракта break; // выйти из цикла {y:bi} if (!CanTrade(opAmount)) { // модуль ветрового контроля. "Я не хочу, чтобы ты был здесь". {y:bi} var depth = _C ((e.GetDepth); // получает информацию о глубине рынка. var amount = Math.min ((insDetail.MaxLimitOrderVolume, needOpen); // Ограничьте количество заказов не может быть больше максимального количества заказов в рамках контракта e.SetDirection ((direction == PD_LONG? buy: sell); // настроить однополые направления в соответствии с параметром direction. var orderId; if (direction == PD_LONG) { // Вызвать различные API для совершения транзакций (открытые или открытые) в зависимости от направления параметра direction orderId = e.Buy ((depth.Asks[0].Price + (insDetail.PriceTick * SlideTick), Math.min ((amount, depth.Asks[0].Amount), contractType, Ask, depth.Asks[0]); // Смотрите API-документацию, CTP товарный фьючерсный ценовой скобок для insDetail.PriceTick, который должен быть целым кратным этому значению // Фактический объем запроса API не больше объема диапазона } else { orderId = e.Sell ((depth.Bids[0].Price - (insDetail.PriceTick * SlideTick), Math.min ((amount, depth.Bids[0].Amount), contractType, Bid, depth.Bids[0]); {y:bi} // Отменить ожидаемые заказы while (true) { // После размещения заказа, через интервал времени, отменяет незавершенные заказы. Спящий (Interval); var orders = _C ((e.GetOrders); // получает все незавершенные заказы if (orders.length === 0) { // Если orders является пустым набором, перейти к текущему while "Я не хочу, чтобы ты был здесь". {y:bi} for (var j = 0; j < orders.length; j++) { // Проходит по массиву незавершенных заказов e.CancelOrder ((orders[j].Id); // Отменить заказ по идентификатору в информации о заказе в текущем индексе. if (j < (orders.length - 1)) { // проходит через интервал определенного времени, с одной стороны частота слишком высока. Sleep ((Interval); // Sleep приостанавливает Interval миллисекунды {y:bi} {y:bi} {y:bi} } // Если главный цикл while выходит var ret = { // объявляет объект для возвращения цена: 0, // средняя цена сделки amount: 0, // количество сделок position: positionNow // Последнее полученное хранилище данной породы }; if (!positionNow) { // Если нет хранения информации, возвращается непосредственно в начале ре return ret; {y:bi} if (!initPosition) { // Если при запуске выполнения текущей функции нет никакой информации о хранении этого сорта. ret.price = positionNow.Price; // текущая информация о состоянии позиции. ret.amount = positionNow.Amount; // также } else { // если в начале уже имелась информация о хранении этого сорта. ret.amount = positionNow.Amount - initPosition.Amount; // значение разницы - количество новых позиций ret.price = _N(((positionNow.Price * positionNow.Amount) - (initPosition.Price * initPosition.Amount)) / ret.amount); // расходы на эту сделку, которые были добавлены, выделены из расходов на новые открытые позиции. {y:bi} return ret; // возвращает ret {y:bi}

function Cover ((e, contractType) { // Однообразная функция, параметры: объект биржи, код контракта var insDetail = _C ((e.SetContractType, contractType); // Установка типа контракта while (true) { // главный цикл while var n = 0; // подсчет операций на плоскости var opAmount = 0; // Заявление Операция Переменная var positions = _C ((e.GetPosition); // вызовы API получать хранилищную информацию, различающие вышеупомянутую функцию получать хранилищную информацию;; подробности см. в документации API for (var i = 0; i < positions.length; i++) { // просмотр Хранить информацию if (positions[i].ContractType!= contractType) { // Если информация, содержащаяся в текущем индексе, контракт не равен контракту, который должен быть выполнен, то это: contractType Продолжайте; // перейти {y:bi} var amount = Math.min ((insDetail.MaxLimitOrderVolume, positions[i].Amount); // контролирует максимальный объем сделок не выше, чем в расчете разной глубины; if (positions[i].Type == PD_LONG の の positions[i].Type == PD_LONG_YD) { // обрабатывает несколько позиций depth = _C ((e.GetDepth); // вызов API для получения текущих данных инвентаря opAmount = Math.min ((amount, depth.Bids[0].Amount); // ограничение Количество операций не больше одного диапазона if (!CanTrade ((opAmount)) { // обнаружение модуля ветрового управления return; {y:bi} e.SetDirection ((positions[i].Type == PD_LONG? closebuy_today кнопка : closebuy кнопка); // настройки

            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 = { // используется для отображения в строке состояния подробной информации об аккаунте Посмотрите, что происходит с аккаунтами, которые вы используете в Интернете, и вы увидите, как они работают. В частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности: Например, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае и в Китае. Брокер ID: Брокерский код компании Брокер ИД. В этом случае, если вы хотите, чтобы ваши деньги были потрачены в банке, вы можете использовать их для получения денег. В этом году, в частности, в Китае наблюдается рост доли недвижимости в среднем на 1,4% в год. Поскольку в этом случае, по мнению экспертов, не будет никаких проблем с оплатой. В этом случае, если вы не хотите, чтобы ваши деньги были потрачены в банке, вы можете использовать их в качестве кредитной карты. В этом случае, если вы не хотите, чтобы ваши деньги были вложены в кредитную систему, вы можете использовать их для получения кредита. Например, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае и в Китае. В результате, в результате, в результате, в результате, в результате, в результате, в результате, в результате, в результате, в результате. Депозитный кошелек: вкладывается в кошелек золотая сумма. Например, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае и в Китае. Например, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае, в Китае и в Китае. "Смертный денежный фонд" - это деньги, которые были заморожены. "Ответственность за то, что мы не можем выполнить свои обязательства, лежит на нас", - говорит он. "Фрозовый маржин" - золото, гарантируемое заморозками. ФондMortgageAvailable: ФондMortgageAvailable: ФондMortgageAvailable: ФондMortgageAvailable: ФондMortgageAvailable: В частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности. В частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности. В частности, в частности, в частности, в случае, если у вас нет денег, то вы не можете получить деньги. В этом случае, если вы хотите, чтобы ваши деньги были вкладываться в кредитную систему, вы можете использовать эту систему. "Я не могу выиграть, потому что у меня нет денег, и у меня нет денег. В частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности, в частности. Позиция прибыли прибыли прибыли прибыли прибыли В прошлый раз, когда она расплачивалась с резервными активами, она не могла получить больше денег. В прошлый раз, когда я оформил кредит, я не мог получить его. Предепозитный депозит: Предепозитный депозит - это предел последнего депозита. В прошлый раз, когда деньги были заложены в сумму, они были вкладываются в кредитную карту, а в прошлый раз - в кредитную карту. После того, как она выпустила свой последний кредитный пакет, она не смогла выполнить свой долг. В этом случае, если вы хотите, чтобы ваши деньги были потрачены на то, что вы делаете, вы должны быть готовы к тому, что вы сделаете. В этом случае, если у вас нет денег, то вы можете не платить долг, если у вас нет денег. Напомним, что в 2012 году в Китае было зарегистрировано более 30 000 случаев случаев заражения. Reserve Balance: резервный баланс, который обеспечивает баланс резервов на будущие платежи. Счетчик SettlementID: Счетчик Settlement ID: Счетчик с номерами, которые указывают на местонахождение, местонахождение, местонахождение, местонахождение. По данным издания, "Специальный продукт" - это продукт, который продается в магазинах и продается в магазинах, а не в магазинах. В этом случае, если вы не хотите, чтобы ваш продукт был доступен для продажи, вы можете использовать его в качестве продукта. Ограничения на продажу специальных продуктов: Ограничения на продажу специальных продуктов гарантируют золотой марж, а также гарантируют доход от продажи специальных продуктов. Специальный продукт замороженный комиссионный: специальный продукт замороженный комиссионный, специальный продукт замороженный комиссионный. Оранжевый SpecProductFrozenMargin: Оранжевый спецпродукт, гарантирующий заморозку золотого маргина. Оригинальное название: "Spec Product Margin" (Специальный маржинальный маржинальный маржинальный маржинальный маржинальный показатель) Показатель "SpecProductPositionProfit" - прибыль или убыток от хранения специальных продуктов. SpecProductPositionProfitByAlg: Специальные продукты, находящиеся в наличии, находящиеся в наличии, находящиеся в наличии. Оригинальное название: "Trading Day" (Традиционный день) В этом случае, если вы хотите, чтобы ваш долг был оплачен, вы должны вывести деньги из банка. Вы можете снять деньги с банковских счетов, но вы не можете снять деньги с банковских счетов. };

function AccountToTable ((jsStr, title) { // Функция для экспорта информации об аккаунте в форму с строкой состояния, параметры: строка JSON, которую нужно отобразить, заголовок if (typeof(title) === undefined) { // Если параметр title не введен, инициализация: title = вкладка "Информация об аккаунте"; {y:bi} var tbl = { // Декларирует объект таблицы, используемый для передачи функции LogStatus, которая отображается в строке состояния type: table, // тип Указанный как table title: title, // параметр title Присвоение значения к поле title tbl cols: [Коллегия полевой колонки, колонки описания колонки, колонки порога], // таблицы колонки заголовки rows: [] // Array field, в котором в таблице хранится данные на каждую строку, и начинается как empty array. }; try { // обнаруживает исключения var fields = JSON.parse ((jsStr); // расшифровка строк jsStr for (var k in fields) { // просматривает свойства объекта fields, k - значение свойства, не понятно. if (k == AccountID k == BrokerID ) { // Если текущее прохождение является одним из этих двух свойств, пропустить. Продолжайте {y:bi} var desc = trans[k]; // По названию свойства в словаре trans. var v = fields[k]; // получает значение текущего имени свойства if (typeof(v) === number) { // Если значение свойства цифрового типа, сохраняется 5-разрядный дробиль. v = _N ((v, 5); {y:bi} tbl.rows.push (([k, typeof(desc) === undefined? : desc, v]); // Вкладывает одномерную массивную комбинацию текущих свойств, описаний свойств, значений свойств в массивную комбинацию свойств rows объекта таблицы tbl. {y:bi} } catch (e) {} // Поймать аномалии, но не обрабатывать return tbl; // возвращает объект tbl {y:bi}

var PositionManager = (function() { // Заявляет, что переменная PositionManager принимает возвращенное значение анонимной функции, которое возвращается как объект конструкции function PositionManager ((e) { // Заявляет, что функция PositionManager находится внутри анонимной функции. if (typeof(e) === undefined) { // Если параметры e не введены, по умолчанию глобальная переменная exchange назначается e e = обмен; {y:bi} if (e.GetName()!== Futures_CTP) { // Проверяет, является ли объект основной биржи e товарной фьючерсной биржей, если нет, выбросить исключения. throw Only support CTP ; // поддерживает только CTP {y:bi} this.e = e; // добавить свойство e к текущей функции (в действительности это также объект) и придать ей значение параметра e this.account = null; // Добавить account Переменная начинается как null {y:bi} // Получить кэш PositionManager.prototype.Account = function() { // Добавить метод к функции Account, заявленной выше в PositionManager if (!this.account) { // Если свойство account в PositionManager имеет значение null this.account = _C ((this.e.GetAccount); // Вызов функции GetAccount (то есть API обмена) объекта this.e для получения информации об учетной записи. {y:bi} return this.account; // Этот метод возвращает информацию об учетной записи PositionManager.account. }; PositionManager.prototype.GetAccount = function ((getTable) { // Добавить метод Этот метод получает самую свежую информацию об аккаунте this.account = _C ((this.e.GetAccount); if (typeof(getTable)!== undefined && getTable) { // Если вы хотите вернуть подробную информацию о последней полученной учетной записи в виде объекта, getTable должен быть истинным return AccountToTable ((this.e.GetRawJSON))) // Функция GetRawJSON Смотрите API-документацию {y:bi} return this.account; // возвращает информацию об аккаунте после обновления. };

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) { // Экспортирует функции, создает объект PositionManager return new PositionManager ((e); };

// через:http://mt.sohu.com/20160429/n446860150.shtml$.IsTrading = function ((symbol) { // Судит о том, находятся ли контракты в торговле В течение промежутка времени параметры symbol Контрактный код var now = new Date ((); // получает объект текущего времени var day = now.getDay ((); // получает текущее время на какой конкретный день недели. var hour = now.getHours ((); // получает час в течение 24 часов var minute = now.getMinutes ((); // получает минуты в течение минуты

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) { // Функция построения объекта очереди для выполнения многообразных операций. Параметр: функция, которая возвращается при завершении задачи. var self = {} // объявляет пустой объект self.ERR_SUCCESS = 0 // Определение возвращает сообщение Успех self.ERR_SET_SYMBOL = 1 // Ошибка настройки контракта self.ERR_GET_RECORDS = 2 // Ошибка получения строки K self.ERR_GET_ORDERS = 3 // Ошибка получения незавершенного заказа self.ERR_GET_POS = 4 // Ошибка получения информации о хранении self.ERR_TRADE = 5 // Ошибка сделок self.ERR_GET_DEPTH = 6 // Ошибка получения глубины диапазона self.ERR_NOT_TRADING = 7 // не в торговое время self.ERR_BUSY = 8 // блокировка

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;


Больше