internal static bool TryEstimateSpeed(PositionsManager posMan, IOptionSeries optSer, IOptionStrikePair[] pairs,
                                              InteractiveSeries smile, NumericalGreekAlgo greekAlgo,
                                              double f, double dF, double timeToExpiry, out double rawSpeed)
        {
            rawSpeed = Double.NaN;

            if (timeToExpiry < Double.Epsilon)
            {
                throw new ArgumentOutOfRangeException("timeToExpiry", "timeToExpiry must be above zero. timeToExpiry:" + timeToExpiry);
            }

            double gamma1, gamma2;
            bool   ok1 = SingleSeriesNumericalGamma.TryEstimateGamma(posMan, optSer, pairs, smile, greekAlgo,
                                                                     f - dF, dF, timeToExpiry, out gamma1);

            if (!ok1)
            {
                return(false);
            }

            bool ok2 = SingleSeriesNumericalGamma.TryEstimateGamma(posMan, optSer, pairs, smile, greekAlgo,
                                                                   f + dF, dF, timeToExpiry, out gamma2);

            if (!ok2)
            {
                return(false);
            }

            rawSpeed = (gamma2 - gamma1) / 2.0 / dF;
            return(true);
        }
Пример #2
0
        /// <summary>
        /// Метод под флаг TemplateTypes.OPTION_SERIES, чтобы подключаться к источнику-серии
        /// </summary>
        public double Execute(double entryPermission,
                              double strike, double risk, double maxRisk, InteractiveSeries smile, IOptionSeries optSer, int barNum)
        {
            double res = Process(entryPermission, strike, risk, maxRisk, smile, optSer, 0, 0, barNum);

            return(res);
        }
        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);
        }
Пример #4
0
        /// <summary>
        /// Обработчик для двух улыбок
        /// </summary>
        public InteractiveSeries Execute(InteractiveSeries marketSmile, InteractiveSeries modelSmile, int barNum)
        {
            InteractiveSeries res = SelectSmile(m_smileIndex,
                                                marketSmile, modelSmile);

            return(res);
        }
Пример #5
0
        public InteractiveSeries Execute(IList <double> xValues, IList <double> yValues)
        {
            if ((xValues == null) || (xValues.Count <= 0) || (yValues == null) || (yValues.Count <= 0))
            {
                string msg = String.Format("[{0}] Null or empty data series.", GetType().Name);
                Context.Log(msg, MessageType.Error, false);
                return(Constants.EmptySeries);
            }

            if (xValues.Count != yValues.Count)
            {
                string msg = String.Format("[{0}] Data series have different length. X.Len:{1}; Y.Len:{2}", GetType().Name, xValues.Count, yValues.Count);
                Context.Log(msg, MessageType.Warning, false);
                //return Constants.EmptySeries;
            }

            int len = Math.Min(xValues.Count, yValues.Count);
            List <InteractiveObject> controlPoints = new List <InteractiveObject>();

            for (int j = 0; j < len; j++)
            {
                InteractivePointLight ip = new InteractivePointLight();
                ip.Value = new Point(xValues[j], yValues[j]);
                //ip.Tooltip = String.Format("F:{0}; D:{1}", f, yStr);

                controlPoints.Add(new InteractiveObject(ip));
            }

            // ReSharper disable once UseObjectOrCollectionInitializer
            InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку

            res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints);

            return(res);
        }
Пример #6
0
        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);
            }
        }
Пример #7
0
        public InteractiveSeries Execute(double xVal, double yVal, int barNum)
        {
            // 1. Если локальный накопитель еще не проинициализирован -- делаем это
            if (m_controlPoints == null)
            {
                m_controlPoints = new List <InteractiveObject>();
            }

            // 3. Добавляем новую точку в локальный накопитель
            InteractivePointLight ip = new InteractivePointLight();

            ip.Value = new Point(xVal, yVal);
            //ip.Tooltip = String.Format("F:{0}; D:{1}", f, yStr);

            m_controlPoints.Add(new InteractiveObject(ip));

            // 5. Если мы еще не добрались до правого края графика -- возвращаем пустую серию
            int barsCount = ContextBarsCount;

            if (barNum < barsCount - 1)
            {
                return(Constants.EmptySeries);
            }

            // 7. На правом краю графика возвращаем подготовленную серию точек
            // ReSharper disable once UseObjectOrCollectionInitializer
            InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку

            res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(m_controlPoints);

            return(res);
        }
Пример #8
0
        /// <summary>
        /// Метод под флаг TemplateTypes.OPTION_SERIES, чтобы подключаться к источнику-серии
        /// </summary>
        public double Execute(bool entryPermission,
                              double strike, double risk, double maxRisk, InteractiveSeries smile, IOptionSeries optSer, InteractiveSeries callDelta, int barNum)
        {
            double permission = entryPermission ? 1 : -1;
            double res        = Process(permission, strike, risk, maxRisk, smile, optSer, callDelta, 0, 0, barNum);

            return(res);
        }
        /// <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);
            }
        }
Пример #10
0
        public void Dispose()
        {
            if (m_clickableSeries != null)
            {
                m_clickableSeries.ClickEvent -= InteractiveSplineOnClickEvent;

                m_clickableSeries = null;
            }
        }
        /// <summary>
        /// Вега будет иметь размерность 'пункты за 100% волатильности'.
        /// Обычно же опционщики любят смотреть размерность 'пункты за 1% волатильности'.
        /// Поэтому полученное сырое значение ещё надо делить на 100%.
        /// (Эквивалентно умножению на интересующий набег волы для получения дифференциала).
        /// </summary>
        internal static bool TryEstimateVega(double putQty, double callQty, IOptionSeries optSer, IOptionStrikePair pair,
                                             InteractiveSeries smile, NumericalGreekAlgo greekAlgo,
                                             double f, double dSigma, double timeToExpiry, double riskFreeRate, 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 pnl1 = 0;
            // Флаг того, что ПНЛ по всем инструментам был расчитан верно
            bool pnlIsCorrect1 = true;
            {
                // Для первой точки улыбку не трогаем
                {
                    double pairPnl;
                    pnlIsCorrect1 &= SingleSeriesProfile.TryGetPairPrice(
                        putQty, callQty, smile, pair, f, timeToExpiry, riskFreeRate, out pairPnl);
                    pnl1 += pairPnl;
                }
            }

            double pnl2 = 0;
            // Флаг того, что ПНЛ по всем инструментам был расчитан верно
            bool pnlIsCorrect2 = true;

            {
                //InteractiveSeries actualSmile = SingleSeriesProfile.GetRaisedSmile(smile, greekAlgo, dSigma);
                SmileInfo actualSmile = SingleSeriesProfile.GetRaisedSmile(sInfo, greekAlgo, dSigma);

                double pairPnl;
                pnlIsCorrect2 &= SingleSeriesProfile.TryGetPairPrice(
                    putQty, callQty, actualSmile, pair, f, timeToExpiry, riskFreeRate, out pairPnl);
                pnl2 += pairPnl;
            }

            if (pnlIsCorrect1 && pnlIsCorrect2)
            {
                // Первая точка совпадает с текущей, поэтому нет деления на 2.
                //rawVega = ((cash2 + pnl2) - (cash1 + pnl1)) / dSigma;
                rawVega = (pnl2 - pnl1) / dSigma;
                return(true);
            }
            else
            {
                return(false);
            }
        }
