// Описание: // За основу взята стратегия Черепах - 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"); } }
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"); } }
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]); } }
//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; } }
/// <summary> /// Обработчик появления новых данных /// Вычисляет среднюю за период /// Вычисляет отклонение источника от средней за период /// </summary> ///// <param name="item">Bar</param> //public override void OnEvent(long id) public void Do(long id) { ///вычисляем новые занчения ///Input double iLowest = Indicator.Lowest_i(Input.Value.ToList <double>(), Period); //(Input.Value.ToList<double>(), Period, Highest.ToList<double>()); Lowest.Add(iLowest); ///вызываем обработчики значений foreach (var handler in HandlersLowest) { handler.Invoke(Lowest.Last()); } ///упаковка посчитанных значений ValueLowest.Add(new ValueDouble() { Id = id, //DateTime = item.DateTime, //TODO 4. сейчас отрисовывается по имени MaFast, надо переделать на стороне отрисовки Name = "Lowest", Value = iLowest }); ///отправка посчитанных значений foreach (var handler in HandlersValueLowest) { handler.Invoke(ValueLowest.Last()); } }
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]; } }
public HiLoLimit(Bars bars, int period, double level, double minrange, string description) : base(bars, description) { for (int bar = period; bar < bars.Count; bar++) { double result = 0.0; double ls = Lowest.Series(bars.Low, period)[bar]; double ds = HiLoRange.Series(bars, period)[bar]; if (minrange == 0.0) { result = ls + (ds * (level / 100)); } else { double l = ls; double range = ds; double mid = l + range / 2; double mrange = l * minrange / 100.0; if (range < mrange) { range = mrange; } result = mid + (level / 100.0 - 0.5) * range; } base[bar] = result; } }
public SeriesIsBelow(DataSeries ds1, DataSeries ds2, int period, string description) : base(ds1, description) { int lp = Math.Min(ds1.Count, ds2.Count); DataSeries delta = ds2 - ds1; if (period > lp) { return; } int i = 0; for (int bar = period; bar < lp; bar++) { if (Lowest.Series(delta, period)[bar] > 0) { i++; } else { i = 0; } base[bar] = i; } }
//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))); }
// 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); }
public BounceRuntimeMetric(int windowSize, double minBouncePercentage) { _lowest = new Lowest(windowSize); _minBouncePercentage = minBouncePercentage; ResetState(); }
//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]; } }
//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; } }
public void FindSmallestInt() { //Arrange Lowest lowest = new Lowest(); //Assert Assert.AreEqual(11, lowest.FindLowestInt(new int[] { 34, 190, 3890, 11, 23, 76 })); }
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]; } }
//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 void Indicators_Lowest_Low_For_Period_Of_Ten() { IEnumerable <double> lowest = Lowest.LowCollection(MakeBars(), 10); Assert.AreEqual(10, lowest.Count()); foreach (double item in lowest) { Assert.AreEqual(137900, item); } }
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; } }
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]; } }
//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; } }
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]; } }
public Lowest2(DataSeries ds1, DataSeries ds2, int period) : base(ds2, "Lowest Of Two") { DataSeries tmp = ds1 - ds1; for (int bar = tmp.FirstValidValue; bar < ds2.Count; bar++) { tmp[bar] = Math.Min(ds1[bar], ds2[bar]); } for (int bar = tmp.FirstValidValue; bar < ds2.Count; bar++) { this[bar] = Lowest.Series(tmp, period)[bar]; } }
/// <summary> /// Returns true if PriceBase instances are equal /// </summary> /// <param name="other">Instance of PriceBase to be compared</param> /// <returns>Boolean</returns> public bool Equals(PriceBase other) { if (other is null) { return(false); } if (ReferenceEquals(this, other)) { return(true); } return (( Id == other.Id || Id != null && Id.Equals(other.Id) ) && ( Opening == other.Opening || Opening != null && Opening.Equals(other.Opening) ) && ( Closing == other.Closing || Closing != null && Closing.Equals(other.Closing) ) && ( Highest == other.Highest || Highest != null && Highest.Equals(other.Highest) ) && ( Lowest == other.Lowest || Lowest != null && Lowest.Equals(other.Lowest) ) && ( Volume == other.Volume || Volume != null && Volume.Equals(other.Volume) ) && ( Turnover == other.Turnover || Turnover != null && Turnover.Equals(other.Turnover) )); }
//(BarHistory bars, BarHistory barsFirst, BarHistory barsSecond, int periodRegression, int periodRegressionMomentum, public override void Populate() { BarHistory ds = Parameters[0].AsBarHistory; Int32 period = Parameters[1].AsInt; DateTimes = ds.DateTimes; if (period <= 0 || DateTimes.Count == 0) { return; } var HH = new Highest(ds.High, period); var LL = new Lowest(ds.Low, period); //Avoid exception errors if (period < 1 || period > ds.Count + 1) { period = ds.Count + 1; } //Assign first bar that contains indicator data var FirstValidValue = 2 * 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 BuyPwr = HH[bar] - LL[bar - period]; double SellPwr = HH[bar - period] - LL[bar]; if (BuyPwr + SellPwr != 0) { Values[bar] = 200 * (BuyPwr - SellPwr) / (BuyPwr + SellPwr); } //else // Values[bar] = 0; } }
public void Indicators_Lowest_Low_For_Period_Of_Five() { IEnumerable <double> lowest = Lowest.LowCollection(MakeBars(), 5); Assert.AreEqual(10, lowest.Count()); Assert.AreEqual(139455, lowest.ElementAt(9)); Assert.AreEqual(139455, lowest.ElementAt(8)); Assert.AreEqual(139455, lowest.ElementAt(7)); Assert.AreEqual(139455, lowest.ElementAt(6)); Assert.AreEqual(139455, lowest.ElementAt(5)); Assert.AreEqual(137900, lowest.ElementAt(4)); Assert.AreEqual(137900, lowest.ElementAt(3)); Assert.AreEqual(137900, lowest.ElementAt(2)); Assert.AreEqual(137900, lowest.ElementAt(1)); Assert.AreEqual(137900, lowest.ElementAt(0)); }
public override void CalculatePartialValue() { if (_bars.Count < _period || _bars.Low.PartialValue == Double.NaN || _bars.High.PartialValue == Double.NaN) { PartialValue = Double.NaN; return; } int bar = _bars.Count; // bar - 1 is last bar number (prior to PartialBar) if (_tradeable) { if (_ppHigh) { if (_bars.High.PartialValue >= Highest.Series(_bars.High, _period)[bar - 1]) { _lastPP = bar; } } else if (_bars.Low.PartialValue <= 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.PartialValue >= Highest.Series(_bars.High, _period)[bar - 1] && _bars.High.PartialValue >= Highest.Value(bar + periodFwd, _bars.High, periodFwd)) // use Value method since periodFwd is variable at the end { _lastPP = bar; } } else if (_bars.Low.PartialValue <= Lowest.Series(_bars.Low, _period)[bar - 1] && _bars.Low.PartialValue <= Lowest.Value(bar + periodFwd, _bars.Low, periodFwd)) { _lastPP = bar; } } this.PartialValue = _lastPP; }
//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 < 1 || period > ds.Count + 1) { period = ds.Count + 1; } var Lo = new Lowest(ds, period); var Hi = new Highest(ds, period); double Value1 = 0, Value = 0; //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; // for (int bar = FirstValidValue; bar < ds.Count; bar++) { Value1 = 0.67 * Value1; if (Lo[bar] < Hi[bar]) { Value1 += 0.33 * (2 * (ds[bar] - Lo[bar]) / (Hi[bar] - Lo[bar]) - 1); } Value = 0.5 * Value; Value += 0.5 * Math.Log((1 + Value1) / (1 - Value1)); Values[bar] = Value; } }
//This static method allows ad-hoc calculation of RSS (single calc mode) public static double Calculate(int bar, BarHistory ds, int period) { //Avoid exception errors if (period < 2) { return(Double.NaN); } double range = Highest.Calculate(bar, ds.High, period) - Lowest.Calculate(bar, ds.Low, period); if (range <= 0) { return(Double.NaN); } return(Math.Log10(range) / Math.Log10(period)); }
public TenkanSen(Bars bars) : base(bars, "TenkanSen") { int p1 = 9; base.FirstValidValue = p1; if (bars.Count < p1) { return; } for (int bar = FirstValidValue; bar < bars.Count; bar++) { base[bar] = ((Highest.Series(bars.High, p1)[bar] + Lowest.Series(bars.Low, p1)[bar]) / 2); } }
//This static method allows ad-hoc calculation of TTF (single calc mode) public static double Calculate(int bar, BarHistory ds, int period) { if (bar < 2 * period - 1) { return(0); } double BuyPwr = Highest.Calculate(bar, ds.High, period) - Lowest.Calculate(bar - period, ds.Low, period); double SellPwr = Highest.Calculate(bar - period, ds.High, period) - Lowest.Calculate(bar, ds.Low, period); if (BuyPwr + SellPwr == 0) { return(0); } return(200 * (BuyPwr - SellPwr) / (BuyPwr + SellPwr)); }