Пример #1
0
        public override void Initialization()
        {
            try
            {
                //Создаем индикаторы для торговой стратегии
                _upperChannelOne = new Highest((int)Parameter(1));
                _lowerChannelOne = new Lowest((int)Parameter(1));
                _upperChannelTwo = new Highest((int)Parameter(1) / 2); //50% от P1
                _lowerChannelTwo = new Lowest((int)Parameter(1) / 2);  //50% от P1

                _adxLookBack  = new ADX((int)Parameter(2));
                _adxThreshold = new ADX((int)Parameter(3));

                _atr     = new ATR((int)Parameter(4));
                _atrStop = (int)Parameter(5);

                _volume = (int)Parameter(6);

                //Для выставления лимитных заявок в рынок для гарантированного исполнения
                _offset = GetSecurity().Tick *10;

                //Инициализируем алгоритмические заявки
                InitializationAlgoOrders();

                //Подписываемся на события при инициализации стратегии
                Subscribe();
            }
            catch (Exception ex)
            {
                ExceptionMessage(ex, "Strategy");
            }
        }
Пример #2
0
        //populate
        public override void Populate()
        {
            BarHistory bars   = Parameters[0].AsBarHistory;
            Int32      period = Parameters[1].AsInt;

            DateTimes = bars.DateTimes;

            if (period <= 0 || bars.Count == 0)
            {
                return;
            }

            var HHH = new TimeSeries(DateTimes);

            var hh = new Highest(bars.Low, period);
            var ll = new Lowest(bars.Low, period);

            //var FirstValidValue = period * 3;
            //for (int bar = FirstValidValue; bar < bars.Count; bar++)
            for (int bar = period; bar < bars.Count; bar++)
            {
                HHH[bar] = bars.High[bar] > bars.High[bar - 1] ?
                           ((bars.High[bar] - ll[bar]) /
                            (hh[bar] - ll[bar])) : 0;
            }

            var ema = new EMA(HHH, period);

            for (int bar = period; bar < bars.Count; bar++)
            {
                Values[bar] = ema[bar] * 100;
            }
        }
Пример #3
0
        // Stochastic D of a DataSeries (as opposed to Bars) per Vervoot
        private TimeSeries StochD(TimeSeries ds, int stoPer, int smooth)
        {
            var result = new TimeSeries(ds.DateTimes);
            var ll     = new Lowest(ds, stoPer);
            var hh     = new Highest(ds, stoPer);

            int start = stoPer + smooth;

            if (start >= ds.Count)
            {
                start = ds.Count;
            }

            for (int bar = 0; bar < start; bar++)
            {
                result[bar] = 50;
            }

            for (int bar = start; bar < ds.Count; bar++)
            {
                double s1 = 0;
                double s2 = 0;
                for (int n = 0; n < smooth; n++)
                {
                    double LL = ll[bar - n];
                    s1 = s1 + ds[bar - n] - LL;
                    s2 = s2 + hh[bar - n] - LL;
                }
                result[bar] = 100 * (s1 / (s2 + 0.0001));
            }
            return(result);
        }
Пример #4
0
        public override void Populate()
        {
            BarHistory bars    = base.Parameters[0].AsBarHistory;
            int        period1 = base.Parameters[1].AsInt;
            int        period2 = base.Parameters[2].AsInt;

            this.DateTimes = bars.DateTimes;
            int FirstValidValue = Math.Max(period1, period2) + 1;

            if (bars.Count < FirstValidValue)
            {
                return;
            }
            var MLTP       = 2.0 / ((double)period1 + 1.0);
            var MLTP2      = (bars.Close - Lowest.Series(bars.Low, period2) - (Highest.Series(bars.High, period2) - bars.Close)).Abs() / (Highest.Series(bars.High, period2) - Lowest.Series(bars.Low, period2));
            var timeSeries = MLTP * (1.0 + MLTP2);

            for (int i = 0; i < FirstValidValue; i++)
            {
                base[i] = FastSMA.Series(bars.Close, period1)[i];
            }

            for (int j = FirstValidValue; j < bars.Count; j++)
            {
                base.Values[j] = base.Values[j - 1] + timeSeries[j] * (bars.Close[j] - base.Values[j - 1]);
            }
        }
        public void HighestDoWhileLoopTest()
        {
            int[] nums   = { 10, 6, 22, 17, 3 };
            var   result = Highest.HighestDoWhileLoop(nums);

            Assert.AreEqual(22, result);
        }
        public void HighestWhileLoopTest()
        {
            int[] nums   = { -3, -6, -22, -17, -2 };
            var   result = Highest.HighestWhileLoop(nums);

            Assert.AreEqual(-2, result);
        }
