コード例 #1
0
ファイル: IvOnF.cs プロジェクト: barbagrigia/Handlers
        /// <summary>
        /// Вычисление нескольких видов наклона улыбки и обновление исторической серии,
        /// которая потенциально может быть сохранена в глобальный кеш.
        /// При записи в кеш серия помещается в NotClearableContainer, чтобы выживать при очистке памяти.
        /// </summary>
        public static bool TryCalcAndWriteSkews(IContext context, NotAKnotCubicSpline smileSpline,
                                                bool useGlobal, bool allowGlobalWrite, int savePeriod,
                                                string futSymbol, DateTime optExpiry, double futPx, DateTime lastBarDate,
                                                TimeRemainMode tRemainMode, double plainTimeAsYears, double timeAsYears)
        {
            bool successSkew = false;
            var  splineD1    = smileSpline.DeriveD1();

            if (splineD1.TryGetValue(futPx, out var skewAtm))
            {
                //string symbol = optSer.UnderlyingAsset.Symbol;
                // А. Записываем в Глобальный Кеш РАЗМЕРНЫЙ наклон (модель времени неважна)
                {
                    string skewKey = IvOnF.GetSkewCashKey(futSymbol, optExpiry, SmileSkewMode.RawSkew, TimeRemainMode.PlainCalendar);
                    var    ivSkews = LoadOrCreateHistoryDict(context, useGlobal, skewKey);
                    // Это просто запись на диск. К успешности вычисления наклона successSkew отношения не имеет
                    successSkew = TryWrite(context, useGlobal, allowGlobalWrite,
                                           savePeriod, skewKey, ivSkews, lastBarDate, skewAtm);
                }

                // В. Записываем в Глобальный Кеш БЕЗРАЗМЕРНЫЙ наклон В БИРЖЕВОМ ВРЕМЕНИ (PlainCalendar)
                {
                    double dSigmaDxExchange = SmileImitation5.GetDSigmaDx(futPx, plainTimeAsYears, skewAtm, 0);
                    if (!DoubleUtil.IsNaN(dSigmaDxExchange))
                    {
                        string skewKey = IvOnF.GetSkewCashKey(futSymbol, optExpiry, SmileSkewMode.ExchangeSkew, TimeRemainMode.PlainCalendar);
                        var    ivSkews = LoadOrCreateHistoryDict(context, useGlobal, skewKey);
                        // Это просто запись на диск. К успешности вычисления наклона successSkew отношения не имеет
                        successSkew = TryWrite(context, useGlobal, allowGlobalWrite,
                                               savePeriod, skewKey, ivSkews, lastBarDate, dSigmaDxExchange);
                    }
                }

                // Д. Записываем в Глобальный Кеш БЕЗРАЗМЕРНЫЙ наклон В НАШЕМ ВРЕМЕНИ (в соответствии с tRemainMode)
                {
                    double dSigmaDxRescaled = SmileImitation5.GetDSigmaDx(futPx, timeAsYears, skewAtm, 0);
                    if (!DoubleUtil.IsNaN(dSigmaDxRescaled))
                    {
                        string skewKey = IvOnF.GetSkewCashKey(futSymbol, optExpiry, SmileSkewMode.RescaledSkew, tRemainMode);
                        var    ivSkews = LoadOrCreateHistoryDict(context, useGlobal, skewKey);
                        // Это просто запись на диск. К успешности вычисления наклона successSkew отношения не имеет
                        successSkew = TryWrite(context, useGlobal, allowGlobalWrite,
                                               savePeriod, skewKey, ivSkews, lastBarDate, dSigmaDxRescaled);
                    }
                }
            } // End if (splineD1.TryGetValue(futPx, out var skewAtm))

            return(successSkew);
        }
コード例 #2
0
ファイル: IvOnF.cs プロジェクト: barbagrigia/Handlers
        public static NotAKnotCubicSpline PrepareExchangeSmileSpline(IOptionSeries optSer, double minStrike, double maxStrike)
        {
            List <double> xs = new List <double>();
            List <double> ys = new List <double>();

            IOptionStrikePair[] pairs = (from pair in optSer.GetStrikePairs()
                                         //orderby pair.Strike ascending -- уже отсортировано!
                                         select pair).ToArray();
            for (int j = 0; j < pairs.Length; j++)
            {
                IOptionStrikePair sInfo = pairs[j];
                // Сверхдалекие страйки игнорируем
                if ((sInfo.Strike < minStrike) || (maxStrike < sInfo.Strike))
                {
                    continue;
                }

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

                // TODO: вернуть ассерт потом
                //System.Diagnostics.Debug.Assert(
                //    DoubleUtil.AreClose(sInfo.PutFinInfo.Volatility.Value, sInfo.CallFinInfo.Volatility.Value),
                //    "Exchange volas on the same strike MUST be equal! PutVola:" + sInfo.PutFinInfo.Volatility.Value +
                //    "; CallVola:" + sInfo.CallFinInfo.Volatility.Value);

                xs.Add(sInfo.Strike);
                ys.Add(sInfo.PutFinInfo.Volatility.Value);
            }

            NotAKnotCubicSpline spline = null;

            if (xs.Count >= BaseCubicSpline.MinNumberOfNodes)
            {
                spline = new NotAKnotCubicSpline(xs, ys);
            }
            return(spline);
        }
コード例 #3
0
        public InteractiveSeries Execute(InteractiveSeries deltaProfile, int barNum)
        {
            //InteractiveSeries res = context.LoadObject(cashKey + "positionDelta") as InteractiveSeries;
            //if (res == null)
            //{
            //    res = new InteractiveSeries();
            //    context.StoreObject(cashKey + "positionDelta", res);
            //}

            InteractiveSeries res = new InteractiveSeries();
            int barsCount         = ContextBarsCount;

            if (barNum < barsCount - 1)
            {
                return(res);
            }

            if (deltaProfile == null)
            {
                return(res);
            }

            SmileInfo sInfo = deltaProfile.GetTag <SmileInfo>();

            if ((sInfo == null) ||
                (sInfo.ContinuousFunction == null) || (sInfo.ContinuousFunctionD1 == null))
            {
                return(res);
            }

            List <double>            xs            = new List <double>();
            List <double>            ys            = new List <double>();
            var                      deltaPoints   = deltaProfile.ControlPoints;
            List <InteractiveObject> controlPoints = new List <InteractiveObject>();

            foreach (InteractiveObject iob in deltaPoints)
            {
                double rawGamma, f = iob.Anchor.ValueX;
                if (sInfo.ContinuousFunctionD1.TryGetValue(f, out rawGamma))
                {
                    InteractivePointActive ip = new InteractivePointActive();
                    ip.IsActive = m_showNodes;
                    //ip.DragableMode = DragableMode.None;
                    //ip.Geometry = Geometries.Rect;
                    //ip.Color = System.Windows.Media.Colors.Orange;
                    double y = rawGamma;
                    ip.Value = new Point(f, y);
                    string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture);
                    ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "F:{0}; G:{1}", f, yStr);

                    controlPoints.Add(new InteractiveObject(ip));

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

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

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

                    NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys);

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

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

            return(res);
        }
