/// <summary>
        /// Вега будет иметь размерность 'пункты за 100% волатильности'.
        /// Обычно же опционщики любят смотреть размерность 'пункты за 1% волатильности'.
        /// Поэтому полученное сырое значение ещё надо делить на 100%.
        /// (Эквивалентно умножению на интересующий набег волы для получения дифференциала).
        /// </summary>
        internal static bool TryEstimateVega(double putQty, double callQty, IOptionSeries optSer, IOptionStrikePair pair,
                                             InteractiveSeries smile, NumericalGreekAlgo greekAlgo,
                                             double f, double dSigma, double timeToExpiry, double riskFreeRate, out double rawVega)
        {
            rawVega = Double.NaN;

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

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

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

            double pnl1 = 0;
            // Флаг того, что ПНЛ по всем инструментам был расчитан верно
            bool pnlIsCorrect1 = true;
            {
                // Для первой точки улыбку не трогаем
                {
                    double pairPnl;
                    pnlIsCorrect1 &= SingleSeriesProfile.TryGetPairPrice(
                        putQty, callQty, smile, pair, f, timeToExpiry, riskFreeRate, out pairPnl);
                    pnl1 += pairPnl;
                }
            }

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

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

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

            if (pnlIsCorrect1 && pnlIsCorrect2)
            {
                // Первая точка совпадает с текущей, поэтому нет деления на 2.
                //rawVega = ((cash2 + pnl2) - (cash1 + pnl1)) / dSigma;
                rawVega = (pnl2 - pnl1) / dSigma;
                return(true);
            }
            else
            {
                return(false);
            }
        }
Exemple #2
0
        /// <summary>
        /// Тета опционной пары по формуле Блека-Шолза
        /// </summary>
        internal static void GetPairTheta(PositionsManager posMan, InteractiveSeries smile, IOptionStrikePair pair,
                                          double f, double dT, out double totalTheta)
        {
            totalTheta = 0;

            ISecurity putSec = pair.Put.Security, callSec = pair.Call.Security;
            // закрытые позы не дают в клада в тету, поэтому беру только активные
            ReadOnlyCollection <IPosition> putPositions  = posMan.GetActiveForBar(putSec);
            ReadOnlyCollection <IPosition> callPositions = posMan.GetActiveForBar(callSec);

            if ((putPositions.Count <= 0) && (callPositions.Count <= 0))
            {
                return;
            }

            double?sigma = null;

            if ((smile.Tag != null) && (smile.Tag is SmileInfo))
            {
                double    tmp;
                SmileInfo info = smile.GetTag <SmileInfo>();
                if (info.ContinuousFunction.TryGetValue(pair.Strike, out tmp))
                {
                    sigma = tmp;
                }
            }

            if (sigma == null)
            {
                sigma = (from d2 in smile.ControlPoints
                         let point = d2.Anchor.Value
                                     where (DoubleUtil.AreClose(pair.Strike, point.X))
                                     select(double?) point.Y).FirstOrDefault();
            }

            if (sigma == null)
            {
                return;
            }

            {
                double putTheta;
                GetOptTheta(putPositions,
                            f, pair.Strike, dT, sigma.Value, 0.0, false, out putTheta);
                totalTheta += putTheta;
            }

            {
                double callTheta;
                GetOptTheta(callPositions,
                            f, pair.Strike, dT, sigma.Value, 0.0, true, out callTheta);
                totalTheta += callTheta;
            }
        }
Exemple #3
0
        /// <summary>
        /// Получить финансовые параметры опционной позиции (колы и путы на одном страйке суммарно)
        /// </summary>
        /// <param name="smileInfo">улыбка</param>
        /// <param name="strike">страйк</param>
        /// <param name="putBarCount">количество баров для пута</param>
        /// <param name="callBarCount">количество баров для кола</param>
        /// <param name="putPositions">позиции пута</param>
        /// <param name="callPositions">позиции кола</param>
        /// <param name="f">текущая цена БА</param>
        /// <param name="dT">время до экспирации</param>
        /// <param name="cash">денежные затраты на формирование позы (могут быть отрицательными)</param>
        /// <param name="pnl">текущая цена позиции</param>
        /// <returns>true, если всё посчитано без ошибок</returns>
        public static bool TryGetPairPnl(SmileInfo smileInfo, double strike,
                                         int putBarCount, int callBarCount,
                                         IList <IPosition> putPositions, IList <IPosition> callPositions,
                                         double f, double dT, double btcUsdInd,
                                         out double cashUsd, out double pnlUsd)
        {
            CashPnlUsd cashPnlUsd;
            CashPnlBtc cashPnlBtc;

            bool res = TryGetPairPnl(smileInfo, strike,
                                     putBarCount, callBarCount,
                                     putPositions, callPositions,
                                     f, dT, btcUsdInd,
                                     out cashPnlUsd, out cashPnlBtc);

            cashUsd = cashPnlUsd.CashUsd;
            pnlUsd  = cashPnlUsd.PnlUsd;

            return(res);
        }