Пример #7
0
        public void HighestDoWhileLoopTest()
        {
            int[] nums   = { -5 };
            var   result = Highest.HighestDoWhileLoop(nums);

            Assert.AreEqual(-5, result);
        }
Пример #8
0
        public SenkouSpanB(Bars bars)
            : base(bars, "Senkou Span B")
        {
            int p1 = 9;
            int p2 = 26;
            int p3 = 52;
            int p4 = 26;

            if (bars.Count < p3)
            {
                return;
            }

            DataSeries TenkanSen = ((Highest.Series(bars.High, p1) + Lowest.Series(bars.Low, p1)) / 2);
            DataSeries KijunSen  = ((Highest.Series(bars.High, p2) + Lowest.Series(bars.Low, p2)) / 2);
            DataSeries ssA       = ((TenkanSen + KijunSen) / 2) >> p4;
            DataSeries ssB       = ((Highest.Series(bars.High, p3) + Lowest.Series(bars.Low, p3)) / 2) >> p4;

            base.FirstValidValue = p3;

            for (int bar = p3; bar < bars.Count; bar++)
            {
                base[bar] = ssB[bar];
            }
        }
Пример #9
0
        // Описание:
        // За основу взята стратегия Черепах - http://www.benvanvliet.net/Downloads/turtlerules.pdf
        // Описание модификации - https://support.softalgotrade.com/forums/topic/turtle-system-revisited

        // Исходные данные: свечки или тики. Рассмотрены оба варианта.
        // Свечки используются для расчета индикаторов.
        // Для более точного входа в позицию и расчета стоп заявок можем использовать тики.

        // Алгоритм:
        // Строится два канала Дончиана - ChannelOne и ChannelTwo. Канал Дончиана - это пара индикаторов Highest и Lowest.
        // Период внутреннего канала ChannelTwo устанавливаем в процентах относительно внешнего канала ChannelOne.
        // Входим в позицию:
        // - Покупаем, когда цена касается канала UpperChannelOne.
        // - Продаем, когда цена касается канала LowerChannelOne.
        // Закрываем позицию:
        // - Покупаем, когда цена касается канала UpperChannelTwo.
        // - Продаем, когда цена касается канала LowerChannelTwo.

        // Дополнительно:
        // 1) Стратегия интрадей. Торгуем с 10:05, в конце торговой сессии (23:40) закрываем все открытые позиции - метод CheckIntraDayTime().
        // 2) При входе/выходе ориентируемся на риск, который рассчитываем по формуле: risk = k * atr,
        // Где к - коэффициент риска, atr - текущее значение индикатора ATR.
        // 3) Для расчета стоп заявок используем соответствующие методы MarkуtToMarket для тиков и свечек.
        // 4) Объем входа в позицию рассчитываем по формуле:
        // Volume = Math.Min(currentFunds * riskPerTrade / risk, currentFunds / initialMargin),
        // Где currentFunds - текущие доступные денежные средства, initialMargin - гарантийное обеспечение
        // riskPerTrade - процент риска, risk - риск (см. выше).
        // 5) Все лимитные заявки выставляем "глубоко в рынок" для мгновенного исполнения как маркет.

        // Индикаторы:
        // Две пары индикаторов Highest и Lowest - два канала Дончиана.
        // Индикатор ATR (Average True Range) - для расчета риска.

        public override void Initialization()
        {
            try
            {
                //Создаем индикаторы для торговой стратегии

                //Период первого канала
                var periodDonchianChannelOne = (int)Parameter(1);
                _upperChannelOne = new Highest(periodDonchianChannelOne);
                _lowerChannelOne = new Lowest(periodDonchianChannelOne);

                //Период второго канала - процент от периода от первого канала
                var periodDonchianChannelTwo = (int)(periodDonchianChannelOne * Parameter(2) * 0.01m);
                _upperChannelTwo = new Highest(periodDonchianChannelTwo);
                _lowerChannelTwo = new Lowest(periodDonchianChannelTwo);

                //Для расчета риска и объема позиции
                _atr          = new ATR((int)Parameter(3));
                _k            = (int)Parameter(4);
                _riskPerTrade = Parameter(5) * 0.01m;

                //Для выставления лимитных заявок в рынок для гарантированного исполнения
                _offset = GetSecurity().Tick * 10;

                //Подписываемся на события при инициализации стратегии
                Subscribe();
            }
            catch (Exception ex)
            {
                ExceptionMessage(ex, "Strategy");
            }
        }
