Ejemplo n.º 1
0
        public static double GetOptPx(this double strikePrice, double strikeStep, bool isCall, double price, double dT, double sigma)
        {
            var optPx = FinMath.GetOptionPrice(price, strikePrice, dT, sigma, 0, isCall);

            optPx /= price;
            optPx  = Math.Truncate(optPx / strikeStep + 1) * strikeStep;
            return(Math.Max(optPx, strikeStep));
        }
Ejemplo n.º 2
0
        public void TestAbs()
        {
            Assert.AreEqual(5, FinMath.Abs(5));
            Assert.AreEqual(5, FinMath.Abs(-5));

            Assert.AreEqual(5d, FinMath.Abs(5d));
            Assert.AreEqual(5d, FinMath.Abs(-5d));
        }
Ejemplo n.º 3
0
        private void ProcessScheduledJump_()
        {
            if (this.scheduledJumpState_ == ScheduledState.STARTING)
            {
                if (this.StateMachine.CanJumpFromGround)
                {
                    var isLongjump = this.StateMachine.State == PlayerState.SLIDING;
                    var isBackflip = this.StateMachine.State == PlayerState.TURNING &&
                                     FinMath.Abs(this.Rigidbody.XVelocity) >
                                     PlayerConstants.UPRIGHT_MAX_SLOW_XSPD;
                    var newYVel =
                        isLongjump
                  ? PlayerConstants.LONGJUMP_SPEED
                  : isBackflip
                      ? PlayerConstants.BACKFLIP_JUMP_SPEED
                      : PlayerConstants.JUMP_SPEED;
                    var newState =
                        isLongjump ? PlayerState.LONGJUMPING :
                        isBackflip ? PlayerState.BACKFLIPPING : PlayerState.JUMPING;

                    this.Rigidbody.YVelocity = newYVel;
                    this.StateMachine.State  = newState;

                    // TODO: Longjump feels weird.
                    if (isLongjump)
                    {
                        this.Rigidbody.XVelocity = this.Rigidbody.XVelocity /
                                                   PlayerConstants.UPRIGHT_MAX_FAST_XSPD *
                                                   PlayerConstants.LONGJUMP_MAX_XSPD;
                    }
                    else if (isBackflip)
                    {
                        // Instantly flip horizontal velocity.
                        var moveDirection = FinMath.Sign(this.Rigidbody.XVelocity);
                        this.Rigidbody.XVelocity =
                            -moveDirection * PlayerConstants.BACKFLIP_XSPD;
                    }
                }
                else if (this.StateMachine.State == PlayerState.WALL_SLIDING)
                {
                    this.StateMachine.State  = PlayerState.WALLJUMPING;
                    this.Rigidbody.YVelocity = PlayerConstants.JUMP_SPEED;
                    this.Rigidbody.XVelocity =
                        (this.StateMachine.WallSlidingOnLeft ? 1 : -1) *
                        PlayerConstants.WALL_SLIDING_XSPD;
                }
            }
            else if (this.scheduledJumpState_ == ScheduledState.STOPPING)
            {
                if (this.StateMachine.CanStallJumpingMomentum &&
                    this.Rigidbody.YVelocity < 0)
                {
                    this.StateMachine.State   = PlayerState.FALLING;
                    this.Rigidbody.YVelocity /= 2;
                }
            }
        }
Ejemplo n.º 4
0
        public void TestSign()
        {
            Assert.AreEqual(-1, FinMath.Sign(-1));
            Assert.AreEqual(1, FinMath.Sign(1));
            Assert.AreEqual(0, FinMath.Sign(0));

            Assert.AreEqual(-1, FinMath.Sign(-1d));
            Assert.AreEqual(1, FinMath.Sign(1d));
            Assert.AreEqual(0, FinMath.Sign(0d));
        }
Ejemplo n.º 5
0
        public void TestMin()
        {
            Assert.AreEqual(0, FinMath.Min(0, 1));
            Assert.AreEqual(0, FinMath.Min(1, 0));

            Assert.AreEqual(1L, FinMath.Min(1L, 2L));

            Assert.AreEqual(.123, FinMath.Min(.123, .234));

            Assert.AreEqual(0x01, FinMath.Min(0x01, 0x02));
        }
Ejemplo n.º 6
0
        public void TestMax()
        {
            Assert.AreEqual(1, FinMath.Max(0, 1));
            Assert.AreEqual(1, FinMath.Max(1, 0));

            Assert.AreEqual(2L, FinMath.Max(1L, 2L));

            Assert.AreEqual(.234, FinMath.Max(.123, .234));

            Assert.AreEqual(0x02, FinMath.Max(0x01, 0x02));
        }