Exemple #4
0
        /// <summary>
        /// Основной метод, который выполняет всю торговую логику по котированию и следит за риском
        /// </summary>
        protected double Process(double entryPermission,
                                 double centralStrike, double risk, double maxRisk, InteractiveSeries smile, IOptionSeries optSer, InteractiveSeries callDelta,
                                 double callRisk, double putRisk, int barNum)
        {
            int barsCount = m_context.BarsCount;

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

            {
                IOptionStrikePair testPair;
                if (!optSer.TryGetStrikePair(centralStrike, out testPair))
                {
                    return(Constants.NaN);
                }
            }

            // Если риск не был измерен говорить вообще не о чем!
            if (Double.IsNaN(risk))
            {
                return(Constants.NaN);
            }

            // Если риск разумен и условие входа НЕ ВЫПОЛНЕНО -- отдыхаем.
            // А вот если риск превышен -- тогда по идее надо бы его подсократить!
            if ((risk < maxRisk) && (entryPermission <= 0))
            {
                return(Constants.NaN);
            }

            PositionsManager posMan = PositionsManager.GetManager(m_context);

            if (posMan.BlockTrading)
            {
                //string msg = String.Format("Trading is blocked. Please, change 'Block Trading' parameter.");
                //m_context.Log(msg, MessageType.Info, true);
                return(Constants.NaN);
            }

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

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

            double dT    = sInfo.dT;
            double futPx = sInfo.F;

            SmileInfo callDeltaInfo = callDelta.GetTag <SmileInfo>();

            if ((callDeltaInfo == null) || (callDeltaInfo.ContinuousFunction == null))
            {
                return(Constants.NaN);
            }

            // Функция для вычисления дельты кола
            IFunction cDf = callDeltaInfo.ContinuousFunction;

            // Набираем риск
            if (risk < maxRisk)
            {
                List <IOptionStrikePair> orderedPairs = BuyOptionGroupDelta.GetFilteredPairs(optSer, centralStrike, cDf,
                                                                                             m_strikeStep, m_minDelta, m_maxDelta, m_checkAbsDelta);

                // Сколько лотов уже выставлено в рынок
                double pendingQty = 0;
                if (orderedPairs.Count > 0)
                {
                    foreach (IOptionStrikePair candidPair in orderedPairs)
                    {
                        double ivAtm;
                        double strike = candidPair.Strike;
                        if ((!sInfo.ContinuousFunction.TryGetValue(strike, out ivAtm)) || Double.IsNaN(ivAtm) || (ivAtm < Double.Epsilon))
                        {
                            string msg = String.Format("[{0}.{1}] Unable to get IV at strike {2}. ivAtm:{3}",
                                                       Context.Runtime.TradeName, GetType().Name, candidPair.Strike, ivAtm);
                            m_context.Log(msg, MessageType.Error, true);
                            return(Constants.NaN);
                        }

                        double theorPutPx  = FinMath.GetOptionPrice(futPx, strike, dT, ivAtm, 0, false);
                        double theorCallPx = FinMath.GetOptionPrice(futPx, strike, dT, ivAtm, 0, true);

                        double cd, pd;
                        // Вычисляю дельту кола и с ее помощью -- дельту пута
                        if (!cDf.TryGetValue(strike, out cd))
                        {
                            // Этого не может быть по правилу отбора страйков!
                            Contract.Assert(false, "Почему мы не смогли вычислить дельту кола???");
                            continue;
                        }

                        // Типа, колл-пут паритет для вычисления дельты путов
                        pd = 1 - cd;
                        if (m_checkAbsDelta)
                        {
                            // Берем дельты по модулю
                            cd = Math.Abs(cd);
                            pd = Math.Abs(pd);
                        }

                        double putPx, callPx;
                        {
                            double   putQty, callQty;
                            DateTime putTime, callTime;
                            putPx  = IvSmile.GetOptPrice(m_context, futPx, candidPair.Put, OptionPxMode.Bid, 0, 0, out putQty, out putTime);
                            callPx = IvSmile.GetOptPrice(m_context, futPx, candidPair.Call, OptionPxMode.Bid, 0, 0, out callQty, out callTime);
                        }

                        #region Набираем риск
                        int executedQty = 0;
                        // Если дельта пута влезает в диапазон -- выставляем котировку в путы
                        if ((m_minDelta <= pd) && (pd <= m_maxDelta))
                        {
                            #region Набираем риск в путах
                            ISecurity sec = candidPair.Put.Security;
                            double    px  = SellOptions.SafeMaxPrice(theorPutPx + m_entryShift * sec.Tick, putPx, sec);
                            double    iv  = FinMath.GetOptionSigma(futPx, candidPair.Strike, dT, px, 0, false);
                            double    qty = Math.Abs(m_fixedQty);
                            // TODO: Немного грубая оценка, но пока сойдет
                            qty = BuyOptions.GetSafeQty(risk + pendingQty * putRisk, maxRisk, qty, putRisk);
                            if (qty > 0)
                            {
                                string sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                                posMan.SellAtPrice(m_context, sec, qty, px, "Open SELL", sigName);
                                pendingQty += qty;

                                m_context.Log(sigName, MessageType.Info, false);

                                executedQty += (int)qty;
                            }
                            #endregion Набираем риск в путах
                        }

                        // Если дельта кола влезает в диапазон -- выставляем котировку в колы
                        if (Math.Abs(executedQty) < Math.Abs(m_fixedQty) &&
                            (m_minDelta <= cd) && (cd <= m_maxDelta))
                        {
                            #region Набираем риск в колах
                            ISecurity sec = candidPair.Call.Security;
                            double    px  = SellOptions.SafeMaxPrice(theorCallPx + m_entryShift * sec.Tick, callPx, sec);
                            double    iv  = FinMath.GetOptionSigma(futPx, candidPair.Strike, dT, px, 0, true);
                            double    qty = Math.Abs(m_fixedQty) - Math.Abs(executedQty);
                            // TODO: Немного грубая оценка, но пока сойдет
                            // Делаю оценку изменения текущего риска, если нам зафилят заявку в путах
                            //qty = BuyOptions.GetSafeQty(risk + Math.Abs(executedQty) * putRisk, maxRisk, qty, callRisk);
                            // Причем здесь уже не нужно отдельно учитывать executedQty, потому что он входит в pendingQty
                            qty = BuyOptions.GetSafeQty(risk + pendingQty * callRisk, maxRisk, qty, callRisk);
                            if (qty > 0)
                            {
                                string sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                                posMan.SellAtPrice(m_context, sec, qty, px, "Open SELL", sigName);
                                pendingQty += qty;

                                m_context.Log(sigName, MessageType.Info, false);

                                //executedQty += (int)qty;
                            }
                            #endregion Набираем риск в колах
                        }
                        #endregion Набираем риск
                    } // End foreach (IOptionStrikePair candidPair in orderedPairs)
                }
                else
                {
                    string msg = String.Format("[{0}] Strike not found. risk:{1}; maxRisk:{2}; orderedPairs.Count:{3}",
                                               Context.Runtime.TradeName, risk, maxRisk, orderedPairs.Count);
                    m_context.Log(msg, MessageType.Warning, true);
                }
            }
            else if (risk > maxRisk)
            {
                string msg;
                //string msg = String.Format("[DEBUG:{0}] risk:{1}; maxRisk:{2}", Context.Runtime.TradeName, risk, maxRisk);
                //m_context.Log(msg, MessageType.Info, true);

                // Надо взять пары, начиная от центральной и далее по возрастанию расстояния
                var orderedPairs = (from p in optSer.GetStrikePairs()
                                    orderby Math.Abs(p.Strike - centralStrike) ascending
                                    select p).ToArray();
                if (orderedPairs.Length > 0)
                {
                    foreach (IOptionStrikePair candidPair in orderedPairs)
                    {
                        #region Проверяю, что в страйке есть КОРОТКАЯ позиция
                        double putOpenQty  = posMan.GetTotalQty(candidPair.Put.Security, barNum);
                        double callOpenQty = posMan.GetTotalQty(candidPair.Call.Security, barNum);

                        if ((putOpenQty >= 0) && (callOpenQty >= 0))
                        {
                            continue;
                        }
                        if (DoubleUtil.IsZero(putOpenQty) && DoubleUtil.IsZero(callOpenQty))
                        {
                            continue;
                        }

                        {
                            msg = String.Format("[{0}:{1}] Strike:{2}; putOpenQty:{3}; callOpenQty:{4}",
                                                Context.Runtime.TradeName, GetType().Name, candidPair.Strike, putOpenQty, callOpenQty);
                            m_context.Log(msg, MessageType.Info, true);
                        }
                        #endregion Проверяю, что в страйке есть КОРОТКАЯ позиция

                        double theorPutPx, theorCallPx;
                        {
                            double iv;
                            if ((!sInfo.ContinuousFunction.TryGetValue(candidPair.Strike, out iv)) ||
                                Double.IsNaN(iv) || (iv < Double.Epsilon))
                            {
                                msg = String.Format("[{0}.{1}] Unable to get IV at strike {2}. IV:{3}",
                                                    Context.Runtime.TradeName, GetType().Name, candidPair.Strike, iv);
                                m_context.Log(msg, MessageType.Error, true);
                                continue;
                            }

                            theorPutPx  = FinMath.GetOptionPrice(futPx, candidPair.Strike, dT, iv, 0, false);
                            theorCallPx = FinMath.GetOptionPrice(futPx, candidPair.Strike, dT, iv, 0, true);
                        }

                        #region Сдаём риск (один квант объёма за раз)
                        double putPx, callPx;
                        {
                            DateTime putTime, callTime;
                            double   putAskQty, callAskQty;
                            putPx  = IvSmile.GetOptPrice(m_context, futPx, candidPair.Put, OptionPxMode.Ask, 0, 0, out putAskQty, out putTime);
                            callPx = IvSmile.GetOptPrice(m_context, futPx, candidPair.Call, OptionPxMode.Ask, 0, 0, out callAskQty, out callTime);
                        }

                        //if (m_optionType == StrikeType.Put)
                        //{
                        //    #region В путах
                        //    if (putOpenQty < 0) // Это означает, что в страйке есть короткие путы
                        //    {
                        //        ISecurity sec = candidPair.Put.Security;
                        //        double px = SafeMinPrice(theorPutPx + m_exitShift * sec.Tick, putPx, sec);
                        //        double iv = FinMath.GetOptionSigma(futPx, candidPair.Strike, dT, px, 0, false);
                        //        double qty = Math.Min(Math.Abs(m_fixedQty), Math.Abs(putOpenQty));
                        //        string sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                        //        posMan.BuyAtPrice(m_context, sec, qty, px, "Close BUY", sigName);

                        //        m_context.Log(sigName, MessageType.Info, false);

                        //        // Выход из foreach (IOptionStrikePair candidPair in orderedPairs)
                        //        break;
                        //    }
                        //    #endregion В путах
                        //}
                        //else if (m_optionType == StrikeType.Call)
                        //{
                        //    #region В колах
                        //    if (callOpenQty < 0) // Это означает, что в страйке есть короткие колы
                        //    {
                        //        ISecurity sec = candidPair.Call.Security;
                        //        double px = SafeMinPrice(theorCallPx + m_exitShift * sec.Tick, callPx, sec);
                        //        double iv = FinMath.GetOptionSigma(futPx, candidPair.Strike, dT, px, 0, true);
                        //        double qty = Math.Min(Math.Abs(m_fixedQty), Math.Abs(callOpenQty));
                        //        string sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                        //        posMan.BuyAtPrice(m_context, sec, qty, px, "Close BUY", sigName);

                        //        m_context.Log(sigName, MessageType.Info, false);

                        //        // Выход из foreach (IOptionStrikePair candidPair in orderedPairs)
                        //        break;
                        //    }
                        //    #endregion В колах
                        //}
                        //else
                        {
                            #region В оба вида опционов сразу встаю
                            int executedQty = 0;
                            if (putOpenQty < 0) // Это означает, что в страйке есть короткие путы
                            {
                                ISecurity sec     = candidPair.Put.Security;
                                double    px      = SellOptions.SafeMinPrice(theorPutPx + m_exitShift * sec.Tick, putPx, sec);
                                double    iv      = FinMath.GetOptionSigma(futPx, candidPair.Strike, dT, px, 0, false);
                                double    qty     = Math.Min(Math.Abs(m_fixedQty), Math.Abs(putOpenQty));
                                string    sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                                posMan.BuyAtPrice(m_context, sec, qty, px, "Close BUY", sigName);

                                m_context.Log(sigName, MessageType.Info, false);

                                executedQty += (int)qty;
                            }

                            if ((callOpenQty < 0) && // Это означает, что в страйке есть короткие колы
                                (Math.Abs(executedQty) < Math.Abs(m_fixedQty)))
                            {
                                ISecurity sec     = candidPair.Call.Security;
                                double    px      = SellOptions.SafeMinPrice(theorCallPx + m_exitShift * sec.Tick, callPx, sec);
                                double    iv      = FinMath.GetOptionSigma(futPx, candidPair.Strike, dT, px, 0, true);
                                double    qty     = Math.Min(Math.Abs(m_fixedQty) - Math.Abs(executedQty), Math.Abs(callOpenQty));
                                string    sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                                posMan.BuyAtPrice(m_context, sec, qty, px, "Close BUY", sigName);

                                m_context.Log(sigName, MessageType.Info, false);

                                executedQty += (int)qty;
                            }

                            if (executedQty > 0)
                            {
                                // Выход из foreach (IOptionStrikePair candidPair in orderedPairs)
                                break;
                            }
                            #endregion В оба вида опционов сразу встаю
                        }
                        #endregion Сдаём риск (один квант объёма за раз)
                    }
                }
                else
                {
                    msg = String.Format("[{0}.{1}] risk:{2}; maxRisk:{3}; orderedPairs.Length:{4}",
                                        Context.Runtime.TradeName, GetType().Name, risk, maxRisk, orderedPairs.Length);
                    m_context.Log(msg, MessageType.Warning, true);
                }
            }

            return(Constants.NaN);
        }
Exemple #5
0
        private int FillEditableCurve(SmileInfo info, List <InteractiveObject> controlPoints, List <double> xs, DragableMode dragMode)
        {
            if (xs.Count < 2)
            {
                string msg = String.Format("[DEBUG:{0}] Not enough points in argument xs. xs.Count:{0}", xs.Count);
                m_context.Log(msg, MessageType.Warning, true);
                return(0);
            }

            int    j          = 0;
            double k          = xs[0];
            double strikeStep = (xs[xs.Count - 1] - xs[0]) / (xs.Count - 1);

            while (k <= xs[xs.Count - 1])
            {
                if ((j < xs.Count) &&
                    (k <= xs[j]) && (xs[j] < k + strikeStep))
                {
                    #region Узел должен быть показан и доступен для редактирования
                    {
                        double sigma = info.ContinuousFunction.Value(xs[j]);

                        InteractivePointActive ip = new InteractivePointActive(xs[j], sigma);
                        ip.IsActive     = true;
                        ip.Geometry     = Geometries.Ellipse;
                        ip.DragableMode = dragMode;
                        ip.Color        = AlphaColors.GreenYellow;
                        ip.Tooltip      = String.Format("K:{0}; IV:{1:0.00}", xs[j], Constants.PctMult * sigma);

                        InteractiveObject obj = new InteractiveObject(ip);

                        controlPoints.Add(obj);
                    }
                    #endregion Узел должен быть показан и доступен для редактирования

                    j++;
                }
                else
                {
                    #region Просто точка без маркера
                    {
                        double sigma = info.ContinuousFunction.Value(k);

                        //InteractivePointActive ip = new InteractivePointActive(k, sigma);
                        //ip.IsActive = false;
                        //ip.Geometry = Geometries.Ellipse;
                        //ip.DragableMode = DragableMode.None;
                        //ip.Color = System.Windows.Media.Colors.Green;
                        //ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", k, Constants.PctMult * sigma);

                        InteractivePointLight ip  = new InteractivePointLight(k, sigma);
                        InteractiveObject     obj = new InteractiveObject(ip);

                        controlPoints.Add(obj);
                    }
                    #endregion Просто точка без маркера
                }

                k += strikeStep;
            }

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

            int barsCount = m_context.BarsCount;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                    tmp.Tag = nodeInfo;

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

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

                    controlPoints.Add(obj);
                }

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

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

                res.Tag = sInfo;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            return(res);
        }
Exemple #8
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);
        }