Пример #10
0
        //populate
        public override void Populate()
        {
            BarHistory ds     = Parameters[0].AsBarHistory;
            Int32      period = Parameters[1].AsInt;

            DateTimes = ds.DateTimes;

            if (period <= 0 || ds.Count == 0)
            {
                return;
            }

            //Remember parameters
            var range     = new Highest(ds.High, period) - new Lowest(ds.Low, period);
            var logperiod = Math.Log10(period);

            //Assign first bar that contains indicator data
            var FirstValidValue = range.FirstValidIndex + period;

            if (FirstValidValue > ds.Count)
            {
                FirstValidValue = ds.Count;
            }

            //Initialize start of series with zeroes
            //for (int bar = 0; bar < FirstValidValue; bar++)
            //    Values[bar] = 0;

            for (int bar = FirstValidValue; bar < ds.Count; bar++)
            {
                Values[bar] = Math.Log10(range[bar]) / logperiod;
            }
        }
Пример #11
0
        public void HighestForLoopTest()
        {
            int[] nums   = { 1, 3 };
            var   result = Highest.HighestForLoop(nums);

            Assert.AreEqual(-5, result);
        }
Пример #12
0
        public void HighestForEachLoopTest()
        {
            int[] nums   = { -10, -6, -22, -17, -3 };
            var   result = Highest.HighestForEachLoop(nums);

            Assert.AreEqual(-3, result);
        }
Пример #13
0
        //populate
        public override void Populate()
        {
            BarHistory bars       = Parameters[0].AsBarHistory;
            Int32      period     = Parameters[1].AsInt;
            Int32      emaPeriod1 = Parameters[2].AsInt;
            Int32      emaPeriod2 = Parameters[3].AsInt;

            DateTimes = bars.DateTimes;

            if (period <= 0 || bars.Count == 0)
            {
                return;
            }

            var FirstValidValue = Math.Max(emaPeriod1, Math.Max(emaPeriod2, period));

            if (FirstValidValue > bars.Count || FirstValidValue < 0)
            {
                FirstValidValue = bars.Count;
            }

            var HP     = Highest.Series(bars.High, period);
            var LP     = Lowest.Series(bars.Low, period);
            var MA12   = EMA.Series(bars.Close, emaPeriod1);
            var MA26   = EMA.Series(bars.Close, emaPeriod2);
            var ST12   = (MA12 - LP) / (HP - LP);
            var ST26   = (MA26 - LP) / (HP - LP);
            var STMACD = (ST12 - ST26) * 100;

            for (int bar = 0; bar < bars.Count; bar++)
            {
                Values[bar] = STMACD[bar];
            }
        }
Пример #14
0
        /// <summary>
        /// Обработчик появления новых данных
        /// Вычисляет среднюю за период
        /// Вычисляет отклонение источника от средней за период
        /// </summary>
        ///// <param name="item">Bar</param>
        //public override void OnEvent(long id)
        public void Do(long id)
        {
            ///вычисляем новые занчения
            ///Input
            double iHighest = Indicator.Highest_i(Input.Value.ToList <double>(), Period);

            //(Input.Value.ToList<double>(), Period, Highest.ToList<double>());

            Highest.Add(iHighest);

            ///вызываем обработчики значений
            foreach (var handler in HandlersHighest)
            {
                handler.Invoke(Highest.Last());
            }

            ///упаковка посчитанных значений
            ValueHighest.Add(new ValueDouble()
            {
                Id = id,
                //DateTime = item.DateTime,
                //TODO 4. сейчас отрисовывается по имени MaFast, надо переделать на стороне отрисовки
                Name  = "Highest",
                Value = iHighest
            });

            ///отправка посчитанных значений
            foreach (var handler in HandlersValueHighest)
            {
                handler.Invoke(ValueHighest.Last());
            }
        }