Ejemplo n.º 7
0
        public void TestMod()
        {
            Assert.AreEqual(0, FinMath.Mod(10, 2));
            Assert.AreEqual(1, FinMath.Mod(10, 3));

            Assert.AreEqual(1L, FinMath.Mod(10L, 3L));

            Assert.AreEqual(.111, FinMath.Mod(.234, .123), .0001);

            Assert.AreEqual(0x01, FinMath.Mod(0x10, 0x03));
        }
Ejemplo n.º 8
0
        public double GetRandom()
        {
            double SearchValue = FreeQuant.Quant.Random.Rndm();
            int    Index       = FinMath.BinarySearch(this.fNBins, this.GetIntegral(), SearchValue);

            if (Index >= 0 && Index < this.fNBins)
            {
                return(this.GetBinMin(Index) + this.fBinSize * (SearchValue - this.fIntegral[Index]) / (this.fIntegral[Index + 1] - this.fIntegral[Index]));
            }
            Console.WriteLine("Index111" + Index);
            return(0.0);
        }
Ejemplo n.º 9
0
        internal static void FillNodeInfo(InteractivePointActive ip,
                                          double f, double dT, IOptionStrikePair sInfo,
                                          StrikeType optionType, OptionPxMode optPxMode,
                                          double optSigma, bool returnPct, bool isVisiblePoints, double scaleMult, double riskfreeRatePct)
        {
            if (optionType == StrikeType.Any)
            {
                throw new ArgumentException("Option type 'Any' is not supported.", "optionType");
            }

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

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

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

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

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

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

            ip.Tooltip = String.Format(CultureInfo.InvariantCulture,
                                       " K: {0}; IV: {1:#0.00}%\r\n   {2} px {3}",
                                       sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx);
        }
Ejemplo n.º 10
0
        public void TestClamp()
        {
            Assert.AreEqual(0, FinMath.Clamp(-1, 0, 1));
            Assert.AreEqual(-1, FinMath.Clamp(-1, -1, 1));
            Assert.AreEqual(1, FinMath.Clamp(-1, 1, 1));
            Assert.AreEqual(1, FinMath.Clamp(-1, 2, 1));
            Assert.AreEqual(-1, FinMath.Clamp(-1, -2, 1));

            Assert.AreEqual(1L, FinMath.Clamp(0L, 2L, 1L));

            Assert.AreEqual(.234, FinMath.Clamp(.123, 456d, .234));

            Assert.AreEqual(-0x01, FinMath.Clamp(-0x01, -0x10, 0x01));
        }
Ejemplo n.º 11
0
        public void TestWrap()
        {
            Assert.AreEqual(0, FinMath.Wrap(-1, 0, 1));
            Assert.AreEqual(-1, FinMath.Wrap(-1, -1, 1));
            Assert.AreEqual(-1, FinMath.Wrap(-1, 1, 1));
            Assert.AreEqual(-1, FinMath.Wrap(-2, 3, 2));
            Assert.AreEqual(1, FinMath.Wrap(-2, -3, 2));

            Assert.AreEqual(1L, FinMath.Wrap(1L, 3L, 2L));

            Assert.AreEqual(.124, FinMath.Wrap(.123, .235, .234), .0001);

            Assert.AreEqual(0x01, FinMath.Wrap(-0x02, -0x03, 0x02));
        }
Ejemplo n.º 12
0
        internal static void FillNodeInfo(InteractivePointActive ip,
                                          double f, double dT, IOptionStrikePair sInfo,
                                          StrikeType optionType, OptionPxMode optPxMode,
                                          double optSigma, bool returnPct, bool isVisiblePoints, double riskfreeRatePct)
        {
            if (optionType == StrikeType.Any)
            {
                throw new ArgumentException("Option type 'Any' is not supported.", "optionType");
            }

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

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

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

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

            ip.Tag          = nodeInfo;
            ip.Color        = AlphaColors.Yellow;
            ip.IsActive     = isVisiblePoints;
            ip.Geometry     = Geometries.Ellipse;
            ip.DragableMode = DragableMode.None;
            //ip.Value = new System.Windows.Point(sInfo.Strike, nodeInfo.Sigma);
            ip.Tooltip = String.Format(CultureInfo.InvariantCulture,
                                       "F:{0}; K:{1}; IV:{2:#0.00}%\r\n{3} px {4}",
                                       f, sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx);
        }