コード例 #4
0
ファイル: IvOnF.cs プロジェクト: barbagrigia/Handlers
        /// <summary>
        /// Обработчик под тип входных данных OPTION_SERIES
        /// </summary>
        public IList <double> Execute(IOptionSeries optSer)
        {
            if (optSer == null)
            {
                string msg = "[IV ATM] (optSer == null)";
                m_context.Log(msg, MessageType.Warning, false);

                return(Constants.EmptyListDouble);
            }

            Dictionary <DateTime, double> ivSigmas;

            #region Get cache
            DateTime expiry  = optSer.ExpirationDate.Date;
            string   cashKey = IvOnF.GetCashKey(optSer.UnderlyingAsset.Symbol, expiry, m_rescaleTime, m_tRemainMode);
            ivSigmas = LoadOrCreateHistoryDict(UseGlobalCache, cashKey);
            #endregion Get cache

            List <double> res;
            ISecurity     sec = optSer.UnderlyingAsset;
            int           len = sec.Bars.Count;
            if (len <= 0)
            {
                return(Constants.EmptyListDouble);
            }

            if (m_context.IsFixedBarsCount)
            {
                #region Ветка с ФИКСИРОВАННЫМ количеством баров
                double lastIv = Double.NaN;
                res = new List <double>(len);
                for (int j = 0; j < len; j++)
                {
                    DateTime now = sec.Bars[j].Date;
                    double   iv;
                    if ((ivSigmas.TryGetValue(now, out iv)) && (!Double.IsNaN(iv)) && (iv > 0))
                    {
                        lastIv = iv;
                        res.Add(iv);
                    }
                    else
                    {
                        if (m_repeatLastIv && (!Double.IsNaN(lastIv)))
                        {
                            res.Add(lastIv);
                        }
                        else
                        {
                            res.Add(Constants.NaN);
                        }
                    }
                }
                #endregion Ветка с ФИКСИРОВАННЫМ количеством баров
            }
            else
            {
                #region Ветка с нарастающим количеством баров
                res = LocalHistory;
                // PROD-1933
                // 1. Выполняю очистку локального кеша в сценарии восстановления соединения после дисконнекта
                if (res.Count > len)
                {
                    res.Clear();
                }

                // 2. Ищу последнее валидное значение в кеше причем только если это может быть нужно
                double lastIv = Double.NaN;
                if (m_repeatLastIv)
                {
                    for (int j = res.Count - 1; j >= 0; j--)
                    {
                        if ((!Double.IsNaN(res[j])) && (res[j] > 0))
                        {
                            lastIv = res[j];
                            break;
                        }
                    }
                }

                for (int j = res.Count; j < len; j++)
                {
                    DateTime now = sec.Bars[j].Date;
                    double   iv;
                    if ((ivSigmas.TryGetValue(now, out iv)) && (!Double.IsNaN(iv)) && (iv > 0))
                    {
                        lastIv = iv;
                        res.Add(iv);
                    }
                    else
                    {
                        if (m_repeatLastIv && (!Double.IsNaN(lastIv)))
                        {
                            res.Add(lastIv);
                        }
                        else
                        {
                            res.Add(Constants.NaN);
                        }
                    }
                }
                #endregion Ветка с нарастающим количеством баров
            }

            Debug.Assert(res != null, "How is it possible (res == null)?");
            Debug.Assert(res.Count == len, String.Format("Wrong res.Count. res.Count:{0}; expected len:{1}; IsFixedBarsCount:{2}",
                                                         res.Count, len, m_context.IsFixedBarsCount));

            FinInfo baseFinInfo = optSer.UnderlyingAsset.FinInfo;
            // Эта проверка намекает на проблемы с маркет-датой.
            if (baseFinInfo.LastPrice == null)
            {
                string msg = "[IV ATM] (baseFinInfo.LastPrice == null)";
                m_context.Log(msg, MessageType.Warning, false);
                return(res);
            }

            try
            {
                double sigma;
                double futPx = baseFinInfo.LastPrice.Value;
                NotAKnotCubicSpline spline = PrepareExchangeSmileSpline(optSer, Double.MinValue, Double.MaxValue);
                if ((spline != null) && spline.TryGetValue(futPx, out sigma) && DoubleUtil.IsPositive(sigma))
                {
                    DateTime lastBarDate = sec.Bars[len - 1].Date;
                    if (m_rescaleTime)
                    {
                        #region Зверская ветка по замене времени
                        double   ivAtm   = sigma;
                        DateTime expDate = optSer.ExpirationDate.Date + m_expiryTime;
                        DateTime now     = baseFinInfo.LastUpdate;

                        // 1. Надо перевести волатильность в абсолютную цену
                        // с учетом плоского календарного времени применяемого РТС
                        double plainTimeAsYears;
                        {
                            double plainTimeAsDays;
                            TimeToExpiry.GetDt(expDate, now, TimeRemainMode.PlainCalendar, false, out plainTimeAsDays,
                                               out plainTimeAsYears);
                        }

                        // 2. Вычисляем 'нормальное' время
                        double timeAsDays, timeAsYears;
                        TimeToExpiry.GetDt(expDate, now, m_tRemainMode, false, out timeAsDays, out timeAsYears);
                        sigma = FinMath.RescaleIvToAnotherTime(plainTimeAsYears, ivAtm, timeAsYears);
                        if (DoubleUtil.IsPositive(sigma))
                        {
                            // Это просто запись на диск. К успешности вычисления волы success отношения не имеет
                            bool success = TryWrite(m_context, UseGlobalCache, AllowGlobalReadWrite,
                                                    GlobalSavePeriod, cashKey, ivSigmas, lastBarDate, sigma);

                            // Теперь надо вычислить безразмерный наклон кодом в классе SmileImitation5
                            bool successSkew = TryCalcAndWriteSkews(m_context, spline, UseGlobalCache, AllowGlobalReadWrite, GlobalSavePeriod,
                                                                    optSer.UnderlyingAsset.Symbol, expiry, futPx, lastBarDate, m_tRemainMode, plainTimeAsYears, timeAsYears);
                        }
                        else
                        {
                            // Если перемасштабировать улыбку не получается придется эту точку проигнорировать
                            // Надо ли сделать соответствующую запись в логе???
                            sigma = Constants.NaN;
                        }
                        #endregion Зверская ветка по замене времени
                    }
                    else
                    {
                        // Это просто запись на диск. К успешности вычисления волы success отношения не имеет
                        bool success = TryWrite(m_context, UseGlobalCache, AllowGlobalReadWrite,
                                                GlobalSavePeriod, cashKey, ivSigmas, lastBarDate, sigma);
                    }
                }
                else
                {
                    sigma = Constants.NaN;
                }

                res[len - 1] = sigma;
            }
            catch (Exception ex)
            {
                m_context.Log(ex.ToString(), MessageType.Error, false);
                return(res);
            }

            if (m_repeatLastIv)
            {
                if (DoubleUtil.AreClose(res[len - 1], Constants.NaN) || Double.IsNaN(res[len - 1]) || (res[len - 1] <= 0))
                {
                    // Итерируюсь с конца в начало пока не найду последний ненулевой элемент.
                    // Использую его в качестве ВСЕХ последних значений ряда.
                    for (int j = len - 1; j >= 0; j--)
                    {
                        if ((!DoubleUtil.AreClose(res[j], Constants.NaN)) && (!Double.IsNaN(res[j])) && (res[j] > 0))
                        {
                            double lastIv = res[j];
                            for (int k = j + 1; k < len; k++)
                            {
                                res[k] = lastIv;
                            }
                            break;
                        }
                    }
                }
            }

            return(new ReadOnlyCollection <double>(res));
        }
