Пример #1
0
        /// <summary>
        /// Применяет настройки видимой области, переданные в аргументе rect
        /// </summary>
        /// <param name="rect">невалидные настройки игнорируются</param>
        private void ApplySettings(ICanvasPane pane, Rect rect)
        {
            // PROD-3577 - Если иы используем кубик SetViewport, то эта настройка всегда должна стоять
            pane.FeetToBorder2ByDefault = true;

            // PROD-5747 - Применяем только валидные настройки
            if ((pane == null) ||
                (!DoubleUtil.IsPositive(rect.Width)) || (!DoubleUtil.IsPositive(rect.Height)))
            {
                string tradeName = (m_context.Runtime?.TradeName ?? "NULL").Replace(Constants.HtmlDot, ".");
                string msg       = String.Format(CultureInfo.InvariantCulture,
                                                 "[{0}.ApplySettings] BAD RECT. TradeName:'{1}'; Width:{2}; Height:{3}",
                                                 GetType().Name, tradeName, rect.Width, rect.Height);
                m_context.Log(msg, MessageType.Info, false);
                return;
            }

            if (ApplyVisualSettings)
            {
                // Флаг выставляется однократно по нажатию кнопки и потом сбрасывается, если нужно
                pane.FeetToBorder2 = true;
            }

            if (ManageX)
            {
                //pane.BorderX1 = rect.X;
                //pane.BorderX2 = rect.X + rect.Width;
                pane.Border2X1 = rect.X;
                pane.Border2X2 = rect.X + rect.Width;
            }

            if (ManageY)
            {
                pane.Border2Y1 = rect.Y - rect.Height;
                pane.Border2Y2 = rect.Y;
            }

            if (ManageXGridStep)
            {
                pane.XAxisStep    = m_xAxisGridStep;
                pane.XAxisDiviser = m_xAxisDivisor;
            }

            if (ManageYGridStep)
            {
                // TODO: прокинуть через АПИ настройки грида оси Y?
                //pane.YA = m_yAxisGridStep;
                //pane.YAX = m_yAxisDivisor;
            }
        }
Пример #2
0
        protected override double?GetValue(FinInfo finInfo)
        {
            if (finInfo == null || finInfo.Security == null)
            {
                return(1);
            }

            var lastPrice = finInfo.LastPrice ?? 0.0;
            var tick      = finInfo.Security.GetTick(lastPrice);

            if (!DoubleUtil.IsPositive(tick))
            {
                tick = Math.Pow(10, -finInfo.Security.Decimals);
            }

            return(tick);
        }
Пример #3
0
        public void Execute(double price, double time, double sigma, ICanvasPane pane, int barNum)
        {
            int barsCount = ContextBarsCount;

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

            // PROD-3577
            pane.FeetToBorder2ByDefault = true;

            double futPx = price;
            double dT    = time;

            //double sigma = sigmas[sigmas.Count - 1];

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

            if (pane != null)
            {
                Rect rect = PrepareVieportSettings(null, futPx, dT, sigma);
                ApplySettings(pane, rect);

                //if (ShouldWarmSecurities)
                //WarmSecurities(Context, rect);
            }
        }
Пример #4
0
        /// <summary>
        /// Выбрать допустимые шаги рабочих страйков на основании расстояния между ними
        /// </summary>
        /// <param name="dK">шаг между страйками</param>
        /// <param name="context"/>
        /// <returns>отсортированный список возможных рабочих расстояний</returns>
        // ReSharper disable once MemberCanBePrivate.Global
        public static ReadOnlyCollection <double> GetStrikeSteps(double dK)
        {
            Contract.Assert(DoubleUtil.IsPositive(dK), "Что делать с отрицательным шагом стрйков? dK: " + dK);
            Contract.Assert(s_stepMultipliers.Count > 0, "Мало мультипликаторов? s_stepMultipliers.Count: " + s_stepMultipliers.Count);

            // Если беда, просто возвращаем 0
            if ((!DoubleUtil.IsPositive(dK)) || (s_stepMultipliers.Count <= 0))
            {
                return(s_singleZero);
            }

            double[] steps = new double[s_stepMultipliers.Count];
            for (int j = 0; j < s_stepMultipliers.Count; j++)
            {
                steps[j] = dK * s_stepMultipliers[j];
                // Выполняю округление до 9-го знака, чтобы красивее выглядели шаги страйков на EDZ7
                steps[j] = Math.Round(steps[j], 9);
            }

            ReadOnlyCollection <double> res = new ReadOnlyCollection <double>(steps);

            return(res);
        }
