МТС – взгляд дилетанта. Часть 1 – Сделки и баланс.

В продолжении нашей первой части.

Мы имеем код, который подает сигналы по нашим данным. Стоит заметить что настройка кода под ТФ (time frame) не предусмотрена, мы просто добавляем ТСистему на график в виде индикатора, а ТФ выбираем самостоятельно.

Теперь нам необходимо разобрать раздел ФУНКЦИИ на сайте документации по торговой системе Transaq.

Так как мы пишем ТСистему для FORTS (для ММВБ проще, и сейчас мы разберем почему), сразу обращаем внимание на шрифт выделенный красным: Не работает на FORTS!

Вот тебе Бабка и Юриев день! Там есть функции, без которых нам в общем-то не обойтись. Так что буде думать, как нам решить проблему, а точнее новую задачу. Главное не отчаиваться.

СДЕЛКИ

А пока рассмотрим как делать сделки.

Все, что касается заявок, редактируется (управляется) функцией trade_action::
Нас интересует только две из общего числа:

  1. trade_action::buy(amount, measure, price)
  2. trade_action::sell(amount, measure, price)

О совершении сделок через ATF читаем § 10 руководства. Читаем:

Каждая из этих функций может принимать от двух до трех параметров. Первый параметр указывает объем сделки (положительное число), второй параметр указывает в каких величинах измеряется объем сделки. Возможны три значения: ::money, ::lots и ::securities, которые обозначают деньги, лоты и отдельные бумаги соответственно. Третий параметр указывает цену. Если его не задать, то сделка будет совершена по рыночной цене (это на данный момент не работает для опционов FORTS).

Что нам необходимо понять.

  1. на FORTS не работают ::money
  2. на FORTS не работает рыночная сделка (но нам как-то пофиг)

ПОЕХАЛИ

Для начала нам нужно несколько переменных. Что это такое (кто не слышал)? Переменная на протяжении расчета программы принимает разные значение — она меняется. в ATF есть помимо простых переменных — которые меняются каждый раз при расчета функции calc (основной функции расчета стратегии), так и статичные, которые не меняются, пока этого явно не указать.

И так в нашем коде следующие дополнения:

8
9
10
11
12
13
14
15
16
17
static name = "T01 "; 	// имя нашей ТСистемы (потом пригодится)
static quant = 1;	// сколько торгуем (в контрактах)
static slip = 100;	// просказывание в пунктах (наш ответ рыночной заявки)
 
var quan;		// кол-во с заявке
var balance;		// баланс по портфелю
var order_id;		// id заявки
var transaction;	// цена сделки
var timer;		// время сервера
var file; 		// файлы

Что добавили:

  1. имя ТСистемы;
  2. кол-во которым торгуем (иначе система в данном виде нахреначит на все плечи без остановок);
  3. т.к. рыночной заявки на FORTS нет, ставим переменную отвечающую за проскальзывание;
  4. quan — количество контрактов в каждой новой заявке. Расчет будет связан с балансом
  5. balance и transaction — переменные отвечающие за соответственно баланс портфеля, и его стоимости (по чем взяли то?)
  6. file — необходимая переменная для работы с файлами.
  7. timer — тут все ясно НО он работает он только на ATF ver. 1.7 и выше. Так что обновитесь (на форуме были файлы). Это конечно не обязательно но до жути удобно.

Пока не забыл, так как мы будем работать с файлами (объясню позже) создадим в корне программы transaq директорию. У меня она !DATA — ее так легче искать, как и скрипты. Создаем в директорию с именем нашей ТСистемы (у меня Т01), а в ней два текстовых файла:

  1. balance.txt
  2. profit.txt

В первую строку каждого поставьте 0 (ноль).

Теперь добавим код помогающий нам посылать автоматические заявки в систему, с правильным количеством (что наверное самое главное). Но начать нужно пожалуй еще с разбора парочки функций:

  1. onATFOrder — вызывается когда ATF посылает заявку
  2. onATFTrade — вызывается когда ATF делает сделку

Сейчас будет понятно.

iЗАЯВКА