コード例 #5
0
        /// <summary>
        /// Обработчик под тип входных данных OPTION_SERIES
        /// </summary>
        public double Execute(IOptionSeries optSer, int barNum)
        {
            double failRes = Constants.NaN;

            if (m_repeatLastIv)
            {
                failRes = Double.IsNaN(m_prevIv) ? Constants.NaN : m_prevIv;
            }

            Dictionary <DateTime, double> ivSigmas;

            #region Get cache
            DateTime expiry  = optSer.ExpirationDate.Date;
            string   cashKey = IvOnF.GetCashKey(optSer.UnderlyingAsset.Symbol, expiry, m_rescaleTime, m_tRemainMode);
            ivSigmas = LoadOrCreateHistoryDict(UseGlobalCache, cashKey);
            #endregion Get cache

            ISecurity sec = optSer.UnderlyingAsset;
            int       len = sec.Bars.Count;
            if (len <= 0)
            {
                return(failRes);
            }

            DateTime lastBarDate = sec.Bars[barNum].Date;
            double   iv;
            if ((ivSigmas.TryGetValue(lastBarDate, out iv)) && (!Double.IsNaN(iv)) && (iv > 0))
            {
                m_prevIv = iv;
                return(iv);
            }
            else
            {
                int barsCount = ContextBarsCount;
                if (barNum < barsCount - 1)
                {
                    // Если история содержит осмысленное значение, то оно уже содержится в failRes
                    return(failRes);
                }
                else
                {
                    #region Process last bar(s)
                    FinInfo baseFinInfo = optSer.UnderlyingAsset.FinInfo;
                    if (baseFinInfo.LastPrice == null)
                    {
                        string msg = "[IV ATM 2] (baseFinInfo.LastPrice == null)";
                        m_context.Log(msg, MessageType.Warning, false);
                        return(failRes);
                    }

                    double futPx = baseFinInfo.LastPrice.Value;
                    if (futPx <= Double.Epsilon)
                    {
                        return(failRes);
                    }

                    NotAKnotCubicSpline spline = null;
                    try
                    {
                        spline = IvOnF.PrepareExchangeSmileSpline(optSer, Double.MinValue, Double.MaxValue);
                    }
                    catch (ScriptException scriptEx)
                    {
                        m_context.Log(scriptEx.ToString(), MessageType.Error, false);
                        return(failRes);
                    }
                    catch (Exception ex)
                    {
                        m_context.Log(ex.ToString(), MessageType.Error, false);
                        return(failRes);
                    }

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

                    try
                    {
                        double sigma;
                        if (spline.TryGetValue(futPx, out sigma) && (sigma > 0))
                        {
                            if (m_rescaleTime)
                            {
                                #region Зверская ветка по замене времени
                                double   ivAtm   = sigma;
                                DateTime expDate = optSer.ExpirationDate.Date + m_expiryTime;
                                DateTime now     = baseFinInfo.LastUpdate;

                                // 1. Надо перевести волатильность в абсолютную цену
                                // с учетом плоского календарного времени применяемого РТС
                                double plainTimeAsYears;
                                {
                                    double plainTimeAsDays;
                                    TimeToExpiry.GetDt(expDate, now, TimeRemainMode.PlainCalendar, false,
                                                       out plainTimeAsDays, out plainTimeAsYears);
                                }

                                // 2. Вычисляем 'нормальное' время
                                double timeAsDays, timeAsYears;
                                TimeToExpiry.GetDt(expDate, now, m_tRemainMode, false, out timeAsDays, out timeAsYears);
                                sigma = FinMath.RescaleIvToAnotherTime(plainTimeAsYears, ivAtm, timeAsYears);
                                if (DoubleUtil.IsPositive(sigma))
                                {
                                    // Это просто запись на диск. К успешности вычисления волы success отношения не имеет
                                    lock (ivSigmas)
                                    {
                                        bool success = IvOnF.TryWrite(m_context, UseGlobalCache, AllowGlobalReadWrite,
                                                                      GlobalSavePeriod, cashKey, ivSigmas, lastBarDate, sigma);
                                        m_prevIv = sigma;

                                        // Теперь надо вычислить безразмерный наклон кодом в классе SmileImitation5
                                        bool successSkew = IvOnF.TryCalcAndWriteSkews(m_context, spline, UseGlobalCache, AllowGlobalReadWrite,
                                                                                      GlobalSavePeriod, optSer.UnderlyingAsset.Symbol, expiry, futPx, lastBarDate,
                                                                                      m_tRemainMode, plainTimeAsYears, timeAsYears);

                                        return(sigma);
                                    }
                                }
                                else
                                {
                                    // Если перемасштабировать улыбку не получается придется эту точку проигнорировать
                                    // Надо ли сделать соответствующую запись в логе???
                                    sigma = Constants.NaN;
                                }
                                #endregion Зверская ветка по замене времени
                            }
                            else
                            {
                                lock (ivSigmas)
                                {
                                    bool success = IvOnF.TryWrite(m_context, UseGlobalCache, AllowGlobalReadWrite,
                                                                  GlobalSavePeriod, cashKey, ivSigmas, lastBarDate, sigma);
                                    m_prevIv = sigma;
                                    return(sigma);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        m_context.Log(ex.ToString(), MessageType.Error, false);
                    }

                    return(failRes);

                    #endregion Process last bar
                }
            }
        }
コード例 #6
0
        public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, int barNum)
        {
            //InteractiveSeries res = context.LoadObject(cashKey + "positionDelta") as InteractiveSeries;
            //if (res == null)
            //{
            //    res = new InteractiveSeries();
            //    context.StoreObject(cashKey + "positionDelta", res);
            //}

            InteractiveSeries res = new InteractiveSeries();
            int barsCount         = ContextBarsCount;

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

            //double F = prices[prices.Count - 1];
            double dT = time;

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

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

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

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

            //// TODO: переписаться на обновление старых значений
            //res.ControlPoints.Clear();

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

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

            foreach (InteractiveObject iob in smilePoints)
            {
                double rawVega, f = iob.Anchor.ValueX;
                if (TryEstimateVega(posMan, optSer, pairs, smile, m_greekAlgo, f, m_sigmaStep, dT, out rawVega))
                {
                    // Переводим вегу в дифференциал 'изменение цены за 1% волы'.
                    rawVega /= Constants.PctMult;

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

                    controlPoints.Add(new InteractiveObject(ip));

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

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

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

                    NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys);

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

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

            return(res);
        }
コード例 #7
0
        public InteractiveSeries Execute(InteractiveSeries smile, int barNum)
        {
            if (m_transformation == SmileTransformation.None)
            {
                if (DoubleUtil.IsZero(m_shiftIv))
                {
                    return(smile);
                }
            }

            int barsCount = m_context.BarsCount;

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

            // Пустая улыбка останется пустой. Что с ней делать непонятно.
            if (smile.ControlPoints.Count <= 1)
            {
                return(smile);
            }

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

            if (sInfo == null)
            {
                throw new ScriptException("ArgumentException: smile.Tag must be filled with SmileInfo object.");
            }

            var cps = smile.ControlPoints;
            int len = cps.Count;

            IFunction         symmetrizedFunc   = null;
            IFunction         symmetrizedFuncD1 = null;
            InteractiveSeries res = new InteractiveSeries();
            List <double>     xs  = new List <double>();
            List <double>     ys  = new List <double>();

            if (m_transformation == SmileTransformation.Simmetrise)
            {
                #region Simmetrise
                double f    = sInfo.F;
                double minX = cps[0].Anchor.ValueX;
                double maxX = cps[len - 1].Anchor.ValueX;

                double width = Math.Min(maxX - f, f - minX);
                if (width < 0)
                {
                    throw new ScriptException("ArgumentException: current price is outside of the smile.");
                }

                // TODO: сократить вычисления вдвое, учитывая явное требование симметричности результирующей улыбки
                double step = 2.0 * width / (len - 1);
                List <InteractiveObject> controlPoints = new List <InteractiveObject>();
                for (int j = 0; j < len; j++)
                {
                    double kLeft  = (f - width) + j * step;
                    double ivLeft = sInfo.ContinuousFunction.Value(kLeft);

                    double kRight  = (f + width) - j * step;
                    double ivRight = sInfo.ContinuousFunction.Value(kRight);

                    double iv = 0.5 * (ivLeft + ivRight) + m_shiftIv;

                    InteractiveObject oldObj = cps[j];

                    if (oldObj.Anchor is InteractivePointActive)
                    {
                        InteractivePointActive ip = (InteractivePointActive)oldObj.Anchor.Clone();

                        ip.Color        = (m_optionPxMode == OptionPxMode.Ask) ? AlphaColors.DarkOrange : AlphaColors.DarkCyan;
                        ip.DragableMode = DragableMode.None;
                        ip.Geometry     = Geometries.Rect;
                        // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect;
                        ip.IsActive = (m_optionPxMode != OptionPxMode.Mid);

                        ip.Value   = new Point(kLeft, iv);
                        ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", kLeft, iv * Constants.PctMult);

                        InteractiveObject newObj = new InteractiveObject(ip);
                        controlPoints.Add(newObj);
                    }
                    else if (oldObj.Anchor is InteractivePointLight)
                    {
                        InteractivePointLight ip = (InteractivePointLight)oldObj.Anchor.Clone();
                        ip.Value = new Point(kLeft, iv);
                        InteractiveObject newObj = new InteractiveObject(ip);
                        controlPoints.Add(newObj);
                    }
                    else
                    {
                        string msg = String.Format("[{0}] Point of type '{1}' is not supported.",
                                                   GetType().Name, oldObj.Anchor.GetType().Name);
                        throw new ScriptException(msg);
                    }

                    xs.Add(kLeft);
                    ys.Add(iv);
                }

                res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints);
                #endregion Simmetrise
            }
            else if (m_transformation == SmileTransformation.LogSimmetrise)
            {
                #region Log Simmetrise
                double f = sInfo.F;

                LogSimmetrizeFunc lsf = new LogSimmetrizeFunc(sInfo.ContinuousFunction, f, m_simmWeight);
                symmetrizedFunc   = lsf;
                symmetrizedFuncD1 = new LogSimmetrizeFunc(sInfo.ContinuousFunctionD1, f, m_simmWeight);
                List <InteractiveObject> controlPoints = new List <InteractiveObject>();
                foreach (var oldObj in smile.ControlPoints)
                {
                    double k = oldObj.Anchor.ValueX;
                    double iv;
                    if (lsf.TryGetValue(k, out iv))
                    {
                        iv += m_shiftIv;

                        if (oldObj.Anchor is InteractivePointActive)
                        {
                            InteractivePointActive ip = (InteractivePointActive)oldObj.Anchor.Clone();

                            ip.Color        = (m_optionPxMode == OptionPxMode.Ask) ? AlphaColors.DarkOrange : AlphaColors.DarkCyan;
                            ip.DragableMode = DragableMode.None;
                            ip.Geometry     = Geometries.Rect;
                            // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect;
                            ip.IsActive = (m_optionPxMode != OptionPxMode.Mid);

                            ip.Value   = new Point(k, iv);
                            ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", k, iv * Constants.PctMult);

                            InteractiveObject newObj = new InteractiveObject(ip);
                            controlPoints.Add(newObj);
                        }
                        else if (oldObj.Anchor is InteractivePointLight)
                        {
                            InteractivePointLight ip = (InteractivePointLight)oldObj.Anchor.Clone();
                            ip.Value = new Point(k, iv);
                            InteractiveObject newObj = new InteractiveObject(ip);
                            controlPoints.Add(newObj);
                        }
                        else
                        {
                            string msg = String.Format("[{0}] Point of type '{1}' is not supported.",
                                                       GetType().Name, oldObj.Anchor.GetType().Name);
                            throw new ScriptException(msg);
                        }

                        xs.Add(k);
                        ys.Add(iv);
                    }
                }

                res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints);
                #endregion Log Simmetrise
            }
            else if (m_transformation == SmileTransformation.None)
            {
                #region None (only vertical shift)
                double f    = sInfo.F;
                double dT   = sInfo.dT;
                double rate = sInfo.RiskFreeRate;
                List <InteractiveObject> controlPoints = new List <InteractiveObject>();
                for (int j = 0; j < len; j++)
                {
                    InteractiveObject oldObj   = cps[j];
                    SmileNodeInfo     nodeInfo = oldObj.Anchor.Tag as SmileNodeInfo;
                    // Обязательно нужна полная информация об узле улыбки, чтобы потом можно было торговать
                    if (nodeInfo == null)
                    {
                        continue;
                    }

                    double k  = oldObj.Anchor.Value.X;
                    double iv = oldObj.Anchor.Value.Y + m_shiftIv;

                    if (oldObj.Anchor is InteractivePointActive)
                    {
                        InteractivePointActive ip = (InteractivePointActive)oldObj.Anchor.Clone();

                        ip.Color        = (m_optionPxMode == OptionPxMode.Ask) ? AlphaColors.DarkOrange : AlphaColors.DarkCyan;
                        ip.DragableMode = DragableMode.None;
                        ip.Geometry     = Geometries.Rect;
                        // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect;
                        ip.IsActive = (m_optionPxMode != OptionPxMode.Mid);

                        //ip.Value = new Point(oldObj.Anchor.Value.X, iv);
                        //ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", k, iv * PctMult);

                        InteractiveObject newObj = new InteractiveObject(ip);

                        if (k <= f) // Puts
                        {
                            FillNodeInfo(ip, f, dT, nodeInfo.Pair, StrikeType.Put, m_optionPxMode, iv, false, rate);
                        }
                        else // Calls
                        {
                            FillNodeInfo(ip, f, dT, nodeInfo.Pair, StrikeType.Call, m_optionPxMode, iv, false, rate);
                        }

                        controlPoints.Add(newObj);
                    }
                    else if (oldObj.Anchor is InteractivePointLight)
                    {
                        InteractivePointLight ip = (InteractivePointLight)oldObj.Anchor.Clone();
                        ip.Value = new Point(k, iv);
                        InteractiveObject newObj = new InteractiveObject(ip);

                        if (k <= f) // Puts
                        {
                            FillNodeInfo(ip, f, dT, nodeInfo.Pair, StrikeType.Put, m_optionPxMode, iv, false, rate);
                        }
                        else // Calls
                        {
                            FillNodeInfo(ip, f, dT, nodeInfo.Pair, StrikeType.Call, m_optionPxMode, iv, false, rate);
                        }

                        controlPoints.Add(newObj);
                    }
                    else
                    {
                        string msg = String.Format("[{0}] Point of type '{1}' is not supported.",
                                                   GetType().Name, oldObj.Anchor.GetType().Name);
                        throw new ScriptException(msg);
                    }

                    xs.Add(k);
                    ys.Add(iv);
                }

                res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints);
                #endregion None (only vertical shift)
            }
            else
            {
                throw new NotImplementedException("Transformation: " + m_transformation);
            }

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

            try
            {
                if (symmetrizedFunc == null)
                {
                    NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys);

                    info.ContinuousFunction   = spline;
                    info.ContinuousFunctionD1 = spline.DeriveD1();
                }
                else
                {
                    // По факту эта ветка обслуживает только алгоритм LogSimm
                    info.ContinuousFunction   = symmetrizedFunc;
                    info.ContinuousFunctionD1 = symmetrizedFuncD1;
                }
                res.Tag = info;
            }
            catch (Exception ex)
            {
                m_context.Log(ex.ToString(), MessageType.Error, true);
                return(Constants.EmptySeries);
            }

            return(res);
        }