Пример #15
0
        public void WhenTheArrayIsOnlyNegativeHighestForLoop()
        {
            int[] nums   = { -1, -50, -100, -20 };
            var   result = Highest.HighestForLoop(nums);

            Assert.AreEqual(-1, result);
        }
Пример #16
0
        //This static method allows ad-hoc calculation of FractDim (single calc mode)
        public static double Calculate(int bar, TimeSeries ds, int period)
        {
            if (period < 2 || period > bar + 1)
            {
                return(0);
            }

            // Calculate length
            double Range = Highest.Calculate(bar, ds, period) - Lowest.Calculate(bar, ds, period);

            if (Range <= 0)
            {
                return(0);
            }

            double L = 0;

            for (int j = bar - period + 2; j <= bar; j++)
            {
                // Transform Y
                double dY = ds[j] - ds[j - 1];
                // Calculate Length - X is transformed to bars
                L += hypot(dY / Range, 1 / (period - 1));
            }

            // Calculate Fractal Dimension Approximation
            return(1 + Math.Log(L) / Math.Log(2 * (period - 1)));
        }
Пример #17
0
        public void WhenTheArrayIsEmptyHighestForEachLoopReturnsMin()
        {
            int[] nums   = { };
            var   result = Highest.HighestForEachLoop(nums);

            Assert.AreEqual(int.MinValue, result);
        }
Пример #18
0
        public void WhenTheArrayIsAllTheSameHighestForLoopReturnsMax()
        {
            int[] nums   = { 10, 10, 10, 10 };
            var   result = Highest.HighestForLoop(nums);

            Assert.AreEqual(10, result);
        }
Пример #19
0
        public MSR(Bars bars, int per1, int per2, string description)
            : base(bars, description)
        {
            base.FirstValidValue = 252;

            //MSR= (10-day median of (H, L, C) – 20-day  MAX (H, L, C))/(20-day  MAX (H, L, C))
            //then take the 252-day percentrank of MSR or percentile ranking

            DataSeries msrTemp = new DataSeries(bars, "msrTemp(" + per1 + "," + per2 + ")");

            for (int bar = Math.Max(per1, per2); bar < bars.Count; bar++)
            {
                double        max20  = Highest.Series(bars.High, per2)[bar];
                List <double> prices = new List <double>(per1 * 3);
                for (int i = bar; i > bar - per1; i--)
                {
                    prices.Add(bars.High[i]); prices.Add(bars.Low[i]); prices.Add(bars.Close[i]);
                }

                prices.Sort();
                msrTemp[bar] = (GetMedian(prices) - max20) / max20;
            }

            for (int bar = FirstValidValue; bar < bars.Count; bar++)
            {
                base[bar] = PercentRank.Series(msrTemp, 252)[bar];
                //base[bar] = PrcRank.Series(msrTemp, 252)[bar];
            }
        }
Пример #20
0
        //Constructor
        public PivotPointBar(Bars bars, int period, bool pivotPointHigh, bool tradeable, string description)
            : base(bars, description)
        {
            _bars      = bars;
            _ppHigh    = pivotPointHigh;
            _period    = period;
            _tradeable = tradeable;

            FirstValidValue += _period;
            if (bars.Count < _period)
            {
                return;
            }

            for (int bar = _period; bar < bars.Count; bar++)
            {
                this[bar] = -1d;        // returns -1 until a Valid Pivot Point is found
            }

            for (int bar = _period; bar < bars.Count; bar++)
            {
                if (_tradeable)
                {
                    if (_ppHigh)
                    {
                        if (bars.High[bar] >= Highest.Series(bars.High, _period)[bar - 1])
                        {
                            _lastPP = bar;
                        }
                    }
                    else if (bars.Low[bar] <= Lowest.Series(bars.Low, _period)[bar - 1])
                    {
                        _lastPP = bar;
                    }
                }
                else
                {
                    int periodFwd = _period;
                    if (bar + period >= bars.Count)
                    {
                        periodFwd = bars.Count - bar - 1;
                    }
                    if (_ppHigh)
                    {
                        if (bars.High[bar] >= Highest.Series(bars.High, _period)[bar - 1] &&
                            bars.High[bar] >= Highest.Value(bar + periodFwd, bars.High, periodFwd))     // use Value method since periodFwd is variable at the end
                        {
                            _lastPP = bar;
                        }
                    }
                    else if (bars.Low[bar] <= Lowest.Series(bars.Low, _period)[bar - 1] &&
                             bars.Low[bar] <= Lowest.Value(bar + periodFwd, bars.Low, periodFwd))
                    {
                        _lastPP = bar;
                    }
                }
                this[bar] = _lastPP;
            }
        }
