/// <summary> /// Вычисление нескольких видов наклона улыбки и обновление исторической серии, /// которая потенциально может быть сохранена в глобальный кеш. /// При записи в кеш серия помещается в NotClearableContainer, чтобы выживать при очистке памяти. /// </summary> public static bool TryCalcAndWriteSkews(IContext context, NotAKnotCubicSpline smileSpline, bool useGlobal, bool allowGlobalWrite, int savePeriod, string futSymbol, DateTime optExpiry, double futPx, DateTime lastBarDate, TimeRemainMode tRemainMode, double plainTimeAsYears, double timeAsYears) { bool successSkew = false; var splineD1 = smileSpline.DeriveD1(); if (splineD1.TryGetValue(futPx, out var skewAtm)) { //string symbol = optSer.UnderlyingAsset.Symbol; // А. Записываем в Глобальный Кеш РАЗМЕРНЫЙ наклон (модель времени неважна) { string skewKey = IvOnF.GetSkewCashKey(futSymbol, optExpiry, SmileSkewMode.RawSkew, TimeRemainMode.PlainCalendar); var ivSkews = LoadOrCreateHistoryDict(context, useGlobal, skewKey); // Это просто запись на диск. К успешности вычисления наклона successSkew отношения не имеет successSkew = TryWrite(context, useGlobal, allowGlobalWrite, savePeriod, skewKey, ivSkews, lastBarDate, skewAtm); } // В. Записываем в Глобальный Кеш БЕЗРАЗМЕРНЫЙ наклон В БИРЖЕВОМ ВРЕМЕНИ (PlainCalendar) { double dSigmaDxExchange = SmileImitation5.GetDSigmaDx(futPx, plainTimeAsYears, skewAtm, 0); if (!DoubleUtil.IsNaN(dSigmaDxExchange)) { string skewKey = IvOnF.GetSkewCashKey(futSymbol, optExpiry, SmileSkewMode.ExchangeSkew, TimeRemainMode.PlainCalendar); var ivSkews = LoadOrCreateHistoryDict(context, useGlobal, skewKey); // Это просто запись на диск. К успешности вычисления наклона successSkew отношения не имеет successSkew = TryWrite(context, useGlobal, allowGlobalWrite, savePeriod, skewKey, ivSkews, lastBarDate, dSigmaDxExchange); } } // Д. Записываем в Глобальный Кеш БЕЗРАЗМЕРНЫЙ наклон В НАШЕМ ВРЕМЕНИ (в соответствии с tRemainMode) { double dSigmaDxRescaled = SmileImitation5.GetDSigmaDx(futPx, timeAsYears, skewAtm, 0); if (!DoubleUtil.IsNaN(dSigmaDxRescaled)) { string skewKey = IvOnF.GetSkewCashKey(futSymbol, optExpiry, SmileSkewMode.RescaledSkew, tRemainMode); var ivSkews = LoadOrCreateHistoryDict(context, useGlobal, skewKey); // Это просто запись на диск. К успешности вычисления наклона successSkew отношения не имеет successSkew = TryWrite(context, useGlobal, allowGlobalWrite, savePeriod, skewKey, ivSkews, lastBarDate, dSigmaDxRescaled); } } } // End if (splineD1.TryGetValue(futPx, out var skewAtm)) return(successSkew); }
/// <summary> /// Метод под флаг TemplateTypes.SECURITY, чтобы подключаться к источнику-БА /// </summary> public IList <double> Execute(ISecurity sec) { if (sec == null) { return(Constants.EmptyListDouble); } Dictionary <DateTime, double> hvSigmas; #region Get cache int barLengthInSeconds = (int)sec.IntervalInstance.ToSeconds(); string cashKey = GetGlobalCashKey(sec.Symbol, false, m_useAllData, barLengthInSeconds, m_annualizingMultiplier, m_period); if (UseGlobalCache) { object globalObj = Context.LoadGlobalObject(cashKey, true); hvSigmas = globalObj as Dictionary <DateTime, double>; // PROD-3970 - 'Важный' объект if (hvSigmas == null) { var container = globalObj as NotClearableContainer; if ((container != null) && (container.Content != null)) { hvSigmas = container.Content as Dictionary <DateTime, double>; } } if (hvSigmas == null) { hvSigmas = new Dictionary <DateTime, double>(); var container = new NotClearableContainer(hvSigmas); Context.StoreGlobalObject(cashKey, container, true); } } else { object locObj = Context.LoadObject(cashKey); hvSigmas = locObj as Dictionary <DateTime, double>; // PROD-3970 - 'Важный' объект if (hvSigmas == null) { var container = locObj as NotClearableContainer; if ((container != null) && (container.Content != null)) { hvSigmas = container.Content as Dictionary <DateTime, double>; } } if (hvSigmas == null) { hvSigmas = new Dictionary <DateTime, double>(); var container = new NotClearableContainer(hvSigmas); Context.StoreObject(cashKey, container); } } #endregion Get cache List <double> historySigmas = LocalHistory; if (m_reset || m_context.Runtime.IsFixedBarsCount) { historySigmas.Clear(); } int len = Context.BarsCount; // Локальный ключ можно всегда формировать вместе с периодом! string logsLocObjKey = VariableId + "_logs_" + m_period; object logsLocObj = m_context.LoadObject(logsLocObjKey); LinkedList <KeyValuePair <DateTime, double> > logs = logsLocObj as LinkedList <KeyValuePair <DateTime, double> >; #region Get cache // PROD-3970 - 'Важный' объект if (logs == null) { var container = logsLocObj as NotClearableContainer; if ((container != null) && (container.Content != null)) { logs = container.Content as LinkedList <KeyValuePair <DateTime, double> >; } } if (logs == null) { logs = new LinkedList <KeyValuePair <DateTime, double> >(); var container = new NotClearableContainer(logs); m_context.StoreObject(logsLocObjKey, container); } #endregion Get cache if (m_reset || m_context.Runtime.IsFixedBarsCount) { logs.Clear(); } if (len <= 0) { return(historySigmas); } // Типа, кеширование? for (int j = historySigmas.Count; j < len; j++) { IDataBar bar; try { // Наблюдал на реале странную картину (ВО ВРЕМЯ КЛИРИНГА!): // Context.BarsCount == 3843 && sec.Bars.Count == 3842 bar = sec.Bars[j]; // если бар технический (то есть имеет нулевой объём), пропускаем его // 2017-04-26 - На эксанте при запросе истории все бары имеют нулевой объём, но при этом большинство из них являются нормальными: // имеют ненулевой размах и осмысленные значения открытия/закрытия if ((bar.Volume <= Double.Epsilon) && (DoubleUtil.AreClose(bar.High, bar.Low))) { // пишу NaN в такой ситуации historySigmas.Add(Constants.NaN); continue; } } catch (ArgumentOutOfRangeException) { // Перехватываю aorEx и пишу NaN в такой ситуации historySigmas.Add(Constants.NaN); continue; } DateTime t = bar.Date; double v = bar.Close; double ln = Math.Log(v); Contract.Assert(logs != null, "Каким образом переменная logs оказалась null?"); logs.AddLast(new KeyValuePair <DateTime, double>(t, ln)); double hv; if (TryEstimateHv( logs, m_period, barLengthInSeconds, m_annualizingMultiplier, m_useAllData, out hv)) { double vol = hv; historySigmas.Add(vol); lock (hvSigmas) { hvSigmas[t] = vol; } } else { historySigmas.Add(Constants.NaN); } } // Попытку записи в глобальный кеш делаю только в самом конце всех вычислений // для экономии времени на сериализацию и запись lock (hvSigmas) { if (sec.Bars.Count > len - 1) { // Наблюдал на реале странную картину (ВО ВРЕМЯ КЛИРИНГА!): // Context.BarsCount == 3760 && sec.Bars.Count == 3759 bool success = IvOnF.TryWrite(m_context, UseGlobalCache, AllowGlobalReadWrite, GlobalSavePeriod, cashKey, hvSigmas, sec.Bars[len - 1].Date, historySigmas[historySigmas.Count - 1]); } return(new ReadOnlyCollection <double>(historySigmas)); } }
private IList <double> PrepareData(ISecurity sec, string expiryDate) { DateTime expiry; if ((!DateTime.TryParseExact(expiryDate, IvOnF.DateFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out expiry)) && (!DateTime.TryParseExact(expiryDate, IvOnF.DateFormat + " HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out expiry))) { string msg = String.Format("[{0}.{1}.PrepareData] Unable to parse expiration date '{2}'. Expected date format '{3}'.", Context.Runtime.TradeName, GetType().Name, expiryDate, IvOnF.DateFormat); m_context.Log(msg, MessageType.Warning, true); return(Constants.EmptyListDouble); } //// и тут я понял, что дату экспирации в любом случае надо задавать руками... //string cashKey = typeof(IvOnF).Name + "_ivExchangeSigmas_" + sec.Symbol + "_" + // expiryDate.Replace(':', '-'); string cashKey = IvOnF.GetCashKey(sec.Symbol, expiry, m_rescaleTime, m_tRemainMode); Dictionary <DateTime, double> ivSigmas = null; try { object globalObj = Context.LoadGlobalObject(cashKey, true); ivSigmas = globalObj as Dictionary <DateTime, double>; // PROD-3970 - 'Важный' объект if (ivSigmas == null) { var container = globalObj as NotClearableContainer; if ((container != null) && (container.Content != null)) { ivSigmas = container.Content as Dictionary <DateTime, double>; } } } catch (NotSupportedException nse) { string fName = "", path = ""; if (nse.Data["fName"] != null) { fName = nse.Data["fName"].ToString(); } if (nse.Data["path"] != null) { path = nse.Data["path"].ToString(); } string msg = String.Format("[{0}.PrepareData] {1} when loading 'ivSigmas' from global cache. cashKey: {2}; Message: {3}\r\n\r\nfName: {4}; path: {5}\r\n\r\n{6}", GetType().Name, nse.GetType().FullName, cashKey, nse.Message, fName, path, nse); m_context.Log(msg, MessageType.Warning, true); } catch (Exception ex) { string msg = String.Format("[{0}.PrepareData] {1} when loading 'ivSigmas' from global cache. cashKey: {2}; Message: {3}\r\n\r\n{4}", GetType().Name, ex.GetType().FullName, cashKey, ex.Message, ex); m_context.Log(msg, MessageType.Warning, true); } if (ivSigmas == null) { // Данного ключа в глобальном кеше нет? Тогда выход. // [{0}.PrepareData] There is no IV in global cache. Probably, you have to start agent 'Collect IV (ALL)' for security '{1}'. string msg = RM.GetStringFormat("OptHandlerMsg.GlobalIvOnF.CacheNotFound", GetType().Name, expiryDate, sec); if (m_context.Runtime.IsAgentMode && (!m_ignoreCacheError)) { throw new ScriptException(msg); // PROD-4624 - Андрей велит кидать исключение. } bool isExpired = true; if (m_context.Runtime.IsAgentMode) { int amount = sec.Bars.Count; DateTime today = (amount > 0) ? sec.Bars[amount - 1].Date : new DateTime(); isExpired = expiry.Date.AddDays(1) < today.Date; } // А если в режиме лаборатории, тогда только жалуемся и продолжаем. m_context.Log(msg, MessageType.Warning, !isExpired); // Если серия уже умерла, пишем только в локальный лог return(Constants.EmptyListDouble); } List <double> res = new List <double>(); int len = sec.Bars.Count; if (len <= 0) { return(res); } int oldResLen = res.Count; double prevIv = Double.NaN; for (int j = oldResLen; j < len; j++) { DateTime now = sec.Bars[j].Date; double iv; if (ivSigmas.TryGetValue(now, out iv) && (!Double.IsNaN(iv)) && (iv > 0)) { prevIv = iv; res.Add(iv); } else { if (m_repeatLastIv) { if (!Double.IsNaN(prevIv)) { iv = prevIv; } else { iv = Constants.NaN; if (j == 0) { #region Отдельно обрабатываю нулевой бар double tmp = Double.NaN; DateTime foundKey = new DateTime(1, 1, 1); // [2016-01-19] Когда история становится слишком длинной, это может вызывать проблемы // при итерировании в foreach. Потому что другой поток может в этот момент добавить новую точку в коллекцию. int repeat = 7; while (repeat > 0) { tmp = Double.NaN; foundKey = new DateTime(1, 1, 1); try { lock (ivSigmas) { foreach (var kvp in ivSigmas) { if (kvp.Key > now) { continue; } if (foundKey < kvp.Key) { foundKey = kvp.Key; tmp = kvp.Value; } } } repeat = -100; } catch (InvalidOperationException invOpEx) { repeat--; Thread.Sleep(10); if (repeat <= 0) { string msg = String.Format("[{0}.PrepareData] {1} when iterate through 'ivSigmas'. cashKey: {2}; Message: {3}\r\n\r\n{4}", GetType().Name, invOpEx.GetType().FullName, cashKey, invOpEx.Message, invOpEx); m_context.Log(msg, MessageType.Warning, true); throw; } } } if ((foundKey.Year > 1) && (!Double.IsNaN(tmp)) && (tmp > 0)) { iv = tmp; prevIv = iv; } #endregion Отдельно обрабатываю нулевой бар } } res.Add(iv); } else { res.Add(Constants.NaN); } } } return(res); }
/// <summary> /// Обработчик под тип входных данных OPTION_SERIES /// </summary> public IList <double> Execute(IOptionSeries optSer) { if (optSer == null) { string msg = "[IV ATM] (optSer == null)"; m_context.Log(msg, MessageType.Warning, false); return(Constants.EmptyListDouble); } Dictionary <DateTime, double> ivSigmas; #region Get cache DateTime expiry = optSer.ExpirationDate.Date; string cashKey = IvOnF.GetCashKey(optSer.UnderlyingAsset.Symbol, expiry, m_rescaleTime, m_tRemainMode); ivSigmas = LoadOrCreateHistoryDict(UseGlobalCache, cashKey); #endregion Get cache List <double> res; ISecurity sec = optSer.UnderlyingAsset; int len = sec.Bars.Count; if (len <= 0) { return(Constants.EmptyListDouble); } if (m_context.IsFixedBarsCount) { #region Ветка с ФИКСИРОВАННЫМ количеством баров double lastIv = Double.NaN; res = new List <double>(len); for (int j = 0; j < len; j++) { DateTime now = sec.Bars[j].Date; double iv; if ((ivSigmas.TryGetValue(now, out iv)) && (!Double.IsNaN(iv)) && (iv > 0)) { lastIv = iv; res.Add(iv); } else { if (m_repeatLastIv && (!Double.IsNaN(lastIv))) { res.Add(lastIv); } else { res.Add(Constants.NaN); } } } #endregion Ветка с ФИКСИРОВАННЫМ количеством баров } else { #region Ветка с нарастающим количеством баров res = LocalHistory; // PROD-1933 // 1. Выполняю очистку локального кеша в сценарии восстановления соединения после дисконнекта if (res.Count > len) { res.Clear(); } // 2. Ищу последнее валидное значение в кеше причем только если это может быть нужно double lastIv = Double.NaN; if (m_repeatLastIv) { for (int j = res.Count - 1; j >= 0; j--) { if ((!Double.IsNaN(res[j])) && (res[j] > 0)) { lastIv = res[j]; break; } } } for (int j = res.Count; j < len; j++) { DateTime now = sec.Bars[j].Date; double iv; if ((ivSigmas.TryGetValue(now, out iv)) && (!Double.IsNaN(iv)) && (iv > 0)) { lastIv = iv; res.Add(iv); } else { if (m_repeatLastIv && (!Double.IsNaN(lastIv))) { res.Add(lastIv); } else { res.Add(Constants.NaN); } } } #endregion Ветка с нарастающим количеством баров } Debug.Assert(res != null, "How is it possible (res == null)?"); Debug.Assert(res.Count == len, String.Format("Wrong res.Count. res.Count:{0}; expected len:{1}; IsFixedBarsCount:{2}", res.Count, len, m_context.IsFixedBarsCount)); FinInfo baseFinInfo = optSer.UnderlyingAsset.FinInfo; // Эта проверка намекает на проблемы с маркет-датой. if (baseFinInfo.LastPrice == null) { string msg = "[IV ATM] (baseFinInfo.LastPrice == null)"; m_context.Log(msg, MessageType.Warning, false); return(res); } try { double sigma; double futPx = baseFinInfo.LastPrice.Value; NotAKnotCubicSpline spline = PrepareExchangeSmileSpline(optSer, Double.MinValue, Double.MaxValue); if ((spline != null) && spline.TryGetValue(futPx, out sigma) && DoubleUtil.IsPositive(sigma)) { DateTime lastBarDate = sec.Bars[len - 1].Date; if (m_rescaleTime) { #region Зверская ветка по замене времени double ivAtm = sigma; DateTime expDate = optSer.ExpirationDate.Date + m_expiryTime; DateTime now = baseFinInfo.LastUpdate; // 1. Надо перевести волатильность в абсолютную цену // с учетом плоского календарного времени применяемого РТС double plainTimeAsYears; { double plainTimeAsDays; TimeToExpiry.GetDt(expDate, now, TimeRemainMode.PlainCalendar, false, out plainTimeAsDays, out plainTimeAsYears); } // 2. Вычисляем 'нормальное' время double timeAsDays, timeAsYears; TimeToExpiry.GetDt(expDate, now, m_tRemainMode, false, out timeAsDays, out timeAsYears); sigma = FinMath.RescaleIvToAnotherTime(plainTimeAsYears, ivAtm, timeAsYears); if (DoubleUtil.IsPositive(sigma)) { // Это просто запись на диск. К успешности вычисления волы success отношения не имеет bool success = TryWrite(m_context, UseGlobalCache, AllowGlobalReadWrite, GlobalSavePeriod, cashKey, ivSigmas, lastBarDate, sigma); // Теперь надо вычислить безразмерный наклон кодом в классе SmileImitation5 bool successSkew = TryCalcAndWriteSkews(m_context, spline, UseGlobalCache, AllowGlobalReadWrite, GlobalSavePeriod, optSer.UnderlyingAsset.Symbol, expiry, futPx, lastBarDate, m_tRemainMode, plainTimeAsYears, timeAsYears); } else { // Если перемасштабировать улыбку не получается придется эту точку проигнорировать // Надо ли сделать соответствующую запись в логе??? sigma = Constants.NaN; } #endregion Зверская ветка по замене времени } else { // Это просто запись на диск. К успешности вычисления волы success отношения не имеет bool success = TryWrite(m_context, UseGlobalCache, AllowGlobalReadWrite, GlobalSavePeriod, cashKey, ivSigmas, lastBarDate, sigma); } } else { sigma = Constants.NaN; } res[len - 1] = sigma; } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, false); return(res); } if (m_repeatLastIv) { if (DoubleUtil.AreClose(res[len - 1], Constants.NaN) || Double.IsNaN(res[len - 1]) || (res[len - 1] <= 0)) { // Итерируюсь с конца в начало пока не найду последний ненулевой элемент. // Использую его в качестве ВСЕХ последних значений ряда. for (int j = len - 1; j >= 0; j--) { if ((!DoubleUtil.AreClose(res[j], Constants.NaN)) && (!Double.IsNaN(res[j])) && (res[j] > 0)) { double lastIv = res[j]; for (int k = j + 1; k < len; k++) { res[k] = lastIv; } break; } } } } return(new ReadOnlyCollection <double>(res)); }
public void Execute(double price, double time, IOptionSeries optSer, ICanvasPane pane, int barNum) { int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (optSer == null)) { return; } // PROD-3577 pane.FeetToBorder2ByDefault = true; double futPx = price; double dT = time; if (!DoubleUtil.IsPositive(futPx)) { return; } if (!DoubleUtil.IsPositive(dT)) { return; } double sigma; IFunction smileFunc = IvOnF.PrepareExchangeSmileSpline(optSer, Double.MinValue, Double.MaxValue); if ((smileFunc == null) || (!smileFunc.TryGetValue(futPx, out sigma)) || (!DoubleUtil.IsPositive(sigma))) { //При работе с Эксанте и прочим Западом Биржевой улыбки не будет //Поэтому надо иметь 'План Б': подставить фиксированную волатильность 30%! sigma = DefaultSigma; // PROD-5968 - У биткойна совсем другой типичный уровень волатильности var parent = optSer.UnderlyingAsset; var secDesc = parent.SecurityDescription; var tp = secDesc.TradePlace; var dsClassName = tp?.DataSource?.GetType().Name; if (dsClassName == "DeribitDS") { sigma = DefaultSigmaDeribit; } else if (dsClassName == "ExanteDataSource") { if (tp.Id == "CME") { if (secDesc.ActiveType.IsFuture() && parent.Symbol.StartsWith("ES")) { sigma = DefaultSigmaEs; } } } } if (pane != null) { string expiryStr = optSer.ExpirationDate.ToString(TimeToExpiry.DateTimeFormat, CultureInfo.InvariantCulture); Rect rect = PrepareVieportSettings(expiryStr, futPx, dT, sigma); ApplySettings(pane, rect); if (ManageXGridStep) { var pairs = optSer.GetStrikePairs().ToArray(); int pLen = pairs.Length; if (pLen > 1) { double dK = pairs[1].Strike - pairs[0].Strike; if (pLen > 2) { int t = pLen / 2; // Делим нацело. Для pLen==3 получаем 1 dK = pairs[t + 1].Strike - pairs[t].Strike; } pane.XAxisStep = GetXAxisStep(dK); pane.XAxisDiviser = GetXAxisDivisor(dK); } } } }
/// <summary> /// Обработчик под тип входных данных OPTION_SERIES /// </summary> public double Execute(IOptionSeries optSer, int barNum) { double failRes = Constants.NaN; if (m_repeatLastIv) { failRes = Double.IsNaN(m_prevIv) ? Constants.NaN : m_prevIv; } Dictionary <DateTime, double> ivSigmas; #region Get cache DateTime expiry = optSer.ExpirationDate.Date; string cashKey = IvOnF.GetCashKey(optSer.UnderlyingAsset.Symbol, expiry, m_rescaleTime, m_tRemainMode); ivSigmas = LoadOrCreateHistoryDict(UseGlobalCache, cashKey); #endregion Get cache ISecurity sec = optSer.UnderlyingAsset; int len = sec.Bars.Count; if (len <= 0) { return(failRes); } DateTime lastBarDate = sec.Bars[barNum].Date; double iv; if ((ivSigmas.TryGetValue(lastBarDate, out iv)) && (!Double.IsNaN(iv)) && (iv > 0)) { m_prevIv = iv; return(iv); } else { int barsCount = ContextBarsCount; if (barNum < barsCount - 1) { // Если история содержит осмысленное значение, то оно уже содержится в failRes return(failRes); } else { #region Process last bar(s) FinInfo baseFinInfo = optSer.UnderlyingAsset.FinInfo; if (baseFinInfo.LastPrice == null) { string msg = "[IV ATM 2] (baseFinInfo.LastPrice == null)"; m_context.Log(msg, MessageType.Warning, false); return(failRes); } double futPx = baseFinInfo.LastPrice.Value; if (futPx <= Double.Epsilon) { return(failRes); } NotAKnotCubicSpline spline = null; try { spline = IvOnF.PrepareExchangeSmileSpline(optSer, Double.MinValue, Double.MaxValue); } catch (ScriptException scriptEx) { m_context.Log(scriptEx.ToString(), MessageType.Error, false); return(failRes); } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, false); return(failRes); } if (spline == null) { return(failRes); } try { double sigma; if (spline.TryGetValue(futPx, out sigma) && (sigma > 0)) { if (m_rescaleTime) { #region Зверская ветка по замене времени double ivAtm = sigma; DateTime expDate = optSer.ExpirationDate.Date + m_expiryTime; DateTime now = baseFinInfo.LastUpdate; // 1. Надо перевести волатильность в абсолютную цену // с учетом плоского календарного времени применяемого РТС double plainTimeAsYears; { double plainTimeAsDays; TimeToExpiry.GetDt(expDate, now, TimeRemainMode.PlainCalendar, false, out plainTimeAsDays, out plainTimeAsYears); } // 2. Вычисляем 'нормальное' время double timeAsDays, timeAsYears; TimeToExpiry.GetDt(expDate, now, m_tRemainMode, false, out timeAsDays, out timeAsYears); sigma = FinMath.RescaleIvToAnotherTime(plainTimeAsYears, ivAtm, timeAsYears); if (DoubleUtil.IsPositive(sigma)) { // Это просто запись на диск. К успешности вычисления волы success отношения не имеет lock (ivSigmas) { bool success = IvOnF.TryWrite(m_context, UseGlobalCache, AllowGlobalReadWrite, GlobalSavePeriod, cashKey, ivSigmas, lastBarDate, sigma); m_prevIv = sigma; // Теперь надо вычислить безразмерный наклон кодом в классе SmileImitation5 bool successSkew = IvOnF.TryCalcAndWriteSkews(m_context, spline, UseGlobalCache, AllowGlobalReadWrite, GlobalSavePeriod, optSer.UnderlyingAsset.Symbol, expiry, futPx, lastBarDate, m_tRemainMode, plainTimeAsYears, timeAsYears); return(sigma); } } else { // Если перемасштабировать улыбку не получается придется эту точку проигнорировать // Надо ли сделать соответствующую запись в логе??? sigma = Constants.NaN; } #endregion Зверская ветка по замене времени } else { lock (ivSigmas) { bool success = IvOnF.TryWrite(m_context, UseGlobalCache, AllowGlobalReadWrite, GlobalSavePeriod, cashKey, ivSigmas, lastBarDate, sigma); m_prevIv = sigma; return(sigma); } } } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, false); } return(failRes); #endregion Process last bar } } }
private bool TryProcessSeries(IOptionSeries optSer, DateTime now, out double ivAtm) { ivAtm = Constants.NaN; if (optSer == null) { return(false); } Dictionary <DateTime, double> ivSigmas; #region Get cache DateTime expiry = optSer.ExpirationDate.Date; string cashKey = IvOnF.GetCashKey(optSer.UnderlyingAsset.Symbol, expiry, m_rescaleTime, m_tRemainMode); object globalObj = Context.LoadGlobalObject(cashKey, true); ivSigmas = globalObj as Dictionary <DateTime, double>; // PROD-3970 - 'Важный' объект if (ivSigmas == null) { var container = globalObj as NotClearableContainer; if ((container != null) && (container.Content != null)) { ivSigmas = container.Content as Dictionary <DateTime, double>; } } if (ivSigmas == null) { ivSigmas = new Dictionary <DateTime, double>(); } #endregion Get cache ISecurity sec = optSer.UnderlyingAsset; int len = sec.Bars.Count; if (len <= 0) { return(false); } FinInfo baseFinInfo = optSer.UnderlyingAsset.FinInfo; if (baseFinInfo.LastPrice == null) { string msg = "[IV ATM (all series)] (baseFinInfo.LastPrice == null)"; m_context.Log(msg, MessageType.Warning, false); return(false); } double futPx = baseFinInfo.LastPrice.Value; if (futPx <= Double.Epsilon) { return(false); } NotAKnotCubicSpline spline = null; try { spline = IvOnF.PrepareExchangeSmileSpline(optSer, Double.MinValue, Double.MaxValue); } catch (ScriptException scriptEx) { m_context.Log(scriptEx.ToString(), MessageType.Error, false); return(false); } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, false); return(false); } if (spline == null) { return(false); } try { double sigma; if (spline.TryGetValue(futPx, out sigma) && (!Double.IsNaN(sigma)) && (sigma > 0)) { ivAtm = sigma; DateTime lastBarDate = sec.Bars[len - 1].Date; if (m_rescaleTime) { #region Зверская ветка по замене времени DateTime expDate = optSer.ExpirationDate.Date + m_expiryTime; // 1. Надо перевести волатильность в абсолютную цену // с учетом плоского календарного времени применяемого РТС double plainTimeAsYears; { double plainTimeAsDays; TimeToExpiry.GetDt(expDate, now, TimeRemainMode.PlainCalendar, false, out plainTimeAsDays, out plainTimeAsYears); } // 2. Вычисляем 'нормальное' время double timeAsDays, timeAsYears; TimeToExpiry.GetDt(expDate, now, m_tRemainMode, false, out timeAsDays, out timeAsYears); sigma = FinMath.RescaleIvToAnotherTime(plainTimeAsYears, ivAtm, timeAsYears); if (DoubleUtil.IsPositive(sigma)) { ivAtm = sigma; // Это просто запись на диск. К успешности вычисления волы success отношения не имеет bool success = IvOnF.TryWrite(m_context, true, true, 1, cashKey, ivSigmas, lastBarDate, sigma); // Теперь надо вычислить безразмерный наклон кодом в классе SmileImitation5 bool successSkew = IvOnF.TryCalcAndWriteSkews(m_context, spline, true, true, 1, optSer.UnderlyingAsset.Symbol, expiry, futPx, lastBarDate, m_tRemainMode, plainTimeAsYears, timeAsYears); return(true); } else { // Если перемасштабировать улыбку не получается придется эту точку проигнорировать // Надо ли сделать соответствующую запись в логе??? return(false); } #endregion Зверская ветка по замене времени } else { // Это просто запись на диск. К успешности вычисления волы success отношения не имеет bool success = IvOnF.TryWrite(m_context, true, true, 1, cashKey, ivSigmas, lastBarDate, sigma); return(true); } } } catch (ScriptException scriptEx) { m_context.Log(scriptEx.ToString(), MessageType.Error, false); } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, false); //throw; } return(false); }