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

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

            IReadOnlyList <InteractiveObject> cps = optPrices.ControlPoints;

            double f  = price;
            double dT = time;

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

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

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

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

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

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

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

                InteractiveObject obj = new InteractiveObject(ip);

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

                        controlPoints.Add(obj);
                    }
                }
            }

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

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

            SmileInfo info;

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

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

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

            double f  = price;
            double dT = time;

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

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

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

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

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

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

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

                InteractiveObject obj = new InteractiveObject(ip);

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

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

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

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

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

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

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

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

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

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

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

            PositionsManager posMan = PositionsManager.GetManager(m_context);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                                executedQty += (int)qty;
                            }

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

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

                                executedQty += (int)qty;
                            }

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

            return(Constants.NaN);
        }
Beispiel #5
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);
        }
Beispiel #6
0
        /// <summary>
        /// Обработчик под тип входных данных OPTION_SERIES
        /// </summary>
        /// <param name="prices">цена БА</param>
        /// <param name="times">время до экспирации в долях года</param>
        /// <param name="optSer">опционная серия</param>
        /// <param name="rates">процентные ставки</param>
        /// <returns>улыбка, восстановленная из цен опционов</returns>
        public InteractiveSeries Execute(IList <double> prices, IList <double> times, IOptionSeries optSer, IList <double> rates)
        {
            if (prices.Count <= 0)
            {
                //throw new ScriptException("There should be some values in first argument 'prices'.");
                return(Constants.EmptySeries);
            }

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

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

            double f    = prices[prices.Count - 1];
            double dT   = times[times.Count - 1];
            double rate = rates[rates.Count - 1];

            if (Double.IsNaN(f))
            {
                //throw new ScriptException("Argument 'prices' contains NaN for some strange reason. F:" + F);
                return(Constants.EmptySeries);
            }
            if ((dT < Double.Epsilon) || (Double.IsNaN(dT)))
            {
                return(Constants.EmptySeries);
            }
            if (Double.IsNaN(rate))
            {
                //throw new ScriptException("Argument 'rate' contains NaN for some strange reason. rate:" + rate);
                return(Constants.EmptySeries);
            }

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

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

                double   putPx, callPx;
                double   putQty, callQty;
                DateTime putTime, callTime;
                {
                    putPx = GetOptPrice(m_context, f, sInfo.Put, m_optionPxMode, sInfo.Tick * m_shiftAsk, sInfo.Tick * m_shiftBid, out putQty, out putTime);
                }

                {
                    callPx = GetOptPrice(m_context, f, sInfo.Call, m_optionPxMode, sInfo.Tick * m_shiftAsk, sInfo.Tick * m_shiftBid, out callQty, out callTime);
                }

                double putSigma = Double.NaN, callSigma = Double.NaN, precision;
                if (!Double.IsNaN(putPx))
                {
                    putSigma = FinMath.GetOptionSigma(f, sInfo.Strike, dT, putPx, rate, false, out precision);
                    putSigma = Math.Min(putSigma, m_maxSigma);
                    if (putSigma <= 0)
                    {
                        putSigma = Double.NaN;
                    }
                }
                if (!Double.IsNaN(callPx))
                {
                    callSigma = FinMath.GetOptionSigma(f, sInfo.Strike, dT, callPx, rate, true, out precision);
                    callSigma = Math.Min(callSigma, m_maxSigma);
                    if (callSigma <= 0)
                    {
                        callSigma = Double.NaN;
                    }
                }

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

                InteractiveObject obj = new InteractiveObject(ip);

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

                        controlPoints.Add(obj);
                    }
                    else if ((!Double.IsNaN(putSigma)) && (Double.IsNaN(callSigma)))
                    {
                        FillNodeInfo(ip, f, dT, sInfo, StrikeType.Put, m_optionPxMode, putPx, putQty, putSigma, putTime, false, rate);
                        controlPoints.Add(obj);
                    }
                    else if ((Double.IsNaN(putSigma)) && (!Double.IsNaN(callSigma)))
                    {
                        FillNodeInfo(ip, f, dT, sInfo, StrikeType.Call, m_optionPxMode, callPx, callQty, callSigma, callTime, false, rate);
                        controlPoints.Add(obj);
                    }
                }
            }

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

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

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

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

            return(res);
        }