Exemple #9
0
        internal static bool TryEstimateDelta(double putQty, double callQty, IOptionSeries optSer, IOptionStrikePair pair,
                                              InteractiveSeries smile, NumericalGreekAlgo greekAlgo,
                                              double f, double dF, double timeToExpiry, double riskFreeRate, out double rawDelta)
        {
            rawDelta = Double.NaN;

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

            SmileInfo sInfo = smile.Tag as SmileInfo;

            double pnl1 = 0;
            // Флаг того, что ПНЛ по всем инструментам был расчитан верно
            bool pnlIsCorrect1 = true;
            {
                if (sInfo != null)
                {
                    SmileInfo actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f - dF);

                    double pairPnl;
                    pnlIsCorrect1 &= SingleSeriesProfile.TryGetPairPrice(
                        putQty, callQty, actualSmile, pair, f - dF, timeToExpiry, riskFreeRate, out pairPnl);
                    pnl1 += pairPnl;
                }
                else
                {
                    // PROD-5746 -- Убираю использование старого неэффективного кода
                    pnlIsCorrect1 = false;

                    Contract.Assert(pnlIsCorrect1, $"[{nameof(OptionsBoardNumericalDelta)}.{nameof(TryEstimateDelta)}] #1 Каким образом получили неподготовленную улыбку? (sInfo == null)");

                    //InteractiveSeries actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f - dF);

                    //double pairPnl;
                    //pnlIsCorrect1 &= SingleSeriesProfile.TryGetPairPrice(
                    //    putQty, callQty, actualSmile, pair, f - dF, timeToExpiry, riskFreeRate, out pairPnl);
                    //pnl1 += pairPnl;
                }
            }

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

            {
                if (sInfo != null)
                {
                    SmileInfo actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f + dF);

                    double pairPnl;
                    pnlIsCorrect2 &= SingleSeriesProfile.TryGetPairPrice(
                        putQty, callQty, actualSmile, pair, f + dF, timeToExpiry, riskFreeRate, out pairPnl);
                    pnl2 += pairPnl;
                }
                else
                {
                    // PROD-5746 -- Убираю использование старого неэффективного кода
                    pnlIsCorrect2 = false;

                    Contract.Assert(pnlIsCorrect2, $"[{nameof(OptionsBoardNumericalDelta)}.{nameof(TryEstimateDelta)}] #2 Каким образом получили неподготовленную улыбку? (sInfo == null)");

                    //InteractiveSeries actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f + dF);

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

            if (pnlIsCorrect1 && pnlIsCorrect2)
            {
                //rawDelta = ((cash2 + pnl2) - (cash1 + pnl1)) / 2.0 / dF;
                rawDelta = (pnl2 - pnl1) / 2.0 / dF;
                return(true);
            }
            else
            {
                return(false);
            }
        }
Exemple #10
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);
        }
        public InteractiveSeries Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, double ratePct, int barNum)
        {
            int barsCount = ContextBarsCount;

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

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

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

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

            double futPx = price;
            double dT    = time;

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

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

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

            double ivAtm, slopeAtm;

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

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

            if (m_setSlopeByHands)
            {
                slopeAtm = m_internalSlopePct.Value / Constants.PctMult;
                slopeAtm = slopeAtm / futPx / Math.Sqrt(dT);
            }

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

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

            //{
            //    string msg = String.Format("[DEBUG:{0}] ivAtm:{1}; shift:{2}; depth:{3};   F:{4}; dT:{5}; ",
            //        GetType().Name, ivAtm, shift, depth, F, dT);
            //    context.Log(msg, MessageType.Info, true);
            //}

            SmileFunction3 tempFunc = new SmileFunction3(ivAtm, m_shift, 0.5, futPx, dT);
            double         depth    = tempFunc.GetDepthUsingSlopeATM(slopeAtm);

            SmileFunction3 smileFunc = new SmileFunction3(ivAtm, m_shift, depth, futPx, dT);

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

            if (!m_setSlopeByHands)
            {
                double dSigmaDx = slopeAtm * futPx * Math.Sqrt(dT);
                m_internalSlopePct.Value = dSigmaDx * Constants.PctMult;
            }

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

            IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray();

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

            double minK  = pairs[0].Strike;
            double dK    = pairs[1].Strike - pairs[0].Strike;
            double width = (SigmaMult * ivAtm * Math.Sqrt(dT)) * futPx;

            width = Math.Max(width, 2 * dK);
            // Generate left invisible tail
            if (m_generateTails)
            {
                GaussSmile.AppendLeftTail(smileFunc, xs, ys, minK, dK, false);
            }

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

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

                double k     = pair.Strike;
                double sigma = smileFunc.Value(k);
                double vol   = sigma;

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

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

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

                if (showPoint)
                {
                    if (k <= futPx) // Puts
                    {
                        FillNodeInfo(ip, futPx, dT, pair, StrikeType.Put, OptionPxMode.Mid, sigma, false, m_isVisiblePoints, ratePct);
                    }
                    else // Calls
                    {
                        FillNodeInfo(ip, futPx, dT, pair, StrikeType.Call, OptionPxMode.Mid, sigma, false, m_isVisiblePoints, ratePct);
                    }
                }

                InteractiveObject obj = new InteractiveObject(ip);

                if (showPoint)
                {
                    controlPoints.Add(obj);
                }

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

            // ReSharper disable once UseObjectOrCollectionInitializer
            InteractiveSeries res = new InteractiveSeries();

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

            double maxK = pairs[pairs.Length - 1].Strike;

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

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

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

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

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

            res.Tag = info;

            SetHandlerInitialized(now);

            return(res);
        }