Пример #5
0
        protected IList <double> CalculateAll(IOption opt, ISecurity sec)
        {
            int len = sec.Bars.Count;

            if (len <= 0)
            {
                return(Constants.EmptyListDouble);
            }

            double[] res     = Context.GetArray <double>(len);
            var      history = LocalHistory;

            bool      exceptionFlag = false;
            Exception lastEx = null;
            DateTime  prevBarDate = new DateTime(), prevExpDate = new DateTime();

            for (int m = 0; m < len - 1; m++)
            {
                DateTime now = sec.Bars[m].Date;

                double time;
                if (history.TryGetValue(now, out time))
                {
                    res[m] = time;
                }
                else
                {
                    time = Constants.NaN;
                    try
                    {
                        double timeAsDays, timeAsYears;
                        time = EstimateTimeForGivenBar(opt, sec, sec.Bars[m].Date, prevBarDate, prevExpDate,
                                                       out timeAsDays, out timeAsYears, out prevBarDate, out prevExpDate);
                        history[now] = time;
                    }
                    catch (Exception ex)
                    {
                        lastEx        = ex;
                        exceptionFlag = true;
                    }
                    res[m] = time;
                }
            }

            if (exceptionFlag && (lastEx != null))
            {
                m_context.Log("[TimeToExpiry] " + lastEx, MessageType.Error, true);
            }

            lastEx        = null;
            exceptionFlag = false;
            if (len > 0)
            {
                IConnectable ds = null;
                DateTime     lastBarNow = DateTime.MaxValue, serverNow = DateTime.MaxValue;
                double       time = Constants.NaN, timeAsDays = Constants.NaN;
                try
                {
                    double timeAsYears;
                    int    intervalSec = Context.Runtime.IntervalInstance.ToSeconds();
                    // Проверка на положительное значение intervalSec не принципиальна. В бою будет использовано серверное время.
                    DateTime now = lastBarNow = sec.Bars[len - 1].Date.AddSeconds(intervalSec);
                    if (Context.Runtime.IsAgentMode)
                    {
                        // [2020-06-03] PROD-7823(?) В режиме агента для последнего бара нужно использовать время провайдера
                        ds = sec.SecurityDescription.TradePlace.DataSource as IConnectable;
                        if ((ds != null) && ds.IsConnected)
                        {
                            serverNow = ds.ServerTime;
                            if (serverNow > lastBarNow)
                            {
                                now = serverNow;
                            }
                        }
                    }
                    time = EstimateTimeForGivenBar(opt, sec, now, prevBarDate, prevExpDate,
                                                   out timeAsDays, out timeAsYears, out prevBarDate, out prevExpDate);
                }
                catch (Exception ex)
                {
                    lastEx        = ex;
                    exceptionFlag = true;
                }
                res[len - 1] = time;
                // Принято решение, что для удобства пользователя время на UI показывается всегда в днях.
                double displayValue = timeAsDays;
                m_dT.Value = displayValue;

                // [2020-06-03] PROD-7823(?) Логгируем отрицательное время для протокола.
                if (Context.Runtime.IsAgentMode && (!DoubleUtil.IsPositive(time)))
                {
                    string dT        = time.ToString(CultureInfo.InvariantCulture);
                    string bts       = lastBarNow.ToString(DateTimeFormatWithMs, CultureInfo.InvariantCulture);
                    string sts       = serverNow.ToString(DateTimeFormatWithMs, CultureInfo.InvariantCulture);
                    string conn      = (ds != null) ? ds.IsConnected.ToString().ToUpperInvariant() : "NULL";
                    string agentName = Context?.Runtime?.TradeName?.Replace(Constants.HtmlDot, ".") ?? "EMPTY";
                    string msg       = String.Format(CultureInfo.InvariantCulture,
                                                     "[{0}:TimeToExpiry] dT < 0! dT:{1}; bar close time:{2} connected:{3}; server time:{4}",
                                                     agentName, dT, bts, conn, sts);
                    m_context.Log(msg, MessageType.Error, false);
                }
            }

            if (exceptionFlag && (lastEx != null))
            {
                m_context.Log("[TimeToExpiry] " + lastEx, MessageType.Error, true);
            }

            //return new ReadOnlyCollection<double>(times);
            return(res);
        }
Пример #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);
        }
Пример #7
0
        /// <summary>
        /// Обработчик под тип входных данных OPTION_SERIES
        /// </summary>
        /// <param name="price">цена БА</param>
        /// <param name="time">время до экспирации в долях года</param>
        /// <param name="optSer">опционная серия</param>
        /// <param name="rate">процентная ставка</param>
        /// <param name="barNum">индекс бара в серии</param>
        /// <returns>улыбка, восстановленная из цен опционов</returns>
        public InteractiveSeries Execute(double price, double time, IOptionSeries optSer, double scaleMult, double rate, int barNum)
        {
            int barsCount = ContextBarsCount;

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

            double f  = price;
            double dT = time;

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

            IOptionStrikePair[] strikes = (from strike in optSer.GetStrikePairs()
                                           //orderby strike.Strike ascending -- уже отсортировано
                                           select strike).ToArray();
            List <InteractiveObject> controlPoints = new List <InteractiveObject>();

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

                double   putPxBtc, callPxBtc, putPxUsd, callPxUsd;
                double   putQty, callQty;
                DateTime putTime, callTime;
                {
                    putPxBtc = IvSmile.GetOptPrice(m_context, f, sInfo.Put, m_optionPxMode, sInfo.Tick * m_shiftAsk, sInfo.Tick * m_shiftBid, out putQty, out putTime);
                    putPxUsd = putPxBtc * scaleMult;
                    // Здесь нельзя сразу домножать на scaleMultiplier! Потому что тогда в метод FillNodeInfo пойдут бредовые цены.
                }

                {
                    callPxBtc = IvSmile.GetOptPrice(m_context, f, sInfo.Call, m_optionPxMode, sInfo.Tick * m_shiftAsk, sInfo.Tick * m_shiftBid, out callQty, out callTime);
                    callPxUsd = callPxBtc * scaleMult;
                    // Здесь нельзя сразу домножать на scaleMultiplier! Потому что тогда в метод FillNodeInfo пойдут бредовые цены.
                }

                double putSigma = Double.NaN, callSigma = Double.NaN, precision;
                if (DoubleUtil.IsPositive(putPxBtc))
                {
                    // Цену опциона переводим в баксы только в момент вычисления айви
                    putSigma = FinMath.GetOptionSigma(f, sInfo.Strike, dT, putPxUsd, rate, false, out precision);
                    putSigma = Math.Min(putSigma, m_maxSigma);
                    if (putSigma <= 0)
                    {
                        putSigma = Double.NaN;
                    }
                }
                if (DoubleUtil.IsPositive(callPxBtc))
                {
                    // Цену опциона переводим в баксы только в момент вычисления айви
                    callSigma = FinMath.GetOptionSigma(f, sInfo.Strike, dT, callPxUsd, rate, true, out precision);
                    callSigma = Math.Min(callSigma, m_maxSigma);
                    if (callSigma <= 0)
                    {
                        callSigma = Double.NaN;
                    }
                }

                InteractivePointActive ip = new InteractivePointActive();
                {
                    //ip.Color = (m_optionPxMode == OptionPxMode.Ask) ? Colors.DarkOrange : Colors.DarkCyan;
                    //ip.DragableMode = DragableMode.None;
                    //ip.Geometry = Geometries.Rect; // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect;
                    //ip.IsActive = true;
                    //ip.Value = new Point(d2.V1, d2.V2);
                    //ip.Tooltip = String.Format("K:{0}; IV:{1:#0.00}", d2.V1, d2.V2 * PctMult);
                }

                InteractiveObject obj = new InteractiveObject(ip);

                if (m_optionType == StrikeType.Put)
                {
                    if (DoubleUtil.IsPositive(putSigma))
                    {
                        // Здесь используем первичную цену в том виде, как ее нам дал Дерибит
                        FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Put, m_optionPxMode, putPxBtc, putQty, putSigma, putTime, false, rate, scaleMult);
                        controlPoints.Add(obj);
                    }
                }
                else if (m_optionType == StrikeType.Call)
                {
                    if (DoubleUtil.IsPositive(callSigma))
                    {
                        // Здесь используем первичную цену в том виде, как ее нам дал Дерибит
                        FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Call, m_optionPxMode, callPxBtc, callQty, callSigma, callTime, false, rate, scaleMult);
                        controlPoints.Add(obj);
                    }
                }
                else if (m_optionType == StrikeType.Any)
                {
                    if (DoubleUtil.IsPositive(putSigma) && DoubleUtil.IsPositive(callSigma))
                    {
                        // Здесь используем первичную цену в том виде, как ее нам дал Дерибит
                        if (m_optionPxMode == OptionPxMode.Ask)
                        {
                            if (putSigma < callSigma)
                            {
                                FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Put, m_optionPxMode, putPxBtc, putQty, putSigma, putTime, false, rate, scaleMult);
                            }
                            else
                            {
                                FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Call, m_optionPxMode, callPxBtc, callQty, callSigma, callTime, false, rate, scaleMult);
                            }
                        }
                        else if (m_optionPxMode == OptionPxMode.Bid)
                        {
                            if (putSigma > callSigma)
                            {
                                FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Put, m_optionPxMode, putPxBtc, putQty, putSigma, putTime, false, rate, scaleMult);
                            }
                            else
                            {
                                FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Call, m_optionPxMode, callPxBtc, callQty, callSigma, callTime, false, rate, scaleMult);
                            }
                        }

                        controlPoints.Add(obj);
                    }
                    else if (DoubleUtil.IsPositive(putSigma) && Double.IsNaN(callSigma))
                    {
                        // Здесь используем первичную цену в том виде, как ее нам дал Дерибит
                        FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Put, m_optionPxMode, putPxBtc, putQty, putSigma, putTime, false, rate, scaleMult);
                        controlPoints.Add(obj);
                    }
                    else if (Double.IsNaN(putSigma) && DoubleUtil.IsPositive(callSigma))
                    {
                        // Здесь используем первичную цену в том виде, как ее нам дал Дерибит
                        FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Call, m_optionPxMode, callPxBtc, callQty, callSigma, callTime, false, rate, scaleMult);
                        controlPoints.Add(obj);
                    }
                }
            }

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

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

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

            if (!IvSmile.TryPrepareSmileInfo(m_context, f, dT, rate, optSer.ExpirationDate, scriptTime, baseSec.Symbol, res, out info))
            {
                return(Constants.EmptySeries);
            }

            return(res);
        }
Пример #8
0
        private IList <double> PrepareData(ISecurity sec, string expiryDate)
        {
            DateTime expiry;

            if ((!DateTime.TryParseExact(expiryDate, IvOnF.DateFormat, CultureInfo.InvariantCulture,
                                         DateTimeStyles.AssumeLocal, out expiry)) &&
                (!DateTime.TryParseExact(expiryDate, IvOnF.DateFormat + " HH:mm", CultureInfo.InvariantCulture,
                                         DateTimeStyles.AssumeLocal, out expiry)))
            {
                string msg = String.Format("[{0}.{1}.PrepareData] Unable to parse expiration date '{2}'. Expected date format '{3}'.",
                                           Context.Runtime.TradeName, GetType().Name, expiryDate, IvOnF.DateFormat);
                m_context.Log(msg, MessageType.Warning, true);
                return(Constants.EmptyListDouble);
            }

            //// и тут я понял, что дату экспирации в любом случае надо задавать руками...
            //string cashKey = typeof(IvOnF).Name + "_ivExchangeSigmas_" + sec.Symbol + "_" +
            //    expiryDate.Replace(':', '-');
            string skewKey = IvOnF.GetSkewCashKey(sec.Symbol, expiry, SkewMode, DistanceMode);
            Dictionary <DateTime, double> ivSkews = null;

            try
            {
                object globalObj = Context.LoadGlobalObject(skewKey, true);
                ivSkews = globalObj as Dictionary <DateTime, double>;
                // PROD-3970 - 'Важный' объект
                if (ivSkews == null)
                {
                    var container = globalObj as NotClearableContainer;
                    if ((container != null) && (container.Content != null))
                    {
                        ivSkews = container.Content as Dictionary <DateTime, double>;
                    }
                }
            }
            catch (NotSupportedException nse)
            {
                string fName = "", path = "";
                if (nse.Data["fName"] != null)
                {
                    fName = nse.Data["fName"].ToString();
                }
                if (nse.Data["path"] != null)
                {
                    path = nse.Data["path"].ToString();
                }
                string msg = String.Format("[{0}.PrepareData] {1} when loading 'ivSkews' from global cache. cashKey: {2}; Message: {3}\r\n\r\nfName: {4}; path: {5}\r\n\r\n{6}",
                                           GetType().Name, nse.GetType().FullName, skewKey, nse.Message, fName, path, nse);
                m_context.Log(msg, MessageType.Warning, true);
            }
            catch (Exception ex)
            {
                string msg = String.Format("[{0}.PrepareData] {1} when loading 'ivSkews' from global cache. cashKey: {2}; Message: {3}\r\n\r\n{4}",
                                           GetType().Name, ex.GetType().FullName, skewKey, ex.Message, ex);
                m_context.Log(msg, MessageType.Warning, true);
            }

            if (ivSkews == null)
            {
                // Данного ключа в глобальном кеше нет? Тогда выход.
                // [{0}.PrepareData] There is no Skew ATM in global cache. Probably, you have to start agent 'Collect IV (ALL)' for security '{1}'.
                string msg = RM.GetStringFormat("OptHandlerMsg.GlobalSkewOnF.CacheNotFound", GetType().Name, expiryDate, sec);
                if (m_context.Runtime.IsAgentMode && (!m_ignoreCacheError))
                {
                    throw new ScriptException(msg); // PROD-4624 - Андрей велит кидать исключение.
                }
                bool isExpired = true;
                if (m_context.Runtime.IsAgentMode)
                {
                    int      amount = sec.Bars.Count;
                    DateTime today  = (amount > 0) ? sec.Bars[amount - 1].Date : new DateTime();
                    isExpired = expiry.Date.AddDays(1) < today.Date;
                }
                // А если в режиме лаборатории, тогда только жалуемся и продолжаем.
                m_context.Log(msg, MessageType.Warning, !isExpired); // Если серия уже умерла, пишем только в локальный лог
                return(Constants.EmptyListDouble);
            }

            List <double> res = new List <double>();

            int len = sec.Bars.Count;

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

            int    oldResLen = res.Count;
            double prevSkew  = Double.NaN;

            for (int j = oldResLen; j < len; j++)
            {
                DateTime now = sec.Bars[j].Date;
                double   skew;
                if (ivSkews.TryGetValue(now, out skew) && (!DoubleUtil.IsNaN(skew)))
                {
                    prevSkew = skew;
                    res.Add(skew);
                }
                else
                {
                    if (m_repeatLastIv)
                    {
                        if (!Double.IsNaN(prevSkew))
                        {
                            skew = prevSkew;
                        }
                        else
                        {
                            skew = Constants.NaN;
                            if (j == 0)
                            {
                                #region Отдельно обрабатываю нулевой бар
                                double   tmp      = Double.NaN;
                                DateTime foundKey = new DateTime(1, 1, 1);
                                // [2016-01-19] Когда история становится слишком длинной, это может вызывать проблемы
                                // при итерировании в foreach. Потому что другой поток может в этот момент добавить новую точку в коллекцию.
                                int repeat = 7;
                                while (repeat > 0)
                                {
                                    tmp      = Double.NaN;
                                    foundKey = new DateTime(1, 1, 1);
                                    try
                                    {
                                        lock (ivSkews)
                                        {
                                            foreach (var kvp in ivSkews)
                                            {
                                                if (kvp.Key > now)
                                                {
                                                    continue;
                                                }

                                                if (foundKey < kvp.Key)
                                                {
                                                    foundKey = kvp.Key;
                                                    tmp      = kvp.Value;
                                                }
                                            }
                                        }
                                        repeat = -100;
                                    }
                                    catch (InvalidOperationException invOpEx)
                                    {
                                        repeat--;
                                        Thread.Sleep(10);
                                        if (repeat <= 0)
                                        {
                                            string msg = String.Format("[{0}.PrepareData] {1} when iterate through 'ivSkews'. cashKey: {2}; Message: {3}\r\n\r\n{4}",
                                                                       GetType().Name, invOpEx.GetType().FullName, skewKey, invOpEx.Message, invOpEx);
                                            m_context.Log(msg, MessageType.Warning, true);
                                            throw;
                                        }
                                    }
                                }

                                if ((foundKey.Year > 1) && DoubleUtil.IsPositive(tmp))
                                {
                                    skew     = tmp;
                                    prevSkew = skew;
                                }
                                #endregion Отдельно обрабатываю нулевой бар
                            }
                        }
                        res.Add(skew);
                    }
                    else
                    {
                        res.Add(Constants.NaN);
                    }
                }
            }

            return(res);
        }
Пример #9
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);
        }
Пример #10
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);
                }
            }
        }
Пример #11
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)
            {
                return(Constants.NaN);
            }

            if ((smile == null) || (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 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.NaN);
            }

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

            double rawTheta, res;

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

            if (SingleSeriesNumericalTheta.TryEstimateTheta(posMan, pairs, smile, m_greekAlgo, futPx, dT, m_tStep, out rawTheta))
            {
                // Переводим тету в дифференциал 'изменение цены за 1 сутки'.
                rawTheta = SingleSeriesNumericalTheta.RescaleThetaToDays(m_tRemainMode, rawTheta);

                res = rawTheta;
            }
            else
            {
                res = Constants.NaN;
            }

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

            SetHandlerInitialized(now);

            return(res);
        }
Пример #12
0
        /// <summary>
        /// Пытается угадать видимую область и если это получается результат складывает в локальный кеш
        /// </summary>
        private Rect PrepareVieportSettings(string seriesExpiry, double futPx, double dT, double sigma)
        {
            Rect rect;
            //string key = VariableId + KeySuffix + "_" + (seriesExpiry ?? "NULL");
            string key       = GetViewportCacheKey(this, seriesExpiry);
            var    container = Context.LoadObject(key) as NotClearableContainer <Rect>;

            // Проверка на ApplyVisualSettings нужна, чтобы безусловно поменять видимую область при нажатии на кнопку в UI
            if ((container != null) /*&& (container.Content != null)*/ && (!ApplyVisualSettings) &&
                DoubleUtil.IsPositive(container.Content.Width) && DoubleUtil.IsPositive(container.Content.Height))   // PROD-3901
            {
                rect = container.Content;
            }
            else
            {
                // PROD-5747 - Если контенер пуст, давайте сделаем в логе запись об этом?
                // Тогда будет понятно в какой момент он чистится.
                if ((container == null) /*|| (container.Content == null)*/ ||
                    (!DoubleUtil.IsPositive(futPx)) || (!DoubleUtil.IsPositive(sigma)))
                {
                    string expStr    = seriesExpiry ?? "NULL";
                    string tradeName = (m_context.Runtime?.TradeName ?? "NULL").Replace(Constants.HtmlDot, ".");
                    string msg       = String.Format(CultureInfo.InvariantCulture,
                                                     "[{0}.PrepareVieportSettings] Empty container. Key:'{1}'; futPx:{2}; dT:{3}; sigma:{4}; expiry:{5}; TradeName:'{6}'",
                                                     GetType().Name, key, futPx, dT, sigma, expStr, tradeName);
                    m_context.Log(msg, MessageType.Info, false);
                }
                else if ((container != null) /*&& (container.Content != null)*/ &&
                         ((!DoubleUtil.IsPositive(container.Content.Width)) ||
                          (!DoubleUtil.IsPositive(container.Content.Height))))
                {
                    string expStr    = seriesExpiry ?? "NULL";
                    string tradeName = (m_context.Runtime?.TradeName ?? "NULL").Replace(Constants.HtmlDot, ".");
                    string msg       = String.Format(CultureInfo.InvariantCulture,
                                                     "[{0}.PrepareVieportSettings] BAD RECT. Key:'{1}'; futPx:{2}; dT:{3}; sigma:{4}; expiry:{5}; TradeName:'{6}'; Width:{7}; Height:{8}",
                                                     GetType().Name, key, futPx, dT, sigma, expStr, tradeName, container.Content.Width, container.Content.Height);
                    m_context.Log(msg, MessageType.Info, false);
                }

                //// При самом первом запуске эмулирую нажатие кнопки Apply, чтобы заставить
                //// CanvasPane реагировать на мои настройки в Borders2.
                //ApplyVisualSettings = true;

                double width = (SigmaMult * sigma * Math.Sqrt(dT)) * futPx;
                // Общая ширина не менее 10% от futPx?
                //width = Math.Max(width, futPx * 0.05);

                double left = Math.Max(0, futPx - width);
                // Чтобы график был симметричен, ширину тоже подрезаю
                width = Math.Abs(futPx - left);
                double height = sigma * (m_verticalMultiplier - 1.0 / m_verticalMultiplier);
                rect = new Rect(futPx - width, sigma * m_verticalMultiplier, 2 * width, height);

                // PROD-5747 - Сохранять область в кеш можно только если прямоугольник имеет нормальные размеры
                if (DoubleUtil.IsPositive(rect.Width) && DoubleUtil.IsPositive(rect.Height))
                {
                    // При самом первом запуске эмулирую нажатие кнопки Apply, чтобы заставить
                    // CanvasPane реагировать на мои настройки в Borders2.
                    ApplyVisualSettings = true;

                    container = new NotClearableContainer <Rect>(rect);
                    Context.StoreObject(key, container);
                }
            }
            return(rect);
        }
Пример #13
0
        public void Execute(double price, double time, IOptionSeries optSer, ICanvasPane pane, int barNum)
        {
            int barsCount = ContextBarsCount;

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

            // PROD-3577
            pane.FeetToBorder2ByDefault = true;

            double futPx = price;
            double dT    = time;

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

            double    sigma;
            IFunction smileFunc = IvOnF.PrepareExchangeSmileSpline(optSer, Double.MinValue, Double.MaxValue);

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

                // PROD-5968 - У биткойна совсем другой типичный уровень волатильности
                var parent      = optSer.UnderlyingAsset;
                var secDesc     = parent.SecurityDescription;
                var tp          = secDesc.TradePlace;
                var dsClassName = tp?.DataSource?.GetType().Name;
                if (dsClassName == "DeribitDS")
                {
                    sigma = DefaultSigmaDeribit;
                }
                else if (dsClassName == "ExanteDataSource")
                {
                    if (tp.Id == "CME")
                    {
                        if (secDesc.ActiveType.IsFuture() && parent.Symbol.StartsWith("ES"))
                        {
                            sigma = DefaultSigmaEs;
                        }
                    }
                }
            }

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

                if (ManageXGridStep)
                {
                    var pairs = optSer.GetStrikePairs().ToArray();
                    int pLen  = pairs.Length;
                    if (pLen > 1)
                    {
                        double dK = pairs[1].Strike - pairs[0].Strike;
                        if (pLen > 2)
                        {
                            int t = pLen / 2; // Делим нацело. Для pLen==3 получаем 1
                            dK = pairs[t + 1].Strike - pairs[t].Strike;
                        }

                        pane.XAxisStep    = GetXAxisStep(dK);
                        pane.XAxisDiviser = GetXAxisDivisor(dK);
                    }
                }
            }
        }
Пример #14
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);
                }
            }
        }
Пример #15
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)
            {
                return(Constants.NaN);
            }

            if ((smile == null) || (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, false);
                }
                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, false);
                }
                return(Constants.NaN);
            }

            double rawVega, res;

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

            if (SingleSeriesNumericalVega.TryEstimateVomma(posMan, optSer, pairs, smile, m_greekAlgo, f, m_sigmaStep, dT, out rawVega))
            {
                // Переводим вомму в дифференциал 'изменение цены за 1% волы'.
                // В знаменателе стоит dSigma^2, поэтому и делить нужно 2 раза на 100%.
                rawVega /= (Constants.PctMult * Constants.PctMult);

                res = rawVega;
            }
            else
            {
                res = Constants.NaN;
            }

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

            SetHandlerInitialized(now);

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

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

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

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

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

            double futPx = price;
            double dT    = time;

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

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

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

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

            double ivAtm, slopeAtm, shape;

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

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

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

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

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

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

            SmileInfo templateInfo;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                    m_context.StoreGlobalObject(m_globalSmileId, templateSmile, true);

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

                    templateInfo = templateSmile;
                }
            }
            #endregion Fill templateInfo

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                InteractiveObject obj = new InteractiveObject(ip);

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

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

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

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

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

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

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

            res.Tag = info;

            SetHandlerInitialized(now);

            return(res);
        }
Пример #18
0
        /// <summary>
        /// Метод под флаг TemplateTypes.OPTION_SERIES, чтобы подключаться к источнику-серии
        /// </summary>
        public double Execute(IOptionSeries optSer, int barNum)
        {
            double failRes = Constants.NaN;

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

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

            switch (m_pxMode)
            {
            case BasePxMode.TheorPxBased:
            {
                Dictionary <DateTime, double> basePrices;

                #region Get cache
                {
                    string cashKey = VariableId + "_basePrices";
                    basePrices = Context.LoadObject(cashKey) as Dictionary <DateTime, double>;
                    if (basePrices == null)
                    {
                        basePrices = new Dictionary <DateTime, double>();
                        Context.StoreObject(cashKey, basePrices);
                    }
                }
                #endregion Get cache

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

                double   px;
                DateTime lastBarDate = sec.Bars[barNum].Date;
                if ((basePrices.TryGetValue(lastBarDate, out px)) && DoubleUtil.IsPositive(px))
                {
                    m_prevPx = px;
                    // Раз мы нашли валидную цену в архиве, то можно обновить failRes
                    failRes = px;
                }

                // Цену в архиве нашли, теперь надо проверить свежие сведения.
                {
                    int barsCount = ContextBarsCount;
                    if (barNum < barsCount - 1)
                    {
                        // Если история содержит осмысленное значение, то оно уже содержится в failRes
                        return(failRes);
                    }
                    else
                    {
                        #region Process last bar(s)
                        IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray();
                        IOptionStrikePair   pair  = pairs[pairs.Length / 2];

                        if ((pair.PutFinInfo.TheoreticalPrice == null) ||
                            (pair.CallFinInfo.TheoreticalPrice == null))
                        {
                            return(failRes);
                        }

                        double putPx  = pair.PutFinInfo.TheoreticalPrice.Value;
                        double callPx = pair.CallFinInfo.TheoreticalPrice.Value;
                        px       = callPx - putPx + pair.Strike;
                        m_prevPx = px;

                        basePrices[lastBarDate] = px;

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

                        return(px);

                        #endregion Process last bar(s)
                    }
                }
            }

            default:
                return(Execute(optSer.UnderlyingAsset, barNum));
            }
        }
Пример #19
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);
            }

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

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

            if (SingleSeriesNumericalGamma.TryEstimateGamma(posMan, optSer, pairs, smile, m_greekAlgo, f, dF, dT, out rawGamma))
            {
                res = rawGamma;
            }
            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("[DEBUG:{0}] F:{1}; dT:{2};   smile.F:{3}; smile.dT:{4}; smile.IV:{5}",
            //        GetType().Name, F, dT, sInfo.F, sInfo.dT, iv);
            //    context.Log(msg, MessageType.Info, true);
            //}

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

            SetHandlerInitialized(now);

            return(res);
        }
Пример #20
0
        /// <summary>
        /// Метод под флаг TemplateTypes.SECURITY, чтобы подключаться к источнику-БА
        /// </summary>
        public double Execute(ISecurity sec, int barNum)
        {
            double failRes = Constants.NaN;

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

            Dictionary <DateTime, double> basePrices;

            #region Get cache
            {
                string cashKey = VariableId + "_basePrices";
                basePrices = Context.LoadObject(cashKey) as Dictionary <DateTime, double>;
                if (basePrices == null)
                {
                    basePrices = new Dictionary <DateTime, double>();
                    Context.StoreObject(cashKey, basePrices);
                }
            }
            #endregion Get cache

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

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

            double   px;
            DateTime lastBarDate = sec.Bars[barNum].Date;
            if ((basePrices.TryGetValue(lastBarDate, out px)) && DoubleUtil.IsPositive(px))
            {
                m_prevPx = px;
                // Раз мы нашли валидную цену в архиве, то можно обновить failRes
                failRes = px;
            }

            // Цену в архиве нашли, теперь надо проверить свежие сведения.
            {
                int barsCount = ContextBarsCount;
                if (barNum < barsCount - 1)
                {
                    // Если история содержит осмысленное значение, то оно уже содержится в failRes
                    return(failRes);
                }
                else
                {
                    #region Process last bar(s)

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

                    case BasePxMode.LastTrade:
                        if (sec.FinInfo.LastPrice.HasValue)
                        {
                            px       = sec.FinInfo.LastPrice.Value;
                            m_prevPx = px;
                        }
                        else
                        {
                            px = failRes;
                        }
                        break;

                    case BasePxMode.BidAskMidPoint:
                    {
                        FinInfo info = sec.FinInfo;
                        if (info.Ask.HasValue && info.Bid.HasValue && info.AskSize.HasValue &&
                            info.BidSize.HasValue && (info.AskSize.Value > 0) && (info.BidSize.Value > 0))
                        {
                            px       = (info.Ask.Value + info.Bid.Value) / 2;
                            m_prevPx = px;
                        }
                        else if (info.Ask.HasValue && info.AskSize.HasValue && (info.AskSize.Value > 0))
                        {
                            px       = info.Ask.Value;
                            m_prevPx = px;
                        }
                        else if (info.Bid.HasValue && info.BidSize.HasValue && (info.BidSize.Value > 0))
                        {
                            px       = info.Bid.Value;
                            m_prevPx = px;
                        }
                        else
                        {
                            // Приемлемо ли такое решение?
                            if (info.LastPrice.HasValue)
                            {
                                px       = info.LastPrice.Value;
                                m_prevPx = px;
                            }
                            else
                            {
                                px = failRes;
                            }
                        }
                    }
                    break;

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

                    basePrices[lastBarDate] = px;

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

                    return(px);

                    #endregion Process last bar(s)
                }
            }
        }
Пример #21
0
        /// <summary>
        /// Обработчик под тип входных данных OPTION_SERIES
        /// </summary>
        public IList <double> Execute(IOptionSeries optSer)
        {
            if (optSer == null)
            {
                string msg = "[IV ATM] (optSer == null)";
                m_context.Log(msg, MessageType.Warning, false);

                return(Constants.EmptyListDouble);
            }

            Dictionary <DateTime, double> ivSigmas;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            double futPx = price;
            double dT    = time;

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

            if (!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);
            }

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

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

            var allRealtimeSecs = Context.Runtime.Securities;

            IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray();
            for (int j = 0; j < pairs.Length; j++)
            {
                IOptionStrikePair pair = pairs[j];

                #region Process put
                {
                    var put = pair.Put.Security;
                    // TODO: Нужно ли тут проверить наличие позиций???
                    //if (put.Positions.HavePositions)

                    ISecurityRt secRt;
                    if (put is ISecurityRt)
                    {
                        secRt = (ISecurityRt)put;
                    }
                    else
                    {
                        secRt = (from s in allRealtimeSecs
                                 where s.SecurityDescription.Equals(put) && (s is ISecurityRt)
                                 select(ISecurityRt) s).SingleOrDefault();
                    }

                    if ((secRt != null) && secRt.HasActiveOrders)
                    {
                        //secRt.SecurityDescription.TradePlace.DataSource
                        // ОТЛИЧНО! Эта коллекция позволит мне нарисовать свои заявки (это коллекция реальных заявок агента из таблицы My Orders)
                        var orders = secRt.Orders.ToList();
                        foreach (IOrder ord in orders)
                        {
                            if (!ord.IsActive)
                            {
                                continue;
                            }

                            // Объект ord является RealtimeOrder. Его идентификатор совпадает с OrderNumber в таблице MyOrders

                            if ((m_showLongOrders && ord.IsBuy) ||
                                ((!m_showLongOrders) && (!ord.IsBuy)))
                            {
                                // Почему-то InteractivePointLight хоть и давал себя настроить, но не отображался толком.
                                double sigma = FinMath.GetOptionSigma(futPx, pair.Strike, dT, ord.Price, riskFreeRatePct, false);
                                var    ip    = new InteractivePointActive(pair.Strike, sigma);
                                ip.Tooltip = String.Format(CultureInfo.InvariantCulture,
                                                           " F: {0}\r\n K: {1}; IV: {2:P2}\r\n {3} px {4} qty {5}",
                                                           futPx, pair.Strike, sigma, pair.Put.StrikeType, ord.Price, ord.RestQuantity);
                                controlPoints.Add(new InteractiveObject(ip));
                            }
                        }
                    }
                }
                #endregion Process put

                #region Process call
                {
                    var call = pair.Call.Security;
                    // TODO: Нужно ли тут проверить наличие позиций???
                    //if (call.Positions.HavePositions)

                    ISecurityRt secRt;
                    if (call is ISecurityRt)
                    {
                        secRt = (ISecurityRt)call;
                    }
                    else
                    {
                        secRt = (from s in allRealtimeSecs
                                 where s.SecurityDescription.Equals(call) && (s is ISecurityRt)
                                 select(ISecurityRt) s).SingleOrDefault();
                    }

                    if ((secRt != null) && secRt.HasActiveOrders)
                    {
                        // ОТЛИЧНО! Эта коллекция позволит мне нарисовать свои заявки (это коллекция реальных заявок агента из таблицы My Orders)
                        var orders = secRt.Orders.ToList();
                        foreach (IOrder ord in orders)
                        {
                            if (!ord.IsActive)
                            {
                                continue;
                            }

                            // Объект ord является RealtimeOrder. Его идентификатор совпадает с OrderNumber в таблице MyOrders

                            if ((m_showLongOrders && ord.IsBuy) ||
                                ((!m_showLongOrders) && (!ord.IsBuy)))
                            {
                                // Почему-то InteractivePointLight хоть и давал себя настроить, но не отображался толком.
                                double sigma = FinMath.GetOptionSigma(futPx, pair.Strike, dT, ord.Price, riskFreeRatePct, true);
                                var    ip    = new InteractivePointActive(pair.Strike, sigma);
                                ip.Tooltip = String.Format(CultureInfo.InvariantCulture,
                                                           " F: {0}\r\n K: {1}; IV: {2:P2}\r\n {3} px {4} qty {5}",
                                                           futPx, pair.Strike, sigma, pair.Call.StrikeType, ord.Price, ord.RestQuantity);
                                controlPoints.Add(new InteractiveObject(ip));
                            }
                        }
                    }
                }
                #endregion Process call
            } // End for (int j = 0; j < pairs.Length; j++)

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

            return(res);
        }
Пример #24
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);
        }
Пример #25
0
        /// <summary>
        /// Обработчик под тип входных данных INTERACTIVESPLINE
        /// </summary>
        /// <param name="price">цена БА</param>
        /// <param name="time">время до экспирации в долях года</param>
        /// <param name="optPrices">опционные цены</param>
        /// <param name="rate">процентная ставка</param>
        /// <param name="barNum">индекс бара в серии</param>
        /// <returns>улыбка, восстановленная из цен опционов</returns>
        public InteractiveSeries Execute(double price, double time, InteractiveSeries optPrices, double scaleMult, double rate, int barNum)
        {
            int barsCount = ContextBarsCount;

            if ((barNum < barsCount - 1) || (optPrices == null) || (optPrices.ControlPoints.Count <= 0))
            {
                return(Constants.EmptySeries);
            }

            IReadOnlyList <InteractiveObject> cps = optPrices.ControlPoints;

            double f  = price;
            double dT = time;

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

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

            for (int j = 0; j < cps.Count; j++)
            {
                InteractiveObject strikeObj = cps[j];
                double            strike    = strikeObj.Anchor.ValueX;
                // Сверхдалекие страйки игнорируем
                if ((strike < m_minStrike) || (m_maxStrike < strike))
                {
                    continue;
                }

                double optPx;
                double stradlePx = Double.NaN;
                double putPx = Double.NaN, callPx = Double.NaN;
                if (m_optionType == StrikeType.Put)
                {
                    putPx = strikeObj.Anchor.ValueY;
                    optPx = putPx;
                    // Здесь нельзя сразу домножать на scaleMultiplier! Потому что тогда в метод FillNodeInfo пойдут бредовые цены.
                }
                else if (m_optionType == StrikeType.Call)
                {
                    callPx = strikeObj.Anchor.ValueY;
                    optPx  = callPx;
                    // Здесь нельзя сразу домножать на scaleMultiplier! Потому что тогда в метод FillNodeInfo пойдут бредовые цены.
                }
                else
                {
                    stradlePx = strikeObj.Anchor.ValueY;
                    optPx     = stradlePx;
                    // Здесь нельзя сразу домножать на scaleMultiplier! Потому что тогда в метод FillNodeInfo пойдут бредовые цены.
                }

                double sigma = Double.NaN;
                double putSigma = Double.NaN, callSigma = Double.NaN, stradleSigma = Double.NaN, precision;
                if (DoubleUtil.IsPositive(putPx))
                {
                    // Цену опциона переводим в баксы только в момент вычисления айви
                    putSigma = FinMath.GetOptionSigma(f, strike, dT, putPx * scaleMult, rate, false, out precision);
                    putSigma = Math.Min(putSigma, m_maxSigma);
                    if (putSigma <= 0)
                    {
                        putSigma = Double.NaN;
                    }
                    else
                    {
                        if (m_optionType == StrikeType.Put)
                        {
                            sigma = putSigma;
                        }
                    }
                }
                if (DoubleUtil.IsPositive(callPx))
                {
                    // Цену опциона переводим в баксы только в момент вычисления айви
                    callSigma = FinMath.GetOptionSigma(f, strike, dT, callPx * scaleMult, rate, true, out precision);
                    callSigma = Math.Min(callSigma, m_maxSigma);
                    if (callSigma <= 0)
                    {
                        callSigma = Double.NaN;
                    }
                    else
                    {
                        if (m_optionType == StrikeType.Call)
                        {
                            sigma = callSigma;
                        }
                    }
                }
                if (DoubleUtil.IsPositive(stradlePx))
                {
                    // Цену опциона переводим в баксы только в момент вычисления айви
                    stradleSigma = FinMath.GetStradleSigma(f, strike, dT, stradlePx * scaleMult, rate, out precision);
                    stradleSigma = Math.Min(stradleSigma, m_maxSigma);
                    if (stradleSigma <= 0)
                    {
                        stradleSigma = Double.NaN;
                    }
                    else
                    {
                        if (m_optionType == StrikeType.Any)
                        {
                            sigma = stradleSigma;
                        }
                    }
                }

                if (Double.IsNaN(sigma) || (sigma <= 0) ||
                    Double.IsNaN(optPx) || (optPx <= 0))
                {
                    continue;
                }

                InteractivePointActive ip = new InteractivePointActive();
                {
                    //ip.Color = (m_optionPxMode == OptionPxMode.Ask) ? Colors.DarkOrange : Colors.DarkCyan;
                    //ip.DragableMode = DragableMode.None;
                    //ip.Geometry = Geometries.Rect; // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect;
                    //ip.IsActive = true;
                    ip.Value = new Point(strike, sigma);
                    string nowStr = DateTime.Now.ToString(DateTimeFormatWithMs, CultureInfo.InvariantCulture);
                    ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "K:{0}; IV:{1:#0.00}%\r\n{2} {3} @ {4}\r\nDate: {5}",
                                               strike, sigma * Constants.PctMult, m_optionType, optPx, 1, nowStr);
                }

                InteractiveObject obj = new InteractiveObject(ip);

                if (m_optionType == StrikeType.Put)
                {
                    if (!Double.IsNaN(putSigma))
                    {
                        //FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Put, m_optionPxMode, putPx, putQty, putSigma, putTime, false);
                        controlPoints.Add(obj);
                    }
                }
                else if (m_optionType == StrikeType.Call)
                {
                    if (!Double.IsNaN(callSigma))
                    {
                        //FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Call, m_optionPxMode, callPx, callQty, callSigma, callTime, false);
                        controlPoints.Add(obj);
                    }
                }
                else if (m_optionType == StrikeType.Any)
                {
                    if (!Double.IsNaN(stradleSigma))
                    {
                        if (m_optionPxMode == OptionPxMode.Ask)
                        {
                            //if (putSigma < callSigma)
                            //    FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Put, m_optionPxMode, putPx, putQty, putSigma, putTime, false);
                            //else
                            //    FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Call, m_optionPxMode, callPx, callQty, callSigma, callTime, false);
                        }
                        else if (m_optionPxMode == OptionPxMode.Bid)
                        {
                            //if (putSigma > callSigma)
                            //    FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Put, m_optionPxMode, putPx, putQty, putSigma, putTime, false);
                            //else
                            //    FillNodeInfoDeribit(ip, f, dT, sInfo, StrikeType.Call, m_optionPxMode, callPx, callQty, callSigma, callTime, false);
                        }

                        controlPoints.Add(obj);
                    }
                }
            }

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

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

            SmileInfo info;

            if (!IvSmile.TryPrepareSmileInfo(m_context, f, dT, rate, new DateTime(), new DateTime(), null, res, out info))
            {
                return(Constants.EmptySeries);
            }

            return(res);
        }