24
25
26
27
28
29
30
31
32
33
// РОБОТ ВЫСТАВИЛ ЗАЯВКУ
 function onATFOrder(var id) {
 order_id = id;
 timer = getFormattedTime(getServerTime());
 var operation;
 var order = getOrder(order_id);
 if (order["operation"] == -1) 	{operation = "заявка SELL ";}
 if (order["operation"] == 1) 	{operation = "заявка BUY ";}
 signal::outputMultiple (name + operation + order["quantity"] + "шт. по " + order["price"] + " #: " + order_id + " | " + timer);
 }

Сообщение о новой заявке, времени и ее цене (с учетом проскальзывания) будет выводится при каждой новой заявке ТСистемы. Так же мы в начале применили код var id — таким образом мы создали новую переменную ID и сразу присвоили ей значение идентификатора заявки в торговой системе (пригодится).

signal::outputMultiple отличается от signal::output тем, что выводит все выданные на данный момент сигналы, а не затирает все предыдущие последним. Например если мы поставим простой вызов, то увидим одно сообщение, а если outputMultiple то увидим все заявки которая система скинула на биржу. Это позволит нам выявить возможные баги, да и вообще это более информативно.

iСДЕЛКА

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// РОБОТ СДЕЛАЛ СДЕЛКУ
function onATFTrade(var id) {
	timer = getFormattedTime(getServerTime());
	file = new_object("file");
	// чтение баланса из файла
	file.ropen("./!DATA/T01/balance.txt"); // открыли на чтение
	balance = file.readLn();
	file.close(); // закрыли
 
	var operation;
	var newBalance;	// новый баланс после сделки
	var trade = getTrade(id);
	if (trade["operation"] == -1){
		operation = "SELL ";
		newBalance = (balance)-trade["quantity"];}
	if (trade["operation"] == 1) {
		operation = "BUY ";
		newBalance = (balance)+trade["quantity"];}
	signal::outputMultiple (name + operation + trade["quantity"] + "шт. по " + trade["price"] + " | " + timer);
 
if (newBalance!=0) { // если мы в позиции
	// запись баланса после сделки
	file.wopen("./!DATA/T01/balance.txt"); // открыли на запись
	file.writeLn(newBalance);
	file.writeLn(trade["price"]);
	file.writeLn("время: " + timer);
	file.close(); // закрыли
	}
if (newBalance==0) { // если закрыли позицию
	// стираем файл баланса
	file = new_object("file");
	file.wopen("./!DATA/T01/balance.txt"); // открыли на запись
	file.writeLn(0);
	file.writeLn();
	file.writeLn();
	file.close(); // закрыли
	// стираем файл профита
	file = new_object("file");
	file.wopen("./!DATA/T01/profit.txt"); // открыли на запись
	file.writeLn(0);
	file.close(); // закрыли
	}
}

И так: file = new_object(«file»); обязательный параметр для работы с файлами. Создает объект.

balance = file.readLn(); — присваиваем переменной balance значение — первая строка из файла, в нашем случае это 0 (при создании файла мы писали его).

новые переменные (как стало понятно, переменные мы можем создавать и внутри функций а не только в начале нашей системы): var operation; — что за операцию совершили, var newBalance; — новый баланс после сделки рассчитываем, var trade = getTrade(id); — так же как и в заявке узнаем id сделки.

trade[«operation»] никак не связан с переменной — это системный запрос операции в трейде, так же как и trade[«quantity»] — количество, а trade[«price»] — цена сделки.

Далее нам надо понять наш новый баланс. Если мы в позиции мы записываем наш новый баланс (они простые арифметические) в файл, если мы вышли из позиции в 0, то мы затираем нулями наши файлы баланса и профита.

РАСЧЕТ СИГНАЛА и подача заявок

и так меняем нашу функцию calc()

