/// <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); }
/// <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); }
/// <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)) { 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.Bid, 0, 0, out putQty, out putTime); callPx = IvSmile.GetOptPrice(m_context, futPx, pair.Call, OptionPxMode.Bid, 0, 0, out callQty, out callTime); } if (m_optionType == StrikeType.Put) { #region В путах ISecurity sec = pair.Put.Security; double qty = Math.Abs(m_fixedQty); qty = BuyOptions.GetSafeQty(risk, maxRisk, qty, putRisk); if (qty > 0) { double px = SafeMaxPrice(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.SellAtPrice(m_context, sec, qty, px, "Open SELL", 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 = BuyOptions.GetSafeQty(risk, maxRisk, qty, callRisk); if (qty > 0) { double px = SafeMaxPrice(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.SellAtPrice(m_context, sec, qty, px, "Open SELL", sigName); m_context.Log(sigName, MessageType.Info, false); } #endregion В колах } else { #region В оба вида опционов сразу встаю int executedQty = 0; { ISecurity sec = pair.Put.Security; double px = SafeMaxPrice(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 = BuyOptions.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.SellAtPrice(m_context, sec, qty, px, "Open SELL", 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 = SafeMaxPrice(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 = BuyOptions.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.SellAtPrice(m_context, sec, qty, px, "Open SELL", 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; { 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 = 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 = 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); }