Exemple #12
0
        /// <summary>
        /// Основной метод, который выполняет всю торговую логику по котированию и следит за риском
        /// </summary>
        protected double Process(double entryPermission,
                                 double centralStrike, double risk, double maxRisk, InteractiveSeries smile, IOptionSeries optSer,
                                 double callRisk, double putRisk, int barNum)
        {
            int barsCount = m_context.BarsCount;

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

            {
                IOptionStrikePair testPair;
                if (!optSer.TryGetStrikePair(centralStrike, out testPair))
                {
                    return(Constants.NaN);
                }
            }

            // Если риск не был измерен говорить вообще не о чем!
            if (Double.IsNaN(risk))
            {
                return(Constants.NaN);
            }

            // Если риск разумен и условие входа НЕ ВЫПОЛНЕНО -- отдыхаем.
            // А вот если риск превышен -- тогда по идее надо бы его подсократить!
            if ((risk < maxRisk) && (entryPermission <= 0))
            {
                return(Constants.NaN);
            }

            PositionsManager posMan = PositionsManager.GetManager(m_context);

            if (posMan.BlockTrading)
            {
                //string msg = String.Format("Trading is blocked. Please, change 'Block Trading' parameter.");
                //m_context.Log(msg, MessageType.Info, true);
                return(Constants.NaN);
            }

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

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

            double dT    = sInfo.dT;
            double futPx = sInfo.F;

            // Набираем риск
            if (risk < maxRisk)
            {
                // Надо взять пары, начиная от центральной и далее по возрастанию расстояния с учетом шага страйков
                IOptionStrikePair[] orderedPairs;
                if (m_strikeStep < Double.Epsilon)
                {
                    // Просто сортируем страйки по расстоянию до Центра
                    orderedPairs = (from p in optSer.GetStrikePairs()
                                    orderby Math.Abs(p.Strike - centralStrike) ascending
                                    select p).ToArray();
                }
                else
                {
                    // Сортировка по возрастанию до Центра + обязательно условие кратности параметру m_strikeStep
                    orderedPairs = (from p in optSer.GetStrikePairs()
                                    let dK = Math.Abs(p.Strike - centralStrike)
                                             let dKStep = (int)Math.Round(dK / m_strikeStep)
                                                          where DoubleUtil.AreClose(dK, m_strikeStep * dKStep) // проверяем, что расстояние от страйка до центра кратно m_strikeStep
                                                          orderby Math.Abs(p.Strike - centralStrike) ascending
                                                          select p).ToArray();
                }

                Contract.Assert(m_strikeAmount >= 0, "Как получился отрицательный m_strikeAmount??? m_strikeAmount: " + m_strikeAmount);
                // Защита от дурака? Или не надо париться?
                m_strikeAmount = Math.Max(0, m_strikeAmount);

                // Котируем либо 1 центральный страйк либо центр + четное число соседей
                int maxStrikeCount = 2 * m_strikeAmount + 1;
                int strikeCounter  = 0;
                // Сколько лотов уже выставлено в рынок
                double pendingQty = 0;
                if (orderedPairs.Length > 0)
                {
                    foreach (IOptionStrikePair candidPair in orderedPairs)
                    {
                        if (strikeCounter >= maxStrikeCount)
                        {
                            // Все, выходим. Цикл завершен.
                            break;
                        }

                        double ivAtm;
                        double strike = candidPair.Strike;
                        if ((!sInfo.ContinuousFunction.TryGetValue(strike, out ivAtm)) || Double.IsNaN(ivAtm) || (ivAtm < Double.Epsilon))
                        {
                            string msg = String.Format("[{0}.{1}] Unable to get IV at strike {2}. ivAtm:{3}",
                                                       Context.Runtime.TradeName, GetType().Name, candidPair.Strike, ivAtm);
                            m_context.Log(msg, MessageType.Error, true);
                            return(Constants.NaN);
                        }

                        double theorPutPx  = FinMath.GetOptionPrice(futPx, strike, dT, ivAtm, 0, false);
                        double theorCallPx = FinMath.GetOptionPrice(futPx, strike, dT, ivAtm, 0, true);

                        #region Набираем риск
                        double putPx, callPx;
                        {
                            double   putQty, callQty;
                            DateTime putTime, callTime;
                            putPx  = IvSmile.GetOptPrice(m_context, futPx, candidPair.Put, OptionPxMode.Bid, 0, 0, out putQty, out putTime);
                            callPx = IvSmile.GetOptPrice(m_context, futPx, candidPair.Call, OptionPxMode.Bid, 0, 0, out callQty, out callTime);
                        }

                        if ((m_optionType == StrikeType.Put) || (m_optionType == StrikeType.Any) && (strike <= futPx))
                        {
                            #region В путах
                            ISecurity sec = candidPair.Put.Security;
                            double    qty = Math.Abs(m_fixedQty);
                            // TODO: Немного грубая оценка, но пока сойдет
                            qty = BuyOptions.GetSafeQty(risk + pendingQty * putRisk, maxRisk, qty, putRisk);
                            if (qty > 0)
                            {
                                double px      = SellOptions.SafeMaxPrice(theorPutPx + m_entryShift * sec.Tick, putPx, sec);
                                double iv      = FinMath.GetOptionSigma(futPx, candidPair.Strike, dT, px, 0, false);
                                string sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                                posMan.SellAtPrice(m_context, sec, qty, px, "Open SELL", sigName);
                                pendingQty += qty;

                                m_context.Log(sigName, MessageType.Info, false);
                            }
                            #endregion В путах
                        }
                        else if ((m_optionType == StrikeType.Call) || (m_optionType == StrikeType.Any) && (futPx <= strike))
                        {
                            #region В колах
                            ISecurity sec = candidPair.Call.Security;
                            double    qty = Math.Abs(m_fixedQty);
                            // TODO: Немного грубая оценка, но пока сойдет
                            qty = BuyOptions.GetSafeQty(risk + pendingQty * callRisk, maxRisk, qty, callRisk);
                            if (qty > 0)
                            {
                                double px      = SellOptions.SafeMaxPrice(theorCallPx + m_entryShift * sec.Tick, callPx, sec);
                                double iv      = FinMath.GetOptionSigma(futPx, candidPair.Strike, dT, px, 0, true);
                                string sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                                posMan.SellAtPrice(m_context, sec, qty, px, "Open SELL", sigName);
                                pendingQty += qty;

                                m_context.Log(sigName, MessageType.Info, false);
                            }
                            #endregion В колах
                        }
                        else
                        {
                            // Вроде бы, сюда не должны приходить никогда?..
                            #region В оба вида опционов сразу встаю
                            int executedQty = 0;
                            {
                                ISecurity sec = candidPair.Put.Security;
                                double    px  = SellOptions.SafeMaxPrice(theorPutPx + m_entryShift * sec.Tick, putPx, sec);
                                double    iv  = FinMath.GetOptionSigma(futPx, candidPair.Strike, dT, px, 0, false);
                                double    qty = Math.Max(1, Math.Abs(m_fixedQty / 2));
                                // TODO: Немного грубая оценка, но пока сойдет
                                qty = BuyOptions.GetSafeQty(risk + pendingQty * putRisk, maxRisk, qty, putRisk);
                                if (qty > 0)
                                {
                                    string sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                                    posMan.SellAtPrice(m_context, sec, qty, px, "Open SELL", sigName);
                                    pendingQty += qty;

                                    m_context.Log(sigName, MessageType.Info, false);

                                    executedQty += (int)qty;
                                }
                            }

                            if (Math.Abs(executedQty) < Math.Abs(m_fixedQty))
                            {
                                ISecurity sec = candidPair.Call.Security;
                                double    px  = SellOptions.SafeMaxPrice(theorCallPx + m_entryShift * sec.Tick, callPx, sec);
                                double    iv  = FinMath.GetOptionSigma(futPx, candidPair.Strike, dT, px, 0, true);
                                double    qty = Math.Abs(m_fixedQty) - Math.Abs(executedQty);
                                // TODO: Немного грубая оценка, но пока сойдет
                                // Делаю оценку изменения текущего риска, если нам зафилят заявку в путах
                                //qty = BuyOptions.GetSafeQty(risk + Math.Abs(executedQty) * putRisk, maxRisk, qty, callRisk);
                                // Причем здесь уже не нужно отдельно учитывать executedQty, потому что он входит в pendingQty
                                qty = BuyOptions.GetSafeQty(risk + pendingQty * callRisk, maxRisk, qty, callRisk);
                                if (qty > 0)
                                {
                                    string sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                                    posMan.SellAtPrice(m_context, sec, qty, px, "Open SELL", sigName);
                                    pendingQty += qty;

                                    m_context.Log(sigName, MessageType.Info, false);

                                    //executedQty += (int)qty;
                                }
                            }
                            #endregion В оба вида опционов сразу встаю
                        }
                        #endregion Набираем риск

                        strikeCounter++;
                    } // End foreach (IOptionStrikePair candidPair in orderedPairs)
                }
                else
                {
                    string msg = String.Format("[{0}] Strike not found. risk:{1}; maxRisk:{2}; orderedPairs.Length:{3}",
                                               Context.Runtime.TradeName, risk, maxRisk, orderedPairs.Length);
                    m_context.Log(msg, MessageType.Warning, true);
                }
            }
            else if (risk > maxRisk)
            {
                string msg;
                //string msg = String.Format("[DEBUG:{0}] risk:{1}; maxRisk:{2}", Context.Runtime.TradeName, risk, maxRisk);
                //m_context.Log(msg, MessageType.Info, true);

                // Надо взять пары, начиная от центральной и далее по возрастанию расстояния
                var orderedPairs = (from p in optSer.GetStrikePairs()
                                    orderby Math.Abs(p.Strike - centralStrike) ascending
                                    select p).ToArray();
                if (orderedPairs.Length > 0)
                {
                    foreach (IOptionStrikePair candidPair in orderedPairs)
                    {
                        #region Проверяю, что в страйке есть КОРОТКАЯ позиция
                        double putOpenQty  = posMan.GetTotalQty(candidPair.Put.Security, barNum);
                        double callOpenQty = posMan.GetTotalQty(candidPair.Call.Security, barNum);

                        if ((putOpenQty >= 0) && (callOpenQty >= 0))
                        {
                            continue;
                        }
                        if (DoubleUtil.IsZero(putOpenQty) && DoubleUtil.IsZero(callOpenQty))
                        {
                            continue;
                        }

                        {
                            msg = String.Format("[{0}:{1}] Strike:{2}; putOpenQty:{3}; callOpenQty:{4}",
                                                Context.Runtime.TradeName, GetType().Name, candidPair.Strike, putOpenQty, callOpenQty);
                            m_context.Log(msg, MessageType.Info, true);
                        }
                        #endregion Проверяю, что в страйке есть КОРОТКАЯ позиция

                        double theorPutPx, theorCallPx;
                        {
                            double iv;
                            if ((!sInfo.ContinuousFunction.TryGetValue(candidPair.Strike, out iv)) ||
                                Double.IsNaN(iv) || (iv < Double.Epsilon))
                            {
                                msg = String.Format("[{0}.{1}] Unable to get IV at strike {2}. IV:{3}",
                                                    Context.Runtime.TradeName, GetType().Name, candidPair.Strike, iv);
                                m_context.Log(msg, MessageType.Error, true);
                                continue;
                            }

                            theorPutPx  = FinMath.GetOptionPrice(futPx, candidPair.Strike, dT, iv, 0, false);
                            theorCallPx = FinMath.GetOptionPrice(futPx, candidPair.Strike, dT, iv, 0, true);
                        }

                        #region Сдаём риск (один квант объёма за раз)
                        double putPx, callPx;
                        {
                            DateTime putTime, callTime;
                            double   putAskQty, callAskQty;
                            putPx  = IvSmile.GetOptPrice(m_context, futPx, candidPair.Put, OptionPxMode.Ask, 0, 0, out putAskQty, out putTime);
                            callPx = IvSmile.GetOptPrice(m_context, futPx, candidPair.Call, OptionPxMode.Ask, 0, 0, out callAskQty, out callTime);
                        }

                        if (m_optionType == StrikeType.Put)
                        {
                            #region В путах
                            if (putOpenQty < 0) // Это означает, что в страйке есть короткие путы
                            {
                                ISecurity sec     = candidPair.Put.Security;
                                double    px      = SellOptions.SafeMinPrice(theorPutPx + m_exitShift * sec.Tick, putPx, sec);
                                double    iv      = FinMath.GetOptionSigma(futPx, candidPair.Strike, dT, px, 0, false);
                                double    qty     = Math.Min(Math.Abs(m_fixedQty), Math.Abs(putOpenQty));
                                string    sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                                posMan.BuyAtPrice(m_context, sec, qty, px, "Close BUY", sigName);

                                m_context.Log(sigName, MessageType.Info, false);

                                // Выход из foreach (IOptionStrikePair candidPair in orderedPairs)
                                break;
                            }
                            #endregion В путах
                        }
                        else if (m_optionType == StrikeType.Call)
                        {
                            #region В колах
                            if (callOpenQty < 0) // Это означает, что в страйке есть короткие колы
                            {
                                ISecurity sec     = candidPair.Call.Security;
                                double    px      = SellOptions.SafeMinPrice(theorCallPx + m_exitShift * sec.Tick, callPx, sec);
                                double    iv      = FinMath.GetOptionSigma(futPx, candidPair.Strike, dT, px, 0, true);
                                double    qty     = Math.Min(Math.Abs(m_fixedQty), Math.Abs(callOpenQty));
                                string    sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                                posMan.BuyAtPrice(m_context, sec, qty, px, "Close BUY", sigName);

                                m_context.Log(sigName, MessageType.Info, false);

                                // Выход из foreach (IOptionStrikePair candidPair in orderedPairs)
                                break;
                            }
                            #endregion В колах
                        }
                        else
                        {
                            #region В оба вида опционов сразу встаю
                            int executedQty = 0;
                            if (putOpenQty < 0) // Это означает, что в страйке есть короткие путы
                            {
                                ISecurity sec     = candidPair.Put.Security;
                                double    px      = SellOptions.SafeMinPrice(theorPutPx + m_exitShift * sec.Tick, putPx, sec);
                                double    iv      = FinMath.GetOptionSigma(futPx, candidPair.Strike, dT, px, 0, false);
                                double    qty     = Math.Min(Math.Abs(m_fixedQty), Math.Abs(putOpenQty));
                                string    sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                                posMan.BuyAtPrice(m_context, sec, qty, px, "Close BUY", sigName);

                                m_context.Log(sigName, MessageType.Info, false);

                                executedQty += (int)qty;
                            }

                            if ((callOpenQty < 0) && // Это означает, что в страйке есть короткие колы
                                (Math.Abs(executedQty) < Math.Abs(m_fixedQty)))
                            {
                                ISecurity sec     = candidPair.Call.Security;
                                double    px      = SellOptions.SafeMinPrice(theorCallPx + m_exitShift * sec.Tick, callPx, sec);
                                double    iv      = FinMath.GetOptionSigma(futPx, candidPair.Strike, dT, px, 0, true);
                                double    qty     = Math.Min(Math.Abs(m_fixedQty) - Math.Abs(executedQty), Math.Abs(callOpenQty));
                                string    sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                                posMan.BuyAtPrice(m_context, sec, qty, px, "Close BUY", sigName);

                                m_context.Log(sigName, MessageType.Info, false);

                                executedQty += (int)qty;
                            }

                            if (executedQty > 0)
                            {
                                // Выход из foreach (IOptionStrikePair candidPair in orderedPairs)
                                break;
                            }
                            #endregion В оба вида опционов сразу встаю
                        }
                        #endregion Сдаём риск (один квант объёма за раз)
                    }
                }
                else
                {
                    msg = String.Format("[{0}.{1}] risk:{2}; maxRisk:{3}; orderedPairs.Length:{4}",
                                        Context.Runtime.TradeName, GetType().Name, risk, maxRisk, orderedPairs.Length);
                    m_context.Log(msg, MessageType.Warning, true);
                }
            }

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

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

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

            double dT = time;

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

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

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

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

            double futPx;

            if (DoubleUtil.IsPositive(oldInfo.F))
            {
                futPx = oldInfo.F;
            }
            else
            {
                futPx = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Close;
            }

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

            if (m_countFutures)
            {
                ReadOnlyCollection <IPosition> futPositions = posMan.GetActiveForBar(optSer.UnderlyingAsset);
                if (futPositions.Count > 0)
                {
                    double futQty;
                    GetTotalQty(futPositions, out futQty);

                    if (!DoubleUtil.IsZero(futQty))
                    {
                        // ReSharper disable once UseObjectOrCollectionInitializer
                        InteractivePointActive ip = new InteractivePointActive(0, futQty);
                        ip.IsActive = true;
                        //ip.DragableMode = DragableMode.None;
                        //ip.Geometry = Geometries.Rect;
                        //ip.Color = Colors.DarkOrange;

                        //// PROD-1194 - Цвет ячеек и фона
                        //if (futQty > 0)
                        //    ip.BackColor = ScriptColors.Aquamarine;
                        //else if (futQty < 0)
                        //    ip.BackColor = ScriptColors.LightSalmon;
                        //if (ip.BackColor != null)
                        //    ip.ForeColor = ScriptColors.Black;

                        // TODO: Ждем решения PROD-6009
                        //// PROD-1194 - Цвет ячеек и фона -- фон красится по принципу "в деньгах или нет", шрифт -- по знаку
                        //if (futQty > 0)
                        //    ip.ForeColor = ScriptColors.LightGreen;
                        //else if (futQty < 0)
                        //    ip.ForeColor = ScriptColors.Red;
                        ////if () // Фьючерс не красится!
                        ////    ip.ForeColor = ScriptColors.Black;

                        ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "Fut Qty:{0}", futQty);

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

            for (int j = 0; j < pairs.Length; j++)
            {
                IOptionStrikePair pair = pairs[j];
                double            putQty, callQty;
                GetPairQty(posMan, pair, out putQty, out callQty);

                if ((!DoubleUtil.IsZero(putQty)) || (!DoubleUtil.IsZero(callQty)))
                {
                    double y       = 0;
                    Color? backCol = null;
                    switch (m_optionType)
                    {
                    case StrikeType.Put:
                        y       = putQty;
                        backCol = (pair.Strike > futPx) ? ScriptColors.LightCyan : ScriptColors.LightYellow;
                        break;

                    case StrikeType.Call:
                        y       = callQty;
                        backCol = (pair.Strike < futPx) ? ScriptColors.LightCyan : ScriptColors.LightYellow;
                        break;

                    case StrikeType.Any:
                        y = putQty + callQty;
                        break;

                    default:
                        throw new NotImplementedException("OptionType: " + m_optionType);
                    }

                    if (
                        (!DoubleUtil.IsZero(y)) ||
                        ((m_optionType == StrikeType.Any) && ((!DoubleUtil.IsZero(putQty)) || (!DoubleUtil.IsZero(callQty))))) // Хотя вообще-то мы внутри if и второй блок проверок всегда true...
                    {
                        // ReSharper disable once UseObjectOrCollectionInitializer
                        InteractivePointActive ip = new InteractivePointActive(pair.Strike, y);
                        ip.IsActive = true;
                        //ip.DragableMode = DragableMode.None;
                        //ip.Geometry = Geometries.Rect;
                        //ip.Color = Colors.DarkOrange;

                        //// PROD-1194 - Цвет ячеек и фона
                        //if (y > 0)
                        //    ip.BackColor = ScriptColors.Aquamarine;
                        //else if (y < 0)
                        //    ip.BackColor = ScriptColors.LightSalmon;
                        //if (ip.BackColor != null)
                        //    ip.ForeColor = ScriptColors.Black;

                        // TODO: Ждем решения PROD-6009
                        //// PROD-1194 - Цвет ячеек и фона -- фон красится по принципу "в деньгах или нет", шрифт -- по знаку
                        //if (y > 0)
                        //    ip.ForeColor = ScriptColors.LightGreen;
                        //else if (y < 0)
                        //    ip.ForeColor = ScriptColors.Red;
                        ////if (backCol != null)
                        ////    ip.BackColor = backCol;

                        ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "K:{0}; Qty:{1}", pair.Strike, y);

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

            // ReSharper disable once UseObjectOrCollectionInitializer
            InteractiveSeries res = new InteractiveSeries(); // Здесь правильно делать new

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

            SetHandlerInitialized(now);

            return(res);
        }
Exemple #14
0
        private double Process(InteractiveSeries profile, double moneyness, int barNum)
        {
            // В данном случае намеренно возвращаю Double.NaN
            double failRes = Double.NaN;

            if (m_repeatLastValue)
            {
                failRes = Double.IsNaN(m_prevValue) ? Double.NaN : m_prevValue; // В данном случае намеренно возвращаю Double.NaN
            }
            Dictionary <DateTime, double> results;

            #region Get cache
            // [2019-01-30] Перевожу на использование NotClearableContainer (PROD-6683)
            string key       = m_variableId + "_results";
            var    container = m_context.LoadObject(key) as NotClearableContainer <Dictionary <DateTime, double> >;
            if (container != null)
            {
                results = container.Content;
            }
            else
            {
                results = m_context.LoadObject(key) as Dictionary <DateTime, double>; // Старая ветка на всякий случай
            }
            if (results == null)
            {
                string msg = String.Format(RM.GetString("OptHandlerMsg.GetValueAtm.CacheNotFound"),
                                           GetType().Name, key.GetHashCode());
                m_context.Log(msg, MessageType.Info);

                results   = new Dictionary <DateTime, double>();
                container = new NotClearableContainer <Dictionary <DateTime, double> >(results);
                m_context.StoreObject(key, container);
            }
            #endregion Get cache

            int len = m_context.BarsCount;
            if (len <= 0)
            {
                return(failRes);
            }

            // Вот так не работает. По всей видимости, это прямая индексация от утра
            //DateTime now = m_context.Runtime.GetBarTime(barNum);

            ISecurity sec = m_context.Runtime.Securities.FirstOrDefault();
            if ((sec == null) || (sec.Bars.Count <= barNum))
            {
                return(failRes);
            }

            DateTime now = sec.Bars[barNum].Date;
            if (results.TryGetValue(now, out double rawRes) &&
                (barNum < len - 1)) // !!! ВАЖНО !!! На последнем баре ВСЕГДА заново делаем вычисления
            {
                m_prevValue = rawRes;
                return(rawRes);
            }
            else
            {
                int barsCount = ContextBarsCount;
                if (barNum < barsCount - 1)
                {
                    // Если история содержит осмысленное значение, то оно уже содержится в failRes
                    return(failRes);
                }
                else
                {
                    #region Process last bar(s)
                    if (profile == null)
                    {
                        return(failRes);
                    }

                    SmileInfo profInfo = profile.GetTag <SmileInfo>();
                    if ((profInfo == null) || (profInfo.ContinuousFunction == null))
                    {
                        return(failRes);
                    }

                    double f  = profInfo.F;
                    double dT = profInfo.dT;
                    if (!DoubleUtil.IsPositive(f))
                    {
                        string msg = String.Format(RM.GetString("OptHandlerMsg.FutPxMustBePositive"), GetType().Name, f);
                        m_context.Log(msg, MessageType.Error);
                        // [GLSP-1557] В данном случае намеренно возвращаю Double.NaN, чтобы предотвратить распространение
                        //             заведомо неправильных данных по системе (их попадание в дельта-хеджер и т.п.).
                        return(Double.NaN);
                    }

                    if (!DoubleUtil.IsPositive(dT))
                    {
                        string msg = String.Format(RM.GetString("OptHandlerMsg.TimeMustBePositive"), GetType().Name, dT);
                        m_context.Log(msg, MessageType.Error);
                        // [GLSP-1557] В данном случае намеренно возвращаю Double.NaN, чтобы предотвратить распространение
                        //             заведомо неправильных данных по системе (их попадание в дельта-хеджер и т.п.).
                        return(Double.NaN);
                    }

                    double effectiveF;
                    if (DoubleUtil.IsZero(moneyness))
                    {
                        effectiveF = f;
                    }
                    else
                    {
                        effectiveF = f * Math.Exp(moneyness * Math.Sqrt(dT));
                    }
                    if (profInfo.ContinuousFunction.TryGetValue(effectiveF, out rawRes))
                    {
                        m_prevValue  = rawRes;
                        results[now] = rawRes;
                    }
                    else
                    {
                        if (barNum < len - 1)
                        {
                            // [GLSP-1557] Не последний бар? Тогда использую failRes
                            rawRes = failRes;
                        }
                        else
                        {
                            // [GLSP-1557] В данном случае намеренно возвращаю Double.NaN, чтобы предотвратить распространение
                            //             заведомо неправильных данных по системе (их попадание в дельта-хеджер и т.п.).
                            return(Double.NaN);
                        }
                    }
                    #endregion Process last bar(s)

                    m_result.Value = rawRes;
                    //m_context.Log(MsgId + ": " + m_result.Value, MessageType.Info, PrintInLog);

                    return(rawRes);
                }
            }
        }
Exemple #15
0
        public void Execute(double price, double time, InteractiveSeries smile, ICanvasPane pane, int barNum)
        {
            int barsCount = ContextBarsCount;

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

            // PROD-3577
            pane.FeetToBorder2ByDefault = true;

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

            if ((smileInfo == null) || (smileInfo.ContinuousFunction == null))
            {
                return;
            }

            double futPx = price;
            double dT    = time;

            if (!DoubleUtil.IsPositive(futPx))
            {
                return;
            }
            if (!DoubleUtil.IsPositive(dT))
            {
                return;
            }

            double sigma;

            if ((!smileInfo.ContinuousFunction.TryGetValue(futPx, out sigma)) ||
                (!DoubleUtil.IsPositive(sigma)))
            {
                //При работе с Эксанте и прочим Западом Биржевой улыбки не будет
                //Поэтому надо иметь 'План Б': подставить фиксированную волатильность 30%!
                sigma = DefaultSigma;
            }

            if (pane != null)
            {
                string expiryStr = smileInfo.Expiry.ToString(TimeToExpiry.DateTimeFormat, CultureInfo.InvariantCulture);
                Rect   rect      = PrepareVieportSettings(expiryStr, futPx, dT, sigma);
                ApplySettings(pane, rect);

                int cpLen = smile.ControlPoints.Count;
                if (ManageXGridStep && (cpLen > 1))
                {
                    double dK = smile.ControlPoints[1].Anchor.ValueX - smile.ControlPoints[0].Anchor.ValueX;
                    if (cpLen > 2)
                    {
                        int t = cpLen / 2; // Делим нацело. Для cpLen==3 получаем 1
                        dK = smile.ControlPoints[t + 1].Anchor.ValueX - smile.ControlPoints[t].Anchor.ValueX;
                    }

                    pane.XAxisStep    = GetXAxisStep(dK);
                    pane.XAxisDiviser = GetXAxisDivisor(dK);
                }
            }
        }
        /// <summary>
        /// Метод под флаг TemplateTypes.INTERACTIVESPLINE
        /// </summary>
        public InteractiveSeries Execute(InteractiveSeries quotes, InteractiveSeries smile, int barNum)
        {
            if ((quotes == null) || (smile == null))
            {
                return(Constants.EmptySeries);
            }

            int barsCount = m_context.BarsCount;

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

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

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

            InteractiveSeries res   = quotes;
            double            futPx = oldInfo.F;
            double            dT    = oldInfo.dT;

            foreach (InteractiveObject obj in res.ControlPoints)
            {
                SmileNodeInfo nodeInfo = obj.Anchor.Tag as SmileNodeInfo;
                if (nodeInfo == null)
                {
                    continue;
                }

                double realOptPx = nodeInfo.OptPx;
                bool   isCall    = (nodeInfo.OptionType == StrikeType.Call);

                double sigma = oldInfo.ContinuousFunction.Value(nodeInfo.Strike);
                if (Double.IsNaN(sigma) || (sigma < Double.Epsilon))
                {
                    //string msg = String.Format("[DEBUG:{0}] Invalid sigma:{1} for strike:{2}", GetType().Name, sigma, nodeInfo.Strike);
                    //m_context.Log(msg, MessageType.Warning, true);
                    continue;
                }

                double theorOptPx = FinMath.GetOptionPrice(futPx, nodeInfo.Strike, dT, sigma, oldInfo.RiskFreeRate, isCall);
                if (Double.IsNaN(theorOptPx) || (theorOptPx < Double.Epsilon))
                {
                    //string msg = String.Format("[DEBUG:{0}] Invalid theorOptPx:{1} for strike:{2}", GetType().Name, theorOptPx, nodeInfo.Strike);
                    //m_context.Log(msg, MessageType.Warning, true);
                    continue;
                }

                if (nodeInfo.PxMode == OptionPxMode.Ask)
                {
                    double doLevel = theorOptPx - m_widthPx * nodeInfo.Pair.Tick;
                    if (realOptPx <= doLevel)
                    {
                        var anchor = (InteractivePointActive)obj.Anchor;

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

                        tmp.IsActive = true;
                        tmp.Tag      = anchor.Tag;
                        tmp.Tooltip  = null; // anchor.Tooltip;
                        int    decimals = (nodeInfo.Security != null) ? nodeInfo.Security.Decimals : 0;
                        string pot      = Math.Abs(theorOptPx - realOptPx).ToString("N" + decimals, CultureInfo.InvariantCulture);
                        tmp.Label        = anchor.Tooltip + " (" + pot + ")";
                        tmp.ValueX       = anchor.ValueX;
                        tmp.ValueY       = anchor.ValueY + m_outlet;
                        tmp.DragableMode = DragableMode.None;
                        tmp.Size         = m_outletSize;

                        //tmp.Color = Colors.White;
                        //tmp.Geometry = m_outletGeometry; // Geometries.Ellipse;

                        obj.ControlPoint1 = tmp;
                    }
                }

                if (nodeInfo.PxMode == OptionPxMode.Bid)
                {
                    double upLevel = theorOptPx + m_widthPx * nodeInfo.Pair.Tick;
                    if (realOptPx >= upLevel)
                    {
                        var anchor = (InteractivePointActive)obj.Anchor;

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

                        tmp.IsActive = true;
                        tmp.Tag      = anchor.Tag;
                        tmp.Tooltip  = null; // anchor.Tooltip;
                        int    decimals = (nodeInfo.Security != null) ? nodeInfo.Security.Decimals : 0;
                        string pot      = Math.Abs(theorOptPx - realOptPx).ToString("N" + decimals, CultureInfo.InvariantCulture);
                        tmp.Label        = anchor.Tooltip + " (" + pot + ")";
                        tmp.ValueX       = anchor.ValueX;
                        tmp.ValueY       = anchor.ValueY - m_outlet;
                        tmp.DragableMode = DragableMode.None;
                        tmp.Size         = m_outletSize;

                        //tmp.Color = Colors.White;
                        //tmp.Geometry = m_outletGeometry; // Geometries.Ellipse;

                        obj.ControlPoint1 = tmp;
                    }
                }
            }

            res.ClickEvent -= InteractiveSplineOnClickEvent;
            res.ClickEvent += InteractiveSplineOnClickEvent;

            m_clickableSeries = res;

            return(res);
        }
Exemple #17
0
        /// <summary>
        /// Метод под флаг TemplateTypes.INTERACTIVESPLINE
        /// </summary>
        public InteractiveSeries Execute(InteractiveSeries smile, IOptionSeries optSer, InteractiveSeries quoteIv, double scaleMult, int barNum)
        {
            if ((smile == null) || (optSer == null))
            {
                return(Constants.EmptySeries);
            }

            int barsCount = m_context.BarsCount;

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

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

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

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

            // 1. Формируем маркеры заявок
            List <InteractiveObject> controlPoints          = new List <InteractiveObject>();
            PositionsManager         posMan                 = PositionsManager.GetManager(m_context);
            IList <PositionsManager.IvTargetInfo> ivTargets = posMan.GetIvTargets(m_isLong, true);

            for (int j = 0; j < ivTargets.Count; j++)
            {
                var ivTarget = ivTargets[j];

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

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

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

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

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

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

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

                double totalQty;
                if (isCall)
                {
                    totalQty = posMan.GetTotalQty(pair.Call.Security, m_context.BarsCount, TotalProfitAlgo.AllPositions, ivTarget.IsLong);
                }
                else
                {
                    totalQty = posMan.GetTotalQty(pair.Put.Security, m_context.BarsCount, TotalProfitAlgo.AllPositions, ivTarget.IsLong);
                }
                double targetQty = Math.Abs(ivTarget.TargetShares) - totalQty;

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

                // Попробуем по-простому?
                tmp.Tag = ivTarget;

                tmp.IsActive     = true;
                tmp.ValueX       = k;
                tmp.ValueY       = sigma;
                tmp.DragableMode = DragableMode.None;
                if (ivTarget.EntryShiftPrice == 0)
                {
                    tmp.Tooltip = String.Format(CultureInfo.InvariantCulture,
                                                " F: {0}\r\n K: {1}; IV: {2:P2}\r\n {3} px {4} rIV {5:P2} @ {6}",
                                                futPx, k, sigma, optionType, theorOptPxBitcoins, ivTarget.EntryIv, targetQty);
                }
                else
                {
                    string shiftStr = (ivTarget.EntryShiftPrice > 0) ? "+" : "-";
                    shiftStr    = shiftStr + Math.Abs(ivTarget.EntryShiftPrice) + "ps";
                    tmp.Tooltip = String.Format(CultureInfo.InvariantCulture,
                                                " F: {0}\r\n K: {1}; IV: {2:P2}\r\n {3} px {4} rIV {5:P2} {6} @ {7}",
                                                futPx, k, sigma, optionType, theorOptPxBitcoins, ivTarget.EntryIv, shiftStr, targetQty);
                }

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

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

                controlPoints.Add(obj);
            }

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

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

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

                m_clickableSeries = res;
            }

            return(res);
        }
Exemple #18
0
        /// <summary>
        /// Получить финансовые параметры опционной позиции (колы и путы на одном страйке суммарно)
        /// </summary>
        /// <param name="smileInfo">улыбка</param>
        /// <param name="strike">страйк</param>
        /// <param name="putBarCount">количество баров для пута</param>
        /// <param name="callBarCount">количество баров для кола</param>
        /// <param name="putPositions">позиции пута</param>
        /// <param name="callPositions">позиции кола</param>
        /// <param name="f">текущая цена БА</param>
        /// <param name="dT">время до экспирации</param>
        /// <param name="cash">денежные затраты на формирование позы (могут быть отрицательными)</param>
        /// <param name="pnl">текущая цена позиции</param>
        /// <returns>true, если всё посчитано без ошибок</returns>
        public static bool TryGetPairPnl(SmileInfo smileInfo, double strike,
                                         int putBarCount, int callBarCount,
                                         IList <IPosition> putPositions, IList <IPosition> callPositions,
                                         double f, double dT, double btcUsdInd,
                                         out CashPnlUsd cashPnlUsd, out CashPnlBtc cashPnlBtc)
        {
            if ((putPositions.Count <= 0) && (callPositions.Count <= 0))
            {
                cashPnlUsd = new CashPnlUsd();
                cashPnlBtc = new CashPnlBtc();
                return(true);
            }

            double?sigma = null;

            if (smileInfo != null)
            {
                double tmp;
                if (smileInfo.ContinuousFunction.TryGetValue(strike, out tmp))
                {
                    sigma = tmp;
                }
            }

            if ((sigma == null) || (!DoubleUtil.IsPositive(sigma.Value)))
            {
                cashPnlUsd = new CashPnlUsd();
                cashPnlBtc = new CashPnlBtc();
                return(false);
            }

            double pnlBtc  = 0;
            double pnlUsd  = 0;
            double cashBtc = 0;
            double cashUsd = 0;

            if (putPositions.Count > 0)
            {
                CashPnlUsd putUsd;
                CashPnlBtc putBtc;
                GetOptPnl(putPositions, putBarCount - 1,
                          f, strike, dT, sigma.Value, 0.0, false, btcUsdInd,
                          out putUsd, out putBtc);
                pnlUsd  += putUsd.PnlUsd;
                cashUsd += putUsd.CashUsd;
                pnlBtc  += putBtc.PnlBtc;
                cashBtc += putBtc.CashBtc;
            }

            if (callPositions.Count > 0)
            {
                CashPnlUsd callUsd;
                CashPnlBtc callBtc;
                GetOptPnl(callPositions, callBarCount - 1,
                          f, strike, dT, sigma.Value, 0.0, true, btcUsdInd,
                          out callUsd, out callBtc);
                pnlUsd  += callUsd.PnlUsd;
                cashUsd += callUsd.CashUsd;
                pnlBtc  += callBtc.PnlBtc;
                cashBtc += callBtc.CashBtc;
            }

            cashPnlUsd = new CashPnlUsd(cashUsd, pnlUsd);
            cashPnlBtc = new CashPnlBtc(cashBtc, pnlBtc);

            return(true);
        }
Exemple #19
0
        public static SmileInfo FromXElement(XElement xel)
        {
            if ((xel == null) || (xel.Name.LocalName != typeof(SmileInfo).FullName))
            {
                return(null);
            }

            double futPx, dT, rate;
            {
                XAttribute xAttr = xel.Attribute("F");
                if ((xAttr == null) || String.IsNullOrWhiteSpace(xAttr.Value) ||
                    (!Double.TryParse(xAttr.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out futPx)))
                {
                    return(null);
                }
            }

            {
                XAttribute xAttr = xel.Attribute("dT");
                if ((xAttr == null) || String.IsNullOrWhiteSpace(xAttr.Value) ||
                    (!Double.TryParse(xAttr.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out dT)))
                {
                    return(null);
                }
            }

            {
                XAttribute xAttr = xel.Attribute("RiskFreeRate");
                if ((xAttr == null) || String.IsNullOrWhiteSpace(xAttr.Value) ||
                    (!Double.TryParse(xAttr.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out rate)))
                {
                    return(null);
                }
            }

            DateTime expiry = new DateTime(), scriptTime = new DateTime();
            {
                XAttribute xAttr = xel.Attribute("Expiry");
                if ((xAttr != null) && (!String.IsNullOrWhiteSpace(xAttr.Value)))
                {
                    DateTime.TryParseExact(xAttr.Value, TimeToExpiry.DateTimeFormat,
                                           CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out expiry);
                }
            }

            {
                XAttribute xAttr = xel.Attribute("ScriptTime");
                if ((xAttr != null) && (!String.IsNullOrWhiteSpace(xAttr.Value)))
                {
                    DateTime.TryParseExact(xAttr.Value, BaseContextHandler.DateTimeFormatWithMs,
                                           CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out scriptTime);
                }
            }

            string baseTicker = null;
            {
                XAttribute xAttr = xel.Attribute("BaseTicker");
                if ((xAttr != null) && (!String.IsNullOrWhiteSpace(xAttr.Value)))
                {
                    baseTicker = xAttr.Value;
                }
            }

            IFunction func = null;
            {
                XElement xCf = (from node in xel.Descendants()
                                where (node.Name.LocalName == "ContinuousFunction")
                                select node).FirstOrDefault();
                if (xCf != null)
                {
                    FunctionDeserializer.TryDeserialize(xCf, out func);
                }
            }

            IFunction funcD1 = null;
            {
                XElement xCfD1 = (from node in xel.Descendants()
                                  where (node.Name.LocalName == "ContinuousFunctionD1")
                                  select node).FirstOrDefault();
                if (xCfD1 != null)
                {
                    FunctionDeserializer.TryDeserialize(xCfD1, out funcD1);
                }
            }

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

            res.F            = futPx;
            res.dT           = dT;
            res.Expiry       = expiry;
            res.ScriptTime   = scriptTime;
            res.RiskFreeRate = rate;
            res.BaseTicker   = baseTicker;

            res.ContinuousFunction   = func;
            res.ContinuousFunctionD1 = funcD1;

            #region PROD-2402 - Парсим дополнительные параметры улыбки
            double ivAtm, skewAtm, shape;
            {
                XAttribute xAttr = xel.Attribute("IvAtm");
                if ((xAttr == null) || String.IsNullOrWhiteSpace(xAttr.Value) ||
                    (!Double.TryParse(xAttr.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out ivAtm)))
                {
                    ivAtm = Double.NaN;
                }
            }

            {
                XAttribute xAttr = xel.Attribute("SkewAtm");
                if ((xAttr == null) || String.IsNullOrWhiteSpace(xAttr.Value) ||
                    (!Double.TryParse(xAttr.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out skewAtm)))
                {
                    skewAtm = Double.NaN;
                }
            }

            {
                XAttribute xAttr = xel.Attribute("Shape");
                if ((xAttr == null) || String.IsNullOrWhiteSpace(xAttr.Value) ||
                    (!Double.TryParse(xAttr.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out shape)))
                {
                    shape = Double.NaN;
                }
            }

            string smileType;
            {
                XAttribute xAttr = xel.Attribute("SmileType");
                if ((xAttr == null) || String.IsNullOrWhiteSpace(xAttr.Value))
                {
                    smileType = null;
                }
                else
                {
                    smileType = xAttr.Value;
                }
            }

            res.IvAtm     = ivAtm;
            res.SkewAtm   = skewAtm;
            res.Shape     = shape;
            res.SmileType = smileType;

            // Если улыбка невалидна, значит все равно толку с 'правильных параметров' нет?
            if (!res.IsValidSmileParams)
            {
                res.IvAtm     = Double.NaN;
                res.SkewAtm   = Double.NaN;
                res.Shape     = Double.NaN;
                res.SmileType = null;
            }
            #endregion PROD-2402 - Парсим дополнительные параметры улыбки

            return(res);
        }
Exemple #20
0
        public double Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, int barNum)
        {
            int barsCount = m_context.BarsCount;

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

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

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

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

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

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

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

            double rawDelta, res;
            double dF = optSer.UnderlyingAsset.Tick;

            IOptionStrikePair[] pairs  = optSer.GetStrikePairs().ToArray();
            PositionsManager    posMan = PositionsManager.GetManager(m_context);

            if (SingleSeriesNumericalDelta.TryEstimateDelta(posMan, optSer, pairs, smile, m_greekAlgo, f, dF, dT, out rawDelta))
            {
                res = rawDelta;
            }
            else
            {
                res = Constants.NaN;
            }

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

            if ((sInfo != null) && (sInfo.ContinuousFunction != null))
            {
                double iv  = sInfo.ContinuousFunction.Value(sInfo.F);
                string msg = String.Format("[{0}] F:{1}; dT:{2};   smile.F:{3}; smile.dT:{4}; smile.IV:{5}",
                                           GetType().Name, f, dT, sInfo.F, sInfo.dT, iv);
                m_context.Log(msg, MessageType.Info, false);
            }

            m_delta.Value = res;
            //context.Log(String.Format("[{0}] Delta: {1}; rawDelta:{2}", MsgId, delta.Value, rawDelta), logColor, true);

            if (m_hedgeDelta)
            {
                #region Hedge logic
                try
                {
                    int rounded = Math.Sign(rawDelta) * ((int)Math.Floor(Math.Abs(rawDelta)));
                    if (rounded == 0)
                    {
                        string msg = String.Format("[{0}] Delta is too low to hedge. Delta: {1}", MsgId, rawDelta);
                        m_context.Log(msg, MessageType.Info, true);
                    }
                    else
                    {
                        int       len = optSer.UnderlyingAsset.Bars.Count;
                        ISecurity sec = (from s in m_context.Runtime.Securities
                                         where (s.SecurityDescription.Equals(optSer.UnderlyingAsset.SecurityDescription))
                                         select s).SingleOrDefault();
                        if (sec == null)
                        {
                            string msg = String.Format("[{0}] There is no security. Symbol: {1}", MsgId, optSer.UnderlyingAsset.Symbol);
                            m_context.Log(msg, MessageType.Warning, true);
                        }
                        else
                        {
                            if (rounded < 0)
                            {
                                string signalName = String.Format("\r\nDelta BUY\r\nF:{0}; dT:{1}; Delta:{2}\r\n", f, dT, rawDelta);
                                m_context.Log(signalName, MessageType.Warning, true);
                                posMan.BuyAtPrice(m_context, sec, Math.Abs(rounded), f, signalName, null);
                            }
                            else if (rounded > 0)
                            {
                                string signalName = String.Format("\r\nDelta SELL\r\nF:{0}; dT:{1}; Delta:+{2}\r\n", f, dT, rawDelta);
                                m_context.Log(signalName, MessageType.Warning, true);
                                posMan.SellAtPrice(m_context, sec, Math.Abs(rounded), f, signalName, null);
                            }
                        }
                    }
                }
                finally
                {
                    m_hedgeDelta = false;
                }
                #endregion Hedge logic
            }

            SetHandlerInitialized(now);

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

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

            double futPx = price;
            double dT    = time;

            if (Double.IsNaN(dT) || (dT < Double.Epsilon) ||
                Double.IsNaN(futPx) || (futPx < Double.Epsilon))
            {
                return(Constants.EmptySeries);
            }

            double width = (SigmaMult * m_sigma * Math.Sqrt(dT)) * futPx;

            List <InteractiveObject> controlPoints = new List <InteractiveObject>();
            int    half = NumControlPoints / 2; // Целочисленное деление!
            double dK   = width / half;

            // Сдвигаю точки, чтобы избежать отрицательных значений
            while ((futPx - half * dK) <= Double.Epsilon)
            {
                half--;
            }
            for (int j = 0; j < NumControlPoints; j++)
            {
                double k = futPx + (j - half) * dK;

                InteractivePointLight ip;
                bool edgePoint = (j == 0) || (j == NumControlPoints - 1);
                if (m_showNodes ||
                    (edgePoint && m_showEdgeLabels)) // На крайние точки повешу Лейблы
                {
                    InteractivePointActive tmp = new InteractivePointActive();

                    tmp.IsActive = m_showNodes || (edgePoint && m_showEdgeLabels);
                    //tmp.DragableMode = DragableMode.None;
                    //tmp.Geometry = Geometries.Rect;
                    //tmp.Color = Colors.DarkOrange;
                    tmp.Tooltip = String.Format(CultureInfo.InvariantCulture,
                                                m_tooltipFormat, k, m_value * Constants.PctMult); // "K:{0}; V:{1:0.00}%"

                    if (edgePoint)
                    {
                        tmp.Label = String.Format(CultureInfo.InvariantCulture,
                                                  m_labelFormat, m_value * Constants.PctMult); // "V:{0:0.00}%"
                    }

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

                ip.Value = new Point(k, m_value);

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

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

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

            SmileInfo info = new SmileInfo();

            info.F            = futPx;
            info.dT           = dT;
            info.RiskFreeRate = 0;

            try
            {
                ConstantFunction spline = new ConstantFunction(m_value, futPx, dT);

                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);
        }
Exemple #22
0
        public InteractiveSeries Execute(double price, InteractiveSeries line, ISecurity sec, int barNum)
        {
            int barsCount = m_context.BarsCount;

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

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

            res.ClickEvent += InteractiveSplineOnClickEvent;

            m_clickableSeries = res;

            double    f        = price;
            double?   sigmaAtm = null;
            SmileInfo sInfo    = line.GetTag <SmileInfo>();

            if ((sInfo != null) && (sInfo.ContinuousFunction != null))
            {
                double tmp;
                if (sInfo.ContinuousFunction.TryGetValue(f, out tmp))
                {
                    if (!Double.IsNaN(tmp))
                    {
                        sigmaAtm = tmp;
                    }
                }
            }

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

            double h = Math.Max(m_minHeight, sigmaAtm.Value * m_offset);

            // Теперь определяем максимальный размах по вертикали
            if (line.ControlPoints.Count > 1)
            {
                var    cps = line.ControlPoints;
                double min = Double.MaxValue, max = Double.MinValue;
                for (int j = 0; j < cps.Count; j++)
                {
                    var    anchor = cps[j].Anchor;
                    double y      = anchor.ValueY;
                    if (y <= min)
                    {
                        min = y;
                    }
                    if (max <= y)
                    {
                        max = y;
                    }
                }

                if ((min < max) && (!Double.IsInfinity(max - min)))
                {
                    h = Math.Max(h, (max - min) * m_offset);
                }
            }

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

            {
                InteractivePointActive ip = new InteractivePointActive();
                ip.IsActive     = true;
                ip.DragableMode = DragableMode.None;
                ip.Geometry     = Geometries.Ellipse; // Geometries.TriangleDown;
                ip.Color        = AlphaColors.Magenta;
                double y = (sigmaAtm.Value - h);
                ip.Value = new Point(f, y);
                string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture);
                // По умолчанию формат 'P2' -- то есть отображение с точностью 2знака,
                // со знаком процента и с автоматическим умножением на 100.
                ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "F:{0}; Y:{1}", f, yStr);

                SmileNodeInfo node = new SmileNodeInfo();
                node.OptPx    = f;
                node.Security = sec;
                node.PxMode   = OptionPxMode.Bid;

                node.Symbol   = node.Security.Symbol;
                node.DSName   = node.Security.SecurityDescription.DSName;
                node.Expired  = node.Security.SecurityDescription.Expired;
                node.FullName = node.Security.SecurityDescription.FullName;

                ip.Tag = node;

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

            {
                InteractivePointActive ip = new InteractivePointActive();
                ip.IsActive     = true;
                ip.DragableMode = DragableMode.None;
                ip.Geometry     = Geometries.Triangle;
                ip.Color        = AlphaColors.GreenYellow;
                double y = (sigmaAtm.Value + h);
                ip.Value = new Point(f, y);
                string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture);
                // По умолчанию формат 'P2' -- то есть отображение с точностью 2знака,
                // со знаком процента и с автоматическим умножением на 100.
                ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "F:{0}; Y:{1}", f, yStr);

                SmileNodeInfo node = new SmileNodeInfo();
                node.OptPx    = f;
                node.Security = sec;
                node.PxMode   = OptionPxMode.Ask;

                node.Symbol   = node.Security.Symbol;
                node.DSName   = node.Security.SecurityDescription.DSName;
                node.Expired  = node.Security.SecurityDescription.Expired;
                node.FullName = node.Security.SecurityDescription.FullName;

                ip.Tag = node;

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

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

            return(res);
        }
        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);
        }
        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);
        }