Пример #26
0
        /// <summary>
        /// Получить финансовые параметры опционной позиции (один опцион)
        /// </summary>
        /// <param name="positions">список закрытых и открытых позиций</param>
        /// <param name="curBar">номер рабочего бара</param>
        /// <param name="f">текущая цена БА</param>
        /// <param name="k">страйк</param>
        /// <param name="dT">время до экспирации</param>
        /// <param name="sigma">волатильность</param>
        /// <param name="r">процентная ставка</param>
        /// <param name="isCall">put-false; call-true</param>
        /// <param name="cash">денежные затраты на формирование позы (могут быть отрицательными)</param>
        /// <param name="pnl">текущая цена позиции</param>
        public static void GetOptPnl(IList <IPosition> positions, int curBar,
                                     double f, double k, double dT, double sigma, double r, bool isCall, double btcUsdInd,
                                     out CashPnlUsd cashPnlUsd, out CashPnlBtc cashPnlBtc)
        {
            if (positions.Count == 0)
            {
                cashPnlUsd = new CashPnlUsd();
                cashPnlBtc = new CashPnlBtc();
                return;
            }

            {
                var msg = $"Как получился отрицательный курс BTC/USD? btcUsdInd:{btcUsdInd}";
                Contract.Assert(DoubleUtil.IsPositive(btcUsdInd), msg);
                if (!DoubleUtil.IsPositive(btcUsdInd))
                {
                    throw new ArgumentException(msg, nameof(btcUsdInd));
                }
            }

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

            foreach (IPosition pos in positions)
            {
                int    sign = pos.IsLong ? 1 : -1;
                double qty  = Math.Abs(pos.Shares);
                // Знак "минус" стоит в честь того, что при покупке инструмента наличные средства уменьшаются
                double locCashBtcEntry = sign * pos.GetBalancePrice(curBar) * qty;
                cashBtc -= locCashBtcEntry;
                cashUsd -= locCashBtcEntry * btcUsdInd;
                double optPxUsd       = FinMath.GetOptionPrice(f, k, dT, sigma, r, isCall);
                double locPnlUsdEntry = sign * optPxUsd * qty;
                pnlUsd += locPnlUsdEntry;
                pnlBtc += locPnlUsdEntry / btcUsdInd;

                // Учет комиссии (комиссия в битках по идее)
                cashBtc -= pos.EntryCommission;
                cashUsd -= pos.EntryCommission * btcUsdInd;

                if (!pos.IsActiveForBar(curBar))
                {
                    // Знак "ПЛЮС" стоит в честь того, что при ЗАКРЫТИИ ЛОНГА наличные средства УВЕЛИЧИВАЮТСЯ
                    double locCashBtcExit = sign * pos.ExitPrice * qty;
                    cashBtc += locCashBtcExit;
                    cashUsd += locCashBtcExit * btcUsdInd;
                    double locPnlUsdExit = sign * optPxUsd * qty;
                    pnlUsd -= locPnlUsdExit;
                    pnlBtc -= locPnlUsdExit / btcUsdInd;

                    // Учет комиссии (комиссия в битках по идее)
                    cashBtc -= pos.ExitCommission;
                    cashUsd -= pos.ExitCommission * btcUsdInd;
                }
            } // End foreach (IPosition pos in positions)

            cashPnlUsd = new CashPnlUsd(cashUsd, pnlUsd);
            cashPnlBtc = new CashPnlBtc(cashBtc, pnlBtc);
        }
Пример #27
0
        public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, int barNum)
        {
            int barsCount = ContextBarsCount;

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

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

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

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

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

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

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

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

            while (f <= m_maxStrike)
            {
                double rawDelta;
                GetBaseDelta(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out rawDelta);

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

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

                controlPoints.Add(new InteractiveObject(ip));

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

                f += m_strikeStep;
            }

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

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

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

                    NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys);

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

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

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

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

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

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

            double futPx = price;
            double dT    = time;

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

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

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

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

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

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

            double ivAtm;

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

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

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

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

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

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

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

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

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

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

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

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

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

            m_displayIv.Value = displayValue;

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

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

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

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

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

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

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

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

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

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

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

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

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

            SetHandlerInitialized(now, true);

            return(res);
        }