Ejemplo n.º 13
0
        protected static void FillNodeInfo(InteractivePointLight ip,
                                           double f, double dT, IOptionStrikePair sInfo,
                                           StrikeType optionType, OptionPxMode optPxMode,
                                           double optSigma, bool returnPct, double pctRate)
        {
            if (optionType == StrikeType.Any)
            {
                throw new ArgumentException("Option type 'Any' is not supported.", "optionType");
            }

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

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

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

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

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

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

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

            SmileNodeInfo nodeInfo = new SmileNodeInfo();

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

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

            ip.Tag          = nodeInfo;
            ip.Color        = AlphaColors.Yellow;
            ip.IsActive     = isVisiblePoints;
            ip.Geometry     = Geometries.Ellipse;
            ip.DragableMode = DragableMode.None;
            //ip.Value = new System.Windows.Point(sInfo.Strike, nodeInfo.Sigma);
            ip.Tooltip = String.Format("F:{0}; K:{1}; IV:{2:#0.00}%\r\n{3} {4} @ {5}",
                                       f, sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx, DefaultOptQty);
        }
Ejemplo n.º 15
0
        /// <summary>
        /// Тета опциона по формуле Блека-Шолза
        /// </summary>
        internal static void GetOptTheta(IEnumerable <IPosition> positions,
                                         double f, double k, double dT, double sigma, double r, bool isCall,
                                         out double theta)
        {
            theta = 0;
            foreach (IPosition pos in positions)
            {
                //if (pos.EntrySignalName.StartsWith("CHT-RI-03.", StringComparison.InvariantCultureIgnoreCase))
                //{
                //    string str = "";
                //}

                // Пока что State лучше не трогать
                //if (pos.PositionState == PositionState.HaveError)
                {
                    int    sign    = pos.IsLong ? 1 : -1;
                    double qty     = Math.Abs(pos.Shares);
                    double optVega = FinMath.GetOptionTheta(f, k, dT, sigma, r, isCall);
                    theta += sign * optVega * qty;
                }
            }
        }
Ejemplo n.º 16
0
        internal static void GetOptGreek(IEnumerable <IPosition> positions,
                                         double f, double k, double dT, double sigma, double ratePct, bool isCall,
                                         Greeks greek, out double optGreek)
        {
            optGreek = 0;
            foreach (IPosition pos in positions)
            {
                // Пока что State лучше не трогать
                //if (pos.PositionState == PositionState.HaveError)
                {
                    int    sign = pos.IsLong ? 1 : -1;
                    double qty  = Math.Abs(pos.Shares);
                    double tmp;
                    switch (greek)
                    {
                    case Greeks.Delta:
                        tmp = FinMath.GetOptionDelta(f, k, dT, sigma, ratePct, isCall);
                        break;

                    case Greeks.Theta:
                        tmp = FinMath.GetOptionTheta(f, k, dT, sigma, ratePct, isCall);
                        break;

                    case Greeks.Vega:
                        tmp = FinMath.GetOptionVega(f, k, dT, sigma, ratePct, isCall);
                        break;

                    case Greeks.Gamma:
                        tmp = FinMath.GetOptionGamma(f, k, dT, sigma, ratePct, isCall);
                        break;

                    default:
                        throw new NotImplementedException("Greek '" + greek + "' is not yet supported.");
                    }
                    optGreek += sign * tmp * qty;
                }
            }
        }
Ejemplo n.º 17
0
 public void TestAddTowards()
 {
     Assert.AreEqual(.1, FinMath.AddTowards(0, 1, .1));
     Assert.AreEqual(.9, FinMath.AddTowards(1, 0, .1));
     Assert.AreEqual(1, FinMath.AddTowards(0, 1, 1.1));
 }
Ejemplo n.º 18
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);
        }
Ejemplo n.º 19
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);
        }
Ejemplo n.º 20
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);
        }
Ejemplo n.º 21
0
        public InteractiveSeries Execute(double price, double time, IOptionSeries optSer, double riskFreeRatePct, int barNum)
        {
            int barsCount = ContextBarsCount;

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

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

            double futPx = price;
            double dT    = time;

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

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

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

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

            var allRealtimeSecs = Context.Runtime.Securities;

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

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

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

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

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

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

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

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

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

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

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

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

            return(res);
        }
Ejemplo n.º 22
0
        /// <summary>
        /// Обработчик под тип входных данных OPTION_SERIES
        /// </summary>
        public IList <double> Execute(IOptionSeries optSer)
        {
            if (optSer == null)
            {
                string msg = "[IV ATM] (optSer == null)";
                m_context.Log(msg, MessageType.Warning, false);

                return(Constants.EmptyListDouble);
            }

            Dictionary <DateTime, double> ivSigmas;

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

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

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

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

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

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

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

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

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

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

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

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

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

            return(new ReadOnlyCollection <double>(res));
        }
