예제 #1
0
        public double Execute(ISecurity sec, int barNum)
        {
            List <double> positionQtys = PreparePositionQtys();

            // Гарантируем совпадение по числу элементов
            int barsCount = m_context.BarsCount;

            if (barsCount <= barNum)
            {
                string msg = String.Format("[{0}] (BarsCount <= barNum)! BarsCount:{1}; barNum:{2}",
                                           GetType().Name, m_context.BarsCount, barNum);
                m_context.Log(msg, MessageType.Warning, true);
            }
            for (int j = positionQtys.Count; j < Math.Max(barsCount, barNum + 1); j++)
            {
                positionQtys.Add(Constants.NaN);
            }

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

            double res;

            {
                PositionsManager posMan = PositionsManager.GetManager(m_context);
                if (posMan != null)
                {
                    res = posMan.GetTotalQty(sec, barNum);
                }
                else
                {
                    res = GetTotalQty(sec, barNum);
                }
            }

            if (m_context.IsFixedBarsCount)
            {
                // В этом режиме сдвигаю все значения влево
                // Если мы уже набрали в буфер необходимое число баров
                if (barsCount <= positionQtys.Count)
                {
                    for (int j = 0; j < positionQtys.Count - 1; j++)
                    {
                        positionQtys[j] = positionQtys[j + 1];
                    }
                }
            }

            positionQtys[barNum] = res;

            m_openQty.Value = res;

            return(res);
        }
예제 #2
0
        protected override bool TryCalculate(Dictionary <DateTime, double> history, DateTime now, int barNum, object[] args, out double val)
        {
            IOption opt = (IOption)args[0];

            double           risk   = 0;
            DateTime         today  = now.Date;
            PositionsManager posMan = PositionsManager.GetManager(m_context);

            foreach (IOptionSeries optSer in opt.GetSeries())
            {
                if (optSer.ExpirationDate.Date < today)
                {
                    continue;
                }

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

                    risk += Math.Abs(putQty + callQty);
                }
            }

            val = risk;

            return(true);
        }
예제 #3
0
        private void InteractiveSplineOnClickEvent(object sender, InteractiveActionEventArgs eventArgs)
        {
            PositionsManager posMan = PositionsManager.GetManager(m_context);

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

            SmileNodeInfo nodeInfo = eventArgs.Point.Tag as SmileNodeInfo;

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

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

            // Передаю событие в PositionsManager дополнив его инфой о количестве лотов
            posMan.InteractiveSplineOnClickEvent(m_context, sender, eventArgs);
        }
예제 #4
0
        public void Execute(int barNum)
        {
            int barsCount = m_context.BarsCount;

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

            if (m_dropVirtualPositions)
            {
                try
                {
                    PositionsManager posMan = PositionsManager.GetManager(m_context);
                    m_context.Log("All virtual positions will be dropped right now.", MessageType.Warning, true);
                    posMan.DropVirtualPositions(m_context);

                    // Безтолку делать повторный пересчет
                    //context.Recalc(true);
                }
                finally
                {
                    m_dropVirtualPositions = false;
                }
            }
        }
예제 #5
0
        public double Execute(ISecurity sec, int barNum)
        {
            List <double> positionProfits = PreparePositionProfits();

            int len = m_context.BarsCount;

            for (int j = positionProfits.Count; j < len; j++)
            {
                positionProfits.Add(Constants.NaN);
            }

            {
                int barsCount = m_context.BarsCount;
                if (!m_context.IsLastBarUsed)
                {
                    barsCount--;
                }
                if (barNum < barsCount - 1)
                {
                    return(positionProfits[barNum]);
                }
            }

            if (sec == null)
            {
                return(Constants.NaN);
            }

            double           cache, pnl;
            IDataBar         bar       = sec.Bars[barNum];
            PositionsManager posMan    = PositionsManager.GetManager(m_context);
            double           rawProfit = posMan.GetTotalProfit(sec, barNum, m_algo, bar.Close, out cache, out pnl);

            if (m_context.IsFixedBarsCount)
            {
                // В этом режиме сдвигаю все значения влево
                // Если мы уже набрали в буфер необходимое число баров
                if (len <= positionProfits.Count)
                {
                    for (int j = 0; j < positionProfits.Count - 1; j++)
                    {
                        positionProfits[j] = positionProfits[j + 1];
                    }
                }
            }

            // Пересчитываю прибыль в привычные денежные единицы
            rawProfit *= ScaleMultiplier;
            positionProfits[barNum] = rawProfit; // заполняю индекс barNumber

            m_profit.Value = rawProfit;
            if (PrintProfitInLog)
            {
                m_context.Log(MsgId + ": " + m_profit.Value, MessageType.Info, PrintProfitInLog);
            }

            return(rawProfit);
        }