Пример #12
0
        public InteractiveSeries Execute(double price, InteractiveSeries line, IOptionSeries optSer, int barNum)
        {
            if (optSer == null)
            {
                return(Constants.EmptySeries);
            }

            return(Execute(price, line, optSer.UnderlyingAsset, barNum));
        }
Пример #13
0
        public void Dispose()
        {
            if (m_clickableSeries != null)
            {
                m_clickableSeries.ClickEvent -= InteractiveSplineOnQuoteIvEvent;
                //m_clickableSeries.EndDragEvent -= res_EndDragEvent;

                m_clickableSeries = null;
            }
        }
Пример #14
0
        /// <summary>
        /// Обработчик под тип входных данных OPTION_SERIES
        /// </summary>
        public InteractiveSeries Execute(double price, double trueTimeToExpiry, IOptionSeries optSer, int barNum)
        {
            if (optSer == null)
            {
                return(Constants.EmptySeries);
            }

            InteractiveSeries res = Execute(price, trueTimeToExpiry, optSer, 0, barNum);

            return(res);
        }
Пример #15
0
        /// <summary>
        /// Обработчик под тип входных данных OPTION_SERIES
        /// </summary>
        public InteractiveSeries Execute(IOptionSeries optSer)
        {
            if (optSer == null)
            {
                return(Constants.EmptySeries);
            }

            InteractiveSeries res = Execute(optSer, new[] { 0.0 });

            return(res);
        }
Пример #16
0
//#if DEBUG
//        /// <summary>
//        /// \~english Extrapolate template with smooth continuation
//        /// \~russian Продлевать шаблон на бесконечность
//        /// </summary>
//        [HelperName("Extrapolate Template", Constants.En)]
//        [HelperName("Продлевать шаблон", Constants.Ru)]
//        [Description("Продлевать шаблон на бесконечность")]
//        [HelperDescription("Extrapolate template with smooth continuation", Language = Constants.En)]
//        [HandlerParameter(true, NotOptimized = true, IsVisibleInBlock = true, Default = "true")]
//        public bool ExtrapolateSmile
//        {
//            get { return m_useSmileTails; }
//            set { m_useSmileTails = value; }
//        }
//#endif
        #endregion Parameters

        public InteractiveSeries Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, double scaleMult, int barNum)
        {
            if (optSer == null)
            {
                return(Constants.EmptySeries);
            }

            InteractiveSeries res = Execute(price, time, smile, optSer, scaleMult, 0, barNum);

            return(res);
        }
Пример #17
0
        /// <summary>
        /// Обработчик под тип входных данных OPTION_SERIES
        /// </summary>
        public InteractiveSeries Execute(IOptionSeries optSer, int barNum)
        {
            if (optSer == null)
            {
                return(Constants.EmptySeries);
            }

            InteractiveSeries res = Execute(optSer, 0, barNum);

            return(res);
        }
Пример #18
0
        /// <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;
            }
        }
Пример #19
0
 public void Dispose()
 {
     if (m_context != null)
     {
         InteractiveSeries oldLine = m_context.LoadObject(m_frozenSmileId) as InteractiveSeries;
         if (oldLine != null)
         {
             // Просто выполняю отписку от старых событий?
             oldLine.EndDragEvent -= res_EndDragEvent;
         }
     }
 }
Пример #20
0
        internal static InteractiveSeries SelectSmile(int index, params InteractiveSeries[] smiles)
        {
            if ((smiles == null) || (smiles.Length <= 0) ||
                (index < 0) || (smiles.Length <= index))
            {
                return(null);
            }

            InteractiveSeries res = smiles[index];

            return(res);
        }
        public InteractiveSeries Execute(InteractiveSeries ser1, InteractiveSeries ser2, int barNum)
        {
            int barsCount = m_context.BarsCount;

            if (!m_context.IsLastBarUsed)
            {
                barsCount--;
            }
            if (barNum < barsCount - 1)
            {
                return(Constants.EmptySeries);
            }

            if ((ser1 == null) && (ser2 == null))
            {
                return(Constants.EmptySeries);
            }
            else if ((ser1 == null) && (ser2 != null))
            {
                return(ser2);
            }
            else if ((ser1 != null) && (ser2 == null))
            {
                return(ser1);
            }

            var query = (from s1 in ser1.ControlPoints
                         from s2 in ser2.ControlPoints
                         where DoubleUtil.AreClose(s1.Anchor.Value.X, s2.Anchor.Value.X)
                         select new { cp1 = s1, cp2 = s2 });

            List <InteractiveObject> controlPoints = new List <InteractiveObject>();

            foreach (var pair in query)
            {
                double x = pair.cp1.Anchor.Value.X;
                double y = pair.cp1.Anchor.Value.Y + pair.cp2.Anchor.Value.Y;
                InteractivePointActive ip = new InteractivePointActive(x, y);
                //ip.Geometry = Geometries.Rect;
                ip.Tooltip = String.Format("F:{0}; PnL:{1}", x, y);

                controlPoints.Add(new InteractiveObject(ip));
            }

            InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку

            res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints);

            return(res);
        }
Пример #22
0
        private void res_EndDragEvent(object sender, InteractiveActionEventArgs e)
        {
            InteractiveSeries oldLine = sender as InteractiveSeries;

            if (oldLine != null)
            {
                //SmileInfo info = oldLine.GetTag<SmileInfo>();
                //if (info != null)
                //{
                //    List<double> xs = new List<double>();
                //    List<double> ys = new List<double>();

                //    foreach (InteractiveObject oldObj in oldLine.ControlPoints)
                //    {
                //        if (!(oldObj.Anchor.IsVisible ?? oldObj.AnchorGraphPointData.IsVisible))
                //            continue;

                //        double k = oldObj.Anchor.ValueX;
                //        double sigma = oldObj.Anchor.ValueY;

                //        xs.Add(k);
                //        ys.Add(sigma);
                //    }

                //    try
                //    {
                //        NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys);

                //        info.ContinuousFunction = spline;
                //        info.ContinuousFunctionD1 = spline.DeriveD1();

                //        oldLine.Tag = info;
                //    }
                //    catch (Exception ex)
                //    {
                //        m_context.Log(ex.ToString(), MessageType.Error, true);
                //        //return Constants.EmptySeries;
                //    }
                //}

                // Сразу отписываюсь!
                oldLine.EndDragEvent -= res_EndDragEvent;
            }

            m_context.Recalc();
        }