Exemple #25
0
        /// <summary>
        /// Тета будет иметь размерность 'пункты за год'.
        /// Обычно же опционщики любят смотреть размерность 'пункты за день'.
        /// Поэтому полученное сырое значение ещё надо делить на количество дней в году.
        /// (Эквивалентно умножению на интересующий набег времени для получения дифференциала).
        /// </summary>
        internal static bool TryEstimateTheta(PositionsManager posMan, IOptionStrikePair[] pairs,
                                              InteractiveSeries smile, NumericalGreekAlgo greekAlgo,
                                              double f, double timeToExpiry, double tStep, out double rawTheta)
        {
            rawTheta = Double.NaN;

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

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

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

            double t1 = (timeToExpiry - tStep > Double.Epsilon) ? (timeToExpiry - tStep) : (0.5 * timeToExpiry);
            double cash1 = 0, pnl1 = 0;
            // Флаг того, что ПНЛ по всем инструментам был расчитан верно
            bool pnlIsCorrect1 = true;
            {
                // TODO: фьюч на даёт вклад в тету???
                //SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out cash1, out pnl1);

                //// 1. Изменение положения БА
                //InteractiveSeries actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f);

                SmileInfo actualSmile;
                // 1. Изменение положения БА
                actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f);

                // 2. Изменение времени
                // ВАЖНО: нормальный алгоритм сдвига улыбки во времени будет в платной версии "Пакета Каленковича"
                actualSmile = SingleSeriesProfile.GetSmileAtTime(actualSmile, NumericalGreekAlgo.FrozenSmile, t1);

                for (int j = 0; j < pairs.Length; j++)
                {
                    IOptionStrikePair pair = pairs[j];
                    double            pairPnl, pairCash;
                    //double putPx = FinMath.GetOptionPrice(f, pair.Strike, dT, sigma.Value, 0, false);
                    //SingleSeriesProfile.GetPairPnl(posMan, actualSmile, pair, f, t1, out pairCash, out pairPnl);
                    pnlIsCorrect1 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f, t1, out pairCash, out pairPnl);
                    cash1         += pairCash;
                    pnl1          += pairPnl;
                }
            }

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

            {
                // фьюч на даёт вклад в вегу
                //SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out cash2, out pnl2);

                //// 1. Изменение положения БА
                //InteractiveSeries actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f);

                SmileInfo actualSmile;
                // 1. Изменение положения БА
                actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f);

                // 2. Изменение времени
                // ВАЖНО: нормальный алгоритм сдвига улыбки во времени будет в платной версии "Пакета Каленковича"
                actualSmile = SingleSeriesProfile.GetSmileAtTime(actualSmile, NumericalGreekAlgo.FrozenSmile, t2);

                for (int j = 0; j < pairs.Length; j++)
                {
                    IOptionStrikePair pair = pairs[j];
                    double            pairPnl, pairCash;
                    //double putPx = FinMath.GetOptionPrice(f, pair.Strike, dT, sigma.Value, 0, false);
                    //SingleSeriesProfile.GetPairPnl(posMan, actualSmile, pair, f, t2, out pairCash, out pairPnl);
                    pnlIsCorrect2 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f, t2, out pairCash, out pairPnl);
                    cash2         += pairCash;
                    pnl2          += pairPnl;
                }
            }

            if (pnlIsCorrect1 && pnlIsCorrect2)
            {
                rawTheta = ((cash2 + pnl2) - (cash1 + pnl1)) / (t2 - t1);
                // Переворачиваю тету, чтобы жить в календарном времени
                rawTheta = -rawTheta;
                return(true);
            }
            else
            {
                return(false);
            }
        }
