суббота, 21 мая 2022 г.

Lua, Script, Sint, Bonds

Sint, Bonds c Валютой


  1. -- Synthetic Bonds For QUIK
  2. -- v1.0
  3. -- (c) Aphelion, 2021
  4.  
  5. --[[
  6.     Для корректной работы скрипта необходимо в настройках заказать получение данных по классам:
  7.     SPBFUT (FORTS), TQBR (МБ ФР: T+ Акции и Др) и FQBR (МБ ФР: T+ Ин.Акции и Др)
  8.     Фильтры по инструментам и параметрам должны быть выключены.
  9. ]]--
  10.  
  11. local requiredYield = 8 -- Показывать синт. облигации с доходностью не ниже X % годовых
  12. local requiredDaysToMaturity = 30 -- Показывать синт. облигации со сроком жизни не меньше X дней
  13.  
  14. local symbols = {
  15.     -- Код фьючерса = {Класс Акции, Код Акции}
  16.  
  17.     AF = {'TQBR', 'AFLT'},
  18.     AL = {'TQBR', 'ALRS'},
  19.     CH = {'TQBR', 'CHMF'},
  20.     FS = {'TQBR', 'FEES'},
  21.     GZ = {'TQBR', 'GAZP'},
  22.     GK = {'TQBR', 'GMKN'},
  23.     HY = {'TQBR', 'HYDR'},
  24.     LK = {'TQBR', 'LKOH'},
  25.     MN = {'TQBR', 'MGNT'},
  26.     ME = {'TQBR', 'MOEX'},
  27.     MT = {'TQBR', 'MTSS'},
  28.     NM = {'TQBR', 'NLMK'},
  29.     NK = {'TQBR', 'NVTK'},
  30.     RN = {'TQBR', 'ROSN'},
  31.     RT = {'TQBR', 'RTKM'},
  32.     SP = {'TQBR', 'SBERP'},
  33.     SR = {'TQBR', 'SBER'},
  34.     SG = {'TQBR', 'SNGSP'},
  35.     SN = {'TQBR', 'SNGS'},
  36.     TT = {'TQBR', 'TATN'},
  37.     TN = {'TQBR', 'TRNFP'},
  38.     VB = {'TQBR', 'VTBR'},
  39.     MG = {'TQBR', 'MAGN'},
  40.     PZ = {'TQBR', 'PLZL'},
  41.     YN = {'TQBR', 'YNDX'},
  42.     AK = {'TQBR', 'AFKS'},
  43.     IR = {'TQBR', 'IRAO'},
  44.     PO = {'TQBR', 'POLY'},
  45.     TI = {'TQBR', 'TCSG'},
  46.     FV = {'TQBR', 'FIVE'},
  47.     ML = {'TQBR', 'MAIL'},
  48.     OZ = {'TQBR', 'OZON'},
  49.     PI = {'TQBR', 'PIKK'},
  50.  
  51.     BA = {'FQBR', 'BABA-RM'},
  52.     BI = {'FQBR', 'BIDU-RM'}
  53. }
  54.  
  55. local running = true
  56. local bondsTable = nil
  57.  
  58. local index = {}
  59.  
  60. function main()
  61.     local result = indexFutures()
  62.     if not result then
  63.         message('SBonds: Failed To Index Futures', 3)
  64.  
  65.         do return end
  66.     end
  67.  
  68.     createSyntheticBondsTable()
  69.  
  70.     while running do
  71.         calculateYields()
  72.  
  73.         sleep(1000)
  74.     end
  75. end
  76.  
  77. function indexFutures()
  78.     local SPBFUT = getClassSecurities('SPBFUT')
  79.     local matches = string.gmatch(SPBFUT, '([^,]+)')
  80.  
  81.     local count = 0
  82.  
  83.     for futures in matches do
  84.         local futuresCode = string.sub(futures, 1, 2)
  85.  
  86.         if symbols[futuresCode] ~= nil then
  87.             if index[futuresCode] == nil then
  88.                 index[futuresCode] = {}
  89.  
  90.                 count = count + 1
  91.             end
  92.  
  93.             index[futuresCode][#index[futuresCode] + 1] = {
  94.                 code = futures,
  95.                 scale = getSecurityInfo('SPBFUT', futures).scale,
  96.                 size = getSecurityInfo('SPBFUT', futures).lot_size,
  97.                 expiration = string.format('%.0f', getParamEx('SPBFUT', futures, 'MAT_DATE').param_value)
  98.             }
  99.         end
  100.     end
  101.  
  102.     if count == 0 then
  103.         return false
  104.     end
  105.  
  106.     return true
  107. end
  108.  
  109. function calculateYields()
  110.     local tableIndex = {}
  111.  
  112.     local rows, _ = GetTableSize(bondsTable)
  113.     for i = 1, rows do
  114.         local cell = GetCell(bondsTable, i, 0)
  115.  
  116.         tableIndex[cell.image] = i
  117.     end
  118.  
  119.     for futuresCode, items in pairs(index) do
  120.         if symbols[futuresCode] ~= nil then
  121.             for _, item in pairs(items) do
  122.  
  123.                 local class = symbols[futuresCode][1]
  124.                 local stock = symbols[futuresCode][2]
  125.  
  126.                 if isTrading(class, stock) and isTrading('SPBFUT', item.code) then
  127.                     local futuresBid = tonumber(getParamEx('SPBFUT', item.code, 'BID').param_value)
  128.                     local stockAsk = tonumber(getParamEx(class, stock, 'OFFER').param_value)
  129.  
  130.                     if futuresBid > 0 and stockAsk > 0 then
  131.                         local term = timeToMaturity(item.expiration)
  132.                         local yield = (futuresBid - stockAsk * item.size) / term / (stockAsk * item.size) * 100
  133.                    
  134.                         if term >= requiredDaysToMaturity / 365 and yield >= requiredYield then
  135.                             local bond = stock .. ' x ' .. item.code
  136.  
  137.                             local row = tableIndex[bond]
  138.                             if row == nil then
  139.                                 row = InsertRow(bondsTable, -1)
  140.  
  141.                                 SetCell(bondsTable, row, 0, bond)
  142.                             end
  143.  
  144.                             local stockScale = getSecurityInfo(class, stock).scale
  145.  
  146.                             SetCell(bondsTable, row, 1, string.format('%.' .. stockScale .. 'f', stockAsk), stockAsk)
  147.                             SetCell(bondsTable, row, 2, string.format('%.' .. item.scale .. 'f', futuresBid), futuresBid)
  148.                             SetCell(bondsTable, row, 3, string.format('%.0f', term * 365), term * 365)
  149.                             SetCell(bondsTable, row, 4, string.format('%.2f', yield) .. '%', yield)
  150.  
  151.                             tableIndex[bond] = nil
  152.                         end
  153.                     end
  154.                 end
  155.             end
  156.         end
  157.     end
  158.  
  159.     for _, row in pairs(tableIndex) do
  160.         DeleteRow(bondsTable, row)
  161.     end
  162. end
  163.  
  164. function OnStop()
  165.     DestroyTable(bondsTable)
  166.     running = false
  167.  
  168.     return 0
  169. end
  170.  
  171. function createSyntheticBondsTable()
  172.     bondsTable = AllocTable()
  173.  
  174.     AddColumn(bondsTable, 0, 'Bond', true, QTABLE_STRING_TYPE, 17)
  175.  
  176.     AddColumn(bondsTable, 1, 'Stock Ask', true, QTABLE_DOUBLE_TYPE, 12)
  177.     AddColumn(bondsTable, 2, 'Futures Bid', true, QTABLE_DOUBLE_TYPE, 12)
  178.     AddColumn(bondsTable, 3, 'Days', true, QTABLE_DOUBLE_TYPE, 10)
  179.     AddColumn(bondsTable, 4, 'Yield', true, QTABLE_DOUBLE_TYPE, 12)
  180.    
  181.     CreateWindow(bondsTable)
  182.    
  183.     SetWindowCaption(bondsTable, 'Synthetic Bonds')
  184.     SetWindowPos(bondsTable, 0, 0, 460, 230)
  185.  
  186.     SetTableNotificationCallback(bondsTable, tableNotificationCallback)
  187. end
  188.  
  189. function tableNotificationCallback(sender, event, param1, param2)
  190.     if sender == bondsTable and event == QTABLE_CLOSE then
  191.         running = false
  192.     end
  193. end
  194.  
  195. function isTrading(class, security)
  196.     local status = tonumber(getParamEx(class, security, class == 'SPBFUT' and 'STATUS' or 'TRADINGSTATUS').param_value)
  197.  
  198.     return status == 1
  199. end
  200.  
  201. function timeToMaturity(string)    
  202.     local year = string.sub(string, 1, 4)
  203.     local month = string.sub(string, 5, 6)
  204.     local day = string.sub(string, 7, 8)
  205.  
  206.     local date = os.time({
  207.         day = tonumber(day),
  208.         year = tonumber(year),
  209.         month = tonumber(month),
  210.         hour = 18,
  211.         min = 45,
  212.         sec = 0
  213.     })
  214.  
  215.     local time = os.difftime(date, os.time()) / (365 * 24 * 60 * 60)
  216.  
  217.     return time
  218. end