Пример #21
0
 public HiLoRange(Bars bars, int period, string description)
     : base(bars, description)
 {
     for (int bar = period; bar < bars.Count; bar++)
     {
         base[bar] = Highest.Series(bars.High, period)[bar] - Lowest.Series(bars.Low, period)[bar];
     }
 }
Пример #22
0
        //populate
        public override void Populate()
        {
            TimeSeries ds     = Parameters[0].AsTimeSeries;
            Int32      period = Parameters[1].AsInt;

            DateTimes = ds.DateTimes;

            if (period <= 0 || ds.Count == 0)
            {
                return;
            }

            //Avoid exception errors
            if (period < 2 || period > ds.Count + 1)
            {
                period = ds.Count + 1;
            }
            var hh   = new Highest(ds, period);
            var ll   = new Lowest(ds, period);
            var ln2p = Math.Log(2 * (period - 1));

            //Assign first bar that contains indicator data
            var FirstValidValue = ds.FirstValidIndex + period - 1;

            if (FirstValidValue > ds.Count)
            {
                FirstValidValue = ds.Count;
            }

            //Initialize start of series with zeroes
            //for (int bar = 0; bar < FirstValidValue; bar++)
            //    Values[bar] := 0;

            //Rest of series
            for (int bar = FirstValidValue; bar < ds.Count; bar++)
            {
                double Range = hh[bar] - ll[bar];
                if (Range <= 0)
                {
                    Values[bar] = 1;
                }
                else
                {
                    // Calculate length
                    double L = 0;
                    for (int j = bar - period + 2; j <= bar; j++)
                    {
                        // Transform Y
                        double dY = ds[j] - ds[j - 1];
                        // Calculate Length - X is transformed to bars
                        L += hypot(dY / Range, 1.0 / (period - 1));
                    }
                    // Calculate Fractal Dimension Approximation
                    Values[bar] = 1 + Math.Log(L) / ln2p;
                }
            }
        }
        public BreakoutAndReturnRuntimeMetric(int windowSize, int priceSelector, int maxInterval, int minInterval)
        {
            _highest       = new Highest(windowSize);
            _priceSelector = priceSelector;
            _maxInterval   = maxInterval;
            _minInterval   = minInterval;

            ResetState();
        }
Пример #24
0
        public RebreakoutRuntimeMetric(int windowSize, int priceSelector, int maxInterval, int minInterval)
        {
            _highest       = new Highest(windowSize);
            _priceSelector = priceSelector;
            _maxInterval   = maxInterval;
            _minInterval   = minInterval;

            CurrentHighest = 0.0;
            Breakout       = false;
            Rebreakout     = false;
        }
Пример #25
0
 public TR2DSeries(Bars bars, string description)
     : base(bars, description)
 {
     this.FirstValidValue = 2;
     for (int bar = FirstValidValue; bar < bars.Count; bar++)
     {
         double h3 = Math.Max(Highest.Series(bars.High, 2)[bar], bars.Close[bar - 2]);
         double l3 = Math.Min(Lowest.Series(bars.Low, 2)[bar], bars.Close[bar - 2]);
         base[bar] = h3 - l3;
     }
 }
Пример #26
0
        public Density(Bars bars, int NBars, string description)
            : base(bars, description)
        {
            base.FirstValidValue = NBars;

            DataSeries Area = (Highest.Series(bars.High, NBars) - Lowest.Series(bars.Low, NBars));

            for (int bar = FirstValidValue; bar < bars.Count; bar++)
            {
                base[bar] = Sum.Series(TrueRange.Series(bars), NBars)[bar] / Area[bar];
            }
        }