예제 #6
0
        private void InteractiveSplineOnClickEvent(object sender, InteractiveActionEventArgs eventArgs)
        {
            OptionPxMode pxMode = m_isLong ? OptionPxMode.Ask : OptionPxMode.Bid;

            PositionsManager posMan = PositionsManager.GetManager(m_context);
            // Из практики торговли часто бывает ситуация, что торговля заблокирована, а снять задачу котирования УЖЕ хочется.
            //if (posMan.BlockTrading)
            //{
            //    //string msg = String.Format("[{0}] Trading is blocked. Please, change 'Block Trading' parameter.", m_optionPxMode);
            //    string msg = String.Format(RM.GetString("OptHandlerMsg.PositionsManager.TradingBlocked"), pxMode);
            //    m_context.Log(msg, MessageType.Info, true);
            //    return;
            //}

            InteractivePointActive tmp = eventArgs.Point;

            if ((tmp.Tag == null) || (!(tmp.Tag is PositionsManager.IvTargetInfo)))
            {
                string msg = String.Format("[{0}.ClickEvent] Denied #1", GetType().Name);
                m_context.Log(msg, MessageType.Warning, false);
                return;
            }

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

            var ivTarget = tmp.Tag as PositionsManager.IvTargetInfo;

            // Передаю событие в PositionsManager
            //posMan.InteractiveSplineOnQuoteIvEvent(m_context, sender, eventArgs);
            // ОЧЕНЬ БОЛЬШОЙ ВОПРОС ЧТО БУДЕТ С КОНТЕКСТОМ ПРИ ЭТОМ???
            int res = posMan.CancelVolatility(m_context, ivTarget, "Left-click");

            if (res > 0)
            {
                string msg = String.Format(RM.GetString("OptHandlerMsg.PositionsManager.IvTargetCancelled"), pxMode, ivTarget);
                m_context.Log(msg, MessageType.Info, true, new Dictionary <string, object> {
                    { "VOLATILITY_ORDER_CANCELLED", msg }
                });

                // Вызываем принудительный пересчет агента, чтобы немедленно убрать заявку из стакана
                m_context.Recalc();
            }
        }
예제 #7
0
        private void InteractiveSplineOnClickEvent(object sender, InteractiveActionEventArgs eventArgs)
        {
            PositionsManager posMan = PositionsManager.GetManager(m_context);

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

            List <InteractiveActionEventArgs> onClickEvents = m_context.LoadObject(VariableId + "OnClickEvent") as List <InteractiveActionEventArgs>;

            if (onClickEvents == null)
            {
                onClickEvents = new List <InteractiveActionEventArgs>();
                m_context.StoreObject(VariableId + "OnClickEvent", onClickEvents);
            }

            onClickEvents.Add(eventArgs);

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

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

            PositionsManager posMan = PositionsManager.GetManager(m_context);

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

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

            SmileNodeInfo nodeInfo = eventArgs.Point.Tag as SmileNodeInfo;

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

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

            nodeInfo.ClickTime = DateTime.Now;

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

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

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

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

            // Передаю событие в PositionsManager дополнив его инфой о количестве лотов
            posMan.InteractiveSplineOnClickEvent(m_context, sender, eventArgs);
        }
예제 #9
0
        public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, int barNum)
        {
            //InteractiveSeries res = context.LoadObject(cashKey + "positionDelta") as InteractiveSeries;
            //if (res == null)
            //{
            //    res = new InteractiveSeries();
            //    context.StoreObject(cashKey + "positionDelta", res);
            //}

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

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

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

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

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

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

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

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

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

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

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

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

                    controlPoints.Add(new InteractiveObject(ip));

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

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

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

                    NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys);

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

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

            return(res);
        }
