Example #1
0
        private void OnNewRatesEvent(RateRecord rec, Candle candle)
        {
            SendNewRatesEvent(rec, candle);

            _strategyController.ProcessTick(rec);
            _orderController.CloseOrdersByLimits(rec);
        }
Example #2
0
 //send events
 private void SendNewRatesEvent(RateRecord rec, Candle candle)
 {
     if (NewRatesEvent != null)
     {
         _asyncOperation.Post(delegate { NewRatesEvent(rec, candle); }, null);
     }
 }
Example #3
0
 private void SendNewRatesEvent(RateRecord rec, Candle candle)
 {
     if (NewRatesEvent != null)
     {
         NewRatesEvent(rec, candle);
     }
 }
Example #4
0
 private void OnNewRates(RateRecord rec, Candle candle)
 {
     try
     {
         if (rec != null)
         {
             if (_currentRates.Keys.Contains(rec.Name))
             {
                 _currentRates[rec.Name] = rec.Value;
             }
             else
             {
                 _currentRates.Add(rec.Name, rec.Value);
             }
             tbCurrentRates.Text = "";
             foreach (string key in _currentRates.Keys)
             {
                 tbCurrentRates.Text += string.Format("{0} - {3:dd-MM-yyyy HH-mm-ss} - {1}{2}", key, _currentRates[key], Environment.NewLine, rec.UpdateTime);
             }
         }
         DisplayCandle(candle);
     }
     catch (Exception ex)
     {
         OnMessage(ex.Message);
     }
 }
Example #5
0
        public IList <RateRecord> GetRates()
        {
            List <RateRecord>   result = new List <RateRecord>();
            NameValueCollection nvc    = new NameValueCollection();

            nvc.Add("Key", KEY);
            PostSubmitter post = new PostSubmitter(CURRENT_RATES_URL, nvc);

            post.Type = PostSubmitter.PostTypeEnum.Post;
            string       resp = post.Post();
            MemoryStream ms   = new MemoryStream(Encoding.UTF8.GetBytes(resp.ToCharArray()));
            DataSet      ds   = new DataSet();

            ds.ReadXml(ms);

            for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
            {
                RateRecord rt = new RateRecord();
                rt.Name = ds.Tables[0].Rows[i]["Quote"].ToString();
                string strRate = ds.Tables[0].Rows[i]["Display"].ToString().Substring(0, ds.Tables[0].Rows[i]["Display"].ToString().IndexOf('/'));
                rt.Value = decimal.Parse(strRate);
                object   dt         = ds.Tables[0].Rows[i]["UpdateTime"];
                DateTime updateTime = Convert.ToDateTime(dt).ToUniversalTime();
                rt.UpdateTime = updateTime;
                result.Add(rt);
            }
            return(result);
        }