Exemple #26
0
        /// <summary>
        /// Вега будет иметь размерность 'пункты за 100% волатильности'.
        /// Обычно же опционщики любят смотреть размерность 'пункты за 1% волатильности'.
        /// Поэтому полученное сырое значение ещё надо делить на 100%.
        /// (Эквивалентно умножению на интересующий набег волы для получения дифференциала).
        /// </summary>
        internal static bool TryEstimateVega(PositionsManager posMan, IOptionSeries optSer, IOptionStrikePair[] pairs,
                                             InteractiveSeries smile, NumericalGreekAlgo greekAlgo,
                                             double f, double dSigma, double timeToExpiry, out double rawVega)
        {
            rawVega = Double.NaN;

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

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

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

            double cash1 = 0, pnl1 = 0;
            // Флаг того, что ПНЛ по всем инструментам был расчитан верно
            bool pnlIsCorrect1 = true;
            {
                // фьюч на даёт вклад в вегу
                //SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out cash1, out pnl1);

                //InteractiveSeries actualSmile;
                //// 1. Изменение положения БА
                //actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f);

                SmileInfo actualSmile;
                // 1. Изменение положения БА
                actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f);

                for (int j = 0; j < pairs.Length; j++)
                {
                    IOptionStrikePair pair = pairs[j];
                    double            pairPnl, pairCash;
                    //double putPx = FinMath.GetOptionPrice(f, pair.Strike, dT, sigma.Value, 0, false);
                    //SingleSeriesProfile.GetPairPnl(posMan, actualSmile, pair, f, timeToExpiry, out pairCash, out pairPnl);
                    pnlIsCorrect1 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f, timeToExpiry, out pairCash, out pairPnl);
                    cash1         += pairCash;
                    pnl1          += pairPnl;
                }
            }

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

            {
                // фьюч на даёт вклад в вегу
                //SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out cash2, out pnl2);

                //InteractiveSeries actualSmile;
                //// 1. Изменение положения БА
                //actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f);
                //// 2. Подъём улыбки на dSigma
                //actualSmile = SingleSeriesProfile.GetRaisedSmile(actualSmile, greekAlgo, dSigma);

                SmileInfo actualSmile;
                // 1. Изменение положения БА
                actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f);
                // 2. Подъём улыбки на dSigma
                actualSmile = SingleSeriesProfile.GetRaisedSmile(actualSmile, greekAlgo, dSigma);

                for (int j = 0; j < pairs.Length; j++)
                {
                    IOptionStrikePair pair = pairs[j];
                    double            pairPnl, pairCash;
                    //double putPx = FinMath.GetOptionPrice(f, pair.Strike, dT, sigma.Value, 0, false);
                    //SingleSeriesProfile.GetPairPnl(posMan, actualSmile, pair, f, timeToExpiry, out pairCash, out pairPnl);
                    pnlIsCorrect2 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f, timeToExpiry, out pairCash, out pairPnl);
                    cash2         += pairCash;
                    pnl2          += pairPnl;
                }
            }

            if (pnlIsCorrect1 && pnlIsCorrect2)
            {
                // Первая точка совпадает с текущей, поэтому нет деления на 2.
                rawVega = ((cash2 + pnl2) - (cash1 + pnl1)) / dSigma;
                return(true);
            }
            else
            {
                return(false);
            }
        }