예제 #10
0
        public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, int barNum)
        {
            InteractiveSeries res = new InteractiveSeries();
            int barsCount         = ContextBarsCount;

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

            double dT = time;

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

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

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

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

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

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

            foreach (InteractiveObject iob in smilePoints)
            {
                double rawTheta, f = iob.Anchor.ValueX;
                if (TryEstimateTheta(posMan, pairs, smile, m_greekAlgo, f, dT, m_tStep, out rawTheta))
                {
                    // Переводим тету в дифференциал 'изменение цены за 1 сутки'.
                    rawTheta = RescaleThetaToDays(m_tRemainMode, rawTheta);

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

                    controlPoints.Add(new InteractiveObject(ip));

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

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

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

                    NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys);

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

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

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

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

            IOptionStrikePair pair;

            if (!optSer.TryGetStrikePair(strike, out pair))
            {
                return(Constants.NaN);
            }

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

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

            PositionsManager posMan = PositionsManager.GetManager(m_context);

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

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

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

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

            // Набираем риск
            if (risk < maxRisk)
            {
                double ivAtm;
                if ((!sInfo.ContinuousFunction.TryGetValue(strike, out ivAtm)) || Double.IsNaN(ivAtm) || (ivAtm < Double.Epsilon))
                {
                    string msg = String.Format("[{0}.{1}] Unable to get IV at strike {2}. ivAtm:{3}",
                                               Context.Runtime.TradeName, GetType().Name, pair.Strike, ivAtm);
                    m_context.Log(msg, MessageType.Error, true);
                    return(Constants.NaN);
                }

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

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

                if (m_optionType == StrikeType.Put)
                {
                    #region В путах
                    ISecurity sec = pair.Put.Security;
                    double    qty = Math.Abs(m_fixedQty);
                    qty = GetSafeQty(risk, maxRisk, qty, putRisk);
                    if (qty > 0)
                    {
                        double px      = SellOptions.SafeMinPrice(theorPutPx + m_entryShift * sec.Tick, putPx, sec);
                        double iv      = FinMath.GetOptionSigma(futPx, pair.Strike, dT, px, 0, false);
                        string sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                        posMan.BuyAtPrice(m_context, sec, qty, px, "Open BUY", sigName);

                        m_context.Log(sigName, MessageType.Info, false);
                    }
                    #endregion В путах
                }
                else if (m_optionType == StrikeType.Call)
                {
                    #region В колах
                    ISecurity sec = pair.Call.Security;
                    double    qty = Math.Abs(m_fixedQty);
                    qty = GetSafeQty(risk, maxRisk, qty, callRisk);
                    if (qty > 0)
                    {
                        double px      = SellOptions.SafeMinPrice(theorCallPx + m_entryShift * sec.Tick, callPx, sec);
                        double iv      = FinMath.GetOptionSigma(futPx, pair.Strike, dT, px, 0, true);
                        string sigName = String.Format("Risk:{0}; MaxRisk:{1}; Px:{2}; Qty:{3}; IV:{4:P2}; dT:{5}", risk, maxRisk, px, qty, iv, dT);
                        posMan.BuyAtPrice(m_context, sec, qty, px, "Open BUY", sigName);

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

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

                            executedQty += (int)qty;
                        }
                    }

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

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

                            //executedQty += (int)qty;
                        }
                    }
                    #endregion В оба вида опционов сразу встаю
                }
                #endregion Набираем риск
            }
            else if (risk > maxRisk)
            {
                string msg;
                //string msg = String.Format("[DEBUG:{0}] risk:{1}; maxRisk:{2}", Context.Runtime.TradeName, risk, maxRisk);
                //m_context.Log(msg, MessageType.Info, true);

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

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

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

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

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

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

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

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

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

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

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

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

                                executedQty += (int)qty;
                            }

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

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

                                executedQty += (int)qty;
                            }

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

            return(Constants.NaN);
        }
예제 #13
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);
        }
        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);
        }
예제 #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 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);
        }
예제 #16
0
        private void RouteOnClickEvents(List <InteractiveActionEventArgs> eventArgs)
        {
            if (eventArgs == null)
            {
                return;
            }

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

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

            bool recalc = false;

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

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

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

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

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

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

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

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

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

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

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

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

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

            PositionsManager posMan = PositionsManager.GetManager(m_context);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                                executedQty += (int)qty;
                            }

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

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

                                executedQty += (int)qty;
                            }

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

            return(Constants.NaN);
        }
예제 #18
0
        private void InteractiveSplineOnClickEvent(object sender, InteractiveActionEventArgs eventArgs)
        {
            PositionsManager posMan = PositionsManager.GetManager(m_context);

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

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

            SmileNodeInfo nodeInfo = eventArgs.Point.Tag as SmileNodeInfo;

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

            nodeInfo.ClickTime = DateTime.Now;

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

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

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

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

            // Передаю событие в PositionsManager дополнив его инфой о количестве лотов
            posMan.InteractiveSplineOnClickEvent(m_context, sender, eventArgs);
        }
예제 #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.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);
        }