Example #6
0
        public IStrategyDecision GetStrategyDecision(RateRecord rec)
        {
            DateTime date  = rec.UpdateTime;
            decimal  price = rec.Value;
            IDictionary <DateTime, decimal> historyData = _historyData[rec.Name];

            if (!historyData.Keys.Contains(date))
            {
                historyData.Add(date, price);
            }
            //return TestAnalize(historyData);
            decimal lastValue   = StrategyHelper.GetLastValue(historyData);
            decimal decrease    = StrategyHelper.GetMaximum(INTERVAL_MINUTES, historyData) - lastValue;
            decimal increase    = lastValue - StrategyHelper.GetMinimum(INTERVAL_MINUTES, historyData);
            decimal largeMedium = (StrategyHelper.GetMaximum(LARGE_MEDIUM_INTERVAL_MINUTES, historyData) + StrategyHelper.GetMinimum(LARGE_MEDIUM_INTERVAL_MINUTES, historyData)) / 2;

            int momentDirection = 0;

            if (decrease > MAX_DIRECTION)
            {
                momentDirection = -1;
            }
            if (increase > MAX_DIRECTION)
            {
                momentDirection = 1;
            }

            int longDirection = 0;

            if (lastValue > largeMedium)
            {
                longDirection = 1;
            }
            if (lastValue < largeMedium)
            {
                longDirection = -1;
            }

            int result = 0;

            if (momentDirection == -1 && longDirection == +1)
            {
                result = 1;
            }
            if (momentDirection == 1 && longDirection == -1)
            {
                result = -1;
            }

            BaseStrategyDecision decision = new BaseStrategyDecision();

            decision.TakeProfit = lastValue + (TAKE_PROFIT_POINTS * result);
            decision.StopLoss   = lastValue - (STOP_LOSS_POINTS * result);
            StringBuilder additionalInfo = new StringBuilder();

            additionalInfo.AppendFormat("Максимальное изменение за две минуты:{0}", (decrease > increase ? decrease : increase) * momentDirection);
            additionalInfo.AppendFormat("Текущее отклонение от среднего за 30 минут:{0}", lastValue - largeMedium);
            return(decision);
        }
 public void ProcessTick(RateRecord rateRecord)
 {
     foreach (IStrategyProvider provider in _strategyProviders)
     {
         //analize
         IStrategyDecision decision = provider.GetStrategyDecision(rateRecord);
         //make orders
         OpenOrCloseOrders(provider.GetName(), decision, rateRecord);
     }
 }
 public void CloseOrdersByLimits(RateRecord rec)
 {
     for (int i = _openedOrders.Count - 1; i >= 0; i--)
     {
         Order order = _openedOrders[i];
         if (rec.Name != order.RateName)
         {
             continue;
         }
         if (order.Direction < 0 && (order.StopLoss < rec.Value || order.TakeProfit > rec.Value))
         {
             CloseOrder(order, rec);
         }
         if (order.Direction > 0 && (order.StopLoss > rec.Value || order.TakeProfit < rec.Value))
         {
             CloseOrder(order, rec);
         }
     }
 }
        public void CloseOrder(Order order, RateRecord rec)
        {
            if (order.RateName != rec.Name)
            {
                return;
            }
            order.ClosePrice = rec.Value;
            order.CloseTime  = rec.UpdateTime;
            if (order.IsReal)
            {
                order.IsReal = _enableRealOrders = StartFx.CloseOrder(order.RateName);
            }
            UpdateStrategyResult(order);

            if (CloseOrderEvent != null)
            {
                CloseOrderEvent(order);
            }


            _openedOrders.Remove(order);
        }
Example #10
0
        public static void CloseCandle(IDictionary <string, IList <Candle> > candles, RateRecord rec, int candlesIintervalMinutes)
        {
            if (!candles.ContainsKey(rec.Name))
            {
                candles.Add(rec.Name, new List <Candle>());
            }

            DateTime openCandleDate  = new DateTime(rec.UpdateTime.Year, rec.UpdateTime.Month, rec.UpdateTime.Day, rec.UpdateTime.Hour, (rec.UpdateTime.Minute / candlesIintervalMinutes) * candlesIintervalMinutes, 0);
            DateTime closeCandleDate = new DateTime(rec.UpdateTime.Year, rec.UpdateTime.Month, rec.UpdateTime.Day, rec.UpdateTime.Hour, (rec.UpdateTime.Minute / candlesIintervalMinutes) * candlesIintervalMinutes, 0).AddMinutes(candlesIintervalMinutes);

            Candle currentCandle = candles[rec.Name].Where(item => item.OpenTime == openCandleDate).FirstOrDefault();

            if (currentCandle == null)
            {
                ClosePreviousCandles(candles[rec.Name].Where(item => item.CandleData != null).ToList());
                currentCandle          = new Candle();
                currentCandle.RateName = rec.Name;
                currentCandle.OpenTime = openCandleDate;
                candles[rec.Name].Add(currentCandle);
            }

            currentCandle.AddNewData(rec.UpdateTime, rec.Value);
        }