Пример #23
0
        /// <summary>
        /// Метод под флаг TemplateTypes.INTERACTIVESPLINE
        /// </summary>
        public InteractiveSeries Execute(InteractiveSeries quotes, int barNum)
        {
            if (quotes == null)
            {
                return(Constants.EmptySeries);
            }

            int barsCount = m_context.BarsCount;

            if (!m_context.IsLastBarUsed)
            {
                barsCount--;
            }
            if (barNum < barsCount - 1)
            {
                return(quotes);
            }

            // Это надо ОБЯЗАТЕЛЬНО делать ПОСЛЕ восстановления виртуальных позиций
            #region Process clicks
            {
                var onClickEvents = m_context.LoadObject(VariableId + "OnClickEvent") as List <InteractiveActionEventArgs>;
                if (onClickEvents == null)
                {
                    onClickEvents = new List <InteractiveActionEventArgs>();
                    m_context.StoreObject(VariableId + "OnClickEvent", onClickEvents);
                }

                if (onClickEvents.Count > 0)
                {
                    RouteOnClickEvents(onClickEvents);
                }
            }
            #endregion Process clicks

            InteractiveSeries res = quotes;
            res.ClickEvent   -= InteractiveSplineOnClickEvent;
            res.ClickEvent   += InteractiveSplineOnClickEvent;
            m_clickableSeries = res;

            return(res);
        }
        public InteractiveSeries Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, int barNum)
        {
            int barsCount = ContextBarsCount;

            if ((barNum < barsCount - 1) || (optSer == null))
            {
                return(Constants.EmptySeries);
            }

            int      lastBarIndex   = optSer.UnderlyingAsset.Bars.Count - 1;
            DateTime now            = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Date;
            bool     wasInitialized = HandlerInitializedToday(now);

            double futPx = price;
            double dT    = time;

            if (!DoubleUtil.IsPositive(dT))
            {
                // [{0}] Time to expiry must be positive value. dT:{1}
                string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT);
                if (wasInitialized)
                {
                    m_context.Log(msg, MessageType.Error, false);
                }
                return(Constants.EmptySeries);
            }

            if (!DoubleUtil.IsPositive(futPx))
            {
                // [{0}] Base asset price must be positive value. F:{1}
                string msg = RM.GetStringFormat("OptHandlerMsg.FutPxMustBePositive", GetType().Name, futPx);
                if (wasInitialized)
                {
                    m_context.Log(msg, MessageType.Error, false);
                }
                return(Constants.EmptySeries);
            }

            if (smile == null)
            {
                string msg = String.Format("[{0}] Argument 'smile' must be filled with InteractiveSeries.", GetType().Name);
                if (wasInitialized)
                {
                    m_context.Log(msg, MessageType.Error, false);
                }
                return(Constants.EmptySeries);
            }

            if (m_optionType == StrikeType.Any)
            {
                string msg = String.Format("[{0}] OptionType '{1}' is not supported.", GetType().Name, m_optionType);
                if (wasInitialized)
                {
                    m_context.Log(msg, MessageType.Error, false);
                }
                return(Constants.EmptySeries);
            }

            SmileInfo oldInfo = smile.GetTag <SmileInfo>();

            if (oldInfo == null)
            {
                string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag);
                if (wasInitialized)
                {
                    m_context.Log(msg, MessageType.Error, false);
                }
                return(Constants.EmptySeries);
            }

            bool          isCall       = m_optionType == StrikeType.Call;
            double        putQty       = isCall ? 0 : m_fixedQty;
            double        callQty      = isCall ? m_fixedQty : 0;
            double        riskFreeRate = 0;
            List <double> xs           = new List <double>();
            List <double> ys           = new List <double>();
            var           smilePoints  = smile.ControlPoints;

            IOptionStrikePair[]      pairs         = optSer.GetStrikePairs().ToArray();
            PositionsManager         posMan        = PositionsManager.GetManager(m_context);
            List <InteractiveObject> controlPoints = new List <InteractiveObject>();

            foreach (IOptionStrikePair pair in pairs)
            {
                double rawTheta;
                if (TryEstimateTheta(putQty, callQty, optSer, pair, smile, m_greekAlgo, futPx, dT, m_tStep, riskFreeRate, out rawTheta))
                {
                    // Переводим тету в дифференциал 'изменение цены за 1 сутки'.
                    rawTheta /= OptionUtils.DaysInYear;

                    InteractivePointActive ip = new InteractivePointActive();
                    ip.IsActive = m_showNodes;
                    //ip.DragableMode = DragableMode.None;
                    //ip.Geometry = Geometries.Rect;
                    //ip.Color = System.Windows.Media.Colors.Green;
                    double y = rawTheta;
                    ip.Value = new Point(pair.Strike, y);
                    string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture);
                    ip.Tooltip = String.Format("K:{0}; Th:{1}", pair.Strike, yStr);

                    controlPoints.Add(new InteractiveObject(ip));

                    xs.Add(pair.Strike);
                    ys.Add(y);
                }
            }

            InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку

            res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints);

            try
            {
                if (xs.Count >= BaseCubicSpline.MinNumberOfNodes)
                {
                    SmileInfo info = new SmileInfo();
                    info.F            = oldInfo.F;
                    info.dT           = oldInfo.dT;
                    info.RiskFreeRate = oldInfo.RiskFreeRate;

                    NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys);

                    info.ContinuousFunction   = spline;
                    info.ContinuousFunctionD1 = spline.DeriveD1();

                    res.Tag = info;
                }
            }
            catch (Exception ex)
            {
                m_context.Log(ex.ToString(), MessageType.Error, true);
                return(Constants.EmptySeries);
            }

            SetHandlerInitialized(now);

            return(res);
        }