コード例 #8
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);
        }
コード例 #9
0
        public InteractiveSeries Execute(InteractiveSeries positionProfile, int barNum)
        {
            int barsCount = ContextBarsCount;

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

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

            SmileInfo sInfo = positionProfile.GetTag <SmileInfo>();

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

            List <double>            xs            = new List <double>();
            List <double>            ys            = new List <double>();
            var                      profilePoints = positionProfile.ControlPoints;
            List <InteractiveObject> controlPoints = new List <InteractiveObject>();

            foreach (InteractiveObject iob in profilePoints)
            {
                double rawDelta, f = iob.Anchor.ValueX;
                if (sInfo.ContinuousFunctionD1.TryGetValue(f, out rawDelta))
                {
                    // ReSharper disable once UseObjectOrCollectionInitializer
                    InteractivePointActive ip = new InteractivePointActive();
                    ip.IsActive = m_showNodes;
                    //ip.DragableMode = DragableMode.None;
                    //ip.Geometry = Geometries.Rect;
                    //ip.Color = System.Windows.Media.Colors.Orange;
                    double y = rawDelta;
                    ip.Value = new Point(f, y);
                    string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture);
                    ip.Tooltip = String.Format("F:{0}; D:{1}", f, yStr);

                    controlPoints.Add(new InteractiveObject(ip));

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

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

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

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

                    NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys);

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

                    res.Tag = info;
                }
            }
            catch (DivideByZeroException dvbEx)
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendFormat("Divide-by-zero exception. Probably it is from NotAKnotCubicSpline:");
                sb.AppendLine();
                sb.AppendLine();
                sb.AppendLine(dvbEx.ToString());
                sb.AppendLine();

                sb.AppendLine("Content of arguments...");
                sb.AppendLine("xs;ys");
                for (int tmpIndex = 0; tmpIndex < xs.Count; tmpIndex++)
                {
                    sb.Append(xs[tmpIndex].ToString(CultureInfo.InvariantCulture) + ";");
                    sb.Append(ys[tmpIndex].ToString(CultureInfo.InvariantCulture));

                    sb.AppendLine();
                }
                sb.AppendLine();

                m_context.Log(sb.ToString(), MessageType.Error, false);
                m_context.Log(dvbEx.ToString(), MessageType.Error, true);
                return(Constants.EmptySeries);
            }
            catch (Exception ex)
            {
                m_context.Log(ex.ToString(), MessageType.Error, true);
                return(Constants.EmptySeries);
            }

            return(res);
        }
