Esempio n. 1
0
        private IList <double> ExecuteAll(ISecurity sec, IOptionSeries optSer)
        {
            if (sec == null)
            {
                return(Constants.EmptyListDouble);
            }

            IList <double> basePrices = CommonStreamExecute(m_variableId + "_basePrices", m_variableId + "_basePriceHistory",
                                                            sec, m_repeatLastPx, true, false, new object[] { sec, optSer });

            if (basePrices.Count > 0)
            {
                double px           = basePrices[basePrices.Count - 1];
                double displayValue = FixedValue.ConvertToDisplayUnits(m_valueMode, px);
                m_displayPrice.Value = displayValue;
            }

            return(new ReadOnlyCollection <double>(basePrices));
        }
Esempio n. 2
0
        /// <summary>
        /// Обработчик под тип входных данных OPTION
        /// </summary>
        public double Execute(IOption opt, int barNum)
        {
            if ((opt == null) || (opt.UnderlyingAsset == null))
            {
                return(Double.NaN); // В данном случае намеренно возвращаю Double.NaN
            }
            int len = m_context.BarsCount;

            if (len <= 0)
            {
                return(Double.NaN); // В данном случае намеренно возвращаю Double.NaN
            }
            if (len <= barNum)
            {
                string msg = String.Format("[{0}:{1}] (BarsCount <= barNum)! BarsCount:{2}; barNum:{3}",
                                           m_context.Runtime.TradeName, GetType().Name, m_context.BarsCount, barNum);
                m_context.Log(msg, MessageType.Warning, true);
                barNum = len - 1;
            }
            DateTime now  = opt.UnderlyingAsset.Bars[barNum].Date;
            double   risk = CommonExecute(m_variableId + "_RiskN2", now, true, true, false, barNum, new object[] { opt });

            //// [2015-07-15] Отключаю вывод отладочных сообщений в лог агента.
            //if (barNum >= 0.9 * len)
            //{
            //    string msg = String.Format("[{0}:{1}] barNum:{2}; risk:{3}; now:{4}",
            //        m_context.Runtime.TradeName, GetType().Name, barNum, risk, now.ToString("dd-MM-yyyy HH:mm:ss.fff"));
            //    m_context.Log(msg, MessageType.Info, false);
            //}

            // Просто заполнение свойства для отображения на UI
            int barsCount = ContextBarsCount;

            if (barNum >= barsCount - 1)
            {
                double displayValue = FixedValue.ConvertToDisplayUnits(m_valueMode, risk);
                m_displayRisk.Value = displayValue;
            }

            return(risk);
        }
Esempio n. 3
0
        /// <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);
        }
Esempio n. 4
0
        /// <summary>
        /// Метод под флаг TemplateTypes.SECURITY, чтобы подключаться к источнику-БА
        /// </summary>
        public double Execute(ISecurity sec, int barNum)
        {
            double failRes = Constants.NaN;

            if (m_repeatLastPx)
            {
                failRes = Double.IsNaN(m_prevPx) ? Constants.NaN : m_prevPx;
            }

            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

            if (sec == null)
            {
                return(failRes);
            }

            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)

                    #region switch(m_pxMode)
                    switch (m_pxMode)
                    {
                    case BasePxMode.FixedPx:
                        px       = m_fixedPx;
                        m_prevPx = px;
                        break;

                    case BasePxMode.LastTrade:
                        if (sec.FinInfo.LastPrice.HasValue)
                        {
                            px       = sec.FinInfo.LastPrice.Value;
                            m_prevPx = px;
                        }
                        else
                        {
                            px = failRes;
                        }
                        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;
                            m_prevPx = px;
                        }
                        else if (info.Ask.HasValue && info.AskSize.HasValue && (info.AskSize.Value > 0))
                        {
                            px       = info.Ask.Value;
                            m_prevPx = px;
                        }
                        else if (info.Bid.HasValue && info.BidSize.HasValue && (info.BidSize.Value > 0))
                        {
                            px       = info.Bid.Value;
                            m_prevPx = px;
                        }
                        else
                        {
                            // Приемлемо ли такое решение?
                            if (info.LastPrice.HasValue)
                            {
                                px       = info.LastPrice.Value;
                                m_prevPx = px;
                            }
                            else
                            {
                                px = failRes;
                            }
                        }
                    }
                    break;

                    default:
                        throw new NotImplementedException("pxMode:" + m_pxMode);
                    }
                    #endregion switch(m_pxMode)

                    basePrices[lastBarDate] = px;

                    double displayValue = FixedValue.ConvertToDisplayUnits(m_valueMode, px);
                    m_displayPrice.Value = displayValue;

                    return(px);

                    #endregion Process last bar(s)
                }
            }
        }
