-
- Функции
Все перечисленные в данной статье торговые функции работают только в случае, если в окне, где выполняется скрипт ATF, разрешена торговля. С разрешением и запрещением торговли связаны следующие четыре функции:
Данная статья не рассматривает использование стакана котировок. Информацию о работе со стаканом смотрите в соответствующей статье.
Для большинства заявок в ATF используются структуры данных (или хэши), которые описаны ниже, однако для самых простых лимитированных заявок есть четыре короткие функции: trade_action::buy(), trade_action::sell(), trade_action::buyMultiple(), trade_action::sellMultiple(). Все они принимают в качестве аргументов три значения: цену заявки, объем, а так же величину, в которой объем измеряется.
Если не указать цену, то будет выставлена рыночная заявка (рыночные заявки запрещены для опционов). Использование кредита определяется из настройки параметров АРМ Трейдера.
Разницу в применении обычных функций и кратных их вариантов (с постфиксом multiple) смотрите в соответствующем разделе ниже.
Примеры:
// Купить 1 лот по рыночной цене trade_action::buy(1, ::lots); // Продать 2000 бумаг по рыночной цене trade_action::sell(2000, ::securities); // Купить бумаг по рыночной цене на 20000 рублей trade_action::buy(20000, ::money); // Купить один лот по цене 100 рублей за бумагу trade_action::buy(1, ::lots, 100);
Для более сложных видов заявок можно использовать функции transact() и transactMultiple(), в качестве аргументов которым передаются хеши с параметрами заявок. Общий алгоритм выставления заявок в этом случае выглядит так:
// Создание хеша order = new_object("hash"); // Заполнение парамеров хеша order["operation"] = OP_BUY; order["quantity"] = 1; order["usecredit"] = false; // Выставление заявку на биржу trade_action::transact(order);
Стоит обратить внимание на следующие моменты:
Полезно так же иметь ввиду, что один и тот же хеш можно использовать многократно. Следующий пример реализует функцию, которая выставляет сразу набор заявок в заданном диапазоне цен с заданным шагом:
function ladder(operation, quantity, from, to, step) { var order = new_object("hash"); order["operation"] = operation; order["quantity"] = quantity; while ((step > 0 and from < to) or (step < 0 and from > to)) { order["price"] = from; trade_action::transactMultiple(order); from += step; } }
Теперь, чтобы выставить заявки по одному лоту на продажу от 100 рублей до 120 с шагом в два рубля, мы можем вызвать нашу функцию следующим образом:
ladder(OP_SELL, 1, 100, 120, 2);
Свойства функции transact игнорировать все параметры хеша, не имеющие отношения к выставлению заявки так могут быть полезно использованы. Пусть мы хотим при каждой покупке бумаги автоматически выставлять заявку на продажу ее по цене на три процента большей стоимости совершения сделки. Это можно сделать следующим простым способом:
function fixProfit(var id) { var trade = getTrade(id); if (trade["operation"] == OP_BUY) { trade["operation"] = OP_SELL; trade["price"] = trade["price"] * 1.03; trade_action::transactMultiple(trade); } }
Обратите внимание, что данный код выставит заявку на продажу бумаги ровно на тот же объем, что была совершена сделка. Все остальные поля структуры сделки игнорируются и никак не мешают функции transact.
Доступные для заполнения поля:
Следующий код выведет диалог ввода заявки на покупку десяти лотов с использованием кредита по цене 123 рубля, действительную до отмены:
order = new_object("hash"); order["operation"] = OP_BUY; order["quantity"] = 10; order["price"] = 123; order["validbefore"] = TILL_CANCELED; order["usecredit"] = true; order["confirm_dialog"] = true; trade_action::transact(order);
Хеш условных заявок точно такой же как и хеш лимитированных заявок, но содержит два дополнительных поля, характеризующих условие заявки:
Поле condition может принимать одно из следующих значений:
Следующий пример выставит рыночную заявку на покупку 300 лотов через три часа после выполнения функиции transact:
order = new_object("hash"); order["operation"] = OP_BUY; order["quantity"] = 300; order["condition"] = COND_TIME; order["condvalue"] = getServerTime() + 3*60*60; trade_action::transact(order);
Следующие поля стоп-заявки дублируют соответствующие поля лимитированных и условных заявок:
Следующие поля можно заполнять для части Take Profit:
Следующие поля можно заполнять для части Stop Loss:
Для установки стоп-заявки, связанной с обычной заявкой, выставленной на биржу, необходимо выставить следующее поле:
В стоп-заявке могут присутствовать либо только часть Take Profit, либо только часть Stop Loss, либо обе части. Следующий пример выставляет заявку, имеющую обе части:
var stop = new_object("hash"); // Заявка на продажу stop["operation"] = OP_SELL; // Часть Take Profit stop["tp_activationprice"] = 110; stop["tp_quantity"] = 1; stop["tp_correction"] = "0.2%"; // Часть Stop Loss stop["sl_activationprice"] = 90; stop["sl_quantity"] = 1; // Выставление заявки на рынок trade_action::transact(stop);
Для снятия заявок в ATF доступен следующий набор функций:
В качестве аргументов функциям cancelOrder() и cancelStopOrder() может быть передан либо идентификатор заявки на сервере Transaq (trnid), либо хеш заявки и стоп-заявки соответственно. В функции cancelOrder() при снятии заявки используются поля orderno и trnid, причем приоритет, в случае если заданы оба поля (и, например, они не согласованы), отдается полю orderno.
Все торговые и сигнальные функции существуют в двух экземплярах: «обычные» и с суффиксом «Multiple» (кратные функции). Разница этих функций заключается в том, что «обычные функции» могут сработать лишь один раз в границах одной свечи, а кратные функции срабатывают каждый раз как вызываются.
Следующий пример подает сигнал как только объем текущей свечки превысит в два раза объем предыдущей свечки:
function calc() { if (volume > volume[-1] * 2) { signal::alert("Объем торгов резко возрос!!!!!!!"); } }
Он кажется простым и логичным, однако здесь как раз неявно используется механизм срабатывания функции лишь один раз на каждой свече. Рассмотрим практически идентичный пример:
function calc() { if (volume > volume[-1] * 2) { signal::alertMultiple("Объем торгов резко возрос!!!!!!!"); } }
Здесь функция signal::alert() заменена на функцию signal::alertMultiple(), следствием чего станет то, что как только объем будет превышен, сообщение с предупреждением будет выдаваться при каждой сделке до тех пор, пока верно условие на объемы.
Если бы в ATF не было функции signal::alert() с поведением срабатывания лишь один раз за свечку, то сигнал, срабатывающий лишь один раз, выглядел бы несколько сложнее:
static signalled = false; function onNewCandle() { signalled = false; } function calc() { if (not signalled and volume > volume[-1] * 2) { signalled = true; signal::alertMultiple("Объем торгов резко возрос!!!!!!!"); } }
Данный код довольно краток, однако если бы сигналов скрипт подавал множество, то и количество переменных для отслеживания этого а так же лишних сравнений было бы значительно больше.
В то время как такая логика сильно упрощает написание сигналов, с выставлением заявок такое поведение ATF редко помогает (лишь в случаях простейших стратегий), из-за того, что часто операции открытия и закрытия позиций завязаны на дополнительные условия связанные с состоянием портфеля, а то и вовсе торговые функции вынесены отдельно.
По этой причине для торговых функций хорошей рекомендацией будет продумывать механизм открытия и закрытия позиций отдельно и выносить его в отдельные функции, где использовать кратные версии функций. Следующий шаблон подойдет для значительной доли торговых стратегий:
static position = ""; function check() { if (not position and checkLong()) { enterLong(); position = "long"; return; } if (not position and checkShort()) { enterShort(); position = "short"; return; } if (position == "long" and checkExitLong()) { exitLong(); position = ""; return; } if (position == "short" and checkExitShort()) { exitShort(); position = ""; return; } }
Здесь функция check() должна вызываться с частотой, требуемой пользователем (например, внутри функции calc(), onNewCandle(), по таймеру или по событию изменения стакана). Функции checkLong(), checkShort(), checkExitLong() и checkExitShort() должны проверять условия на вхождение в лонг, шорт, а так же выход из лонга и шорта соответственно. Функции же enterLong(), enterShort(), exitLong() и exitShort() собственно выполняет открытие и закрытие длинной и короткой позиции. Глобальная переменная position содержит информацию о текущей позиции, длинная она или короткая.
Как пример, рассмотрим простую стратегию, которая будет торговать только в лонг при пересечении ценой скользящей средней снизу вверх, а закрываться когда скользящая средняя (другого периода) будет выше цены, причем не раньше чем через count свечек (здесь можно было бы использовать автоматическое отслеживание пересечений, но для простоты мы воспользуемся только простейшими функциями):
extern period1 = 13; extern period2 = 10; extern quantity = 1; extern count = 5; static position = ""; static n = 0; function checkLong() { return close > MovAvg(ind_ema, period1, pt_close); } function checkShort() { return false; } function checkExitLong() { return noCandle() > n + count and close < MovAvg(ind_ema, period2, pt_close); } function checkExitShort() { return false; } function enterLong() { trade_action::buy(1, ::lots); } function enterShort() { return; } function exitLong() { trade_action::sell(1, ::lots); } function exitShort() { return; } // ... // Здесь следует код функции check(), приведенный выше // ... function calc() { check(); }
Приведенный код конечно кажется избыточно большим (мы могли бы вообще не упоминать здесь шорты, а открывать и закрывать позицию не во внешних функциях, а непосредственно в функции check()), однако этот пример лишь призван продемонстрировать общую идею. В сложных скриптах приведенный шаблон оказался бы уже не таким избыточным, а довольно удобным и кратким обрамлением логики работы торговой системы.
Все заявки и сделки в Transaq имеют связанный с ними номер транзакции, обозначаемый в структурах как trnid. Этот номер trnid передается в функции onOrder, onStopOrder и onTrade, а так же используется для снятия заявок и получения их структур:
function onOrder(var trnid) { var = getOrder(trnid); // получить структуру по номеру транзакции }
Идентификатор заявки возвращается функциями trade_action::…. Если в качестве идентификатора возвращается нулевое значение, то это говорит о том, что сервер Transaq не принял заявку. В этом случае описание ошибки можно получить с помощью функции getLastErrorMessage().
Важно понимать, что ошибка при выставлении заявки может произойти либо при приеме её сервером Transaq, либо при попытке сервера выставить ее на биржу. На этом этапе могут быть либо какие-то проблемы со связью, либо заявка может по разным причинам не устроить сервер самой биржи. В этом случае заявке всё равно будет присвоен номер trnid, а при отклонении заявки произойдет событие onOrder(). В самой заявке при этом будет заполнено поле message, в котором будет содержаться сообщение об ошибке.
В случае снятия заявок так же могут возникать ошибки. В этом случае сообщение об ошибке будет возвращено непосредственно функцией, которую вы используете для снятия заявки - в случае отсутствия ошибки будет возвращена пустая строка, в противном случае это будет строка с описанием проблемы.
Следующий пример демонстрирует изложенное. В нем выставляется заявка при наступлении условия EnterExpr(), а если до исполнения сделки срабатывает условие CancelExpr() заявка снимается, при этом все возникающие ошибки выводятся в окно вывода ATF:
static order = 0; function onOrder(var trnid) { if (trnid != order) {return;} var order = getOrder(trnid); // Ошибка при выставлении заявки на биржу: if (order["message"]) { signal::output(order["message"]); } } function calc() { if ((not order) and EnterExpr()) { order = trade_action::buy(1, ::lots); // Сервер Transaq не принял заявку: if (not order) { signal::output(getLastErrorMessage()); } } if (order and CancelExpr()) { var error = cancelOrder(order); // Если не удалось снять заявку: if (error) { signal::output(error); } order = 0; } }
Обратите внимание, что начиная с ATF версии 1.16 функции onATFOrder, onClientOrder, onATFStopOrder, onClientStopOrder, onATFTrade и onClientTrade устарели и вместо них теперь используются функции onOrder, onStopOrder и onTrade. Логика, которая закладывалась в старые функции, не может быть целиком выдержана из-за специфики поведения серверов, поэтому от нее было решено отказаться. Старые функции будут поддерживать вплоть до ATF 1.20, до этого периода желательно перевести свои исходные коды на использование новых версий этих функций.
После выставления условной или обычной заявки, в первую очередь срабатывает событие onOrder(). Для стоп заявок это будет событие onStopOrder(). Далее данные функции будут вызываться при каждом изменении статуса заявки. Если заявка будет удовлетворена, то так же сработает событие onTrade().
Аргументом данных событий являются идентификаторы заявок на сервере Transaq (он отличается от биржевого номера заявки), по которым используя функции getOrder() и getStopOrder() вы можете получить хеш с заявками (подробное описание структуры заявки рассматривается ниже), в котором одним из наиболее существенных элементов является поле status, показывающее в каком состоянии находится заявка.
Константы для статусов обычных и условных заявок имеют префикс OS_, а статусы для стоп-заявок префикс SS_.
Для обычных заявок статус вначале становится OS_ACTIVE, а затем, когда заявка исполнена, OS_MATCHED. Если заявка рыночная, то статус может сразу оказаться OS_MATCHED.
В случае выставления условной заявки, статус вначале будет OS_WATCHING, и лишь затем сменится на OS_ACTIVE. Если для заявки задано время активации, то пока оно не наступило, ее статус будет OS_WAIT. Так же возможны другие статусы, сигнализирующие об ошибках, снятии заявки или прекращении времени ее действия. Полный список смотрите внизу в разделе «Константы».
При выставлении стоп-заявок последовательность статусов заявки аналогичная. Вначале она имеет статус SS_WATCHING. При исполнении заявки статус поменяется на SS_EXECUTED. В случае активации следящего Take Profit (с параметром correction), на момент ожидания коррекции статус будет SS_TP_CORRECTION. Если стоп-заявка активируется по выполнению биржевой заявки, и заявка еще не активирована, то ее статус будет SS_LINKWAIT. Так же имеется ряд промежуточных статусов для снятых заявок, ошибочных ситуаций и периодов защитного времени. Полный перечень смотрите в списке ниже.
Получить структуру заявки можно с помощью функции getOrder(), структуру стоп-заявки с помощью функиции getStopOrder(), а структуру сделки с помощью функции getTrade(). Эти функции принимают в качестве параметра номер транзакции, который передается в качестве аргумента при выполнении событий onOrder(), onStopOrder() и onTrade(). Все эти функции возвращают хеш с заполненными данными о заявке или сделке. Описание хешей содержатся ниже.
Номера транзакций можно получить и списком, не только через функции getOrder() и getStopOrder(). Для этого служат следующие функции:
Хеш, возвращаемый функцией getTrade() имеет следующие поля:
Хеш, возвращаемый функций getOrder() имеет следующие поля:
Хеш, возвращаемый функций getStopOrder() имеет следующие поля: