protected override bool TryCalculate(Dictionary <DateTime, double> history, DateTime now, int barNum, object[] args, out double val) { IOption opt = (IOption)args[0]; double risk = 0; DateTime today = now.Date; PositionsManager posMan = PositionsManager.GetManager(m_context); foreach (IOptionSeries optSer in opt.GetSeries()) { if (optSer.ExpirationDate.Date < today) { continue; } IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double putQty, callQty; SingleSeriesPositionGrid.GetPairQty(posMan, pair, out putQty, out callQty); risk += Math.Abs(putQty + callQty); } } val = risk; return(true); }
internal static void FillNodeInfoDeribit(InteractivePointActive ip, double f, double dT, IOptionStrikePair sInfo, StrikeType optionType, OptionPxMode optPxMode, double optPx, double optQty, double optSigma, DateTime optTime, bool returnPct, double riskfreeRatePct, double scaleMult) { // Вызов базовой реализации FillNodeInfo(ip, f, dT, sInfo, optionType, optPxMode, optPx, optQty, optSigma, optTime, returnPct, riskfreeRatePct); // Заменяю тултип, чтобы в нем были и биткойны и доллары var opTypeStr = String.Intern(optionType.ToString()); var opTypeSpace = String.Intern(new string(' ', opTypeStr.Length)); // Пробелы по виду занимают меньше места, чем буквы, поэтому чуть-чуть подравниваю? opTypeSpace += " "; if (optQty > 0) { ip.Tooltip = String.Format(CultureInfo.InvariantCulture, " K:{0}; IV:{1:###0.00}%\r\n" + " {2} px(B) {3:##0.0000} qty {4}\r\n" + " {5} px($) {6:######0.00} qty {4}", sInfo.Strike, optSigma * Constants.PctMult, opTypeStr, optPx, optQty, opTypeSpace, optPx * scaleMult); } else { ip.Tooltip = String.Format(CultureInfo.InvariantCulture, " K:{0}; IV:{1:###0.00}%\r\n" + " {2} px(B) {3:##0.0000}\r\n" + " {4} px($) {5:######0.00}", sInfo.Strike, optSigma * Constants.PctMult, opTypeStr, optPx, opTypeSpace, optPx * scaleMult); } }
/// <summary> /// Обработчик для двух аргументов -- опционной серии и страйка /// </summary> public ISecurity Execute(IOptionSeries optSer, IList <double> actualStrikes) { if ((optSer == null) || (optSer.UnderlyingAsset == null) || (actualStrikes == null) || (actualStrikes.Count <= 0)) { return(null); } // Выбор страйка по последнему значению! double actualStrike = actualStrikes[actualStrikes.Count - 1]; IOptionStrikePair pair = GetStrikePair(optSer, m_selectionMode, actualStrike); if (pair == null) { // Данного страйка нет в списке? Тогда выход. string expiryDate = optSer.ExpirationDate.Date.ToString(IvOnF.DateFormat, CultureInfo.InvariantCulture); // [{0}.Execute] There is no strike '{1}' in the option series '{2} ~ {3}'. Search mode: '{4}' string msg = RM.GetStringFormat("OptHandlerMsg.SingleOption.OptionNotFound", GetType().Name, actualStrike, optSer.UnderlyingAsset, expiryDate, m_selectionMode); if (Context.Runtime.IsAgentMode /* && (!m_ignoreCacheError) */) { throw new ScriptException(msg); // PROD-5501 - Кидаем исключение. } //bool isExpired = true; //// Поскольку в этом блоке настройки m_ignoreCacheError нет, то здесь всегда IsAgentMode == false! //if (Context.Runtime.IsAgentMode) //{ // int amount = optSer.UnderlyingAsset.Bars.Count; // DateTime today = (amount > 0) ? optSer.UnderlyingAsset.Bars[amount - 1].Date : new DateTime(); // isExpired = optSer.ExpirationDate.Date.AddDays(1) < today.Date; //} // А если в режиме лаборатории, тогда только жалуемся в Главный Лог и продолжаем. Context.Log(msg, MessageType.Warning, true /* !isExpired */); return(null); } ISecurity res; if (m_optionType == StrikeType.Put) { res = pair.Put.Security; } else if (m_optionType == StrikeType.Call) { res = pair.Call.Security; } else { throw new NotSupportedException("Не могу найти опцион вида: " + m_optionType); } return(res); }
internal static void FillNodeInfo(InteractivePointActive ip, double f, double dT, IOptionStrikePair sInfo, StrikeType optionType, OptionPxMode optPxMode, double optSigma, bool returnPct, bool isVisiblePoints, double scaleMult, double riskfreeRatePct) { if (optionType == StrikeType.Any) { throw new ArgumentException("Option type 'Any' is not supported.", "optionType"); } bool isCall = (optionType == StrikeType.Call); double optPx = FinMath.GetOptionPrice(f, sInfo.Strike, dT, optSigma, 0, isCall); // Сразу переводим котировку из баксов в битки optPx /= scaleMult; // ReSharper disable once UseObjectOrCollectionInitializer SmileNodeInfo nodeInfo = new SmileNodeInfo(); nodeInfo.F = f; nodeInfo.dT = dT; nodeInfo.RiskFreeRate = riskfreeRatePct; nodeInfo.Sigma = returnPct ? optSigma * Constants.PctMult : optSigma; nodeInfo.OptPx = optPx; nodeInfo.Strike = sInfo.Strike; // Сюда мы приходим когда хотим торговать, поэтому обращение к Security уместно nodeInfo.Security = (optionType == StrikeType.Put) ? sInfo.Put.Security : sInfo.Call.Security; nodeInfo.PxMode = optPxMode; nodeInfo.OptionType = optionType; nodeInfo.Pair = sInfo; //nodeInfo.ScriptTime = optTime; nodeInfo.CalendarTime = DateTime.Now; nodeInfo.Symbol = nodeInfo.Security.Symbol; nodeInfo.DSName = nodeInfo.Security.SecurityDescription.DSName; nodeInfo.Expired = nodeInfo.Security.SecurityDescription.Expired; nodeInfo.FullName = nodeInfo.Security.SecurityDescription.FullName; ip.Tag = nodeInfo; //ip.Color = Colors.Yellow; ip.IsActive = isVisiblePoints; ip.Geometry = Geometries.Ellipse; ip.DragableMode = DragableMode.None; //ip.Value = new System.Windows.Point(sInfo.Strike, nodeInfo.Sigma); // Поскольку цены скорее всего расчетные, показываю на 1 знак болььше, чем объявлена точность в инструменте int decim = Math.Max(0, nodeInfo.Security.Decimals + 1); string optPxStr = optPx.ToString("N" + decim, CultureInfo.InvariantCulture); ip.Tooltip = String.Format(CultureInfo.InvariantCulture, " K: {0}; IV: {1:#0.00}%\r\n {2} px {3}", sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx); }
public static void GetPairQty(PositionsManager posMan, IOptionStrikePair pair, out double putQty, out double callQty) { putQty = 0; callQty = 0; ISecurity putSec = pair.Put.Security, callSec = pair.Call.Security; ReadOnlyCollection <IPosition> putPositions = posMan.GetActiveForBar(putSec); ReadOnlyCollection <IPosition> callPositions = posMan.GetActiveForBar(callSec); if ((putPositions.Count <= 0) && (callPositions.Count <= 0)) { return; } GetTotalQty(putPositions, out putQty); GetTotalQty(callPositions, out callQty); }
internal static void FillNodeInfo(InteractivePointActive ip, double f, double dT, IOptionStrikePair sInfo, StrikeType optionType, OptionPxMode optPxMode, double optSigma, bool returnPct, bool isVisiblePoints, double riskfreeRatePct) { if (optionType == StrikeType.Any) { throw new ArgumentException("Option type 'Any' is not supported.", "optionType"); } bool isCall = (optionType == StrikeType.Call); double optPx = FinMath.GetOptionPrice(f, sInfo.Strike, dT, optSigma, 0, isCall); // ReSharper disable once UseObjectOrCollectionInitializer SmileNodeInfo nodeInfo = new SmileNodeInfo(); nodeInfo.F = f; nodeInfo.dT = dT; nodeInfo.RiskFreeRate = riskfreeRatePct; nodeInfo.Sigma = returnPct ? optSigma * Constants.PctMult : optSigma; nodeInfo.OptPx = optPx; nodeInfo.Strike = sInfo.Strike; // Сюда мы приходим когда хотим торговать, поэтому обращение к Security уместно nodeInfo.Security = (optionType == StrikeType.Put) ? sInfo.Put.Security : sInfo.Call.Security; nodeInfo.PxMode = optPxMode; nodeInfo.OptionType = optionType; nodeInfo.Pair = sInfo; //nodeInfo.ScriptTime = optTime; nodeInfo.CalendarTime = DateTime.Now; nodeInfo.Symbol = nodeInfo.Security.Symbol; nodeInfo.DSName = nodeInfo.Security.SecurityDescription.DSName; nodeInfo.Expired = nodeInfo.Security.SecurityDescription.Expired; nodeInfo.FullName = nodeInfo.Security.SecurityDescription.FullName; ip.Tag = nodeInfo; ip.Color = AlphaColors.Yellow; ip.IsActive = isVisiblePoints; ip.Geometry = Geometries.Ellipse; ip.DragableMode = DragableMode.None; //ip.Value = new System.Windows.Point(sInfo.Strike, nodeInfo.Sigma); ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "F:{0}; K:{1}; IV:{2:#0.00}%\r\n{3} px {4}", f, sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx); }
/// <summary> /// Метод под флаг TemplateTypes.INTERACTIVESPLINE /// </summary> public double Execute(IOptionSeries src, IOptionSeries dest, int barNum) { if ((src == null) || (dest == null)) { return(Constants.NaN); } int barsCount = m_context.BarsCount; if (!m_context.IsLastBarUsed) { barsCount--; } if (barNum < barsCount - 1) { return(Constants.NaN); } if (src.UnderlyingAsset.FinInfo.LastPrice == null) { return(Constants.NaN); } double f = src.UnderlyingAsset.FinInfo.LastPrice.Value; IOptionStrikePair[] srcPairs = src.GetStrikePairs().ToArray(); IOptionStrikePair[] destPairs = dest.GetStrikePairs().ToArray(); double counter = 0; for (int j = 0; j < srcPairs.Length; j++) { IOptionStrikePair srcPair = srcPairs[j]; if (srcPair.Strike < f - m_widthPx) { continue; } if (srcPair.Strike < f) { } } return(counter); }
protected static void FillNodeInfo(InteractivePointLight ip, double f, double dT, IOptionStrikePair sInfo, StrikeType optionType, OptionPxMode optPxMode, double optSigma, bool returnPct, double pctRate) { if (optionType == StrikeType.Any) { throw new ArgumentException("Option type 'Any' is not supported.", "optionType"); } bool isCall = (optionType == StrikeType.Call); double optPx = FinMath.GetOptionPrice(f, sInfo.Strike, dT, optSigma, pctRate, isCall); // ReSharper disable once UseObjectOrCollectionInitializer SmileNodeInfo nodeInfo = new SmileNodeInfo(); nodeInfo.F = f; nodeInfo.dT = dT; nodeInfo.RiskFreeRate = pctRate; nodeInfo.Sigma = returnPct ? optSigma * Constants.PctMult : optSigma; nodeInfo.OptPx = optPx; nodeInfo.Strike = sInfo.Strike; nodeInfo.Security = (optionType == StrikeType.Put) ? sInfo.Put.Security : sInfo.Call.Security; nodeInfo.PxMode = optPxMode; nodeInfo.OptionType = optionType; nodeInfo.Pair = sInfo; //nodeInfo.ScriptTime = optTime; nodeInfo.CalendarTime = DateTime.Now; nodeInfo.Symbol = nodeInfo.Security.Symbol; nodeInfo.DSName = nodeInfo.Security.SecurityDescription.DSName; nodeInfo.FullName = nodeInfo.Security.SecurityDescription.FullName; nodeInfo.Expired = nodeInfo.Security.SecurityDescription.Expired; ip.Tag = nodeInfo; ip.Value = new Point(sInfo.Strike, nodeInfo.Sigma); if (ip is InteractivePointActive) { InteractivePointActive ipa = (InteractivePointActive)ip; ipa.Tooltip = String.Format("K:{0}; IV:{1:#0.00}%\r\n{2} {3} @ {4}", sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx, DefaultOptQty); } }
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); }
protected static void FillNodeInfo(InteractivePointActive ip, double f, double dT, IOptionStrikePair sInfo, StrikeType optionType, OptionPxMode optPxMode, double optSigma, bool returnPct, bool isVisiblePoints) { if (optionType == StrikeType.Any) { throw new ArgumentException("Option type 'Any' is not supported.", "optionType"); } bool isCall = (optionType == StrikeType.Call); double optPx = FinMath.GetOptionPrice(f, sInfo.Strike, dT, optSigma, 0, isCall); SmileNodeInfo nodeInfo = new SmileNodeInfo(); nodeInfo.F = f; nodeInfo.dT = dT; nodeInfo.Sigma = returnPct ? optSigma * Constants.PctMult : optSigma; nodeInfo.OptPx = optPx; nodeInfo.Strike = sInfo.Strike; nodeInfo.Security = (optionType == StrikeType.Put) ? sInfo.Put.Security : sInfo.Call.Security; nodeInfo.PxMode = optPxMode; nodeInfo.OptionType = optionType; nodeInfo.Pair = sInfo; //nodeInfo.ScriptTime = optTime; nodeInfo.CalendarTime = DateTime.Now; nodeInfo.Symbol = nodeInfo.Security.Symbol; nodeInfo.Expired = nodeInfo.Security.SecurityDescription.Expired; ip.Tag = nodeInfo; ip.Color = AlphaColors.Yellow; ip.IsActive = isVisiblePoints; ip.Geometry = Geometries.Ellipse; ip.DragableMode = DragableMode.None; //ip.Value = new System.Windows.Point(sInfo.Strike, nodeInfo.Sigma); ip.Tooltip = String.Format("F:{0}; K:{1}; IV:{2:#0.00}%\r\n{3} {4} @ {5}", f, sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx, DefaultOptQty); }
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); }
/// <summary> /// Обработчик под тип входных данных OPTION_SERIES /// </summary> /// <param name="price">цена БА</param> /// <param name="time">время до экспирации в долях года</param> /// <param name="optSer">опционная серия</param> /// <param name="rate">процентная ставка</param> /// <param name="barNum">индекс бара в серии</param> /// <returns>улыбка, восстановленная из цен опционов</returns> public InteractiveSeries Execute(double price, double time, IOptionSeries optSer, double scaleMult, double rate, int barNum) { int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (optSer == null)) { return(Constants.EmptySeries); } double f = price; double dT = time; if (!DoubleUtil.IsPositive(f)) { //throw new ScriptException("Argument 'price' contains NaN for some strange reason. f:" + f); return(Constants.EmptySeries); } if (!DoubleUtil.IsPositive(scaleMult)) { //throw new ScriptException("Argument 'scaleMult' contains NaN for some strange reason. scaleMult:" + scaleMult); return(Constants.EmptySeries); } if (!DoubleUtil.IsPositive(dT)) { return(Constants.EmptySeries); } if (Double.IsNaN(rate)) { //throw new ScriptException("Argument 'rate' contains NaN for some strange reason. rate:" + rate); return(Constants.EmptySeries); } IOptionStrikePair[] strikes = (from strike in optSer.GetStrikePairs() //orderby strike.Strike ascending -- уже отсортировано select strike).ToArray(); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); for (int j = 0; j < strikes.Length; j++) { IOptionStrikePair sInfo = strikes[j]; // Сверхдалекие страйки игнорируем if ((sInfo.Strike < m_minStrike) || (m_maxStrike < sInfo.Strike)) { continue; } double putPxBtc, callPxBtc, putPxUsd, callPxUsd; double putQty, callQty; DateTime putTime, callTime; { putPxBtc = IvSmile.GetOptPrice(m_context, f, sInfo.Put, m_optionPxMode, sInfo.Tick * m_shiftAsk, sInfo.Tick * m_shiftBid, out putQty, out putTime); putPxUsd = putPxBtc * scaleMult; // Здесь нельзя сразу домножать на scaleMultiplier! Потому что тогда в метод FillNodeInfo пойдут бредовые цены. } { callPxBtc = IvSmile.GetOptPrice(m_context, f, sInfo.Call, m_optionPxMode, sInfo.Tick * m_shiftAsk, sInfo.Tick * m_shiftBid, out callQty, out callTime); callPxUsd = callPxBtc * scaleMult; // Здесь нельзя сразу домножать на scaleMultiplier! Потому что тогда в метод FillNodeInfo пойдут бредовые цены. } double putSigma = Double.NaN, callSigma = Double.NaN, precision; if (DoubleUtil.IsPositive(putPxBtc)) { // Цену опциона переводим в баксы только в момент вычисления айви putSigma = FinMath.GetOptionSigma(f, sInfo.Strike, dT, putPxUsd, rate, false, out precision); putSigma = Math.Min(putSigma, m_maxSigma); if (putSigma <= 0) { putSigma = Double.NaN; } } if (DoubleUtil.IsPositive(callPxBtc)) { // Цену опциона переводим в баксы только в момент вычисления айви callSigma = FinMath.GetOptionSigma(f, sInfo.Strike, dT, callPxUsd, rate, true, out precision); callSigma = Math.Min(callSigma, m_maxSigma); if (callSigma <= 0) { callSigma = Double.NaN; } } InteractivePointActive ip = new InteractivePointActive(); { //ip.Color = (m_optionPxMode == OptionPxMode.Ask) ? Colors.DarkOrange : Colors.DarkCyan; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect; //ip.IsActive = true; //ip.Value = new Point(d2.V1, d2.V2); //ip.Tooltip = String.Format("K:{0}; IV:{1:#0.00}", d2.V1, d2.V2 * PctMult); } InteractiveObject obj = new InteractiveObject(ip); if (m_optionType == StrikeType.Put) { if (DoubleUtil.IsPositive(putSigma)) { // Здесь используем первичную цену в том виде, как ее нам дал Дерибит FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Put, m_optionPxMode, putPxBtc, putQty, putSigma, putTime, false, rate, scaleMult); controlPoints.Add(obj); } } else if (m_optionType == StrikeType.Call) { if (DoubleUtil.IsPositive(callSigma)) { // Здесь используем первичную цену в том виде, как ее нам дал Дерибит FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Call, m_optionPxMode, callPxBtc, callQty, callSigma, callTime, false, rate, scaleMult); controlPoints.Add(obj); } } else if (m_optionType == StrikeType.Any) { if (DoubleUtil.IsPositive(putSigma) && DoubleUtil.IsPositive(callSigma)) { // Здесь используем первичную цену в том виде, как ее нам дал Дерибит if (m_optionPxMode == OptionPxMode.Ask) { if (putSigma < callSigma) { FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Put, m_optionPxMode, putPxBtc, putQty, putSigma, putTime, false, rate, scaleMult); } else { FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Call, m_optionPxMode, callPxBtc, callQty, callSigma, callTime, false, rate, scaleMult); } } else if (m_optionPxMode == OptionPxMode.Bid) { if (putSigma > callSigma) { FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Put, m_optionPxMode, putPxBtc, putQty, putSigma, putTime, false, rate, scaleMult); } else { FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Call, m_optionPxMode, callPxBtc, callQty, callSigma, callTime, false, rate, scaleMult); } } controlPoints.Add(obj); } else if (DoubleUtil.IsPositive(putSigma) && Double.IsNaN(callSigma)) { // Здесь используем первичную цену в том виде, как ее нам дал Дерибит FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Put, m_optionPxMode, putPxBtc, putQty, putSigma, putTime, false, rate, scaleMult); controlPoints.Add(obj); } else if (Double.IsNaN(putSigma) && DoubleUtil.IsPositive(callSigma)) { // Здесь используем первичную цену в том виде, как ее нам дал Дерибит FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Call, m_optionPxMode, callPxBtc, callQty, callSigma, callTime, false, rate, scaleMult); controlPoints.Add(obj); } } } // ReSharper disable once UseObjectOrCollectionInitializer InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); SmileInfo info; var baseSec = optSer.UnderlyingAsset; DateTime scriptTime = baseSec.Bars[baseSec.Bars.Count - 1].Date; if (!IvSmile.TryPrepareSmileInfo(m_context, f, dT, rate, optSer.ExpirationDate, scriptTime, baseSec.Symbol, res, out info)) { return(Constants.EmptySeries); } return(res); }
/// <summary> /// Обработчик для одиночного аргумента -- опционной серии /// </summary> public ISecurity Execute(IOptionSeries optSer, int barNum) { if ((optSer == null) || (optSer.UnderlyingAsset == null)) { return(null); } int barsCount = Context.BarsCount; if (!Context.IsLastBarUsed) { barsCount--; } double actualStrike = m_fixedStrike; // TODO: При работе с фиксированным страйком можно и кеширование сделать. // А вот для второго метода кеширование не подойдет (страйк может измениться на любом индексе) IOptionStrikePair pair = SingleOption.GetStrikePair(optSer, m_selectionMode, actualStrike); if (pair == null) { if (barNum < barsCount - 1) { // Чтобы не спамить зря на старых барах молча возвращаю null return(null); } else { // Данного страйка нет в списке? Тогда выход. string expiryDate = optSer.ExpirationDate.Date.ToString(IvOnF.DateFormat, CultureInfo.InvariantCulture); // [{0}.Execute] There is no strike '{1}' in the option series '{2} ~ {3}'. Search mode: '{4}' string msg = RM.GetStringFormat("OptHandlerMsg.SingleOption.OptionNotFound", GetType().Name, actualStrike, optSer.UnderlyingAsset, expiryDate, m_selectionMode); if (Context.Runtime.IsAgentMode /* && (!m_ignoreCacheError) */) { throw new ScriptException(msg); // PROD-5501 - Кидаем исключение. } //bool isExpired = true; //// Поскольку в этом блоке настройки m_ignoreCacheError нет, то здесь всегда IsAgentMode == false! //if (Context.Runtime.IsAgentMode) //{ // int amount = optSer.UnderlyingAsset.Bars.Count; // DateTime today = (amount > 0) ? optSer.UnderlyingAsset.Bars[amount - 1].Date : new DateTime(); // isExpired = optSer.ExpirationDate.Date.AddDays(1) < today.Date; //} // А если в режиме лаборатории, тогда только жалуемся в Главный Лог и продолжаем. Context.Log(msg, MessageType.Warning, true /* !isExpired */); return(null); } } ISecurity res; if (m_optionType == StrikeType.Put) { res = pair.Put.Security; } else if (m_optionType == StrikeType.Call) { res = pair.Call.Security; } else { throw new NotSupportedException("Не могу найти опцион вида: " + m_optionType); } return(res); }
public InteractiveSeries Execute(double price, double time, IOptionSeries optSer, double riskFreeRatePct, int barNum) { int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (optSer == null)) { return(Constants.EmptySeries); } // В оптимизации ничего рисовать не надо if (Context.IsOptimization) { return(Constants.EmptySeries); } double futPx = price; double dT = time; if (!DoubleUtil.IsPositive(futPx)) { // [{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 (!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); } // TODO: Нужно ли писать отдельный код для лаборатории? Чтобы показывать позиции из симуляции? // if (!Context.Runtime.IsAgentMode) List <InteractiveObject> controlPoints = new List <InteractiveObject>(); var allRealtimeSecs = Context.Runtime.Securities; IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; #region Process put { var put = pair.Put.Security; // TODO: Нужно ли тут проверить наличие позиций??? //if (put.Positions.HavePositions) ISecurityRt secRt; if (put is ISecurityRt) { secRt = (ISecurityRt)put; } else { secRt = (from s in allRealtimeSecs where s.SecurityDescription.Equals(put) && (s is ISecurityRt) select(ISecurityRt) s).SingleOrDefault(); } if ((secRt != null) && secRt.HasActiveOrders) { //secRt.SecurityDescription.TradePlace.DataSource // ОТЛИЧНО! Эта коллекция позволит мне нарисовать свои заявки (это коллекция реальных заявок агента из таблицы My Orders) var orders = secRt.Orders.ToList(); foreach (IOrder ord in orders) { if (!ord.IsActive) { continue; } // Объект ord является RealtimeOrder. Его идентификатор совпадает с OrderNumber в таблице MyOrders if ((m_showLongOrders && ord.IsBuy) || ((!m_showLongOrders) && (!ord.IsBuy))) { // Почему-то InteractivePointLight хоть и давал себя настроить, но не отображался толком. double sigma = FinMath.GetOptionSigma(futPx, pair.Strike, dT, ord.Price, riskFreeRatePct, false); var ip = new InteractivePointActive(pair.Strike, sigma); ip.Tooltip = String.Format(CultureInfo.InvariantCulture, " F: {0}\r\n K: {1}; IV: {2:P2}\r\n {3} px {4} qty {5}", futPx, pair.Strike, sigma, pair.Put.StrikeType, ord.Price, ord.RestQuantity); controlPoints.Add(new InteractiveObject(ip)); } } } } #endregion Process put #region Process call { var call = pair.Call.Security; // TODO: Нужно ли тут проверить наличие позиций??? //if (call.Positions.HavePositions) ISecurityRt secRt; if (call is ISecurityRt) { secRt = (ISecurityRt)call; } else { secRt = (from s in allRealtimeSecs where s.SecurityDescription.Equals(call) && (s is ISecurityRt) select(ISecurityRt) s).SingleOrDefault(); } if ((secRt != null) && secRt.HasActiveOrders) { // ОТЛИЧНО! Эта коллекция позволит мне нарисовать свои заявки (это коллекция реальных заявок агента из таблицы My Orders) var orders = secRt.Orders.ToList(); foreach (IOrder ord in orders) { if (!ord.IsActive) { continue; } // Объект ord является RealtimeOrder. Его идентификатор совпадает с OrderNumber в таблице MyOrders if ((m_showLongOrders && ord.IsBuy) || ((!m_showLongOrders) && (!ord.IsBuy))) { // Почему-то InteractivePointLight хоть и давал себя настроить, но не отображался толком. double sigma = FinMath.GetOptionSigma(futPx, pair.Strike, dT, ord.Price, riskFreeRatePct, true); var ip = new InteractivePointActive(pair.Strike, sigma); ip.Tooltip = String.Format(CultureInfo.InvariantCulture, " F: {0}\r\n K: {1}; IV: {2:P2}\r\n {3} px {4} qty {5}", futPx, pair.Strike, sigma, pair.Call.StrikeType, ord.Price, ord.RestQuantity); controlPoints.Add(new InteractiveObject(ip)); } } } } #endregion Process call } // End for (int j = 0; j < pairs.Length; j++) // ReSharper disable once UseObjectOrCollectionInitializer InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); return(res); }
protected override bool TryCalculate(Dictionary <DateTime, double> history, DateTime now, int barNum, object[] args, out double val) { ISecurity sec = (ISecurity)args[0]; IOptionSeries optSer = (IOptionSeries)args[1]; val = Double.NaN; double px; #region switch(m_pxMode) switch (m_pxMode) { case BasePxMode.FixedPx: px = m_fixedPx; break; case BasePxMode.LastTrade: if (sec.FinInfo.LastPrice.HasValue) { px = sec.FinInfo.LastPrice.Value; } else { return(false); } break; case BasePxMode.BidAskMidPoint: { FinInfo info = sec.FinInfo; if (info.Ask.HasValue && info.Bid.HasValue && info.AskSize.HasValue && info.BidSize.HasValue && (info.AskSize.Value > 0) && (info.BidSize.Value > 0)) { px = (info.Ask.Value + info.Bid.Value) / 2; } else if (info.Ask.HasValue && info.AskSize.HasValue && (info.AskSize.Value > 0)) { px = info.Ask.Value; } else if (info.Bid.HasValue && info.BidSize.HasValue && (info.BidSize.Value > 0)) { px = info.Bid.Value; } else { // Приемлемо ли такое решение? if (info.LastPrice.HasValue) { px = info.LastPrice.Value; } else { return(false); } } } break; case BasePxMode.TheorPxBased: { if (optSer == null) { return(false); } IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); IOptionStrikePair pair = pairs[pairs.Length / 2]; if ((pair.PutFinInfo.TheoreticalPrice == null) || (pair.CallFinInfo.TheoreticalPrice == null)) { return(false); } double putPx = pair.PutFinInfo.TheoreticalPrice.Value; double callPx = pair.CallFinInfo.TheoreticalPrice.Value; px = callPx - putPx + pair.Strike; } break; default: throw new NotImplementedException("pxMode:" + m_pxMode); } #endregion switch(m_pxMode) val = px; return(true); }
/// <summary> /// Обработчик под тип входных данных OPTION_SERIES /// </summary> public IList <double> Execute(IOptionSeries optSer) { if (optSer == null) { return(Constants.EmptyListDouble); } ISecurity sec = optSer.UnderlyingAsset; int len = sec.Bars.Count; if (len <= 0) { return(Constants.EmptyListDouble); } IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); if ((!Double.IsNaN(m_strikeStep)) && (m_strikeStep > Double.Epsilon)) { // Выделяем страйки, которые нацело делятся на StrikeStep var tmp = (from p in pairs let test = m_strikeStep * Math.Round(p.Strike / m_strikeStep) where DoubleUtil.AreClose(p.Strike, test) select p).ToArray(); if (tmp.Length > 1) { // Нормальная ситуация pairs = tmp; } // [02-06-2016] PROD-2812 - Защита от ошибок при указании шага страйков // В противном случае буду показывать все страйки (уже лежат в pairs). // Это хотя бы позволит Пользователю продолжить работу. } if (pairs.Length < 2) { return(Constants.EmptyListDouble); } double[] res = Context?.GetArray <double>(len) ?? new double[len]; var history = LocalHistory; double prevK = Double.NaN; for (int m = 0; m < len; m++) { DateTime now = sec.Bars[m].Date; double ck; if (history.TryGetValue(now, out ck)) { prevK = ck; res[m] = prevK; } else { double futPx = sec.Bars[m].Close; // 0. Валидация диапазона IOptionStrikePair left = pairs[0]; IOptionStrikePair right = pairs[pairs.Length - 1]; if ((futPx <= left.Strike + Double.Epsilon) || (right.Strike - Double.Epsilon <= futPx)) { res[m] = Double.IsNaN(prevK) ? Constants.NaN : prevK; continue; } // 1. Пробуем проверить середину серии в качестве кандидата на левую границу int li = pairs.Length / 2; if (pairs[li].Strike < futPx) { left = pairs[li]; } else { li = 0; } // 2. Ищем правый страйк double ratio = Double.NaN; int leftIndex = Int32.MinValue, rightIndex = Int32.MaxValue; for (int j = li; j < pairs.Length - 1; j++) { if ((pairs[j].Strike - Double.Epsilon <= futPx) && (futPx < pairs[j + 1].Strike)) { left = pairs[j]; right = pairs[j + 1]; leftIndex = Math.Max(0, j + m_shiftStrike); leftIndex = Math.Min(leftIndex, pairs.Length - 2); rightIndex = Math.Max(1, j + 1 + m_shiftStrike); rightIndex = Math.Min(rightIndex, pairs.Length - 1); ratio = (futPx - left.Strike) / (right.Strike - left.Strike); break; } } if (ratio <= (1.0 - m_switchRatio)) { prevK = pairs[leftIndex].Strike; // left.Strike; res[m] = prevK; history[now] = prevK; } else if (m_switchRatio <= ratio) { prevK = pairs[rightIndex].Strike; // right.Strike; res[m] = prevK; history[now] = prevK; } else { if (Double.IsNaN(prevK) || (prevK <= 0)) { try { prevK = pairs[rightIndex].Strike; // right.Strike; } catch (IndexOutOfRangeException ioex) { string msg = String.Format(CultureInfo.InvariantCulture, "{0} when trying to get StrikePair. rightIndex:{1}; pairs.Length:{2}; leftIndex:{3}; li:{4}; ratio:{5}; prevK:{6}; futPx:{7}; ticker:{8}", ioex.GetType().FullName, rightIndex, pairs.Length, leftIndex, li, ratio, prevK, futPx, optSer.UnderlyingAsset.Symbol); m_context.Log(msg, MessageType.Error, true); // Здесь ПОКА оставляю выброс исключения, чтобы ситуация с самозакрытием окон агента воспроизводилась. throw; } res[m] = prevK; history[now] = prevK; } else { res[m] = prevK; // Надо ли здесь обновить history или это бессмысленно и расточительно??? } } } //// "For some strange reason I didn't fill value at index m:" + m); //if (DoubleUtil.IsZero(res[m])) // m_context.Log("[DEBUG:CentralStrike] For some strange reason I didn't fill value at index m:" + m, MessageType.Error, true); } double displayValue = FixedValue.ConvertToDisplayUnits(m_valueMode, res[len - 1]); m_displayPrice.Value = displayValue; return(res); }
public InteractiveSeries Execute(IOptionSeries optSer, int barNum) { int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (optSer == null)) { return(Constants.EmptySeries); } IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); if (m_countFutures) { var futPositions = posMan.GetClosedOrActiveForBar(optSer.UnderlyingAsset); if (futPositions.Count > 0) { double futQty, futCommission; SingleSeriesProfile.GetTotalCommission(futPositions, barNum, m_longPositions, out futCommission, out futQty); if (!DoubleUtil.IsZero(futQty)) { InteractivePointActive ip = new InteractivePointActive(0, futQty); ip.IsActive = true; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = Colors.DarkOrange; ip.Tooltip = String.Format("Fut commission:{0}", futCommission); controlPoints.Add(new InteractiveObject(ip)); } } } for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double putQty = 0, putCommission = Double.NaN; { var putPositions = posMan.GetClosedOrActiveForBar(pair.Put.Security); if (putPositions.Count > 0) { SingleSeriesProfile.GetTotalCommission(putPositions, barNum, m_longPositions, out putCommission, out putQty); } } double callQty = 0, callCommission = Double.NaN; { var callPositions = posMan.GetClosedOrActiveForBar(pair.Call.Security); if (callPositions.Count > 0) { SingleSeriesProfile.GetTotalCommission(callPositions, barNum, m_longPositions, out callCommission, out callQty); } } if ((!DoubleUtil.IsZero(putQty)) || (!DoubleUtil.IsZero(callQty))) { double y = 0; switch (m_optionType) { case StrikeType.Put: y = putCommission; break; case StrikeType.Call: y = callCommission; break; case StrikeType.Any: y = putCommission + callCommission; break; default: throw new NotImplementedException("OptionType: " + m_optionType); } // Не хочу видеть ячейки таблицы с NaN if (Double.IsNaN(y)) { continue; } if ( (!DoubleUtil.IsZero(y)) || ((m_optionType == StrikeType.Any) && ((!DoubleUtil.IsZero(putQty)) || (!DoubleUtil.IsZero(callQty))))) // Хотя вообще-то мы внутри if и второй блок проверок всегда true... { InteractivePointActive ip = new InteractivePointActive(pair.Strike, y); ip.IsActive = true; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = Colors.DarkOrange; ip.Tooltip = String.Format("K:{0}; Commission:{1}", pair.Strike, y); controlPoints.Add(new InteractiveObject(ip)); } } } InteractiveSeries res = new InteractiveSeries(); // Здесь правильно делать new res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); return(res); }
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); } }
internal static bool TryEstimateGamma(double putQty, double callQty, IOptionSeries optSer, IOptionStrikePair pair, InteractiveSeries smile, NumericalGreekAlgo greekAlgo, double f, double dF, double timeToExpiry, double riskFreeRate, out double rawGamma) { rawGamma = Double.NaN; if (timeToExpiry < Double.Epsilon) { throw new ArgumentOutOfRangeException("timeToExpiry", "timeToExpiry must be above zero. timeToExpiry:" + timeToExpiry); } double delta1, delta2; bool ok1 = OptionsBoardNumericalDelta.TryEstimateDelta(putQty, callQty, optSer, pair, smile, greekAlgo, f - dF, dF, timeToExpiry, riskFreeRate, out delta1); if (!ok1) { return(false); } bool ok2 = OptionsBoardNumericalDelta.TryEstimateDelta(putQty, callQty, optSer, pair, smile, greekAlgo, f + dF, dF, timeToExpiry, riskFreeRate, out delta2); if (!ok2) { return(false); } rawGamma = (delta2 - delta1) / 2.0 / dF; return(true); }
public InteractiveSeries Execute(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); IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); if (m_countFutures) { var futPositions = posMan.GetClosedOrActiveForBar(optSer.UnderlyingAsset); if (futPositions.Count > 0) { double futQty, futAvgPx; SingleSeriesProfile.GetAveragePrice(futPositions, barNum, m_longPositions, out futAvgPx, out futQty); if (!DoubleUtil.IsZero(futQty)) { double valueToDisplay = m_countQty ? futQty : futAvgPx; // ReSharper disable once UseObjectOrCollectionInitializer InteractivePointActive ip = new InteractivePointActive(0, valueToDisplay); ip.IsActive = true; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = Colors.DarkOrange; ip.Tooltip = String.Format("AvgPx:{0}; Qty:{1}", futAvgPx, futQty); controlPoints.Add(new InteractiveObject(ip)); } } } for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double putQty = 0, putAvgPx = Double.NaN; { var putPositions = posMan.GetClosedOrActiveForBar(pair.Put.Security); if (putPositions.Count > 0) { SingleSeriesProfile.GetAveragePrice(putPositions, barNum, m_longPositions, out putAvgPx, out putQty); } } double callQty = 0, callAvgPx = Double.NaN; { var callPositions = posMan.GetClosedOrActiveForBar(pair.Call.Security); if (callPositions.Count > 0) { SingleSeriesProfile.GetAveragePrice(callPositions, barNum, m_longPositions, out callAvgPx, out callQty); } } if ((!DoubleUtil.IsZero(putQty)) || (!DoubleUtil.IsZero(callQty))) { double averagePrice = 0, lotSize = 0; switch (m_optionType) { case StrikeType.Put: averagePrice = putAvgPx; lotSize = putQty; break; case StrikeType.Call: averagePrice = callAvgPx; lotSize = callQty; break; //case StrikeType.Any: // y = putQty + callQty; // break; default: throw new NotSupportedException("OptionType: " + m_optionType); } // Не хочу видеть ячейки таблицы с NaN if (Double.IsNaN(averagePrice)) { continue; } if (!DoubleUtil.IsZero(lotSize)) { double valueToDisplay = m_countQty ? lotSize : averagePrice; // ReSharper disable once UseObjectOrCollectionInitializer InteractivePointActive ip = new InteractivePointActive(pair.Strike, valueToDisplay); ip.IsActive = true; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = Colors.DarkOrange; ip.Tooltip = String.Format("K:{0}; AvgPx:{1}; Qty:{2}", pair.Strike, averagePrice, lotSize); controlPoints.Add(new InteractiveObject(ip)); } } } // ReSharper disable once UseObjectOrCollectionInitializer InteractiveSeries res = new InteractiveSeries(); // Здесь правильно делать new res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); SetHandlerInitialized(now); return(res); }
internal static bool TryEstimatePrice(double putQty, double callQty, IOptionSeries optSer, IOptionStrikePair pair, InteractiveSeries smile, double f, double timeToExpiry, double riskFreeRate, out double rawPrice) { rawPrice = Double.NaN; if (timeToExpiry < Double.Epsilon) { throw new ArgumentOutOfRangeException("timeToExpiry", "timeToExpiry must be above zero. timeToExpiry:" + timeToExpiry); } double pnl1 = 0; // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect1 = true; { double pairPnl; pnlIsCorrect1 &= SingleSeriesProfile.TryGetPairPrice( putQty, callQty, smile, pair, f, timeToExpiry, riskFreeRate, out pairPnl); pnl1 += pairPnl; } if (pnlIsCorrect1) { //rawPrice = (cash1 + pnl1); // В моих терминах "Цена одного опциона" будет даваться величиной pnl1 rawPrice = pnl1; return(true); } else { return(false); } }
/// <summary> /// Тета опционной пары по формуле Блека-Шолза /// </summary> internal static void GetPairTheta(PositionsManager posMan, InteractiveSeries smile, IOptionStrikePair pair, double f, double dT, out double totalTheta) { totalTheta = 0; ISecurity putSec = pair.Put.Security, callSec = pair.Call.Security; // закрытые позы не дают в клада в тету, поэтому беру только активные ReadOnlyCollection <IPosition> putPositions = posMan.GetActiveForBar(putSec); ReadOnlyCollection <IPosition> callPositions = posMan.GetActiveForBar(callSec); if ((putPositions.Count <= 0) && (callPositions.Count <= 0)) { return; } double?sigma = null; if ((smile.Tag != null) && (smile.Tag is SmileInfo)) { double tmp; SmileInfo info = smile.GetTag <SmileInfo>(); if (info.ContinuousFunction.TryGetValue(pair.Strike, out tmp)) { sigma = tmp; } } if (sigma == null) { sigma = (from d2 in smile.ControlPoints let point = d2.Anchor.Value where (DoubleUtil.AreClose(pair.Strike, point.X)) select(double?) point.Y).FirstOrDefault(); } if (sigma == null) { return; } { double putTheta; GetOptTheta(putPositions, f, pair.Strike, dT, sigma.Value, 0.0, false, out putTheta); totalTheta += putTheta; } { double callTheta; GetOptTheta(callPositions, f, pair.Strike, dT, sigma.Value, 0.0, true, out callTheta); totalTheta += callTheta; } }
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); }
/// <summary> /// Метод под флаг TemplateTypes.OPTION_SERIES, чтобы подключаться к источнику-серии /// </summary> public double Execute(IOptionSeries optSer, int barNum) { double failRes = Constants.NaN; if (m_repeatLastPx) { failRes = Double.IsNaN(m_prevPx) ? Constants.NaN : m_prevPx; } if (optSer == null) { return(failRes); } switch (m_pxMode) { case BasePxMode.TheorPxBased: { Dictionary <DateTime, double> basePrices; #region Get cache { string cashKey = VariableId + "_basePrices"; basePrices = Context.LoadObject(cashKey) as Dictionary <DateTime, double>; if (basePrices == null) { basePrices = new Dictionary <DateTime, double>(); Context.StoreObject(cashKey, basePrices); } } #endregion Get cache ISecurity sec = optSer.UnderlyingAsset; int len = sec.Bars.Count; if (len <= 0) { return(failRes); } double px; DateTime lastBarDate = sec.Bars[barNum].Date; if ((basePrices.TryGetValue(lastBarDate, out px)) && DoubleUtil.IsPositive(px)) { m_prevPx = px; // Раз мы нашли валидную цену в архиве, то можно обновить failRes failRes = px; } // Цену в архиве нашли, теперь надо проверить свежие сведения. { int barsCount = ContextBarsCount; if (barNum < barsCount - 1) { // Если история содержит осмысленное значение, то оно уже содержится в failRes return(failRes); } else { #region Process last bar(s) IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); IOptionStrikePair pair = pairs[pairs.Length / 2]; if ((pair.PutFinInfo.TheoreticalPrice == null) || (pair.CallFinInfo.TheoreticalPrice == null)) { return(failRes); } double putPx = pair.PutFinInfo.TheoreticalPrice.Value; double callPx = pair.CallFinInfo.TheoreticalPrice.Value; px = callPx - putPx + pair.Strike; m_prevPx = px; basePrices[lastBarDate] = px; double displayValue = FixedValue.ConvertToDisplayUnits(m_valueMode, px); m_displayPrice.Value = displayValue; return(px); #endregion Process last bar(s) } } } default: return(Execute(optSer.UnderlyingAsset, barNum)); } }
public InteractiveSeries Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, 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 (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); if (wasInitialized) { 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); if (wasInitialized) { 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 ivAtm, slopeAtm; 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_internalSlopePct.Value / Constants.PctMult; slopeAtm = slopeAtm / futPx / Math.Sqrt(dT); } if (Double.IsNaN(ivAtm) || (ivAtm < Double.Epsilon)) { // [{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); } //{ // string msg = String.Format("[DEBUG:{0}] ivAtm:{1}; shift:{2}; depth:{3}; F:{4}; dT:{5}; ", // GetType().Name, ivAtm, shift, depth, F, dT); // context.Log(msg, MessageType.Info, true); //} SmileFunction3 tempFunc = new SmileFunction3(ivAtm, m_shift, 0.5, futPx, dT); double depth = tempFunc.GetDepthUsingSlopeATM(slopeAtm); SmileFunction3 smileFunc = new SmileFunction3(ivAtm, m_shift, depth, futPx, dT); if (!m_setIvByHands) { m_ivAtmPct.Value = ivAtm * Constants.PctMult; } m_depthPct.Value = depth * Constants.PctMult; if (!m_setSlopeByHands) { double dSigmaDx = slopeAtm * futPx * Math.Sqrt(dT); m_internalSlopePct.Value = dSigmaDx * Constants.PctMult; } 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 dK = pairs[1].Strike - pairs[0].Strike; double width = (SigmaMult * ivAtm * Math.Sqrt(dT)) * futPx; width = Math.Max(width, 2 * dK); // Generate left invisible tail if (m_generateTails) { GaussSmile.AppendLeftTail(smileFunc, xs, ys, minK, dK, false); } List <InteractiveObject> controlPoints = new List <InteractiveObject>(); for (int j = 0; j < pairs.Length; j++) { bool showPoint = true; IOptionStrikePair pair = pairs[j]; // Сверхдалекие страйки игнорируем if ((pair.Strike < futPx - width) || (futPx + width < pair.Strike)) { showPoint = false; } double k = pair.Strike; double sigma = smileFunc.Value(k); double vol = sigma; if (Double.IsNaN(sigma) || Double.IsInfinity(sigma) || (sigma < Double.Epsilon)) { 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); if (showPoint) { if (k <= futPx) // Puts { FillNodeInfo(ip, futPx, dT, pair, StrikeType.Put, OptionPxMode.Mid, sigma, false, m_isVisiblePoints, ratePct); } else // Calls { FillNodeInfo(ip, futPx, dT, pair, StrikeType.Call, OptionPxMode.Mid, sigma, false, m_isVisiblePoints, ratePct); } } InteractiveObject obj = new InteractiveObject(ip); if (showPoint) { controlPoints.Add(obj); } xs.Add(k); ys.Add(vol); } // ReSharper disable once UseObjectOrCollectionInitializer InteractiveSeries res = new InteractiveSeries(); res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); double maxK = pairs[pairs.Length - 1].Strike; // 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 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 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 (smile == null) { string msg = String.Format("[{0}] Argument 'smile' must be filled with InteractiveSeries.", GetType().Name); 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; if (DoubleUtil.IsPositive(oldInfo.F)) { futPx = oldInfo.F; } else { futPx = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Close; } IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); if (m_countFutures) { ReadOnlyCollection <IPosition> futPositions = posMan.GetActiveForBar(optSer.UnderlyingAsset); if (futPositions.Count > 0) { double futQty; GetTotalQty(futPositions, out futQty); if (!DoubleUtil.IsZero(futQty)) { // ReSharper disable once UseObjectOrCollectionInitializer InteractivePointActive ip = new InteractivePointActive(0, futQty); ip.IsActive = true; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = Colors.DarkOrange; //// PROD-1194 - Цвет ячеек и фона //if (futQty > 0) // ip.BackColor = ScriptColors.Aquamarine; //else if (futQty < 0) // ip.BackColor = ScriptColors.LightSalmon; //if (ip.BackColor != null) // ip.ForeColor = ScriptColors.Black; // TODO: Ждем решения PROD-6009 //// PROD-1194 - Цвет ячеек и фона -- фон красится по принципу "в деньгах или нет", шрифт -- по знаку //if (futQty > 0) // ip.ForeColor = ScriptColors.LightGreen; //else if (futQty < 0) // ip.ForeColor = ScriptColors.Red; ////if () // Фьючерс не красится! //// ip.ForeColor = ScriptColors.Black; ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "Fut Qty:{0}", futQty); controlPoints.Add(new InteractiveObject(ip)); } } } for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double putQty, callQty; GetPairQty(posMan, pair, out putQty, out callQty); if ((!DoubleUtil.IsZero(putQty)) || (!DoubleUtil.IsZero(callQty))) { double y = 0; Color? backCol = null; switch (m_optionType) { case StrikeType.Put: y = putQty; backCol = (pair.Strike > futPx) ? ScriptColors.LightCyan : ScriptColors.LightYellow; break; case StrikeType.Call: y = callQty; backCol = (pair.Strike < futPx) ? ScriptColors.LightCyan : ScriptColors.LightYellow; break; case StrikeType.Any: y = putQty + callQty; break; default: throw new NotImplementedException("OptionType: " + m_optionType); } if ( (!DoubleUtil.IsZero(y)) || ((m_optionType == StrikeType.Any) && ((!DoubleUtil.IsZero(putQty)) || (!DoubleUtil.IsZero(callQty))))) // Хотя вообще-то мы внутри if и второй блок проверок всегда true... { // ReSharper disable once UseObjectOrCollectionInitializer InteractivePointActive ip = new InteractivePointActive(pair.Strike, y); ip.IsActive = true; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = Colors.DarkOrange; //// PROD-1194 - Цвет ячеек и фона //if (y > 0) // ip.BackColor = ScriptColors.Aquamarine; //else if (y < 0) // ip.BackColor = ScriptColors.LightSalmon; //if (ip.BackColor != null) // ip.ForeColor = ScriptColors.Black; // TODO: Ждем решения PROD-6009 //// PROD-1194 - Цвет ячеек и фона -- фон красится по принципу "в деньгах или нет", шрифт -- по знаку //if (y > 0) // ip.ForeColor = ScriptColors.LightGreen; //else if (y < 0) // ip.ForeColor = ScriptColors.Red; ////if (backCol != null) //// ip.BackColor = backCol; ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "K:{0}; Qty:{1}", pair.Strike, y); controlPoints.Add(new InteractiveObject(ip)); } } } // ReSharper disable once UseObjectOrCollectionInitializer InteractiveSeries res = new InteractiveSeries(); // Здесь правильно делать new res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); SetHandlerInitialized(now); return(res); }
/// <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); } }
/// <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> /// Тета будет иметь размерность 'пункты за год'. /// Обычно же опционщики любят смотреть размерность 'пункты за день'. /// Поэтому полученное сырое значение ещё надо делить на количество дней в году. /// (Эквивалентно умножению на интересующий набег времени для получения дифференциала). /// </summary> internal static bool TryEstimateTheta(double putQty, double callQty, IOptionSeries optSer, IOptionStrikePair pair, InteractiveSeries smile, NumericalGreekAlgo greekAlgo, double f, double timeToExpiry, double tStep, double riskFreeRate, out double rawTheta) { rawTheta = Double.NaN; if (timeToExpiry < Double.Epsilon) { throw new ArgumentOutOfRangeException("timeToExpiry", "timeToExpiry must be above zero. timeToExpiry:" + timeToExpiry); } double t1 = (timeToExpiry - tStep > Double.Epsilon) ? (timeToExpiry - tStep) : (0.5 * timeToExpiry); double pnl1 = 0; // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect1 = true; { // 2. Изменение времени // ВАЖНО: нормальный алгоритм сдвига улыбки во времени будет в платной версии "Пакета Каленковича" InteractiveSeries actualSmile = SingleSeriesProfile.GetSmileAtTime(smile, NumericalGreekAlgo.FrozenSmile, t1); { double pairPnl; pnlIsCorrect1 &= SingleSeriesProfile.TryGetPairPrice( putQty, callQty, actualSmile, pair, f, t1, riskFreeRate, out pairPnl); pnl1 += pairPnl; } } double t2 = timeToExpiry + tStep; double pnl2 = 0; // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect2 = true; { // ВАЖНО: нормальный алгоритм сдвига улыбки во времени будет в платной версии "Пакета Каленковича" InteractiveSeries actualSmile = SingleSeriesProfile.GetSmileAtTime(smile, NumericalGreekAlgo.FrozenSmile, t2); { double pairPnl; pnlIsCorrect2 &= SingleSeriesProfile.TryGetPairPrice( putQty, callQty, actualSmile, pair, f, t2, riskFreeRate, out pairPnl); pnl2 += pairPnl; } } if (pnlIsCorrect1 && pnlIsCorrect2) { //rawTheta = ((cash2 + pnl2) - (cash1 + pnl1)) / (t2 - t1); rawTheta = (pnl2 - pnl1) / (t2 - t1); // Переворачиваю тету, чтобы жить в календарном времени rawTheta = -rawTheta; 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); } }