Example #1
0
        private void InteractiveSplineOnClickEvent(object sender, InteractiveActionEventArgs eventArgs)
        {
            PositionsManager posMan = PositionsManager.GetManager(m_context);

            if (posMan.BlockTrading)
            {
                //string msg = String.Format("[{0}] Trading is blocked. Please, change 'Block Trading' parameter.", m_optionPxMode);
                string msg = RM.GetStringFormat("OptHandlerMsg.PositionsManager.TradingBlocked", m_optionPxMode);
                m_context.Log(msg, MessageType.Info, true);
                return;
            }

            SmileNodeInfo nodeInfo = eventArgs.Point.Tag as SmileNodeInfo;

            if (nodeInfo == null)
            {
                //string msg = String.Format("[{0}] There is no nodeInfo. Quote type: {1}; Strike: {2}", m_optionPxMode);
                string msg = RM.GetStringFormat(CultureInfo.InvariantCulture, "OptHandlerMsg.ThereIsNoNodeInfo",
                                                m_context.Runtime.TradeName, m_optionPxMode, eventArgs.Point.ValueX);
                m_context.Log(msg, MessageType.Error, true);
                return;
            }

            nodeInfo.Qty       = m_qty;
            nodeInfo.ClickTime = DateTime.Now;

            // Передаю событие в PositionsManager дополнив его инфой о количестве лотов
            posMan.InteractiveSplineOnClickEvent(m_context, sender, eventArgs);
        }
        internal static void FillNodeInfo(InteractivePointActive ip,
                                          double f, double dT, IOptionStrikePair sInfo,
                                          StrikeType optionType, OptionPxMode optPxMode,
                                          double optSigma, bool returnPct, bool isVisiblePoints, double scaleMult, double riskfreeRatePct)
        {
            if (optionType == StrikeType.Any)
            {
                throw new ArgumentException("Option type 'Any' is not supported.", "optionType");
            }

            bool   isCall = (optionType == StrikeType.Call);
            double optPx  = FinMath.GetOptionPrice(f, sInfo.Strike, dT, optSigma, 0, isCall);

            // Сразу переводим котировку из баксов в битки
            optPx /= scaleMult;

            // ReSharper disable once UseObjectOrCollectionInitializer
            SmileNodeInfo nodeInfo = new SmileNodeInfo();

            nodeInfo.F            = f;
            nodeInfo.dT           = dT;
            nodeInfo.RiskFreeRate = riskfreeRatePct;
            nodeInfo.Sigma        = returnPct ? optSigma * Constants.PctMult : optSigma;
            nodeInfo.OptPx        = optPx;
            nodeInfo.Strike       = sInfo.Strike;
            // Сюда мы приходим когда хотим торговать, поэтому обращение к Security уместно
            nodeInfo.Security   = (optionType == StrikeType.Put) ? sInfo.Put.Security : sInfo.Call.Security;
            nodeInfo.PxMode     = optPxMode;
            nodeInfo.OptionType = optionType;
            nodeInfo.Pair       = sInfo;
            //nodeInfo.ScriptTime = optTime;
            nodeInfo.CalendarTime = DateTime.Now;

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

            ip.Tag = nodeInfo;
            //ip.Color = Colors.Yellow;
            ip.IsActive     = isVisiblePoints;
            ip.Geometry     = Geometries.Ellipse;
            ip.DragableMode = DragableMode.None;
            //ip.Value = new System.Windows.Point(sInfo.Strike, nodeInfo.Sigma);
            // Поскольку цены скорее всего расчетные, показываю на 1 знак болььше, чем объявлена точность в инструменте
            int    decim    = Math.Max(0, nodeInfo.Security.Decimals + 1);
            string optPxStr = optPx.ToString("N" + decim, CultureInfo.InvariantCulture);

            ip.Tooltip = String.Format(CultureInfo.InvariantCulture,
                                       " K: {0}; IV: {1:#0.00}%\r\n   {2} px {3}",
                                       sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx);
        }
Example #3
0
        protected static void FillNodeInfo(InteractivePointLight ip,
                                           double f, double dT, IOptionStrikePair sInfo,
                                           StrikeType optionType, OptionPxMode optPxMode,
                                           double optSigma, bool returnPct, double pctRate)
        {
            if (optionType == StrikeType.Any)
            {
                throw new ArgumentException("Option type 'Any' is not supported.", "optionType");
            }

            bool   isCall = (optionType == StrikeType.Call);
            double optPx  = FinMath.GetOptionPrice(f, sInfo.Strike, dT, optSigma, pctRate, isCall);

            // ReSharper disable once UseObjectOrCollectionInitializer
            SmileNodeInfo nodeInfo = new SmileNodeInfo();

            nodeInfo.F            = f;
            nodeInfo.dT           = dT;
            nodeInfo.RiskFreeRate = pctRate;
            nodeInfo.Sigma        = returnPct ? optSigma * Constants.PctMult : optSigma;
            nodeInfo.OptPx        = optPx;
            nodeInfo.Strike       = sInfo.Strike;
            nodeInfo.Security     = (optionType == StrikeType.Put) ? sInfo.Put.Security : sInfo.Call.Security;
            nodeInfo.PxMode       = optPxMode;
            nodeInfo.OptionType   = optionType;
            nodeInfo.Pair         = sInfo;
            //nodeInfo.ScriptTime = optTime;
            nodeInfo.CalendarTime = DateTime.Now;

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

            ip.Tag   = nodeInfo;
            ip.Value = new Point(sInfo.Strike, nodeInfo.Sigma);

            if (ip is InteractivePointActive)
            {
                InteractivePointActive ipa = (InteractivePointActive)ip;
                ipa.Tooltip = String.Format("K:{0}; IV:{1:#0.00}%\r\n{2} {3} @ {4}",
                                            sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx, DefaultOptQty);
            }
        }
Example #4
0
        protected static void FillNodeInfo(InteractivePointActive ip,
                                           double f, double dT, IOptionStrikePair sInfo,
                                           StrikeType optionType, OptionPxMode optPxMode,
                                           double optSigma, bool returnPct, bool isVisiblePoints)
        {
            if (optionType == StrikeType.Any)
            {
                throw new ArgumentException("Option type 'Any' is not supported.", "optionType");
            }

            bool   isCall = (optionType == StrikeType.Call);
            double optPx  = FinMath.GetOptionPrice(f, sInfo.Strike, dT, optSigma, 0, isCall);

            SmileNodeInfo nodeInfo = new SmileNodeInfo();

            nodeInfo.F          = f;
            nodeInfo.dT         = dT;
            nodeInfo.Sigma      = returnPct ? optSigma * Constants.PctMult : optSigma;
            nodeInfo.OptPx      = optPx;
            nodeInfo.Strike     = sInfo.Strike;
            nodeInfo.Security   = (optionType == StrikeType.Put) ? sInfo.Put.Security : sInfo.Call.Security;
            nodeInfo.PxMode     = optPxMode;
            nodeInfo.OptionType = optionType;
            nodeInfo.Pair       = sInfo;
            //nodeInfo.ScriptTime = optTime;
            nodeInfo.CalendarTime = DateTime.Now;

            nodeInfo.Symbol  = nodeInfo.Security.Symbol;
            nodeInfo.Expired = nodeInfo.Security.SecurityDescription.Expired;

            ip.Tag          = nodeInfo;
            ip.Color        = AlphaColors.Yellow;
            ip.IsActive     = isVisiblePoints;
            ip.Geometry     = Geometries.Ellipse;
            ip.DragableMode = DragableMode.None;
            //ip.Value = new System.Windows.Point(sInfo.Strike, nodeInfo.Sigma);
            ip.Tooltip = String.Format("F:{0}; K:{1}; IV:{2:#0.00}%\r\n{3} {4} @ {5}",
                                       f, sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx, DefaultOptQty);
        }
Example #5
0
        private void InteractiveSplineOnQuoteIvEvent(object sender, InteractiveActionEventArgs eventArgs)
        {
            OptionPxMode pxMode = (m_qty > 0) ? OptionPxMode.Ask : OptionPxMode.Bid;

            PositionsManager posMan = PositionsManager.GetManager(m_context);

            if (posMan.BlockTrading)
            {
                //string msg = String.Format("[{0}] Trading is blocked. Please, change 'Block Trading' parameter.", m_optionPxMode);
                string msg = RM.GetStringFormat("OptHandlerMsg.PositionsManager.TradingBlocked", pxMode);
                m_context.Log(msg, MessageType.Info, true);
                return;
            }

            InteractivePointActive tmp = eventArgs.Point;

            if ((tmp == null) || (tmp.IsActive == null) || (!tmp.IsActive.Value) ||
                DoubleUtil.IsZero(m_qty))
            {
                //string msg = String.Format("[{0}] Unable to get direction of the order. Qty:{1}", pxMode, m_qty);
                string msg = RM.GetStringFormat("OptHandlerMsg.PositionsManager.UndefinedOrderDirection", pxMode, m_qty);
                m_context.Log(msg, MessageType.Error, true);
                return;
            }

            SmileNodeInfo nodeInfo = tmp.Tag as SmileNodeInfo;

            if (nodeInfo == null)
            {
                //string msg = String.Format("[{0}] There is no nodeInfo. Quote type: {1}; Strike: {2}", pxMode);
                string msg = RM.GetStringFormat(CultureInfo.InvariantCulture, "OptHandlerMsg.ThereIsNoNodeInfo",
                                                m_context.Runtime.TradeName, pxMode, eventArgs.Point.ValueX);
                m_context.Log(msg, MessageType.Error, true);
                return;
            }

            {
                string msg = String.Format(CultureInfo.InvariantCulture, "[{0}.ClickEvent] Strike: {1}", GetType().Name, tmp.ValueX);
                m_context.Log(msg, MessageType.Info, false);
            }

            nodeInfo.OptPx      = m_shiftIv;
            nodeInfo.ShiftOptPx = m_shiftPriceStep;
            nodeInfo.ClickTime  = DateTime.Now;
            nodeInfo.PxMode     = pxMode;

            // [2015-10-02] Подписка на данный инструмент, чтобы он появился в коллекции Context.Runtime.Securities
            var candids = (from s in Context.Runtime.Securities
                           where s.SecurityDescription.Name.Equals(nodeInfo.Symbol, StringComparison.InvariantCultureIgnoreCase)
                           select s).ToList();

            candids = (from s in candids
                       where s.SecurityDescription.DSName.Equals(nodeInfo.DSName, StringComparison.InvariantCultureIgnoreCase)
                       select s).ToList();
            ISecurity testSec = (from s in candids
                                 let secDesc = s.SecurityDescription
                                               where secDesc.FullName.Equals(nodeInfo.FullName, StringComparison.InvariantCultureIgnoreCase)
                                               select s).SingleOrDefault();

            if ((testSec == null) && (nodeInfo.Security != null))
            {
                ISecurity sec = nodeInfo.Security;
                int       bc  = sec.Bars.Count;
                string    msg = String.Format("[{0}] There is security DsName: {1}; Symbol: {2}; Security: {3} with {4} bars available.",
                                              pxMode, nodeInfo.DSName, nodeInfo.Symbol, nodeInfo.FullName, bc);
                Context.Log(msg, MessageType.Info, false);

                // Пересчитываю целочисленный параметр Qty в фактические лоты конкретного инструмента
                double actQty = Math.Abs(m_qty * sec.LotTick); // Модуль, потому что тут направление передается через PxMode
                nodeInfo.Qty = actQty;
            }
            else if ((nodeInfo.Pair != null) && (nodeInfo.Pair.Put != null))
            {
                // Аварийная ветка
                // Пересчитываю целочисленный параметр Qty в фактические лоты конкретного инструмента
                double actQty = Math.Abs(m_qty * nodeInfo.Pair.Put.LotTick); // Модуль, потому что тут направление передается через PxMode
                nodeInfo.Qty = actQty;
            }
            else
            {
                // Аварийная ветка
                // Не могу пересчитать целочисленный параметр Qty в фактические лоты конкретного инструмента!
                //double actQty = Math.Abs(m_qty * nodeInfo.Pair.Put.LotTick);
                nodeInfo.Qty = Math.Abs(m_qty); // Модуль, потому что тут направление передается через PxMode

                string msg = String.Format(CultureInfo.InvariantCulture, "[{0}.ClickEvent] LotTick will be set to 1.", GetType().Name);
                m_context.Log(msg, MessageType.Warning, false);
            }

            // Не страшно, если сюда пойдет null - posMan потом сам найдет нужный опцион
            nodeInfo.Security = testSec;

            // Передаю событие в PositionsManager
            //posMan.InteractiveSplineOnClickEvent(m_context, sender, eventArgs);
            posMan.InteractiveSplineOnQuoteIvEvent(m_context, sender, eventArgs);
        }
Example #6
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);
        }
Example #7
0
        private void InteractiveSplineOnClickEvent(object sender, InteractiveActionEventArgs eventArgs)
        {
            PositionsManager posMan = PositionsManager.GetManager(m_context);

            if (posMan.BlockTrading)
            {
                //string msg = String.Format("[{0}] Trading is blocked. Please, change 'Block Trading' parameter.", MsgId);
                string msg = RM.GetStringFormat("OptHandlerMsg.PositionsManager.TradingBlocked", MsgId);
                m_context.Log(msg, MessageType.Warning, true);
                return;
            }

            if (m_qty == 0)
            {
                //string msg = String.Format("[{0}] Trading is blocked for zero quantity. Qty: {1}", MsgId, qty);
                string msg = RM.GetStringFormat("OptHandlerMsg.PositionsManager.TradingBlockedForZeroQty", MsgId, m_qty);
                m_context.Log(msg, MessageType.Warning, true);
                return;
            }
            else if (m_qty < 0)
            {
                // GLSP-252 - Запрет на совершение сделок при отрицательном Qty
                //string msg = String.Format("[{0}] Trading is blocked for negative quantity. Qty: {1}", MsgId, qty);
                string msg = RM.GetStringFormat("OptHandlerMsg.PositionsManager.TradingBlockedForNegativeQty", MsgId, m_qty);
                m_context.Log(msg, MessageType.Warning, true);
                return;
            }

            SmileNodeInfo nodeInfo = eventArgs.Point.Tag as SmileNodeInfo;

            if (nodeInfo == null)
            {
                //string msg = String.Format("[{0}] There is no nodeInfo. Base asset price: {1}", MsgId);
                string msg = RM.GetStringFormat(CultureInfo.InvariantCulture, "OptHandlerMsg.CurrentFutPx.ThereIsNoNodeInfo",
                                                MsgId, eventArgs.Point.ValueX);
                m_context.Log(msg, MessageType.Error, true);
                return;
            }

            nodeInfo.ClickTime = DateTime.Now;

            // [2015-10-02] Подписка на данный инструмент, чтобы он появился в коллекции Context.Runtime.Securities
            ISecurity testSec = (from s in m_context.Runtime.Securities
                                 let secDesc = s.SecurityDescription
                                               where secDesc.FullName.Equals(nodeInfo.FullName, StringComparison.InvariantCultureIgnoreCase) &&
                                               secDesc.DSName.Equals(nodeInfo.DSName, StringComparison.InvariantCultureIgnoreCase) &&
                                               secDesc.Name.Equals(nodeInfo.Symbol, StringComparison.InvariantCultureIgnoreCase)
                                               select s).SingleOrDefault();

            if (testSec == null)
            {
                ISecurity sec = nodeInfo.Security;
                int       bc  = sec.Bars.Count;
                string    msg = String.Format("[{0}] There is security DsName: {1}; Symbol: {2}; Security: {3} with {4} bars available.",
                                              nodeInfo.PxMode, nodeInfo.DSName, nodeInfo.Symbol, nodeInfo.FullName, bc);
                Context.Log(msg, MessageType.Info, false);

                // Пересчитываю целочисленный параметр Qty в фактические лоты конкретного инструмента
                double actQty = m_qty * sec.LotTick; // Здесь нет модуля, потому что направление сделки несет в себе знак Qty
                nodeInfo.Qty = actQty;
            }
            else if ((nodeInfo.Security != null) && (nodeInfo.Security != null))
            {
                // Аварийная ветка
                // Пересчитываю целочисленный параметр Qty в фактические лоты конкретного инструмента
                double actQty = m_qty * nodeInfo.Security.LotTick; // Здесь нет модуля, потому что направление сделки несет в себе знак Qty
                nodeInfo.Qty = actQty;
            }
            else
            {
                // Аварийная ветка
                // Не могу пересчитать целочисленный параметр Qty в фактические лоты конкретного инструмента!
                nodeInfo.Qty = m_qty; // Здесь нет модуля, потому что направление сделки несет в себе знак Qty

                string msg = String.Format(CultureInfo.InvariantCulture, "[{0}.ClickEvent] LotTick is set to 1.", GetType().Name);
                m_context.Log(msg, MessageType.Warning, false);
            }

            // Передаю событие в PositionsManager дополнив его инфой о количестве лотов
            posMan.InteractiveSplineOnClickEvent(m_context, sender, eventArgs);
        }
Example #8
0
        public InteractiveSeries Execute(double price, ISecurity sec, int barNum)
        {
            InteractiveSeries res = new InteractiveSeries();
            int barsCount         = m_context.BarsCount;

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

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

            m_clickableSeries = res;

            double f = price;

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

            {
                InteractivePointActive ip = new InteractivePointActive(f, m_sigmaLow);
                ip.IsActive     = true;
                ip.DragableMode = DragableMode.None;
                ip.Geometry     = Geometries.Ellipse;
                ip.Color        = AlphaColors.BlueViolet;
                ip.Tooltip      = String.Format("F:{0}; Y:{1:0.00}%", f, m_sigmaLow * Constants.PctMult);

                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(f, m_sigmaHigh);
                ip.IsActive     = true;
                ip.DragableMode = DragableMode.None;
                ip.Geometry     = Geometries.Triangle;
                ip.Color        = AlphaColors.Green;
                ip.Tooltip      = String.Format("F:{0}; Y:{1:0.00}%", f, m_sigmaHigh * Constants.PctMult);

                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);
        }
Example #9
0
        private void InteractiveSplineOnClickEvent(object sender, InteractiveActionEventArgs eventArgs)
        {
            // [2016-03-01] PROD-2452: Запрещаю торговлю по узлу, если нет "подсветки"
            {
                InteractiveObject obj = eventArgs.InteractiveObject;
                if ((obj == null) || (obj.Anchor == null) || (obj.ControlPoint1 == null) ||
                    (!(obj.Anchor is InteractivePointActive)) || (!(obj.ControlPoint1 is InteractivePointActive)))
                {
                    string msg = String.Format("[{0}.ClickEvent] Denied #1", GetType().Name);
                    m_context.Log(msg, MessageType.Warning, false);
                    return;
                }

                var cp1 = (InteractivePointActive)obj.ControlPoint1;
                // PROD-4967 - Необязательно проверять активность якоря.
                // Потому что эта настройка делается на более позднем этапе в момент создания графического объекта
                // методом smilePanePane.AddList("SmilePane_pane_TradeAsks_chart", <...>)
                //var anchor = (InteractivePointActive)obj.Anchor;
                //if ((anchor.IsActive == null) || (!anchor.IsActive.Value) ||
                if ((cp1.IsActive == null) || (!cp1.IsActive.Value))
                {
                    string msg = String.Format("[{0}.ClickEvent] Denied #3 (ControlPoint1 is not active)", GetType().Name);
                    m_context.Log(msg, MessageType.Warning, false);
                    return;
                }
            }

            PositionsManager posMan = PositionsManager.GetManager(m_context);

            if (posMan.BlockTrading)
            {
                //string msg = String.Format("[{0}] Trading is blocked. Please, change 'Block Trading' parameter.", m_optionPxMode);
                string msg = String.Format(RM.GetString("OptHandlerMsg.PositionsManager.TradingBlocked"), m_optionPxMode);
                m_context.Log(msg, MessageType.Info, true);
                return;
            }

            // Здесь нет проверки знака m_qty, потому что в данном кубике мы действительно можем и продавать и покупать

            SmileNodeInfo nodeInfo = eventArgs.Point.Tag as SmileNodeInfo;

            if (nodeInfo == null)
            {
                //string msg = String.Format("[{0}] There is no nodeInfo. Quote type: {1}; Strike: {2}",
                string msg = RM.GetStringFormat(CultureInfo.InvariantCulture, "OptHandlerMsg.ThereIsNoNodeInfo",
                                                m_context.Runtime.TradeName, m_optionPxMode, eventArgs.Point.ValueX);
                m_context.Log(msg, MessageType.Error, true);
                return;
            }

            {
                string msg = String.Format(CultureInfo.InvariantCulture, "[{0}.ClickEvent] Strike: {1}; Security: {2}",
                                           GetType().Name, eventArgs.Point.ValueX, nodeInfo.FullName);
                m_context.Log(msg, MessageType.Info, false);
            }

            nodeInfo.ClickTime = DateTime.Now;

            // [2015-10-02] Подписка на данный инструмент, чтобы он появился в коллекции Context.Runtime.Securities
            ISecurity testSec = (from s in Context.Runtime.Securities
                                 let secDesc = s.SecurityDescription
                                               where secDesc.FullName.Equals(nodeInfo.FullName, StringComparison.InvariantCultureIgnoreCase) &&
                                               secDesc.DSName.Equals(nodeInfo.DSName, StringComparison.InvariantCultureIgnoreCase) &&
                                               secDesc.Name.Equals(nodeInfo.Symbol, StringComparison.InvariantCultureIgnoreCase)
                                               select s).SingleOrDefault();

            if (testSec == null)
            {
                ISecurity sec = nodeInfo.Security;
                int       bc  = sec.Bars.Count;
                string    msg = String.Format("[{0}] There is security DsName: {1}; Symbol: {2}; Security: {3} with {4} bars available.",
                                              m_optionPxMode, nodeInfo.DSName, nodeInfo.Symbol, nodeInfo.FullName, bc);
                Context.Log(msg, MessageType.Info, false);

                // Пересчитываю целочисленный параметр Qty в фактические лоты конкретного инструмента
                double actQty = m_qty * sec.LotTick; // Здесь нет модуля, потому что направление сделки несет в себе знак Qty
                nodeInfo.Qty = actQty;
            }
            else if ((nodeInfo.Pair != null) && (nodeInfo.Pair.Put != null))
            {
                // Аварийная ветка
                // Пересчитываю целочисленный параметр Qty в фактические лоты конкретного инструмента
                double actQty = m_qty * nodeInfo.Pair.Put.LotTick; // Здесь нет модуля, потому что направление сделки несет в себе знак Qty
                nodeInfo.Qty = actQty;
            }
            else
            {
                // Аварийная ветка
                // Не могу пересчитать целочисленный параметр Qty в фактические лоты конкретного инструмента!
                //double actQty = Math.Abs(m_qty * nodeInfo.Pair.Put.LotTick);
                nodeInfo.Qty = m_qty; // Здесь нет модуля, потому что направление сделки несет в себе знак Qty

                string msg = String.Format(CultureInfo.InvariantCulture, "[{0}.ClickEvent] LotTick is set to 1.", GetType().Name);
                m_context.Log(msg, MessageType.Warning, false);
            }

            // Передаю событие в PositionsManager дополнив его инфой о количестве лотов
            posMan.InteractiveSplineOnClickEvent(m_context, sender, eventArgs);
        }
Example #10
0
        /// <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);
        }
Example #11
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);
        }
Example #12
0
        private void RouteOnClickEvents(List <InteractiveActionEventArgs> eventArgs)
        {
            if (eventArgs == null)
            {
                return;
            }

            int argLen = eventArgs.Count;
            PositionsManager posMan = PositionsManager.GetManager(m_context);

            if (posMan.BlockTrading && (argLen > 0))
            {
                eventArgs.Clear();
                string msg = String.Format("[{0}] ERROR! Trading is blocked. Should NOT be here. All events were removed.", m_optionPxMode);
                m_context.Log(msg, MessageType.Warning, true);
                return;
            }

            bool recalc = false;

            for (int j = argLen - 1; j >= 0; j--)
            {
                InteractiveActionEventArgs eventArg = eventArgs[j];
                eventArgs.RemoveAt(j);

                try
                {
                    SmileNodeInfo nodeInfo = eventArg.Point.Tag as SmileNodeInfo;
                    if (nodeInfo == null)
                    {
                        //string msg = String.Format("[{0}] There is no nodeInfo. Quote type: {1}; Strike: {2}", m_optionPxMode);
                        string msg = RM.GetStringFormat(CultureInfo.InvariantCulture, "OptHandlerMsg.ThereIsNoNodeInfo",
                                                        m_context.Runtime.TradeName, m_optionPxMode, eventArg.Point.ValueX);
                        m_context.Log(msg, MessageType.Error, true);
                        continue;
                    }

                    if ((!posMan.UseVirtualPositions) && nodeInfo.Expired)
                    {
                        string msg = String.Format("[{0}] Security {1} is already expired. Expiry: {2}",
                                                   m_optionPxMode, nodeInfo.Symbol, nodeInfo.Security.SecurityDescription.ExpirationDate);
                        m_context.Log(msg, MessageType.Error, true);
                        continue;
                    }

                    ISecurity sec = (from s in m_context.Runtime.Securities
                                     where (s.Symbol.Equals(nodeInfo.Symbol, StringComparison.InvariantCultureIgnoreCase)) &&
                                     (String.IsNullOrWhiteSpace(nodeInfo.DSName) || (s.SecurityDescription.DSName == nodeInfo.DSName)) &&
                                     (String.IsNullOrWhiteSpace(nodeInfo.FullName) || (s.SecurityDescription.FullName == nodeInfo.FullName))
                                     select s).SingleOrDefault();
                    if (sec == null)
                    {
                        string msg = String.Format("[{0}] There is no security. Symbol: {1}", m_optionPxMode, nodeInfo.Symbol);
                        m_context.Log(msg, MessageType.Error, true);
                        continue;
                    }

                    double actualPx = nodeInfo.OptPx;
                    if (m_optionType != StrikeType.Any)
                    {
                        // Заменяю инструмент в соответствии с настройками кубика
                        switch (m_optionType)
                        {
                        case StrikeType.Put:
                            if (sec.SecurityDescription.ActiveType != DataSource.ActiveType.OptionPut)
                            {
                                var oldCall = sec;
                                sec = nodeInfo.Pair.Put.Security;
                                // Теперь еще нужно заменить ЦЕНУ. Проще всего это сделать через паритет.
                                // C - P = F - K ==> P = C + K - F
                                double putPx = nodeInfo.OptPx + nodeInfo.Strike - nodeInfo.F;
                                // Но не меньше 1 ш.ц.!
                                actualPx = Math.Max(putPx, sec.Tick);
                                string txt = String.Format(CultureInfo.InvariantCulture,
                                                           "[{0}] Symbol '{1}' has been replaced to '{2}' and changed price from {3} to {4}",
                                                           m_optionPxMode, nodeInfo.Symbol, sec.Symbol, nodeInfo.OptPx, actualPx);
                                m_context.Log(txt, MessageType.Warning, true);
                            }
                            break;

                        case StrikeType.Call:
                            if (sec.SecurityDescription.ActiveType != DataSource.ActiveType.OptionCall)
                            {
                                var oldPut = sec;
                                sec = nodeInfo.Pair.Call.Security;
                                // Теперь еще нужно заменить ЦЕНУ. Проще всего это сделать через паритет.
                                // C - P = F - K ==> C = P + F - K
                                double callPx = nodeInfo.OptPx + nodeInfo.F - nodeInfo.Strike;
                                // Но не меньше 1 ш.ц.!
                                actualPx = Math.Max(callPx, sec.Tick);
                                string txt = String.Format(CultureInfo.InvariantCulture,
                                                           "[{0}] Symbol '{1}' has been replaced to '{2}' and changed price from {3} to {4}",
                                                           m_optionPxMode, nodeInfo.Symbol, sec.Symbol, nodeInfo.OptPx, actualPx);
                                m_context.Log(txt, MessageType.Warning, true);
                            }
                            break;
                        }
                    }

                    int len = sec.Bars.Count;
                    // Валидирую наличие правильной котировки
                    if (sec.FinInfo != null)
                    {
                        Debug.WriteLine("AskPx: " + sec.FinInfo.Ask);
                        Debug.WriteLine("BidPx: " + sec.FinInfo.Bid);
                    }

                    recalc = true;
                    if ((m_optionPxMode == OptionPxMode.Ask) && (m_qty > 0) ||
                        (m_optionPxMode == OptionPxMode.Bid) && (m_qty < 0))
                    {
                        string signalName = "\r\nLeft-Click BUY \r\n" + eventArg.Point.Tooltip + "\r\n";
                        posMan.BuyAtPrice(m_context, sec, Math.Abs(m_qty), actualPx, signalName, null);
                    }
                    else if ((m_optionPxMode == OptionPxMode.Bid) && (m_qty > 0) ||
                             (m_optionPxMode == OptionPxMode.Ask) && (m_qty < 0))
                    {
                        string signalName = "\r\nLeft-Click SELL \r\n" + eventArg.Point.Tooltip + "\r\n";
                        posMan.SellAtPrice(m_context, sec, Math.Abs(m_qty), actualPx, signalName, null);
                    }
                    else if (m_optionPxMode == OptionPxMode.Mid)
                    {
                        string msg = String.Format("[{0}] OptionPxMode.Mid is not implemented.", m_optionPxMode);
                        m_context.Log(msg, MessageType.Warning, true);
                    }
                    else if (m_qty == 0)
                    {
                        string msg = String.Format("[{0}] Qty: {1}. Trading is blocked.", m_optionPxMode, m_qty);
                        m_context.Log(msg, MessageType.Warning, true);
                    }
                }
                catch (Exception ex)
                {
                    string msg = String.Format("[{0}] {1} in RouteOnClickEvents. Message: {2}\r\n{3}",
                                               m_optionPxMode, ex.GetType().FullName, ex.Message, ex);
                    m_context.Log(msg, MessageType.Warning, true);
                }
            }

            if (recalc)
            {
                m_context.Recalc(RecalcReasons.ChartTrading, null);
            }
        }
Example #13
0
        public InteractiveSeries Execute(InteractiveSeries smile, int barNum)
        {
            if (m_transformation == SmileTransformation.None)
            {
                if (DoubleUtil.IsZero(m_shiftIv))
                {
                    return(smile);
                }
            }

            int barsCount = m_context.BarsCount;

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

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

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

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

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

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

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

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

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

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

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

                    InteractiveObject oldObj = cps[j];

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                        InteractiveObject newObj = new InteractiveObject(ip);

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

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

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

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

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

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

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

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

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

            return(res);
        }
Example #14
0
        private void RouteOnClickEvents(List <InteractiveActionEventArgs> eventArgs)
        {
            if (eventArgs == null)
            {
                return;
            }

            int argLen = eventArgs.Count;
            PositionsManager posMan = PositionsManager.GetManager(m_context);

            if (posMan.BlockTrading && (argLen > 0))
            {
                eventArgs.Clear();
                string msg = String.Format("[{0}] ERROR! Trading is blocked. Should NOT be here. All events were removed.", m_optionPxMode);
                m_context.Log(msg, MessageType.Warning, true);
                return;
            }

            bool recalc = false;

            for (int j = argLen - 1; j >= 0; j--)
            {
                InteractiveActionEventArgs eventArg = eventArgs[j];
                eventArgs.RemoveAt(j);

                try
                {
                    SmileNodeInfo nodeInfo = eventArg.Point.Tag as SmileNodeInfo;
                    if (nodeInfo == null)
                    {
                        //string msg = String.Format("[{0}] There is no nodeInfo. Quote type: {1}; Strike: {2}", m_optionPxMode);
                        string msg = RM.GetStringFormat(CultureInfo.InvariantCulture, "OptHandlerMsg.ThereIsNoNodeInfo",
                                                        m_context.Runtime.TradeName, m_optionPxMode, eventArg.Point.ValueX);
                        m_context.Log(msg, MessageType.Error, true);
                        continue;
                    }

                    if ((!posMan.UseVirtualPositions) && nodeInfo.Expired)
                    {
                        string msg = String.Format("[{0}] Security {1} is already expired. Expiry: {2}",
                                                   m_optionPxMode, nodeInfo.Symbol, nodeInfo.Security.SecurityDescription.ExpirationDate);
                        m_context.Log(msg, MessageType.Error, true);
                        continue;
                    }

                    ISecurity sec = (from s in m_context.Runtime.Securities
                                     where (s.Symbol.Equals(nodeInfo.Symbol, StringComparison.InvariantCultureIgnoreCase)) &&
                                     (String.IsNullOrWhiteSpace(nodeInfo.DSName) || (s.SecurityDescription.DSName == nodeInfo.DSName)) &&
                                     (String.IsNullOrWhiteSpace(nodeInfo.FullName) || (s.SecurityDescription.FullName == nodeInfo.FullName))
                                     select s).SingleOrDefault();
                    if (sec == null)
                    {
                        string msg = String.Format("[{0}] There is no security. Symbol: {1}", m_optionPxMode, nodeInfo.Symbol);
                        m_context.Log(msg, MessageType.Error, true);
                        continue;
                    }

                    int len = sec.Bars.Count;
                    // Валидирую наличие правильной котировки
                    if (sec.FinInfo != null)
                    {
                        Debug.WriteLine("AskPx: " + sec.FinInfo.Ask);
                        Debug.WriteLine("BidPx: " + sec.FinInfo.Bid);
                    }

                    recalc = true;
                    if ((m_optionPxMode == OptionPxMode.Ask) && (m_qty > 0) ||
                        (m_optionPxMode == OptionPxMode.Bid) && (m_qty < 0))
                    {
                        string signalName = "\r\nLeft-Click BUY \r\n" + eventArg.Point.Tooltip + "\r\n";
                        posMan.BuyAtPrice(m_context, sec, Math.Abs(m_qty), nodeInfo.OptPx, signalName, null);
                    }
                    else if ((m_optionPxMode == OptionPxMode.Bid) && (m_qty > 0) ||
                             (m_optionPxMode == OptionPxMode.Ask) && (m_qty < 0))
                    {
                        string signalName = "\r\nLeft-Click SELL \r\n" + eventArg.Point.Tooltip + "\r\n";
                        posMan.SellAtPrice(m_context, sec, Math.Abs(m_qty), nodeInfo.OptPx, signalName, null);
                    }
                    else if (m_optionPxMode == OptionPxMode.Mid)
                    {
                        string msg = String.Format("[{0}] OptionPxMode.Mid is not implemented.", m_optionPxMode);
                        m_context.Log(msg, MessageType.Alert, true);
                    }
                    else if (m_qty == 0)
                    {
                        string msg = String.Format("[{0}] Qty: {1}. Trading is blocked.", m_optionPxMode, m_qty);
                        m_context.Log(msg, MessageType.Warning, true);
                    }
                }
                catch (Exception ex)
                {
                    string msg = String.Format("[{0}] {1} in RouteOnClickEvents. Message: {2}\r\n{3}",
                                               m_optionPxMode, ex.GetType().FullName, ex.Message, ex);
                    m_context.Log(msg, MessageType.Alert, true);
                }
            }

            if (recalc)
            {
                m_context.Recalc();
            }
        }
Example #15
0
        internal static void FillNodeInfo(InteractivePointActive ip,
                                          double f, double dT, IOptionStrikePair sInfo,
                                          StrikeType optionType, OptionPxMode optPxMode,
                                          double optPx, double optQty, double optSigma, DateTime optTime, bool returnPct, double riskfreeRatePct)
        {
            if (optionType == StrikeType.Any)
            {
                throw new ArgumentException("Option type 'Any' is not supported.", "optionType");
            }

            // ReSharper disable once UseObjectOrCollectionInitializer
            SmileNodeInfo nodeInfo = new SmileNodeInfo();

            nodeInfo.F            = f;
            nodeInfo.dT           = dT;
            nodeInfo.RiskFreeRate = riskfreeRatePct;
            nodeInfo.Sigma        = returnPct ? optSigma * Constants.PctMult : optSigma;
            nodeInfo.OptPx        = optPx;
            nodeInfo.Strike       = sInfo.Strike;
            // Сюда мы приходим когда хотим торговать, поэтому обращение к Security уместно
            nodeInfo.Security     = (optionType == StrikeType.Put) ? sInfo.Put.Security : sInfo.Call.Security;
            nodeInfo.PxMode       = optPxMode;
            nodeInfo.OptionType   = optionType;
            nodeInfo.Pair         = sInfo;
            nodeInfo.ScriptTime   = optTime;
            nodeInfo.CalendarTime = DateTime.Now;

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

            ip.Tag          = nodeInfo;
            ip.DragableMode = DragableMode.None;
            ip.Value        = new Point(sInfo.Strike, nodeInfo.Sigma);
            // [2015-08-26] Алексей дал инструкцию убрать дату из тултипа.
            bool tooltipWithTime = false;

#if DEBUG
            tooltipWithTime = true;
#endif
            // [2015-12-07] В режиме отладки возвращаю.
            // Потому что иначе вообще непонятно что происходит и с какими данными скрипт работает.
            if (optQty > 0)
            {
                if (tooltipWithTime)
                {
                    ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "K:{0}; IV:{1:#0.00}%\r\n{2} px {3} @ {4}\r\nDate: {5}",
                                               sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx, optQty,
                                               optTime.ToString(DateTimeFormatWithMs, CultureInfo.InvariantCulture));
                }
                else
                {
                    ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "K:{0}; IV:{1:#0.00}%\r\n{2} px {3} @ {4}",
                                               sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx, optQty);
                }
            }
            else
            {
                if (tooltipWithTime)
                {
                    ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "K:{0}; IV:{1:#0.00}%\r\n{2} px {3}\r\nDate: {4}",
                                               sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx,
                                               optTime.ToString(DateTimeFormatWithMs, CultureInfo.InvariantCulture));
                }
                else
                {
                    ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "K:{0}; IV:{1:#0.00}%\r\n{2} px {3}",
                                               sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx);
                }
            }
        }