コード例 #10
0
        public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, int barNum)
        {
            int barsCount = ContextBarsCount;

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

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

            double dT = time;

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

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

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

            double futPx = oldInfo.F;

            double ivAtm;

            if (!oldInfo.ContinuousFunction.TryGetValue(futPx, out ivAtm))
            {
                string msg = String.Format("[{0}] Unable to get IV ATM from smile. Tag:{1}", GetType().Name, smile.Tag);
                if (wasInitialized)
                {
                    m_context.Log(msg, MessageType.Error, true);
                }
                return(Constants.EmptySeries);
            }

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

            List <double>    xs      = new List <double>();
            List <double>    ys      = new List <double>();
            double           futStep = optSer.UnderlyingAsset.Tick;
            PositionsManager posMan  = PositionsManager.GetManager(m_context);

            SortedDictionary <double, IOptionStrikePair> futPrices;

            if (!SmileImitation5.TryPrepareImportantPoints(pairs, futPx, futStep, -1, out futPrices))
            {
                string msg = String.Format("[{0}] It looks like there is no suitable points for the smile. pairs.Length:{1}", GetType().Name, pairs.Length);
                if (wasInitialized)
                {
                    m_context.Log(msg, MessageType.Warning, true);
                }
                return(Constants.EmptySeries);
            }

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

            foreach (var kvp in futPrices)
            {
                double f = kvp.Key;
                bool   tradableStrike = (kvp.Value != null);

                double rawGreek;
                GetBaseGreek(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, m_greek, out rawGreek);

                for (int j = 0; j < pairs.Length; j++)
                {
                    IOptionStrikePair pair = pairs[j];
                    double            totalGreek;
                    //double putDelta = FinMath.GetOptionDelta(f, pair.Strike, dT, sigma.Value, 0, false);
                    GetPairGreek(posMan, smile, pair, f, dT, m_greek, out totalGreek);
                    rawGreek += totalGreek;
                }

                InteractivePointActive ip = new InteractivePointActive();
                ip.IsActive = m_showNodes;
                //ip.DragableMode = DragableMode.None;
                //ip.Geometry = Geometries.Rect;
                //ip.Color = AlphaColors.Green;
                double y = rawGreek;
                ip.Value = new Point(f, y);
                string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture);
                ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "F:{0}; {1}:{2}", f, m_greek, yStr);

                controlPoints.Add(new InteractiveObject(ip));

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

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

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

            SmileInfo info = new SmileInfo();

            info.F            = oldInfo.F;
            info.dT           = oldInfo.dT;
            info.Expiry       = optSer.ExpirationDate;
            info.ScriptTime   = now;
            info.RiskFreeRate = oldInfo.RiskFreeRate;
            info.BaseTicker   = optSer.UnderlyingAsset.Symbol;

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

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

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

            return(res);
        }
コード例 #11
0
        /// <summary>
        /// Обработчик под тип входных данных OPTION_SERIES
        /// </summary>
        public InteractiveSeries Execute(IOptionSeries optSer, IList <double> rates)
        {
            InteractiveSeries res = m_context.LoadObject(VariableId + "theorSmile") as InteractiveSeries;

            if (res == null)
            {
                res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку
                m_context.StoreObject(VariableId + "theorSmile", res);
            }

            if (optSer == null)
            {
                return(res);
            }

            int len = optSer.UnderlyingAsset.Bars.Count;

            if (len <= 0)
            {
                return(res);
            }

            FinInfo bSecFinInfo = optSer.UnderlyingAsset.FinInfo;

            if (!bSecFinInfo.LastPrice.HasValue)
            {
                return(res);
            }

            if (rates.Count <= 0)
            {
                //throw new ScriptException("There should be some values in second argument 'rates'.");
                return(res);
            }

            //IDataBar bar = optSer.UnderlyingAsset.Bars[len - 1];
            double futPx = bSecFinInfo.LastPrice.Value;
            // ФОРТС использует плоское календарное время
            DateTime optExpiry = optSer.ExpirationDate.Date.Add(m_expiryTime);
            double   dT        = (optExpiry - bSecFinInfo.LastUpdate).TotalYears();
            double   ratePct   = rates[rates.Count - 1];

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

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

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

            // TODO: переписаться на обновление старых значений
            //res.ControlPoints.Clear();
            List <InteractiveObject> controlPoints = new List <InteractiveObject>();

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

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

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

                double prec;
                // Биржа шлет несогласованную улыбку
                //double virtualExchangeF = sInfo.CallFinInfo.TheoreticalPrice.Value - sInfo.PutFinInfo.TheoreticalPrice.Value + sInfo.Strike;
                if ((m_optionType == StrikeType.Any) || (m_optionType == StrikeType.Put))
                {
                    double optSigma = sInfo.PutFinInfo.Volatility.Value;
                    if ((!DoubleUtil.IsOne(m_multPx)) || (!DoubleUtil.IsZero(m_shiftPx)))
                    {
                        double optPx = sInfo.PutFinInfo.TheoreticalPrice.Value * m_multPx + m_shiftPx * sInfo.Tick;
                        if ((optPx <= 0) || (Double.IsNaN(optPx)))
                        {
                            optSigma = 0;
                        }
                        else
                        {
                            optSigma = FinMath.GetOptionSigma(futPx, k, dT, optPx, ratePct, false, out prec);
                        }
                    }

                    double vol = optSigma;

                    // ReSharper disable once UseObjectOrCollectionInitializer
                    InteractivePointActive ip = new InteractivePointActive(k, vol);
                    ip.Geometry = Geometries.Ellipse;
                    ip.Color    = AlphaColors.Cyan;
                    ip.Tooltip  = String.Format("K:{0}; IV:{1:0.00}", k, Constants.PctMult * optSigma);

                    if (showPoint && (vol > 0))
                    {
                        controlPoints.Add(new InteractiveObject(ip));
                    }

                    if ((xs.Count <= 0) ||
                        (!DoubleUtil.AreClose(k, xs[xs.Count - 1])))
                    {
                        xs.Add(k);
                        ys.Add(vol);
                    }
                }

                if ((m_optionType == StrikeType.Any) || (m_optionType == StrikeType.Call))
                {
                    double optSigma = sInfo.CallFinInfo.Volatility.Value;
                    if ((!DoubleUtil.IsOne(m_multPx)) || (!DoubleUtil.IsZero(m_shiftPx)))
                    {
                        double optPx = sInfo.CallFinInfo.TheoreticalPrice.Value * m_multPx + m_shiftPx * sInfo.Tick;
                        if ((optPx <= 0) || (Double.IsNaN(optPx)))
                        {
                            optSigma = 0;
                        }
                        else
                        {
                            optSigma = FinMath.GetOptionSigma(futPx, k, dT, optPx, ratePct, true, out prec);
                        }
                    }

                    double vol = optSigma;

                    // ReSharper disable once UseObjectOrCollectionInitializer
                    InteractivePointActive ip = new InteractivePointActive(k, vol);
                    ip.Geometry = Geometries.Ellipse;
                    ip.Color    = AlphaColors.DarkCyan;
                    ip.Tooltip  = String.Format("K:{0}; IV:{1:0.00}", k, Constants.PctMult * optSigma);

                    if (showPoint && (vol > 0))
                    {
                        controlPoints.Add(new InteractiveObject(ip));
                    }

                    if ((xs.Count <= 0) ||
                        (!DoubleUtil.AreClose(k, xs[xs.Count - 1])))
                    {
                        xs.Add(k);
                        ys.Add(vol);
                    }
                }
            }

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

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

            // ReSharper disable once UseObjectOrCollectionInitializer
            SmileInfo info = new SmileInfo();

            info.F            = futPx;
            info.dT           = dT;
            info.Expiry       = optSer.ExpirationDate;
            info.ScriptTime   = scriptTime;
            info.RiskFreeRate = ratePct;
            info.BaseTicker   = baseSec.Symbol;

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

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

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

            return(res);
        }