79
80
81
82
83
84
85
86
87
// ОСНОВНОЙ РАСЧЕТ СИГНАЛОВ
 function calc() {
 var result;
 timer = getFormattedTime(getServerTime());
 file = new_object("file");
 file.ropen("./!DATA/T01/balance.txt"); // чтение баланса из файла
 balance = file.readLn();
 transaction = file.readLn();
 file.close();

Что имеем:

  1. result — тут будем считать наш результат по сделке
  2. timer — снова узнали время, поточнее. Это же переменная! Вот мы ее и обновили.
  3. создали объект file и прочитали наш баланс.
  4. transaction — это цена сделки, нам она нужна для подсчета «легких денег».
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// РЕЗУЛЬТАТ и СИГНАЛЫ
if (balance!=0) {	// если на балансе что-то есть
// читаем ПРОШЛЫЙ РЕЗУЛЬТАТ
if (isHistoryCalculated()) {	// ждем пока загрузится вся история
file.ropen("./!DATA/T01/profit.txt");
result = file.readLn();
file.close();
if (balance <0) {result=transaction-close;}
if (balance >0) {result=close-transaction;}
		// записываем РЕЗУЛЬТАТ
		file = new_object("file");
		file.wopen("./!DATA/T01/profit.txt"); // открыли на запись
		file.writeLn(result);
		file.writeLn("время: " + timer);
		file.close();
	}
}

вообще писать и считать (или наоборот) результат мы будем только если мы в позиции:
if (balance!=0)
close это последняя сделка (не цена) на рынке по нашему инструменту.

106
107
108
line[0] = MovAvg(ind_ema, fast, pt_close);
line[1] = MovAvg(ind_ema, slow, pt_close);
quan = abs(balance)+quant; // кол-во в заявке связано с балансом

ну вот наше количество в выставляемой заявке. abc() это абсолютное значение, на случай отрицательных результатов. Например мы были в шорте и торгуем одним контрактом: -1 + 1 = 0 а нам надо бы перевернуться тогда abc(-1) + 1 = 2 Таким образом мы закроем шорт, и откроем лонг.

signal::alert мне никогда не нравились, меняем все на signal::output

110
111
112
113
114
115
116
117
118
119
if (trend == 1 and line[0][-1] < line[1][-1]) {
	signal::output (name + "сигнал на продажу");
	// ПРОДАЕМ
		if (balance >=0) {
		// снимаем предыдущую заявку
		if (order_id!=0) {trade_action::cancelOrder(order_id); order_id = 0;}
		// заявка на продажу
		trade_action::sell(quan, ::lots,close-slip);
		}
	}
121
122
123
124
125
126
127
128
129
130
if (trend == -1 and line[0][-1] > line[1][-1]) {
	signal::output (name + "сигнал на покупку");
	// ПОКУПАЕМ
		if (balance <=0) {
		// снимаем предыдущую заявку
		if (order_id!=0) {trade_action::cancelOrder(order_id); order_id = 0;}
		// заявка на покупку
		trade_action::buy(quan, ::lots,close+slip);
		}
	}

Что имеем:
Хочу обратить внимание, что при наличии order_id (id заявки) мы перед выставлением новой, снимем ту старую. Для чего? Например у нас прошел не весь объем, и что-то осталось. И мало ли что, может стоп какой забыли? (об этом потом).

мы немного отредактировали сигнал:
line[0][-1] и line[1][-1] это значения линий на ПРЕДЫДУЩЕЕ закрытие свечей не позволит нам торговать на «зарождении» сигнала, а делать сделки уже «наверняка» при его наличии.

result — в начале мы его запрашиваем, и только потом пересчитываем.
Запрашиваем мы его только при наличии загрузки всей истории. Это необходимо при разрыве связи например. Таким образом мы защищаем наш расчет от искаженных данных. Нам ведь нужна именно последняя сделка на рынке, а не недельной давности.
Функция isHistoryCalculated() помогает нам в этом. Все что будет в ней рассчитываться, будет иметь свежие данные.

ИТОГИ

Сегодня мы научились хранить баланс и результат последней сделки.
Все это мы делали потому, что ATF Transaq не может нам сообщить баланс. А функции getBought() и getSold() работают только в одну торговую сессию, и не позволяют их использовать при работе overnight.

В следующий раз мы подумаем что нам мне хватает. А не хватает нам тут стопов! Именно для этого мы немного забежали вперед и начали писать результат нашей последней сделки. Тут можно и не только stopLoss ставить, но и takeProfit.

PS

кажется ошибок нет.
Вопросы в каменты, и если что, отредактирую этот пост, о чем конечно сообщу.

Добавить комментарий