예제 #21
0
        public InteractiveSeries Execute(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);

            IOptionStrike[]  options = optSer.GetStrikes().ToArray();
            PositionsManager posMan  = PositionsManager.GetManager(m_context);

            //if (Context.Runtime.IsAgentMode)
            //{
            // PROD-6089 - Если мы в режиме агента, значит все инструменты уже ISecurityRt
            //    SortedList<DateTime, IOrder> sortedOrders = new SortedList<DateTime, IOrder>(s_comparer);
            //}

            SortedList <DateTime, IPosition> sortedPos = new SortedList <DateTime, IPosition>(s_comparer);

            if (m_countFutures)
            {
                ReadOnlyCollection <IPosition> futPositions = posMan.GetActiveForBar(optSer.UnderlyingAsset);
                if (futPositions.Count > 0)
                {
                    for (int j = 0; j < futPositions.Count; j++)
                    {
                        IPosition pos = futPositions[j];
                        AddSafely(sortedPos, pos);
                    }
                }
            }

            for (int m = 0; m < options.Length; m++)
            {
                IOptionStrike optStrike = options[m];
                ReadOnlyCollection <IPosition> optPositions = posMan.GetActiveForBar(optStrike.Security);
                if (optPositions.Count > 0)
                {
                    for (int j = 0; j < optPositions.Count; j++)
                    {
                        IPosition pos = optPositions[j];
                        AddSafely(sortedPos, pos);
                    }
                }
            }

            int counter = 0;
            List <InteractiveObject> controlPoints = new List <InteractiveObject>();

            foreach (var node in sortedPos)
            {
                InteractivePointActive ip = new InteractivePointActive();
                ip.IsActive = true;
                //ip.DragableMode = DragableMode.None;
                ip.DateTime = node.Key;
                ip.ValueX   = 0; // все одинаковые 'страйки' имеют
                ip.ValueY   = PrepareValue(node, barNum, m_displayMode);
                ip.Tooltip  = PrepareTooltip(node, barNum, m_displayMode);

                controlPoints.Add(new InteractiveObject(ip));

                counter++;
                if (counter >= m_maxPositions)
                {
                    break;
                }
            }

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

            // Задаю свойство для отображения
            switch (m_displayMode)
            {
            case PositionGridDisplayMode.Px:
            case PositionGridDisplayMode.Qty:
                res.DisplayProperty.Name = nameof(IInteractivePointLight.ValueY);
                break;

            default:
                res.DisplayProperty.Name = nameof(InteractivePointActive.Tooltip);
                break;
            }
            res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints);

            SetHandlerInitialized(now);

            return(res);
        }
예제 #22
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);
        }
예제 #23
0
        public InteractiveSeries Execute(IOptionSeries optSer, int barNum)
        {
            int barsCount = ContextBarsCount;

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

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

            if (m_countFutures)
            {
                var futPositions = posMan.GetClosedOrActiveForBar(optSer.UnderlyingAsset);
                if (futPositions.Count > 0)
                {
                    double futQty, futCommission;
                    SingleSeriesProfile.GetTotalCommission(futPositions, barNum, m_longPositions, out futCommission, out futQty);

                    if (!DoubleUtil.IsZero(futQty))
                    {
                        InteractivePointActive ip = new InteractivePointActive(0, futQty);
                        ip.IsActive = true;
                        //ip.DragableMode = DragableMode.None;
                        //ip.Geometry = Geometries.Rect;
                        //ip.Color = Colors.DarkOrange;
                        ip.Tooltip = String.Format("Fut commission:{0}", futCommission);

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

            for (int j = 0; j < pairs.Length; j++)
            {
                IOptionStrikePair pair = pairs[j];
                double            putQty = 0, putCommission = Double.NaN;
                {
                    var putPositions = posMan.GetClosedOrActiveForBar(pair.Put.Security);
                    if (putPositions.Count > 0)
                    {
                        SingleSeriesProfile.GetTotalCommission(putPositions, barNum, m_longPositions, out putCommission, out putQty);
                    }
                }

                double callQty = 0, callCommission = Double.NaN;
                {
                    var callPositions = posMan.GetClosedOrActiveForBar(pair.Call.Security);
                    if (callPositions.Count > 0)
                    {
                        SingleSeriesProfile.GetTotalCommission(callPositions, barNum, m_longPositions, out callCommission, out callQty);
                    }
                }

                if ((!DoubleUtil.IsZero(putQty)) || (!DoubleUtil.IsZero(callQty)))
                {
                    double y = 0;
                    switch (m_optionType)
                    {
                    case StrikeType.Put:
                        y = putCommission;
                        break;

                    case StrikeType.Call:
                        y = callCommission;
                        break;

                    case StrikeType.Any:
                        y = putCommission + callCommission;
                        break;

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

                    // Не хочу видеть ячейки таблицы с NaN
                    if (Double.IsNaN(y))
                    {
                        continue;
                    }

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

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

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

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

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

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

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

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

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

            PositionsManager posMan = PositionsManager.GetManager(m_context);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                                    executedQty += (int)qty;
                                }
                            }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                                executedQty += (int)qty;
                            }

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

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

                                executedQty += (int)qty;
                            }

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

            return(Constants.NaN);
        }
예제 #25
0
        private void InteractiveSplineOnQuoteIvEvent(object sender, InteractiveActionEventArgs eventArgs)
        {
            OptionPxMode pxMode = (m_qty > 0) ? OptionPxMode.Ask : OptionPxMode.Bid;

            PositionsManager posMan = PositionsManager.GetManager(m_context);

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

            InteractivePointActive tmp = eventArgs.Point;

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

            SmileNodeInfo nodeInfo = tmp.Tag as SmileNodeInfo;

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

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

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

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

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

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

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

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

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

            // Передаю событие в PositionsManager
            //posMan.InteractiveSplineOnClickEvent(m_context, sender, eventArgs);
            posMan.InteractiveSplineOnQuoteIvEvent(m_context, sender, eventArgs);
        }
예제 #26
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);
        }