Exemple #27
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);
        }
Exemple #28
0
        /// <summary>
        /// Вомма будет иметь размерность 'пункты за 100% волатильности'.
        /// Обычно же опционщики любят смотреть размерность 'пункты за 1% волатильности'.
        /// Поэтому полученное сырое значение ещё надо делить на 100%.
        /// (Эквивалентно умножению на интересующий набег волы для получения дифференциала).
        /// </summary>
        internal static bool TryEstimateVomma(PositionsManager posMan, IOptionSeries optSer, IOptionStrikePair[] pairs,
                                              InteractiveSeries smile, NumericalGreekAlgo greekAlgo,
                                              double f, double dSigma, double timeToExpiry, out double rawVomma)
        {
            rawVomma = Double.NaN;

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

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

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

            double cash1 = 0, pnl1 = 0;
            // Флаг того, что ПНЛ по всем инструментам был расчитан верно
            bool pnlIsCorrect1 = true;
            {
                // фьюч на даёт вклад в вегу
                //SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out cash1, out pnl1);

                //InteractiveSeries actualSmile;
                //// 1. Изменение положения БА
                //actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f);

                SmileInfo actualSmile;
                // 1. Изменение положения БА
                actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f);

                for (int j = 0; j < pairs.Length; j++)
                {
                    IOptionStrikePair pair = pairs[j];
                    double            pairPnl, pairCash;
                    pnlIsCorrect1 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f, timeToExpiry, out pairCash, out pairPnl);
                    cash1         += pairCash;
                    pnl1          += pairPnl;
                }
            }

            double cash2 = 0, pnl2 = 0;
            // Флаг того, что ПНЛ по всем инструментам был расчитан верно
            bool pnlIsCorrect2 = true;
            {
                // фьюч на даёт вклад в вегу
                //SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out cash2, out pnl2);

                //InteractiveSeries actualSmile;
                //// 1. Изменение положения БА
                //actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f);
                //// 2. Подъём улыбки на dSigma
                //actualSmile = SingleSeriesProfile.GetRaisedSmile(actualSmile, greekAlgo, dSigma);

                SmileInfo actualSmile;
                // 1. Изменение положения БА
                actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f);
                // 2. Подъём улыбки на dSigma
                actualSmile = SingleSeriesProfile.GetRaisedSmile(actualSmile, greekAlgo, dSigma);

                for (int j = 0; j < pairs.Length; j++)
                {
                    IOptionStrikePair pair = pairs[j];
                    double            pairPnl, pairCash;
                    pnlIsCorrect2 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f, timeToExpiry, out pairCash, out pairPnl);
                    cash2         += pairCash;
                    pnl2          += pairPnl;
                }
            }

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

            {
                // фьюч на даёт вклад в вегу
                //SingleSeriesProfile.GetBasePnl(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out cash2, out pnl2);

                //InteractiveSeries actualSmile;
                //// 1. Изменение положения БА
                //actualSmile = SingleSeriesProfile.GetActualSmile(smile, greekAlgo, f);
                //// 2. Подъём улыбки на dSigma
                //actualSmile = SingleSeriesProfile.GetRaisedSmile(actualSmile, greekAlgo, dSigma);

                SmileInfo actualSmile;
                // 1. Изменение положения БА
                actualSmile = SingleSeriesProfile.GetActualSmile(sInfo, greekAlgo, f);
                // 2. Подъём улыбки на 2*dSigma
                actualSmile = SingleSeriesProfile.GetRaisedSmile(actualSmile, greekAlgo, 2 * dSigma);

                for (int j = 0; j < pairs.Length; j++)
                {
                    IOptionStrikePair pair = pairs[j];
                    double            pairPnl, pairCash;
                    pnlIsCorrect2 &= SingleSeriesProfile.TryGetPairPnl(posMan, actualSmile, pair, f, timeToExpiry, out pairCash, out pairPnl);
                    cash3         += pairCash;
                    pnl3          += pairPnl;
                }
            }

            if (pnlIsCorrect1 && pnlIsCorrect2 && pnlIsCorrect3)
            {
                // См. Вики случай r=2, N=2: https://ru.wikipedia.org/wiki/Численное_дифференцирование
                // f''(x0) ~= (f0 - 2*f1 + f2)/h/h
                rawVomma = ((cash1 + pnl1) - 2 * (cash2 + pnl2) + (cash3 + pnl3)) / dSigma / dSigma;
                return(true);
            }
            else
            {
                return(false);
            }
        }
        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);
        }
Exemple #30
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);
        }