Пример #25
0
        public InteractiveSeries Execute(double trueTimeToExpiry, InteractiveSeries smile, int barNum)
        {
            int barsCount = m_context.BarsCount;

            if (!m_context.IsLastBarUsed)
            {
                barsCount--;
            }
            if (barNum < barsCount - 1)
            {
                return(Constants.EmptySeries);
            }

            SmileInfo refSmileInfo = smile.GetTag <SmileInfo>();

            if ((refSmileInfo == null) || (refSmileInfo.ContinuousFunction == null))
            {
                return(Constants.EmptySeries);
            }

            if (Double.IsNaN(trueTimeToExpiry) || (trueTimeToExpiry < Double.Epsilon))
            {
                string msg = String.Format("[{0}] trueTimeToExpiry must be positive value. dT:{1}", GetType().Name, trueTimeToExpiry);
                m_context.Log(msg, MessageType.Error, true);
                return(Constants.EmptySeries);
            }

            InteractiveSeries res = FrozenSmile;

            // Поскольку редактируемые узлы идут с заданным шагом, то
            // допустим только режим Yonly.
            DragableMode dragMode = DragableMode.Yonly;
            SmileInfo    oldInfo  = res.GetTag <SmileInfo>();

            if (m_resetSmile ||
                (oldInfo == null) || (oldInfo.ContinuousFunction == null))
            {
                oldInfo = refSmileInfo;

                //oldInfo.F = sInfo.F;
                //oldInfo.dT = sInfo.dT;
                //oldInfo.RiskFreeRate = sInfo.RiskFreeRate;
            }

            double futPx = refSmileInfo.F;
            double dT    = trueTimeToExpiry;
            double ivAtm = refSmileInfo.ContinuousFunction.Value(futPx);

            SmileInfo info = new SmileInfo();
            {
                info.F            = futPx;
                info.dT           = trueTimeToExpiry;
                info.RiskFreeRate = oldInfo.RiskFreeRate;

                List <double>            xs           = new List <double>();
                List <double>            ys           = new List <double>();
                List <InteractiveObject> visibleNodes = (from oldObj in res.ControlPoints
                                                         where oldObj.AnchorIsActive
                                                         select oldObj).ToList();
                int visibleNodesCount = visibleNodes.Count;
                if (m_resetSmile || (visibleNodesCount != m_numberOfNodes))
                {
                    // Здесь обязательно в начале
                    //res.ControlPoints.Clear();
                    res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(new InteractiveObject[0]);

                    int half = m_numberOfNodes / 2; // Целочисленное деление!

                    #region 1. Готовлю сплайн
                    {
                        double mult = m_nodeStep * ivAtm * Math.Sqrt(dT);
                        double dK   = futPx * (Math.Exp(mult) - 1);
                        // Сдвигаю точки, чтобы избежать отрицательных значений
                        while ((futPx - half * dK) <= Double.Epsilon)
                        {
                            half--;
                        }
                        for (int j = 0; j < m_numberOfNodes; j++)
                        {
                            double k = futPx + (j - half) * dK;
                            // Обычно здесь будет лежать сплайн от замороженной улыбки...
                            double sigma;
                            if ((!oldInfo.ContinuousFunction.TryGetValue(k, out sigma)) || Double.IsNaN(sigma))
                            {
                                string msg = String.Format("[DEBUG:{0}] Unable to get IV for strike:{1}. Please, try to decrease NodeStep.",
                                                           GetType().Name, k);
                                m_context.Log(msg, MessageType.Warning, true);
                                return(res);
                            }

                            xs.Add(k);
                            ys.Add(sigma);
                        }
                    }
                    #endregion 1. Готовлю сплайн
                }
                else
                {
                    // Здесь обязательно в начале
                    //res.ControlPoints.Clear();
                    res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(new InteractiveObject[0]);

                    int half = m_numberOfNodes / 2; // Целочисленное деление!

                    #region 2. Готовлю сплайн
                    {
                        double mult = m_nodeStep * ivAtm * Math.Sqrt(dT);
                        double dK   = futPx * (Math.Exp(mult) - 1);
                        // Сдвигаю точки, чтобы избежать отрицательных значений
                        while ((futPx - half * dK) <= Double.Epsilon)
                        {
                            half--;
                        }

                        // внутренние узлы...
                        for (int j = 0; j < m_numberOfNodes; j++)
                        {
                            double k = futPx + (j - half) * dK;
                            //// Обычно здесь будет лежать сплайн от замороженной улыбки...
                            //double sigma = oldInfo.ContinuousFunction.Value(k);
                            double sigma = visibleNodes[j].Anchor.ValueY;

                            xs.Add(k);
                            ys.Add(sigma);
                        }
                    }
                    #endregion 2. Готовлю сплайн
                }

                try
                {
                    if (xs.Count >= BaseCubicSpline.MinNumberOfNodes)
                    {
                        NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys);

                        info.ContinuousFunction   = spline;
                        info.ContinuousFunctionD1 = spline.DeriveD1();

                        //info.F = F;
                        //info.dT = trueTimeToExpiry;
                        res.Tag = info;
                    }
                }
                catch (Exception ex)
                {
                    m_context.Log(ex.ToString(), MessageType.Error, true);
                    return(Constants.EmptySeries);
                }

                // 2. Формирую кривую с более плотным шагом
                List <InteractiveObject> controlPoints = new List <InteractiveObject>();
                int editableNodesCount = FillEditableCurve(info, controlPoints, xs, dragMode);
                res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints);

                if (editableNodesCount != m_numberOfNodes)
                {
                    string msg = String.Format("[DEBUG:{0}] {1} nodes requested, but only {2} were prepared.",
                                               GetType().Name,
                                               m_numberOfNodes,
                                               editableNodesCount);
                    m_context.Log(msg, MessageType.Warning, true);
                }
            }

            if (!m_resetSmile)
            {
                if (m_loadSplineCoeffs)
                {
                }

                if (m_prepareSplineCoeffs)
                {
                    #region Prepare global spline
                    // Надо пересчитать сплайн в безразмерные коэффициенты

                    // Обновляю уровень IV ATM?
                    ivAtm = info.ContinuousFunction.Value(futPx);

                    SmileInfo globInfo = new SmileInfo();
                    globInfo.F            = futPx;
                    globInfo.dT           = trueTimeToExpiry;
                    globInfo.RiskFreeRate = oldInfo.RiskFreeRate;

                    StringBuilder sb = new StringBuilder(GlobalSmileKey);
                    sb.AppendLine();
                    sb.AppendFormat(CultureInfo.InvariantCulture, "F:{0}", futPx);
                    sb.AppendLine();
                    sb.AppendFormat(CultureInfo.InvariantCulture, "dT:{0}", dT);
                    sb.AppendLine();
                    sb.AppendFormat(CultureInfo.InvariantCulture, "IvAtm:{0}", ivAtm);
                    sb.AppendLine();
                    sb.AppendFormat(CultureInfo.InvariantCulture, "RiskFreeRate:{0}", globInfo.RiskFreeRate);
                    sb.AppendLine();
                    sb.AppendFormat(CultureInfo.InvariantCulture, "ShapePct:{0}", ShapePct);
                    sb.AppendLine();
                    sb.AppendLine("~~~");
                    sb.AppendFormat(CultureInfo.InvariantCulture, "X;Y");
                    sb.AppendLine();

                    //LogSimmetrizeFunc logSimmFunc = new LogSimmetrizeFunc(info.ContinuousFunction, F, 0.5);

                    List <double> xs = new List <double>();
                    List <double> ys = new List <double>();
                    foreach (InteractiveObject oldObj in res.ControlPoints)
                    {
                        if (!oldObj.AnchorIsActive)
                        {
                            continue;
                        }

                        double k = oldObj.Anchor.ValueX;
                        double x = Math.Log(k / futPx) / Math.Pow(dT, DefaultPow + m_shape) / ivAtm;

                        double sigma           = oldObj.Anchor.ValueY;
                        double sigmaNormalized = sigma / ivAtm;

                        xs.Add(x);
                        ys.Add(sigmaNormalized);

                        sb.AppendFormat(CultureInfo.InvariantCulture, "{0};{1}", x, sigmaNormalized);
                        sb.AppendLine();
                    }

                    try
                    {
                        NotAKnotCubicSpline globSpline = new NotAKnotCubicSpline(xs, ys);

                        globInfo.ContinuousFunction   = globSpline;
                        globInfo.ContinuousFunctionD1 = globSpline.DeriveD1();

                        //global.Tag = globInfo;

                        m_context.StoreGlobalObject(GlobalSmileKey, globInfo, true);

                        string msg = String.Format("[{0}] The globInfo was saved in Global cache as '{1}'.",
                                                   GetType().Name, GlobalSmileKey);
                        m_context.Log(msg, MessageType.Warning, true);

                        msg = String.Format("[{0}] The globInfo was saved in file tslab.log also. Smile:\r\n{1}",
                                            GetType().Name, sb);
                        m_context.Log(msg, MessageType.Info, true);

                        // Запись в клипбоард
                        try
                        {
                            //Thread thread = ThreadProfiler.Create(() => System.Windows.Clipboard.SetText(sb.ToString()));
                            XElement xel    = globInfo.ToXElement();
                            string   xelStr =
                                @"<?xml version=""1.0""?>
" + xel.AsString();
                            // PROD-5987 - Отключаю работу с клипбордом. Только пишу в tslab.log
                            //Thread thread = ThreadProfiler.Create(() => System.Windows.Clipboard.SetText(xelStr));
                            //thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
                            //thread.Start();
                            //thread.Join(); // Надо ли делать Join?

                            s_log.WarnFormat("Global smile info:\r\n\r\n{0}\r\n\r\n", xelStr);
                        }
                        catch (Exception clipEx)
                        {
                            m_context.Log(clipEx.ToString(), MessageType.Error, true);
                        }

                        m_context.Recalc();
                    }
                    catch (Exception ex)
                    {
                        m_context.Log(ex.ToString(), MessageType.Error, true);
                        //return Constants.EmptySeries;
                    }
                    #endregion Prepare global spline
                }
                else if (m_pasteGlobal)
                {
                    // PROD-5987 - Работа с клипбордом отключена. Функция вставки сейчас работать не будет.
                    m_context.Log($"[{GetType().Name}] Clipboard is not available. Sorry.", MessageType.Warning, true);

                    #region Paste spline from clipboard
                    //string xelStr = "";
                    //// Чтение из клипбоард
                    //try
                    //{
                    //    // PROD-5987 - Работа с клипбордом отключена. Функция вставки сейчас работать не будет.
                    //    ////Thread thread = ThreadProfiler.Create(() => System.Windows.Clipboard.SetText(sb.ToString()));
                    //    //Thread thread = ThreadProfiler.Create(() => xelStr = System.Windows.Clipboard.GetText());
                    //    //thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
                    //    //thread.Start();
                    //    //thread.Join(); // Надо ли делать Join?

                    //    if (!String.IsNullOrWhiteSpace(xelStr))
                    //    {
                    //        XDocument xDoc = XDocument.Parse(xelStr);
                    //        XElement xInfo = xDoc.Root;
                    //        SmileInfo templateSmile = SmileInfo.FromXElement(xInfo);

                    //        // Обновляю уровень IV ATM?
                    //        // TODO: перепроверить как работает редактирование шаблона
                    //        ivAtm = info.ContinuousFunction.Value(futPx);
                    //        if (Double.IsNaN(ivAtm))
                    //        {
                    //            ivAtm = refSmileInfo.ContinuousFunction.Value(futPx);

                    //            m_context.Log(String.Format("[DEBUG:{0}] ivAtm was NaN. I'll use value ivAtm:{1}", GetType().Name, ivAtm), MessageType.Warning, true);

                    //            if (Double.IsNaN(ivAtm))
                    //            {
                    //                throw new Exception(String.Format("[DEBUG:{0}] ivAtm is NaN.", GetType().Name));
                    //            }
                    //        }

                    //        templateSmile.F = futPx;
                    //        templateSmile.dT = trueTimeToExpiry;
                    //        templateSmile.RiskFreeRate = oldInfo.RiskFreeRate;

                    //        // Здесь обязательно в начале
                    //        //res.ControlPoints.Clear();
                    //        res.ControlPoints = new ReadOnlyCollection<InteractiveObject>(new InteractiveObject[0]);

                    //        SmileFunction5 func = new SmileFunction5(templateSmile.ContinuousFunction, templateSmile.ContinuousFunctionD1,
                    //            ivAtm, 0, 0, futPx, dT);
                    //        info.ContinuousFunction = func;
                    //        info.ContinuousFunctionD1 = func.DeriveD1();

                    //        // info уже лежит в Tag, поэтому при следующем пересчете сплайн уже должен пересчитаться
                    //        // по новой улыбке. Правильно?

                    //        string msg = String.Format("[DEBUG:{0}] SmileInfo was loaded from clipboard.", GetType().Name);
                    //        m_context.Log(msg, MessageType.Warning, true);

                    //        m_context.Recalc();
                    //    }
                    //}
                    //catch (Exception clipEx)
                    //{
                    //    m_context.Log(clipEx.ToString(), MessageType.Error, true);
                    //}
                    #endregion Paste spline from clipboard
                }
            }

            //res.ClickEvent -= res_ClickEvent;
            //res.ClickEvent += res_ClickEvent;
            //res.DragEvent -= res_DragEvent;
            //res.DragEvent += res_DragEvent;
            res.EndDragEvent -= res_EndDragEvent;
            res.EndDragEvent += res_EndDragEvent;

            return(res);
        }
Пример #26
0
        /// <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);
            }
        }
Пример #27
0
        public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, int barNum)
        {
            InteractiveSeries res = new InteractiveSeries();
            int barsCount         = ContextBarsCount;

            if ((barNum < barsCount - 1) || (optSer == null))
            {
                return(res);
            }

            double dT = time;

            if (Double.IsNaN(dT) || (dT < Double.Epsilon))
            {
                // [{0}] Time to expiry must be positive value. dT:{1}
                string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT);
                m_context.Log(msg, MessageType.Error, true);
                return(res);
            }

            if (smile == null)
            {
                string msg = String.Format("[{0}] Argument 'smile' must be filled with InteractiveSeries.", GetType().Name);
                m_context.Log(msg, MessageType.Error, true);
                return(res);
            }

            SmileInfo oldInfo = smile.GetTag <SmileInfo>();

            if (oldInfo == null)
            {
                string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag);
                m_context.Log(msg, MessageType.Error, true);
                return(res);
            }

            List <double> xs          = new List <double>();
            List <double> ys          = new List <double>();
            var           smilePoints = smile.ControlPoints;

            IOptionStrikePair[]      pairs         = optSer.GetStrikePairs().ToArray();
            PositionsManager         posMan        = PositionsManager.GetManager(m_context);
            List <InteractiveObject> controlPoints = new List <InteractiveObject>();

            foreach (InteractiveObject iob in smilePoints)
            {
                double rawTheta, f = iob.Anchor.ValueX;
                if (TryEstimateTheta(posMan, pairs, smile, m_greekAlgo, f, dT, m_tStep, out rawTheta))
                {
                    // Переводим тету в дифференциал 'изменение цены за 1 сутки'.
                    rawTheta = RescaleThetaToDays(m_tRemainMode, rawTheta);

                    // ReSharper disable once UseObjectOrCollectionInitializer
                    InteractivePointActive ip = new InteractivePointActive();
                    ip.IsActive = m_showNodes;
                    //ip.DragableMode = DragableMode.None;
                    //ip.Geometry = Geometries.Rect;
                    //ip.Color = System.Windows.Media.Colors.Green;
                    double y = rawTheta;
                    ip.Value = new Point(f, y);
                    string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture);
                    ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "F:{0}; Th:{1}", f, yStr);

                    controlPoints.Add(new InteractiveObject(ip));

                    xs.Add(f);
                    ys.Add(y);
                }
            }

            res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints);

            try
            {
                if (xs.Count >= BaseCubicSpline.MinNumberOfNodes)
                {
                    // ReSharper disable once UseObjectOrCollectionInitializer
                    SmileInfo info = new SmileInfo();
                    info.F            = oldInfo.F;
                    info.dT           = oldInfo.dT;
                    info.RiskFreeRate = oldInfo.RiskFreeRate;

                    NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys);

                    info.ContinuousFunction   = spline;
                    info.ContinuousFunctionD1 = spline.DeriveD1();

                    res.Tag = info;
                }
            }
            catch (Exception ex)
            {
                m_context.Log(ex.ToString(), MessageType.Error, true);
                return(Constants.EmptySeries);
            }

            return(res);
        }
