private void InteractiveSplineOnClickEvent(object sender, InteractiveActionEventArgs eventArgs) { PositionsManager posMan = PositionsManager.GetManager(m_context); if (posMan.BlockTrading) { //string msg = String.Format("[{0}] Trading is blocked. Please, change 'Block Trading' parameter.", m_optionPxMode); string msg = RM.GetStringFormat("OptHandlerMsg.PositionsManager.TradingBlocked", m_optionPxMode); m_context.Log(msg, MessageType.Info, true); return; } SmileNodeInfo nodeInfo = eventArgs.Point.Tag as SmileNodeInfo; if (nodeInfo == null) { //string msg = String.Format("[{0}] There is no nodeInfo. Quote type: {1}; Strike: {2}", m_optionPxMode); string msg = RM.GetStringFormat(CultureInfo.InvariantCulture, "OptHandlerMsg.ThereIsNoNodeInfo", m_context.Runtime.TradeName, m_optionPxMode, eventArgs.Point.ValueX); m_context.Log(msg, MessageType.Error, true); return; } nodeInfo.Qty = m_qty; nodeInfo.ClickTime = DateTime.Now; // Передаю событие в PositionsManager дополнив его инфой о количестве лотов posMan.InteractiveSplineOnClickEvent(m_context, sender, eventArgs); }
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); }
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); } }
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); }
private void InteractiveSplineOnQuoteIvEvent(object sender, InteractiveActionEventArgs eventArgs) { OptionPxMode pxMode = (m_qty > 0) ? OptionPxMode.Ask : OptionPxMode.Bid; PositionsManager posMan = PositionsManager.GetManager(m_context); if (posMan.BlockTrading) { //string msg = String.Format("[{0}] Trading is blocked. Please, change 'Block Trading' parameter.", m_optionPxMode); string msg = RM.GetStringFormat("OptHandlerMsg.PositionsManager.TradingBlocked", pxMode); m_context.Log(msg, MessageType.Info, true); return; } InteractivePointActive tmp = eventArgs.Point; if ((tmp == null) || (tmp.IsActive == null) || (!tmp.IsActive.Value) || DoubleUtil.IsZero(m_qty)) { //string msg = String.Format("[{0}] Unable to get direction of the order. Qty:{1}", pxMode, m_qty); string msg = RM.GetStringFormat("OptHandlerMsg.PositionsManager.UndefinedOrderDirection", pxMode, m_qty); m_context.Log(msg, MessageType.Error, true); return; } SmileNodeInfo nodeInfo = tmp.Tag as SmileNodeInfo; if (nodeInfo == null) { //string msg = String.Format("[{0}] There is no nodeInfo. Quote type: {1}; Strike: {2}", pxMode); string msg = RM.GetStringFormat(CultureInfo.InvariantCulture, "OptHandlerMsg.ThereIsNoNodeInfo", m_context.Runtime.TradeName, pxMode, eventArgs.Point.ValueX); m_context.Log(msg, MessageType.Error, true); return; } { string msg = String.Format(CultureInfo.InvariantCulture, "[{0}.ClickEvent] Strike: {1}", GetType().Name, tmp.ValueX); m_context.Log(msg, MessageType.Info, false); } nodeInfo.OptPx = m_shiftIv; nodeInfo.ShiftOptPx = m_shiftPriceStep; nodeInfo.ClickTime = DateTime.Now; nodeInfo.PxMode = pxMode; // [2015-10-02] Подписка на данный инструмент, чтобы он появился в коллекции Context.Runtime.Securities var candids = (from s in Context.Runtime.Securities where s.SecurityDescription.Name.Equals(nodeInfo.Symbol, StringComparison.InvariantCultureIgnoreCase) select s).ToList(); candids = (from s in candids where s.SecurityDescription.DSName.Equals(nodeInfo.DSName, StringComparison.InvariantCultureIgnoreCase) select s).ToList(); ISecurity testSec = (from s in candids let secDesc = s.SecurityDescription where secDesc.FullName.Equals(nodeInfo.FullName, StringComparison.InvariantCultureIgnoreCase) select s).SingleOrDefault(); if ((testSec == null) && (nodeInfo.Security != null)) { ISecurity sec = nodeInfo.Security; int bc = sec.Bars.Count; string msg = String.Format("[{0}] There is security DsName: {1}; Symbol: {2}; Security: {3} with {4} bars available.", pxMode, nodeInfo.DSName, nodeInfo.Symbol, nodeInfo.FullName, bc); Context.Log(msg, MessageType.Info, false); // Пересчитываю целочисленный параметр Qty в фактические лоты конкретного инструмента double actQty = Math.Abs(m_qty * sec.LotTick); // Модуль, потому что тут направление передается через PxMode nodeInfo.Qty = actQty; } else if ((nodeInfo.Pair != null) && (nodeInfo.Pair.Put != null)) { // Аварийная ветка // Пересчитываю целочисленный параметр Qty в фактические лоты конкретного инструмента double actQty = Math.Abs(m_qty * nodeInfo.Pair.Put.LotTick); // Модуль, потому что тут направление передается через PxMode nodeInfo.Qty = actQty; } else { // Аварийная ветка // Не могу пересчитать целочисленный параметр Qty в фактические лоты конкретного инструмента! //double actQty = Math.Abs(m_qty * nodeInfo.Pair.Put.LotTick); nodeInfo.Qty = Math.Abs(m_qty); // Модуль, потому что тут направление передается через PxMode string msg = String.Format(CultureInfo.InvariantCulture, "[{0}.ClickEvent] LotTick will be set to 1.", GetType().Name); m_context.Log(msg, MessageType.Warning, false); } // Не страшно, если сюда пойдет null - posMan потом сам найдет нужный опцион nodeInfo.Security = testSec; // Передаю событие в PositionsManager //posMan.InteractiveSplineOnClickEvent(m_context, sender, eventArgs); posMan.InteractiveSplineOnQuoteIvEvent(m_context, sender, eventArgs); }
/// <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); }
private void InteractiveSplineOnClickEvent(object sender, InteractiveActionEventArgs eventArgs) { PositionsManager posMan = PositionsManager.GetManager(m_context); if (posMan.BlockTrading) { //string msg = String.Format("[{0}] Trading is blocked. Please, change 'Block Trading' parameter.", MsgId); string msg = RM.GetStringFormat("OptHandlerMsg.PositionsManager.TradingBlocked", MsgId); m_context.Log(msg, MessageType.Warning, true); return; } if (m_qty == 0) { //string msg = String.Format("[{0}] Trading is blocked for zero quantity. Qty: {1}", MsgId, qty); string msg = RM.GetStringFormat("OptHandlerMsg.PositionsManager.TradingBlockedForZeroQty", MsgId, m_qty); m_context.Log(msg, MessageType.Warning, true); return; } else if (m_qty < 0) { // GLSP-252 - Запрет на совершение сделок при отрицательном Qty //string msg = String.Format("[{0}] Trading is blocked for negative quantity. Qty: {1}", MsgId, qty); string msg = RM.GetStringFormat("OptHandlerMsg.PositionsManager.TradingBlockedForNegativeQty", MsgId, m_qty); m_context.Log(msg, MessageType.Warning, true); return; } SmileNodeInfo nodeInfo = eventArgs.Point.Tag as SmileNodeInfo; if (nodeInfo == null) { //string msg = String.Format("[{0}] There is no nodeInfo. Base asset price: {1}", MsgId); string msg = RM.GetStringFormat(CultureInfo.InvariantCulture, "OptHandlerMsg.CurrentFutPx.ThereIsNoNodeInfo", MsgId, eventArgs.Point.ValueX); m_context.Log(msg, MessageType.Error, true); return; } nodeInfo.ClickTime = DateTime.Now; // [2015-10-02] Подписка на данный инструмент, чтобы он появился в коллекции Context.Runtime.Securities ISecurity testSec = (from s in m_context.Runtime.Securities let secDesc = s.SecurityDescription where secDesc.FullName.Equals(nodeInfo.FullName, StringComparison.InvariantCultureIgnoreCase) && secDesc.DSName.Equals(nodeInfo.DSName, StringComparison.InvariantCultureIgnoreCase) && secDesc.Name.Equals(nodeInfo.Symbol, StringComparison.InvariantCultureIgnoreCase) select s).SingleOrDefault(); if (testSec == null) { ISecurity sec = nodeInfo.Security; int bc = sec.Bars.Count; string msg = String.Format("[{0}] There is security DsName: {1}; Symbol: {2}; Security: {3} with {4} bars available.", nodeInfo.PxMode, nodeInfo.DSName, nodeInfo.Symbol, nodeInfo.FullName, bc); Context.Log(msg, MessageType.Info, false); // Пересчитываю целочисленный параметр Qty в фактические лоты конкретного инструмента double actQty = m_qty * sec.LotTick; // Здесь нет модуля, потому что направление сделки несет в себе знак Qty nodeInfo.Qty = actQty; } else if ((nodeInfo.Security != null) && (nodeInfo.Security != null)) { // Аварийная ветка // Пересчитываю целочисленный параметр Qty в фактические лоты конкретного инструмента double actQty = m_qty * nodeInfo.Security.LotTick; // Здесь нет модуля, потому что направление сделки несет в себе знак Qty nodeInfo.Qty = actQty; } else { // Аварийная ветка // Не могу пересчитать целочисленный параметр Qty в фактические лоты конкретного инструмента! nodeInfo.Qty = m_qty; // Здесь нет модуля, потому что направление сделки несет в себе знак Qty string msg = String.Format(CultureInfo.InvariantCulture, "[{0}.ClickEvent] LotTick is set to 1.", GetType().Name); m_context.Log(msg, MessageType.Warning, false); } // Передаю событие в PositionsManager дополнив его инфой о количестве лотов posMan.InteractiveSplineOnClickEvent(m_context, sender, eventArgs); }
public InteractiveSeries Execute(double price, ISecurity sec, int barNum) { InteractiveSeries res = new InteractiveSeries(); int barsCount = m_context.BarsCount; if (!m_context.IsLastBarUsed) { barsCount--; } if (barNum < barsCount - 1) { return(res); } res.ClickEvent -= InteractiveSplineOnClickEvent; res.ClickEvent += InteractiveSplineOnClickEvent; m_clickableSeries = res; double f = price; List <InteractiveObject> controlPoints = new List <InteractiveObject>(); { InteractivePointActive ip = new InteractivePointActive(f, m_sigmaLow); ip.IsActive = true; ip.DragableMode = DragableMode.None; ip.Geometry = Geometries.Ellipse; ip.Color = AlphaColors.BlueViolet; ip.Tooltip = String.Format("F:{0}; Y:{1:0.00}%", f, m_sigmaLow * Constants.PctMult); SmileNodeInfo node = new SmileNodeInfo(); node.OptPx = f; node.Security = sec; node.PxMode = OptionPxMode.Bid; node.Symbol = node.Security.Symbol; node.DSName = node.Security.SecurityDescription.DSName; node.Expired = node.Security.SecurityDescription.Expired; node.FullName = node.Security.SecurityDescription.FullName; ip.Tag = node; controlPoints.Add(new InteractiveObject(ip)); } { InteractivePointActive ip = new InteractivePointActive(f, m_sigmaHigh); ip.IsActive = true; ip.DragableMode = DragableMode.None; ip.Geometry = Geometries.Triangle; ip.Color = AlphaColors.Green; ip.Tooltip = String.Format("F:{0}; Y:{1:0.00}%", f, m_sigmaHigh * Constants.PctMult); SmileNodeInfo node = new SmileNodeInfo(); node.OptPx = f; node.Security = sec; node.PxMode = OptionPxMode.Ask; node.Symbol = node.Security.Symbol; node.DSName = node.Security.SecurityDescription.DSName; node.Expired = node.Security.SecurityDescription.Expired; node.FullName = node.Security.SecurityDescription.FullName; ip.Tag = node; controlPoints.Add(new InteractiveObject(ip)); } res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); return(res); }
private void InteractiveSplineOnClickEvent(object sender, InteractiveActionEventArgs eventArgs) { // [2016-03-01] PROD-2452: Запрещаю торговлю по узлу, если нет "подсветки" { InteractiveObject obj = eventArgs.InteractiveObject; if ((obj == null) || (obj.Anchor == null) || (obj.ControlPoint1 == null) || (!(obj.Anchor is InteractivePointActive)) || (!(obj.ControlPoint1 is InteractivePointActive))) { string msg = String.Format("[{0}.ClickEvent] Denied #1", GetType().Name); m_context.Log(msg, MessageType.Warning, false); return; } var cp1 = (InteractivePointActive)obj.ControlPoint1; // PROD-4967 - Необязательно проверять активность якоря. // Потому что эта настройка делается на более позднем этапе в момент создания графического объекта // методом smilePanePane.AddList("SmilePane_pane_TradeAsks_chart", <...>) //var anchor = (InteractivePointActive)obj.Anchor; //if ((anchor.IsActive == null) || (!anchor.IsActive.Value) || if ((cp1.IsActive == null) || (!cp1.IsActive.Value)) { string msg = String.Format("[{0}.ClickEvent] Denied #3 (ControlPoint1 is not active)", GetType().Name); m_context.Log(msg, MessageType.Warning, false); return; } } PositionsManager posMan = PositionsManager.GetManager(m_context); if (posMan.BlockTrading) { //string msg = String.Format("[{0}] Trading is blocked. Please, change 'Block Trading' parameter.", m_optionPxMode); string msg = String.Format(RM.GetString("OptHandlerMsg.PositionsManager.TradingBlocked"), m_optionPxMode); m_context.Log(msg, MessageType.Info, true); return; } // Здесь нет проверки знака m_qty, потому что в данном кубике мы действительно можем и продавать и покупать SmileNodeInfo nodeInfo = eventArgs.Point.Tag as SmileNodeInfo; if (nodeInfo == null) { //string msg = String.Format("[{0}] There is no nodeInfo. Quote type: {1}; Strike: {2}", string msg = RM.GetStringFormat(CultureInfo.InvariantCulture, "OptHandlerMsg.ThereIsNoNodeInfo", m_context.Runtime.TradeName, m_optionPxMode, eventArgs.Point.ValueX); m_context.Log(msg, MessageType.Error, true); return; } { string msg = String.Format(CultureInfo.InvariantCulture, "[{0}.ClickEvent] Strike: {1}; Security: {2}", GetType().Name, eventArgs.Point.ValueX, nodeInfo.FullName); m_context.Log(msg, MessageType.Info, false); } nodeInfo.ClickTime = DateTime.Now; // [2015-10-02] Подписка на данный инструмент, чтобы он появился в коллекции Context.Runtime.Securities ISecurity testSec = (from s in Context.Runtime.Securities let secDesc = s.SecurityDescription where secDesc.FullName.Equals(nodeInfo.FullName, StringComparison.InvariantCultureIgnoreCase) && secDesc.DSName.Equals(nodeInfo.DSName, StringComparison.InvariantCultureIgnoreCase) && secDesc.Name.Equals(nodeInfo.Symbol, StringComparison.InvariantCultureIgnoreCase) select s).SingleOrDefault(); if (testSec == null) { ISecurity sec = nodeInfo.Security; int bc = sec.Bars.Count; string msg = String.Format("[{0}] There is security DsName: {1}; Symbol: {2}; Security: {3} with {4} bars available.", m_optionPxMode, nodeInfo.DSName, nodeInfo.Symbol, nodeInfo.FullName, bc); Context.Log(msg, MessageType.Info, false); // Пересчитываю целочисленный параметр Qty в фактические лоты конкретного инструмента double actQty = m_qty * sec.LotTick; // Здесь нет модуля, потому что направление сделки несет в себе знак Qty nodeInfo.Qty = actQty; } else if ((nodeInfo.Pair != null) && (nodeInfo.Pair.Put != null)) { // Аварийная ветка // Пересчитываю целочисленный параметр Qty в фактические лоты конкретного инструмента double actQty = m_qty * nodeInfo.Pair.Put.LotTick; // Здесь нет модуля, потому что направление сделки несет в себе знак Qty nodeInfo.Qty = actQty; } else { // Аварийная ветка // Не могу пересчитать целочисленный параметр Qty в фактические лоты конкретного инструмента! //double actQty = Math.Abs(m_qty * nodeInfo.Pair.Put.LotTick); nodeInfo.Qty = m_qty; // Здесь нет модуля, потому что направление сделки несет в себе знак Qty string msg = String.Format(CultureInfo.InvariantCulture, "[{0}.ClickEvent] LotTick is set to 1.", GetType().Name); m_context.Log(msg, MessageType.Warning, false); } // Передаю событие в PositionsManager дополнив его инфой о количестве лотов posMan.InteractiveSplineOnClickEvent(m_context, sender, eventArgs); }
/// <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); }
public InteractiveSeries Execute(double price, InteractiveSeries line, ISecurity sec, int barNum) { int barsCount = m_context.BarsCount; if (!m_context.IsLastBarUsed) { barsCount--; } if (barNum < barsCount - 1) { return(Constants.EmptySeries); } InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку res.ClickEvent += InteractiveSplineOnClickEvent; m_clickableSeries = res; double f = price; double? sigmaAtm = null; SmileInfo sInfo = line.GetTag <SmileInfo>(); if ((sInfo != null) && (sInfo.ContinuousFunction != null)) { double tmp; if (sInfo.ContinuousFunction.TryGetValue(f, out tmp)) { if (!Double.IsNaN(tmp)) { sigmaAtm = tmp; } } } if (sigmaAtm == null) { return(res); } double h = Math.Max(m_minHeight, sigmaAtm.Value * m_offset); // Теперь определяем максимальный размах по вертикали if (line.ControlPoints.Count > 1) { var cps = line.ControlPoints; double min = Double.MaxValue, max = Double.MinValue; for (int j = 0; j < cps.Count; j++) { var anchor = cps[j].Anchor; double y = anchor.ValueY; if (y <= min) { min = y; } if (max <= y) { max = y; } } if ((min < max) && (!Double.IsInfinity(max - min))) { h = Math.Max(h, (max - min) * m_offset); } } List <InteractiveObject> controlPoints = new List <InteractiveObject>(); { InteractivePointActive ip = new InteractivePointActive(); ip.IsActive = true; ip.DragableMode = DragableMode.None; ip.Geometry = Geometries.Ellipse; // Geometries.TriangleDown; ip.Color = AlphaColors.Magenta; double y = (sigmaAtm.Value - h); ip.Value = new Point(f, y); string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture); // По умолчанию формат 'P2' -- то есть отображение с точностью 2знака, // со знаком процента и с автоматическим умножением на 100. ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "F:{0}; Y:{1}", f, yStr); SmileNodeInfo node = new SmileNodeInfo(); node.OptPx = f; node.Security = sec; node.PxMode = OptionPxMode.Bid; node.Symbol = node.Security.Symbol; node.DSName = node.Security.SecurityDescription.DSName; node.Expired = node.Security.SecurityDescription.Expired; node.FullName = node.Security.SecurityDescription.FullName; ip.Tag = node; controlPoints.Add(new InteractiveObject(ip)); } { InteractivePointActive ip = new InteractivePointActive(); ip.IsActive = true; ip.DragableMode = DragableMode.None; ip.Geometry = Geometries.Triangle; ip.Color = AlphaColors.GreenYellow; double y = (sigmaAtm.Value + h); ip.Value = new Point(f, y); string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture); // По умолчанию формат 'P2' -- то есть отображение с точностью 2знака, // со знаком процента и с автоматическим умножением на 100. ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "F:{0}; Y:{1}", f, yStr); SmileNodeInfo node = new SmileNodeInfo(); node.OptPx = f; node.Security = sec; node.PxMode = OptionPxMode.Ask; node.Symbol = node.Security.Symbol; node.DSName = node.Security.SecurityDescription.DSName; node.Expired = node.Security.SecurityDescription.Expired; node.FullName = node.Security.SecurityDescription.FullName; ip.Tag = node; controlPoints.Add(new InteractiveObject(ip)); } res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); return(res); }
private void RouteOnClickEvents(List <InteractiveActionEventArgs> eventArgs) { if (eventArgs == null) { return; } int argLen = eventArgs.Count; PositionsManager posMan = PositionsManager.GetManager(m_context); if (posMan.BlockTrading && (argLen > 0)) { eventArgs.Clear(); string msg = String.Format("[{0}] ERROR! Trading is blocked. Should NOT be here. All events were removed.", m_optionPxMode); m_context.Log(msg, MessageType.Warning, true); return; } bool recalc = false; for (int j = argLen - 1; j >= 0; j--) { InteractiveActionEventArgs eventArg = eventArgs[j]; eventArgs.RemoveAt(j); try { SmileNodeInfo nodeInfo = eventArg.Point.Tag as SmileNodeInfo; if (nodeInfo == null) { //string msg = String.Format("[{0}] There is no nodeInfo. Quote type: {1}; Strike: {2}", m_optionPxMode); string msg = RM.GetStringFormat(CultureInfo.InvariantCulture, "OptHandlerMsg.ThereIsNoNodeInfo", m_context.Runtime.TradeName, m_optionPxMode, eventArg.Point.ValueX); m_context.Log(msg, MessageType.Error, true); continue; } if ((!posMan.UseVirtualPositions) && nodeInfo.Expired) { string msg = String.Format("[{0}] Security {1} is already expired. Expiry: {2}", m_optionPxMode, nodeInfo.Symbol, nodeInfo.Security.SecurityDescription.ExpirationDate); m_context.Log(msg, MessageType.Error, true); continue; } ISecurity sec = (from s in m_context.Runtime.Securities where (s.Symbol.Equals(nodeInfo.Symbol, StringComparison.InvariantCultureIgnoreCase)) && (String.IsNullOrWhiteSpace(nodeInfo.DSName) || (s.SecurityDescription.DSName == nodeInfo.DSName)) && (String.IsNullOrWhiteSpace(nodeInfo.FullName) || (s.SecurityDescription.FullName == nodeInfo.FullName)) select s).SingleOrDefault(); if (sec == null) { string msg = String.Format("[{0}] There is no security. Symbol: {1}", m_optionPxMode, nodeInfo.Symbol); m_context.Log(msg, MessageType.Error, true); continue; } double actualPx = nodeInfo.OptPx; if (m_optionType != StrikeType.Any) { // Заменяю инструмент в соответствии с настройками кубика switch (m_optionType) { case StrikeType.Put: if (sec.SecurityDescription.ActiveType != DataSource.ActiveType.OptionPut) { var oldCall = sec; sec = nodeInfo.Pair.Put.Security; // Теперь еще нужно заменить ЦЕНУ. Проще всего это сделать через паритет. // C - P = F - K ==> P = C + K - F double putPx = nodeInfo.OptPx + nodeInfo.Strike - nodeInfo.F; // Но не меньше 1 ш.ц.! actualPx = Math.Max(putPx, sec.Tick); string txt = String.Format(CultureInfo.InvariantCulture, "[{0}] Symbol '{1}' has been replaced to '{2}' and changed price from {3} to {4}", m_optionPxMode, nodeInfo.Symbol, sec.Symbol, nodeInfo.OptPx, actualPx); m_context.Log(txt, MessageType.Warning, true); } break; case StrikeType.Call: if (sec.SecurityDescription.ActiveType != DataSource.ActiveType.OptionCall) { var oldPut = sec; sec = nodeInfo.Pair.Call.Security; // Теперь еще нужно заменить ЦЕНУ. Проще всего это сделать через паритет. // C - P = F - K ==> C = P + F - K double callPx = nodeInfo.OptPx + nodeInfo.F - nodeInfo.Strike; // Но не меньше 1 ш.ц.! actualPx = Math.Max(callPx, sec.Tick); string txt = String.Format(CultureInfo.InvariantCulture, "[{0}] Symbol '{1}' has been replaced to '{2}' and changed price from {3} to {4}", m_optionPxMode, nodeInfo.Symbol, sec.Symbol, nodeInfo.OptPx, actualPx); m_context.Log(txt, MessageType.Warning, true); } break; } } int len = sec.Bars.Count; // Валидирую наличие правильной котировки if (sec.FinInfo != null) { Debug.WriteLine("AskPx: " + sec.FinInfo.Ask); Debug.WriteLine("BidPx: " + sec.FinInfo.Bid); } recalc = true; if ((m_optionPxMode == OptionPxMode.Ask) && (m_qty > 0) || (m_optionPxMode == OptionPxMode.Bid) && (m_qty < 0)) { string signalName = "\r\nLeft-Click BUY \r\n" + eventArg.Point.Tooltip + "\r\n"; posMan.BuyAtPrice(m_context, sec, Math.Abs(m_qty), actualPx, signalName, null); } else if ((m_optionPxMode == OptionPxMode.Bid) && (m_qty > 0) || (m_optionPxMode == OptionPxMode.Ask) && (m_qty < 0)) { string signalName = "\r\nLeft-Click SELL \r\n" + eventArg.Point.Tooltip + "\r\n"; posMan.SellAtPrice(m_context, sec, Math.Abs(m_qty), actualPx, signalName, null); } else if (m_optionPxMode == OptionPxMode.Mid) { string msg = String.Format("[{0}] OptionPxMode.Mid is not implemented.", m_optionPxMode); m_context.Log(msg, MessageType.Warning, true); } else if (m_qty == 0) { string msg = String.Format("[{0}] Qty: {1}. Trading is blocked.", m_optionPxMode, m_qty); m_context.Log(msg, MessageType.Warning, true); } } catch (Exception ex) { string msg = String.Format("[{0}] {1} in RouteOnClickEvents. Message: {2}\r\n{3}", m_optionPxMode, ex.GetType().FullName, ex.Message, ex); m_context.Log(msg, MessageType.Warning, true); } } if (recalc) { m_context.Recalc(RecalcReasons.ChartTrading, null); } }
public InteractiveSeries Execute(InteractiveSeries smile, int barNum) { if (m_transformation == SmileTransformation.None) { if (DoubleUtil.IsZero(m_shiftIv)) { return(smile); } } int barsCount = m_context.BarsCount; if (!m_context.IsLastBarUsed) { barsCount--; } if (barNum < barsCount - 1) { return(smile); } // Пустая улыбка останется пустой. Что с ней делать непонятно. if (smile.ControlPoints.Count <= 1) { return(smile); } SmileInfo sInfo = smile.GetTag <SmileInfo>(); if (sInfo == null) { throw new ScriptException("ArgumentException: smile.Tag must be filled with SmileInfo object."); } var cps = smile.ControlPoints; int len = cps.Count; IFunction symmetrizedFunc = null; IFunction symmetrizedFuncD1 = null; InteractiveSeries res = new InteractiveSeries(); List <double> xs = new List <double>(); List <double> ys = new List <double>(); if (m_transformation == SmileTransformation.Simmetrise) { #region Simmetrise double f = sInfo.F; double minX = cps[0].Anchor.ValueX; double maxX = cps[len - 1].Anchor.ValueX; double width = Math.Min(maxX - f, f - minX); if (width < 0) { throw new ScriptException("ArgumentException: current price is outside of the smile."); } // TODO: сократить вычисления вдвое, учитывая явное требование симметричности результирующей улыбки double step = 2.0 * width / (len - 1); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); for (int j = 0; j < len; j++) { double kLeft = (f - width) + j * step; double ivLeft = sInfo.ContinuousFunction.Value(kLeft); double kRight = (f + width) - j * step; double ivRight = sInfo.ContinuousFunction.Value(kRight); double iv = 0.5 * (ivLeft + ivRight) + m_shiftIv; InteractiveObject oldObj = cps[j]; if (oldObj.Anchor is InteractivePointActive) { InteractivePointActive ip = (InteractivePointActive)oldObj.Anchor.Clone(); ip.Color = (m_optionPxMode == OptionPxMode.Ask) ? AlphaColors.DarkOrange : AlphaColors.DarkCyan; ip.DragableMode = DragableMode.None; ip.Geometry = Geometries.Rect; // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect; ip.IsActive = (m_optionPxMode != OptionPxMode.Mid); ip.Value = new Point(kLeft, iv); ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", kLeft, iv * Constants.PctMult); InteractiveObject newObj = new InteractiveObject(ip); controlPoints.Add(newObj); } else if (oldObj.Anchor is InteractivePointLight) { InteractivePointLight ip = (InteractivePointLight)oldObj.Anchor.Clone(); ip.Value = new Point(kLeft, iv); InteractiveObject newObj = new InteractiveObject(ip); controlPoints.Add(newObj); } else { string msg = String.Format("[{0}] Point of type '{1}' is not supported.", GetType().Name, oldObj.Anchor.GetType().Name); throw new ScriptException(msg); } xs.Add(kLeft); ys.Add(iv); } res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); #endregion Simmetrise } else if (m_transformation == SmileTransformation.LogSimmetrise) { #region Log Simmetrise double f = sInfo.F; LogSimmetrizeFunc lsf = new LogSimmetrizeFunc(sInfo.ContinuousFunction, f, m_simmWeight); symmetrizedFunc = lsf; symmetrizedFuncD1 = new LogSimmetrizeFunc(sInfo.ContinuousFunctionD1, f, m_simmWeight); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); foreach (var oldObj in smile.ControlPoints) { double k = oldObj.Anchor.ValueX; double iv; if (lsf.TryGetValue(k, out iv)) { iv += m_shiftIv; if (oldObj.Anchor is InteractivePointActive) { InteractivePointActive ip = (InteractivePointActive)oldObj.Anchor.Clone(); ip.Color = (m_optionPxMode == OptionPxMode.Ask) ? AlphaColors.DarkOrange : AlphaColors.DarkCyan; ip.DragableMode = DragableMode.None; ip.Geometry = Geometries.Rect; // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect; ip.IsActive = (m_optionPxMode != OptionPxMode.Mid); ip.Value = new Point(k, iv); ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", k, iv * Constants.PctMult); InteractiveObject newObj = new InteractiveObject(ip); controlPoints.Add(newObj); } else if (oldObj.Anchor is InteractivePointLight) { InteractivePointLight ip = (InteractivePointLight)oldObj.Anchor.Clone(); ip.Value = new Point(k, iv); InteractiveObject newObj = new InteractiveObject(ip); controlPoints.Add(newObj); } else { string msg = String.Format("[{0}] Point of type '{1}' is not supported.", GetType().Name, oldObj.Anchor.GetType().Name); throw new ScriptException(msg); } xs.Add(k); ys.Add(iv); } } res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); #endregion Log Simmetrise } else if (m_transformation == SmileTransformation.None) { #region None (only vertical shift) double f = sInfo.F; double dT = sInfo.dT; double rate = sInfo.RiskFreeRate; List <InteractiveObject> controlPoints = new List <InteractiveObject>(); for (int j = 0; j < len; j++) { InteractiveObject oldObj = cps[j]; SmileNodeInfo nodeInfo = oldObj.Anchor.Tag as SmileNodeInfo; // Обязательно нужна полная информация об узле улыбки, чтобы потом можно было торговать if (nodeInfo == null) { continue; } double k = oldObj.Anchor.Value.X; double iv = oldObj.Anchor.Value.Y + m_shiftIv; if (oldObj.Anchor is InteractivePointActive) { InteractivePointActive ip = (InteractivePointActive)oldObj.Anchor.Clone(); ip.Color = (m_optionPxMode == OptionPxMode.Ask) ? AlphaColors.DarkOrange : AlphaColors.DarkCyan; ip.DragableMode = DragableMode.None; ip.Geometry = Geometries.Rect; // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect; ip.IsActive = (m_optionPxMode != OptionPxMode.Mid); //ip.Value = new Point(oldObj.Anchor.Value.X, iv); //ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", k, iv * PctMult); InteractiveObject newObj = new InteractiveObject(ip); if (k <= f) // Puts { FillNodeInfo(ip, f, dT, nodeInfo.Pair, StrikeType.Put, m_optionPxMode, iv, false, rate); } else // Calls { FillNodeInfo(ip, f, dT, nodeInfo.Pair, StrikeType.Call, m_optionPxMode, iv, false, rate); } controlPoints.Add(newObj); } else if (oldObj.Anchor is InteractivePointLight) { InteractivePointLight ip = (InteractivePointLight)oldObj.Anchor.Clone(); ip.Value = new Point(k, iv); InteractiveObject newObj = new InteractiveObject(ip); if (k <= f) // Puts { FillNodeInfo(ip, f, dT, nodeInfo.Pair, StrikeType.Put, m_optionPxMode, iv, false, rate); } else // Calls { FillNodeInfo(ip, f, dT, nodeInfo.Pair, StrikeType.Call, m_optionPxMode, iv, false, rate); } controlPoints.Add(newObj); } else { string msg = String.Format("[{0}] Point of type '{1}' is not supported.", GetType().Name, oldObj.Anchor.GetType().Name); throw new ScriptException(msg); } xs.Add(k); ys.Add(iv); } res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); #endregion None (only vertical shift) } else { throw new NotImplementedException("Transformation: " + m_transformation); } // ReSharper disable once UseObjectOrCollectionInitializer SmileInfo info = new SmileInfo(); info.F = sInfo.F; info.dT = sInfo.dT; info.Expiry = sInfo.Expiry; info.ScriptTime = sInfo.ScriptTime; info.RiskFreeRate = sInfo.RiskFreeRate; info.BaseTicker = sInfo.BaseTicker; try { if (symmetrizedFunc == null) { NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); } else { // По факту эта ветка обслуживает только алгоритм LogSimm info.ContinuousFunction = symmetrizedFunc; info.ContinuousFunctionD1 = symmetrizedFuncD1; } res.Tag = info; } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } return(res); }
private void RouteOnClickEvents(List <InteractiveActionEventArgs> eventArgs) { if (eventArgs == null) { return; } int argLen = eventArgs.Count; PositionsManager posMan = PositionsManager.GetManager(m_context); if (posMan.BlockTrading && (argLen > 0)) { eventArgs.Clear(); string msg = String.Format("[{0}] ERROR! Trading is blocked. Should NOT be here. All events were removed.", m_optionPxMode); m_context.Log(msg, MessageType.Warning, true); return; } bool recalc = false; for (int j = argLen - 1; j >= 0; j--) { InteractiveActionEventArgs eventArg = eventArgs[j]; eventArgs.RemoveAt(j); try { SmileNodeInfo nodeInfo = eventArg.Point.Tag as SmileNodeInfo; if (nodeInfo == null) { //string msg = String.Format("[{0}] There is no nodeInfo. Quote type: {1}; Strike: {2}", m_optionPxMode); string msg = RM.GetStringFormat(CultureInfo.InvariantCulture, "OptHandlerMsg.ThereIsNoNodeInfo", m_context.Runtime.TradeName, m_optionPxMode, eventArg.Point.ValueX); m_context.Log(msg, MessageType.Error, true); continue; } if ((!posMan.UseVirtualPositions) && nodeInfo.Expired) { string msg = String.Format("[{0}] Security {1} is already expired. Expiry: {2}", m_optionPxMode, nodeInfo.Symbol, nodeInfo.Security.SecurityDescription.ExpirationDate); m_context.Log(msg, MessageType.Error, true); continue; } ISecurity sec = (from s in m_context.Runtime.Securities where (s.Symbol.Equals(nodeInfo.Symbol, StringComparison.InvariantCultureIgnoreCase)) && (String.IsNullOrWhiteSpace(nodeInfo.DSName) || (s.SecurityDescription.DSName == nodeInfo.DSName)) && (String.IsNullOrWhiteSpace(nodeInfo.FullName) || (s.SecurityDescription.FullName == nodeInfo.FullName)) select s).SingleOrDefault(); if (sec == null) { string msg = String.Format("[{0}] There is no security. Symbol: {1}", m_optionPxMode, nodeInfo.Symbol); m_context.Log(msg, MessageType.Error, true); continue; } int len = sec.Bars.Count; // Валидирую наличие правильной котировки if (sec.FinInfo != null) { Debug.WriteLine("AskPx: " + sec.FinInfo.Ask); Debug.WriteLine("BidPx: " + sec.FinInfo.Bid); } recalc = true; if ((m_optionPxMode == OptionPxMode.Ask) && (m_qty > 0) || (m_optionPxMode == OptionPxMode.Bid) && (m_qty < 0)) { string signalName = "\r\nLeft-Click BUY \r\n" + eventArg.Point.Tooltip + "\r\n"; posMan.BuyAtPrice(m_context, sec, Math.Abs(m_qty), nodeInfo.OptPx, signalName, null); } else if ((m_optionPxMode == OptionPxMode.Bid) && (m_qty > 0) || (m_optionPxMode == OptionPxMode.Ask) && (m_qty < 0)) { string signalName = "\r\nLeft-Click SELL \r\n" + eventArg.Point.Tooltip + "\r\n"; posMan.SellAtPrice(m_context, sec, Math.Abs(m_qty), nodeInfo.OptPx, signalName, null); } else if (m_optionPxMode == OptionPxMode.Mid) { string msg = String.Format("[{0}] OptionPxMode.Mid is not implemented.", m_optionPxMode); m_context.Log(msg, MessageType.Alert, true); } else if (m_qty == 0) { string msg = String.Format("[{0}] Qty: {1}. Trading is blocked.", m_optionPxMode, m_qty); m_context.Log(msg, MessageType.Warning, true); } } catch (Exception ex) { string msg = String.Format("[{0}] {1} in RouteOnClickEvents. Message: {2}\r\n{3}", m_optionPxMode, ex.GetType().FullName, ex.Message, ex); m_context.Log(msg, MessageType.Alert, true); } } if (recalc) { m_context.Recalc(); } }
internal static void FillNodeInfo(InteractivePointActive ip, double f, double dT, IOptionStrikePair sInfo, StrikeType optionType, OptionPxMode optPxMode, double optPx, double optQty, double optSigma, DateTime optTime, bool returnPct, double riskfreeRatePct) { if (optionType == StrikeType.Any) { throw new ArgumentException("Option type 'Any' is not supported.", "optionType"); } // 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.DragableMode = DragableMode.None; ip.Value = new Point(sInfo.Strike, nodeInfo.Sigma); // [2015-08-26] Алексей дал инструкцию убрать дату из тултипа. bool tooltipWithTime = false; #if DEBUG tooltipWithTime = true; #endif // [2015-12-07] В режиме отладки возвращаю. // Потому что иначе вообще непонятно что происходит и с какими данными скрипт работает. if (optQty > 0) { if (tooltipWithTime) { ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "K:{0}; IV:{1:#0.00}%\r\n{2} px {3} @ {4}\r\nDate: {5}", sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx, optQty, optTime.ToString(DateTimeFormatWithMs, CultureInfo.InvariantCulture)); } else { ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "K:{0}; IV:{1:#0.00}%\r\n{2} px {3} @ {4}", sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx, optQty); } } else { if (tooltipWithTime) { ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "K:{0}; IV:{1:#0.00}%\r\n{2} px {3}\r\nDate: {4}", sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx, optTime.ToString(DateTimeFormatWithMs, CultureInfo.InvariantCulture)); } else { ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "K:{0}; IV:{1:#0.00}%\r\n{2} px {3}", sInfo.Strike, optSigma * Constants.PctMult, optionType, optPx); } } }