Здесь буду выкладывать функции, которые могут пригодиться:
math_round() -- Округляет число до указанной точности
1
2
3
4
5
| -- Округляет число до указанной точности
math_round = function(num, idp)
local mult = 10^(idp or 0)
return math.floor(num * mult + 0.5) / mult
end |
GetClassBySec() -- Возвращает код класса по коду бумаги
1
2
3
4
5
6
7
8
| -- Возвращает код класса по коду бумаги
GetClassBySec = function(sec_code)
for class_code in string.gmatch ('QJSIM,TQBR,SPBFUT,SPBOPT,CETS,', '(%P*),') do
for sec in string.gmatch (getClassSecurities(class_code), '(%P*_*%P*_*%P*),') do
if sec == sec_code then return class_code end
end
end
end |
WaitUpdateDataAfterReconnect() -- Ждет подключения к серверу, после чего ждет еще UpdateDataSecQty секунд подгрузки пропущенных данных с сервера
Для работы функции в скрипте должны быть объявлены глобальные переменные:
1
2
| RUN = true -- Флаг поддерживающий работу скрипта
UpdateDataSecQty = 10 -- Количество секунд ожидания подгрузки данных с сервера после возобновления подключения |
1
2
3
4
5
6
7
| -- Ждет подключения к серверу, после чего ждет еще UpdateDataSecQty секунд подгрузки пропущенных данных с сервера
WaitUpdateDataAfterReconnect = function()
while RUN and isConnected() == 0 do sleep(100) end
if RUN then sleep(UpdateDataSecQty * 1000) end
-- Повторяет операцию если соединение снова оказалось разорвано
if RUN and isConnected() == 0 then WaitUpdateDataAfterReconnect() end
end |
GetServerDateTime() -- Возвращает текущую дату/время сервера в виде таблицы datetime
Для работы функции в скрипте должна быть объявлена глобальная переменная:
1
| RUN = true -- Флаг поддерживающий работу скрипта |
Для работы функции в скрипте должна присутствовать функция:
WaitUpdateDataAfterReconnect()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| --- Возвращает текущую дату/время сервера в виде таблицы datetime
GetServerDateTime = function()
local dt = {}
-- Пытается получить дату/время сервера
while RUN and dt.day == nil do
dt.day,dt.month,dt.year,dt.hour,dt.min,dt.sec = string.match(getInfoParam('TRADEDATE')..' '..getInfoParam('SERVERTIME'),"(%d*).(%d*).(%d*) (%d*):(%d*):(%d*)")
-- Если не удалось получить, или разрыв связи, ждет подключения и подгрузки с сервера актуальных данных
if dt.day == nil or isConnected() == 0 then WaitUpdateDataAfterReconnect() end
end
-- Если во время ожидания скрипт был остановлен пользователем, возвращает таблицу datetime даты/времени компьютера, чтобы не вернуть пустую таблицу и не вызвать ошибку в алгоритме
if not RUN then return os.date('*t', os.time()) end
-- Приводит полученные значения к типу number
for key,value in pairs(dt) do dt[key] = tonumber(value) end
-- Возвращает итоговую таблицу
return dt
end |
StrToTime() -- Приводит время из строкового формата ЧЧ:ММ к формату datetime
Для работы функции в скрипте должна быть объявлена глобальная переменная:
1
| RUN = true -- Флаг поддерживающий работу скрипта |
Для работы функции в скрипте должна присутствовать функция:
GetServerDateTime()
Так же, должна присутствовать переменная RUN, которая поддерживает работу основного цикла while в функции main.
1
2
3
4
5
6
7
8
9
10
11
| -- Приводит время из строкового формата ЧЧ:ММ к формату datetime
StrToTime = function(str_time)
while RUN and GetServerDateTime().day == nil do sleep(100) end
if not RUN then return false end
local dt = GetServerDateTime()
local h,m = string.match( str_time, "(%d%d):(%d%d)")
dt.hour = tonumber(h)
dt.min = tonumber(m)
dt.sec = 0
return dt
end |
CheckDemo() -- Узнать является ли терминал демо от компании Arqa, или демо брокера Открытие
1
2
3
4
5
6
7
8
9
10
11
12
| function main()
if CheckDemo() then message('DEMO') else message('REAL') end
end
-- Функция возвращает true, если это демо от компании Arqa, или брокера Открытие, иначе false
CheckDemo = function()
if getClassInfo('QJSIM') ~= nil or getClassInfo('SPBFUT').name:find('Фьючерсы') ~= nil then
return true
else
return false
end
end |
GetTotalnet() -- Получает текущую чистую позицию по инструменту
Для работы функции в скрипте должны быть объявлены глобальные переменные:
1
2
3
4
5
6
| ACCOUNT = 'SPBFUT00c41' -- Код счета
CLASS_CODE = 'SPBFUT' -- Код класса
SEC_CODE = 'RIM7' -- Код инструмента
LIMIT_KIND = 0 -- Тип лимита (акции), для демо счета должно быть 0, для реального 2
BALANCE_TYPE = 1 -- Тип отображения баланса в таблице "Таблица лимитов по денежным средствам" (1 - в лотах, 2 - с учетом количества в лоте)
-- Например, при покупке 1 лота USDRUB одни брокеры в поле "Баланс" транслируют 1, другие 1000 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
| -- Получает текущую чистую позицию по инструменту
GetTotalnet = function()
-- ФЬЮЧЕРСЫ, ОПЦИОНЫ
if CLASS_CODE == 'SPBFUT' or CLASS_CODE == 'SPBOPT' then
local num = getNumberOf('futures_client_holding')
if num > 0 then
-- Находит размер лота
local lot = tonumber(getParamEx(CLASS_CODE, SEC_CODE, 'LOTSIZE').param_value)
if num > 1 then
for i = 0, num - 1 do
local futures_client_holding = getItem('futures_client_holding',i)
if futures_client_holding.sec_code == SEC_CODE and futures_client_holding.trdaccid == ACCOUNT then
if BALANCE_TYPE == 1 then
return futures_client_holding.totalnet
else
return futures_client_holding.totalnet/lot
end
end
end
else
local futures_client_holding = getItem('futures_client_holding',0)
if futures_client_holding.sec_code == SEC_CODE and futures_client_holding.trdaccid == ACCOUNT then
if BALANCE_TYPE == 1 then
return futures_client_holding.totalnet
else
return futures_client_holding.totalnet/lot
end
end
end
end
-- АКЦИИ
elseif CLASS_CODE == 'TQBR' or CLASS_CODE == 'QJSIM' then
local num = getNumberOf('depo_limits')
if num > 0 then
local lot = tonumber(getParamEx(CLASS_CODE, SEC_CODE, 'LOTSIZE').param_value)
if num > 1 then
for i = 0, num - 1 do
local depo_limit = getItem('depo_limits', i)
if depo_limit.sec_code == SEC_CODE
and depo_limit.trdaccid == ACCOUNT
and depo_limit.limit_kind == LIMIT_KIND then
if BALANCE_TYPE == 1 then
return depo_limit.currentbal
else
return depo_limit.currentbal/lot
end
end
end
else
local depo_limit = getItem('depo_limits', 0)
if depo_limit.sec_code == SEC_CODE
and depo_limit.trdaccid == ACCOUNT
and depo_limit.limit_kind == LIMIT_KIND then
if BALANCE_TYPE == 1 then
return depo_limit.currentbal
else
return depo_limit.currentbal/lot
end
end
end
end
-- ВАЛЮТА
elseif CLASS_CODE == 'CETS' then
local num = getNumberOf('money_limits')
if num > 0 then
-- Находит валюту
local cur = string.sub(SEC_CODE, 1, 3)
local lot = tonumber(getParamEx(CLASS_CODE, SEC_CODE, 'LOTSIZE').param_value)
if num > 1 then
for i = 0, num - 1 do
local money_limit = getItem('money_limits', i)
if money_limit.currcode == cur
and money_limit.client_code == CLIENT_CODE
and money_limit.limit_kind == LIMIT_KIND then
if BALANCE_TYPE == 1 then
return money_limit.currentbal
else
return money_limit.currentbal/lot
end
end
end
else
local money_limit = getItem('money_limits', 0)
if money_limit.currcode == cur
and money_limit.client_code == CLIENT_CODE
and money_limit.limit_kind == LIMIT_KIND then
if BALANCE_TYPE == 1 then
return money_limit.currentbal
else
return money_limit.currentbal/lot
end
end
end
end
end
-- Если позиция по инструменту в таблице не найдена, возвращает 0
return 0
end |
GetFreeMoney() -- Возвращает доступные средства
Для работы функции в скрипте должны быть объявлены глобальные переменные:
1
2
3
| ACCOUNT = 'SPBFUT00010' -- Код счета
FIRM_ID = 'NC0011100000' -- Фирма
CLIENT_CODE = '10814' -- Код клиента |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| -- Возвращает доступные средства
GetFreeMoney = function()
if CLASS_CODE == 'SPBFUT' or CLASS_CODE == 'SPBOPT' then
local num = getNumberOf('futures_client_limits')
if num > 0 then
if num > 1 then
for i = 0, num - 1 do
local limit = getItem('futures_client_limits', i)
if limit.firmid == FIRM_ID and limit.trdaccid == ACCOUNT and limit.limit_type == 0 then
return limit.cbplplanned
end
end
else
local limit = getItem('futures_client_limits', 0)
if limit.firmid == FIRM_ID and limit.trdaccid == ACCOUNT and limit.limit_type == 0 then
return limit.cbplplanned
end
end
end
elseif CLASS_CODE == 'TQBR' or CLASS_CODE == 'QJSIM' or CLASS_CODE == 'CETS' then
local num = getNumberOf('money_limits')
if num > 0 then
if num > 1 then
for i = 0, num - 1 do
local limit = getItem('money_limits', i)
if limit.currcode == 'SUR' and limit.firmid == FIRM_ID and limit.client_code == CLIENT_CODE and limit.limit_kind == 0 then
return limit.currentbal
end
end
else
local limit = getItem('money_limits', 0)
if limit.currcode == 'SUR' and limit.firmid == FIRM_ID and limit.client_code == CLIENT_CODE and limit.limit_kind == 0 then
return limit.currentbal
end
end
end
end
return 0
end |
GetCorrectPrice() -- Приводит переданную цену к требуемому для транзакции по инструменту виду
Для работы функции в скрипте должны быть объявлены глобальные переменные:
1
2
| CLASS_CODE = 'SPBFUT' -- Код класса
SEC_CODE = 'RIM7' -- Код инструмента |
Так же, для работы функции в скрипте должна присутствовать функция:
math_round()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| -- Приводит переданную цену к требуемому для транзакции по инструменту виду
GetCorrectPrice = function(price) -- STRING
-- Получает точность цены по инструменту
local scale = getSecurityInfo(CLASS_CODE, SEC_CODE).scale
-- Получает минимальный шаг цены инструмента
local PriceStep = tonumber(getParamEx(CLASS_CODE, SEC_CODE, "SEC_PRICE_STEP").param_value)
-- Если после запятой должны быть цифры
if scale > 0 then
price = tostring(price)
-- Ищет в числе позицию запятой, или точки
local dot_pos = price:find('.')
local comma_pos = price:find(',')
-- Если передано целое число
if dot_pos == nil and comma_pos == nil then
-- Добавляет к числу ',' и необходимое количество нулей и возвращает результат
price = price..','
for i=1,scale do price = price..'0' end
return price
else -- передано вещественное число
-- Если нужно, заменяет запятую на точку
if comma_pos ~= nil then price:gsub(',', '.') end
-- Округляет число до необходимого количества знаков после запятой
price = math_round(tonumber(price), scale)
-- Корректирует на соответствие шагу цены
price = math_round(price/PriceStep)*PriceStep
price = string.gsub(tostring(price),'[\.]+', ',')
return price
end
else -- После запятой не должно быть цифр
-- Корректирует на соответствие шагу цены
price = math_round(price/PriceStep)*PriceStep
return tostring(math.floor(price))
end
end |
GetPriceForMarketOrder() -- Возвращает корректную цену для рыночной заявки по текущему инструменту
Для работы функции в скрипте должны быть объявлены глобальные переменные:
1
2
| CLASS_CODE = 'SPBFUT' -- Код класса
SEC_CODE = 'RIM7' -- Код инструмента |
Так же, для работы функции в скрипте должна присутствовать функция:
GetCorrectPrice()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| -- Возвращает корректную цену для рыночной заявки по текущему инструменту (принимает 'S',или 'B')
GetPriceForMarketOrder = function(operation) -- STRING
-- Получает минимальный шаг цены инструмента
local PriceStep = tonumber(getParamEx(CLASS_CODE, SEC_CODE, "SEC_PRICE_STEP").param_value)
-- В зависимости от направления
if operation == 'B' then -- BUY
-- Пытается получить максимально возможную цену для инструмента
local PriceMax = tonumber(getParamEx(CLASS_CODE, SEC_CODE, 'PRICEMAX').param_value)
-- Если максимально возможная цена получена
if PriceMax ~= nil and PriceMax ~= 0 then
-- Возвращает ее в нужном для транзакции формате
return GetCorrectPrice(PriceMax)
-- Иначе, максимально возможная цена не получена
else
-- Получает цену последней сделки
local Last = tonumber(getParamEx(CLASS_CODE, SEC_CODE, 'LAST').param_value)
-- Возвращает ее в нужном для транзакции формате, увеличив перед этим на 50 шагов цены
return GetCorrectPrice(Last + 50*PriceStep)
end
else -- SELL
-- Пытается получить минимально возможную цену для инструмента
local PriceMin = tonumber(getParamEx(CLASS_CODE, SEC_CODE, 'PRICEMIN').param_value)
-- Если минимально возможная цена получена
if PriceMin ~= nil and PriceMin ~= 0 then
-- Возвращает ее в нужном для транзакции формате
return GetCorrectPrice(PriceMin)
-- Иначе, минимально возможная цена не получена
else
-- Получает цену последней сделки
local Last = tonumber(getParamEx(CLASS_CODE, SEC_CODE, 'LAST').param_value)
-- Возвращает ее в нужном для транзакции формате, уменьшив перед этим на 50 шагов цены
return GetCorrectPrice(Last - 50*PriceStep)
end
end
end |
SetOrder() -- Выставляет обычную лимитную заявку
Для работы функции в скрипте должны быть объявлены глобальные переменные:
1
2
3
4
| ACCOUNT = 'SPBFUT00c41' -- Код счета
CLASS_CODE = 'SPBFUT' -- Код класса
SEC_CODE = 'RIM7' -- Код инструмента
trans_id = os.time() -- ID транзакции |
Так же, для работы функции в скрипте должны присутствовать функции:
GetCorrectPrice()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| -- Выставляет заявку
SetOrder = function(
price, -- Цена заявки
operation, -- Операция ('B' - buy, 'S' - sell)
qty -- Количество
)
-- Выставляет заявку
-- Получает ID для следующей транзакции
trans_id = trans_id + 1
-- Заполняет структуру для отправки транзакции
local T = {}
T['TRANS_ID'] = tostring(trans_id) -- Номер транзакции
T['ACCOUNT'] = ACCOUNT -- Код счета
T['CLASSCODE'] = CLASS_CODE -- Код класса
T['SECCODE'] = SEC_CODE -- Код инструмента
T['ACTION'] = 'NEW_ORDER' -- Тип транзакции ('NEW_ORDER' - новая заявка)
T['TYPE'] = 'L' -- Тип ('L' - лимитированная, 'M' - рыночная)
T['OPERATION'] = operation -- Операция ('B' - buy, или 'S' - sell)
T['PRICE'] = GetCorrectPrice(price) -- Цена
T['QUANTITY'] = tostring(qty) -- Количество
-- Отправляет транзакцию
local Res = sendTransaction(T)
-- Если при отправке транзакции возникла ошибка
if Res ~= '' then
-- Выводит сообщение об ошибке
message('Ошибка транзакции открытия/закрытия: '..Res)
end
end |
SetMarketOrder() -- Выставляет рыночную (по сути) заявку
Для работы функции в скрипте должны быть объявлены глобальные переменные:
1
2
3
4
| ACCOUNT = 'SPBFUT00c41' -- Код счета
CLASS_CODE = 'SPBFUT' -- Код класса
SEC_CODE = 'RIM7' -- Код инструмента
trans_id = os.time() -- ID транзакции |
Так же, для работы функции в скрипте должны присутствовать функции:
GetPriceForMarketOrder()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| -- Выставляет рыночную (по сути) заявку
SetMarketOrder = function(
operation, -- Операция ('B' - buy, 'S' - sell)
qty -- Количество
)
-- Выставляет рыночную заявку
-- Получает ID для следующей транзакции
trans_id = trans_id + 1
-- Заполняет структуру для отправки транзакции
local T = {}
T['TRANS_ID'] = tostring(trans_id) -- Номер транзакции
T['ACCOUNT'] = ACCOUNT -- Код счета
T['CLASSCODE'] = CLASS_CODE -- Код класса
T['SECCODE'] = SEC_CODE -- Код инструмента
T['ACTION'] = 'NEW_ORDER' -- Тип транзакции ('NEW_ORDER' - новая заявка)
T['TYPE'] = 'L' -- Тип ('L' - лимитированная, 'M' - рыночная)
T['OPERATION'] = operation -- Операция ('B' - buy, или 'S' - sell)
T['PRICE'] = GetPriceForMarketOrder(operation) -- Цена
T['QUANTITY'] = tostring(qty) -- Количество
-- Отправляет транзакцию
local Res = sendTransaction(T)
-- Если при отправке транзакции возникла ошибка
if Res ~= '' then
-- Выводит сообщение об ошибке
message('Ошибка транзакции открытия/закрытия по рынку: '..Res)
end
end |
CheckOrder() -- Проверяет наличие в системе заявки с определенным ID транзакции
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| -- Проверяет наличие в системе заявки с определенным ID транзакции
CheckOrder = function(id)
-- Перебирает таблицу заявок от последней к первой
for i=getNumberOf('orders') - 1, 0, -1 do
-- Получает заявку из строки таблицы с индексом i
local order = getItem('orders', i)
-- Если ID транзакции совпадает
if order.trans_id == id then
return true
end
end
-- Заявка с нужным ID транзакции не найдена
return false
end |
GetOrderNum() -- Возвращает номер заявки по ее ID транзакции
1
2
3
4
5
6
7
8
9
10
11
12
13
| -- Возвращает номер заявки по ее ID транзакции
GetOrderNum = function(id)
-- Перебирает таблицу заявок от последней к первой
for i=getNumberOf('orders') - 1, 0, -1 do
-- Получает заявку из строки таблицы с индексом i
local order = getItem('orders', i)
-- Если ID транзакции совпадает
if order.trans_id == id then
-- Возвращает номер заявки
return order.order_num
end
end
end |
WaitOrderComplete () -- Ожидает исполнения заявки по ID транзакции
Для работы функции в скрипте должны быть объявлены глобальные переменные:
1
| SEC_CODE = 'RIM7' -- Код инструмента |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
| -- Ожидает исполнения заявки по trans_id
WaitOrderComplete = function(trans_id)
-- Находит заявку по trans_id
local order_line_idx = 0
while RUN and order_line_idx == 0 do
sleep(100)
-- Перебирает таблицу заявок с последней строки к первой
local number = getNumberOf('orders')
if number > 0 then
if number > 1 then
for i = number-1,0,-1 do
-- Получает строку таблицы
local order_line = getItem('orders', i)
if order_line.trans_id == trans_id and order_line.sec_code == SEC_CODE then
order_line_idx = i
break
end
end
else
-- Получает строку таблицы
local order_line = getItem('orders', 0)
if order_line.trans_id == trans_id and order_line.sec_code == SEC_CODE then
order_line_idx = 0
end
end
end
end
-- Ждет когда заявка будет полностью исполнена
while RUN and getItem('orders', order_line_idx).balance ~= 0 do sleep(100) end
-- Ждет появления всех сделок заявки в таблице сделок (для исключения ошибок дальнейшей обработки события)
-- Узнает количество лотов в заявке
local qty = getItem('orders', order_line_idx).qty
-- Счетчик найденных лотов
local lots_counter = 0
-- Индекс строки последней обработанной сделки
local last_complete_index = -1
while RUN and lots_counter ~= qty do
-- Перебирает еще не обработанные строки таблицы сделок
local last_index = getNumberOf('trades')-1
local tmp_index = 0
if last_complete_index + 1 <= last_index then
for i = last_complete_index + 1, last_index do
-- Получает строку таблицы
local trade_line = getItem('trades', i)
-- Если сделка с нужным ID, и сделка по нужному инструменту
if trade_line.trans_id == trans_id and trade_line.sec_code == SEC_CODE then
-- Увеличивает счетчик
lots_counter = lots_counter + trade_line.qty
-- Если найдены все сделки
if lots_counter == qty then
break
end
end
tmp_index = i
end
last_complete_index = tmp_index
end
sleep(100)
end
end |
Set_SL() -- Выставляет 'Стоп лимит' заявку
Для работы функции в скрипте должны быть объявлены глобальные переменные:
1
2
3
4
5
| ACCOUNT = 'SPBFUT00c41' -- Код счета
CLASS_CODE = 'SPBFUT' -- Код класса
SEC_CODE = 'RIM7' -- Код инструмента
EXPIRY_DATE = 'TODAY' -- Срок действия стоп-заявки: 'TODAY' - до окончания текущей торговой сессии, 'GTC' -до отмены, или время в формате 'ГГГГММДД'
trans_id = os.time() -- ID транзакции |
Так же, для работы функции в скрипте должны присутствовать функции:
GetCorrectPrice()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| -- Выставляет стоп-лимит заявку
Set_SL = function(
operation, -- Операция ('B' - buy, 'S' - sell)
stop_price, -- Цена Стоп-Лосса
qty -- Количество в лотах
)
-- Получает ID для следующей транзакции
trans_id = trans_id + 1
-- Вычисляет цену, по которой выставится заявка при срабатывании стопа
local price = stop_price - 50*PriceStep
if operation == 'B' then price = stop_price + 50*PriceStep end
-- Заполняет структуру для отправки транзакции на Стоп-лосс
local T = {}
T['TRANS_ID'] = tostring(trans_id)
T['CLASSCODE'] = CLASS_CODE
T['SECCODE'] = SEC_CODE
T['ACCOUNT'] = ACCOUNT
T['ACTION'] = 'NEW_STOP_ORDER' -- Тип заявки
T['OPERATION'] = operation -- Операция ('B' - покупка(BUY), 'S' - продажа(SELL))
T['QUANTITY'] = tostring(qty) -- Количество в лотах
T['STOPPRICE'] = GetCorrectPrice(stop_price) -- Цена Стоп-Лосса
T['PRICE'] = GetCorrectPrice(price) -- Цена, по которой выставится заявка при срабатывании Стоп-Лосса (для рыночной заявки по акциям должна быть 0)
T['EXPIRY_DATE'] = EXPIRY_DATE -- 'TODAY', 'GTC', или время
-- Отправляет транзакцию
local Res = sendTransaction(T)
-- Если при отправке транзакции возникла ошибка
if Res ~= '' then
-- Выводит ошибку
message('Ошибка транзакции стоп-лимит: '..Res)
end
end |
SetTP() -- Выставляет 'Тейк профит' заявку
Для работы функции в скрипте должны быть объявлены глобальные переменные:
1
2
3
4
5
| ACCOUNT = 'SPBFUT00c41' -- Код счета
CLASS_CODE = 'SPBFUT' -- Код класса
SEC_CODE = 'RIM7' -- Код инструмента
EXPIRY_DATE = 'TODAY' -- Срок действия стоп-заявки: 'TODAY' - до окончания текущей торговой сессии, 'GTC' -до отмены, или время в формате 'ГГГГММДД'
trans_id = os.time() -- ID транзакции |
Так же, для работы функции в скрипте должна присутствовать функция:
GetCorrectPrice()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
| -- Выставляет "Тейк профит" заявку
SetTP = function(
operation, -- Операция ('B', или 'S')
pos_price, -- Цена позиции, на которую выставляется стоп-заявка
qty, -- Количество лотов
profit_size -- Размер профита в шагах цены
)
-- Получает минимальный шаг цены
local PriceStep = tonumber(getParamEx(CLASS_CODE, SEC_CODE, "SEC_PRICE_STEP").param_value)
-- Получает максимально возможную цену заявки
local PriceMax = tonumber(getParamEx(CLASS_CODE, SEC_CODE, 'PRICEMAX').param_value)
-- Получает минимально возможную цену заявки
local PriceMin = tonumber(getParamEx(CLASS_CODE, SEC_CODE, 'PRICEMIN').param_value)
-- Заполняет структуру для отправки транзакции на Стоп-лосс и Тэйк-профит
local T = {}
T['TRANS_ID'] = tostring(trans_id)
T['CLASSCODE'] = CLASS_CODE
T['SECCODE'] = SEC_CODE
T['ACCOUNT'] = ACCOUNT
T['ACTION'] = 'NEW_STOP_ORDER' -- Тип заявки
T['STOP_ORDER_KIND'] = 'TAKE_PROFIT_STOP_ORDER' -- Тип стоп-заявки
T['OPERATION'] = operation -- Операция ('B' - покупка(BUY), 'S' - продажа(SELL))
T['QUANTITY'] = tostring(qty) -- Количество в лотах
-- Вычисляет цену профита
local stopprice = 0
if operation == 'B' then
stopprice = pos_price - profit_size*PriceStep
if PriceMin ~= nil and PriceMin ~= 0 and stopprice < PriceMin then
stopprice = PriceMin
end
elseif operation == 'S' then
stopprice = pos_price + profit_size*PriceStep
if PriceMax ~= nil and PriceMax ~= 0 and stopprice > PriceMax then
stopprice = PriceMax
end
end
T['STOPPRICE'] = GetCorrectPrice(stopprice) -- Цена Тэйк-Профита
T['OFFSET'] = '0' -- отступ
T['OFFSET_UNITS'] = 'PRICE_UNITS' -- в шагах цены
local spread = 50*PriceStep
if operation == 'B' then
if PriceMax ~= nil and PriceMax ~= 0 and stopprice + spread > PriceMax then
spread = PriceMax - stopprice - 1*PriceStep
end
elseif operation == 'S' then
if PriceMin ~= nil and PriceMin ~= 0 and stopprice - spread < PriceMin then
spread = stopprice - PriceMin - 1*PriceStep
end
end
T['SPREAD'] = GetCorrectPrice(spread) -- Защитный спред
T['SPREAD_UNITS'] = 'PRICE_UNITS' -- в шагах цены
T['EXPIRY_DATE'] = EXPIRY_DATE -- 'TODAY', 'GTC', или время
-- Отправляет транзакцию
local Res = sendTransaction(T)
if Res ~= '' then
message('Ошибка выставления стоп-заявки: '..Res)
end
end |
SetTP_SL() -- Выставляет 'Тейк профит и Стоп лимит' заявку
Для работы функции в скрипте должны быть объявлены глобальные переменные:
1
2
3
4
5
| ACCOUNT = 'SPBFUT00c41' -- Код счета
CLASS_CODE = 'SPBFUT' -- Код класса
SEC_CODE = 'RIM7' -- Код инструмента
EXPIRY_DATE = 'TODAY' -- Срок действия стоп-заявки: 'TODAY' - до окончания текущей торговой сессии, 'GTC' -до отмены, или время в формате 'ГГГГММДД'
trans_id = os.time() -- ID транзакции |
Так же, для работы функции в скрипте должна присутствовать функция:
GetCorrectPrice()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
| -- Выставляет "Тейк профит и Стоп лимит" заявку
SetTP_SL = function(
operation, -- Операция ('B', или 'S')
pos_price, -- Цена позиции, на которую выставляется стоп-заявка
qty, -- Количество лотов
profit_size, -- Размер профита в шагах цены
stop_size -- Размер стопа в шагах цены
)
-- Получает минимальный шаг цены
local PriceStep = tonumber(getParamEx(CLASS_CODE, SEC_CODE, "SEC_PRICE_STEP").param_value)
-- Получает максимально возможную цену заявки
local PriceMax = tonumber(getParamEx(CLASS_CODE, SEC_CODE, 'PRICEMAX').param_value)
-- Получает минимально возможную цену заявки
local PriceMin = tonumber(getParamEx(CLASS_CODE, SEC_CODE, 'PRICEMIN').param_value)
-- Заполняет структуру для отправки транзакции на Стоп-лосс и Тэйк-профит
local T = {}
T['TRANS_ID'] = tostring(trans_id)
T['CLASSCODE'] = CLASS_CODE
T['SECCODE'] = SEC_CODE
T['ACCOUNT'] = ACCOUNT
T['ACTION'] = 'NEW_STOP_ORDER' -- Тип заявки
T['STOP_ORDER_KIND'] = 'TAKE_PROFIT_AND_STOP_LIMIT_ORDER' -- Тип стоп-заявки
T['OPERATION'] = operation -- Операция ('B' - покупка(BUY), 'S' - продажа(SELL))
T['QUANTITY'] = tostring(qty) -- Количество в лотах
-- Вычисляет цену профита
local stopprice = 0
if operation == 'B' then
stopprice = pos_price - profit_size*PriceStep
if PriceMin ~= nil and PriceMin ~= 0 and stopprice < PriceMin then
stopprice = PriceMin
end
elseif operation == 'S' then
stopprice = pos_price + profit_size*PriceStep
if PriceMax ~= nil and PriceMax ~= 0 and stopprice > PriceMax then
stopprice = PriceMax
end
end
T['STOPPRICE'] = GetCorrectPrice(stopprice) -- Цена Тэйк-Профита
T['OFFSET'] = '0' -- отступ
T['OFFSET_UNITS'] = 'PRICE_UNITS' -- в шагах цены
local spread = 50*PriceStep
if operation == 'B' then
if PriceMax ~= nil and PriceMax ~= 0 and stopprice + spread > PriceMax then
spread = PriceMax - stopprice - 1*PriceStep
end
elseif operation == 'S' then
if PriceMin ~= nil and PriceMin ~= 0 and stopprice - spread < PriceMin then
spread = stopprice - PriceMin - 1*PriceStep
end
end
T['SPREAD'] = GetCorrectPrice(spread) -- Защитный спред
T['SPREAD_UNITS'] = 'PRICE_UNITS' -- в шагах цены
T['MARKET_TAKE_PROFIT'] = 'NO' -- 'YES', или 'NO'
-- Вычисляет цену стопа
local stopprice2 = 0
if operation == 'B' then
stopprice2 = pos_price + stop_size*PriceStep
if PriceMax ~= nil and PriceMax ~= 0 and stopprice2 > PriceMax then
stopprice2 = PriceMax
end
elseif operation == 'S' then
stopprice2 = pos_price - stop_size*PriceStep
if PriceMin ~= nil and PriceMin ~= 0 and stopprice2 < PriceMin then
stopprice2 = PriceMin
end
end
T['STOPPRICE2'] = GetCorrectPrice(stopprice2) -- Цена Стоп-Лосса
-- Вычисляет цену, по которой выставится заявка при срабатывании стопа
local price = 0
if operation == 'B' then
price = stopprice2 + 50*PriceStep
if PriceMax ~= nil and PriceMax ~= 0 and price > PriceMax then
price = PriceMax
end
elseif operation == 'S' then
price = stopprice2 - 50*PriceStep
if PriceMin ~= nil and PriceMin ~= 0 and price < PriceMin then
price = PriceMin
end
end
T['PRICE'] = GetCorrectPrice(price) -- Цена, по которой выставится заявка при срабатывании Стоп-Лосса (для рыночной заявки по акциям должна быть 0)
T['MARKET_STOP_LIMIT'] = 'NO' -- 'YES', или 'NO'
T['EXPIRY_DATE'] = EXPIRY_DATE -- 'TODAY', 'GTC', или время
T['IS_ACTIVE_IN_TIME'] = 'NO' -- Признак действия заявки типа «Тэйк-профит и стоп-лимит» в течение определенного интервала времени. Значения «YES» или «NO»
-- Отправляет транзакцию
local Res = sendTransaction(T)
if Res ~= '' then
message('Ошибка выставления стоп-заявки: '..Res)
end
end |
CheckStopOrder() -- Проверяет наличие в системе стоп-заявки с определенным ID транзакции
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| -- Проверяет наличие в системе стоп-заявки с определенным ID транзакции
CheckStopOrder = function(id)
-- Перебирает таблицу стоп-заявок от последней к первой
for i=getNumberOf('stop_orders') - 1, 0, -1 do
-- Получает стоп-заявку из строки таблицы с индексом i
local stop_order = getItem('stop_orders', i)
-- Если ID транзакции совпадает
if stop_order.trans_id == id then
return true
end
end
-- Стоп-заявка с нужным ID не найдена
return false
end |
GetStopOrderNum() -- Возвращает номер стоп-заявки по ее ID транзакции
1
2
3
4
5
6
7
8
9
10
11
12
13
| -- Возвращает номер стоп-заявки по ее ID транзакции
GetStopOrderNum = function(id)
-- Перебирает таблицу стоп-заявок от последней к первой
for i=getNumberOf('stop_orders') - 1, 0, -1 do
-- Получает стоп-заявку из строки таблицы с индексом i
local stop_order = getItem('stop_orders', i)
-- Если ID транзакции совпадает
if stop_order.trans_id == id then
-- Возвращает номер стоп-заявки
return stop_order.order_num
end
end
end |
CheckStopOrderActive() -- Проверяет по номеру активна ли стоп-заявка
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| -- Проверяет по номеру активна ли стоп-заявка
CheckStopOrderActive = function(order_num)
-- Перебирает таблицу стоп-заявок от последней к первой
for i=getNumberOf('stop_orders') - 1, 0, -1 do
-- Получает стоп-заявку из строки таблицы с индексом i
local stop_order = getItem('stop_orders', i)
-- Если номер транзакции совпадает
if stop_order.order_num == order_num then
-- Если стоп-заявка активна
if bit.test(stop_order.flags, 0) then
return true
else
return false
end
end
end
end |
KillOrder() -- Снимает заявку
Для работы функции в скрипте должны быть объявлены глобальные переменные:
1
2
3
4
| CLASS_CODE = 'SPBFUT' -- Код класса
SEC_CODE = 'RIM7' -- Код инструмента
RUN = true -- Флаг поддержания работы "бесконечного" цикла в функции main
trans_id = os.time() -- ID транзакции |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
| -- Снимает заявку
KillOrder = function(
order_num -- Номер снимаемой заявки
)
-- Находит заявку (30 сек. макс.)
local index = 0
local start_sec = os.time()
local find_order = false
while RUN and not find_order and os.time() - start_sec < 30 do
for i=getNumberOf('orders')-1,0,-1 do
local order = getItem('orders', i)
if order.order_num == order_num then
-- Если заявка уже была исполнена (не активна)
if not bit.test(order.flags, 0) then
return false
end
index = i
find_order = true
break
end
end
end
if not find_order then
message('Ошибка: не найдена заявка!')
return false
end
-- Получает ID для следующей транзакции
trans_id = trans_id + 1
-- Заполняет структуру для отправки транзакции на снятие заявки
local T = {}
T['TRANS_ID'] = tostring(trans_id)
T['CLASSCODE'] = CLASS_CODE
T['SECCODE'] = SEC_CODE
T['ACTION'] = 'KILL_ORDER' -- Тип заявки
T['ORDER_KEY'] = tostring(order_num) -- Номер заявки, снимаемой из торговой системы
-- Отправляет транзакцию
local Res = sendTransaction(T)
-- Если при отправке транзакции возникла ошибка
if Res ~= '' then
-- Выводит ошибку
message('Ошибка снятия заявки: '..Res)
return false
end
-- Ожидает когда заявка перестанет быть активна (30 сек. макс.)
start_sec = os.time()
local active = true
while RUN and os.time() - start_sec < 30 do
local order = getItem('orders', index)
-- Если заявка не активна
if not bit.test(order.flags, 0) then
-- Если заявка успела исполниться
if not bit.test(order.flags, 1) then
return false
end
active = false
break
end
sleep(10)
end
if active then
message('Возникла неизвестная ошибка при снятии ЗАЯВКИ')
return false
end
return true
end |
Kill_SO() -- Снимает стоп-заявку
Для работы функции в скрипте должны быть объявлены глобальные переменные:
1
2
3
4
| CLASS_CODE = 'SPBFUT' -- Код класса
SEC_CODE = 'RIM7' -- Код инструмента
RUN = true -- Флаг поддержания работы "бесконечного" цикла в функции main
trans_id = os.time() -- ID транзакции |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
| -- Снимает стоп-заявку
Kill_SO = function(
stop_order_num -- Номер снимаемой стоп-заявки
)
-- Находит стоп-заявку (30 сек. макс.)
local index = 0
local start_sec = os.time()
local find_so = false
while RUN and not find_so and os.time() - start_sec < 30 do
for i=getNumberOf('stop_orders')-1,0,-1 do
local stop_order = getItem('stop_orders', i)
if stop_order.order_num == stop_order_num then
-- Если стоп-заявка уже была исполнена (не активна)
if not bit.test(stop_order.flags, 0) then
return false
end
index = i
find_so = true
break
end
end
end
if not find_so then
message('Ошибка: не найдена стоп-заявка!')
return false
end
-- Получает ID для следующей транзакции
trans_id = trans_id + 1
-- Заполняет структуру для отправки транзакции на снятие стоп-заявки
local T = {}
T['TRANS_ID'] = tostring(trans_id)
T['CLASSCODE'] = CLASS_CODE
T['SECCODE'] = SEC_CODE
T['ACTION'] = 'KILL_STOP_ORDER' -- Тип заявки
T['STOP_ORDER_KEY'] = tostring(stop_order_num) -- Номер стоп-заявки, снимаемой из торговой системы
-- Отправляет транзакцию
local Res = sendTransaction(T)
-- Если при отправке транзакции возникла ошибка
if Res ~= '' then
-- Выводит ошибку
message('Ошибка снятия стоп-заявки: '..Res)
return false
end
-- Ожидает когда стоп-заявка перестанет быть активна (30 сек. макс.)
start_sec = os.time()
local active = true
while RUN and os.time() - start_sec < 30 do
local stop_order = getItem('stop_orders', index)
-- Если стоп-заявка не активна
if not bit.test(stop_order.flags, 0) then
-- Если стоп-заявка успела исполниться
if not bit.test(stop_order.flags, 1) then
return false
end
active = false
break
end
sleep(10)
end
if active then
message('Возникла неизвестная ошибка при снятии СТОП-ЗАЯВКИ')
return false
end
return true
end |
StackFIFO() -- Создает объект стека FIFO(Первым вошел, первым вышел)
Использование:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| -- Создает объект стека максимальным размером 3 элемента (при добавлении более старые будут затираться более новыми)
MyStack = StackFIFO(3)
-- Создает 3 элемента
local elem_1 = 'Элемент 1'
local elem_2 = 'Элемент 2'
local elem_3 = 'Элемент 3'
-- Добавляет 3 элемента в стек
MyStack:Push(elem_1)
MyStack:Push(elem_2)
MyStack:Push(elem_3)
-- Получает самый ранний добавленный элемент
local elem = MyStack:Pop()
-- Теперь в elem находится элемент 1, а в стеке остались элементы 2 и 3
-- Добавим еще один элемент в стек
MyStack:Push('Элемент 4')
-- Теперь в стеке 4,2,3
-- Добавим еще один элемент в стек
MyStack:Push('Элемент 5')
-- 5-й элемент затер 2-й и в стеке сейчас 4,5,3
-- Получает самый ранний добавленный элемент
local elem = MyStack:Pop()
-- Теперь в elem элемент 3, а в стеке остались 4,5
-- Объект стека предоставляет следующие функции:
-- Добавляет запись в стек
MyStack:Push()
-- Извлекает самую старую запись из стека (с удалением)
MyStack:Pop()
-- Возвращает последнюю запись без ее удаления
MyStack:GetLast()
-- Заменяет последний элемент новым
MyStack:ChangeLast(new_elem)
-- Возвращает содержимое в виде массива, где самый старый элемент под индексом 1
MyStack:Array() |
Код, который нужно добавить в скрипт:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
78
79
80
81
82
83
84
| -- Необходимые объявления для ускорения работы стека
local table_insert = table.insert
local table_sinsert = table.sinsert
local table_remove = table.remove
local table_sremove = table.sremove
-- КЛАСС СТЕКА (FIFO)
StackFIFO = function(max)
local Stack = {}
Stack.items = {} -- Массив для стека
Stack.idx_for_add = 1 -- Индекс для добавления следующей, или первой записи
Stack.idx_for_get = 1 -- Индекс для изъятия следующей, или первой записи
Stack.count = 0 -- Количество находящихся в стеке записей
Stack.max = max -- Максимально возможное количество записей в стеке (при переполнении старые записи будут замещаться новыми)
-- Добавляет запись в стек
Stack.Push = function(this, NewEntry)
-- Добавляет запись в стек
if this.items[this.idx_for_add] ~= nil then table_sremove(this.items, this.idx_for_add) end
table_sinsert(this.items, this.idx_for_add, NewEntry)
-- Корректирует счетчик находящихся в стеке записей
if this.count < this.max then this.count = this.count + 1 end
-- Увеличивает индекс для добавления следующей записи
this.idx_for_add = this.idx_for_add + 1
-- Если индекс больше максимально допустимого, то следующая запись будет добавляться в начало стека
if this.idx_for_add > this.max then this.idx_for_add = 1 end
-- Если изъятие записей отстало от записи (новая запись переписала старую), то увеличивает индекс для изъятия следующей записи
if this.idx_for_add - this.idx_for_get == 1 and this.count > 1 then -- смещение внутри стека
this.idx_for_get = this.idx_for_get + 1
-- Добавил в конец, когда индекс для изъятия тоже был в конце и количество не равно 0
elseif this.idx_for_get - this.idx_for_add == this.max - 1 and this.count > 1 then
this.idx_for_get = 1
end
end
-- Извлекает самую старую запись из стека (с удалением)
Stack.Pop = function(this)
local OldInxForGet = this.idx_for_get
if this.count == 0 then return nil end
-- Уменьшает количество записей на 1
this.count = this.count - 1
-- Корректирует, если это была единственная запись
if this.count == -1 then
this.count = 0
this.idx_for_get = this.idx_for_add -- Выравнивает индексы
else -- Если еще есть записи
-- Сдвигает индекс изъятия на 1 вправо
this.idx_for_get = this.idx_for_get + 1
-- Корректирует, если достигнут конец
if this.idx_for_get > this.max then this.idx_for_get = 1 end
end
return this.items[OldInxForGet]
end
-- Возвращает последнюю запись без ее удаления
Stack.GetLast = function(this)
if this.idx_for_get > 1 then
return this.items[this.idx_for_get - 1]
else
return this.items[#this.items]
end
end
-- Заменяет последний элемент новым
Stack.ChangeLast = function(this, new_elem)
if this.idx_for_get > 1 then
this.items[this.idx_for_get - 1] = new_elem
else
return this.items[#this.items] = new_elem
end
end
-- Возвращает содержимое в виде массива, где самый старый элемент под индексом 1
Stack.Array = function(this)
local Array = {}
for i = this.idx_for_get, #this.items do
table_insert(Array, this.items[i])
end
for i = 1, this.idx_for_get - 1 do
table_insert(Array, this.items[i])
end
return Array
end
return Stack
end |
Может кому пригодится.
Если вам нужно распарсить строку сообщения квик, то там могут находится символы, которые визуально смотрятся как пробел, в частности char(160) используется в ценах, например 63 800. Обычным string.gsub("%s", "") такой "пробел" не убрать. Пользуйтесь:
local function ConvertPrice(text)
local s = text:gsub("%s+", "")
s = s:gsub('.',
function (s)
local sb = s:byte()
return (sb == 160 and "") or (sb == 44 and ".") or s
end)
return tonumber(s)
end
for class_code in string.gmatch("QJSIM,TQBR,SPBFUT,SPBOPT,CETS,OPTW,", "(%P*),") do -- Добавить код если надо
Добрый вечер Дмитрий,сразу о наболевшем ) при переборе таблиц циклом индек i надо уменьшать на единицу ) так как начало с нуля ,------и вопрос при отправке транзакции потом получениее ёё номера sleep(1000) и снятия sleep(1000) только при такой задержкевсе коректно работает можно както сократить задержку и с чем это связано,?
Здравствуйте, я после отправки транзакции, например, на выставление заявки, перебираю таблицу заявок и жду пока в ней появится новая с тем же trans_id. Таким образом никаких лишних пауз не добавляется.
Дмитрий, привет! А что будешь делать, если транзакция потерялась по дороге к серверу квик?
Привет, Александр. Например, поставить лимит времени на ожидание и повторять.
Да, лимит времени это гуд, но не панацея, я сделал дополнительную тестовую заявку "покупка" по pricemin, которую отправляю через лимит времени, получаю по ней ответ...
Отлично! Значит все гуд и целевая заявка потерялась, можно ее повторить.
В итоге получаю две заявки, первая потеряшка чудесным образом нашлась!
И это нормально!
Сеть выбирает наиболее не загруженный канал, но это не гарантирует, что в момент движения пакета, этот канал резко не загрузился каким-то архивом в пару экзобайт, а для последующих заявок сеть выберет другой маршрут.
Отсюда вывод, запоминать ранее отправленную заявку, отправлять новую, и если старая вдруг нарисовалась удалить ее или сразу закрыть открытую ей позицию. Как я не люблю эти танцы с бубном.
Здравствуйте, изучал функции время. Вроде все понял и получилось его получить. Но пока не пойму как это использовать. Не нашел информации. Например для работы скрипта от и до заданного времени. Или точнее для выполнения функции main.
Здравствуйте! Вот такой простой пример могу предложить:
Спасибо. Все наладил, с условием от и до. Не понимал как именно сравнивать. Выставлять условие из чего. Какие брать значения. Из примера все стало понятно. Немного затянул с ответом, думал еще будут вопросы)
Всегда пожалуйста! 🙂
Дмитрий здравствуйте!
Вопрос, можно ли использовать следующий код для получение объеиа по инструменту? Если нет, то почему.
LotSize=0.0;
for i = 0,getNumberOf('depo_limits') - 1 do
local depo_limit = getItem("depo_limits", i)
if depo_limit.sec_code==SEC_CODE and depo_limit.currentbal>LotSize then LotSize=depo_limit.currentbal end
end
Здравствуйте! Можно, наверно, только тип лимита нужно бы еще проверять, а чем Вас функция GetTotalnet не устраивает?
Все устраивает, это я для развития и понимания. А про тип лимита, вы имеете в виду,прверка на отложенные и рыночные ордера ?
Тип лимита это Т0, Т2
Понял Вас, спасибо!
Всегда пожалуйста
Дмитрий здравствуйте.
У Вас случайно не было ли такого что depo_limits.awg_position_price возвращала бы цену деленную на 100 ?
У меня на демо тесте вылетает такая ошибка, на реале нету возможности испытать это. Задал вопрос Арки, но они уже пол недели молчат...
т.е. допустим открылся по цене 1 000, а depo_limits. awg_position_price = 10.
Здравствуйте, не сталкивался с этим, ничего не могу сказать.
Благодарю Вас за ответ.
Вполне возможно, что это ошибка моей версии терминала (судя по ответу техподдержки) на всякий случай скину сюда ссылку на вопрос, вдруг местным завсегдатым будет полезно (если еще кто нибудь наткнется на подобное) (https://forum.quik.ru/forum10/topic3445/)
Дмитрий здравствуйте,
У меня вопрос по выставлению стопа (делаю функцию сейчас которая отдельно стоп и отдельно тейк выставляла бы)
Я запутался в заполнении таблицы. а именно с полями
STOPPRICE - по чему у Вас при стоп лоссе на покупку там минимальная цена фьюча используется, а в тейке наоборот
MARKET_STOP_LIMIT и MARKET_TAKE_PROFIT - почему у Вас они отмечены как Не рыночные ? стопы и тейки же для ограничения убытка по рынку бить должны на сколько я их воспринемаю (или же у Вас делается жмуляция рыночной заявки для фьючей если да, то будет ли этот же метод работать на акциях ?)
PRICE - опять же немого мудренно для меня, по чему она опять как то высчитывается из цен STOPPRICE ?
Я для себя хочу сделать просто функцию, в которую заношу цену активации, а далее стоп (или тейк) срабатывают как обычный стоп или же тейк, для ограничения убытка по позиции. Заполнил следующие поля (и описал свои условия срабатывания) : TRANS_ID, CLASSCODE, SEC_CODE, ACCOUNT, ACTION, QUANTITY, OPERATION, STOP_ORDER_KIND.
Причем последнее поле для стопа оставил без внимания, а для тейка - поставил как "TAKE_PROFIT_STOP_ORDER"
Мне нужно еще описанные поля заполнить как я понимаю ( PRICE, STOPPRICE, MARKET_STOP_LIMIT и MARKET_TAKE_PROFIT ) и вот они то как раз и вызвали затруднение... надеюсь на Вашу помощь) Благодарю заранее)
я как для акций, так и для фьючей делаю.
Здравствуйте! Я обычно эмулирую рыночную заявку, т.е. рыночных заявок вообще не существует, по идее, рыночная заявка это просто заявка по очень "плохой" цене, которая при выставлении в стакан исполнится по первой встречной заявке. Они там что-то меняют постоянно, раньше по фьючам нельзя было рыночную заявку отправлять, сейчас можно стало, по этому я использую универсальный подход. По поводу PRICE, это цена заявки (реальной), которая выставится в стакан при достижении цены стопа условия STOPPRICE для стопа и STOPPRICE2 для тейкпрофит-стоп. Так как я ее тоже условно рыночной делаю, то проще отступить сколько-то от стоп-цены. А вообще, самый простой способ разобраться, это повыставлять программно разные заявки на демке и посмотреть как они исполняются. Значения параметров с примерами есть в файле справки info.chm, там раздел что-то типа "Работа с другими приложениями", "Импорт транзакций через API", "Параметры транзакций", "Примеры строк"
То есть получается следующая схема:
Допустим мне нужно поставить стоп для лонга (или тейк для шорта) по цене 150.
SL:
1) Ставится цена активации - STOPPRICE = 150
2) При достижении этой цены 150 ставится PRICE = 100 и далее робот рубит по рынку пока не проталкнет весь объем или же не дойдет до 100
TP:
1) Ставится цена активации - STOPPRICE = 150
2) При достижении этой цены 150 ставится PRICE = 200 и далее робот рубит по рынку пока не проталкнет весь объем или же не дойдет до 200
Я верно схему понял ? За ссылки благодарю, по изучаю завтра как торги начнутся.
Да, Вы правильно поняли
Да, брокера любят вот такие "рыночные эмуляции" когда у клиента много денег в позиции и стоит вот такой стоп, который может двинуть цену в "дно".
Дмитрий здравствуйте.
А почему в примерах функций практически везде есть блок который в цикле дожидается результата ? (при подписки на котировки к примеру, или же при удалении стоп ордеров) ?
Разве это не синхронный запрос ? На сколько я понимаю, этот код выполняется в контексте одного потока и по этому следующая строчка кода не должна выполняться пока не будет выполнено действие (удаление ордера к примеру или же готовая структура с подпиской на котировки. )
Здравствуйте, удаление ордера, или любая другая подобная операция, это всего лишь отправка транзакции на сервер брокера, она еще должна на нем обработаться и только потом придет результат в квик, Вы же когда руками транзакцию отправляете через квик, результат так же позже появляется.
Да, верное замечание. благодарю.
Всегда пожалуйста!
Тогда уж проще писать на QPILE там уже все это есть. Если робот торгует сотней инструментов одновременно, то такой подход не пойдет. Все ваши функции не верны априори.
Все зависит от задач, если робот одним инструментом торгует, то очень даже норм 🙂
Вне зависимости от задач, функции должны выполняться максимально эффективно.
Сейчас задача торговать одним инструментом, завтра - сотней и что? Переделывать все функции?
Зачем в datetime менять string на number? Не за чем.
Вот функция, которая заменяет 2 ваши функции по преобразованию цены, да и просто округляет все что нужно, в разумных пределах, например время(posix) до нужного таймфрейма, до H1 включительно.
И никаких танцев с бубном, мало того, эта функция в 14 раз быстрее, чем math_round, который по привычке пользует 99% народу.
Медленно - это ключевое слово, для описания всех ваших "полезных" функций, особенно CheckOrder - жесть просто.
Ну ладно пару сотен записей в таблице заявок, а ну как 5-10 тыс и это минут за 20, что будет делать эта функция? Правильно, заниматься хер..й.
Без обид.
string на number потому что дата это числовой тип, а не строковый, по моему все логично, наоборот непонятно для чего работать с датой как со строкой 🙂 99,9% клиентов заказывают роботов, которые торгуют одним инструментом и никаких проблем нет с этими функциями. Ты со своей колокольни судишь, у тебя задачи другие и они тебе не подходят, а я всех роботов, за редким исключением, писал раньше, используя эти функции с минимальной кастомизацией и все довольны 🙂 И чем твоя floor быстрее math_round ? И там и там внутри math.floor.
Никаких обид, естественно 🙂
Странно, что-то в квике нет даты числового типа. Функции преобразования типов медленные. Если уж и получать дату в виде числа, то так:
yyyymmdd = tonumber(os.date("%Y%m%d, os.time())) -- tonumber вызван всего 1 раз.
Плюс там в цикле вы меняете булевый флаг(isdst) дневного времени суток на nil, что при его использовании приведет к логической! трудно находимой ошибке.
А как по мне - posix надежнее, округляется как мне нужно, ну не суть.
Как работают os.time и os.date и что с ними можно и нужно делать: http://www.bot4sale.ru/blog-menu/qlua/368-lua-time.html
На счет минимальной кастомизации, сильно прям сомневаюсь.
А отсутствие проблем с "нужными" функциями соблюдается при единственном условии - 99,9% клиентов через неделю торгов на реальные деньги удалили робота, использующего эти функции и пошли устраиваться на работу, как то так.
По поводу math.floor - моя функция не занимается возведением в степень и выдает сразу желаемый результат, который для заявки достаточно привести к строковому типу данных без использования GetCorrectPrice - вот где тоже жесть, это даже не с бубном танцы, а с барабаном-тайко))).
В итоге вывод: если сайт создан для самообразования пользователей, то не нужно их учить сразу делать не правильно, потом им будет тяжело переучиваться или, что еще хуже - лень.
Мы получаем дату/время сервера в функции, а не компа, так что os.date с tonumber нам не помощник 🙂 А по поводу работы роботов никто из клиентов ещё не жаловался. По поводу долгого возведения в степень, мне кажется ты путаешь Квик с прямым подключением, здесь у нас HFT не пишут, где критичны микросекунды, здесь задачи тривиальные решаются, а лучшее, как известно, враг хорошего 🙂
А ещё, я считаю, ты не понимаешь суть задачи дать возможность человеку, который ничего в этом не понимает, простейший способ начать писать роботов. Ты вот вспомни себя, когда ты только пришел в Квик и программирование, представь что тебе сразу дали бы пример твоей смарт заявки, у тебя бы задымилась голова и ты бы с большой вероятностью ушел из этой темы, решив что это не твое 🙂 для новичков важен минимальный порог входа, а дальше можно все совершенствовать до бесконечности, ты сам тому пример. Сейчас наши программисты при выполнении заказов используют свои функции, которые они взращивали годами и продолжают взращивать, но все они начинали с простых вещей и с простых функций 🙂
Отнюдь) я ни чего не путаю, разницы нет, какую дату/время получать, сервера или компа.
А по поводу работы роботов никто жаловаться и не будет - чего жаловаться, если стратегия не работает.
По поводу HFT готов поспорить, дело не в самой скорости, у меня есть роботы, которые несколько раз в день включаются и чего-то там быстро считают. И тут ключевое слово - быстро, т.к. мне важно не загружать проц бесполезными расчетами.
И да код на Lua нужно писать таким образом, что бы была возможность его быстро портировать на С.
Вот видишь, ты свой подход хочешь навязать как общепринятый, но, по опыту работы с клиентами, этот подход далеко не распространен, у наших клиентов другие цели.
Суть задачи это научить, согласен. Я помню как я начинал писать, голова дымилась это точно.
Но если бы мне тогда дали мой смарт, дыму бы не убавилось это точно, но года 3-4 я бы сэкономил, и лишь это! важно.
Ты же предлагаешь всем пройти путь, который мы прошли сами (минус 3-4 года жизни), а это не правильно.
С таким подходом можно было дать ребенку азбуку, объяснить, что из букв можно составить слова, а из слов предложения, а дальше пусть сам додумывает.
Т.е. используя твой метод обучения, мы бы еще жили в каменном веке и вообще бы не парились с роботами.
Естественно, я хочу навязать свой подход, стандарты это хорошо, без них далеко не уедешь.
Путь всегда один, от простого к сложному, и это не потому что мы так захотели, так природа человека устроена, он развивается эволюционно. У меня здесь уровень для старта, дальше каждый идет своей дорогой, обращаясь за советом, по необходимости. Я не могу здесь выложить следующие шаги для развития, т.к. дальше каждый путь индивидуален и их огромное множество, продумывать, прорабатывать и выкладывать все возможные варианты - это безумие, по моему мнению 🙂
Если хочешь внести свой вклад, то напиши статью, в которой донеси в понятной для широкой общественности форме свои подходы, то, к чему ты пришел за эти годы.
Возможно, наберу пива по-больше и напишу, а может будет лень, тогда увы и ах)