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); }
/// <summary> /// Обработчик под тип входных данных OPTION_SERIES /// </summary> public IList <double> Execute(IOptionSeries optSer) { if (optSer == null) { // [{0}] Empty input (option series is NULL). string msg = RM.GetStringFormat("OptHandlerMsg.OptionSeriesIsNull", GetType().Name); m_context.Log(msg, MessageType.Warning, true); return(Constants.EmptyListDouble); } IList <double> res; ISecurity sec = optSer.UnderlyingAsset; switch (m_expiryMode) { case ExpiryMode.FixedExpiry: res = PrepareData(sec, Expiry); break; default: string optSerExpiry = optSer.ExpirationDate.ToString(IvOnF.DateFormat, CultureInfo.InvariantCulture); res = PrepareData(sec, optSerExpiry); break; } return(res); }
/// <summary> /// Обработчик для двух аргументов -- опционной серии и страйка /// </summary> public ISecurity Execute(IOptionSeries optSer, IList <double> actualStrikes) { if ((optSer == null) || (optSer.UnderlyingAsset == null) || (actualStrikes == null) || (actualStrikes.Count <= 0)) { return(null); } // Выбор страйка по последнему значению! double actualStrike = actualStrikes[actualStrikes.Count - 1]; IOptionStrikePair pair = GetStrikePair(optSer, m_selectionMode, actualStrike); if (pair == null) { // Данного страйка нет в списке? Тогда выход. string expiryDate = optSer.ExpirationDate.Date.ToString(IvOnF.DateFormat, CultureInfo.InvariantCulture); // [{0}.Execute] There is no strike '{1}' in the option series '{2} ~ {3}'. Search mode: '{4}' string msg = RM.GetStringFormat("OptHandlerMsg.SingleOption.OptionNotFound", GetType().Name, actualStrike, optSer.UnderlyingAsset, expiryDate, m_selectionMode); if (Context.Runtime.IsAgentMode /* && (!m_ignoreCacheError) */) { throw new ScriptException(msg); // PROD-5501 - Кидаем исключение. } //bool isExpired = true; //// Поскольку в этом блоке настройки m_ignoreCacheError нет, то здесь всегда IsAgentMode == false! //if (Context.Runtime.IsAgentMode) //{ // int amount = optSer.UnderlyingAsset.Bars.Count; // DateTime today = (amount > 0) ? optSer.UnderlyingAsset.Bars[amount - 1].Date : new DateTime(); // isExpired = optSer.ExpirationDate.Date.AddDays(1) < today.Date; //} // А если в режиме лаборатории, тогда только жалуемся в Главный Лог и продолжаем. Context.Log(msg, MessageType.Warning, true /* !isExpired */); return(null); } ISecurity res; if (m_optionType == StrikeType.Put) { res = pair.Put.Security; } else if (m_optionType == StrikeType.Call) { res = pair.Call.Security; } else { throw new NotSupportedException("Не могу найти опцион вида: " + m_optionType); } return(res); }
/// <summary> /// Метод под флаг TemplateTypes.OPTION_SERIES, чтобы подключаться к источнику-БА /// </summary> public IList <double> Execute(IOptionSeries optSer) { if (optSer == null) { // [{0}] Empty input (option series is NULL). string msg = RM.GetStringFormat("OptHandlerMsg.OptionSeriesIsNull", GetType().Name); m_context.Log(msg, MessageType.Warning, true); return(Constants.EmptyListDouble); } return(Execute(optSer.UnderlyingAsset)); }
/// <summary> /// Обработчик под тип входных данных SECURITY /// </summary> public IList <double> Execute(ISecurity sec) { if (sec == null) { // [{0}] Empty input (security is NULL). string msg = RM.GetStringFormat("OptHandlerMsg.SecurityIsNull", GetType().Name); return(Constants.EmptyListDouble); } var res = PrepareData(sec, Expiry); 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.", m_optionPxMode); string msg = RM.GetStringFormat("OptHandlerMsg.PositionsManager.TradingBlocked", m_optionPxMode); m_context.Log(msg, MessageType.Warning, true); return; } List <InteractiveActionEventArgs> onClickEvents = m_context.LoadObject(VariableId + "OnClickEvent") as List <InteractiveActionEventArgs>; if (onClickEvents == null) { onClickEvents = new List <InteractiveActionEventArgs>(); m_context.StoreObject(VariableId + "OnClickEvent", onClickEvents); } onClickEvents.Add(eventArgs); m_context.Recalc(RecalcReasons.ChartTrading, null); }
public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, int barNum) { //InteractiveSeries res = context.LoadObject(cashKey + "positionDelta") as InteractiveSeries; //if (res == null) //{ // res = new InteractiveSeries(); // context.StoreObject(cashKey + "positionDelta", res); //} InteractiveSeries res = new InteractiveSeries(); int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (optSer == null)) { return(res); } //double F = prices[prices.Count - 1]; double dT = time; if (Double.IsNaN(dT) || (dT < Double.Epsilon)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); m_context.Log(msg, MessageType.Error, true); return(res); } if (smile == null) { string msg = String.Format("[{0}] Argument 'smile' must be filled with InteractiveSeries.", GetType().Name); m_context.Log(msg, MessageType.Error, true); return(res); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if (oldInfo == null) { string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag); m_context.Log(msg, MessageType.Error, true); return(res); } //// TODO: переписаться на обновление старых значений //res.ControlPoints.Clear(); List <double> xs = new List <double>(); List <double> ys = new List <double>(); var smilePoints = smile.ControlPoints; IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); foreach (InteractiveObject iob in smilePoints) { double rawVega, f = iob.Anchor.ValueX; if (TryEstimateVega(posMan, optSer, pairs, smile, m_greekAlgo, f, m_sigmaStep, dT, out rawVega)) { // Переводим вегу в дифференциал 'изменение цены за 1% волы'. rawVega /= Constants.PctMult; InteractivePointActive ip = new InteractivePointActive(); ip.IsActive = m_showNodes; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = System.Windows.Media.Colors.Green; double y = rawVega; ip.Value = new Point(f, y); string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture); ip.Tooltip = String.Format("F:{0}; V:{1}", f, yStr); controlPoints.Add(new InteractiveObject(ip)); xs.Add(f); ys.Add(y); } } res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { SmileInfo info = new SmileInfo(); info.F = oldInfo.F; info.dT = oldInfo.dT; info.RiskFreeRate = oldInfo.RiskFreeRate; NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } return(res); }
public InteractiveSeries Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, double scaleMult, double ratePct, int barNum) { int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (smile == null) || (optSer == null)) { return(Constants.EmptySeries); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if ((oldInfo == null) || (oldInfo.ContinuousFunction == null) || (oldInfo.ContinuousFunctionD1 == null)) { return(Constants.EmptySeries); } int lastBarIndex = optSer.UnderlyingAsset.Bars.Count - 1; DateTime now = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Date; bool wasInitialized = HandlerInitializedToday(now); double futPx = price; double dT = time; if (!DoubleUtil.IsPositive(dT)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); if (wasInitialized) { m_context.Log(msg, MessageType.Error, 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); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } if (!DoubleUtil.IsPositive(scaleMult)) { //throw new ScriptException("Argument 'scaleMult' contains NaN for some strange reason. scaleMult:" + scaleMult); return(Constants.EmptySeries); } if (Double.IsNaN(ratePct)) { //throw new ScriptException("Argument 'ratePct' contains NaN for some strange reason. rate:" + rate); return(Constants.EmptySeries); } double ivAtm, slopeAtm, shape; if ((!oldInfo.ContinuousFunction.TryGetValue(futPx, out ivAtm)) || (!oldInfo.ContinuousFunctionD1.TryGetValue(futPx, out slopeAtm))) { return(Constants.EmptySeries); } if (m_setIvByHands) { ivAtm = m_ivAtmPct.Value / Constants.PctMult; } if (m_setSlopeByHands) { slopeAtm = m_slopePct.Value / Constants.PctMult; //slopeAtm = slopeAtm / F / Math.Pow(dT, Pow + shape); } //if (setShapeByHands) { shape = m_shapePct.Value / Constants.PctMult; } 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); } if (Double.IsNaN(slopeAtm)) { // [{0}] Smile skew at the money must be some number. skewAtm:{1} string msg = RM.GetStringFormat("OptHandlerMsg.SkewAtmMustBeNumber", GetType().Name, slopeAtm); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } SmileInfo templateInfo; #region Fill templateInfo if (m_useLocalTemplate) { InteractiveSeries templateSmile = m_context.LoadObject(m_frozenSmileId) as InteractiveSeries; if (templateSmile == null) { // [{0}] There is no LOCAL smile with ID:{1} string msg = RM.GetStringFormat("SmileImitation5.NoLocalSmile", GetType().Name, m_frozenSmileId); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } SmileInfo locInfo = new SmileInfo(); locInfo.F = futPx; locInfo.dT = dT; locInfo.RiskFreeRate = oldInfo.RiskFreeRate; List <double> locXs = new List <double>(); List <double> locYs = new List <double>(); foreach (InteractiveObject oldObj in templateSmile.ControlPoints) { if (!oldObj.AnchorIsActive) { continue; } double k = oldObj.Anchor.ValueX; double sigma = oldObj.Anchor.ValueY; double x = Math.Log(k / futPx) / Math.Pow(dT, DefaultPow + shape) / ivAtm; double sigmaNormalized = sigma / ivAtm; locXs.Add(x); locYs.Add(sigmaNormalized); } try { NotAKnotCubicSpline spline = new NotAKnotCubicSpline(locXs, locYs); locInfo.ContinuousFunction = spline; locInfo.ContinuousFunctionD1 = spline.DeriveD1(); templateInfo = locInfo; } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } } else { //templateSmile = context.LoadGlobalObject(globalSmileID, true) as InteractiveSeries; templateInfo = m_context.LoadGlobalObject(m_globalSmileId, true) as SmileInfo; if (templateInfo == null) { // [{0}] There is no global templateInfo with ID:{1}. I'll try to use default one. string msg = RM.GetStringFormat("SmileImitation5.TemplateWasSaved", GetType().Name, m_globalSmileId); m_context.Log(msg, MessageType.Error, true); System.Xml.Linq.XDocument xDoc = System.Xml.Linq.XDocument.Parse(SmileFunction5.XmlSmileRiz4Nov1); System.Xml.Linq.XElement xInfo = xDoc.Root; SmileInfo templateSmile = SmileInfo.FromXElement(xInfo); // Обновляю уровень IV ATM? if (Double.IsNaN(ivAtm)) { ivAtm = oldInfo.ContinuousFunction.Value(futPx); m_context.Log(String.Format("[DEBUG:{0}] ivAtm was NaN. I'll use value ivAtm:{1}", GetType().Name, ivAtm), MessageType.Warning, true); if (Double.IsNaN(ivAtm)) { throw new Exception(String.Format("[DEBUG:{0}] ivAtm is NaN.", GetType().Name)); } } templateSmile.F = futPx; templateSmile.dT = dT; templateSmile.RiskFreeRate = oldInfo.RiskFreeRate; m_context.StoreGlobalObject(m_globalSmileId, templateSmile, true); // [{0}] Default templateInfo was saved to Global Cache with ID:{1}. msg = RM.GetStringFormat("SmileImitation5.TemplateWasSaved", GetType().Name, m_globalSmileId); m_context.Log(msg, MessageType.Warning, true); templateInfo = templateSmile; } } #endregion Fill templateInfo if (!m_setIvByHands) { m_ivAtmPct.Value = ivAtm * Constants.PctMult; } if (!m_setShapeByHands) { // так я ещё не умею } if (!m_setSlopeByHands) { // Пересчитываю наклон в безразмерку double dSigmaDx = slopeAtm * futPx * Math.Pow(dT, DefaultPow + shape); m_slopePct.Value = dSigmaDx * Constants.PctMult; // и теперь апдейчу локальную переменную slopeAtm: slopeAtm = m_slopePct.Value / Constants.PctMult; } // Это функция в нормированных координатах // поэтому достаточно обычной симметризации // PROD-3111: заменяю вызов на SmileFunctionExtended //SimmetrizeFunc simmFunc = new SimmetrizeFunc(templateInfo.ContinuousFunction); //SimmetrizeFunc simmFuncD1 = new SimmetrizeFunc(templateInfo.ContinuousFunctionD1); //SmileFunction5 smileFunc = new SmileFunction5(simmFunc, simmFuncD1, ivAtm, slopeAtm, shape, futPx, dT); SmileFunctionExtended smileFunc = new SmileFunctionExtended( (NotAKnotCubicSpline)templateInfo.ContinuousFunction, ivAtm, slopeAtm, shape, futPx, dT); smileFunc.UseTails = m_useSmileTails; List <double> xs = new List <double>(); List <double> ys = new List <double>(); 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 minK = pairs[0].Strike; double maxK = pairs[pairs.Length - 1].Strike; double futStep = optSer.UnderlyingAsset.Tick; double dK = pairs[1].Strike - pairs[0].Strike; double width = (SigmaMult * ivAtm * Math.Sqrt(dT)) * futPx; width = Math.Max(width, 10 * dK); // Нельзя вылезать за границу страйков??? width = Math.Min(width, Math.Abs(futPx - minK)); width = Math.Min(width, Math.Abs(maxK - futPx)); // Generate left invisible tail if (m_generateTails) { GaussSmile.AppendLeftTail(smileFunc, xs, ys, minK, dK, false); } SortedDictionary <double, IOptionStrikePair> strikePrices; if (!SmileImitation5.TryPrepareImportantPoints(pairs, futPx, futStep, width, out strikePrices)) { string msg = String.Format("[{0}] It looks like there is no suitable points for the smile. pairs.Length:{1}", GetType().Name, pairs.Length); if (wasInitialized) { m_context.Log(msg, MessageType.Warning, true); } return(Constants.EmptySeries); } List <InteractiveObject> controlPoints = new List <InteractiveObject>(); //for (int j = 0; j < pairs.Length; j++) foreach (var kvp in strikePrices) { bool showPoint = (kvp.Value != null); //IOptionStrikePair pair = pairs[j]; //// Сверхдалекие страйки игнорируем //if ((pair.Strike < futPx - width) || (futPx + width < pair.Strike)) //{ // showPoint = false; //} //double k = pair.Strike; double k = kvp.Key; double sigma; if (!smileFunc.TryGetValue(k, out sigma)) { continue; } double vol = sigma; if (!DoubleUtil.IsPositive(sigma)) { continue; } //InteractivePointActive ip = new InteractivePointActive(k, vol); //ip.Color = (optionPxMode == OptionPxMode.Ask) ? Colors.DarkOrange : Colors.DarkCyan; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect; // Иначе неправильно выставляются координаты??? //ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", k, PctMult * sigma); InteractivePointLight ip; if (showPoint) { var tip = new InteractivePointActive(k, vol); if (k <= futPx) // Puts { SmileImitationDeribit5.FillNodeInfo(tip, futPx, dT, kvp.Value, StrikeType.Put, OptionPxMode.Mid, sigma, false, m_showNodes, scaleMult, ratePct); } else // Calls { SmileImitationDeribit5.FillNodeInfo(tip, futPx, dT, kvp.Value, StrikeType.Call, OptionPxMode.Mid, sigma, false, m_showNodes, scaleMult, ratePct); } ip = tip; } else { ip = new InteractivePointLight(k, vol); } InteractiveObject obj = new InteractiveObject(ip); //if (showPoint) // Теперь мы понимаем, что точки либо рисуются // потому что это страйки (и тогда они автоматом InteractivePointActive) // либо они присутствуют, но не рисуются их узлы. Потому что они InteractivePointLight. { controlPoints.Add(obj); } xs.Add(k); ys.Add(vol); } // ReSharper disable once UseObjectOrCollectionInitializer InteractiveSeries res = new InteractiveSeries(); res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); // Generate right invisible tail if (m_generateTails) { GaussSmile.AppendRightTail(smileFunc, xs, ys, maxK, dK, false); } var baseSec = optSer.UnderlyingAsset; DateTime scriptTime = baseSec.Bars[baseSec.Bars.Count - 1].Date; // ReSharper disable once UseObjectOrCollectionInitializer SmileInfo info = new SmileInfo(); info.F = futPx; info.dT = dT; info.Expiry = optSer.ExpirationDate; info.ScriptTime = scriptTime; info.RiskFreeRate = ratePct; info.BaseTicker = baseSec.Symbol; info.ContinuousFunction = smileFunc; info.ContinuousFunctionD1 = smileFunc.DeriveD1(); res.Tag = info; SetHandlerInitialized(now); return(res); }
public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, int barNum) { InteractiveSeries res = new InteractiveSeries(); int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (optSer == null)) { return(res); } double dT = time; if (Double.IsNaN(dT) || (dT < Double.Epsilon)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); m_context.Log(msg, MessageType.Error, true); return(res); } if (smile == null) { string msg = String.Format("[{0}] Argument 'smile' must be filled with InteractiveSeries.", GetType().Name); m_context.Log(msg, MessageType.Error, true); return(res); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if (oldInfo == null) { string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag); m_context.Log(msg, MessageType.Error, true); return(res); } List <double> xs = new List <double>(); List <double> ys = new List <double>(); var smilePoints = smile.ControlPoints; IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); foreach (InteractiveObject iob in smilePoints) { double rawTheta, f = iob.Anchor.ValueX; if (TryEstimateTheta(posMan, pairs, smile, m_greekAlgo, f, dT, m_tStep, out rawTheta)) { // Переводим тету в дифференциал 'изменение цены за 1 сутки'. rawTheta = RescaleThetaToDays(m_tRemainMode, rawTheta); // ReSharper disable once UseObjectOrCollectionInitializer InteractivePointActive ip = new InteractivePointActive(); ip.IsActive = m_showNodes; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = System.Windows.Media.Colors.Green; double y = rawTheta; ip.Value = new Point(f, y); string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture); ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "F:{0}; Th:{1}", f, yStr); controlPoints.Add(new InteractiveObject(ip)); xs.Add(f); ys.Add(y); } } res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { // ReSharper disable once UseObjectOrCollectionInitializer SmileInfo info = new SmileInfo(); info.F = oldInfo.F; info.dT = oldInfo.dT; info.RiskFreeRate = oldInfo.RiskFreeRate; NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } return(res); }
public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, int barNum) { int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (smile == null) || (optSer == null)) { return(Constants.EmptySeries); } int lastBarIndex = optSer.UnderlyingAsset.Bars.Count - 1; DateTime now = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Date; bool wasInitialized = HandlerInitializedToday(now); double dT = time; if (!DoubleUtil.IsPositive(dT)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if (oldInfo == null) { string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.EmptySeries); } double futPx = oldInfo.F; double ivAtm; if (!oldInfo.ContinuousFunction.TryGetValue(futPx, out ivAtm)) { string msg = String.Format("[{0}] Unable to get IV ATM from smile. Tag:{1}", GetType().Name, smile.Tag); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); if (pairs.Length < 2) { string msg = String.Format("[{0}] optSer must contain few strike pairs. pairs.Length:{1}", GetType().Name, pairs.Length); if (wasInitialized) { m_context.Log(msg, MessageType.Warning, true); } return(Constants.EmptySeries); } List <double> xs = new List <double>(); List <double> ys = new List <double>(); double futStep = optSer.UnderlyingAsset.Tick; PositionsManager posMan = PositionsManager.GetManager(m_context); SortedDictionary <double, IOptionStrikePair> futPrices; if (!SmileImitation5.TryPrepareImportantPoints(pairs, futPx, futStep, -1, out futPrices)) { string msg = String.Format("[{0}] It looks like there is no suitable points for the smile. pairs.Length:{1}", GetType().Name, pairs.Length); if (wasInitialized) { m_context.Log(msg, MessageType.Warning, true); } return(Constants.EmptySeries); } List <InteractiveObject> controlPoints = new List <InteractiveObject>(); foreach (var kvp in futPrices) { double f = kvp.Key; bool tradableStrike = (kvp.Value != null); double rawGreek; GetBaseGreek(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, m_greek, out rawGreek); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double totalGreek; //double putDelta = FinMath.GetOptionDelta(f, pair.Strike, dT, sigma.Value, 0, false); GetPairGreek(posMan, smile, pair, f, dT, m_greek, out totalGreek); rawGreek += totalGreek; } InteractivePointActive ip = new InteractivePointActive(); ip.IsActive = m_showNodes; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = AlphaColors.Green; double y = rawGreek; ip.Value = new Point(f, y); string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture); ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "F:{0}; {1}:{2}", f, m_greek, yStr); controlPoints.Add(new InteractiveObject(ip)); xs.Add(f); ys.Add(y); } InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); SmileInfo info = new SmileInfo(); info.F = oldInfo.F; info.dT = oldInfo.dT; info.Expiry = optSer.ExpirationDate; info.ScriptTime = now; info.RiskFreeRate = oldInfo.RiskFreeRate; info.BaseTicker = optSer.UnderlyingAsset.Symbol; try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } return(res); }
public double Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, int barNum) { int barsCount = m_context.BarsCount; if (!m_context.IsLastBarUsed) { barsCount--; } if ((barNum < barsCount - 1) || (optSer == null)) { return(Constants.NaN); } int lastBarIndex = optSer.UnderlyingAsset.Bars.Count - 1; DateTime now = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Date; bool wasInitialized = HandlerInitializedToday(now); double f = price; double dT = time; if (!DoubleUtil.IsPositive(dT)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.NaN); } if (!DoubleUtil.IsPositive(f)) { // [{0}] Base asset price must be positive value. F:{1} string msg = RM.GetStringFormat("OptHandlerMsg.FutPxMustBePositive", GetType().Name, f); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.NaN); } double rawGamma, res; double dF = optSer.UnderlyingAsset.Tick; IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); if (SingleSeriesNumericalGamma.TryEstimateGamma(posMan, optSer, pairs, smile, m_greekAlgo, f, dF, dT, out rawGamma)) { res = rawGamma; } else { res = Constants.NaN; } //SmileInfo sInfo = smile.GetTag<SmileInfo>(); //if ((sInfo != null) && (sInfo.ContinuousFunction != null)) //{ // double iv = sInfo.ContinuousFunction.Value(sInfo.F); // string msg = String.Format("[DEBUG:{0}] F:{1}; dT:{2}; smile.F:{3}; smile.dT:{4}; smile.IV:{5}", // GetType().Name, F, dT, sInfo.F, sInfo.dT, iv); // context.Log(msg, MessageType.Info, true); //} m_gamma.Value = res; //context.Log(String.Format("[{0}] Delta: {1}; rawDelta:{2}", MsgId, delta.Value, rawDelta), logColor, true); SetHandlerInitialized(now); return(res); }
public double Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, int barNum) { int barsCount = m_context.BarsCount; if (!m_context.IsLastBarUsed) { barsCount--; } if (barNum < barsCount - 1) { return(Constants.NaN); } if ((smile == null) || (optSer == null)) { return(Constants.NaN); } int lastBarIndex = optSer.UnderlyingAsset.Bars.Count - 1; DateTime now = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Date; bool wasInitialized = HandlerInitializedToday(now); double futPx = price; double dT = time; if (!DoubleUtil.IsPositive(dT)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.NaN); } if (!DoubleUtil.IsPositive(futPx)) { // [{0}] Base asset price must be positive value. F:{1} string msg = RM.GetStringFormat("OptHandlerMsg.FutPxMustBePositive", GetType().Name, futPx); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.NaN); } double rawTheta, res; IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); if (SingleSeriesNumericalTheta.TryEstimateTheta(posMan, pairs, smile, m_greekAlgo, futPx, dT, m_tStep, out rawTheta)) { // Переводим тету в дифференциал 'изменение цены за 1 сутки'. rawTheta = SingleSeriesNumericalTheta.RescaleThetaToDays(m_tRemainMode, rawTheta); res = rawTheta; } else { res = Constants.NaN; } m_theta.Value = res; //context.Log(String.Format("[{0}] Delta: {1}; rawDelta:{2}", MsgId, delta.Value, rawDelta), logColor, true); SetHandlerInitialized(now); return(res); }
/// <summary> /// Обработчик для одиночного аргумента -- опционной серии /// </summary> public ISecurity Execute(IOptionSeries optSer, int barNum) { if ((optSer == null) || (optSer.UnderlyingAsset == null)) { return(null); } int barsCount = Context.BarsCount; if (!Context.IsLastBarUsed) { barsCount--; } double actualStrike = m_fixedStrike; // TODO: При работе с фиксированным страйком можно и кеширование сделать. // А вот для второго метода кеширование не подойдет (страйк может измениться на любом индексе) IOptionStrikePair pair = SingleOption.GetStrikePair(optSer, m_selectionMode, actualStrike); if (pair == null) { if (barNum < barsCount - 1) { // Чтобы не спамить зря на старых барах молча возвращаю null return(null); } else { // Данного страйка нет в списке? Тогда выход. string expiryDate = optSer.ExpirationDate.Date.ToString(IvOnF.DateFormat, CultureInfo.InvariantCulture); // [{0}.Execute] There is no strike '{1}' in the option series '{2} ~ {3}'. Search mode: '{4}' string msg = RM.GetStringFormat("OptHandlerMsg.SingleOption.OptionNotFound", GetType().Name, actualStrike, optSer.UnderlyingAsset, expiryDate, m_selectionMode); if (Context.Runtime.IsAgentMode /* && (!m_ignoreCacheError) */) { throw new ScriptException(msg); // PROD-5501 - Кидаем исключение. } //bool isExpired = true; //// Поскольку в этом блоке настройки m_ignoreCacheError нет, то здесь всегда IsAgentMode == false! //if (Context.Runtime.IsAgentMode) //{ // int amount = optSer.UnderlyingAsset.Bars.Count; // DateTime today = (amount > 0) ? optSer.UnderlyingAsset.Bars[amount - 1].Date : new DateTime(); // isExpired = optSer.ExpirationDate.Date.AddDays(1) < today.Date; //} // А если в режиме лаборатории, тогда только жалуемся в Главный Лог и продолжаем. Context.Log(msg, MessageType.Warning, true /* !isExpired */); return(null); } } ISecurity res; if (m_optionType == StrikeType.Put) { res = pair.Put.Security; } else if (m_optionType == StrikeType.Call) { res = pair.Call.Security; } else { throw new NotSupportedException("Не могу найти опцион вида: " + m_optionType); } 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); } }
/// <summary> /// Метод под флаг TemplateTypes.SECURITY, чтобы подключаться к источнику-БА /// </summary> public IList <double> Execute(ISecurity sec) { if (sec == null) { // [{0}] Empty input (security is NULL). string msg = RM.GetStringFormat("OptHandlerMsg.SecurityIsNull", GetType().Name); m_context.Log(msg, MessageType.Warning, true); return(Constants.EmptyListDouble); } Dictionary <DateTime, double> hvSigmas = null; int barLengthInSeconds = m_timeframe; string cashKey = HV.GetGlobalCashKey(sec.Symbol, false, m_useAllData, barLengthInSeconds, m_annualizingMultiplier, m_period); try { object globalObj = Context.LoadGlobalObject(cashKey, true); hvSigmas = globalObj as Dictionary <DateTime, double>; // 'Важный' объект if (hvSigmas == null) { var container = globalObj as NotClearableContainer; if ((container != null) && (container.Content != null)) { hvSigmas = container.Content as Dictionary <DateTime, double>; } } } catch (NotSupportedException nse) { string fName = "", path = ""; if (nse.Data["fName"] != null) { fName = nse.Data["fName"].ToString(); } if (nse.Data["path"] != null) { path = nse.Data["path"].ToString(); } string msg = String.Format("[{0}.PrepareData] {1} when loading 'hvSigmas' from global cache. cashKey: {2}; Message: {3}\r\n\r\nfName: {4}; path: {5}\r\n\r\n{6}", GetType().Name, nse.GetType().FullName, cashKey, nse.Message, fName, path, nse); m_context.Log(msg, MessageType.Warning, true); } catch (Exception ex) { string msg = String.Format("[{0}.PrepareData] {1} when loading 'hvSigmas' from global cache. cashKey: {2}; Message: {3}\r\n\r\n{4}", GetType().Name, ex.GetType().FullName, cashKey, ex.Message, ex); m_context.Log(msg, MessageType.Warning, true); } if (hvSigmas == null) { // Данного ключа в глобальном кеше нет? Тогда выход. // [{0}.PrepareData] There is no HV in global cache. Probably, you have to start agent 'HV (ALL)' for security '{1}'. string msg = RM.GetStringFormat("OptHandlerMsg.GlobalHv.CacheNotFound", GetType().Name, sec); if (m_context.Runtime.IsAgentMode) { throw new ScriptException(msg); } // А если в режиме лаборатории, тогда только жалуемся и продолжаем. m_context.Log(msg, MessageType.Warning, true); return(Constants.EmptyListDouble); } List <double> res = new List <double>(); int len = sec.Bars.Count; if (len <= 0) { return(res); } int oldResLen = res.Count; double prevHv = Double.NaN; for (int j = oldResLen; j < len; j++) { DateTime now = sec.Bars[j].Date; double hv; if ((hvSigmas.TryGetValue(now, out hv)) && (!Double.IsNaN(hv)) && (hv > 0)) { prevHv = hv; res.Add(hv); } else { if (m_repeatLastHv) { if (!Double.IsNaN(prevHv)) { hv = prevHv; } else { hv = Constants.NaN; if (j == 0) { #region Отдельно обрабатываю нулевой бар double tmp = Double.NaN; DateTime foundKey = new DateTime(1, 1, 1); // [2016-02-02] Когда история становится слишком длинной, это может вызывать проблемы // при итерировании в foreach. Потому что другой поток может в этот момент добавить новую точку в коллекцию. int repeat = 7; while (repeat > 0) { tmp = Double.NaN; foundKey = new DateTime(1, 1, 1); try { lock (hvSigmas) { foreach (var kvp in hvSigmas) { if (kvp.Key > now) { continue; } if (foundKey < kvp.Key) { foundKey = kvp.Key; tmp = kvp.Value; } } } repeat = -100; } catch (InvalidOperationException invOpEx) { repeat--; Thread.Sleep(10); if (repeat <= 0) { string msg = String.Format("[{0}.Execute] {1} when iterate through 'hvSigmas'. cashKey: {2}; Message: {3}\r\n\r\n{4}", GetType().Name, invOpEx.GetType().FullName, cashKey, invOpEx.Message, invOpEx); m_context.Log(msg, MessageType.Warning, true); throw; } } } if ((foundKey.Year > 1) && (!Double.IsNaN(tmp)) && (tmp > 0)) { hv = tmp; prevHv = hv; } #endregion Отдельно обрабатываю нулевой бар } } res.Add(hv); } else { res.Add(Constants.NaN); } } } return(res); }
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); }
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); }
public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, int barNum) { int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (optSer == null)) { return(Constants.EmptySeries); } int lastBarIndex = optSer.UnderlyingAsset.Bars.Count - 1; DateTime now = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Date; bool wasInitialized = HandlerInitializedToday(now); double dT = time; if (!DoubleUtil.IsPositive(dT)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } if (smile == null) { string msg = String.Format("[{0}] Argument 'smile' must be filled with InteractiveSeries.", GetType().Name); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if (oldInfo == null) { string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.EmptySeries); } double futPx; if (DoubleUtil.IsPositive(oldInfo.F)) { futPx = oldInfo.F; } else { futPx = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Close; } IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); if (m_countFutures) { ReadOnlyCollection <IPosition> futPositions = posMan.GetActiveForBar(optSer.UnderlyingAsset); if (futPositions.Count > 0) { double futQty; GetTotalQty(futPositions, out futQty); if (!DoubleUtil.IsZero(futQty)) { // ReSharper disable once UseObjectOrCollectionInitializer InteractivePointActive ip = new InteractivePointActive(0, futQty); ip.IsActive = true; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = Colors.DarkOrange; //// PROD-1194 - Цвет ячеек и фона //if (futQty > 0) // ip.BackColor = ScriptColors.Aquamarine; //else if (futQty < 0) // ip.BackColor = ScriptColors.LightSalmon; //if (ip.BackColor != null) // ip.ForeColor = ScriptColors.Black; // TODO: Ждем решения PROD-6009 //// PROD-1194 - Цвет ячеек и фона -- фон красится по принципу "в деньгах или нет", шрифт -- по знаку //if (futQty > 0) // ip.ForeColor = ScriptColors.LightGreen; //else if (futQty < 0) // ip.ForeColor = ScriptColors.Red; ////if () // Фьючерс не красится! //// ip.ForeColor = ScriptColors.Black; ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "Fut Qty:{0}", futQty); controlPoints.Add(new InteractiveObject(ip)); } } } for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double putQty, callQty; GetPairQty(posMan, pair, out putQty, out callQty); if ((!DoubleUtil.IsZero(putQty)) || (!DoubleUtil.IsZero(callQty))) { double y = 0; Color? backCol = null; switch (m_optionType) { case StrikeType.Put: y = putQty; backCol = (pair.Strike > futPx) ? ScriptColors.LightCyan : ScriptColors.LightYellow; break; case StrikeType.Call: y = callQty; backCol = (pair.Strike < futPx) ? ScriptColors.LightCyan : ScriptColors.LightYellow; break; case StrikeType.Any: y = putQty + callQty; break; default: throw new NotImplementedException("OptionType: " + m_optionType); } if ( (!DoubleUtil.IsZero(y)) || ((m_optionType == StrikeType.Any) && ((!DoubleUtil.IsZero(putQty)) || (!DoubleUtil.IsZero(callQty))))) // Хотя вообще-то мы внутри if и второй блок проверок всегда true... { // ReSharper disable once UseObjectOrCollectionInitializer InteractivePointActive ip = new InteractivePointActive(pair.Strike, y); ip.IsActive = true; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = Colors.DarkOrange; //// PROD-1194 - Цвет ячеек и фона //if (y > 0) // ip.BackColor = ScriptColors.Aquamarine; //else if (y < 0) // ip.BackColor = ScriptColors.LightSalmon; //if (ip.BackColor != null) // ip.ForeColor = ScriptColors.Black; // TODO: Ждем решения PROD-6009 //// PROD-1194 - Цвет ячеек и фона -- фон красится по принципу "в деньгах или нет", шрифт -- по знаку //if (y > 0) // ip.ForeColor = ScriptColors.LightGreen; //else if (y < 0) // ip.ForeColor = ScriptColors.Red; ////if (backCol != null) //// ip.BackColor = backCol; ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "K:{0}; Qty:{1}", pair.Strike, y); controlPoints.Add(new InteractiveObject(ip)); } } } // ReSharper disable once UseObjectOrCollectionInitializer InteractiveSeries res = new InteractiveSeries(); // Здесь правильно делать new res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); SetHandlerInitialized(now); return(res); }
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); }
private IList <double> PrepareData(ISecurity sec, string expiryDate) { DateTime expiry; if ((!DateTime.TryParseExact(expiryDate, IvOnF.DateFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out expiry)) && (!DateTime.TryParseExact(expiryDate, IvOnF.DateFormat + " HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out expiry))) { string msg = String.Format("[{0}.{1}.PrepareData] Unable to parse expiration date '{2}'. Expected date format '{3}'.", Context.Runtime.TradeName, GetType().Name, expiryDate, IvOnF.DateFormat); m_context.Log(msg, MessageType.Warning, true); return(Constants.EmptyListDouble); } //// и тут я понял, что дату экспирации в любом случае надо задавать руками... //string cashKey = typeof(IvOnF).Name + "_ivExchangeSigmas_" + sec.Symbol + "_" + // expiryDate.Replace(':', '-'); string cashKey = IvOnF.GetCashKey(sec.Symbol, expiry, m_rescaleTime, m_tRemainMode); Dictionary <DateTime, double> ivSigmas = null; try { object globalObj = Context.LoadGlobalObject(cashKey, true); ivSigmas = globalObj as Dictionary <DateTime, double>; // PROD-3970 - 'Важный' объект if (ivSigmas == null) { var container = globalObj as NotClearableContainer; if ((container != null) && (container.Content != null)) { ivSigmas = container.Content as Dictionary <DateTime, double>; } } } catch (NotSupportedException nse) { string fName = "", path = ""; if (nse.Data["fName"] != null) { fName = nse.Data["fName"].ToString(); } if (nse.Data["path"] != null) { path = nse.Data["path"].ToString(); } string msg = String.Format("[{0}.PrepareData] {1} when loading 'ivSigmas' from global cache. cashKey: {2}; Message: {3}\r\n\r\nfName: {4}; path: {5}\r\n\r\n{6}", GetType().Name, nse.GetType().FullName, cashKey, nse.Message, fName, path, nse); m_context.Log(msg, MessageType.Warning, true); } catch (Exception ex) { string msg = String.Format("[{0}.PrepareData] {1} when loading 'ivSigmas' from global cache. cashKey: {2}; Message: {3}\r\n\r\n{4}", GetType().Name, ex.GetType().FullName, cashKey, ex.Message, ex); m_context.Log(msg, MessageType.Warning, true); } if (ivSigmas == null) { // Данного ключа в глобальном кеше нет? Тогда выход. // [{0}.PrepareData] There is no IV in global cache. Probably, you have to start agent 'Collect IV (ALL)' for security '{1}'. string msg = RM.GetStringFormat("OptHandlerMsg.GlobalIvOnF.CacheNotFound", GetType().Name, expiryDate, sec); if (m_context.Runtime.IsAgentMode && (!m_ignoreCacheError)) { throw new ScriptException(msg); // PROD-4624 - Андрей велит кидать исключение. } bool isExpired = true; if (m_context.Runtime.IsAgentMode) { int amount = sec.Bars.Count; DateTime today = (amount > 0) ? sec.Bars[amount - 1].Date : new DateTime(); isExpired = expiry.Date.AddDays(1) < today.Date; } // А если в режиме лаборатории, тогда только жалуемся и продолжаем. m_context.Log(msg, MessageType.Warning, !isExpired); // Если серия уже умерла, пишем только в локальный лог return(Constants.EmptyListDouble); } List <double> res = new List <double>(); int len = sec.Bars.Count; if (len <= 0) { return(res); } int oldResLen = res.Count; double prevIv = Double.NaN; for (int j = oldResLen; j < len; j++) { DateTime now = sec.Bars[j].Date; double iv; if (ivSigmas.TryGetValue(now, out iv) && (!Double.IsNaN(iv)) && (iv > 0)) { prevIv = iv; res.Add(iv); } else { if (m_repeatLastIv) { if (!Double.IsNaN(prevIv)) { iv = prevIv; } else { iv = Constants.NaN; if (j == 0) { #region Отдельно обрабатываю нулевой бар double tmp = Double.NaN; DateTime foundKey = new DateTime(1, 1, 1); // [2016-01-19] Когда история становится слишком длинной, это может вызывать проблемы // при итерировании в foreach. Потому что другой поток может в этот момент добавить новую точку в коллекцию. int repeat = 7; while (repeat > 0) { tmp = Double.NaN; foundKey = new DateTime(1, 1, 1); try { lock (ivSigmas) { foreach (var kvp in ivSigmas) { if (kvp.Key > now) { continue; } if (foundKey < kvp.Key) { foundKey = kvp.Key; tmp = kvp.Value; } } } repeat = -100; } catch (InvalidOperationException invOpEx) { repeat--; Thread.Sleep(10); if (repeat <= 0) { string msg = String.Format("[{0}.PrepareData] {1} when iterate through 'ivSigmas'. cashKey: {2}; Message: {3}\r\n\r\n{4}", GetType().Name, invOpEx.GetType().FullName, cashKey, invOpEx.Message, invOpEx); m_context.Log(msg, MessageType.Warning, true); throw; } } } if ((foundKey.Year > 1) && (!Double.IsNaN(tmp)) && (tmp > 0)) { iv = tmp; prevIv = iv; } #endregion Отдельно обрабатываю нулевой бар } } res.Add(iv); } else { res.Add(Constants.NaN); } } } return(res); }
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); }
public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, int barNum) { int barsCount = ContextBarsCount; if (barNum < barsCount - 1) { return(Constants.EmptySeries); } //double F = prices[prices.Count - 1]; double dT = time; if (!DoubleUtil.IsPositive(dT)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); m_context.Log(msg, MessageType.Error, true); return(Constants.EmptySeries); } if (smile == null) { string msg = String.Format("[{0}] Argument 'smile' must be filled with InteractiveSeries.", GetType().Name); m_context.Log(msg, MessageType.Error, true); return(Constants.EmptySeries); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if (oldInfo == null) { string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag); m_context.Log(msg, MessageType.Error, true); return(Constants.EmptySeries); } double f = m_minStrike; List <double> xs = new List <double>(); List <double> ys = new List <double>(); IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); while (f <= m_maxStrike) { double rawDelta; GetBaseDelta(posMan, optSer.UnderlyingAsset, optSer.UnderlyingAsset.Bars.Count - 1, f, out rawDelta); for (int j = 0; j < pairs.Length; j++) { IOptionStrikePair pair = pairs[j]; double totalDelta; //double putDelta = FinMath.GetOptionDelta(f, pair.Strike, dT, sigma.Value, 0, false); GetPairDelta(posMan, smile, pair, f, dT, out totalDelta); rawDelta += totalDelta; } InteractivePointActive ip = new InteractivePointActive(); ip.IsActive = m_showNodes; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = AlphaColors.Green; double y = rawDelta; ip.Value = new Point(f, y); string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture); ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "F:{0}; D:{1}", f, yStr); controlPoints.Add(new InteractiveObject(ip)); xs.Add(f); ys.Add(y); f += m_strikeStep; } InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { SmileInfo info = new SmileInfo(); info.F = oldInfo.F; info.dT = oldInfo.dT; info.RiskFreeRate = oldInfo.RiskFreeRate; NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } return(res); }
public InteractiveSeries Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, double ratePct, int barNum) { int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (smile == null) || (optSer == null)) { return(Constants.EmptySeries); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if ((oldInfo == null) || (oldInfo.ContinuousFunction == null) || (oldInfo.ContinuousFunctionD1 == null)) { return(Constants.EmptySeries); } int lastBarIndex = optSer.UnderlyingAsset.Bars.Count - 1; DateTime now = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Date; bool wasInitialized = HandlerInitializedToday(now); double futPx = price; double dT = time; if (Double.IsNaN(dT) || (dT < Double.Epsilon)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } if (Double.IsNaN(futPx) || (futPx < Double.Epsilon)) { // [{0}] Base asset price must be positive value. F:{1} string msg = RM.GetStringFormat("OptHandlerMsg.FutPxMustBePositive", GetType().Name, futPx); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } if (Double.IsNaN(ratePct)) { //throw new ScriptException("Argument 'ratePct' contains NaN for some strange reason. rate:" + rate); return(Constants.EmptySeries); } double ivAtm, slopeAtm; if ((!oldInfo.ContinuousFunction.TryGetValue(futPx, out ivAtm)) || (!oldInfo.ContinuousFunctionD1.TryGetValue(futPx, out slopeAtm))) { return(Constants.EmptySeries); } if (m_setIvByHands) { ivAtm = m_ivAtmPct.Value / Constants.PctMult; } if (m_setSlopeByHands) { slopeAtm = m_internalSlopePct.Value / Constants.PctMult; slopeAtm = slopeAtm / futPx / Math.Sqrt(dT); } if (Double.IsNaN(ivAtm) || (ivAtm < Double.Epsilon)) { // [{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); } if (Double.IsNaN(slopeAtm)) { // [{0}] Smile skew at the money must be some number. skewAtm:{1} string msg = RM.GetStringFormat("OptHandlerMsg.SkewAtmMustBeNumber", GetType().Name, slopeAtm); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } //{ // string msg = String.Format("[DEBUG:{0}] ivAtm:{1}; shift:{2}; depth:{3}; F:{4}; dT:{5}; ", // GetType().Name, ivAtm, shift, depth, F, dT); // context.Log(msg, MessageType.Info, true); //} SmileFunction3 tempFunc = new SmileFunction3(ivAtm, m_shift, 0.5, futPx, dT); double depth = tempFunc.GetDepthUsingSlopeATM(slopeAtm); SmileFunction3 smileFunc = new SmileFunction3(ivAtm, m_shift, depth, futPx, dT); if (!m_setIvByHands) { m_ivAtmPct.Value = ivAtm * Constants.PctMult; } m_depthPct.Value = depth * Constants.PctMult; if (!m_setSlopeByHands) { double dSigmaDx = slopeAtm * futPx * Math.Sqrt(dT); m_internalSlopePct.Value = dSigmaDx * Constants.PctMult; } List <double> xs = new List <double>(); List <double> ys = new List <double>(); 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 minK = pairs[0].Strike; double dK = pairs[1].Strike - pairs[0].Strike; double width = (SigmaMult * ivAtm * Math.Sqrt(dT)) * futPx; width = Math.Max(width, 2 * dK); // Generate left invisible tail if (m_generateTails) { GaussSmile.AppendLeftTail(smileFunc, xs, ys, minK, dK, false); } List <InteractiveObject> controlPoints = new List <InteractiveObject>(); for (int j = 0; j < pairs.Length; j++) { bool showPoint = true; IOptionStrikePair pair = pairs[j]; // Сверхдалекие страйки игнорируем if ((pair.Strike < futPx - width) || (futPx + width < pair.Strike)) { showPoint = false; } double k = pair.Strike; double sigma = smileFunc.Value(k); double vol = sigma; if (Double.IsNaN(sigma) || Double.IsInfinity(sigma) || (sigma < Double.Epsilon)) { continue; } InteractivePointActive ip = new InteractivePointActive(k, vol); //ip.Color = (optionPxMode == OptionPxMode.Ask) ? Colors.DarkOrange : Colors.DarkCyan; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect; // Иначе неправильно выставляются координаты??? //ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", k, PctMult * sigma); if (showPoint) { if (k <= futPx) // Puts { FillNodeInfo(ip, futPx, dT, pair, StrikeType.Put, OptionPxMode.Mid, sigma, false, m_isVisiblePoints, ratePct); } else // Calls { FillNodeInfo(ip, futPx, dT, pair, StrikeType.Call, OptionPxMode.Mid, sigma, false, m_isVisiblePoints, ratePct); } } InteractiveObject obj = new InteractiveObject(ip); if (showPoint) { controlPoints.Add(obj); } xs.Add(k); ys.Add(vol); } // ReSharper disable once UseObjectOrCollectionInitializer InteractiveSeries res = new InteractiveSeries(); res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); double maxK = pairs[pairs.Length - 1].Strike; // Generate right invisible tail if (m_generateTails) { GaussSmile.AppendRightTail(smileFunc, xs, ys, maxK, dK, false); } var baseSec = optSer.UnderlyingAsset; DateTime scriptTime = baseSec.Bars[baseSec.Bars.Count - 1].Date; // ReSharper disable once UseObjectOrCollectionInitializer SmileInfo info = new SmileInfo(); info.F = futPx; info.dT = dT; info.Expiry = optSer.ExpirationDate; info.ScriptTime = scriptTime; info.RiskFreeRate = ratePct; info.BaseTicker = baseSec.Symbol; info.ContinuousFunction = smileFunc; info.ContinuousFunctionD1 = smileFunc.DeriveD1(); res.Tag = info; SetHandlerInitialized(now); return(res); }
public InteractiveSeries Execute(double price, double time, IOptionSeries optSer, int barNum) { int barsCount = ContextBarsCount; if (barNum < barsCount - 1) { return(Constants.EmptySeries); } double f = price; double dT = time; if (Double.IsNaN(dT) || (dT < Double.Epsilon)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); m_context.Log(msg, MessageType.Error, true); return(Constants.EmptySeries); } if (Double.IsNaN(f) || (f < Double.Epsilon)) { // [{0}] Base asset price must be positive value. F:{1} string msg = RM.GetStringFormat("OptHandlerMsg.FutPxMustBePositive", GetType().Name, f); m_context.Log(msg, MessageType.Error, true); return(Constants.EmptySeries); } //{ // string msg = String.Format("[DEBUG:{0}] ivAtm:{1}; shift:{2}; depth:{3}; F:{4}; dT:{5}; ", // GetType().Name, ivAtm, shift, depth, F, dT); // context.Log(msg, MessageType.Info, true); //} SmileFunction3 smileFunc = new SmileFunction3(m_ivAtm, m_shift, m_depth, f, dT); List <double> xs = new List <double>(); List <double> ys = new List <double>(); IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); if (pairs.Length < 2) { string msg = String.Format("[WARNING:{0}] optSer must contain few strike pairs. pairs.Length:{1}", GetType().Name, pairs.Length); m_context.Log(msg, MessageType.Warning, true); return(Constants.EmptySeries); } double minK = pairs[0].Strike; double dK = pairs[1].Strike - pairs[0].Strike; // Generate left invisible tail if (m_generateTails) { AppendLeftTail(smileFunc, xs, ys, minK, dK, false); } List <InteractiveObject> controlPoints = new List <InteractiveObject>(); for (int j = 0; j < pairs.Length; j++) { bool showPoint = true; IOptionStrikePair pair = pairs[j]; // Сверхдалекие страйки игнорируем if ((pair.Strike < m_minStrike) || (m_maxStrike < pair.Strike)) { showPoint = false; } double k = pair.Strike; double sigma = smileFunc.Value(k); double vol = sigma; InteractivePointActive ip = new InteractivePointActive(k, vol); //ip.Color = (optionPxMode == OptionPxMode.Ask) ? Colors.DarkOrange : Colors.DarkCyan; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; // (optionPxMode == OptionPxMode.Ask) ? Geometries.Rect : Geometries.Rect; // Иначе неправильно выставляются координаты??? //ip.Tooltip = String.Format("K:{0}; IV:{1:0.00}", k, PctMult * sigma); if (showPoint) { if (k <= f) // Puts { FillNodeInfo(ip, f, dT, pair, StrikeType.Put, OptionPxMode.Mid, sigma, false, m_isVisiblePoints); } else // Calls { FillNodeInfo(ip, f, dT, pair, StrikeType.Call, OptionPxMode.Mid, sigma, false, m_isVisiblePoints); } } InteractiveObject obj = new InteractiveObject(ip); if (showPoint) { controlPoints.Add(obj); } xs.Add(k); ys.Add(vol); } // ReSharper disable once UseObjectOrCollectionInitializer InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); double maxK = pairs[pairs.Length - 1].Strike; // Generate right invisible tail if (m_generateTails) { AppendRightTail(smileFunc, xs, ys, maxK, dK, false); } // ReSharper disable once UseObjectOrCollectionInitializer SmileInfo info = new SmileInfo(); info.F = f; info.dT = dT; info.RiskFreeRate = 0; info.ContinuousFunction = smileFunc; info.ContinuousFunctionD1 = smileFunc.DeriveD1(); res.Tag = info; return(res); }
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); }
public InteractiveSeries Execute(double time, InteractiveSeries smile, IOptionSeries optSer, double btcUsdIndex, int barNum) { int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (smile == null) || (optSer == null)) { return(Constants.EmptySeries); } int lastBarIndex = optSer.UnderlyingAsset.Bars.Count - 1; DateTime now = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Date; bool wasInitialized = HandlerInitializedToday(now); double dT = time; if (!DoubleUtil.IsPositive(dT)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } if (!DoubleUtil.IsPositive(btcUsdIndex)) { // TODO: сделать ресурс! [{0}] Price of BTC/USD must be positive value. scaleMult:{1} //string msg = RM.GetStringFormat("OptHandlerMsg.CurrencyScaleMustBePositive", GetType().Name, scaleMult); string msg = String.Format("[{0}] Price of BTC/USD must be positive value. scaleMult:{1}", GetType().Name, btcUsdIndex); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if (oldInfo == null) { string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.EmptySeries); } double futPx = oldInfo.F; double ivAtm; if (!oldInfo.ContinuousFunction.TryGetValue(futPx, out ivAtm)) { string msg = String.Format("[{0}] Unable to get IV ATM from smile. Tag:{1}", GetType().Name, smile.Tag); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.EmptySeries); } IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); if (pairs.Length < 2) { string msg = String.Format("[{0}] optSer must contain few strike pairs. pairs.Length:{1}", GetType().Name, pairs.Length); if (wasInitialized) { m_context.Log(msg, MessageType.Warning, true); } return(Constants.EmptySeries); } List <double> xs = new List <double>(); List <double> ys = new List <double>(); double futStep = optSer.UnderlyingAsset.Tick; PositionsManager posMan = PositionsManager.GetManager(m_context); ReadOnlyCollection <IPosition> basePositions = posMan.GetClosedOrActiveForBar(optSer.UnderlyingAsset); var optPositions = SingleSeriesProfile.GetAllOptionPositions(posMan, pairs); SortedDictionary <double, IOptionStrikePair> futPrices; if (!SmileImitation5.TryPrepareImportantPoints(pairs, futPx, futStep, -1, out futPrices)) { string msg = String.Format("[{0}] It looks like there is no suitable points for the smile. pairs.Length:{1}", GetType().Name, pairs.Length); if (wasInitialized) { m_context.Log(msg, MessageType.Warning, true); } return(Constants.EmptySeries); } // Чтобы учесть базис между фьючерсом и индексом, вычисляю их отношение: // Пример: BtcInd==9023; FutPx==8937 --> indexDivByFutRatio == 1.009623 double indexDivByFutRatio = btcUsdIndex / futPx; List <InteractiveObject> controlPoints = new List <InteractiveObject>(); // Список точек для вычисления дельт + улыбки для этих точек var deltaPoints = new List <Tuple <double, InteractivePointActive> >(); foreach (var kvp in futPrices) { // Если у нас новая цена фьючерса, значит, будет новая цена индекса double f = kvp.Key; // И при пересчете опционов в баксы НУЖНО ИСПОЛЬЗОВАТЬ ИМЕННО ЕЁ!!! double newScaleMult = f * indexDivByFutRatio; bool tradableStrike = (kvp.Value != null); CashPnlUsd cashPnlUsd; CashPnlBtc cashPnlBtc; GetBasePnl(basePositions, lastBarIndex, f, m_futNominal, out cashPnlUsd, out cashPnlBtc); double cashDollars = cashPnlUsd.CashUsd; double pnlDollars = cashPnlUsd.PnlUsd; double cashBtc = cashPnlBtc.CashBtc; double pnlBtc = cashPnlBtc.PnlBtc; SmileInfo actualSmile = SingleSeriesProfile.GetActualSmile(oldInfo, m_greekAlgo, f); // Флаг того, что ПНЛ по всем инструментам был расчитан верно bool pnlIsCorrect = true; for (int j = 0; (j < pairs.Length) && pnlIsCorrect; j++) { var tuple = optPositions[j]; IOptionStrikePair pair = pairs[j]; //int putBarCount = pair.Put.UnderlyingAsset.Bars.Count; //int callBarCount = pair.Call.UnderlyingAsset.Bars.Count; CashPnlUsd pairCashUsd; CashPnlBtc pairCashBtc; bool localRes = TryGetPairPnl(actualSmile, pair.Strike, lastBarIndex, lastBarIndex, tuple.Item1, tuple.Item2, f, dT, newScaleMult, out pairCashUsd, out pairCashBtc); pnlIsCorrect &= localRes; cashDollars += pairCashUsd.CashUsd; pnlDollars += pairCashUsd.PnlUsd; cashBtc += pairCashBtc.CashBtc; pnlBtc += pairCashBtc.PnlBtc; } // Профиль позиции будет рисоваться только если ПНЛ был посчитан верно по ВСЕМ инструментам! if (pnlIsCorrect) { InteractivePointLight ip; // Показаны будут только узлы, совпадающие с реальными страйками. // Потенциально это позволит сделать эти узлы пригодными для торговли по клику наподобие улыбки. if (m_showNodes && tradableStrike) { // ReSharper disable once UseObjectOrCollectionInitializer InteractivePointActive tmp = new InteractivePointActive(); tmp.IsActive = m_showNodes && tradableStrike; string pnlUsdStr = (cashDollars + pnlDollars).ToString(m_tooltipFormat, CultureInfo.InvariantCulture); string pnlBtcStr = (cashBtc + pnlBtc).ToString(DefaultBtcTooltipFormat, CultureInfo.InvariantCulture); tmp.Tooltip = String.Format(CultureInfo.InvariantCulture, " F: {0}\r\n PnL($): {1}\r\n PnL(B): {2}", f, pnlUsdStr, pnlBtcStr); // Если у нас получился сплайн по профилю позиции, значит мы можем вычислить дельту! if (m_showNodes && tradableStrike) { // Готовим важные точки var tuple = new Tuple <double, InteractivePointActive>(f, tmp); deltaPoints.Add(tuple); } ip = tmp; } else { ip = new InteractivePointLight(); } // PROD-6103 - Выводить профиль позиции в биткойнах if (ProfileAsBtc) { ip.Value = new Point(f, cashBtc + pnlBtc); } else { ip.Value = new Point(f, cashDollars + pnlDollars); } controlPoints.Add(new InteractiveObject(ip)); xs.Add(f); // PROD-6103 - Выводить профиль позиции в биткойнах if (ProfileAsBtc) { ys.Add(cashBtc + pnlBtc); } else { ys.Add(cashDollars + pnlDollars); } } // End if (pnlIsCorrect) } // End foreach (var kvp in futPrices) // ReSharper disable once UseObjectOrCollectionInitializer InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { SmileInfo info = new SmileInfo(); info.F = oldInfo.F; info.dT = oldInfo.dT; info.RiskFreeRate = oldInfo.RiskFreeRate; NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; // Если у нас получился сплайн по профилю позиции, значит мы можем вычислить дельту! for (int j = 1; j < deltaPoints.Count - 1; j++) { var tuple = deltaPoints[j]; double f = tuple.Item1; var ip = tuple.Item2; double actualDelta, deltaLeft, deltaRight; if (m_twoSideDelta) { double prevF = deltaPoints[j - 1].Item1; double nextF = deltaPoints[j + 1].Item1; double currY = deltaPoints[j].Item2.ValueY; double prevY = deltaPoints[j - 1].Item2.ValueY; double nextY = deltaPoints[j + 1].Item2.ValueY; deltaLeft = (currY - prevY) / (f - prevF); deltaRight = (nextY - currY) / (nextF - f); // Считаем дельты слева и справа // Мы передвинули улыбку в точку f и считаем дельту позиции В ЭТОЙ ЖЕ ТОЧКЕ(!) //if (info.ContinuousFunction.TryGetValue(f - 100 * futStep, out deltaLeft) && // info.ContinuousFunctionD1.TryGetValue(f + 100 * futStep, out deltaRight)) { // Первый пробел уже учтен в Tooltip ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "{0}\r\n LeftD: {1:0.0}; RightD: {2:0.0}", ip.Tooltip, deltaLeft, deltaRight); } } else { // Мы передвинули улыбку в точку f и считаем дельту позиции В ЭТОЙ ЖЕ ТОЧКЕ(!) if (info.ContinuousFunctionD1.TryGetValue(f, out actualDelta)) { // Первый пробел уже учтен в Tooltip ip.Tooltip = String.Format(CultureInfo.InvariantCulture, "{0}\r\n D: {1:0.0}", ip.Tooltip, actualDelta); } } } } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } SetHandlerInitialized(now, true); return(res); }
/// <summary> /// Общая реализация потоковой обработки для всех типов входных аргументов /// </summary> private IList <double> ExecuteStream(ISecurity sec, string symbolKey) { if ((sec == null) || String.IsNullOrWhiteSpace(symbolKey)) { return(Constants.EmptyListDouble); } // Здесь используется имя агента-источника-данных из свойства AgentName. // Это _НЕ_ имя агента, внутри которого мы сейчас находимся! string tradeName = AgentName.Replace(Constants.HtmlDot, "."); if (String.IsNullOrWhiteSpace(ValuesName)) { string msg = String.Format("[{0}:{1}] ValuesName is null or empty. Please, provide unique series name. ValuesName: '{2}'", tradeName, GetType().Name, ValuesName ?? "NULL"); m_context.Log(msg, MessageType.Warning, true); return(Constants.EmptyListDouble); } string symbol = String.IsNullOrWhiteSpace(OverrideSymbol) ? symbolKey : OverrideSymbol; string cashKey = SaveToGlobalCache.GetGlobalCashKey(tradeName, ValuesName, symbol); if (String.IsNullOrWhiteSpace(cashKey)) { string msg = String.Format("[{0}:{1}] cashKey is null or empty. cashKey: '{2}'", tradeName, GetType().Name, cashKey ?? "NULL"); m_context.Log(msg, MessageType.Warning, true); return(Constants.EmptyListDouble); } // Проверяю наличие в Глобальном Кеше объекта с указанным именем object testObj = m_context.LoadGlobalObject(cashKey); var testDict = testObj as Dictionary <DateTime, double>; if (testDict == null) { var container = testObj as NotClearableContainer; if ((container != null) && (container.Content != null)) { testDict = container.Content as Dictionary <DateTime, double>; } } if ((testObj == null) || (testDict == null)) { // Данного ключа в глобальном кеше нет? Тогда выход. // [{0}] There is no value in global cache. Probably, you have to start agent '{1}' for security '{2}' to collect values '{3}'. string msg = RM.GetStringFormat("OptHandlerMsg.LoadFromGlobalCache.CacheNotFound", GetType().Name, tradeName, symbol, ValuesName); if (m_context.Runtime.IsAgentMode) { throw new ScriptException(msg); // PROD-4624 - Андрей велит кидать исключение. } // А если в режиме лаборатории, тогда только жалуемся и продолжаем. m_context.Log(msg, MessageType.Warning, true); return(Constants.EmptyListDouble); } // По факту передаю управление в метод CommonExecute IList <double> updatedValues = CommonStreamExecute(m_variableId + "_" + cashKey, cashKey, sec, RepeatLastValue, true, true, EmptyArrays.Object); //if (basePrices.Count > 0) //{ // double px = basePrices[basePrices.Count - 1]; // double displayValue = FixedValue.ConvertToDisplayUnits(m_valueMode, px); // m_displayPrice.Value = displayValue; //} return(new ReadOnlyCollection <double>(updatedValues)); }
public double Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, int barNum) { int barsCount = m_context.BarsCount; if (!m_context.IsLastBarUsed) { barsCount--; } if ((barNum < barsCount - 1) || (optSer == null)) { return(Constants.NaN); } int lastBarIndex = optSer.UnderlyingAsset.Bars.Count - 1; DateTime now = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Date; bool wasInitialized = HandlerInitializedToday(now); double f = price; double dT = time; if (!DoubleUtil.IsPositive(dT)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.NaN); } if (!DoubleUtil.IsPositive(f)) { // [{0}] Base asset price must be positive value. F:{1} string msg = RM.GetStringFormat("OptHandlerMsg.FutPxMustBePositive", GetType().Name, f); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.NaN); } if (smile == null) { string msg = String.Format("[{0}] Argument 'smile' must be filled with InteractiveSeries.", GetType().Name); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.NaN); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if (oldInfo == null) { string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag); if (wasInitialized) { m_context.Log(msg, MessageType.Error, true); } return(Constants.NaN); } double rawDelta, res; double dF = optSer.UnderlyingAsset.Tick; IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); if (SingleSeriesNumericalDelta.TryEstimateDelta(posMan, optSer, pairs, smile, m_greekAlgo, f, dF, dT, out rawDelta)) { res = rawDelta; } else { res = Constants.NaN; } SmileInfo sInfo = smile.GetTag <SmileInfo>(); if ((sInfo != null) && (sInfo.ContinuousFunction != null)) { double iv = sInfo.ContinuousFunction.Value(sInfo.F); string msg = String.Format("[{0}] F:{1}; dT:{2}; smile.F:{3}; smile.dT:{4}; smile.IV:{5}", GetType().Name, f, dT, sInfo.F, sInfo.dT, iv); m_context.Log(msg, MessageType.Info, false); } m_delta.Value = res; //context.Log(String.Format("[{0}] Delta: {1}; rawDelta:{2}", MsgId, delta.Value, rawDelta), logColor, true); if (m_hedgeDelta) { #region Hedge logic try { int rounded = Math.Sign(rawDelta) * ((int)Math.Floor(Math.Abs(rawDelta))); if (rounded == 0) { string msg = String.Format("[{0}] Delta is too low to hedge. Delta: {1}", MsgId, rawDelta); m_context.Log(msg, MessageType.Info, true); } else { int len = optSer.UnderlyingAsset.Bars.Count; ISecurity sec = (from s in m_context.Runtime.Securities where (s.SecurityDescription.Equals(optSer.UnderlyingAsset.SecurityDescription)) select s).SingleOrDefault(); if (sec == null) { string msg = String.Format("[{0}] There is no security. Symbol: {1}", MsgId, optSer.UnderlyingAsset.Symbol); m_context.Log(msg, MessageType.Warning, true); } else { if (rounded < 0) { string signalName = String.Format("\r\nDelta BUY\r\nF:{0}; dT:{1}; Delta:{2}\r\n", f, dT, rawDelta); m_context.Log(signalName, MessageType.Warning, true); posMan.BuyAtPrice(m_context, sec, Math.Abs(rounded), f, signalName, null); } else if (rounded > 0) { string signalName = String.Format("\r\nDelta SELL\r\nF:{0}; dT:{1}; Delta:+{2}\r\n", f, dT, rawDelta); m_context.Log(signalName, MessageType.Warning, true); posMan.SellAtPrice(m_context, sec, Math.Abs(rounded), f, signalName, null); } } } } finally { m_hedgeDelta = false; } #endregion Hedge logic } SetHandlerInitialized(now); return(res); }
public InteractiveSeries Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, int barNum) { int barsCount = ContextBarsCount; if ((barNum < barsCount - 1) || (optSer == null)) { return(Constants.EmptySeries); } int lastBarIndex = optSer.UnderlyingAsset.Bars.Count - 1; DateTime now = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Date; bool wasInitialized = HandlerInitializedToday(now); double futPx = price; double dT = time; if (!DoubleUtil.IsPositive(dT)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.EmptySeries); } if (!DoubleUtil.IsPositive(futPx)) { // [{0}] Base asset price must be positive value. F:{1} string msg = RM.GetStringFormat("OptHandlerMsg.FutPxMustBePositive", GetType().Name, futPx); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.EmptySeries); } if (smile == null) { string msg = String.Format("[{0}] Argument 'smile' must be filled with InteractiveSeries.", GetType().Name); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.EmptySeries); } if (m_optionType == StrikeType.Any) { string msg = String.Format("[{0}] OptionType '{1}' is not supported.", GetType().Name, m_optionType); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.EmptySeries); } SmileInfo oldInfo = smile.GetTag <SmileInfo>(); if (oldInfo == null) { string msg = String.Format("[{0}] Property Tag of object smile must be filled with SmileInfo. Tag:{1}", GetType().Name, smile.Tag); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.EmptySeries); } bool isCall = m_optionType == StrikeType.Call; double putQty = isCall ? 0 : m_fixedQty; double callQty = isCall ? m_fixedQty : 0; double riskFreeRate = 0; List <double> xs = new List <double>(); List <double> ys = new List <double>(); var smilePoints = smile.ControlPoints; IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); List <InteractiveObject> controlPoints = new List <InteractiveObject>(); foreach (IOptionStrikePair pair in pairs) { double rawTheta; if (TryEstimateTheta(putQty, callQty, optSer, pair, smile, m_greekAlgo, futPx, dT, m_tStep, riskFreeRate, out rawTheta)) { // Переводим тету в дифференциал 'изменение цены за 1 сутки'. rawTheta /= OptionUtils.DaysInYear; InteractivePointActive ip = new InteractivePointActive(); ip.IsActive = m_showNodes; //ip.DragableMode = DragableMode.None; //ip.Geometry = Geometries.Rect; //ip.Color = System.Windows.Media.Colors.Green; double y = rawTheta; ip.Value = new Point(pair.Strike, y); string yStr = y.ToString(m_tooltipFormat, CultureInfo.InvariantCulture); ip.Tooltip = String.Format("K:{0}; Th:{1}", pair.Strike, yStr); controlPoints.Add(new InteractiveObject(ip)); xs.Add(pair.Strike); ys.Add(y); } } InteractiveSeries res = new InteractiveSeries(); // Здесь так надо -- мы делаем новую улыбку res.ControlPoints = new ReadOnlyCollection <InteractiveObject>(controlPoints); try { if (xs.Count >= BaseCubicSpline.MinNumberOfNodes) { SmileInfo info = new SmileInfo(); info.F = oldInfo.F; info.dT = oldInfo.dT; info.RiskFreeRate = oldInfo.RiskFreeRate; NotAKnotCubicSpline spline = new NotAKnotCubicSpline(xs, ys); info.ContinuousFunction = spline; info.ContinuousFunctionD1 = spline.DeriveD1(); res.Tag = info; } } catch (Exception ex) { m_context.Log(ex.ToString(), MessageType.Error, true); return(Constants.EmptySeries); } SetHandlerInitialized(now); return(res); }
public double Execute(double price, double time, InteractiveSeries smile, IOptionSeries optSer, int barNum) { int barsCount = m_context.BarsCount; if (!m_context.IsLastBarUsed) { barsCount--; } if (barNum < barsCount - 1) { return(Constants.NaN); } if ((smile == null) || (optSer == null)) { return(Constants.NaN); } int lastBarIndex = optSer.UnderlyingAsset.Bars.Count - 1; DateTime now = optSer.UnderlyingAsset.Bars[Math.Min(barNum, lastBarIndex)].Date; bool wasInitialized = HandlerInitializedToday(now); double f = price; double dT = time; if (!DoubleUtil.IsPositive(dT)) { // [{0}] Time to expiry must be positive value. dT:{1} string msg = RM.GetStringFormat("OptHandlerMsg.TimeMustBePositive", GetType().Name, dT); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.NaN); } if (!DoubleUtil.IsPositive(f)) { // [{0}] Base asset price must be positive value. F:{1} string msg = RM.GetStringFormat("OptHandlerMsg.FutPxMustBePositive", GetType().Name, f); if (wasInitialized) { m_context.Log(msg, MessageType.Error, false); } return(Constants.NaN); } double rawVega, res; IOptionStrikePair[] pairs = optSer.GetStrikePairs().ToArray(); PositionsManager posMan = PositionsManager.GetManager(m_context); if (SingleSeriesNumericalVega.TryEstimateVomma(posMan, optSer, pairs, smile, m_greekAlgo, f, m_sigmaStep, dT, out rawVega)) { // Переводим вомму в дифференциал 'изменение цены за 1% волы'. // В знаменателе стоит dSigma^2, поэтому и делить нужно 2 раза на 100%. rawVega /= (Constants.PctMult * Constants.PctMult); res = rawVega; } else { res = Constants.NaN; } m_vomma.Value = res; //context.Log(String.Format("[{0}] Delta: {1}; rawDelta:{2}", MsgId, delta.Value, rawDelta), logColor, true); SetHandlerInitialized(now); return(res); }