Esempio n. 5
0
        /// <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));
            }
        }
Esempio n. 6
0
        public InteractiveSeries Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, double riskFreeRatePct, int barNum)
        {
            int barsCount = ContextBarsCount;

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

            // В оптимизации ничего рисовать не надо
            if (Context.IsOptimization)
            {
                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);
                m_context.Log(msg, MessageType.Error, true);
                return(Constants.EmptySeries);
            }

            if (!DoubleUtil.IsPositive(futPx))
            {
                // [{0}] Base asset price must be positive value. F:{1}
                string msg = RM.GetStringFormat("OptHandlerMsg.FutPxMustBePositive", GetType().Name, futPx);
                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, 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);
            }

            if (!oldInfo.IsValidSmileParams)
            {
                string msg = String.Format("[{0}] SmileInfo must have valid smile params. IsValidSmileParams:{1}", GetType().Name, oldInfo.IsValidSmileParams);
                if (wasInitialized)
                {
                    m_context.Log(msg, MessageType.Error, false);
                }
                return(Constants.EmptySeries);
            }

            double ivAtm;

            if (!oldInfo.ContinuousFunction.TryGetValue(futPx, out ivAtm))
            {
                return(Constants.EmptySeries);
            }

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

            // TODO: Нужно ли писать отдельный код для лаборатории? Чтобы показывать позиции из симуляции?
            // if (!Context.Runtime.IsAgentMode)

            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           futStep = optSer.UnderlyingAsset.Tick;
            PositionsManager posMan  = PositionsManager.GetManager(m_context);
            // Вытаскиваем ВСЕ позиции фьючерса
            ReadOnlyCollection <IPosition> basePositions = posMan.GetClosedOrActiveForBar(optSer.UnderlyingAsset);
            // Вытаскиваем ВСЕ позиции опционов
            var optPositions = SingleSeriesProfile.GetAllOptionPositions(posMan, pairs);

            // 1. Если в позиции вообще нет опционов -- сразу выходим. Эффективную волатильность построить нельзя.
            int posAmount = (from t in optPositions select(t.Item1.Count + t.Item2.Count)).Sum();

            if (posAmount <= 0)
            {
                return(Constants.EmptySeries);
            }

            // 2. TODO: посчитать позиции без учета синтетических фьючерсов. Если позиция только из синтетики -- выходим.

            // 3. Вычисляем эффективную волатильность и заодно разбиваем позицию на длинные и короткие
            //    Но это имеет смысл только если сразу рисовать позу!!!
            double effectiveLongIvAtm, effectiveShortIvAtm;

            Tuple <ReadOnlyCollection <IPosition>, ReadOnlyCollection <IPosition> >[] longPositions;
            Tuple <ReadOnlyCollection <IPosition>, ReadOnlyCollection <IPosition> >[] shortPositions;
            bool ok = posMan.TryEstimateEffectiveIv(oldInfo, optSer, lastBarIndex,
                                                    out effectiveLongIvAtm, out effectiveShortIvAtm, out longPositions, out shortPositions);

            if (!ok)
            {
                // Мы не смогли завершить алгоритм, но получили какую-то оценку волатильностей. Нарисуем ее?
                if ((!DoubleUtil.IsPositive(effectiveLongIvAtm)) ||
                    (!DoubleUtil.IsPositive(effectiveShortIvAtm)))
                {
                    return(Constants.EmptySeries);
                }
            }

            Contract.Assert(longPositions != null, "longPositions==null ???");
            Contract.Assert(shortPositions != null, "shortPositions==null ???");

            double actualIv     = ShowLongPositions ? effectiveLongIvAtm : effectiveShortIvAtm;
            double displayValue = FixedValue.ConvertToDisplayUnits(m_valueMode, actualIv);

            m_displayIv.Value = displayValue;

            Contract.Assert(DoubleUtil.IsPositive(actualIv), $"Это вообще что-то странное. Почему плохой айви? actualIv:{actualIv}");

            // Это вообще что-то странное. Как так?
            if (!DoubleUtil.IsPositive(actualIv))
            {
                return(Constants.EmptySeries);
            }

            // 5. Подготавливаю улыбку (достаточно функции, без обвязки)
            var lowSmileFunc = new SmileFunctionExtended(
                SmileFunction5.TemplateFuncRiz4Nov1,
                actualIv, oldInfo.SkewAtm, oldInfo.Shape, futPx, dT);

            // 7. Подготавливаем графическое отображение позиции. Причем нам даже сплайн не нужен.
            var actualPositions = ShowLongPositions ? longPositions : shortPositions;
            List <InteractiveObject> controlPoints = new List <InteractiveObject>();

            for (int j = 0; j < pairs.Length; j++)
            {
                var pair  = pairs[j];
                var tuple = actualPositions[j];

                // На данном страйке позиций нет? Идем дальше.
                if ((tuple.Item1.Count <= 0) && (tuple.Item2.Count <= 0))
                {
                    continue;
                }

                double sigma;
                if ((!lowSmileFunc.TryGetValue(pair.Strike, out sigma)) ||
                    (!DoubleUtil.IsPositive(sigma)))
                {
                    // TODO: Это очень странно. Вывести в лог? Проигнорировать страйк?
                    sigma = actualIv;
                }

                var    putPositions  = tuple.Item1;
                var    callPositions = tuple.Item2;
                double putQty        = PositionsManager.GetTotalQty(putPositions);
                double callQty       = PositionsManager.GetTotalQty(callPositions);

                int    decimals  = optSer.UnderlyingAsset.Decimals + 1;
                double putPx     = FinMath.GetOptionPrice(futPx, pair.Strike, dT, sigma, riskFreeRatePct, false);
                double callPx    = FinMath.GetOptionPrice(futPx, pair.Strike, dT, sigma, riskFreeRatePct, true);
                string putPxStr  = putPx.ToString("N" + decimals, CultureInfo.InvariantCulture);
                string callPxStr = callPx.ToString("N" + decimals, CultureInfo.InvariantCulture);

                // ReSharper disable once UseObjectOrCollectionInitializer
                InteractivePointActive ip = new InteractivePointActive();
                // TODO: вывести в тултип дополнительные подробности о составе позиции на этом страйке
                ip.Tooltip = String.Format(CultureInfo.InvariantCulture,
                                           " K: {0}; IV: {1:#0.00}%\r\n PutQty: {2}; CallQty: {3}\r\n PutPx: {4}; CallPx: {5}\r\n Total: {6}",
                                           pair.Strike, sigma * Constants.PctMult, putQty, callQty, putPxStr, callPxStr, putQty + callQty);
                ip.Value = new Point(pair.Strike, sigma);

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

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

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

            SetHandlerInitialized(now, true);

            return(res);
        }
        /// <summary>
        /// Обработчик под тип входных данных OPTION_SERIES
        /// </summary>
        public InteractiveSeries Execute(double price, double trueTimeToExpiry, IOptionSeries optSer, double ratePct, int barNum)
        {
            int barsCount = ContextBarsCount;

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

            // PROD-5952 - Не надо дергать стакан без нужды
            //optSer.UnderlyingAsset.UpdateQueueData();
            FinInfo bSecFinInfo = optSer.UnderlyingAsset.FinInfo;

            if (bSecFinInfo.LastPrice == null)
            {
                return(Constants.EmptySeries);
            }

            double futPx = price;
            // ФОРТС использует плоское календарное время
            DateTime optExpiry = optSer.ExpirationDate.Date.Add(m_expiryTime);
            double   dT        = (optExpiry - bSecFinInfo.LastUpdate).TotalYears();

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

            if (Double.IsNaN(futPx) || (futPx < Double.Epsilon))
            {
                // [{0}] Base asset price must be positive value. F:{1}
                string msg = RM.GetStringFormat("OptHandlerMsg.FutPxMustBePositive", GetType().Name, futPx);
                m_context.Log(msg, MessageType.Error, true);
                return(Constants.EmptySeries);
            }

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

            if (Double.IsNaN(ratePct))
            {
                //throw new ScriptException("Argument 'ratePct' contains NaN for some strange reason. rate:" + rate);
                return(Constants.EmptySeries);
            }

            double effectiveTime = m_rescaleTime ? trueTimeToExpiry : dT;

            List <double> xs = new List <double>();
            List <double> ys = new List <double>();

            IOptionStrikePair[] pairs = (from pair in optSer.GetStrikePairs()
                                         //orderby pair.Strike ascending -- уже отсортировано!
                                         select pair).ToArray();

            if (pairs.Length < 2)
            {
                string msg = String.Format("[{0}] optSer must contain few strike pairs. pairs.Length:{1}", GetType().Name, pairs.Length);
                m_context.Log(msg, MessageType.Warning, true);
                return(Constants.EmptySeries);
            }

            for (int j = 0; j < pairs.Length; j++)
            {
                //bool showPoint = true;
                IOptionStrikePair sInfo = pairs[j];
                double            k     = sInfo.Strike;
                //// Сверхдалекие страйки игнорируем
                //if ((k < m_minStrike) || (m_maxStrike < k))
                //{
                //    showPoint = false;
                //}

                if ((sInfo.PutFinInfo == null) || (sInfo.CallFinInfo == null) ||
                    (!sInfo.PutFinInfo.TheoreticalPrice.HasValue) || (!sInfo.PutFinInfo.Volatility.HasValue) ||
                    (sInfo.PutFinInfo.TheoreticalPrice.Value <= 0) || (sInfo.PutFinInfo.Volatility.Value <= 0) ||
                    (!sInfo.CallFinInfo.TheoreticalPrice.HasValue) || (!sInfo.CallFinInfo.Volatility.HasValue) ||
                    (sInfo.CallFinInfo.TheoreticalPrice.Value <= 0) || (sInfo.CallFinInfo.Volatility.Value <= 0))
                {
                    continue;
                }

                // Биржа шлет несогласованную улыбку
                //double virtualExchangeF = sInfo.CallFinInfo.TheoreticalPrice.Value - sInfo.PutFinInfo.TheoreticalPrice.Value + sInfo.Strike;
                if ((m_optionType == StrikeType.Any) || (m_optionType == StrikeType.Put))
                {
                    double optSigma = sInfo.PutFinInfo.Volatility.Value;
                    if (m_rescaleTime)
                    {
                        // optSigma = FinMath.GetOptionSigma(futPx, k, effectiveTime, optPx, 0, false);
                        optSigma = FinMath.RescaleIvToAnotherTime(dT, optSigma, trueTimeToExpiry);
                    }

                    double optPx = sInfo.PutFinInfo.TheoreticalPrice ?? Double.NaN;

                    //// ReSharper disable once UseObjectOrCollectionInitializer
                    //InteractivePointActive ip = new InteractivePointActive(k, optSigma);
                    //ip.IsActive = m_showNodes;
                    //ip.Geometry = Geometries.Ellipse;
                    //ip.Color = System.Windows.Media.Colors.Cyan;
                    //ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}; Px:{2}\r\ndT:{3:0.000}; Date:{4}",
                    //    k, Constants.PctMult * optSigma, optPx,
                    //    FixedValue.ConvertToDisplayUnits(FixedValueMode.YearsAsDays, effectiveTime),
                    //    bSecFinInfo.LastUpdate.ToString(DateTimeFormatWithMs, CultureInfo.InvariantCulture));

                    if (optSigma > 0)
                    {
                        // Это условие позволяет не вставлять точки с совпадающими абсциссами
                        if ((xs.Count <= 0) ||
                            (!DoubleUtil.AreClose(k, xs[xs.Count - 1])))
                        {
                            xs.Add(k);
                            ys.Add(optSigma);
                        }
                    }
                }

                if ((m_optionType == StrikeType.Any) || (m_optionType == StrikeType.Call))
                {
                    double optSigma = sInfo.CallFinInfo.Volatility.Value;
                    if (m_rescaleTime)
                    {
                        // optSigma = FinMath.GetOptionSigma(futPx, k, effectiveTime, optPx, 0, false);
                        optSigma = FinMath.RescaleIvToAnotherTime(dT, optSigma, trueTimeToExpiry);
                    }

                    double optPx = sInfo.CallFinInfo.TheoreticalPrice ?? Double.NaN;

                    //// ReSharper disable once UseObjectOrCollectionInitializer
                    //InteractivePointActive ip = new InteractivePointActive(k, optSigma);
                    //ip.IsActive = m_showNodes;
                    //ip.Geometry = Geometries.Ellipse;
                    //ip.Color = System.Windows.Media.Colors.Cyan;
                    //ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}; Px:{2}\r\ndT:{3:0.000}; Date:{4}",
                    //    k, Constants.PctMult * optSigma, optPx,
                    //    FixedValue.ConvertToDisplayUnits(FixedValueMode.YearsAsDays, effectiveTime),
                    //    bSecFinInfo.LastUpdate.ToString(DateTimeFormatWithMs, CultureInfo.InvariantCulture));

                    if (optSigma > 0)
                    {
                        // Это условие позволяет не вставлять точки с совпадающими абсциссами
                        if ((xs.Count <= 0) ||
                            (!DoubleUtil.AreClose(k, xs[xs.Count - 1])))
                        {
                            xs.Add(k);
                            ys.Add(optSigma);
                        }
                    }
                }
            }

            #region 3. Строим сплайн по биржевой улыбке с узлами в страйках
            NotAKnotCubicSpline spline = null, splineD1 = null;
            try
            {
                if (xs.Count >= BaseCubicSpline.MinNumberOfNodes)
                {
                    spline   = new NotAKnotCubicSpline(xs, ys);
                    splineD1 = spline.DeriveD1();
                }
                else
                {
                    return(Constants.EmptySeries);
                }
            }
            catch (Exception ex)
            {
                string msg = String.Format("bSecFinInfo.LastUpdate:{0}; Bars.Last.Date:{1}\r\n\r\nException:{2}",
                                           bSecFinInfo.LastUpdate, optSer.UnderlyingAsset.Bars[optSer.UnderlyingAsset.Bars.Count - 1].Date, ex);
                m_context.Log(msg, MessageType.Error, true);
                return(Constants.EmptySeries);
            }
            #endregion 3. Строим сплайн по биржевой улыбке с узлами в страйках

            #region 5. С помощью сплайна уже можно строить гладкую улыбку
            double futStep = optSer.UnderlyingAsset.Tick;
            double dK = pairs[1].Strike - pairs[0].Strike;
            SortedDictionary <double, IOptionStrikePair> strikePrices;
            if (!SmileImitation5.TryPrepareImportantPoints(pairs, futPx, futStep, -1, out strikePrices))
            {
                string msg = String.Format("[WARNING:{0}] It looks like there is no suitable points for the smile. pairs.Length:{1}", GetType().Name, pairs.Length);
                m_context.Log(msg, MessageType.Warning, true);
                return(Constants.EmptySeries);
            }

            List <InteractiveObject> controlPoints = new List <InteractiveObject>();
            //for (int j = 0; j < pairs.Length; j++)
            foreach (var kvp in strikePrices)
            {
                bool   showPoint = (kvp.Value != null);
                double k         = kvp.Key;
                double sigma;
                if (!spline.TryGetValue(k, out sigma))
                {
                    continue;
                }
                double vol = sigma;

                if (Double.IsNaN(sigma) || Double.IsInfinity(sigma) || (sigma < Double.Epsilon))
                {
                    continue;
                }

                InteractivePointLight ip;
                if (m_showNodes && showPoint)
                {
                    // Как правило, эта ветка вообще не используется,
                    // потому что я не смотрю узлы биржевой улыбки.
                    double optPx = Double.NaN;
                    // ReSharper disable once UseObjectOrCollectionInitializer
                    InteractivePointActive tip = new InteractivePointActive(k, vol);
                    tip.IsActive = m_showNodes && showPoint;
                    tip.Geometry = Geometries.Ellipse;
                    tip.Color    = AlphaColors.Cyan;
                    tip.Tooltip  = String.Format("K:{0}; IV:{1:P2}; Px:{2}\r\ndT:{3:0.000}; Date:{4}",
                                                 k, vol, optPx,
                                                 FixedValue.ConvertToDisplayUnits(FixedValueMode.YearsAsDays, effectiveTime),
                                                 bSecFinInfo.LastUpdate.ToString(DateTimeFormatWithMs, CultureInfo.InvariantCulture));
                    ip = tip;
                }
                else
                {
                    ip = new InteractivePointLight(k, vol);
                }

                InteractiveObject obj = new InteractiveObject(ip);
                controlPoints.Add(obj);
            }
            #endregion 5. С помощью сплайна уже можно строить гладкую улыбку

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

            var      baseSec    = optSer.UnderlyingAsset;
            DateTime scriptTime = baseSec.Bars[baseSec.Bars.Count - 1].Date;

            // ReSharper disable once UseObjectOrCollectionInitializer
            SmileInfo info = new SmileInfo();
            info.F            = futPx;
            info.dT           = effectiveTime;
            info.Expiry       = optSer.ExpirationDate;
            info.ScriptTime   = scriptTime;
            info.RiskFreeRate = ratePct;
            info.BaseTicker   = baseSec.Symbol;

            info.ContinuousFunction   = spline;
            info.ContinuousFunctionD1 = splineD1;

            res.Tag = info;

            return(res);
        }