예제 #27
0
        public InteractiveSeries Execute(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);

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

            if (m_countFutures)
            {
                var futPositions = posMan.GetClosedOrActiveForBar(optSer.UnderlyingAsset);
                if (futPositions.Count > 0)
                {
                    double futQty, futAvgPx;
                    SingleSeriesProfile.GetAveragePrice(futPositions, barNum, m_longPositions, out futAvgPx, out futQty);

                    if (!DoubleUtil.IsZero(futQty))
                    {
                        double valueToDisplay = m_countQty ? futQty : futAvgPx;

                        // ReSharper disable once UseObjectOrCollectionInitializer
                        InteractivePointActive ip = new InteractivePointActive(0, valueToDisplay);
                        ip.IsActive = true;
                        //ip.DragableMode = DragableMode.None;
                        //ip.Geometry = Geometries.Rect;
                        //ip.Color = Colors.DarkOrange;
                        ip.Tooltip = String.Format("AvgPx:{0}; Qty:{1}", futAvgPx, futQty);

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

            for (int j = 0; j < pairs.Length; j++)
            {
                IOptionStrikePair pair = pairs[j];
                double            putQty = 0, putAvgPx = Double.NaN;
                {
                    var putPositions = posMan.GetClosedOrActiveForBar(pair.Put.Security);
                    if (putPositions.Count > 0)
                    {
                        SingleSeriesProfile.GetAveragePrice(putPositions, barNum, m_longPositions, out putAvgPx, out putQty);
                    }
                }

                double callQty = 0, callAvgPx = Double.NaN;
                {
                    var callPositions = posMan.GetClosedOrActiveForBar(pair.Call.Security);
                    if (callPositions.Count > 0)
                    {
                        SingleSeriesProfile.GetAveragePrice(callPositions, barNum, m_longPositions, out callAvgPx, out callQty);
                    }
                }

                if ((!DoubleUtil.IsZero(putQty)) || (!DoubleUtil.IsZero(callQty)))
                {
                    double averagePrice = 0, lotSize = 0;
                    switch (m_optionType)
                    {
                    case StrikeType.Put:
                        averagePrice = putAvgPx;
                        lotSize      = putQty;
                        break;

                    case StrikeType.Call:
                        averagePrice = callAvgPx;
                        lotSize      = callQty;
                        break;

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

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

                    // Не хочу видеть ячейки таблицы с NaN
                    if (Double.IsNaN(averagePrice))
                    {
                        continue;
                    }

                    if (!DoubleUtil.IsZero(lotSize))
                    {
                        double valueToDisplay = m_countQty ? lotSize : averagePrice;

                        // ReSharper disable once UseObjectOrCollectionInitializer
                        InteractivePointActive ip = new InteractivePointActive(pair.Strike, valueToDisplay);
                        ip.IsActive = true;
                        //ip.DragableMode = DragableMode.None;
                        //ip.Geometry = Geometries.Rect;
                        //ip.Color = Colors.DarkOrange;
                        ip.Tooltip = String.Format("K:{0}; AvgPx:{1}; Qty:{2}", pair.Strike, averagePrice, lotSize);

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

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

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

            SetHandlerInitialized(now);

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