/// <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); }
public static NotAKnotCubicSpline PrepareExchangeSmileSpline(IOptionSeries optSer, double minStrike, double maxStrike) { List <double> xs = new List <double>(); List <double> ys = new List <double>(); IOptionStrikePair[] pairs = (from pair in optSer.GetStrikePairs() //orderby pair.Strike ascending -- уже отсортировано! select pair).ToArray(); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair sInfo = pairs[j]; // Сверхдалекие страйки игнорируем if ((sInfo.Strike < minStrike) || (maxStrike < sInfo.Strike)) { continue; } if ((sInfo.PutFinInfo == null) || (sInfo.CallFinInfo == null) || (!sInfo.PutFinInfo.TheoreticalPrice.HasValue) || (!sInfo.PutFinInfo.Volatility.HasValue) || (sInfo.PutFinInfo.TheoreticalPrice.Value <= 0) || (sInfo.PutFinInfo.Volatility.Value <= 0) || (!sInfo.CallFinInfo.TheoreticalPrice.HasValue) || (!sInfo.CallFinInfo.Volatility.HasValue) || (sInfo.CallFinInfo.TheoreticalPrice.Value <= 0) || (sInfo.CallFinInfo.Volatility.Value <= 0)) { continue; } // TODO: вернуть ассерт потом //System.Diagnostics.Debug.Assert( // DoubleUtil.AreClose(sInfo.PutFinInfo.Volatility.Value, sInfo.CallFinInfo.Volatility.Value), // "Exchange volas on the same strike MUST be equal! PutVola:" + sInfo.PutFinInfo.Volatility.Value + // "; CallVola:" + sInfo.CallFinInfo.Volatility.Value); xs.Add(sInfo.Strike); ys.Add(sInfo.PutFinInfo.Volatility.Value); } NotAKnotCubicSpline spline = null; if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { spline = new NotAKnotCubicSpline(xs, ys); } return(spline); }
public InteractiveSeries Execute(InteractiveSeries deltaProfile, int barNum) { //InteractiveSeries res = context.LoadObject(cashKey + "positionDelta") as InteractiveSeries; //if (res == null) //{ // res = new InteractiveSeries(); // context.StoreObject(cashKey + "positionDelta", res); //} InteractiveSeries res = new InteractiveSeries(); int barsCount = ContextBarsCount; if (barNum < barsCount - 1) { return(res); } if (deltaProfile == null) { return(res); } SmileInfo sInfo = deltaProfile.GetTag <SmileInfo>(); if ((sInfo == null) || (sInfo.ContinuousFunction == null) || (sInfo.ContinuousFunctionD1 == null)) { return(res); } List <double> xs = new List <double>(); List <double> ys = new List <double>(); var deltaPoints = deltaProfile.ControlPoints; List <InteractiveObject> controlPoints = new List <InteractiveObject>(); foreach (InteractiveObject iob in deltaPoints) { double rawGamma, f = iob.Anchor.ValueX; if (sInfo.ContinuousFunctionD1.TryGetValue(f, out rawGamma)) { InteractivePointActive ip = new InteractivePointActive(); ip.IsActive = m_showNodes; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = System.Windows.Media.Colors.Orange; double y = rawGamma; ip.Value = new Point(f, y); string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture); ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "F:{0}; G:{1}", f, yStr); controlPoints.Add(new InteractiveObject(ip)); xs.Add(f); ys.Add(y); } } res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { SmileInfo info = new SmileInfo(); info.F = sInfo.F; info.dT = sInfo.dT; info.RiskFreeRate = sInfo.RiskFreeRate; NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } 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)); }
/// <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 } } }
public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, int barNum) { //InteractiveSeries res = context.LoadObject(cashKey + "positionDelta") as InteractiveSeries; //if (res == null) //{ // res = new InteractiveSeries(); // context.StoreObject(cashKey + "positionDelta", res); //} InteractiveSeries res = new InteractiveSeries(); int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (optSer == null)) { return(res); } //double F = prices[prices.Count - 1]; double dT = time; if (Double.IsNaN(dT) || (dT < Double.Epsilon)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); m_context.Log(msg, MessageType.Error, true); return(res); } if (smile == null) { string msg = String.Format("[{0}] Argument 'smile' must be filled with InteractiveSeries.", GetType().Name); m_context.Log(msg, MessageType.Error, true); return(res); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if (oldInfo == null) { string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag); m_context.Log(msg, MessageType.Error, true); return(res); } //// TODO: переписаться на обновление старых значений //res.ControlPoints.Clear(); List <double> xs = new List <double>(); List <double> ys = new List <double>(); var smilePoints = smile.ControlPoints; IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); foreach (InteractiveObject iob in smilePoints) { double rawVega, f = iob.Anchor.ValueX; if (TryEstimateVega(posMan, optSer, pairs, smile, m_greekAlgo, f, m_sigmaStep, dT, out rawVega)) { // Переводим вегу в дифференциал 'изменение цены за 1% волы'. rawVega /= Constants.PctMult; InteractivePointActive ip = new InteractivePointActive(); ip.IsActive = m_showNodes; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = System.Windows.Media.Colors.Green; double y = rawVega; ip.Value = new Point(f, y); string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture); ip.Tooltip = String.Format("F:{0}; V:{1}", f, yStr); controlPoints.Add(new InteractiveObject(ip)); xs.Add(f); ys.Add(y); } } res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { SmileInfo info = new SmileInfo(); info.F = oldInfo.F; info.dT = oldInfo.dT; info.RiskFreeRate = oldInfo.RiskFreeRate; NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } return(res); }
public InteractiveSeries Execute(InteractiveSeries smile, int barNum) { if (m_transformation == SmileTransformation.None) { if (DoubleUtil.IsZero(m_shiftIv)) { return(smile); } } int barsCount = m_context.BarsCount; if (!m_context.IsLastBarUsed) { barsCount--; } if (barNum < barsCount - 1) { return(smile); } // Пустая улыбка останется пустой. Что с ней делать непонятно. if (smile.ControlPoints.Count <= 1) { return(smile); } SmileInfo sInfo = smile.GetTag <SmileInfo>(); if (sInfo == null) { throw new ScriptException("ArgumentException: smile.Tag must be filled with SmileInfo object."); } var cps = smile.ControlPoints; int len = cps.Count; IFunction symmetrizedFunc = null; IFunction symmetrizedFuncD1 = null; InteractiveSeries res = new InteractiveSeries(); List <double> xs = new List <double>(); List <double> ys = new List <double>(); if (m_transformation == SmileTransformation.Simmetrise) { #region Simmetrise double f = sInfo.F; double minX = cps[0].Anchor.ValueX; double maxX = cps[len - 1].Anchor.ValueX; double width = Math.Min(maxX - f, f - minX); if (width < 0) { throw new ScriptException("ArgumentException: current price is outside of the smile."); } // TODO: сократить вычисления вдвое, учитывая явное требование симметричности результирующей улыбки double step = 2.0 * width / (len - 1); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); for (int j = 0; j < len; j++) { double kLeft = (f - width) + j * step; double ivLeft = sInfo.ContinuousFunction.Value(kLeft); double kRight = (f + width) - j * step; double ivRight = sInfo.ContinuousFunction.Value(kRight); double iv = 0.5 * (ivLeft + ivRight) + m_shiftIv; InteractiveObject oldObj = cps[j]; if (oldObj.Anchor is InteractivePointActive) { InteractivePointActive ip = (InteractivePointActive)oldObj.Anchor.Clone(); ip.Color = (m_optionPxMode == OptionPxMode.Ask) ? AlphaColors.DarkOrange : AlphaColors.DarkCyan; ip.DragableMode = DragableMode.None; ip.Geometry = Geometries.Rect; // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect; ip.IsActive = (m_optionPxMode != OptionPxMode.Mid); ip.Value = new Point(kLeft, iv); ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", kLeft, iv * Constants.PctMult); InteractiveObject newObj = new InteractiveObject(ip); controlPoints.Add(newObj); } else if (oldObj.Anchor is InteractivePointLight) { InteractivePointLight ip = (InteractivePointLight)oldObj.Anchor.Clone(); ip.Value = new Point(kLeft, iv); InteractiveObject newObj = new InteractiveObject(ip); controlPoints.Add(newObj); } else { string msg = String.Format("[{0}] Point of type '{1}' is not supported.", GetType().Name, oldObj.Anchor.GetType().Name); throw new ScriptException(msg); } xs.Add(kLeft); ys.Add(iv); } res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); #endregion Simmetrise } else if (m_transformation == SmileTransformation.LogSimmetrise) { #region Log Simmetrise double f = sInfo.F; LogSimmetrizeFunc lsf = new LogSimmetrizeFunc(sInfo.ContinuousFunction, f, m_simmWeight); symmetrizedFunc = lsf; symmetrizedFuncD1 = new LogSimmetrizeFunc(sInfo.ContinuousFunctionD1, f, m_simmWeight); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); foreach (var oldObj in smile.ControlPoints) { double k = oldObj.Anchor.ValueX; double iv; if (lsf.TryGetValue(k, out iv)) { iv += m_shiftIv; if (oldObj.Anchor is InteractivePointActive) { InteractivePointActive ip = (InteractivePointActive)oldObj.Anchor.Clone(); ip.Color = (m_optionPxMode == OptionPxMode.Ask) ? AlphaColors.DarkOrange : AlphaColors.DarkCyan; ip.DragableMode = DragableMode.None; ip.Geometry = Geometries.Rect; // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect; ip.IsActive = (m_optionPxMode != OptionPxMode.Mid); ip.Value = new Point(k, iv); ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", k, iv * Constants.PctMult); InteractiveObject newObj = new InteractiveObject(ip); controlPoints.Add(newObj); } else if (oldObj.Anchor is InteractivePointLight) { InteractivePointLight ip = (InteractivePointLight)oldObj.Anchor.Clone(); ip.Value = new Point(k, iv); InteractiveObject newObj = new InteractiveObject(ip); controlPoints.Add(newObj); } else { string msg = String.Format("[{0}] Point of type '{1}' is not supported.", GetType().Name, oldObj.Anchor.GetType().Name); throw new ScriptException(msg); } xs.Add(k); ys.Add(iv); } } res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); #endregion Log Simmetrise } else if (m_transformation == SmileTransformation.None) { #region None (only vertical shift) double f = sInfo.F; double dT = sInfo.dT; double rate = sInfo.RiskFreeRate; List <InteractiveObject> controlPoints = new List <InteractiveObject>(); for (int j = 0; j < len; j++) { InteractiveObject oldObj = cps[j]; SmileNodeInfo nodeInfo = oldObj.Anchor.Tag as SmileNodeInfo; // Обязательно нужна полная информация об узле улыбки, чтобы потом можно было торговать if (nodeInfo == null) { continue; } double k = oldObj.Anchor.Value.X; double iv = oldObj.Anchor.Value.Y + m_shiftIv; if (oldObj.Anchor is InteractivePointActive) { InteractivePointActive ip = (InteractivePointActive)oldObj.Anchor.Clone(); ip.Color = (m_optionPxMode == OptionPxMode.Ask) ? AlphaColors.DarkOrange : AlphaColors.DarkCyan; ip.DragableMode = DragableMode.None; ip.Geometry = Geometries.Rect; // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect; ip.IsActive = (m_optionPxMode != OptionPxMode.Mid); //ip.Value = new Point(oldObj.Anchor.Value.X, iv); //ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", k, iv * PctMult); InteractiveObject newObj = new InteractiveObject(ip); if (k <= f) // Puts { FillNodeInfo(ip, f, dT, nodeInfo.Pair, StrikeType.Put, m_optionPxMode, iv, false, rate); } else // Calls { FillNodeInfo(ip, f, dT, nodeInfo.Pair, StrikeType.Call, m_optionPxMode, iv, false, rate); } controlPoints.Add(newObj); } else if (oldObj.Anchor is InteractivePointLight) { InteractivePointLight ip = (InteractivePointLight)oldObj.Anchor.Clone(); ip.Value = new Point(k, iv); InteractiveObject newObj = new InteractiveObject(ip); if (k <= f) // Puts { FillNodeInfo(ip, f, dT, nodeInfo.Pair, StrikeType.Put, m_optionPxMode, iv, false, rate); } else // Calls { FillNodeInfo(ip, f, dT, nodeInfo.Pair, StrikeType.Call, m_optionPxMode, iv, false, rate); } controlPoints.Add(newObj); } else { string msg = String.Format("[{0}] Point of type '{1}' is not supported.", GetType().Name, oldObj.Anchor.GetType().Name); throw new ScriptException(msg); } xs.Add(k); ys.Add(iv); } res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); #endregion None (only vertical shift) } else { throw new NotImplementedException("Transformation: " + m_transformation); } // ReSharper disable once UseObjectOrCollectionInitializer SmileInfo info = new SmileInfo(); info.F = sInfo.F; info.dT = sInfo.dT; info.Expiry = sInfo.Expiry; info.ScriptTime = sInfo.ScriptTime; info.RiskFreeRate = sInfo.RiskFreeRate; info.BaseTicker = sInfo.BaseTicker; try { if (symmetrizedFunc == null) { NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); } else { // По факту эта ветка обслуживает только алгоритм LogSimm info.ContinuousFunction = symmetrizedFunc; info.ContinuousFunctionD1 = symmetrizedFuncD1; } res.Tag = info; } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } return(res); }
public InteractiveSeries Execute(double trueTimeToExpiry, InteractiveSeries smile, int barNum) { int barsCount = m_context.BarsCount; if (!m_context.IsLastBarUsed) { barsCount--; } if (barNum < barsCount - 1) { return(Constants.EmptySeries); } SmileInfo refSmileInfo = smile.GetTag <SmileInfo>(); if ((refSmileInfo == null) || (refSmileInfo.ContinuousFunction == null)) { return(Constants.EmptySeries); } if (Double.IsNaN(trueTimeToExpiry) || (trueTimeToExpiry < Double.Epsilon)) { string msg = String.Format("[{0}] trueTimeToExpiry must be positive value. dT:{1}", GetType().Name, trueTimeToExpiry); m_context.Log(msg, MessageType.Error, true); return(Constants.EmptySeries); } InteractiveSeries res = FrozenSmile; // Поскольку редактируемые узлы идут с заданным шагом, то // допустим только режим Yonly. DragableMode dragMode = DragableMode.Yonly; SmileInfo oldInfo = res.GetTag <SmileInfo>(); if (m_resetSmile || (oldInfo == null) || (oldInfo.ContinuousFunction == null)) { oldInfo = refSmileInfo; //oldInfo.F = sInfo.F; //oldInfo.dT = sInfo.dT; //oldInfo.RiskFreeRate = sInfo.RiskFreeRate; } double futPx = refSmileInfo.F; double dT = trueTimeToExpiry; double ivAtm = refSmileInfo.ContinuousFunction.Value(futPx); SmileInfo info = new SmileInfo(); { info.F = futPx; info.dT = trueTimeToExpiry; info.RiskFreeRate = oldInfo.RiskFreeRate; List <double> xs = new List <double>(); List <double> ys = new List <double>(); List <InteractiveObject> visibleNodes = (from oldObj in res.ControlPoints where oldObj.AnchorIsActive select oldObj).ToList(); int visibleNodesCount = visibleNodes.Count; if (m_resetSmile || (visibleNodesCount != m_numberOfNodes)) { // Здесь обязательно в начале //res.ControlPoints.Clear(); res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(new InteractiveObject[0]); int half = m_numberOfNodes / 2; // Целочисленное деление! #region 1. Готовлю сплайн { double mult = m_nodeStep * ivAtm * Math.Sqrt(dT); double dK = futPx * (Math.Exp(mult) - 1); // Сдвигаю точки, чтобы избежать отрицательных значений while ((futPx - half * dK) <= Double.Epsilon) { half--; } for (int j = 0; j < m_numberOfNodes; j++) { double k = futPx + (j - half) * dK; // Обычно здесь будет лежать сплайн от замороженной улыбки... double sigma; if ((!oldInfo.ContinuousFunction.TryGetValue(k, out sigma)) || Double.IsNaN(sigma)) { string msg = String.Format("[DEBUG:{0}] Unable to get IV for strike:{1}. Please, try to decrease NodeStep.", GetType().Name, k); m_context.Log(msg, MessageType.Warning, true); return(res); } xs.Add(k); ys.Add(sigma); } } #endregion 1. Готовлю сплайн } else { // Здесь обязательно в начале //res.ControlPoints.Clear(); res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(new InteractiveObject[0]); int half = m_numberOfNodes / 2; // Целочисленное деление! #region 2. Готовлю сплайн { double mult = m_nodeStep * ivAtm * Math.Sqrt(dT); double dK = futPx * (Math.Exp(mult) - 1); // Сдвигаю точки, чтобы избежать отрицательных значений while ((futPx - half * dK) <= Double.Epsilon) { half--; } // внутренние узлы... for (int j = 0; j < m_numberOfNodes; j++) { double k = futPx + (j - half) * dK; //// Обычно здесь будет лежать сплайн от замороженной улыбки... //double sigma = oldInfo.ContinuousFunction.Value(k); double sigma = visibleNodes[j].Anchor.ValueY; xs.Add(k); ys.Add(sigma); } } #endregion 2. Готовлю сплайн } try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); //info.F = F; //info.dT = trueTimeToExpiry; res.Tag = info; } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } // 2. Формирую кривую с более плотным шагом List <InteractiveObject> controlPoints = new List <InteractiveObject>(); int editableNodesCount = FillEditableCurve(info, controlPoints, xs, dragMode); res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); if (editableNodesCount != m_numberOfNodes) { string msg = String.Format("[DEBUG:{0}] {1} nodes requested, but only {2} were prepared.", GetType().Name, m_numberOfNodes, editableNodesCount); m_context.Log(msg, MessageType.Warning, true); } } if (!m_resetSmile) { if (m_loadSplineCoeffs) { } if (m_prepareSplineCoeffs) { #region Prepare global spline // Надо пересчитать сплайн в безразмерные коэффициенты // Обновляю уровень IV ATM? ivAtm = info.ContinuousFunction.Value(futPx); SmileInfo globInfo = new SmileInfo(); globInfo.F = futPx; globInfo.dT = trueTimeToExpiry; globInfo.RiskFreeRate = oldInfo.RiskFreeRate; StringBuilder sb = new StringBuilder(GlobalSmileKey); sb.AppendLine(); sb.AppendFormat(CultureInfo.InvariantCulture, "F:{0}", futPx); sb.AppendLine(); sb.AppendFormat(CultureInfo.InvariantCulture, "dT:{0}", dT); sb.AppendLine(); sb.AppendFormat(CultureInfo.InvariantCulture, "IvAtm:{0}", ivAtm); sb.AppendLine(); sb.AppendFormat(CultureInfo.InvariantCulture, "RiskFreeRate:{0}", globInfo.RiskFreeRate); sb.AppendLine(); sb.AppendFormat(CultureInfo.InvariantCulture, "ShapePct:{0}", ShapePct); sb.AppendLine(); sb.AppendLine("~~~"); sb.AppendFormat(CultureInfo.InvariantCulture, "X;Y"); sb.AppendLine(); //LogSimmetrizeFunc logSimmFunc = new LogSimmetrizeFunc(info.ContinuousFunction, F, 0.5); List <double> xs = new List <double>(); List <double> ys = new List <double>(); foreach (InteractiveObject oldObj in res.ControlPoints) { if (!oldObj.AnchorIsActive) { continue; } double k = oldObj.Anchor.ValueX; double x = Math.Log(k / futPx) / Math.Pow(dT, DefaultPow + m_shape) / ivAtm; double sigma = oldObj.Anchor.ValueY; double sigmaNormalized = sigma / ivAtm; xs.Add(x); ys.Add(sigmaNormalized); sb.AppendFormat(CultureInfo.InvariantCulture, "{0};{1}", x, sigmaNormalized); sb.AppendLine(); } try { NotAKnotCubicSpline globSpline = new NotAKnotCubicSpline(xs, ys); globInfo.ContinuousFunction = globSpline; globInfo.ContinuousFunctionD1 = globSpline.DeriveD1(); //global.Tag = globInfo; m_context.StoreGlobalObject(GlobalSmileKey, globInfo, true); string msg = String.Format("[{0}] The globInfo was saved in Global cache as '{1}'.", GetType().Name, GlobalSmileKey); m_context.Log(msg, MessageType.Warning, true); msg = String.Format("[{0}] The globInfo was saved in file tslab.log also. Smile:\r\n{1}", GetType().Name, sb); m_context.Log(msg, MessageType.Info, true); // Запись в клипбоард try { //Thread thread = ThreadProfiler.Create(() => System.Windows.Clipboard.SetText(sb.ToString())); XElement xel = globInfo.ToXElement(); string xelStr = @"<?xml version=""1.0""?> " + xel.AsString(); // PROD-5987 - Отключаю работу с клипбордом. Только пишу в tslab.log //Thread thread = ThreadProfiler.Create(() => System.Windows.Clipboard.SetText(xelStr)); //thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA //thread.Start(); //thread.Join(); // Надо ли делать Join? s_log.WarnFormat("Global smile info:\r\n\r\n{0}\r\n\r\n", xelStr); } catch (Exception clipEx) { m_context.Log(clipEx.ToString(), MessageType.Error, true); } m_context.Recalc(); } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); //return Constants.EmptySeries; } #endregion Prepare global spline } else if (m_pasteGlobal) { // PROD-5987 - Работа с клипбордом отключена. Функция вставки сейчас работать не будет. m_context.Log($"[{GetType().Name}] Clipboard is not available. Sorry.", MessageType.Warning, true); #region Paste spline from clipboard //string xelStr = ""; //// Чтение из клипбоард //try //{ // // PROD-5987 - Работа с клипбордом отключена. Функция вставки сейчас работать не будет. // ////Thread thread = ThreadProfiler.Create(() => System.Windows.Clipboard.SetText(sb.ToString())); // //Thread thread = ThreadProfiler.Create(() => xelStr = System.Windows.Clipboard.GetText()); // //thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA // //thread.Start(); // //thread.Join(); // Надо ли делать Join? // if (!String.IsNullOrWhiteSpace(xelStr)) // { // XDocument xDoc = XDocument.Parse(xelStr); // XElement xInfo = xDoc.Root; // SmileInfo templateSmile = SmileInfo.FromXElement(xInfo); // // Обновляю уровень IV ATM? // // TODO: перепроверить как работает редактирование шаблона // ivAtm = info.ContinuousFunction.Value(futPx); // if (Double.IsNaN(ivAtm)) // { // ivAtm = refSmileInfo.ContinuousFunction.Value(futPx); // m_context.Log(String.Format("[DEBUG:{0}] ivAtm was NaN. I'll use value ivAtm:{1}", GetType().Name, ivAtm), MessageType.Warning, true); // if (Double.IsNaN(ivAtm)) // { // throw new Exception(String.Format("[DEBUG:{0}] ivAtm is NaN.", GetType().Name)); // } // } // templateSmile.F = futPx; // templateSmile.dT = trueTimeToExpiry; // templateSmile.RiskFreeRate = oldInfo.RiskFreeRate; // // Здесь обязательно в начале // //res.ControlPoints.Clear(); // res.ControlPoints = new ReadOnlyCollection<InteractiveObject>(new InteractiveObject[0]); // SmileFunction5 func = new SmileFunction5(templateSmile.ContinuousFunction, templateSmile.ContinuousFunctionD1, // ivAtm, 0, 0, futPx, dT); // info.ContinuousFunction = func; // info.ContinuousFunctionD1 = func.DeriveD1(); // // info уже лежит в Tag, поэтому при следующем пересчете сплайн уже должен пересчитаться // // по новой улыбке. Правильно? // string msg = String.Format("[DEBUG:{0}] SmileInfo was loaded from clipboard.", GetType().Name); // m_context.Log(msg, MessageType.Warning, true); // m_context.Recalc(); // } //} //catch (Exception clipEx) //{ // m_context.Log(clipEx.ToString(), MessageType.Error, true); //} #endregion Paste spline from clipboard } } //res.ClickEvent -= res_ClickEvent; //res.ClickEvent += res_ClickEvent; //res.DragEvent -= res_DragEvent; //res.DragEvent += res_DragEvent; res.EndDragEvent -= res_EndDragEvent; res.EndDragEvent += res_EndDragEvent; return(res); }
public InteractiveSeries Execute(InteractiveSeries positionProfile, int barNum) { int barsCount = ContextBarsCount; if (barNum < barsCount - 1) { return(Constants.EmptySeries); } if (positionProfile == null) { return(Constants.EmptySeries); } SmileInfo sInfo = positionProfile.GetTag <SmileInfo>(); if ((sInfo == null) || (sInfo.ContinuousFunction == null) || (sInfo.ContinuousFunctionD1 == null)) { return(Constants.EmptySeries); } List <double> xs = new List <double>(); List <double> ys = new List <double>(); var profilePoints = positionProfile.ControlPoints; List <InteractiveObject> controlPoints = new List <InteractiveObject>(); foreach (InteractiveObject iob in profilePoints) { double rawDelta, f = iob.Anchor.ValueX; if (sInfo.ContinuousFunctionD1.TryGetValue(f, out rawDelta)) { // ReSharper disable once UseObjectOrCollectionInitializer InteractivePointActive ip = new InteractivePointActive(); ip.IsActive = m_showNodes; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = System.Windows.Media.Colors.Orange; double y = rawDelta; ip.Value = new Point(f, y); string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture); ip.Tooltip = String.Format("F:{0}; D:{1}", f, yStr); controlPoints.Add(new InteractiveObject(ip)); xs.Add(f); ys.Add(y); } } // ReSharper disable once UseObjectOrCollectionInitializer InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { // ReSharper disable once UseObjectOrCollectionInitializer SmileInfo info = new SmileInfo(); info.F = sInfo.F; info.dT = sInfo.dT; info.RiskFreeRate = sInfo.RiskFreeRate; NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; } } catch (DivideByZeroException dvbEx) { StringBuilder sb = new StringBuilder(); sb.AppendFormat("Divide-by-zero exception. Probably it is from NotAKnotCubicSpline:"); sb.AppendLine(); sb.AppendLine(); sb.AppendLine(dvbEx.ToString()); sb.AppendLine(); sb.AppendLine("Content of arguments..."); sb.AppendLine("xs;ys"); for (int tmpIndex = 0; tmpIndex < xs.Count; tmpIndex++) { sb.Append(xs[tmpIndex].ToString(CultureInfo.InvariantCulture) + ";"); sb.Append(ys[tmpIndex].ToString(CultureInfo.InvariantCulture)); sb.AppendLine(); } sb.AppendLine(); m_context.Log(sb.ToString(), MessageType.Error, false); m_context.Log(dvbEx.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } return(res); }
public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, int barNum) { int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (smile == null) || (optSer == null)) { return(Constants.EmptySeries); } int lastBarIndex = optSer.UnderlyingAsset.Bars.Count - 1; DateTime now = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Date; bool wasInitialized = HandlerInitializedToday(now); double dT = time; if (!DoubleUtil.IsPositive(dT)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if (oldInfo == null) { string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.EmptySeries); } double futPx = oldInfo.F; double ivAtm; if (!oldInfo.ContinuousFunction.TryGetValue(futPx, out ivAtm)) { string msg = String.Format("[{0}] Unable to get IV ATM from smile. Tag:{1}", GetType().Name, smile.Tag); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); if (pairs.Length < 2) { string msg = String.Format("[{0}] optSer must contain few strike pairs. pairs.Length:{1}", GetType().Name, pairs.Length); if (wasInitialized) { m_context.Log(msg, MessageType.Warning, true); } return(Constants.EmptySeries); } List <double> xs = new List <double>(); List <double> ys = new List <double>(); double futStep = optSer.UnderlyingAsset.Tick; PositionsManager posMan = PositionsManager.GetManager(m_context); SortedDictionary <double, IOptionStrikePair> futPrices; if (!SmileImitation5.TryPrepareImportantPoints(pairs, futPx, futStep, -1, out futPrices)) { string msg = String.Format("[{0}] It looks like there is no suitable points for the smile. pairs.Length:{1}", GetType().Name, pairs.Length); if (wasInitialized) { m_context.Log(msg, MessageType.Warning, true); } return(Constants.EmptySeries); } List <InteractiveObject> controlPoints = new List <InteractiveObject>(); foreach (var kvp in futPrices) { double f = kvp.Key; bool tradableStrike = (kvp.Value != null); double rawGreek; GetBaseGreek(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, m_greek, out rawGreek); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double totalGreek; //double putDelta = FinMath.GetOptionDelta(f, pair.Strike, dT, sigma.Value, 0, false); GetPairGreek(posMan, smile, pair, f, dT, m_greek, out totalGreek); rawGreek += totalGreek; } InteractivePointActive ip = new InteractivePointActive(); ip.IsActive = m_showNodes; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = AlphaColors.Green; double y = rawGreek; ip.Value = new Point(f, y); string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture); ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "F:{0}; {1}:{2}", f, m_greek, yStr); controlPoints.Add(new InteractiveObject(ip)); xs.Add(f); ys.Add(y); } InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); SmileInfo info = new SmileInfo(); info.F = oldInfo.F; info.dT = oldInfo.dT; info.Expiry = optSer.ExpirationDate; info.ScriptTime = now; info.RiskFreeRate = oldInfo.RiskFreeRate; info.BaseTicker = optSer.UnderlyingAsset.Symbol; try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } return(res); }
/// <summary> /// Обработчик под тип входных данных OPTION_SERIES /// </summary> public InteractiveSeries Execute(IOptionSeries optSer, IList <double> rates) { InteractiveSeries res = m_context.LoadObject(VariableId + "theorSmile") as InteractiveSeries; if (res == null) { res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку m_context.StoreObject(VariableId + "theorSmile", res); } if (optSer == null) { return(res); } int len = optSer.UnderlyingAsset.Bars.Count; if (len <= 0) { return(res); } FinInfo bSecFinInfo = optSer.UnderlyingAsset.FinInfo; if (!bSecFinInfo.LastPrice.HasValue) { return(res); } if (rates.Count <= 0) { //throw new ScriptException("There should be some values in second argument 'rates'."); return(res); } //IDataBar bar = optSer.UnderlyingAsset.Bars[len - 1]; double futPx = bSecFinInfo.LastPrice.Value; // ФОРТС использует плоское календарное время DateTime optExpiry = optSer.ExpirationDate.Date.Add(m_expiryTime); double dT = (optExpiry - bSecFinInfo.LastUpdate).TotalYears(); double ratePct = rates[rates.Count - 1]; if (Double.IsNaN(dT) || (dT < Double.Epsilon)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); m_context.Log(msg, MessageType.Error, true); return(res); } if (Double.IsNaN(futPx) || (futPx < Double.Epsilon)) { // [{0}] Base asset price must be positive value. F:{1} string msg = RM.GetStringFormat("OptHandlerMsg.FutPxMustBePositive", GetType().Name, futPx); m_context.Log(msg, MessageType.Error, true); return(res); } if (Double.IsNaN(ratePct)) { //throw new ScriptException("Argument 'rate' contains NaN for some strange reason. rate:" + rate); return(res); } // TODO: переписаться на обновление старых значений //res.ControlPoints.Clear(); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); List <double> xs = new List <double>(); List <double> ys = new List <double>(); IOptionStrikePair[] pairs = (from pair in optSer.GetStrikePairs() //orderby pair.Strike ascending -- уже отсортировано! select pair).ToArray(); for (int j = 0; j < pairs.Length; j++) { bool showPoint = true; IOptionStrikePair sInfo = pairs[j]; double k = sInfo.Strike; //// Сверхдалекие страйки игнорируем //if ((sInfo.Strike < m_minStrike) || (m_maxStrike < sInfo.Strike)) //{ // showPoint = false; //} if ((sInfo.PutFinInfo == null) || (sInfo.CallFinInfo == null) || (!sInfo.PutFinInfo.TheoreticalPrice.HasValue) || (!sInfo.PutFinInfo.Volatility.HasValue) || (sInfo.PutFinInfo.TheoreticalPrice.Value <= 0) || (sInfo.PutFinInfo.Volatility.Value <= 0) || (!sInfo.CallFinInfo.TheoreticalPrice.HasValue) || (!sInfo.CallFinInfo.Volatility.HasValue) || (sInfo.CallFinInfo.TheoreticalPrice.Value <= 0) || (sInfo.CallFinInfo.Volatility.Value <= 0)) { continue; } double prec; // Биржа шлет несогласованную улыбку //double virtualExchangeF = sInfo.CallFinInfo.TheoreticalPrice.Value - sInfo.PutFinInfo.TheoreticalPrice.Value + sInfo.Strike; if ((m_optionType == StrikeType.Any) || (m_optionType == StrikeType.Put)) { double optSigma = sInfo.PutFinInfo.Volatility.Value; if ((!DoubleUtil.IsOne(m_multPx)) || (!DoubleUtil.IsZero(m_shiftPx))) { double optPx = sInfo.PutFinInfo.TheoreticalPrice.Value * m_multPx + m_shiftPx * sInfo.Tick; if ((optPx <= 0) || (Double.IsNaN(optPx))) { optSigma = 0; } else { optSigma = FinMath.GetOptionSigma(futPx, k, dT, optPx, ratePct, false, out prec); } } double vol = optSigma; // ReSharper disable once UseObjectOrCollectionInitializer InteractivePointActive ip = new InteractivePointActive(k, vol); ip.Geometry = Geometries.Ellipse; ip.Color = AlphaColors.Cyan; ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", k, Constants.PctMult * optSigma); if (showPoint && (vol > 0)) { controlPoints.Add(new InteractiveObject(ip)); } if ((xs.Count <= 0) || (!DoubleUtil.AreClose(k, xs[xs.Count - 1]))) { xs.Add(k); ys.Add(vol); } } if ((m_optionType == StrikeType.Any) || (m_optionType == StrikeType.Call)) { double optSigma = sInfo.CallFinInfo.Volatility.Value; if ((!DoubleUtil.IsOne(m_multPx)) || (!DoubleUtil.IsZero(m_shiftPx))) { double optPx = sInfo.CallFinInfo.TheoreticalPrice.Value * m_multPx + m_shiftPx * sInfo.Tick; if ((optPx <= 0) || (Double.IsNaN(optPx))) { optSigma = 0; } else { optSigma = FinMath.GetOptionSigma(futPx, k, dT, optPx, ratePct, true, out prec); } } double vol = optSigma; // ReSharper disable once UseObjectOrCollectionInitializer InteractivePointActive ip = new InteractivePointActive(k, vol); ip.Geometry = Geometries.Ellipse; ip.Color = AlphaColors.DarkCyan; ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", k, Constants.PctMult * optSigma); if (showPoint && (vol > 0)) { controlPoints.Add(new InteractiveObject(ip)); } if ((xs.Count <= 0) || (!DoubleUtil.AreClose(k, xs[xs.Count - 1]))) { xs.Add(k); ys.Add(vol); } } } res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); var baseSec = optSer.UnderlyingAsset; DateTime scriptTime = baseSec.Bars[baseSec.Bars.Count - 1].Date; // ReSharper disable once UseObjectOrCollectionInitializer SmileInfo info = new SmileInfo(); info.F = futPx; info.dT = dT; info.Expiry = optSer.ExpirationDate; info.ScriptTime = scriptTime; info.RiskFreeRate = ratePct; info.BaseTicker = baseSec.Symbol; try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } return(res); }
/// <summary> /// Создание сплайна и заполнение SmileInfo по предложенной таблице /// </summary> /// <param name="externalContext">контекст блока для логгирования проблемы</param> /// <param name="f">цена БА (нажна как верхняя оценка цены колла</param> /// <param name="dT">время до экспирации</param> /// <param name="riskFreeRate">беспроцентная ставка</param> /// <param name="expiry">дата экспирации (без времени)</param> /// <param name="scriptTime">время в скрипте</param> /// <param name="baseTicker">тикер БА</param> /// <param name="res">Таблица с данными для интерполирования</param> /// <param name="info">Заполненный SmileInfo</param> public static bool TryPrepareSmileInfo(IContext externalContext, double f, double dT, double riskFreeRate, DateTime expiry, DateTime scriptTime, string baseTicker, InteractiveSeries res, out SmileInfo info) { List <double> xs = new List <double>(); List <double> ys = new List <double>(); foreach (InteractiveObject obj in res.ControlPoints) { Point point = obj.Anchor.Value; xs.Add(point.X); ys.Add(point.Y); } // ReSharper disable once UseObjectOrCollectionInitializer info = new SmileInfo(); info.F = f; info.dT = dT; info.Expiry = expiry; info.ScriptTime = scriptTime; info.RiskFreeRate = riskFreeRate; info.BaseTicker = baseTicker; try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; } } catch (DivideByZeroException dvbz) { StringBuilder sb = new StringBuilder(); sb.AppendLine(dvbz.GetType().Name); sb.AppendLine(); sb.AppendLine("X;Y"); for (int j = 0; j < xs.Count; j++) { sb.AppendFormat("{0};{1}", xs[j].ToString(CultureInfo.InvariantCulture), ys[j].ToString(CultureInfo.InvariantCulture)); sb.AppendLine(); } sb.AppendLine(); sb.AppendLine(); sb.AppendLine(); sb.AppendLine(dvbz.ToString()); sb.AppendLine(); externalContext.Log(sb.ToString(), MessageType.Error, true); return(false); } catch (Exception ex) { externalContext.Log(ex.ToString(), MessageType.Error, true); return(false); } xs.Clear(); ys.Clear(); return(true); }
/// <summary> /// Обработчик под тип входных данных OPTION_SERIES /// </summary> public InteractiveSeries Execute(double price, double trueTimeToExpiry, IOptionSeries optSer, double ratePct, int barNum) { int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (optSer == null)) { return(Constants.EmptySeries); } // PROD-5952 - Не надо дергать стакан без нужды //optSer.UnderlyingAsset.UpdateQueueData(); FinInfo bSecFinInfo = optSer.UnderlyingAsset.FinInfo; if (bSecFinInfo.LastPrice == null) { return(Constants.EmptySeries); } double futPx = price; // ФОРТС использует плоское календарное время DateTime optExpiry = optSer.ExpirationDate.Date.Add(m_expiryTime); double dT = (optExpiry - bSecFinInfo.LastUpdate).TotalYears(); if (Double.IsNaN(dT) || (dT < Double.Epsilon)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); m_context.Log(msg, MessageType.Error, true); return(Constants.EmptySeries); } if (Double.IsNaN(futPx) || (futPx < Double.Epsilon)) { // [{0}] Base asset price must be positive value. F:{1} string msg = RM.GetStringFormat("OptHandlerMsg.FutPxMustBePositive", GetType().Name, futPx); m_context.Log(msg, MessageType.Error, true); return(Constants.EmptySeries); } if (Double.IsNaN(trueTimeToExpiry) || (trueTimeToExpiry < Double.Epsilon)) { string msg = String.Format("[{0}] trueTimeToExpiry must be positive value. dT:{1}", GetType().Name, trueTimeToExpiry); m_context.Log(msg, MessageType.Error, true); return(Constants.EmptySeries); } if (Double.IsNaN(ratePct)) { //throw new ScriptException("Argument 'ratePct' contains NaN for some strange reason. rate:" + rate); return(Constants.EmptySeries); } double effectiveTime = m_rescaleTime ? trueTimeToExpiry : dT; List <double> xs = new List <double>(); List <double> ys = new List <double>(); IOptionStrikePair[] pairs = (from pair in optSer.GetStrikePairs() //orderby pair.Strike ascending -- уже отсортировано! select pair).ToArray(); if (pairs.Length < 2) { string msg = String.Format("[{0}] optSer must contain few strike pairs. pairs.Length:{1}", GetType().Name, pairs.Length); m_context.Log(msg, MessageType.Warning, true); return(Constants.EmptySeries); } for (int j = 0; j < pairs.Length; j++) { //bool showPoint = true; IOptionStrikePair sInfo = pairs[j]; double k = sInfo.Strike; //// Сверхдалекие страйки игнорируем //if ((k < m_minStrike) || (m_maxStrike < k)) //{ // showPoint = false; //} if ((sInfo.PutFinInfo == null) || (sInfo.CallFinInfo == null) || (!sInfo.PutFinInfo.TheoreticalPrice.HasValue) || (!sInfo.PutFinInfo.Volatility.HasValue) || (sInfo.PutFinInfo.TheoreticalPrice.Value <= 0) || (sInfo.PutFinInfo.Volatility.Value <= 0) || (!sInfo.CallFinInfo.TheoreticalPrice.HasValue) || (!sInfo.CallFinInfo.Volatility.HasValue) || (sInfo.CallFinInfo.TheoreticalPrice.Value <= 0) || (sInfo.CallFinInfo.Volatility.Value <= 0)) { continue; } // Биржа шлет несогласованную улыбку //double virtualExchangeF = sInfo.CallFinInfo.TheoreticalPrice.Value - sInfo.PutFinInfo.TheoreticalPrice.Value + sInfo.Strike; if ((m_optionType == StrikeType.Any) || (m_optionType == StrikeType.Put)) { double optSigma = sInfo.PutFinInfo.Volatility.Value; if (m_rescaleTime) { // optSigma = FinMath.GetOptionSigma(futPx, k, effectiveTime, optPx, 0, false); optSigma = FinMath.RescaleIvToAnotherTime(dT, optSigma, trueTimeToExpiry); } double optPx = sInfo.PutFinInfo.TheoreticalPrice ?? Double.NaN; //// ReSharper disable once UseObjectOrCollectionInitializer //InteractivePointActive ip = new InteractivePointActive(k, optSigma); //ip.IsActive = m_showNodes; //ip.Geometry = Geometries.Ellipse; //ip.Color = System.Windows.Media.Colors.Cyan; //ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}; Px:{2}\r\ndT:{3:0.000}; Date:{4}", // k, Constants.PctMult * optSigma, optPx, // FixedValue.ConvertToDisplayUnits(FixedValueMode.YearsAsDays, effectiveTime), // bSecFinInfo.LastUpdate.ToString(DateTimeFormatWithMs, CultureInfo.InvariantCulture)); if (optSigma > 0) { // Это условие позволяет не вставлять точки с совпадающими абсциссами if ((xs.Count <= 0) || (!DoubleUtil.AreClose(k, xs[xs.Count - 1]))) { xs.Add(k); ys.Add(optSigma); } } } if ((m_optionType == StrikeType.Any) || (m_optionType == StrikeType.Call)) { double optSigma = sInfo.CallFinInfo.Volatility.Value; if (m_rescaleTime) { // optSigma = FinMath.GetOptionSigma(futPx, k, effectiveTime, optPx, 0, false); optSigma = FinMath.RescaleIvToAnotherTime(dT, optSigma, trueTimeToExpiry); } double optPx = sInfo.CallFinInfo.TheoreticalPrice ?? Double.NaN; //// ReSharper disable once UseObjectOrCollectionInitializer //InteractivePointActive ip = new InteractivePointActive(k, optSigma); //ip.IsActive = m_showNodes; //ip.Geometry = Geometries.Ellipse; //ip.Color = System.Windows.Media.Colors.Cyan; //ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}; Px:{2}\r\ndT:{3:0.000}; Date:{4}", // k, Constants.PctMult * optSigma, optPx, // FixedValue.ConvertToDisplayUnits(FixedValueMode.YearsAsDays, effectiveTime), // bSecFinInfo.LastUpdate.ToString(DateTimeFormatWithMs, CultureInfo.InvariantCulture)); if (optSigma > 0) { // Это условие позволяет не вставлять точки с совпадающими абсциссами if ((xs.Count <= 0) || (!DoubleUtil.AreClose(k, xs[xs.Count - 1]))) { xs.Add(k); ys.Add(optSigma); } } } } #region 3. Строим сплайн по биржевой улыбке с узлами в страйках NotAKnotCubicSpline spline = null, splineD1 = null; try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { spline = new NotAKnotCubicSpline(xs, ys); splineD1 = spline.DeriveD1(); } else { return(Constants.EmptySeries); } } catch (Exception ex) { string msg = String.Format("bSecFinInfo.LastUpdate:{0}; Bars.Last.Date:{1}\r\n\r\nException:{2}", bSecFinInfo.LastUpdate, optSer.UnderlyingAsset.Bars[optSer.UnderlyingAsset.Bars.Count - 1].Date, ex); m_context.Log(msg, MessageType.Error, true); return(Constants.EmptySeries); } #endregion 3. Строим сплайн по биржевой улыбке с узлами в страйках #region 5. С помощью сплайна уже можно строить гладкую улыбку double futStep = optSer.UnderlyingAsset.Tick; double dK = pairs[1].Strike - pairs[0].Strike; SortedDictionary <double, IOptionStrikePair> strikePrices; if (!SmileImitation5.TryPrepareImportantPoints(pairs, futPx, futStep, -1, out strikePrices)) { string msg = String.Format("[WARNING:{0}] It looks like there is no suitable points for the smile. pairs.Length:{1}", GetType().Name, pairs.Length); m_context.Log(msg, MessageType.Warning, true); return(Constants.EmptySeries); } List <InteractiveObject> controlPoints = new List <InteractiveObject>(); //for (int j = 0; j < pairs.Length; j++) foreach (var kvp in strikePrices) { bool showPoint = (kvp.Value != null); double k = kvp.Key; double sigma; if (!spline.TryGetValue(k, out sigma)) { continue; } double vol = sigma; if (Double.IsNaN(sigma) || Double.IsInfinity(sigma) || (sigma < Double.Epsilon)) { continue; } InteractivePointLight ip; if (m_showNodes && showPoint) { // Как правило, эта ветка вообще не используется, // потому что я не смотрю узлы биржевой улыбки. double optPx = Double.NaN; // ReSharper disable once UseObjectOrCollectionInitializer InteractivePointActive tip = new InteractivePointActive(k, vol); tip.IsActive = m_showNodes && showPoint; tip.Geometry = Geometries.Ellipse; tip.Color = AlphaColors.Cyan; tip.Tooltip = String.Format("K:{0}; IV:{1:P2}; Px:{2}\r\ndT:{3:0.000}; Date:{4}", k, vol, optPx, FixedValue.ConvertToDisplayUnits(FixedValueMode.YearsAsDays, effectiveTime), bSecFinInfo.LastUpdate.ToString(DateTimeFormatWithMs, CultureInfo.InvariantCulture)); ip = tip; } else { ip = new InteractivePointLight(k, vol); } InteractiveObject obj = new InteractiveObject(ip); controlPoints.Add(obj); } #endregion 5. С помощью сплайна уже можно строить гладкую улыбку // ReSharper disable once UseObjectOrCollectionInitializer InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); var baseSec = optSer.UnderlyingAsset; DateTime scriptTime = baseSec.Bars[baseSec.Bars.Count - 1].Date; // ReSharper disable once UseObjectOrCollectionInitializer SmileInfo info = new SmileInfo(); info.F = futPx; info.dT = effectiveTime; info.Expiry = optSer.ExpirationDate; info.ScriptTime = scriptTime; info.RiskFreeRate = ratePct; info.BaseTicker = baseSec.Symbol; info.ContinuousFunction = spline; info.ContinuousFunctionD1 = splineD1; res.Tag = info; return(res); }
public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, int barNum) { int barsCount = ContextBarsCount; if (barNum < barsCount - 1) { return(Constants.EmptySeries); } //double F = prices[prices.Count - 1]; double dT = time; if (!DoubleUtil.IsPositive(dT)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); m_context.Log(msg, MessageType.Error, true); return(Constants.EmptySeries); } if (smile == null) { string msg = String.Format("[{0}] Argument 'smile' must be filled with InteractiveSeries.", GetType().Name); m_context.Log(msg, MessageType.Error, true); return(Constants.EmptySeries); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if (oldInfo == null) { string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag); m_context.Log(msg, MessageType.Error, true); return(Constants.EmptySeries); } double f = m_minStrike; List <double> xs = new List <double>(); List <double> ys = new List <double>(); IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); while (f <= m_maxStrike) { double rawDelta; GetBaseDelta(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out rawDelta); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double totalDelta; //double putDelta = FinMath.GetOptionDelta(f, pair.Strike, dT, sigma.Value, 0, false); GetPairDelta(posMan, smile, pair, f, dT, out totalDelta); rawDelta += totalDelta; } InteractivePointActive ip = new InteractivePointActive(); ip.IsActive = m_showNodes; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = AlphaColors.Green; double y = rawDelta; ip.Value = new Point(f, y); string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture); ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "F:{0}; D:{1}", f, yStr); controlPoints.Add(new InteractiveObject(ip)); xs.Add(f); ys.Add(y); f += m_strikeStep; } InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { SmileInfo info = new SmileInfo(); info.F = oldInfo.F; info.dT = oldInfo.dT; info.RiskFreeRate = oldInfo.RiskFreeRate; NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } return(res); }
public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, double btcUsdIndex, int barNum) { int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (smile == null) || (optSer == null)) { return(Constants.EmptySeries); } int lastBarIndex = optSer.UnderlyingAsset.Bars.Count - 1; DateTime now = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Date; bool wasInitialized = HandlerInitializedToday(now); double dT = time; if (!DoubleUtil.IsPositive(dT)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } if (!DoubleUtil.IsPositive(btcUsdIndex)) { // TODO: сделать ресурс! [{0}] Price of BTC/USD must be positive value. scaleMult:{1} //string msg = RM.GetStringFormat("OptHandlerMsg.CurrencyScaleMustBePositive", GetType().Name, scaleMult); string msg = String.Format("[{0}] Price of BTC/USD must be positive value. scaleMult:{1}", GetType().Name, btcUsdIndex); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if (oldInfo == null) { string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.EmptySeries); } double futPx = oldInfo.F; double ivAtm; if (!oldInfo.ContinuousFunction.TryGetValue(futPx, out ivAtm)) { string msg = String.Format("[{0}] Unable to get IV ATM from smile. Tag:{1}", GetType().Name, smile.Tag); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); if (pairs.Length < 2) { string msg = String.Format("[{0}] optSer must contain few strike pairs. pairs.Length:{1}", GetType().Name, pairs.Length); if (wasInitialized) { m_context.Log(msg, MessageType.Warning, true); } return(Constants.EmptySeries); } List <double> xs = new List <double>(); List <double> ys = new List <double>(); double futStep = optSer.UnderlyingAsset.Tick; PositionsManager posMan = PositionsManager.GetManager(m_context); ReadOnlyCollection <IPosition> basePositions = posMan.GetClosedOrActiveForBar(optSer.UnderlyingAsset); var optPositions = SingleSeriesProfile.GetAllOptionPositions(posMan, pairs); SortedDictionary <double, IOptionStrikePair> futPrices; if (!SmileImitation5.TryPrepareImportantPoints(pairs, futPx, futStep, -1, out futPrices)) { string msg = String.Format("[{0}] It looks like there is no suitable points for the smile. pairs.Length:{1}", GetType().Name, pairs.Length); if (wasInitialized) { m_context.Log(msg, MessageType.Warning, true); } return(Constants.EmptySeries); } // Чтобы учесть базис между фьючерсом и индексом, вычисляю их отношение: // Пример: BtcInd==9023; FutPx==8937 --> indexDivByFutRatio == 1.009623 double indexDivByFutRatio = btcUsdIndex / futPx; List <InteractiveObject> controlPoints = new List <InteractiveObject>(); // Список точек для вычисления дельт + улыбки для этих точек var deltaPoints = new List <Tuple <double, InteractivePointActive> >(); foreach (var kvp in futPrices) { // Если у нас новая цена фьючерса, значит, будет новая цена индекса double f = kvp.Key; // И при пересчете опционов в баксы НУЖНО ИСПОЛЬЗОВАТЬ ИМЕННО ЕЁ!!! double newScaleMult = f * indexDivByFutRatio; bool tradableStrike = (kvp.Value != null); CashPnlUsd cashPnlUsd; CashPnlBtc cashPnlBtc; GetBasePnl(basePositions, lastBarIndex, f, m_futNominal, out cashPnlUsd, out cashPnlBtc); double cashDollars = cashPnlUsd.CashUsd; double pnlDollars = cashPnlUsd.PnlUsd; double cashBtc = cashPnlBtc.CashBtc; double pnlBtc = cashPnlBtc.PnlBtc; SmileInfo actualSmile = SingleSeriesProfile.GetActualSmile(oldInfo, m_greekAlgo, f); // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect = true; for (int j = 0; (j < pairs.Length) && pnlIsCorrect; j++) { var tuple = optPositions[j]; IOptionStrikePair pair = pairs[j]; //int putBarCount = pair.Put.UnderlyingAsset.Bars.Count; //int callBarCount = pair.Call.UnderlyingAsset.Bars.Count; CashPnlUsd pairCashUsd; CashPnlBtc pairCashBtc; bool localRes = TryGetPairPnl(actualSmile, pair.Strike, lastBarIndex, lastBarIndex, tuple.Item1, tuple.Item2, f, dT, newScaleMult, out pairCashUsd, out pairCashBtc); pnlIsCorrect &= localRes; cashDollars += pairCashUsd.CashUsd; pnlDollars += pairCashUsd.PnlUsd; cashBtc += pairCashBtc.CashBtc; pnlBtc += pairCashBtc.PnlBtc; } // Профиль позиции будет рисоваться только если ПНЛ был посчитан верно по ВСЕМ инструментам! if (pnlIsCorrect) { InteractivePointLight ip; // Показаны будут только узлы, совпадающие с реальными страйками. // Потенциально это позволит сделать эти узлы пригодными для торговли по клику наподобие улыбки. if (m_showNodes && tradableStrike) { // ReSharper disable once UseObjectOrCollectionInitializer InteractivePointActive tmp = new InteractivePointActive(); tmp.IsActive = m_showNodes && tradableStrike; string pnlUsdStr = (cashDollars + pnlDollars).ToString(m_tooltipFormat, CultureInfo.InvariantCulture); string pnlBtcStr = (cashBtc + pnlBtc).ToString(DefaultBtcTooltipFormat, CultureInfo.InvariantCulture); tmp.Tooltip = String.Format(CultureInfo.InvariantCulture, " F: {0}\r\n PnL($): {1}\r\n PnL(B): {2}", f, pnlUsdStr, pnlBtcStr); // Если у нас получился сплайн по профилю позиции, значит мы можем вычислить дельту! if (m_showNodes && tradableStrike) { // Готовим важные точки var tuple = new Tuple <double, InteractivePointActive>(f, tmp); deltaPoints.Add(tuple); } ip = tmp; } else { ip = new InteractivePointLight(); } // PROD-6103 - Выводить профиль позиции в биткойнах if (ProfileAsBtc) { ip.Value = new Point(f, cashBtc + pnlBtc); } else { ip.Value = new Point(f, cashDollars + pnlDollars); } controlPoints.Add(new InteractiveObject(ip)); xs.Add(f); // PROD-6103 - Выводить профиль позиции в биткойнах if (ProfileAsBtc) { ys.Add(cashBtc + pnlBtc); } else { ys.Add(cashDollars + pnlDollars); } } // End if (pnlIsCorrect) } // End foreach (var kvp in futPrices) // ReSharper disable once UseObjectOrCollectionInitializer InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { SmileInfo info = new SmileInfo(); info.F = oldInfo.F; info.dT = oldInfo.dT; info.RiskFreeRate = oldInfo.RiskFreeRate; NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; // Если у нас получился сплайн по профилю позиции, значит мы можем вычислить дельту! for (int j = 1; j < deltaPoints.Count - 1; j++) { var tuple = deltaPoints[j]; double f = tuple.Item1; var ip = tuple.Item2; double actualDelta, deltaLeft, deltaRight; if (m_twoSideDelta) { double prevF = deltaPoints[j - 1].Item1; double nextF = deltaPoints[j + 1].Item1; double currY = deltaPoints[j].Item2.ValueY; double prevY = deltaPoints[j - 1].Item2.ValueY; double nextY = deltaPoints[j + 1].Item2.ValueY; deltaLeft = (currY - prevY) / (f - prevF); deltaRight = (nextY - currY) / (nextF - f); // Считаем дельты слева и справа // Мы передвинули улыбку в точку f и считаем дельту позиции В ЭТОЙ ЖЕ ТОЧКЕ(!) //if (info.ContinuousFunction.TryGetValue(f - 100 * futStep, out deltaLeft) && // info.ContinuousFunctionD1.TryGetValue(f + 100 * futStep, out deltaRight)) { // Первый пробел уже учтен в Tooltip ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "{0}\r\n LeftD: {1:0.0}; RightD: {2:0.0}", ip.Tooltip, deltaLeft, deltaRight); } } else { // Мы передвинули улыбку в точку f и считаем дельту позиции В ЭТОЙ ЖЕ ТОЧКЕ(!) if (info.ContinuousFunctionD1.TryGetValue(f, out actualDelta)) { // Первый пробел уже учтен в Tooltip ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "{0}\r\n D: {1:0.0}", ip.Tooltip, actualDelta); } } } } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } SetHandlerInitialized(now, true); return(res); }
public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, int barNum) { InteractiveSeries res = new InteractiveSeries(); int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (optSer == null)) { return(res); } double dT = time; if (Double.IsNaN(dT) || (dT < Double.Epsilon)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); m_context.Log(msg, MessageType.Error, true); return(res); } if (smile == null) { string msg = String.Format("[{0}] Argument 'smile' must be filled with InteractiveSeries.", GetType().Name); m_context.Log(msg, MessageType.Error, true); return(res); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if (oldInfo == null) { string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag); m_context.Log(msg, MessageType.Error, true); return(res); } List <double> xs = new List <double>(); List <double> ys = new List <double>(); var smilePoints = smile.ControlPoints; IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); foreach (InteractiveObject iob in smilePoints) { double rawTheta, f = iob.Anchor.ValueX; if (TryEstimateTheta(posMan, pairs, smile, m_greekAlgo, f, dT, m_tStep, out rawTheta)) { // Переводим тету в дифференциал 'изменение цены за 1 сутки'. rawTheta = RescaleThetaToDays(m_tRemainMode, rawTheta); // ReSharper disable once UseObjectOrCollectionInitializer InteractivePointActive ip = new InteractivePointActive(); ip.IsActive = m_showNodes; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = System.Windows.Media.Colors.Green; double y = rawTheta; ip.Value = new Point(f, y); string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture); ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "F:{0}; Th:{1}", f, yStr); controlPoints.Add(new InteractiveObject(ip)); xs.Add(f); ys.Add(y); } } res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { // ReSharper disable once UseObjectOrCollectionInitializer SmileInfo info = new SmileInfo(); info.F = oldInfo.F; info.dT = oldInfo.dT; info.RiskFreeRate = oldInfo.RiskFreeRate; NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } return(res); }
public InteractiveSeries Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, double scaleMult, double ratePct, int barNum) { int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (smile == null) || (optSer == null)) { return(Constants.EmptySeries); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if ((oldInfo == null) || (oldInfo.ContinuousFunction == null) || (oldInfo.ContinuousFunctionD1 == null)) { return(Constants.EmptySeries); } int lastBarIndex = optSer.UnderlyingAsset.Bars.Count - 1; DateTime now = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Date; bool wasInitialized = HandlerInitializedToday(now); double futPx = price; double dT = time; if (!DoubleUtil.IsPositive(dT)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } if (!DoubleUtil.IsPositive(futPx)) { // [{0}] Base asset price must be positive value. F:{1} string msg = RM.GetStringFormat("OptHandlerMsg.FutPxMustBePositive", GetType().Name, futPx); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } if (!DoubleUtil.IsPositive(scaleMult)) { //throw new ScriptException("Argument 'scaleMult' contains NaN for some strange reason. scaleMult:" + scaleMult); return(Constants.EmptySeries); } if (Double.IsNaN(ratePct)) { //throw new ScriptException("Argument 'ratePct' contains NaN for some strange reason. rate:" + rate); return(Constants.EmptySeries); } double ivAtm, slopeAtm, shape; if ((!oldInfo.ContinuousFunction.TryGetValue(futPx, out ivAtm)) || (!oldInfo.ContinuousFunctionD1.TryGetValue(futPx, out slopeAtm))) { return(Constants.EmptySeries); } if (m_setIvByHands) { ivAtm = m_ivAtmPct.Value / Constants.PctMult; } if (m_setSlopeByHands) { slopeAtm = m_slopePct.Value / Constants.PctMult; //slopeAtm = slopeAtm / F / Math.Pow(dT, Pow + shape); } //if (setShapeByHands) { shape = m_shapePct.Value / Constants.PctMult; } if (!DoubleUtil.IsPositive(ivAtm)) { // [{0}] ivAtm must be positive value. ivAtm:{1} string msg = RM.GetStringFormat("OptHandlerMsg.IvAtmMustBePositive", GetType().Name, ivAtm); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } if (Double.IsNaN(slopeAtm)) { // [{0}] Smile skew at the money must be some number. skewAtm:{1} string msg = RM.GetStringFormat("OptHandlerMsg.SkewAtmMustBeNumber", GetType().Name, slopeAtm); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } SmileInfo templateInfo; #region Fill templateInfo if (m_useLocalTemplate) { InteractiveSeries templateSmile = m_context.LoadObject(m_frozenSmileId) as InteractiveSeries; if (templateSmile == null) { // [{0}] There is no LOCAL smile with ID:{1} string msg = RM.GetStringFormat("SmileImitation5.NoLocalSmile", GetType().Name, m_frozenSmileId); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } SmileInfo locInfo = new SmileInfo(); locInfo.F = futPx; locInfo.dT = dT; locInfo.RiskFreeRate = oldInfo.RiskFreeRate; List <double> locXs = new List <double>(); List <double> locYs = new List <double>(); foreach (InteractiveObject oldObj in templateSmile.ControlPoints) { if (!oldObj.AnchorIsActive) { continue; } double k = oldObj.Anchor.ValueX; double sigma = oldObj.Anchor.ValueY; double x = Math.Log(k / futPx) / Math.Pow(dT, DefaultPow + shape) / ivAtm; double sigmaNormalized = sigma / ivAtm; locXs.Add(x); locYs.Add(sigmaNormalized); } try { NotAKnotCubicSpline spline = new NotAKnotCubicSpline(locXs, locYs); locInfo.ContinuousFunction = spline; locInfo.ContinuousFunctionD1 = spline.DeriveD1(); templateInfo = locInfo; } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } } else { //templateSmile = context.LoadGlobalObject(globalSmileID, true) as InteractiveSeries; templateInfo = m_context.LoadGlobalObject(m_globalSmileId, true) as SmileInfo; if (templateInfo == null) { // [{0}] There is no global templateInfo with ID:{1}. I'll try to use default one. string msg = RM.GetStringFormat("SmileImitation5.TemplateWasSaved", GetType().Name, m_globalSmileId); m_context.Log(msg, MessageType.Error, true); System.Xml.Linq.XDocument xDoc = System.Xml.Linq.XDocument.Parse(SmileFunction5.XmlSmileRiz4Nov1); System.Xml.Linq.XElement xInfo = xDoc.Root; SmileInfo templateSmile = SmileInfo.FromXElement(xInfo); // Обновляю уровень IV ATM? if (Double.IsNaN(ivAtm)) { ivAtm = oldInfo.ContinuousFunction.Value(futPx); m_context.Log(String.Format("[DEBUG:{0}] ivAtm was NaN. I'll use value ivAtm:{1}", GetType().Name, ivAtm), MessageType.Warning, true); if (Double.IsNaN(ivAtm)) { throw new Exception(String.Format("[DEBUG:{0}] ivAtm is NaN.", GetType().Name)); } } templateSmile.F = futPx; templateSmile.dT = dT; templateSmile.RiskFreeRate = oldInfo.RiskFreeRate; m_context.StoreGlobalObject(m_globalSmileId, templateSmile, true); // [{0}] Default templateInfo was saved to Global Cache with ID:{1}. msg = RM.GetStringFormat("SmileImitation5.TemplateWasSaved", GetType().Name, m_globalSmileId); m_context.Log(msg, MessageType.Warning, true); templateInfo = templateSmile; } } #endregion Fill templateInfo if (!m_setIvByHands) { m_ivAtmPct.Value = ivAtm * Constants.PctMult; } if (!m_setShapeByHands) { // так я ещё не умею } if (!m_setSlopeByHands) { // Пересчитываю наклон в безразмерку double dSigmaDx = slopeAtm * futPx * Math.Pow(dT, DefaultPow + shape); m_slopePct.Value = dSigmaDx * Constants.PctMult; // и теперь апдейчу локальную переменную slopeAtm: slopeAtm = m_slopePct.Value / Constants.PctMult; } // Это функция в нормированных координатах // поэтому достаточно обычной симметризации // PROD-3111: заменяю вызов на SmileFunctionExtended //SimmetrizeFunc simmFunc = new SimmetrizeFunc(templateInfo.ContinuousFunction); //SimmetrizeFunc simmFuncD1 = new SimmetrizeFunc(templateInfo.ContinuousFunctionD1); //SmileFunction5 smileFunc = new SmileFunction5(simmFunc, simmFuncD1, ivAtm, slopeAtm, shape, futPx, dT); SmileFunctionExtended smileFunc = new SmileFunctionExtended( (NotAKnotCubicSpline)templateInfo.ContinuousFunction, ivAtm, slopeAtm, shape, futPx, dT); smileFunc.UseTails = m_useSmileTails; List <double> xs = new List <double>(); List <double> ys = new List <double>(); IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); if (pairs.Length < 2) { string msg = String.Format("[{0}] optSer must contain few strike pairs. pairs.Length:{1}", GetType().Name, pairs.Length); if (wasInitialized) { m_context.Log(msg, MessageType.Warning, true); } return(Constants.EmptySeries); } double minK = pairs[0].Strike; double maxK = pairs[pairs.Length - 1].Strike; double futStep = optSer.UnderlyingAsset.Tick; double dK = pairs[1].Strike - pairs[0].Strike; double width = (SigmaMult * ivAtm * Math.Sqrt(dT)) * futPx; width = Math.Max(width, 10 * dK); // Нельзя вылезать за границу страйков??? width = Math.Min(width, Math.Abs(futPx - minK)); width = Math.Min(width, Math.Abs(maxK - futPx)); // Generate left invisible tail if (m_generateTails) { GaussSmile.AppendLeftTail(smileFunc, xs, ys, minK, dK, false); } SortedDictionary <double, IOptionStrikePair> strikePrices; if (!SmileImitation5.TryPrepareImportantPoints(pairs, futPx, futStep, width, out strikePrices)) { string msg = String.Format("[{0}] It looks like there is no suitable points for the smile. pairs.Length:{1}", GetType().Name, pairs.Length); if (wasInitialized) { m_context.Log(msg, MessageType.Warning, true); } return(Constants.EmptySeries); } List <InteractiveObject> controlPoints = new List <InteractiveObject>(); //for (int j = 0; j < pairs.Length; j++) foreach (var kvp in strikePrices) { bool showPoint = (kvp.Value != null); //IOptionStrikePair pair = pairs[j]; //// Сверхдалекие страйки игнорируем //if ((pair.Strike < futPx - width) || (futPx + width < pair.Strike)) //{ // showPoint = false; //} //double k = pair.Strike; double k = kvp.Key; double sigma; if (!smileFunc.TryGetValue(k, out sigma)) { continue; } double vol = sigma; if (!DoubleUtil.IsPositive(sigma)) { continue; } //InteractivePointActive ip = new InteractivePointActive(k, vol); //ip.Color = (optionPxMode == OptionPxMode.Ask) ? Colors.DarkOrange : Colors.DarkCyan; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect; // Иначе неправильно выставляются координаты??? //ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", k, PctMult * sigma); InteractivePointLight ip; if (showPoint) { var tip = new InteractivePointActive(k, vol); if (k <= futPx) // Puts { SmileImitationDeribit5.FillNodeInfo(tip, futPx, dT, kvp.Value, StrikeType.Put, OptionPxMode.Mid, sigma, false, m_showNodes, scaleMult, ratePct); } else // Calls { SmileImitationDeribit5.FillNodeInfo(tip, futPx, dT, kvp.Value, StrikeType.Call, OptionPxMode.Mid, sigma, false, m_showNodes, scaleMult, ratePct); } ip = tip; } else { ip = new InteractivePointLight(k, vol); } InteractiveObject obj = new InteractiveObject(ip); //if (showPoint) // Теперь мы понимаем, что точки либо рисуются // потому что это страйки (и тогда они автоматом InteractivePointActive) // либо они присутствуют, но не рисуются их узлы. Потому что они InteractivePointLight. { controlPoints.Add(obj); } xs.Add(k); ys.Add(vol); } // ReSharper disable once UseObjectOrCollectionInitializer InteractiveSeries res = new InteractiveSeries(); res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); // Generate right invisible tail if (m_generateTails) { GaussSmile.AppendRightTail(smileFunc, xs, ys, maxK, dK, false); } var baseSec = optSer.UnderlyingAsset; DateTime scriptTime = baseSec.Bars[baseSec.Bars.Count - 1].Date; // ReSharper disable once UseObjectOrCollectionInitializer SmileInfo info = new SmileInfo(); info.F = futPx; info.dT = dT; info.Expiry = optSer.ExpirationDate; info.ScriptTime = scriptTime; info.RiskFreeRate = ratePct; info.BaseTicker = baseSec.Symbol; info.ContinuousFunction = smileFunc; info.ContinuousFunctionD1 = smileFunc.DeriveD1(); res.Tag = info; SetHandlerInitialized(now); return(res); }
public InteractiveSeries Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, int barNum) { int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (optSer == null)) { return(Constants.EmptySeries); } int lastBarIndex = optSer.UnderlyingAsset.Bars.Count - 1; DateTime now = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Date; bool wasInitialized = HandlerInitializedToday(now); double futPx = price; double dT = time; if (!DoubleUtil.IsPositive(dT)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.EmptySeries); } if (!DoubleUtil.IsPositive(futPx)) { // [{0}] Base asset price must be positive value. F:{1} string msg = RM.GetStringFormat("OptHandlerMsg.FutPxMustBePositive", GetType().Name, futPx); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.EmptySeries); } if (smile == null) { string msg = String.Format("[{0}] Argument 'smile' must be filled with InteractiveSeries.", GetType().Name); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.EmptySeries); } if (m_optionType == StrikeType.Any) { string msg = String.Format("[{0}] OptionType '{1}' is not supported.", GetType().Name, m_optionType); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.EmptySeries); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if (oldInfo == null) { string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.EmptySeries); } bool isCall = m_optionType == StrikeType.Call; double putQty = isCall ? 0 : m_fixedQty; double callQty = isCall ? m_fixedQty : 0; double riskFreeRate = 0; List <double> xs = new List <double>(); List <double> ys = new List <double>(); var smilePoints = smile.ControlPoints; IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); foreach (IOptionStrikePair pair in pairs) { double rawTheta; if (TryEstimateTheta(putQty, callQty, optSer, pair, smile, m_greekAlgo, futPx, dT, m_tStep, riskFreeRate, out rawTheta)) { // Переводим тету в дифференциал 'изменение цены за 1 сутки'. rawTheta /= OptionUtils.DaysInYear; InteractivePointActive ip = new InteractivePointActive(); ip.IsActive = m_showNodes; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = System.Windows.Media.Colors.Green; double y = rawTheta; ip.Value = new Point(pair.Strike, y); string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture); ip.Tooltip = String.Format("K:{0}; Th:{1}", pair.Strike, yStr); controlPoints.Add(new InteractiveObject(ip)); xs.Add(pair.Strike); ys.Add(y); } } InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { SmileInfo info = new SmileInfo(); info.F = oldInfo.F; info.dT = oldInfo.dT; info.RiskFreeRate = oldInfo.RiskFreeRate; NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } SetHandlerInitialized(now); return(res); }
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); }