Example #11
0
        private void MainThread()
        {
            if (_rates.Count == 0)
            {
                SendMessageEvent("Невыбрано ни одной валютной пары!");
                _shouldStop = true;
            }

            Dictionary <string, DateTime> lastProcessedDates = new Dictionary <string, DateTime>();

            if (!_shouldStop)
            {
                IDictionary <string, IDictionary <DateTime, decimal> > ratesHistory = GetRatesHistory(_rates, _dtStart.AddHours(-4), _dtStart);
                _candles = StrategyHelper.BuildCandles(ratesHistory, RateController.CANDLES_INTERVAL_MINUTES);
                if (HistoryEvent != null)
                {
                    HistoryEvent(ratesHistory, _candles);
                }
                foreach (string rateName in _rates)
                {
                    if (ratesHistory[rateName].Count == 0)
                    {
                        lastProcessedDates.Add(rateName, _dtStart);
                    }
                    else
                    {
                        lastProcessedDates.Add(rateName, ratesHistory[rateName].Keys.Max());
                    }
                }
            }

            while (!_shouldStop)
            {
                //читаем котировки из интернета
                IDictionary <string, IDictionary <DateTime, decimal> > ratesCache;
                IDictionary <string, IList <Candle> > candlesCache;
                if (_dtEnd == null)
                {
                    ratesCache   = GetCurrentRates(_rates);
                    candlesCache = GetCurrentCandles(_rates);
                }
                else
                {
                    ratesCache   = GetRatesHistory(_rates, lastProcessedDates.Values.Min().AddSeconds(1), lastProcessedDates.Values.Min().AddHours(2));
                    candlesCache = StrategyHelper.BuildCandles(ratesCache, CANDLES_INTERVAL_MINUTES);
                }

                //обрабатываем котировки по каждой валютной паре
                foreach (string rateName in _rates)
                {
                    if (_shouldStop)
                    {
                        break;
                    }
                    if (!ratesCache.ContainsKey(rateName))
                    {
                        continue;
                    }
                    IDictionary <DateTime, decimal> rateCache = ratesCache[rateName];
                    IList <Candle> candleCache = candlesCache[rateName];

                    while (rateCache.Count > 0 && rateCache.Keys.Min() > lastProcessedDates[rateName] && !_shouldStop)
                    {
                        RateRecord rec = new RateRecord();
                        rec.Name       = rateName;
                        rec.UpdateTime = rateCache.Keys.Min();
                        rec.Value      = rateCache[rec.UpdateTime];

                        Candle candle = candlesCache[rateName].Where(item => item.OpenTime < rec.UpdateTime && rec.UpdateTime < item.OpenTime.AddMinutes(RateController.CANDLES_INTERVAL_MINUTES)).FirstOrDefault();
                        SendNewRatesEvent(rec, candle);

                        rateCache.Remove(rec.UpdateTime);
                        candlesCache[rateName].Remove(candle);

                        lastProcessedDates[rateName] = rec.UpdateTime;
                    }
                }
                if (_dtEnd != null && lastProcessedDates.Values.Min() > _dtEnd)
                {
                    break;
                }
                Thread.Sleep(5000);
            }
            if (StopEvent != null)
            {
                StopEvent();
            }
            SendMessageEvent("Работа остановлена");
        }
    public IEnumerable <IList <T> > GetBatch()
    {
        var stopwatch = new Stopwatch();

        var             batch      = new List <T>();
        var             benchmarks = new List <RateRecord>(5);
        IEnumerator <T> enumerator = null;

        try
        {
            enumerator = _collection.GetEnumerator();

            uint count = 0;
            stopwatch.Start();

            while (enumerator.MoveNext())
            {
                if (count == batchLength)
                {
                    benchmarks.Add(new RateRecord {
                        Length = BatchLength, ElapsedTicks = stopwatch.ElapsedTicks
                    });

                    var currentBatch = batch.ToList();
                    batch.Clear();

                    if (benchmarks.Count == 10)
                    {
                        var currentRate = benchmarks.Average(x => x.Rate);
                        if (currentRate > bestRate.Rate)
                        {
                            bestRate = new RateRecord {
                                Length = BatchLength, ElapsedTicks = (long)benchmarks.Average(x => x.ElapsedTicks)
                            };
                            batchLength = NextPowerOf2(batchLength);
                        }
                        // Set margin of error at 10%
                        else if ((bestRate.Rate * .9) > currentRate)
                        {
                            // Shift the current length and make sure it's >= 1
                            var currentPowOf2 = ((batchLength >> 1) | 1);
                            batchLength = PreviousPowerOf2(currentPowOf2);
                        }

                        benchmarks.Clear();
                    }
                    count = 0;
                    stopwatch.Restart();

                    yield return(currentBatch);
                }

                batch.Add(enumerator.Current);
                count++;
            }
        }
        finally
        {
            if (enumerator != null)
            {
                enumerator.Dispose();
            }
        }

        stopwatch.Stop();
    }
        private void OpenOrCloseOrders(string strategyName, IStrategyDecision decision, RateRecord rec)
        {
            decimal lastValue = rec.Value;

            if (decision.Direction == 0)
            {
                return;
            }
            Order openedOrder = _orderController.GetOpenedOrder(strategyName, rec.Name);

            if (openedOrder != null)
            {
                if (openedOrder.Direction * decision.Direction > 0)
                {
                    openedOrder.TakeProfit = decision.TakeProfit;
                }
                else
                {
                    _orderController.CloseOrder(openedOrder, rec);
                }
            }
            else
            {
                _orderController.OpenOrder(strategyName, lastValue, decision.StopLoss, decision.TakeProfit, rec.Name, rec.UpdateTime, decision.AdditionalInfo);
            }
        }