コード例 #12
0
ファイル: IvSmile.cs プロジェクト: barbagrigia/Handlers
        /// <summary>
        /// Создание сплайна и заполнение SmileInfo по предложенной таблице
        /// </summary>
        /// <param name="externalContext">контекст блока для логгирования проблемы</param>
        /// <param name="f">цена БА (нажна как верхняя оценка цены колла</param>
        /// <param name="dT">время до экспирации</param>
        /// <param name="riskFreeRate">беспроцентная ставка</param>
        /// <param name="expiry">дата экспирации (без времени)</param>
        /// <param name="scriptTime">время в скрипте</param>
        /// <param name="baseTicker">тикер БА</param>
        /// <param name="res">Таблица с данными для интерполирования</param>
        /// <param name="info">Заполненный SmileInfo</param>
        public static bool TryPrepareSmileInfo(IContext externalContext,
                                               double f, double dT, double riskFreeRate, DateTime expiry, DateTime scriptTime,
                                               string baseTicker, InteractiveSeries res, out SmileInfo info)
        {
            List <double> xs = new List <double>();
            List <double> ys = new List <double>();

            foreach (InteractiveObject obj in res.ControlPoints)
            {
                Point point = obj.Anchor.Value;
                xs.Add(point.X);
                ys.Add(point.Y);
            }

            // ReSharper disable once UseObjectOrCollectionInitializer
            info              = new SmileInfo();
            info.F            = f;
            info.dT           = dT;
            info.Expiry       = expiry;
            info.ScriptTime   = scriptTime;
            info.RiskFreeRate = riskFreeRate;
            info.BaseTicker   = baseTicker;

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

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

                    res.Tag = info;
                }
            }
            catch (DivideByZeroException dvbz)
            {
                StringBuilder sb = new StringBuilder();

                sb.AppendLine(dvbz.GetType().Name);
                sb.AppendLine();
                sb.AppendLine("X;Y");
                for (int j = 0; j < xs.Count; j++)
                {
                    sb.AppendFormat("{0};{1}",
                                    xs[j].ToString(CultureInfo.InvariantCulture),
                                    ys[j].ToString(CultureInfo.InvariantCulture));
                    sb.AppendLine();
                }
                sb.AppendLine();
                sb.AppendLine();
                sb.AppendLine();
                sb.AppendLine(dvbz.ToString());
                sb.AppendLine();

                externalContext.Log(sb.ToString(), MessageType.Error, true);

                return(false);
            }
            catch (Exception ex)
            {
                externalContext.Log(ex.ToString(), MessageType.Error, true);
                return(false);
            }

            xs.Clear();
            ys.Clear();

            return(true);
        }
コード例 #13
0
        /// <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);
        }
コード例 #14
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);
        }
コード例 #15
0
        public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, double btcUsdIndex, int barNum)
        {
            int barsCount = ContextBarsCount;

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

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

            double dT = time;

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

            if (!DoubleUtil.IsPositive(btcUsdIndex))
            {
                // TODO: сделать ресурс! [{0}] Price of BTC/USD must be positive value. scaleMult:{1}
                //string msg = RM.GetStringFormat("OptHandlerMsg.CurrencyScaleMustBePositive", GetType().Name, scaleMult);
                string msg = String.Format("[{0}] Price of BTC/USD must be positive value. scaleMult:{1}", GetType().Name, btcUsdIndex);
                if (wasInitialized)
                {
                    m_context.Log(msg, MessageType.Error, true);
                }
                return(Constants.EmptySeries);
            }

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

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

            double futPx = oldInfo.F;

            double ivAtm;

            if (!oldInfo.ContinuousFunction.TryGetValue(futPx, out ivAtm))
            {
                string msg = String.Format("[{0}] Unable to get IV ATM from smile. Tag:{1}", GetType().Name, smile.Tag);
                if (wasInitialized)
                {
                    m_context.Log(msg, MessageType.Error, true);
                }
                return(Constants.EmptySeries);
            }

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

            List <double>    xs      = new List <double>();
            List <double>    ys      = new List <double>();
            double           futStep = optSer.UnderlyingAsset.Tick;
            PositionsManager posMan  = PositionsManager.GetManager(m_context);
            ReadOnlyCollection <IPosition> basePositions = posMan.GetClosedOrActiveForBar(optSer.UnderlyingAsset);
            var optPositions = SingleSeriesProfile.GetAllOptionPositions(posMan, pairs);

            SortedDictionary <double, IOptionStrikePair> futPrices;

            if (!SmileImitation5.TryPrepareImportantPoints(pairs, futPx, futStep, -1, out futPrices))
            {
                string msg = String.Format("[{0}] It looks like there is no suitable points for the smile. pairs.Length:{1}", GetType().Name, pairs.Length);
                if (wasInitialized)
                {
                    m_context.Log(msg, MessageType.Warning, true);
                }
                return(Constants.EmptySeries);
            }

            // Чтобы учесть базис между фьючерсом и индексом, вычисляю их отношение:
            // Пример: BtcInd==9023; FutPx==8937 --> indexDivByFutRatio == 1.009623
            double indexDivByFutRatio = btcUsdIndex / futPx;

            List <InteractiveObject> controlPoints = new List <InteractiveObject>();
            // Список точек для вычисления дельт + улыбки для этих точек
            var deltaPoints = new List <Tuple <double, InteractivePointActive> >();

            foreach (var kvp in futPrices)
            {
                // Если у нас новая цена фьючерса, значит, будет новая цена индекса
                double f = kvp.Key;
                // И при пересчете опционов в баксы НУЖНО ИСПОЛЬЗОВАТЬ ИМЕННО ЕЁ!!!
                double newScaleMult = f * indexDivByFutRatio;

                bool tradableStrike = (kvp.Value != null);

                CashPnlUsd cashPnlUsd;
                CashPnlBtc cashPnlBtc;
                GetBasePnl(basePositions, lastBarIndex, f, m_futNominal, out cashPnlUsd, out cashPnlBtc);
                double cashDollars = cashPnlUsd.CashUsd;
                double pnlDollars  = cashPnlUsd.PnlUsd;
                double cashBtc     = cashPnlBtc.CashBtc;
                double pnlBtc      = cashPnlBtc.PnlBtc;

                SmileInfo actualSmile = SingleSeriesProfile.GetActualSmile(oldInfo, m_greekAlgo, f);

                // Флаг того, что ПНЛ по всем инструментам был расчитан верно
                bool pnlIsCorrect = true;
                for (int j = 0; (j < pairs.Length) && pnlIsCorrect; j++)
                {
                    var tuple = optPositions[j];
                    IOptionStrikePair pair = pairs[j];
                    //int putBarCount = pair.Put.UnderlyingAsset.Bars.Count;
                    //int callBarCount = pair.Call.UnderlyingAsset.Bars.Count;

                    CashPnlUsd pairCashUsd;
                    CashPnlBtc pairCashBtc;
                    bool       localRes = TryGetPairPnl(actualSmile, pair.Strike, lastBarIndex, lastBarIndex,
                                                        tuple.Item1, tuple.Item2, f, dT, newScaleMult,
                                                        out pairCashUsd, out pairCashBtc);

                    pnlIsCorrect &= localRes;
                    cashDollars  += pairCashUsd.CashUsd;
                    pnlDollars   += pairCashUsd.PnlUsd;
                    cashBtc      += pairCashBtc.CashBtc;
                    pnlBtc       += pairCashBtc.PnlBtc;
                }

                // Профиль позиции будет рисоваться только если ПНЛ был посчитан верно по ВСЕМ инструментам!
                if (pnlIsCorrect)
                {
                    InteractivePointLight ip;
                    // Показаны будут только узлы, совпадающие с реальными страйками.
                    // Потенциально это позволит сделать эти узлы пригодными для торговли по клику наподобие улыбки.
                    if (m_showNodes && tradableStrike)
                    {
                        // ReSharper disable once UseObjectOrCollectionInitializer
                        InteractivePointActive tmp = new InteractivePointActive();

                        tmp.IsActive = m_showNodes && tradableStrike;
                        string pnlUsdStr = (cashDollars + pnlDollars).ToString(m_tooltipFormat, CultureInfo.InvariantCulture);
                        string pnlBtcStr = (cashBtc + pnlBtc).ToString(DefaultBtcTooltipFormat, CultureInfo.InvariantCulture);
                        tmp.Tooltip = String.Format(CultureInfo.InvariantCulture, " F: {0}\r\n PnL($): {1}\r\n PnL(B): {2}", f, pnlUsdStr, pnlBtcStr);

                        // Если у нас получился сплайн по профилю позиции, значит мы можем вычислить дельту!
                        if (m_showNodes && tradableStrike)
                        {
                            // Готовим важные точки
                            var tuple = new Tuple <double, InteractivePointActive>(f, tmp);
                            deltaPoints.Add(tuple);
                        }

                        ip = tmp;
                    }
                    else
                    {
                        ip = new InteractivePointLight();
                    }

                    // PROD-6103 - Выводить профиль позиции в биткойнах
                    if (ProfileAsBtc)
                    {
                        ip.Value = new Point(f, cashBtc + pnlBtc);
                    }
                    else
                    {
                        ip.Value = new Point(f, cashDollars + pnlDollars);
                    }

                    controlPoints.Add(new InteractiveObject(ip));

                    xs.Add(f);
                    // PROD-6103 - Выводить профиль позиции в биткойнах
                    if (ProfileAsBtc)
                    {
                        ys.Add(cashBtc + pnlBtc);
                    }
                    else
                    {
                        ys.Add(cashDollars + pnlDollars);
                    }
                } // End if (pnlIsCorrect)
            }     // End foreach (var kvp in futPrices)

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

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

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

                    NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys);

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

                    res.Tag = info;

                    // Если у нас получился сплайн по профилю позиции, значит мы можем вычислить дельту!
                    for (int j = 1; j < deltaPoints.Count - 1; j++)
                    {
                        var    tuple = deltaPoints[j];
                        double f     = tuple.Item1;
                        var    ip    = tuple.Item2;

                        double actualDelta, deltaLeft, deltaRight;
                        if (m_twoSideDelta)
                        {
                            double prevF = deltaPoints[j - 1].Item1;
                            double nextF = deltaPoints[j + 1].Item1;

                            double currY = deltaPoints[j].Item2.ValueY;
                            double prevY = deltaPoints[j - 1].Item2.ValueY;
                            double nextY = deltaPoints[j + 1].Item2.ValueY;

                            deltaLeft  = (currY - prevY) / (f - prevF);
                            deltaRight = (nextY - currY) / (nextF - f);
                            // Считаем дельты слева и справа
                            // Мы передвинули улыбку в точку f и считаем дельту позиции В ЭТОЙ ЖЕ ТОЧКЕ(!)
                            //if (info.ContinuousFunction.TryGetValue(f - 100 * futStep, out deltaLeft) &&
                            //    info.ContinuousFunctionD1.TryGetValue(f + 100 * futStep, out deltaRight))
                            {
                                // Первый пробел уже учтен в Tooltip
                                ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "{0}\r\n LeftD: {1:0.0}; RightD: {2:0.0}",
                                                           ip.Tooltip, deltaLeft, deltaRight);
                            }
                        }
                        else
                        {
                            // Мы передвинули улыбку в точку f и считаем дельту позиции В ЭТОЙ ЖЕ ТОЧКЕ(!)
                            if (info.ContinuousFunctionD1.TryGetValue(f, out actualDelta))
                            {
                                // Первый пробел уже учтен в Tooltip
                                ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "{0}\r\n D: {1:0.0}", ip.Tooltip, actualDelta);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                m_context.Log(ex.ToString(), MessageType.Error, true);
                return(Constants.EmptySeries);
            }

            SetHandlerInitialized(now, true);

            return(res);
        }
