/// <summary> /// Тета будет иметь размерность 'пункты за год'. /// Обычно же опционщики любят смотреть размерность 'пункты за день'. /// Поэтому полученное сырое значение ещё надо делить на количество дней в году. /// (Эквивалентно умножению на интересующий набег времени для получения дифференциала). /// </summary> internal static bool TryEstimateTheta(PositionsManager posMan, IOptionStrikePair[] pairs, InteractiveSeries smile, NumericalGreekAlgo greekAlgo, double f, double timeToExpiry, double tStep, out double rawTheta) { rawTheta = Double.NaN; if (timeToExpiry < Double.Epsilon) { throw new ArgumentOutOfRangeException("timeToExpiry", "timeToExpiry must be above zero. timeToExpiry:" + timeToExpiry); } SmileInfo sInfo = smile.GetTag <SmileInfo>(); if (sInfo == null) { return(false); } double t1 = (timeToExpiry - tStep > Double.Epsilon) ? (timeToExpiry - tStep) : (0.5 * timeToExpiry); double cash1 = 0, pnl1 = 0; // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect1 = true; { // TODO: фьюч на даёт вклад в тету??? //SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out cash1, out pnl1); //// 1. Изменение положения БА //InteractiveSeries actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f); SmileInfo actualSmile; // 1. Изменение положения БА actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f); // 2. Изменение времени // ВАЖНО: нормальный алгоритм сдвига улыбки во времени будет в платной версии "Пакета Каленковича" actualSmile = SingleSeriesProfile.GetSmileAtTime(actualSmile, NumericalGreekAlgo.FrozenSmile, t1); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double pairPnl, pairCash; //double putPx = FinMath.GetOptionPrice(f, pair.Strike, dT, sigma.Value, 0, false); //SingleSeriesProfile.GetPairPnl(posMan, actualSmile, pair, f, t1, out pairCash, out pairPnl); pnlIsCorrect1 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f, t1, out pairCash, out pairPnl); cash1 += pairCash; pnl1 += pairPnl; } } double t2 = timeToExpiry + tStep; double cash2 = 0, pnl2 = 0; // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect2 = true; { // фьюч на даёт вклад в вегу //SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out cash2, out pnl2); //// 1. Изменение положения БА //InteractiveSeries actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f); SmileInfo actualSmile; // 1. Изменение положения БА actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f); // 2. Изменение времени // ВАЖНО: нормальный алгоритм сдвига улыбки во времени будет в платной версии "Пакета Каленковича" actualSmile = SingleSeriesProfile.GetSmileAtTime(actualSmile, NumericalGreekAlgo.FrozenSmile, t2); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double pairPnl, pairCash; //double putPx = FinMath.GetOptionPrice(f, pair.Strike, dT, sigma.Value, 0, false); //SingleSeriesProfile.GetPairPnl(posMan, actualSmile, pair, f, t2, out pairCash, out pairPnl); pnlIsCorrect2 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f, t2, out pairCash, out pairPnl); cash2 += pairCash; pnl2 += pairPnl; } } if (pnlIsCorrect1 && pnlIsCorrect2) { rawTheta = ((cash2 + pnl2) - (cash1 + pnl1)) / (t2 - t1); // Переворачиваю тету, чтобы жить в календарном времени rawTheta = -rawTheta; return(true); } else { return(false); } }
internal static bool TryEstimateDelta(double putQty, double callQty, IOptionSeries optSer, IOptionStrikePair pair, InteractiveSeries smile, NumericalGreekAlgo greekAlgo, double f, double dF, double timeToExpiry, double riskFreeRate, out double rawDelta) { rawDelta = Double.NaN; if (timeToExpiry < Double.Epsilon) { throw new ArgumentOutOfRangeException("timeToExpiry", "timeToExpiry must be above zero. timeToExpiry:" + timeToExpiry); } SmileInfo sInfo = smile.Tag as SmileInfo; double pnl1 = 0; // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect1 = true; { if (sInfo != null) { SmileInfo actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f - dF); double pairPnl; pnlIsCorrect1 &= SingleSeriesProfile.TryGetPairPrice( putQty, callQty, actualSmile, pair, f - dF, timeToExpiry, riskFreeRate, out pairPnl); pnl1 += pairPnl; } else { // PROD-5746 -- Убираю использование старого неэффективного кода pnlIsCorrect1 = false; Contract.Assert(pnlIsCorrect1, $"[{nameof(OptionsBoardNumericalDelta)}.{nameof(TryEstimateDelta)}] #1 Каким образом получили неподготовленную улыбку? (sInfo == null)"); //InteractiveSeries actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f - dF); //double pairPnl; //pnlIsCorrect1 &= SingleSeriesProfile.TryGetPairPrice( // putQty, callQty, actualSmile, pair, f - dF, timeToExpiry, riskFreeRate, out pairPnl); //pnl1 += pairPnl; } } double pnl2 = 0; // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect2 = true; { if (sInfo != null) { SmileInfo actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f + dF); double pairPnl; pnlIsCorrect2 &= SingleSeriesProfile.TryGetPairPrice( putQty, callQty, actualSmile, pair, f + dF, timeToExpiry, riskFreeRate, out pairPnl); pnl2 += pairPnl; } else { // PROD-5746 -- Убираю использование старого неэффективного кода pnlIsCorrect2 = false; Contract.Assert(pnlIsCorrect2, $"[{nameof(OptionsBoardNumericalDelta)}.{nameof(TryEstimateDelta)}] #2 Каким образом получили неподготовленную улыбку? (sInfo == null)"); //InteractiveSeries actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f + dF); //double pairPnl; //pnlIsCorrect2 &= SingleSeriesProfile.TryGetPairPrice( // putQty, callQty, actualSmile, pair, f + dF, timeToExpiry, riskFreeRate, out pairPnl); //pnl2 += pairPnl; } } if (pnlIsCorrect1 && pnlIsCorrect2) { //rawDelta = ((cash2 + pnl2) - (cash1 + pnl1)) / 2.0 / dF; rawDelta = (pnl2 - pnl1) / 2.0 / dF; return(true); } else { return(false); } }
/// <summary> /// Вомма будет иметь размерность 'пункты за 100% волатильности'. /// Обычно же опционщики любят смотреть размерность 'пункты за 1% волатильности'. /// Поэтому полученное сырое значение ещё надо делить на 100%. /// (Эквивалентно умножению на интересующий набег волы для получения дифференциала). /// </summary> internal static bool TryEstimateVomma(PositionsManager posMan, IOptionSeries optSer, IOptionStrikePair[] pairs, InteractiveSeries smile, NumericalGreekAlgo greekAlgo, double f, double dSigma, double timeToExpiry, out double rawVomma) { rawVomma = Double.NaN; if (timeToExpiry < Double.Epsilon) { throw new ArgumentOutOfRangeException("timeToExpiry", "timeToExpiry must be above zero. timeToExpiry:" + timeToExpiry); } SmileInfo sInfo = smile.GetTag <SmileInfo>(); if (sInfo == null) { return(false); } double cash1 = 0, pnl1 = 0; // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect1 = true; { // фьюч на даёт вклад в вегу //SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out cash1, out pnl1); //InteractiveSeries actualSmile; //// 1. Изменение положения БА //actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f); SmileInfo actualSmile; // 1. Изменение положения БА actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double pairPnl, pairCash; pnlIsCorrect1 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f, timeToExpiry, out pairCash, out pairPnl); cash1 += pairCash; pnl1 += pairPnl; } } double cash2 = 0, pnl2 = 0; // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect2 = true; { // фьюч на даёт вклад в вегу //SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out cash2, out pnl2); //InteractiveSeries actualSmile; //// 1. Изменение положения БА //actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f); //// 2. Подъём улыбки на dSigma //actualSmile = SingleSeriesProfile.GetRaisedSmile(actualSmile, greekAlgo, dSigma); SmileInfo actualSmile; // 1. Изменение положения БА actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f); // 2. Подъём улыбки на dSigma actualSmile = SingleSeriesProfile.GetRaisedSmile(actualSmile, greekAlgo, dSigma); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double pairPnl, pairCash; pnlIsCorrect2 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f, timeToExpiry, out pairCash, out pairPnl); cash2 += pairCash; pnl2 += pairPnl; } } double cash3 = 0, pnl3 = 0; // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect3 = true; { // фьюч на даёт вклад в вегу //SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out cash2, out pnl2); //InteractiveSeries actualSmile; //// 1. Изменение положения БА //actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f); //// 2. Подъём улыбки на dSigma //actualSmile = SingleSeriesProfile.GetRaisedSmile(actualSmile, greekAlgo, dSigma); SmileInfo actualSmile; // 1. Изменение положения БА actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f); // 2. Подъём улыбки на 2*dSigma actualSmile = SingleSeriesProfile.GetRaisedSmile(actualSmile, greekAlgo, 2 * dSigma); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double pairPnl, pairCash; pnlIsCorrect2 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f, timeToExpiry, out pairCash, out pairPnl); cash3 += pairCash; pnl3 += pairPnl; } } if (pnlIsCorrect1 && pnlIsCorrect2 && pnlIsCorrect3) { // См. Вики случай r=2, N=2: https://ru.wikipedia.org/wiki/Численное_дифференцирование // f''(x0) ~= (f0 - 2*f1 + f2)/h/h rawVomma = ((cash1 + pnl1) - 2 * (cash2 + pnl2) + (cash3 + pnl3)) / dSigma / dSigma; return(true); } else { return(false); } }
/// <summary> /// Вега будет иметь размерность 'пункты за 100% волатильности'. /// Обычно же опционщики любят смотреть размерность 'пункты за 1% волатильности'. /// Поэтому полученное сырое значение ещё надо делить на 100%. /// (Эквивалентно умножению на интересующий набег волы для получения дифференциала). /// </summary> internal static bool TryEstimateVega(PositionsManager posMan, IOptionSeries optSer, IOptionStrikePair[] pairs, InteractiveSeries smile, NumericalGreekAlgo greekAlgo, double f, double dSigma, double timeToExpiry, out double rawVega) { rawVega = Double.NaN; if (timeToExpiry < Double.Epsilon) { throw new ArgumentOutOfRangeException("timeToExpiry", "timeToExpiry must be above zero. timeToExpiry:" + timeToExpiry); } SmileInfo sInfo = smile.GetTag <SmileInfo>(); if (sInfo == null) { return(false); } double cash1 = 0, pnl1 = 0; // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect1 = true; { // фьюч на даёт вклад в вегу //SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out cash1, out pnl1); //InteractiveSeries actualSmile; //// 1. Изменение положения БА //actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f); SmileInfo actualSmile; // 1. Изменение положения БА actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double pairPnl, pairCash; //double putPx = FinMath.GetOptionPrice(f, pair.Strike, dT, sigma.Value, 0, false); //SingleSeriesProfile.GetPairPnl(posMan, actualSmile, pair, f, timeToExpiry, out pairCash, out pairPnl); pnlIsCorrect1 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f, timeToExpiry, out pairCash, out pairPnl); cash1 += pairCash; pnl1 += pairPnl; } } double cash2 = 0, pnl2 = 0; // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect2 = true; { // фьюч на даёт вклад в вегу //SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out cash2, out pnl2); //InteractiveSeries actualSmile; //// 1. Изменение положения БА //actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f); //// 2. Подъём улыбки на dSigma //actualSmile = SingleSeriesProfile.GetRaisedSmile(actualSmile, greekAlgo, dSigma); SmileInfo actualSmile; // 1. Изменение положения БА actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f); // 2. Подъём улыбки на dSigma actualSmile = SingleSeriesProfile.GetRaisedSmile(actualSmile, greekAlgo, dSigma); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double pairPnl, pairCash; //double putPx = FinMath.GetOptionPrice(f, pair.Strike, dT, sigma.Value, 0, false); //SingleSeriesProfile.GetPairPnl(posMan, actualSmile, pair, f, timeToExpiry, out pairCash, out pairPnl); pnlIsCorrect2 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f, timeToExpiry, out pairCash, out pairPnl); cash2 += pairCash; pnl2 += pairPnl; } } if (pnlIsCorrect1 && pnlIsCorrect2) { // Первая точка совпадает с текущей, поэтому нет деления на 2. rawVega = ((cash2 + pnl2) - (cash1 + pnl1)) / dSigma; return(true); } else { return(false); } }
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); }
internal static bool TryEstimateDelta(PositionsManager posMan, IOptionSeries optSer, IOptionStrikePair[] pairs, InteractiveSeries smile, NumericalGreekAlgo greekAlgo, double f, double dF, double timeToExpiry, out double rawDelta) { rawDelta = Double.NaN; if (timeToExpiry < Double.Epsilon) { throw new ArgumentOutOfRangeException("timeToExpiry", "timeToExpiry must be above zero. timeToExpiry:" + timeToExpiry); } SmileInfo sInfo = smile.GetTag <SmileInfo>(); if (sInfo == null) { Contract.Assert(false, $"[{nameof(SingleSeriesNumericalDelta)}.{nameof(TryEstimateDelta)}] #1 Каким образом получили неподготовленную улыбку? (sInfo == null)"); return(false); } double cash1, pnl1; // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect1 = true; { SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f - dF, out cash1, out pnl1); // PROD-5746 -- Убираю использование старого неэффективного кода //InteractiveSeries actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f - dF); SmileInfo actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f - dF); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double pairPnl, pairCash; //double putPx = FinMath.GetOptionPrice(f, pair.Strike, dT, sigma.Value, 0, false); //SingleSeriesProfile.GetPairPnl(posMan, actualSmile, pair, f - dF, timeToExpiry, out pairCash, out pairPnl); pnlIsCorrect1 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f - dF, timeToExpiry, out pairCash, out pairPnl); cash1 += pairCash; pnl1 += pairPnl; } } double cash2, pnl2; // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect2 = true; { SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f + dF, out cash2, out pnl2); // PROD-5746 -- Убираю использование старого неэффективного кода //InteractiveSeries actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f + dF); SmileInfo actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f + dF); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double pairPnl, pairCash; //double putPx = FinMath.GetOptionPrice(f, pair.Strike, dT, sigma.Value, 0, false); //SingleSeriesProfile.GetPairPnl(posMan, actualSmile, pair, f + dF, timeToExpiry, out pairCash, out pairPnl); pnlIsCorrect2 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f + dF, timeToExpiry, out pairCash, out pairPnl); cash2 += pairCash; pnl2 += pairPnl; } } if (pnlIsCorrect1 && pnlIsCorrect2) { rawDelta = ((cash2 + pnl2) - (cash1 + pnl1)) / 2.0 / dF; return(true); } else { return(false); } }