Example #14
0
        /// <summary>
        /// 计算一个金融衍生品交易的定价和风险指标
        /// </summary>
        /// <param name="trade">交易</param>
        /// <param name="market">市场数据对象</param>
        /// <param name="request">计算请求类型</param>
        /// <returns>计算结果</returns>
        public override IPricingResult Calculate(TTrade trade, IMarketCondition market, PricingRequest request)
        {
            var result = new PricingResult(market.ValuationDate, request);

            if (result.IsRequested(PricingRequest.Pv))
            {
                result.Pv = CalcPv(trade, market);
            }

            if (result.IsRequested(PricingRequest.Carry))
            {
                result.Carry = CalcCarry(trade, market);
            }

            if (result.IsRequested(PricingRequest.Dv01))
            {
                if (double.IsNaN(result.Pv))
                {
                    result.Pv = CalcPv(trade, market);
                }
                var mktDown = market.FixingCurve.HasValue
                                        ? market.UpdateCondition(
                    new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, market.DiscountCurve.Value.Shift(1)),
                    new UpdateMktConditionPack <IYieldCurve>(x => x.FixingCurve, market.FixingCurve.Value.Shift(1)))
                                        : market.UpdateCondition(
                    new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, market.DiscountCurve.Value.Shift(1)));
                result.Dv01 = CalcPv(trade, mktDown) - result.Pv;
            }

            if (result.IsRequested(PricingRequest.Cashflow))
            {
                result.Cashflows = trade.GetCashflows(market, false);
            }

            //Ai and AiEod are mutually exclusive requests
            var isEod = result.IsRequested(PricingRequest.AiEod);

            if (result.IsRequested(PricingRequest.Ai) || result.IsRequested(PricingRequest.AiEod))
            {
                if (result.Cashflows == null || result.Cashflows.Length == 0)
                {
                    result.Cashflows = trade.GetCashflows(market, false);
                }

                result.Ai = trade.GetAccruedInterest(market.ValuationDate, market, isEod);
            }

            if (result.IsRequested(PricingRequest.KeyRateDv01))
            {
                if (double.IsNaN(result.Pv))
                {
                    result.Pv = CalcPv(trade, market);
                }
                var dc = new Dictionary <string, CurveRisk[]>();
                var fc = new Dictionary <string, CurveRisk[]>();

                Parallel.Invoke(
                    () => CalcDiscountDv01(trade, market, result.Pv, ref dc),
                    () => CalcResetDv01(trade, market, result.Pv, ref fc)
                    );


                result.KeyRateDv01 = PricingResultExtension.Aggregate(dc, fc);
            }

            if (result.IsRequested(PricingRequest.FairQuote))
            {
                result.FairQuote = GetFairQuote(trade, market);
            }

            if (result.IsRequested(PricingRequest.MacDuration))
            {
                if (result.Cashflows == null || result.Cashflows.Length == 0)
                {
                    result.Cashflows = trade.GetCashflows(market, false);
                }
                var weightedCf = 0.0;
                var totalCf    = 0.0;
                foreach (var cashflow in result.Cashflows)
                {
                    if (cashflow.PaymentDate > market.ValuationDate)
                    {
                        var t  = market.DiscountCurve.Value.DayCount.CalcDayCountFraction(market.ValuationDate, cashflow.PaymentDate);
                        var df = market.DiscountCurve.Value.GetDf(market.ValuationDate, cashflow.PaymentDate);

                        weightedCf += cashflow.PaymentAmount * df * t;
                        totalCf    += cashflow.PaymentAmount * df;
                    }
                }
                result.MacDuration = weightedCf / totalCf;
            }

            if (result.IsRequested(PricingRequest.Pv01))
            {
                result.Pv01 = CalcPv01(trade, market, result.Pv);
            }

            Date valueDate = result.ValuationDate;

            if (result.IsRequested(PricingRequest.ProductSpecific))
            {
                var yieldCurve = market.DiscountCurve.Value;

                #region

                var psDict = new Dictionary <string, Dictionary <string, RateRecord> >();
                var dayGap = new DayGap("+0BD");
                var T      = (trade is InterestRateSwap) ? (trade as InterestRateSwap).FloatingLeg : trade as SwapLeg;

                //forward rate points
                var tenors           = new[] { "1D", "7D", "3M", "1Y" };
                var fwdStartInTenors = new List <string> {
                    "1D", "1W", "2W", "1M", "2M", "3M", "4M", "5M", "6M", "7M", "8M", "9M", "10M", "11M", "1Y"
                };
                var totalMonths = Convert.ToInt16((yieldCurve.KeyPoints.Last().Item1 - yieldCurve.KeyPoints.First().Item1) / 30.0) + 1;
                for (var i = 15; i <= totalMonths; i += 3)
                {
                    fwdStartInTenors.Add(i + "M");
                }
                foreach (var tenor in tenors)
                {
                    var fwdRates = new Dictionary <string, RateRecord>();
                    var fwdTerm  = new Term(tenor);
                    foreach (var fwdStartInTenor in fwdStartInTenors)
                    {
                        var fwdStartDate = dayGap.Get(T.Calendar, new Term(fwdStartInTenor).Next(valueDate));
                        var fwdEndDate   = dayGap.Get(T.Calendar, fwdTerm.Next(fwdStartDate));
                        if (fwdEndDate < yieldCurve.KeyPoints.Last().Item1)
                        {
                            fwdRates[fwdStartInTenor] = new RateRecord()
                            {
                                Date = fwdStartDate.ToString(),
                                Rate = yieldCurve.GetForwardRate(fwdStartDate, fwdTerm)
                            };
                        }
                    }

                    psDict["forwardrates" + tenor] = fwdRates;
                }

                //spot rate
                var spotRates    = new Dictionary <string, RateRecord>();
                var spotInTenors = fwdStartInTenors;

                foreach (var spotInTenor in spotInTenors)
                {
                    var spotDate = dayGap.Get(T.Calendar, new Term(spotInTenor).Next(valueDate));
                    if (spotDate <= yieldCurve.KeyPoints.Last().Item1)
                    {
                        spotRates[spotInTenor] = new RateRecord
                        {
                            Date = spotDate.ToString(),
                            Rate = yieldCurve.ZeroRate(valueDate, spotDate, Compound.Simple)
                        };
                    }
                }
                psDict["spotRates"] = spotRates;

                //key rates
                var rates          = new Dictionary <string, RateRecord>();
                var ccTenors       = yieldCurve.GetKeyTenors().ToArray();
                var mktInstruments = yieldCurve.MarketInstruments;
                if (mktInstruments != null)
                {
                    if (mktInstruments.Length != ccTenors.Length)
                    {
                        throw new PricingBaseException("Number of calibration instruments mismatches number of calibrated points!");
                    }
                }
                for (var i = 0; i < ccTenors.Count(); ++i)
                {
                    //var spotDate = mktInstruments != null ? mktInstruments[i].Instrument.GetClibrationDate() : dayGap.Get(T.Calendar, new Term(ccTenors[i]).Next(valueDate));
                    var spotDate = dayGap.Get(T.Calendar, new Term(ccTenors[i]).Next(valueDate));
                    rates[ccTenors[i]] = new RateRecord()
                    {
                        ContinuousRate                           = yieldCurve.ZeroRate(valueDate, spotDate),
                        Date                                     = spotDate.ToString(),
                        DiscountFactor                           = yieldCurve.GetDf(valueDate, spotDate),
                        Rate                                     = mktInstruments == null?yieldCurve.GetSpotRate(spotDate) : mktInstruments[i].TargetValue,
                                                     ProductType = mktInstruments == null ? "None" : (mktInstruments[i].Instrument is Deposit) ? "Index" : "Swap",
                                                     ZeroRate    = yieldCurve.ZeroRate(valueDate, spotDate, Compound.Simple),
                                                     Term        = ccTenors[i]
                    };
                }
                psDict["rates"] = rates;

                //discount at cash flow dates
                var dfs   = new Dictionary <string, RateRecord>();
                var dates = result.Cashflows.Select(x => x.PaymentDate);
                foreach (var date in dates)
                {
                    dfs[date.ToString()] = new RateRecord
                    {
                        DiscountFactor = yieldCurve.GetDf(date)
                    };
                }

                psDict["discountfactor"] = dfs;
                //qb rate return
                result.ProductSpecific = psDict;

                #endregion
            }

            return(result);
        }
