Здесь буду выкладывать функции, которые могут пригодиться:
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 |
math_average() -- Возвращает среднее значение из переданных чисел
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| -- Возвращает среднее значение из переданных чисел
math_average = function(...)
local arg = {...}
local sum = 0
local count = 0
for i=1,#arg do
if type(arg[i]) == 'number' then
sum = sum + arg[i]
count = count + 1
end
end
if count == 0 then
return 0
else
return sum / count
end
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 |
GetMilliseconds() -- Возвращает количество миллисекунд
1
2
3
4
5
| -- Возвращает количество миллисекунд
GetMilliseconds = function()
local dt = os.sysdate()
return os.time(dt) * 1000 + dt.ms
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 os.date('*t', os.time()) end
local dt = GetServerDateTime()
local h,m,s = string.match( str_time, "(%d%d):(%d%d):(%d%d)")
dt.hour = tonumber(h)
dt.min = tonumber(m)
dt.sec = tonumber(s)
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
7
8
| ACCOUNT = 'SPBFUT00c41' -- Код счета
CLASS_CODE = 'SPBFUT' -- Код класса
SEC_CODE = 'RIM7' -- Код инструмента
LIMIT_KIND = 0 -- Тип лимита (акции), для демо счета должно быть 0, для реального 2
CLIENT_CODE = '' -- Код клиента, нужен для получения позиции по валюте
BALANCE_TYPE = 1 -- Тип отображения баланса в таблице "Таблица лимитов по денежным средствам" (1 - в лотах, 2 - с учетом количества в лоте)
-- Например, при покупке 1 лота USDRUB одни брокеры в поле "Баланс" транслируют 1, другие 1000
-- 1 лот акций Сбербанка может отображаться в таблице "Позиции по инструментов" в поле "Текущий остаток" как 1, или 10 |
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 math.floor(futures_client_holding.totalnet)
else
return math.floor(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 math.floor(futures_client_holding.totalnet)
else
return math.floor(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 math.floor(depo_limit.currentbal)
else
return math.floor(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 math.floor(depo_limit.currentbal)
else
return math.floor(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 math.floor(money_limit.currentbal)
else
return math.floor(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 math.floor(money_limit.currentbal)
else
return math.floor(money_limit.currentbal/lot)
end
end
end
end
end
-- Если позиция по инструменту в таблице не найдена, возвращает 0
return 0
end |
GetPosPrice() -- Получает цену текущей позиции по инструменту (АКЦИЯ, ФЬЮЧЕРС, ОПЦИОН)
Для работы функции в скрипте должны быть объявлены глобальные переменные:
1
2
3
4
5
6
7
| ACCOUNT = 'SPBFUT00c41' -- Код счета
CLASS_CODE = 'SPBFUT' -- Код класса
SEC_CODE = 'RIM7' -- Код инструмента
LIMIT_KIND = 0 -- Тип лимита (акции), для демо счета должно быть 0, для реального 2
BALANCE_TYPE = 1 -- Тип отображения баланса в таблице "Таблица лимитов по денежным средствам" (1 - в лотах, 2 - с учетом количества в лоте)
-- Например, при покупке 1 лота USDRUB одни брокеры в поле "Баланс" транслируют 1, другие 1000
-- 1 лот акций Сбербанка может отображаться в таблице "Позиции по инструментов" в поле "Текущий остаток" как 1, или 10 |
Так же, для работы функции в скрипте должна присутствовать функция:
GetTotalnet()
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
| -- Получает цену текущей позиции по инструменту
GetPosPrice = function()
-- Акции
if CLASS_CODE == 'TQBR' or CLASS_CODE == 'QJSIM' then
-- Перебирает таблицу "Позиции по инструментам"
local num = getNumberOf('depo_limits')
local depo_limit = nil
for i=0,num-1 do
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
return depo_limit.awg_position_price
end
end
-- Фьючерсы, опционы
elseif CLASS_CODE == 'SPBFUT' or CLASS_CODE == 'SPBOPT' then
local totalnet = GetTotalnet()
-- Если позиция есть
if totalnet ~= 0 then
local abs_totalnet = math.abs(totalnet)
local sum = 0
local sum_lots = 0
local trade = nil
-- Перебирает сделки
local num = getNumberOf('trades')
for i=num-1,0,-1 do
trade = getItem('trades', i)
if trade.sec_code == SEC_CODE then
if (totalnet < 0 and bit.test(trade.flags, 2)) or (totalnet > 0 and not bit.test(trade.flags, 2)) or totalnet == 0 then
sum = sum + trade.price*trade.qty
sum_lots = sum_lots + trade.qty
-- Если найдены все сделки набора позиции
if sum_lots >= abs_totalnet then
-- Возвращает среднюю цену
return sum/sum_lots
end
end
end
end
-- Не удалось найти все сделки набора позиции
-- Если найдены хоть какие-то сделки набора
if sum_lots > 0 then
-- Возвращает среднюю цену найденных
return sum/sum_lots
-- Сделок набора не найдено
else
-- Возвращает эффективную цену позиции
local num = getNumberOf('futures_client_holding')
if num > 0 then
-- Находит размер лота
local lot = tonumber(getParamEx(CLASS_CODE, SEC_CODE, 'LOTSIZE').param_value)
local futures_client_holding = nil
if num > 1 then
for i = 0, num - 1 do
futures_client_holding = getItem('futures_client_holding', i)
if futures_client_holding.sec_code == SEC_CODE and futures_client_holding.trdaccid == ACCOUNT then
return futures_client_holding.avrposnprice
end
end
else
futures_client_holding = getItem('futures_client_holding', 0)
if futures_client_holding.sec_code == SEC_CODE and futures_client_holding.trdaccid == ACCOUNT then
return futures_client_holding.avrposnprice
end
end
end
end
end
end
-- Если не удалось получить значение, возвращает цену последней сделки по инструменту
return tonumber(getParamEx(CLASS_CODE, SEC_CODE, 'LAST').param_value)
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 |
GetOrderAverTradesPrice() -- Возвращает среднюю цену сделок по заявке
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
| -- Возвращает среднюю цену сделок по заявке
GetOrderAverTradesPrice = function(order_num)
-- Находит кол-во исполненных лотов в заявке
local num = getNumberOf('orders')
local order = nil
local order_qty = 0
for i=num-1,0,-1 do
order = getItem('orders', i)
if order.order_num == order_num then
order_qty = order.qty - order.balance
break
end
end
-- Находит среднюю цену
local sum = 0
local find_lots = 0
local trade = nil
num = getNumberOf('trades')
for i=num-1,0,-1 do
trade = getItem('trades', i)
if trade.order_num == order_num then
sum = sum + trade.qty * trade.price
find_lots = find_lots + trade.qty
if find_lots == order_qty then break end
end
end
return sum/order_qty
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 = -1
while RUN and order_line_idx == -1 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
62
63
| -- Выставляет "Тейк профит" заявку
SetTP = function(
operation, -- Операция ('B', или 'S')
pos_price, -- Цена позиции, на которую выставляется стоп-заявка
qty, -- Количество лотов
profit_size -- Размер профита в шагах цены
)
-- Получает ID для следующей транзакции
trans_id = trans_id + 1
-- Получает минимальный шаг цены
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
94
95
| -- Выставляет "Тейк профит и Стоп лимит" заявку
SetTP_SL = function(
operation, -- Операция ('B', или 'S')
pos_price, -- Цена позиции, на которую выставляется стоп-заявка
qty, -- Количество лотов
profit_size, -- Размер профита в шагах цены
stop_size -- Размер стопа в шагах цены
)
-- Получает ID для следующей транзакции
trans_id = trans_id + 1
-- Получает минимальный шаг цены
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 |
CheckStopOrderCompleted() -- Проверяет по номеру исполнена ли стоп-заявка
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| -- Проверяет по номеру исполнена ли стоп-заявка
CheckStopOrderCompleted = 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 not bit.test(stop_order.flags, 0) and not bit.test(stop_order.flags, 1) 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
| -- Снимает заявку
KillOrder = function(
order_num -- Номер снимаемой заявки
)
-- Находит заявку
local index = 0
local find_order = false
while RUN and not find_order 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
-- Ожидает когда заявка перестанет быть активна
local active = true
while RUN 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
| -- Снимает стоп-заявку
Kill_SO = function(
stop_order_num -- Номер снимаемой стоп-заявки
)
-- Находит стоп-заявку
local index = 0
local find_so = false
while RUN and not find_so 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
-- Ожидает когда стоп-заявка перестанет быть активна
local active = true
while RUN 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
35
36
| -- Создает объект стека максимальным размером 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:Count()
-- Добавляет запись в стек
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
85
86
87
88
| -- Необходимые объявления для ускорения работы стека
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.Count = function(this)
return this.count
end
-- Добавляет запись в стек
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
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 |
StaticVar.dll -- Обмен данными между Lua-скриптами в QUIK
Источник:
quik2dde.ru
Готовая библиотека - распаковать в каталог с QUIK файл StaticVar.dll из архива и использовать
\x32\StaticVar.dll - для QUIK 6.x и 7.x версий
\x64\StaticVar.dll - для QUIK 8.x версий и более новых
Возможности:
Библиотека позволяет создать общее хранилище данных для нескольких Lua-скриптов, а также сохранять данные между запусками Lua-скрипта (в пределах одного запуска терминала QUIK!). Между запусками - сохраняйте в файлы самостоятельно.
Каждый элемент сохраняется в виде пары "Назначаемое имя параметра" - "Значение".
Поддерживается концепция "пространства имен" (Name Space): скрипт может назначить для себя текущий Name Space, после чего сохраняться и считываться данные будут только в пределах этого пространства. Имена данных, сохраняемых в разных Name Space, могут пересекаться, это будут разные данные. Если Name Space не назначен - используется "глобальное пространство" (по сути обособленное пространство имен nil). В любой момент текущее пространство имен может переключаться. (Name Space рекомендуется использовать только при надобности.)
Поддерживаются функции:
SetVar("var_name", var_data) -- создает переменную с именем "var_name" в текущем Name Space и записывает в нее значение var_data; если передать только 1 параметр или второй параметр задать равным nil - значение с именем "var_name" будет удалено из хранилища
GetVar("var_name") -- возвращает сохраненное значение с именем "var_name" из текущего Name Space; если переменной с указанным именем нет - вернет nil
UseNameSpace("NameSpace1") -- использовать пространство имен "NameSpace1"; если вызвать без параметров UseNameSpace() (или с пустой строкой "") - активным станет "неименованное" пространство имен; после старта скрипта Lua до вызова UseNameSpace с непустой строкой действует "неименованное" пространство имен
GetCurrentNameSpace() -- получить текущее пространство имен; возвращает строку, если какое-то пространство имет выбрано вызовом GetCurrentNameSpace, либо nil, если используется "неименованное" пространство
GetVarList() -- возвращает таблицу формата ["имя_переменной"]=значение, содержащую все установленные (и не удаленные) значения для текущего пространства имен; надо понимать, если заполнено много переменных-данных - то таблица будет большая, и ее заполнение окажется накладными
SetVarList(table) -- из переданной в качестве аргумента таблицы формата ["var_name"]=var_data заполняет данные с указанными именами в текущем пространстве имен; список сохраненных данных не заменяется, а лишь дополняется из таблицы
Clear() -- удаляет все сохранённые значения для текущего NameSpace (потребовалась для тестов)
ClearAll() -- все значения из всех NameSpace (потребовалась для тестов)
Корректно сохраняются (из var_data) и считываются следующие типы Lua:
string
boolean
number
table (любой вложенности)
данные остальных типов при сохранении преобразуется в строку "как получится".
Ограничения: передача данных между разными скриптами только в пределах одного процесса, т.е. одного QUIK;
Простой пример:
1
2
3
4
5
6
7
8
9
10
11
| -- Файл test-1.lua
require("StaticVar")
stv.UseNameSpace("testNS")
function main()
sleep(10000)
stv.SetVar("xxx", "xxx-test-value")
message("(test-1) " .. tostring(stv.GetCurrentNameSpace()) .. ".xxx=" .. tostring(stv.GetVar("xxx")), 1)
end |
1
2
3
4
5
6
7
8
9
10
| -- Файл test-2.lua
require("StaticVar")
stv.UseNameSpace("testNS")
function main()
sleep(10000)
message("(test-2) " .. tostring(stv.GetCurrentNameSpace()) .. ".xxx=" .. tostring(stv.GetVar("xxx")), 1)
end |
Запускаем сначала test-1.lua, затем test-2.lua. Чрез 10 сек. Будет выведено 2 сообщения:
(test-1) testNS.xxx=xxx-test-value - первый скрипт сохранил значение и отобразил его
(test-2) testNS.xxx=xxx-test-value - второй скрипт отобразил значение, сохраненное первым скриптом
SharedMemory - аналог StaticVar.dll написанный на Lua
Для обеспечения независимости от версии Quik было создано решение SharedMemory, которое позволяет иметь подобие общей памяти между скриптами.
Скачайте архив
SharedMemory.zip, распакуйте его. Создайте в корне терминала Quik папку
"SharedMemory", поместите в нее файл
SharedMemoryManager.lua и запустите его в терминале. Второй файл
SharedMemoryClient.lua из архива подключайте к скриптам, которым нужен данный функционал.
Пример использования (в примере файл SharedMemoryClient.lua находится в одной папке со скриптом):
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
| require('SharedMemoryClient')
Sharm = nil
RUN = true
main = function()
-- Добавление клиента, функция new_SharM принимает один параметр - номер клиента, должен быть уникальным от 1 до 100
Sharm = new_SharM(1)
-- Решение имеет всего 3 метода:
-- SetVar - устанавливает значение переменной, в примере значение 1 переменной 'Var1' пространства имен 'NS'
-- устанавливаемое значение может иметь тип boolean, number, string, либо table
Sharm:SetVar('NS', 'Var1', 1)
-- GetVar - получает значение переменной, в примере значение переменной 'Var1' из пространства имен 'NS'
local value = Sharm:GetVar('NS', 'Var1')
while RUN do
-- Значения могут быть изменены в любом из подключенных клиентов
message('Var1: '..tostring(Sharm:GetVar('NS', 'Var1')))
sleep(1000)
end
end
OnStop = function()
-- Delete - удаляет клиента (необязательно)
Sharm:Delete()
RUN = false
end |
СОХРАНЕНИЕ ПАРАМЕТРОВ СКРИПТА QLUA(LUA) МЕЖДУ ЗАПУСКАМИ
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
| main = function()
local FPath = getScriptPath()..'//.params'
-- Создание таблицы для примера
local Params = {}
Params.a = 10
Params.b = 'qwerty'
Params.c = true
Params.d = {}
Params.d[1] = 100
Params.d[2] = 'asdfg'
Params.d[3] = false
Params.d[4] = {
['a'] = 10,
['b'] = 'qwerty',
['c'] = true
}
-- Сохраняет таблицу в файл
SaveTable(Params, FPath)
-- Загружает таблицу из файла
local NewParams = LoadTable(FPath)
if NewParams ~= nil then message(NewParams.d[4].b) end -- выведет "qwerty"
end
-- Сохраняет таблицу в файл
SaveTable = function(Table, FilePath)
local Lines = {}
local level = 0
function Rec(a)
local first = true
level = level + 1
local s = '' for i=1,level do s = ' '..s end
for key, val in pairs(a) do
if not first then Lines[#Lines] = Lines[#Lines]..',' end
local k = '[\''..key..'\']'
if type(key) == 'number' then k = '['..key..']' end
if type(val) ~= 'table' then
if type(val) == 'string' then
val = '\''..val..'\''
else
val = tostring(val)
end
table.insert(Lines, s..k..'='..val)
first = false
else
table.insert(Lines, s..k..'={')
first = false
Rec(val)
table.insert(Lines, s..'}')
level = level - 1
end
end
end
table.insert(Lines, 'local a = {')
Rec(Table)
table.insert(Lines, '}')
table.insert(Lines, 'return a')
local f = io.open(FilePath, 'w')
for i=1,#Lines do
f:write(Lines[i]..'\n')
f:flush()
end
f:close()
end
-- Загружает таблицу из файла
LoadTable = function(FilePath)
local func, err = loadfile(FilePath)
if not func then
message('Ошибка загрузки таблицы из файла: '..err)
return nil
else
return func()
end
end |
Данный пример создаст файл ".params" (можно открыть Блокнотом, это обычный текстовый файл, можете назвать его как угодно) со следующим содержимым:
local a = {
['a']=10,
['d']={
[1]=100,
[2]='asdfg',
[3]=false,
[4]={
['a']=10,
['c']=true,
['b']='qwerty'
}
},
['c']=true,
['b']='qwerty'
}
return a
Для правильного округления цены до шага цены, для некоторых инструментов требуется делитель integer
на примере Si, шаг цены у него 1, а getParamEx("SPBFUT", "SiM" "sec_price_step").param_value вернет 1.0
в итоге:
p = math.floor( (86200 + 300) / 1.0) * 1.0 --86500.0
можно получать корректный шаг цены:
function current_price_step(s)
s = tonumber(s)
local a, a1 = math.modf(s)
if a1 ~= 0 then return s end
return a
end
price_step = current_price_step(getParamEx("SPBFUT", "SiM" "sec_price_step").param_value) --> 1
p = math.floor( (86200 + 300) / price_step) * price_step --> 86500