Ejemplo n.º 23
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);
        }
Ejemplo n.º 24
0
        /// <summary>
        /// Метод под флаг TemplateTypes.INTERACTIVESPLINE
        /// </summary>
        public InteractiveSeries Execute(InteractiveSeries quotes, InteractiveSeries smile, int barNum)
        {
            if ((quotes == null) || (smile == null))
            {
                return(Constants.EmptySeries);
            }

            int barsCount = m_context.BarsCount;

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

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

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

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

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

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

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

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

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

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

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

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

                        obj.ControlPoint1 = tmp;
                    }
                }

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

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

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

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

                        obj.ControlPoint1 = tmp;
                    }
                }
            }

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

            m_clickableSeries = res;

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

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

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

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

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

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

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

            cashPnlUsd = new CashPnlUsd(cashUsd, pnlUsd);
            cashPnlBtc = new CashPnlBtc(cashBtc, pnlBtc);
        }
Ejemplo n.º 30
0
        private void ProcessScheduledHorizontalMovement_()
        {
            var heldXAxis     = this.HeldXAxis;
            var isRunning     = this.IsRunning;
            var isTryingToRun = FloatMath.Abs(heldXAxis) > .5f;

            var heldXAxisSign = FinMath.Sign(heldXAxis);

            float?targetXVelocity = null;
            float xAcceleration   = 0;

            if (this.StateMachine.CanMoveUprightOnGround)
            {
                var maxGroundXVelocity =
                    isRunning
                ? PlayerConstants.UPRIGHT_MAX_FAST_XSPD
                : PlayerConstants.UPRIGHT_MAX_SLOW_XSPD;
                var groundAcceleration =
                    isTryingToRun
                ? PlayerConstants.GROUND_UPRIGHT_FAST_XACC
                : PlayerConstants.GROUND_UPRIGHT_SLOW_XACC;
                var reactionFraction =
                    heldXAxisSign == -FinMath.Sign(this.Rigidbody.XVelocity)
                ? PlayerConstants.GROUND_REACTION_FRAC
                : 1;

                targetXVelocity = maxGroundXVelocity * heldXAxis;
                xAcceleration   = groundAcceleration * reactionFraction * heldXAxisSign;

                // If holding a direction on the ground, we're either turning, running, or walking.
                if (heldXAxisSign != 0)
                {
                    this.StateMachine.State = reactionFraction != 1
                                        ? PlayerState.TURNING
                                        : isTryingToRun
                                            ? PlayerState.RUNNING
                                            : PlayerState.WALKING;
                }
                // If not holding a direction on the ground but velocity is not zero, we're stopping.
                else if (FinMath.Abs(this.Rigidbody.XVelocity) > .001)
                {
                    this.StateMachine.State = PlayerState.STOPPING;
                }
            }
            else if (this.StateMachine.CanMoveDuckedOnGround)
            {
                var maxGroundXVelocity =
                    isRunning
                ? PlayerConstants.DUCKED_MAX_FAST_XSPD
                : PlayerConstants.DUCKED_MAX_SLOW_XSPD;
                var groundAcceleration =
                    isTryingToRun
                ? PlayerConstants.GROUND_DUCKED_FAST_XACC
                : PlayerConstants.GROUND_DUCKED_SLOW_XACC;

                targetXVelocity = maxGroundXVelocity * heldXAxis;
                xAcceleration   = groundAcceleration * heldXAxisSign;

                // If holding a direction on the ground, we're either turning, running, or walking.
                if (heldXAxisSign != 0)
                {
                    this.StateMachine.State = PlayerState.DUCKWALKING;
                }
            }
            else if (this.StateMachine.CanMoveInAir)
            {
                var maxAirXVelocity =
                    isRunning
                ? PlayerConstants.UPRIGHT_MAX_FAST_XSPD
                : PlayerConstants.UPRIGHT_MAX_SLOW_XSPD;
                var airAcceleration =
                    isTryingToRun
                ? PlayerConstants.AIR_FAST_XACC
                : PlayerConstants.AIR_SLOW_XACC;

                targetXVelocity = maxAirXVelocity * heldXAxis;
                xAcceleration   = airAcceleration * heldXAxisSign;
            }

            this.Rigidbody.TargetXVelocity = targetXVelocity;
            this.Rigidbody.XAcceleration   = xAcceleration;
        }