Вот скрипт попроще, который довольно быстро воспроизводит эту проблему:
Скрытый текст
Код
class_code, sec_code = "SPBFUT", "SiH1"
local run = true
local logFile
function OnStop()
run = nil
end
local function WriteLog(message)
local t = os.sysdate()
local timestamp = string.format("%02u:%02u:%02u.%03u", t.hour, t.min, t.sec, t.ms)
logFile:write(timestamp .. " " .. message .. "\n")
logFile:flush()
end
function main()
logFile = io.open("ds_test.log", "a+")
WriteLog("subscribing for D1")
local dsd = CreateDataSource(class_code, sec_code, INTERVAL_D1)
while dsd:Size() == 0 do
sleep(100)
end
sleep(200)
WriteLog("D1 filled: " .. dsd:Size())
local count = 10
while run do
WriteLog("subscribing for H2 & H4")
local ds2 = CreateDataSource(class_code, sec_code, INTERVAL_H2)
local ds4 = CreateDataSource(class_code, sec_code, INTERVAL_H4)
while ds2:Size() == 0 or ds4:Size() == 0 do
sleep(100)
end
sleep(200)
WriteLog("H2 filled: " .. ds2:Size())
WriteLog("H4 filled: " .. ds4:Size())
local vol2, vol4
repeat
local vol4 = ds4:V(ds4:Size())
local vol2 = ds2:V(ds2:Size()) + (ds4:T(ds4:Size()).hour == ds2:T(ds2:Size()).hour and 0 or ds2:V(ds2:Size() - 1))
if vol4 == vol2 then
WriteLog("V4 = " .. vol4 .. " V2 = " .. vol2)
break
end
WriteLog("error: V4 = " .. vol4 .. " V2 = " .. vol2 .. " difference = " .. (vol4 - vol2))
sleep(1000)
count = count - 1
if count == 0 then
run = false
end
until not run
WriteLog("closing H2 & H4")
ds2:Close()
ds2 = nil
ds4:Close()
ds4 = nil
end
dsd:Close()
logFile:close()
logFile = nil
end
Уведомление о необходимости обновления торговых терминалов в связи с изменениями на срочном рынке Московской биржи, Список проблем при работе устаревших версий QUIK после обновления торговой системы срочного рынка МБ
В новости написано, что на тестовом полигоне срочного рынка уже включена 19-значная нумерация. При этом на демо-сервере Арки по-прежнему используются короткие номера заявок и сделок. У нас будет возможность протестировать работу терминала и скриптов с длинными номерами на демо-сервере?
Денис написал: Что Вы имеете ввиду под общим объемом заказанных данных?
Кроме сделок есть и другие данные - текущие параметры, стаканы и т.д. Они сохраняются в файл info.log, и по суммарному объёму info.log и alltrade.dat можно грубо оценить размер использованной памяти. 69 + 318 Мб это вполне нормально.
Я встречал отзывы, что его нужно перезапускать хотя бы раз в неделю, иначе появляются проблемы. Сам перезапускаю каждый день, поэтому у меня таких проблем не было.
Денис написал: Созвонившись с БКС мне сказали, что очень большой объем данных и желательно каждую неделю удалять все файлы с расширением .dat.
Вам неправильно сказали. Эти файлы автоматически очищаются при смене сессии на сервере, то есть как минимум каждый торговый день.
Я не думаю, что таблица обезличенных сделок является причиной проблемы. У меня, например, в том же БКС заказывается весь срочный, весь валютный и большая часть фондового рынка. Размер файла alltrade.dat, хранящего обезличенные сделки, в конце дня достигает 400-500 Мб, а в волатильные дни вроде 9 апреля бывает и больше 900 Мб. При этом зависаний терминала ни разу не было.
Возможно, у вас общий объём заказанных данных слишком большой - посмотрите, сколько памяти использует процесс Квика в конце дня, и какой размер файлов info.log и alltrade.dat.
Виктор Столетов написал: Все же при расчете прибыли/убытка по позиции у меня не сходится с остатком собственных средств, когда позиция закрывается после 19 ч (реальный остаток немного меньше расчетного). Брокерскую и биржевую комиссию при этом учитываю в равной мере за открытие и закрытие. Может беру в расчет не тот индикативный курс? К примеру, если позиция по фьючерсу на нефть закрылась 15.02.2018 в 19-10, то какой индикативный курс надо брать для расчета прибыли/убытка – курс основного клиринга на 18-30 или промежуточного клиринга на 13-45 на дату 16.02.2018? По идее надо брать курс на 18-30.
Это позиция, существующая несколько торговых дней. Для неё нужно рассчитывать вариационную маржу отдельно за каждый торговый день.
Например: купили один контракт BR-3.18 по 63.90 в 18:05 15.02.18, потом прошёл вечерний клиринг и определена расчётная цена 63.30, потом закрыли эту позицию продажей по 63.43 в 19:10 15.02.18.
Вариационная маржа за торговый день 15.02.18 будет равна: round(63.30 * round(5.6491 / 0.01; 5); 2) - round(63.90 * round(5.6491 / 0.01; 5); 2) = -338.95 Вариационная маржа за торговый день 16.02.18 будет равна: round(63.43 * round(5.62582 / 0.01; 5); 2) - round(63.30 * round(5.62582 / 0.01; 5); 2) = 73.14 Итого вариационная маржа по позиции будет 73.14 - 338.95 = -265.81 Биржевые комиссии - 1.45 за открытие и 1.43 за закрытие.
Когда начисляется и списывается со счета эта комиссия - в дату заключения сделки? в дату закрытия сделки? в каждый клиринг, включая дневной (если сделка длится несколько дней)?
Сначала по терминологии: сделка не может длиться несколько дней, она совершается мгновенно, когда ваша заявка сводится в стакане с другой заявкой противоположного направления. В результате сделки появляется открытая позиция, которая будет существовать, пока вы её не закроете другой сделкой противоположного направления, или пока не наступит экспирация фьючерса. Комиссия за заключение сделки списывается в ближайший клиринг. Комиссия за исполнение фьючерса на экспирации - в клиринг, когда произошло исполнение. За удержание открытой позиции комиссия не берётся. Обратите внимание, комиссия за заключение сделки и комиссия за исполнение - это разные комиссии.
Цитата
Верно ли то, что комиссия за клиринг по фьючерсам на российские акции равна 0,00425% от суммы контракта?
Комиссия за заключение сделки равна 0.006% от расчётной цены последнего вечернего клиринга с учётом округления до копеек.
Цитата
Чему равна комиссия по фьючерсу на золото - 1 или 2 руб?
Комиссия за заключение сделки равна 0.004% от расчётной цены, переведённой в рубли, с учётом округлений. Комиссия за исполнение - 1 рубль за контракт.
Цитата
Где я могу посмотреть комиссию за клиринг по своим сделкам? (в брокерском отчете, в личном кабинете в реестре комиссий и в отчетах в Quik эта комиссия не указана)
В таблице сделок столбец "комиссия ТС" содержит комиссию за заключение этой сделки. Только сейчас квик некорректно отображает комиссию по скальперским сделкам - все сделки считает обычными. В таблице ограничений по клиентским счетам в столбец "Биржевые сборы" во время вечернего клиринга записывается суммарная комиссия за день. В брокерском отчёте тоже должна быть хотя бы общая комиссия за день, иначе итоговая сумма не сойдётся.
Цитата
В "Тарифах клирингового центра" в п. 36.4 на стр. 65 говорится, что комиссия начисляется в дату ЗАКЛЮЧЕНИЯ сделки. В другом документе "Тарифы * кредитной организации ..." в п. 7 раздела V говорится, что комиссия начисляется в дату ИСПОЛНЕНИЯ фьючерсного контракта.
В первом случае речь идёт о комиссии за заключение сделки, во втором - за исполнение контракта.
Цитата
По золоту в упомянутом разделе V тарифов указано 1 руб., а в таблице "клиринговые сборы за исполнение срочных контрактов (вызывается по одноименной ссылке в п. 4 сайта) - 2 руб.
Таблица "клиринговые сборы..." неправильная. Смотрите либо документы на сайте НКЦ, либо спецификацию контрактов на сайте биржи.
Во-первых, если вы перезаписали lua5.1.dll, то восстановите ту версию, которая поставлялась вместе с Квиком. Во-вторых, имя функции регистрации библиотеки должно иметь вид luaopen_имябиблиотеки, а у вас функция называется luaopen_QluaCSharpConnector, а библиотека - QluaCharpConnector.
В обезличенных сделках есть поля datetime и settlecode. Сделки вчерашней вечерней сессии отделяются от сегодняшних по дате. Сегодняшние дневные сделки в settlecode содержат пустую строку, а сегодняшние вечерние - значение "T1".
В своих сделках всё то же, и есть ещё параметр settle_date, содержащий дату ближайшего клиринга для этой сделки.
Ещё я посмотрел по логам, что происходило во время сбоя 15.10.2015, когда дневную сессию продлили до 19:25, а вечерняя началась в 19:45. Тогда параметр settle_date содержал корректные значения для всех сделок. А вот settlecode в первые 15 минут вечерней сессии был пустым и только в 20:00 стал равным "T1".
Логов ТВС за тот день у меня нет, но подозреваю, что с settlecode в ней было то же самое.
Если функцию sendTransaction вызвать одновременно из двух потоков - один вызов из main, а другой из функции обратного вызова, то на корректную транзакцию может прийти ответ с ошибкой "Неверный формат заявки".
Ниже приведён скрипт, воспроизводящий проблему. В нём нужно перед запуском заменить номер счёта в первой строке. Для появления ошибки может потребоваться несколько запусков скрипта.
В терминале версии 7.2.2.3 ошибки не было. В версиях 7.4, 7.5 и 7.6 ошибка есть.
Скрипт:
Скрытый текст
Код
local accountName = "SPBFUT00000" -- заменить на правильный номер счёта
local classCode = "SPBFUT"
local secCode = "SRZ6"
local priceMin = nil
local logFile = nil
local running = true
local testStarted = false
local activeOrders = {}
function Log(message)
if logFile ~= nil then
local timestamp = os.date("[%Y-%m-%d %X]")
logFile:write(timestamp .. " " .. message .. "\n")
logFile:flush()
end
end
function LogTable(message, tbl)
local res = {}
for key, value in pairs(tbl) do
local vtype = type(value)
if vtype == "nil" then
res[#res + 1] = key .. " = <nil>"
elseif vtype == "number" then
res[#res + 1] = key .. " = " .. value
elseif vtype == "string" then
res[#res + 1] = key .. " = " .. string.format("%q", value)
else
res[#res + 1] = key .. " = ?"
end
end
Log(message .. table.concat(res, "; "))
end
function PlaceOrders(price, count, id)
for i = 1, count do
local transaction = {
["ACTION"] = "NEW_ORDER",
["CLASSCODE"] = classCode,
["SECCODE"] = secCode,
["ACCOUNT"] = accountName,
["OPERATION"] = "B",
["PRICE"] = tostring(price),
["QUANTITY"] = "1",
["TRANS_ID"] = tostring(id),
}
LogTable("Placing order: ", transaction)
local message = sendTransaction(transaction)
Log("Result: " .. id .. ", [" .. message .. "]")
id = id + 1
end
end
function CancelOrders(id)
for ordernum, value in pairs(activeOrders) do
local transaction = {
["ACTION"] = "KILL_ORDER",
["CLASSCODE"] = classCode,
["SECCODE"] = secCode,
["ORDER_KEY"] = tostring(ordernum),
["TRANS_ID"] = tostring(id),
}
LogTable("Cancelling order: ", transaction)
local message = sendTransaction(transaction)
Log("Result: " .. id .. ", [" .. message .. "]")
id = id + 1
end
end
function OnTransReply(transaction)
LogTable("OnTransReply: ", transaction)
if not testStarted then
-- этот код вызывается только один раз для старта теста
Log("Starting test")
testStarted = true
-- ставим тестовые заявки
PlaceOrders(priceMin, 5, 3000)
end
if transaction.status == 3 then
activeOrders[transaction.order_num] = true
else
-- если попали сюда, значит произошла ошибка
Log("ERROR: " .. transaction.result_msg)
end
end
function OnStop()
running = false
end
function GetPriceMin()
local param = getParamEx(classCode, secCode, "pricemin")
if param == nil or param.result ~= "1" then
return 0
end
local value = tonumber(param.param_value)
if value == nil then
return 0
end
Log("pricemin = " .. value)
return value
end
function OnInit(scriptName)
logFile = io.open(scriptName .. ".log", "at")
end
function main()
-- получаем нижний лимит цены
priceMin = GetPriceMin()
while running and priceMin == 0 do
sleep(100)
priceMin = GetPriceMin()
end
-- отправляем первую заявку
PlaceOrders(priceMin, 1, 1000)
-- активное ожидание начала теста
while not testStarted do
end
-- ставим тестовые заявки
PlaceOrders(priceMin, 5, 2000)
-- ждём исполнение всех транзакций
sleep(2000)
-- снимаем все заявки
CancelOrders(4000)
-- ждём исполнение всех транзакций
sleep(2000)
logFile:close()
logFile = nil
end