Esempio n. 8
0
        public double Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, double riskFreeRatePct, int barNum)
        {
            int barsCount = ContextBarsCount;

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

            // В оптимизации ничего рисовать не надо
            if (Context.IsOptimization)
            {
                return(Constants.NaN);
            }

            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);
                m_context.Log(msg, MessageType.Error, true);
                return(Constants.NaN);
            }

            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.NaN);
            }

            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.NaN);
            }

            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.NaN);
            }

            if (!oldInfo.IsValidSmileParams)
            {
                string msg = String.Format("[{0}] SmileInfo must have valid smile params. IsValidSmileParams:{1}", GetType().Name, oldInfo.IsValidSmileParams);
                if (wasInitialized)
                {
                    m_context.Log(msg, MessageType.Error, false);
                }
                return(Constants.NaN);
            }

            double ivAtm;

            if (!oldInfo.ContinuousFunction.TryGetValue(futPx, out ivAtm))
            {
                return(Constants.NaN);
            }

            if (!DoubleUtil.IsPositive(ivAtm))
            {
                // [{0}] ivAtm must be positive value. ivAtm:{1}
                string msg = RM.GetStringFormat("OptHandlerMsg.IvAtmMustBePositive", GetType().Name, ivAtm);
                if (wasInitialized)
                {
                    m_context.Log(msg, MessageType.Error, true);
                }
                return(Constants.NaN);
            }

            // TODO: Нужно ли писать отдельный код для лаборатории? Чтобы показывать позиции из симуляции?
            // if (!Context.Runtime.IsAgentMode)

            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.NaN);
            }

            double           futStep = optSer.UnderlyingAsset.Tick;
            PositionsManager posMan  = PositionsManager.GetManager(m_context);
            // Вытаскиваем ВСЕ позиции фьючерса
            ReadOnlyCollection <IPosition> basePositions = posMan.GetClosedOrActiveForBar(optSer.UnderlyingAsset);
            // Вытаскиваем ВСЕ позиции опционов
            var optPositions = SingleSeriesProfile.GetAllOptionPositions(posMan, pairs);

            // 1. Если в позиции вообще нет опционов -- сразу выходим. Эффективную волатильность построить нельзя.
            int posAmount = (from t in optPositions select(t.Item1.Count + t.Item2.Count)).Sum();

            if (posAmount <= 0)
            {
                return(Constants.NaN);
            }

            // 3. Вычисляем эффективную волатильность и заодно разбиваем позицию на длинные и короткие
            //    Но это имеет смысл только если сразу рисовать позу!!!
            double effectiveLongIvAtm, effectiveShortIvAtm;
            bool   ok = posMan.TryEstimateEffectiveIv(oldInfo, optSer, lastBarIndex,
                                                      out effectiveLongIvAtm, out effectiveShortIvAtm);

            if (!ok)
            {
                return(Constants.NaN);
            }

            double res;

            if (ShowLongPositions)
            {
                res = effectiveLongIvAtm;
            }
            else
            {
                res = effectiveShortIvAtm;
            }

            double disp = FixedValue.ConvertToDisplayUnits(m_valueMode, res);

            m_displayIv.Value = disp;

            SetHandlerInitialized(now, true);

            return(res);
        }