Здесь буду выкладывать функции, которые могут пригодиться:
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
Код вставляется не полностью
Вот сделал такую функцию для поиска окна графика по названию для проверки взял название SiZ0 График цены и объёма #22 и сгенерировал вторую часть таймфрейм и минуты, но думаю что первую часть генерировать не получится, поэтому хочу просто в настройки прописывать и сохранять первую часть. Окно не находит. Что не так ? Но как это окно активировать, то есть открыть(также как в менеджере окон?
А если график объема не #22? Не найдет ничего.
И так ничего не находит через WinSpy пытаюсь вычислить все графики находятся в этом классе MDIClient
https://wasm.in/blogs/win32-api-urok-32-mdi-interfejs.46/
Подскажите пожалуйста, есть такие функции в квике и если нет возможно как то это реализовать?
1. Поиск и (или) открытие окна графика в квике через Lua по идентификатору или как то еще
Попробуйте 'lua_multilist' https://quik2dde.ru/viewtopic.php?id=304
Если график уже открыт, но свернут, то можно найти соответствие в заголовке графика(например "RIZ0 График цены и объёма") и его восстановить.
Если графика нет, то уже сложнее, найти инструмент в таблице текущих торгов или в доске опционов и через контекстное меню открыть график.
Спасибо kalikazandr, попробую разобраться с этим
Не за что,
я пользую 'lua_multilist' для установки соединения(автологин) и обработки системных сообщений (ошибки всякие, типа - нельзя установить соединение).
Графики не открываю програмно - нет нужды, и не будет точно, достаточно 1 графика для торговли(визуализации), который связан якорем с "таблицей текущих торгов" и одного стакана.
Если вам для стратегии нужно больше графиков и бот от них зависит - это нужно выкинуть бота в помойку и продумать алгоритм, который не зависит от открытых графиков или стаканов или еще чего.
Робот не должен завязываться на всякую хрень.
Задача робота - бабло, чем меньше прослойка, между "бабло" и необходимой для этого функциональностью, тем надёжнее будет бабло.
Иначе, с вероятностью в 100% будет минус бабло. Как то так.
Если честно не представляю ка можно торговать без графика и индикаторов, можно наверно получать все в скрипте не открывая графиков, но торговать без индикаторов понятия не имею как можно, то о чем вы говорите похоже на какой то волшебный грааль ))
Индикаторы основываются на исторических данных - было, да былью поросло.
Да еще и обезличенные сделки (и не только), по которым строятся графики, переписывают каждый клиринг, что бы скрыть крупных ММ и что бы графики были "красивыми" для всяких там индикаторов.
Пользоваться графиками и индикаторами в принципе плохая затея. Это вроде гадания на кофейной гуще.
Невозможно определить куда двинет цена с никакой вероятностью.
Будет "отбой" от уровня или будет "пробой" уровня - не знают даже те, кто сейчас двигает цену.
Проще прогноз погоды послушать - более достоверная информация.
Ну или заняться фундаментальным анализом, покататься по предприятиям, посмотреть, чем они дышат.
А на что тогда ориентироваться если все не подходит, графики и индикаторы дают хоть какую то вероятность дальнейшего движения. Закономерности индикаторов существуют все равно и они работают, да может быть вход будет не по идеальным ценам но вероятность продолжения движения все равно есть. Если без них то работать придется либо с вероятностью 50-50 либо в обще без ее учета, что в принципе одно и тоже что 50-50. Ну и соответственно на комиссии прогоришь в итоге
Простому смертному трейдеру который торгует через квик остается лишь следовать за рынком то есть за ценой и объемами которая и формирует значения индикаторов, а цена как известно учитывает всю существую на данный момент информацию и ожидание информации в будущем, или какое то текущее событие или которое должно произойти в будущем. Рынок живет ожиданиями. И если появился сигнал это значит что вероятность уже сместилась в большую сторону, а на сколько уйдет цена это уже вопрос статистики.
С какой вероятностью будет 1 или 2 из [1; 2]? 50х50 или как то иначе?
Например, 2 не будет никогда и это нормально с точки зрения вероятности.
Строить прогнозы 50х50 или вообще говорить о смещении вероятности в какую-то сторону - полная чушь, как и ожидания.
Вероятности вообще пофик до людишек с их прогнозами.
А рынок - да, живет ожиданиями, только оправдываются ожидания у 1-2% трейдеров.
Вот такая есть книга не знаяю не читал еще но судя по оглавлению есть что почитать
Изображение:

Изображение:
БулашевС.В.Б 91Статистикадлятрейдеров. -М.: КомпанияСпутник+,2003. - 245с.ISBN 5-93406-577-7
А счет у него растет? или так, просто про арифметику?
Если человек пишет книгу о трединге, скорее всего, в трейдинге у него не срослось, иначе не писал бы.
Т.к. на трейдинг можно потратить много времени и ничего более не уметь или не хотеть, то писанина - самое то.
Интернет пестрит всякими гуру-бумагомараками и с докторскими степенями хватает тоже гуру, которые слили пару раз депо и учат других, как не сливать.
Я бы не был столь категоричен на счет индикаторов. Зарабатывают люди. Вот пример эквити, человек на купайле до сих пор торгует. Куча графиков у него открыты для ботов с простейшими индикаторами. Даже на 8 квик не перешел, как-то обошел потребность в 19 значных номерах заявок.

Изображение:
Что то картинки эквити не видно, но если кому-то интересно, можно щелкнуть правой кнопкой на квадратики изображение и выбрать "открыть картинку в новой вкладке". Так у меня загрузилось.
Вот это что серьезно так делают ?
"Да еще и обезличенные сделки (и не только), по которым строятся графики, переписывают каждый клиринг, что бы скрыть крупных ММ и что бы графики были "красивыми" для всяких там индикаторов."
Знаю что есть заявки типа айсберг, которые и так размазывают объемы но чтобы переписывать сделки, интересно у какого брокера это происходит?
При чем тут брокеры? Биржа у нас такая честная. А брокеры еще честнее.
быстрый поиск информации по инструменту
Перешел на Quik 8.5+ и столкнулся, как раз с проблемой, что ["PRICE"] = tostring(price), передает строку 5600.0 т.е. есть потребность менять ''.' на ','. Попробовал функцию GetCorrectPrice() . Она выдает ошибку Syntax error while compiling ... invalid escape sequence near ''[\.' Это в строке:
string.gsub(tostring(price),'[\.]+', ',')
Т.е. как я понимаю, надо "заэкранировать" точку каким-то образом? Может в луа 5.3 этот вариант не работает?
Разобрался там видимо опечатки в коде были.
Я тоже столкнулся с этой проблемой. Я в луа не силён, подскажешь как исправить?
a, b = string.gsub(tostring(75.45), '%.', ',') --где b = число замен
У меня работают варианты, '%.+' или '%.' или '[.]+' или '[.]' Вместо '[\.]+', ',')
Для отладки добавил функции: arr_to_str и msg
увидел, что как то сохранилось криво или не там скопировал)
да не, таки криво сохраняет в 21-й строке заменить "0 then" на ") then"
Здравствуйте. Функция не возвращает код класса: "STOCK_USA" и "FQBR" ("SPBXM" - не проверял). В чем подвох?
На форуме квика посоветовали регулярные выражения переписать - попробовал - не получается
Привет, все проще
local function getClassCode(ticker) -- Функция возвращает код класса по коду инструмента
for i= 0, getNumberOf("securities")-1 do
local item = getItem("securities", i)
if item.code == ticker then
-- message("getClassCode: "..tostring(item.class_code).." "..ticker,2)
return item.class_code
end
end
return ""
end
Спасибо, Дмитрию, с его помощью так написал
это танец с бубном ))
kalikazandr ваш код элегантнее и эффективнее, но тикер может быть в нескольких классах.
Поэтому можно предложить небольшую модификацию вашего кода:
согласен, ваша правда, но список классов по тикеру ничего особо не даст, т.к. нет параметра, который будет уточнять правило поиска, например, что бы различить акции от неполных лотов этих акций
Костя, вот так тебе пойдет