コード例 #16
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);
        }
コード例 #17
0
        public InteractiveSeries Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, double scaleMult, double ratePct, int barNum)
        {
            int barsCount = ContextBarsCount;

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

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

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

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

            double futPx = price;
            double dT    = time;

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

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

            if (!DoubleUtil.IsPositive(scaleMult))
            {
                //throw new ScriptException("Argument 'scaleMult' contains NaN for some strange reason. scaleMult:" + scaleMult);
                return(Constants.EmptySeries);
            }

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

            double ivAtm, slopeAtm, shape;

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

            if (m_setIvByHands)
            {
                ivAtm = m_ivAtmPct.Value / Constants.PctMult;
            }

            if (m_setSlopeByHands)
            {
                slopeAtm = m_slopePct.Value / Constants.PctMult;
                //slopeAtm = slopeAtm / F / Math.Pow(dT, Pow + shape);
            }

            //if (setShapeByHands)
            {
                shape = m_shapePct.Value / Constants.PctMult;
            }

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

            if (Double.IsNaN(slopeAtm))
            {
                // [{0}] Smile skew at the money must be some number. skewAtm:{1}
                string msg = RM.GetStringFormat("OptHandlerMsg.SkewAtmMustBeNumber", GetType().Name, slopeAtm);
                if (wasInitialized)
                {
                    m_context.Log(msg, MessageType.Error, true);
                }
                return(Constants.EmptySeries);
            }

            SmileInfo templateInfo;

            #region Fill templateInfo
            if (m_useLocalTemplate)
            {
                InteractiveSeries templateSmile = m_context.LoadObject(m_frozenSmileId) as InteractiveSeries;
                if (templateSmile == null)
                {
                    // [{0}] There is no LOCAL smile with ID:{1}
                    string msg = RM.GetStringFormat("SmileImitation5.NoLocalSmile", GetType().Name, m_frozenSmileId);
                    if (wasInitialized)
                    {
                        m_context.Log(msg, MessageType.Error, true);
                    }
                    return(Constants.EmptySeries);
                }

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

                List <double> locXs = new List <double>();
                List <double> locYs = new List <double>();
                foreach (InteractiveObject oldObj in templateSmile.ControlPoints)
                {
                    if (!oldObj.AnchorIsActive)
                    {
                        continue;
                    }

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

                    double x = Math.Log(k / futPx) / Math.Pow(dT, DefaultPow + shape) / ivAtm;
                    double sigmaNormalized = sigma / ivAtm;

                    locXs.Add(x);
                    locYs.Add(sigmaNormalized);
                }

                try
                {
                    NotAKnotCubicSpline spline = new NotAKnotCubicSpline(locXs, locYs);

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

                    templateInfo = locInfo;
                }
                catch (Exception ex)
                {
                    m_context.Log(ex.ToString(), MessageType.Error, true);
                    return(Constants.EmptySeries);
                }
            }
            else
            {
                //templateSmile = context.LoadGlobalObject(globalSmileID, true) as InteractiveSeries;
                templateInfo = m_context.LoadGlobalObject(m_globalSmileId, true) as SmileInfo;
                if (templateInfo == null)
                {
                    // [{0}] There is no global templateInfo with ID:{1}. I'll try to use default one.
                    string msg = RM.GetStringFormat("SmileImitation5.TemplateWasSaved", GetType().Name, m_globalSmileId);
                    m_context.Log(msg, MessageType.Error, true);

                    System.Xml.Linq.XDocument xDoc  = System.Xml.Linq.XDocument.Parse(SmileFunction5.XmlSmileRiz4Nov1);
                    System.Xml.Linq.XElement  xInfo = xDoc.Root;
                    SmileInfo templateSmile         = SmileInfo.FromXElement(xInfo);

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

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

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

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

                    m_context.StoreGlobalObject(m_globalSmileId, templateSmile, true);

                    // [{0}] Default templateInfo was saved to Global Cache with ID:{1}.
                    msg = RM.GetStringFormat("SmileImitation5.TemplateWasSaved", GetType().Name, m_globalSmileId);
                    m_context.Log(msg, MessageType.Warning, true);

                    templateInfo = templateSmile;
                }
            }
            #endregion Fill templateInfo

            if (!m_setIvByHands)
            {
                m_ivAtmPct.Value = ivAtm * Constants.PctMult;
            }

            if (!m_setShapeByHands)
            {
                // так я ещё не умею
            }

            if (!m_setSlopeByHands)
            {
                // Пересчитываю наклон в безразмерку
                double dSigmaDx = slopeAtm * futPx * Math.Pow(dT, DefaultPow + shape);
                m_slopePct.Value = dSigmaDx * Constants.PctMult;

                // и теперь апдейчу локальную переменную slopeAtm:
                slopeAtm = m_slopePct.Value / Constants.PctMult;
            }

            // Это функция в нормированных координатах
            // поэтому достаточно обычной симметризации
            // PROD-3111: заменяю вызов на SmileFunctionExtended
            //SimmetrizeFunc simmFunc = new SimmetrizeFunc(templateInfo.ContinuousFunction);
            //SimmetrizeFunc simmFuncD1 = new SimmetrizeFunc(templateInfo.ContinuousFunctionD1);
            //SmileFunction5 smileFunc = new SmileFunction5(simmFunc, simmFuncD1, ivAtm, slopeAtm, shape, futPx, dT);
            SmileFunctionExtended smileFunc = new SmileFunctionExtended(
                (NotAKnotCubicSpline)templateInfo.ContinuousFunction, ivAtm, slopeAtm, shape, futPx, dT);
            smileFunc.UseTails = m_useSmileTails;

            List <double>       xs    = new List <double>();
            List <double>       ys    = new List <double>();
            IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray();

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

            double minK    = pairs[0].Strike;
            double maxK    = pairs[pairs.Length - 1].Strike;
            double futStep = optSer.UnderlyingAsset.Tick;
            double dK      = pairs[1].Strike - pairs[0].Strike;
            double width   = (SigmaMult * ivAtm * Math.Sqrt(dT)) * futPx;
            width = Math.Max(width, 10 * dK);
            // Нельзя вылезать за границу страйков???
            width = Math.Min(width, Math.Abs(futPx - minK));
            width = Math.Min(width, Math.Abs(maxK - futPx));
            // Generate left invisible tail
            if (m_generateTails)
            {
                GaussSmile.AppendLeftTail(smileFunc, xs, ys, minK, dK, false);
            }

            SortedDictionary <double, IOptionStrikePair> strikePrices;
            if (!SmileImitation5.TryPrepareImportantPoints(pairs, futPx, futStep, width, out strikePrices))
            {
                string msg = String.Format("[{0}] It looks like there is no suitable points for the smile. pairs.Length:{1}", GetType().Name, pairs.Length);
                if (wasInitialized)
                {
                    m_context.Log(msg, MessageType.Warning, true);
                }
                return(Constants.EmptySeries);
            }

            List <InteractiveObject> controlPoints = new List <InteractiveObject>();
            //for (int j = 0; j < pairs.Length; j++)
            foreach (var kvp in strikePrices)
            {
                bool showPoint = (kvp.Value != null);
                //IOptionStrikePair pair = pairs[j];
                //// Сверхдалекие страйки игнорируем
                //if ((pair.Strike < futPx - width) || (futPx + width < pair.Strike))
                //{
                //    showPoint = false;
                //}

                //double k = pair.Strike;
                double k = kvp.Key;
                double sigma;
                if (!smileFunc.TryGetValue(k, out sigma))
                {
                    continue;
                }
                double vol = sigma;

                if (!DoubleUtil.IsPositive(sigma))
                {
                    continue;
                }

                //InteractivePointActive ip = new InteractivePointActive(k, vol);
                //ip.Color = (optionPxMode == OptionPxMode.Ask) ? Colors.DarkOrange : Colors.DarkCyan;
                //ip.DragableMode = DragableMode.None;
                //ip.Geometry = Geometries.Rect; // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect;

                // Иначе неправильно выставляются координаты???
                //ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", k, PctMult * sigma);

                InteractivePointLight ip;
                if (showPoint)
                {
                    var tip = new InteractivePointActive(k, vol);
                    if (k <= futPx) // Puts
                    {
                        SmileImitationDeribit5.FillNodeInfo(tip, futPx, dT, kvp.Value, StrikeType.Put, OptionPxMode.Mid, sigma, false, m_showNodes, scaleMult, ratePct);
                    }
                    else // Calls
                    {
                        SmileImitationDeribit5.FillNodeInfo(tip, futPx, dT, kvp.Value, StrikeType.Call, OptionPxMode.Mid, sigma, false, m_showNodes, scaleMult, ratePct);
                    }
                    ip = tip;
                }
                else
                {
                    ip = new InteractivePointLight(k, vol);
                }

                InteractiveObject obj = new InteractiveObject(ip);

                //if (showPoint)
                // Теперь мы понимаем, что точки либо рисуются
                // потому что это страйки (и тогда они автоматом InteractivePointActive)
                // либо они присутствуют, но не рисуются их узлы. Потому что они InteractivePointLight.
                {
                    controlPoints.Add(obj);
                }

                xs.Add(k);
                ys.Add(vol);
            }

            // ReSharper disable once UseObjectOrCollectionInitializer
            InteractiveSeries res = new InteractiveSeries();
            res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints);

            // Generate right invisible tail
            if (m_generateTails)
            {
                GaussSmile.AppendRightTail(smileFunc, xs, ys, maxK, dK, false);
            }

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

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

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

            res.Tag = info;

            SetHandlerInitialized(now);

            return(res);
        }