Пример #28
0
        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);
        }
Пример #29
0
        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);
        }
Пример #30
0
        /// <summary>
        /// Метод под флаг TemplateTypes.INTERACTIVESPLINE
        /// </summary>
        public InteractiveSeries Execute(InteractiveSeries smile, IOptionSeries optSer, double scaleMult, int barNum)
        {
            if ((smile == null) || (optSer == null))
            {
                return(Constants.EmptySeries);
            }

            int barsCount = m_context.BarsCount;

            if (!m_context.IsLastBarUsed)
            {
                barsCount--;
            }
            if (barNum < barsCount - 1)
            {
                return(Constants.EmptySeries);
            }

            SmileInfo oldInfo = smile.GetTag <SmileInfo>();

            if ((oldInfo == null) || (oldInfo.ContinuousFunction == null))
            {
                return(Constants.EmptySeries);
            }

            double futPx = oldInfo.F;
            double dT    = oldInfo.dT;

            if (m_executeCommand)
            {
                string msg = String.Format("[{0}.StartButton] Strike: {1}", GetType().Name, m_strike);
                m_context.Log(msg, MessageType.Info, false);
            }

            #region 1. Список страйков
            HashSet <string> serList = StrikeList;
            serList.Clear();

            IOptionStrikePair[] pairs;
            if (Double.IsNaN(m_strikeStep) || (m_strikeStep <= Double.Epsilon))
            {
                pairs = optSer.GetStrikePairs().ToArray();
            }
            else
            {
                // Выделяем страйки, которые нацело делятся на StrikeStep
                pairs = (from p in optSer.GetStrikePairs()
                         let test = m_strikeStep * Math.Round(p.Strike / m_strikeStep)
                                    where DoubleUtil.AreClose(p.Strike, test)
                                    select p).ToArray();

                // [2015-12-24] Если шаг страйков по ошибке задан совершенно неправильно,
                // то в коллекцию ставим все имеющиеся страйки.
                // Пользователь потом разберется
                if (pairs.Length <= 0)
                {
                    pairs = optSer.GetStrikePairs().ToArray();
                }
            }
            //if (pairs.Length < 2)
            //    return Constants.EmptyListDouble;

            foreach (IOptionStrikePair pair in pairs)
            {
                double k = pair.Strike;
                serList.Add(k.ToString(StrikeFormat, CultureInfo.InvariantCulture));
            }
            #endregion 1. Список страйков

            InteractiveSeries        res           = Constants.EmptySeries;
            List <InteractiveObject> controlPoints = new List <InteractiveObject>();

            #region 2. Формируем улыбку просто для отображения текущего положения потенциальной котировки
            // При нулевом рабочем объёме не утруждаемся рисованием лишних линий
            if (!DoubleUtil.IsZero(m_qty))
            {
                for (int j = 0; j < pairs.Length; j++)
                {
                    var    pair  = pairs[j];
                    double sigma = oldInfo.ContinuousFunction.Value(pair.Strike) + m_shiftIv;
                    if (!DoubleUtil.IsPositive(sigma))
                    {
                        //string msg = String.Format("[DEBUG:{0}] Invalid sigma:{1} for strike:{2}", GetType().Name, sigma, nodeInfo.Strike);
                        //m_context.Log(msg, MessageType.Warning, true);
                        continue;
                    }

                    //bool isCall = (futPx <= pair.Strike);
                    bool isCall;
                    if (m_optionType == StrikeType.Call)
                    {
                        isCall = true;
                    }
                    else if (m_optionType == StrikeType.Put)
                    {
                        isCall = false;
                    }
                    else
                    {
                        isCall = (futPx <= pair.Strike);
                    }

                    StrikeType optionType = isCall ? StrikeType.Call : StrikeType.Put;
                    Contract.Assert(pair.Tick < 1, $"#1 На тестовом контуре Дерибит присылает неправильный шаг цены! Tick:{pair.Tick}; Decimals:{pair.Put.Security.Decimals}");
                    double theorOptPxDollars = FinMath.GetOptionPrice(futPx, pair.Strike, dT, sigma, oldInfo.RiskFreeRate, isCall);
                    // Сразу(!!!) переводим котировку из баксов в битки
                    double theorOptPxBitcoins = theorOptPxDollars / scaleMult;

                    // Сдвигаем цену в долларах (с учетом ш.ц. в баксах)
                    theorOptPxDollars += m_shiftPriceStep * pair.Tick * scaleMult;
                    theorOptPxDollars  = Math.Round(theorOptPxDollars / (pair.Tick * scaleMult)) * (pair.Tick * scaleMult);

                    // Сдвигаем цену в биткойнах (с учетом ш.ц. в битках)
                    theorOptPxBitcoins += m_shiftPriceStep * pair.Tick;
                    theorOptPxBitcoins  = Math.Round(theorOptPxBitcoins / pair.Tick) * pair.Tick;
                    if ((!DoubleUtil.IsPositive(theorOptPxBitcoins)) || (!DoubleUtil.IsPositive(theorOptPxDollars)))
                    {
                        //string msg = String.Format("[DEBUG:{0}] Invalid theorOptPx:{1} for strike:{2}", GetType().Name, theorOptPx, nodeInfo.Strike);
                        //m_context.Log(msg, MessageType.Warning, true);
                        continue;
                    }

                    // Пересчитываем сигму обратно, ЕСЛИ мы применили сдвиг цены в абсолютном выражении
                    if (m_shiftPriceStep != 0)
                    {
                        // Обратный пересчет в волатильность
                        sigma = FinMath.GetOptionSigma(futPx, pair.Strike, dT, theorOptPxDollars, oldInfo.RiskFreeRate, isCall);
                        if (!DoubleUtil.IsPositive(sigma))
                        {
                            //string msg = String.Format("[DEBUG:{0}] Invalid sigma:{1} for strike:{2}", GetType().Name, sigma, nodeInfo.Strike);
                            //m_context.Log(msg, MessageType.Warning, true);
                            continue;
                        }
                    }

                    // ReSharper disable once UseObjectOrCollectionInitializer
                    SmileNodeInfo nodeInfo = new SmileNodeInfo();
                    var           secDesc  = isCall ? pair.CallFinInfo.Security : pair.PutFinInfo.Security;
                    nodeInfo.F            = oldInfo.F;
                    nodeInfo.dT           = oldInfo.dT;
                    nodeInfo.RiskFreeRate = oldInfo.RiskFreeRate;
                    nodeInfo.Strike       = pair.Strike;
                    nodeInfo.Sigma        = sigma;
                    nodeInfo.OptPx        = theorOptPxBitcoins;
                    nodeInfo.OptionType   = isCall ? StrikeType.Call : StrikeType.Put;
                    nodeInfo.Pair         = pair;

                    nodeInfo.Symbol   = secDesc.Name;
                    nodeInfo.DSName   = secDesc.DSName;
                    nodeInfo.Expired  = secDesc.Expired;
                    nodeInfo.FullName = secDesc.FullName;

                    // ReSharper disable once UseObjectOrCollectionInitializer
                    InteractivePointActive tmp = new InteractivePointActive();

                    tmp.IsActive     = true;
                    tmp.ValueX       = pair.Strike;
                    tmp.ValueY       = sigma;
                    tmp.DragableMode = DragableMode.Yonly;
                    tmp.Tooltip      = String.Format(CultureInfo.InvariantCulture,
                                                     " F: {0}\r\n K: {1}; IV: {2:P2}\r\n {3} px {4}",
                                                     futPx, pair.Strike, sigma, optionType, theorOptPxBitcoins);

                    tmp.Tag = nodeInfo;

                    //tmp.Color = Colors.White;
                    if (m_qty > 0)
                    {
                        tmp.Geometry = Geometries.Triangle;
                    }
                    else if (m_qty < 0)
                    {
                        tmp.Geometry = Geometries.TriangleDown;
                    }
                    else
                    {
                        tmp.Geometry = Geometries.None;
                    }

                    InteractiveObject obj = new InteractiveObject();
                    obj.Anchor = tmp;

                    controlPoints.Add(obj);
                }

                // ReSharper disable once UseObjectOrCollectionInitializer
                res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку
                res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints);

                // ReSharper disable once UseObjectOrCollectionInitializer
                SmileInfo sInfo = new SmileInfo();
                sInfo.F            = futPx;
                sInfo.dT           = dT;
                sInfo.Expiry       = oldInfo.Expiry;
                sInfo.ScriptTime   = oldInfo.ScriptTime;
                sInfo.RiskFreeRate = oldInfo.RiskFreeRate;
                sInfo.BaseTicker   = oldInfo.BaseTicker;

                res.Tag = sInfo;

                if (controlPoints.Count > 0)
                {
                    res.ClickEvent -= InteractiveSplineOnQuoteIvEvent;
                    res.ClickEvent += InteractiveSplineOnQuoteIvEvent;

                    m_clickableSeries = res;
                }
            }
            #endregion 2. Формируем улыбку просто для отображения текущего положения потенциальной котировки

            PositionsManager posMan = PositionsManager.GetManager(m_context);
            if (m_cancelAllLong)
            {
                posMan.DropAllLongIvTargets(m_context);
            }
            if (m_cancelAllShort)
            {
                posMan.DropAllShortIvTargets(m_context);
            }

            #region 4. Котирование
            {
                var longTargets  = posMan.GetIvTargets(true);
                var shortTargets = posMan.GetIvTargets(false);
                var ivTargets    = longTargets.Union(shortTargets).ToList();
                for (int j = 0; j < ivTargets.Count; j++)
                {
                    var ivTarget = ivTargets[j];

                    // PROD-6102 - Требуется точное совпадение опционной серии
                    if (optSer.ExpirationDate.Date != ivTarget.SecInfo.Expiry.Date)
                    {
                        // Вывести предупреждение???
                        continue;
                    }

                    IOptionStrikePair pair;
                    double            k = ivTarget.SecInfo.Strike;
                    if (!optSer.TryGetStrikePair(k, out pair))
                    {
                        // Вывести предупреждение???
                        continue;
                    }

                    double      sigma;
                    QuoteIvMode quoteMode = ivTarget.QuoteMode;
                    if (quoteMode == QuoteIvMode.Absolute)
                    {
                        sigma = ivTarget.EntryIv;
                    }
                    else
                    {
                        sigma = oldInfo.ContinuousFunction.Value(k) + ivTarget.EntryIv;
                        if (!DoubleUtil.IsPositive(sigma))
                        {
                            //string msg = String.Format("[DEBUG:{0}] Invalid sigma:{1} for strike:{2}", GetType().Name, sigma, nodeInfo.Strike);
                            //m_context.Log(msg, MessageType.Warning, true);
                            continue;
                        }
                    }

                    //bool isCall = (futPx <= pair.Strike);
                    // Определяю тип опциона на основании информации в Задаче
                    StrikeType taskOptionType = StrikeType.Any;
                    if (ivTarget.SecInfo.StrikeType.HasValue)
                    {
                        taskOptionType = ivTarget.SecInfo.StrikeType.Value;
                    }

                    bool isCall;
                    if (taskOptionType == StrikeType.Call)
                    {
                        isCall = true;
                    }
                    else if (taskOptionType == StrikeType.Put)
                    {
                        isCall = false;
                    }
                    else
                    {
                        isCall = (futPx <= pair.Strike); // Это аварийная ситуация?
                    }
                    StrikeType optionType = isCall ? StrikeType.Call : StrikeType.Put;
                    Contract.Assert(pair.Tick < 1, $"#3 На тестовом контуре Дерибит присылает неправильный шаг цены! Tick:{pair.Tick}; Decimals:{pair.Put.Security.Decimals}");
                    double theorOptPxDollars = FinMath.GetOptionPrice(futPx, pair.Strike, dT, sigma, oldInfo.RiskFreeRate, isCall);
                    // Сразу(!!!) переводим котировку из баксов в битки
                    double theorOptPxBitcoins = theorOptPxDollars / scaleMult;

                    // Сдвигаем цену в долларах (с учетом ш.ц. в баксах)
                    theorOptPxDollars += ivTarget.EntryShiftPrice * pair.Tick * scaleMult;
                    theorOptPxDollars  = Math.Round(theorOptPxDollars / (pair.Tick * scaleMult)) * (pair.Tick * scaleMult);

                    // Сдвигаем цену в биткойнах (с учетом ш.ц. в битках)
                    theorOptPxBitcoins += ivTarget.EntryShiftPrice * pair.Tick;
                    theorOptPxBitcoins  = Math.Round(theorOptPxBitcoins / pair.Tick) * pair.Tick;
                    if ((!DoubleUtil.IsPositive(theorOptPxBitcoins)) || (!DoubleUtil.IsPositive(theorOptPxDollars)))
                    {
                        //string msg = String.Format("[DEBUG:{0}] Invalid theorOptPx:{1} for strike:{2}", GetType().Name, theorOptPx, nodeInfo.Strike);
                        //m_context.Log(msg, MessageType.Warning, true);
                        continue;
                    }

                    IOptionStrike optStrike = isCall ? pair.Call : pair.Put;
                    ISecurity     sec       = optStrike.Security;
                    double        totalQty  = posMan.GetTotalQty(sec, m_context.BarsCount, TotalProfitAlgo.AllPositions, ivTarget.IsLong);
                    // Поскольку котирование страйка по волатильности -- это вопрос набора нужного количества СТРЕДДЛОВ,
                    // то учитывать надо суммарный объём опционов как в колах, так и в путах.
                    // НО ЗАДАЧУ-ТО Я СТАВЛЮ ДЛЯ КОНКРЕТНОГО ИНСТРУМЕНТА!
                    // Как быть?
                    //double totalQty = posMan.GetTotalQty(pair.Put.Security, m_context.BarsCount, TotalProfitAlgo.AllPositions, ivTarget.IsLong);
                    //totalQty += posMan.GetTotalQty(pair.Call.Security, m_context.BarsCount, TotalProfitAlgo.AllPositions, ivTarget.IsLong);
                    double targetQty = Math.Abs(ivTarget.TargetShares) - totalQty;
                    // Если имеется дробный LotTick (как в Дерибит к примеру), то надо предварительно округлить
                    targetQty = sec.RoundShares(targetQty);
                    if (targetQty > 0)
                    {
                        string note = String.Format(CultureInfo.InvariantCulture,
                                                    "{0}; ActQty:{1}; Px:{2}; IV:{3:P2}",
                                                    ivTarget.EntryNotes, targetQty, theorOptPxBitcoins, sigma);
                        if (ivTarget.IsLong)
                        {
                            posMan.BuyAtPrice(m_context, sec, targetQty, theorOptPxBitcoins, ivTarget.EntrySignalName, note);
                        }
                        else
                        {
                            posMan.SellAtPrice(m_context, sec, targetQty, theorOptPxBitcoins, ivTarget.EntrySignalName, note);
                        }
                    }
                    else
                    {
                        string msg = String.Format(CultureInfo.InvariantCulture,
                                                   "IvTarget cancelled. SignalName:{0}; Notes:{1}", ivTarget.EntrySignalName, ivTarget.EntryNotes);
                        posMan.CancelVolatility(m_context, ivTarget, msg);

                        // TODO: потом убрать из ГЛ
                        m_context.Log(msg, MessageType.Info, true, new Dictionary <string, object> {
                            { "VOLATILITY_ORDER_CANCELLED", msg }
                        });
                    }
                }
            }
            #endregion 4. Котирование

            #region 5. Торговля
            if (m_executeCommand && (!DoubleUtil.IsZero(m_qty)))
            {
                double k;
                if ((!Double.TryParse(m_strike, out k)) &&
                    (!Double.TryParse(m_strike, NumberStyles.Any, CultureInfo.InvariantCulture, out k)))
                {
                    return(res);
                }

                var pair = (from p in pairs where DoubleUtil.AreClose(k, p.Strike) select p).SingleOrDefault();
                if (pair == null)
                {
                    return(res);
                }

                InteractiveObject obj = (from o in controlPoints where DoubleUtil.AreClose(k, o.Anchor.ValueX) select o).SingleOrDefault();
                if (obj == null)
                {
                    return(res);
                }

                // TODO: для режима котирования в абсолютных числах сделать отдельную ветку
                //double iv = obj.Anchor.ValueY;
                const QuoteIvMode QuoteMode = QuoteIvMode.Relative;
                if (posMan.BlockTrading)
                {
                    string msg = String.Format(RM.GetString("OptHandlerMsg.PositionsManager.TradingBlocked"),
                                               m_context.Runtime.TradeName + ":QuoteIv");
                    m_context.Log(msg, MessageType.Warning, true);
                    return(res);
                }

                // Выбираю тип инструмента пут или колл?
                bool isCall;
                if (m_optionType == StrikeType.Call)
                {
                    isCall = true;
                }
                else if (m_optionType == StrikeType.Put)
                {
                    isCall = false;
                }
                else
                {
                    isCall = (futPx <= k);
                }

                double iv     = m_shiftIv;
                int    shift  = m_shiftPriceStep;
                var    option = isCall ? pair.Call : pair.Put;
                if (m_qty > 0)
                {
                    // Пересчитываю целочисленный параметр Qty в фактические лоты конкретного инструмента
                    double actQty  = m_qty * option.LotTick;
                    string sigName = String.Format(CultureInfo.InvariantCulture,
                                                   "Qty:{0}; IV:{1:P2}+{2}; dT:{3}; Mode:{4}", actQty, iv, shift, dT, QuoteMode);
                    posMan.BuyVolatility(m_context, option, Math.Abs(actQty), QuoteMode, iv, shift, "BuyVola", sigName);

                    m_context.Log(sigName, MessageType.Info, false);
                }
                else if (m_qty < 0)
                {
                    // Пересчитываю целочисленный параметр Qty в фактические лоты конкретного инструмента
                    double actQty  = m_qty * option.LotTick;
                    string sigName = String.Format(CultureInfo.InvariantCulture,
                                                   "Qty:{0}; IV:{1:P2}+{2}; dT:{3}; Mode:{4}", actQty, iv, shift, dT, QuoteMode);
                    posMan.SellVolatility(m_context, option, Math.Abs(actQty), QuoteMode, iv, shift, "SellVola", sigName);

                    m_context.Log(sigName, MessageType.Info, false);
                }
            }
            #endregion 5. Торговля

            return(res);
        }