Example #15
0
        public virtual IStrategyDecision GetStrategyDecision(RateRecord rec)
        {
            StrategyHelper.CloseCandle(_candles, rec, CANDLES_INTERVAL_MINUTES);

            BaseStrategyDecision decision = new BaseStrategyDecision();
            Candle maximum = GetMaximumForInterval(rec.Name, CANDLES_RANGE, _candles[rec.Name].Last());
            Candle minimum = GetMinimumForInterval(rec.Name, CANDLES_RANGE, _candles[rec.Name].Last());

            if (maximum == null || minimum == null)
            {
                return(decision);
            }

            if (maximum.OpenTime > minimum.OpenTime) //тренд вверх
            {
                Candle previousMaximum = GetMaximumBetween(rec.Name, minimum, maximum);
                if (previousMaximum != null)
                {
                    Candle previousMinimum = GetMinimumBetween(rec.Name, previousMaximum, maximum);
                    if (previousMinimum != null)
                    {
                        //пробойный бар
                        Candle breakout = GetMaximumBreakout(rec.Name, previousMinimum, previousMaximum.HighPrice);
                        if (breakout != null)
                        {
                            Candle previousMaximum2 = GetMaximumBetween(rec.Name, minimum, previousMaximum);
                            if (previousMaximum2 != null)
                            {
                                //наш takeProfit
                                Candle previousMinimum2 = GetMinimumBetween(rec.Name, previousMaximum2, previousMaximum);
                                if (previousMinimum2 != null && rec.Value < breakout.LowPrice)
                                {
                                    if (rec.Value - previousMinimum2.LowPrice > 0.0010m
                                        &&
                                        breakout.HighPrice - rec.Value > 0.0005m
                                        )
                                    {
                                        decision.TakeProfit = previousMinimum2.LowPrice;
                                        decision.StopLoss   = breakout.HighPrice;
                                        StringBuilder sb = new StringBuilder();
                                        sb.AppendFormat("Takeprofit:{0}{1}", decision.TakeProfit, Environment.NewLine);
                                        sb.AppendFormat("Stoploss:{0}{1}", decision.StopLoss, Environment.NewLine);
                                        sb.AppendFormat("Предпоследний колебательный минимум был:{0:yyy-MM-dd HH:mm}{1}", previousMinimum2.OpenTime, Environment.NewLine);
                                        sb.AppendFormat("Пробойный бар:{0:yyy-MM-dd HH:mm}{1}", breakout.OpenTime, Environment.NewLine);
                                        decision.AdditionalInfo = sb.ToString();
                                    }
                                }
                            }
                        }
                    }
                }
            }

            if (minimum.OpenTime > maximum.OpenTime) // тренд вниз
            {
                Candle previousMinimum = GetMinimumBetween(rec.Name, maximum, minimum);

                if (previousMinimum != null)
                {
                    Candle previousMaximum = GetMaximumBetween(rec.Name, previousMinimum, minimum);
                    if (previousMaximum != null)
                    {
                        //пробойный бар
                        Candle breakout = GetMinimumBreakout(rec.Name, previousMaximum, previousMinimum.LowPrice);
                        if (breakout != null)
                        {
                            Candle previousMinimum2 = GetMinimumBetween(rec.Name, maximum, previousMinimum);
                            if (previousMinimum2 != null)
                            {
                                //наш takeProfit
                                Candle previousMaximum2 = GetMaximumBetween(rec.Name, previousMinimum2, previousMinimum);
                                if (previousMaximum2 != null && rec.Value > breakout.HighPrice)
                                {
                                    if (previousMaximum2.HighPrice - rec.Value > 0.0010m
                                        &&
                                        rec.Value - breakout.LowPrice > 0.0005m
                                        )
                                    {
                                        decision.TakeProfit = previousMaximum2.HighPrice;
                                        decision.StopLoss   = breakout.LowPrice;
                                        StringBuilder sb = new StringBuilder();
                                        sb.AppendFormat("Takeprofit:{0}{1}", decision.TakeProfit, Environment.NewLine);
                                        sb.AppendFormat("Stoploss:{0}{1}", decision.StopLoss, Environment.NewLine);
                                        sb.AppendFormat("Предпоследний колебательный максимум был:{0:yyy-MM-dd HH:mm}{1}", previousMaximum2.OpenTime, Environment.NewLine);
                                        sb.AppendFormat("Пробойный бар:{0:yyy-MM-dd HH:mm}{1}", breakout.OpenTime, Environment.NewLine);
                                        decision.AdditionalInfo = sb.ToString();
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(decision);
        }