コード例 #18
0
        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);
        }
コード例 #19
0
        private bool TryProcessSeries(IOptionSeries optSer, DateTime now, out double ivAtm)
        {
            ivAtm = Constants.NaN;
            if (optSer == null)
            {
                return(false);
            }

            Dictionary <DateTime, double> ivSigmas;

            #region Get cache
            DateTime expiry    = optSer.ExpirationDate.Date;
            string   cashKey   = IvOnF.GetCashKey(optSer.UnderlyingAsset.Symbol, expiry, m_rescaleTime, m_tRemainMode);
            object   globalObj = Context.LoadGlobalObject(cashKey, true);
            ivSigmas = globalObj as Dictionary <DateTime, double>;
            // PROD-3970 - 'Важный' объект
            if (ivSigmas == null)
            {
                var container = globalObj as NotClearableContainer;
                if ((container != null) && (container.Content != null))
                {
                    ivSigmas = container.Content as Dictionary <DateTime, double>;
                }
            }
            if (ivSigmas == null)
            {
                ivSigmas = new Dictionary <DateTime, double>();
            }
            #endregion Get cache

            ISecurity sec = optSer.UnderlyingAsset;
            int       len = sec.Bars.Count;
            if (len <= 0)
            {
                return(false);
            }

            FinInfo baseFinInfo = optSer.UnderlyingAsset.FinInfo;
            if (baseFinInfo.LastPrice == null)
            {
                string msg = "[IV ATM (all series)] (baseFinInfo.LastPrice == null)";
                m_context.Log(msg, MessageType.Warning, false);
                return(false);
            }

            double futPx = baseFinInfo.LastPrice.Value;
            if (futPx <= Double.Epsilon)
            {
                return(false);
            }

            NotAKnotCubicSpline spline = null;
            try
            {
                spline = IvOnF.PrepareExchangeSmileSpline(optSer, Double.MinValue, Double.MaxValue);
            }
            catch (ScriptException scriptEx)
            {
                m_context.Log(scriptEx.ToString(), MessageType.Error, false);
                return(false);
            }
            catch (Exception ex)
            {
                m_context.Log(ex.ToString(), MessageType.Error, false);
                return(false);
            }

            if (spline == null)
            {
                return(false);
            }

            try
            {
                double sigma;
                if (spline.TryGetValue(futPx, out sigma) && (!Double.IsNaN(sigma)) && (sigma > 0))
                {
                    ivAtm = sigma;
                    DateTime lastBarDate = sec.Bars[len - 1].Date;

                    if (m_rescaleTime)
                    {
                        #region Зверская ветка по замене времени
                        DateTime expDate = optSer.ExpirationDate.Date + m_expiryTime;

                        // 1. Надо перевести волатильность в абсолютную цену
                        // с учетом плоского календарного времени применяемого РТС
                        double plainTimeAsYears;
                        {
                            double plainTimeAsDays;
                            TimeToExpiry.GetDt(expDate, now, TimeRemainMode.PlainCalendar,
                                               false, out plainTimeAsDays, out plainTimeAsYears);
                        }

                        // 2. Вычисляем 'нормальное' время
                        double timeAsDays, timeAsYears;
                        TimeToExpiry.GetDt(expDate, now, m_tRemainMode,
                                           false, out timeAsDays, out timeAsYears);
                        sigma = FinMath.RescaleIvToAnotherTime(plainTimeAsYears, ivAtm, timeAsYears);
                        if (DoubleUtil.IsPositive(sigma))
                        {
                            ivAtm = sigma;
                            // Это просто запись на диск. К успешности вычисления волы success отношения не имеет
                            bool success = IvOnF.TryWrite(m_context, true, true, 1, cashKey, ivSigmas,
                                                          lastBarDate, sigma);

                            // Теперь надо вычислить безразмерный наклон кодом в классе SmileImitation5
                            bool successSkew = IvOnF.TryCalcAndWriteSkews(m_context, spline, true, true, 1,
                                                                          optSer.UnderlyingAsset.Symbol, expiry, futPx, lastBarDate,
                                                                          m_tRemainMode, plainTimeAsYears, timeAsYears);

                            return(true);
                        }
                        else
                        {
                            // Если перемасштабировать улыбку не получается придется эту точку проигнорировать
                            // Надо ли сделать соответствующую запись в логе???
                            return(false);
                        }
                        #endregion Зверская ветка по замене времени
                    }
                    else
                    {
                        // Это просто запись на диск. К успешности вычисления волы success отношения не имеет
                        bool success = IvOnF.TryWrite(m_context, true, true, 1, cashKey, ivSigmas,
                                                      lastBarDate, sigma);
                        return(true);
                    }
                }
            }
            catch (ScriptException scriptEx)
            {
                m_context.Log(scriptEx.ToString(), MessageType.Error, false);
            }
            catch (Exception ex)
            {
                m_context.Log(ex.ToString(), MessageType.Error, false);
                //throw;
            }

            return(false);
        }