Тема: Опционы

Недавно занимаюсь опционами, набросал пару функций, может кому пригодится, может кто дополнит.

# forts.lua

local tonumber = tonumber
local math_abs, math_sqrt, math_exp, math_max, math_log = math.abs, math.sqrt, math.exp, math.max, math.log
local pi = math.pi

local forts = {}

local function N(x)
    -- Функция стандартного нормального распределения
   if x > 10 then return 1 end
   if x < -10 then return 0 end
   local ax = math_abs(x)
   local t = 1 / (1 + 0.2316419 * ax)
   local d = 1 / math_sqrt(2 * pi) * math_exp(-x * x / 2)
   local p = d * t * ((((1.330274429 * t - 1.821255978) * t + 1.781477937) * t - 0.356563782) * t + 0.31938153)
   if x > 0 then return 1 - p else return p end
end
function forts.Black_Scholes(F, S, V, T)
-- F: Текущая цена фьючерса
-- S: Страйк
-- V: Волатильность
-- T: Время в долях года до окончания срока действия опциона (4 / 365)
    if T <= 0 then
        local Call = math_max(F - S, 0)
        local Put = Call + S - F
        return Call, Put -- теоретические цены
    end
    local d1 = (math_log(F / S) + V * V * T / 2) / (V * math_sqrt(T))
    local d2 = d1 - V * math_sqrt(T)
   
  local Call = round(F * N(d1) - S * N(d2))
  local Put = Call + S - F
  return Call, Put
end

function forts.getFutInfo(base) -- base =  Si or RTS or BR
    --возвращает 2 инструмента, текущий и следующий
    local info = {}
    local count = 0
    for ticker in string.gmatch(getClassSecurities("SPBFUT"), "(%w+)") do
        if base == getParamEx("SPBFUT", ticker, "optionbase").param_image then
            local dtmd = 0 + getParamEx("SPBFUT", ticker, "days_to_mat_date").param_value
            if dtmd > -1 then
                count = count + 1
                info[count] = {dtmd, ticker}
                if count == 2 then break end
            end
        end
    end
    --первый элемент info инструмент с наименьшим числом дней до погашения
    --ticker_1 = info[1][2]
    table.sort(info, function(a, b) return a[1] < b[1] end)
    if #info > 0 then return info end
    
    assert(nil,"forts.getFutInfo не найдены фьючерсы по базовому активу "..tostring(base))
end
local function getOptKey(s)
    if not tonumber(s:sub(-1)) then return "W" end
    return "M"
end
function forts.getOptInfo(futcode, opttype, WM, strike)
    -- futcode = "Sim9"
    -- opttype = "Put" or "Call"
    -- WM = "W" or "M" (week/month)
    local info, count = {}, 0
    for ticker in string.gmatch(getClassSecurities("SPBOPT"), "(%w+)") do
        if    futcode == getParamEx("SPBOPT", ticker, "optionbase").param_image
        and    tonumber(getParamEx("SPBOPT", ticker, "strike").param_value) == strike then
            
            local dtmd = tonumber(getParamEx("SPBOPT", ticker, "days_to_mat_date").param_value) 
            if dtmd > -1
            and getParamEx("SPBOPT", ticker, "optiontype").param_image == opttype
            and getOptKey(ticker) == WM then
                count = count + 1
                info[count] = {dtmd, ticker}
                if count == 2 then
                    table.sort(info, function(a, b) return a[1] < b[1] end)
                    break
                end
            end
        end
    end
    if #info > 0 then return info end
    
    assert(nil,"forts.getOptInfo не найдены опционы по активу "..tostring(futcode)..
    "\n opttype "..tostring(opttype).."; WM "..tostring(WM).."; strike "..tostring(WM))
end
--Коды опционов
--C    P    K    M    Y W

--C – код базового актива, 2 символа;
--P – цена страйк, переменное количество символов;
--К – тип расчетов;
--M – месяц исполнения (а также тип для опциона), 1 символ;
--Y – год исполнения, 1 символ;
--W – признак недельного опциона, 1 символ or nil

local CP = {
    A = "M", M = "A",
    B = "N", N = "B",
    C = "O", O = "C",
    D = "P", P = "D",
    E = "Q", Q = "E",
    F = "R", R = "F",
    G = "S", S = "G",
    H = "T", T = "H",
    I = "U", U = "I",
    J = "V", V = "J",
    K = "W", W = "K",
    L = "X", X = "L",
}
-- что бы не вызывать getOptInfo более 1 го раза
function forts.OppOpt(ticker)
    -- замена инструмента по типу: Call >> Put or Put >> Call
    local x = ticker:sub(-1)
    local W = (tonumber(x) and "") or x
    local C, P, K, M, Y = ticker:match("(%a%a)([%d%.]+)(%a)(%a)(%d)(%a-)")
    return C..P..K..CP[M]..Y..W
end
function forts.OppStrike(ticker, strike)
    -- замена инструмента по страйку: "Si66250BN9A" >> "Si67500BN9A"
    local x = ticker:sub(-1)
    local W = (tonumber(x) and "") or x
    local C, _, K, M, Y = ticker:match("(%a%a)([%d%.]+)(%a)(%a)(%d)(%a-)")
    return C..strike..K..M..Y..W
end

return forts