Пример #27
0
 //===========================================================================================================
 // Show 52 weeks high
 //-----------------------------------------------------------------------------------------------------------
 protected void show52WeeksHigh(int numBarsIn52Weeks)
 {
     // 52-week high
     for (int bar = numBarsIn52Weeks; bar < Bars.Count; bar++)
     {
         if (High[bar] > Highest.Series(High, numBarsIn52Weeks)[bar - 1])
         {
             // New 52-week high detected.  Paint the chart blue
             SetBackgroundColor(bar, Color.FromArgb(15, Color.Blue));
         }
     }
 }
Пример #28
0
        //populate
        public override void Populate()
        {
            BarHistory source   = Parameters[0].AsBarHistory;
            Int32      period   = Parameters[1].AsInt;
            Double     level    = Parameters[2].AsDouble;
            Double     minrange = Parameters[3].AsDouble;

            DateTimes = source.DateTimes;

            if (period > DateTimes.Count)
            {
                period = DateTimes.Count;
            }
            if (DateTimes.Count < period || period <= 0)
            {
                return;
            }

            var HiLoRange = Highest.Series(source.High, period) - Lowest.Series(source.Low, period);

            for (int n = 0; n < period; n++)
            {
                Values[n] = 0;
            }

            for (int bar = period; bar < source.Count; bar++)
            {
                double result = 0.0;

                double ls = Lowest.Series(source.Low, period)[bar];

                if (minrange == 0.0)
                {
                    result = ls + (HiLoRange[bar] * (level / 100));
                }
                else
                {
                    double l      = ls;
                    double range  = HiLoRange[bar];
                    double mid    = l + range / 2;
                    double mrange = l * minrange / 100.0;

                    if (range < mrange)
                    {
                        range = mrange;
                    }

                    result = mid + (level / 100.0 - 0.5) * range;
                }

                Values[bar] = result;
            }
        }
Пример #29
0
        public override void Populate()
        {
            BarHistory bars    = Parameters[0].AsBarHistory;
            Int32      periodK = Parameters[1].AsInt;
            Int32      smoothK = Parameters[2].AsInt;

            DateTimes = bars.DateTimes;
            var period = Math.Max(periodK, smoothK);

            if (period <= 0 || DateTimes.Count == 0)
            {
                return;
            }

            FastSMA    sma     = new FastSMA(bars.Close, 2);
            TimeSeries sma1    = sma * 5;
            TimeSeries sma2    = new FastSMA(sma, 2) * 4;
            TimeSeries sma3    = new FastSMA(new FastSMA(sma, 2), 2) * 3;
            TimeSeries sma4    = new FastSMA(new FastSMA(new FastSMA(sma, 2), 2), 2) * 2;
            TimeSeries sma5    = new FastSMA(new FastSMA(new FastSMA(new FastSMA(sma, 2), 2), 2), 2);
            TimeSeries sma6    = new FastSMA(sma5, 2);
            TimeSeries sma7    = new FastSMA(sma6, 2);
            TimeSeries sma8    = new FastSMA(sma7, 2);
            TimeSeries sma9    = new FastSMA(sma8, 2);
            TimeSeries sma10   = new FastSMA(sma9, 2);
            TimeSeries Rainbow = (sma1 + sma2 + sma3 + sma4 + sma5 + sma6 + sma7 + sma8 + sma9 + sma10) / 20;

            TimeSeries RBC = (Rainbow + bars.AveragePriceHLC) / 2;
            TimeSeries nom = RBC - new Lowest(bars.Low, periodK);
            TimeSeries den = new Highest(bars.High, periodK) - new Lowest(RBC, periodK);

            var fastK = new TimeSeries(DateTimes);

            for (int bar = 0; bar < bars.Count; bar++)
            {
                if (bar >= periodK)
                {
                    fastK[bar] = (Math.Min(100, Math.Max(0, 100 * nom[bar] / den[bar])));
                }
                else
                {
                    fastK[bar] = 0d;
                }
            }

            var K = new FastSMA(fastK, smoothK);

            for (int bar = 0; bar < bars.Count; bar++)
            {
                Values[bar] = K[bar];
            }
        }
Пример #30
0
        public PercentRange(DataSeries ds, int period, string description)
            : base(ds, description)
        {
            base.FirstValidValue = period;

            DataSeries r = ds * 0;

            r = ds / (Highest.Series(ds, period) - Lowest.Series(ds, period));

            for (int bar = FirstValidValue; bar < ds.Count; bar++)
            {
                base[bar] = r[bar];
            }
        }