/// <summary> /// Содержит главную логику робота по анализу свечей и принятия решений о заключении сделок. Этот метод открытый, потому что он используется при /// тестировании робота. /// </summary> public void CandlesAnalysisAndTrade(CandleData candle, bool isHistoryStartOff) { if (virtualDeal != null) { var virtDealResult = DalSpot.Instance.GetPointsValue(ticker, virtualDeal.Side * (candle.close - virtualDeal.PriceEnter)); virtualResults.DequeueLast(); virtualResults.Add(virtDealResult); } var currentSign = Math.Sign(queueFast.Average() - queueSlow.Average()); // Вычисляем текущее расположение скользящих средних try { if (prevSign == 0 || prevSign == currentSign) { return; } // СС пересеклись // Считаем средней результат виртуальных сделок var avgVirtualResult = virtualResults.Length == 0 ? 0 : virtualResults.Average(); // Если следний редультат виртуальных сделок не входит в диапазон "не торговать", то высчитываем знак текущей сделки maDifSign = Math.Abs(avgVirtualResult) < unknownUnitProfit ? 0 : Math.Sign(avgVirtualResult); // "На будущее" запоминаем текущую стуацию, для расчёта виртуальных сделок на следующих итерациях virtualDeal = new MarketOrder { PriceEnter = candle.close, Side = currentSign }; virtualResults.Add(0); // тут будет результат новой сделки на следующей итерации // пересечение скользящих средних произошло не на истории if (!isHistoryStartOff) { List <MarketOrder> orders; robotContext.GetMarketOrders(robotContext.AccountInfo.ID, out orders); var tradeSide = deriveSiteFromHistory ? currentSign * maDifSign : currentSign; Trade(candle, orders, tradeSide); } } finally { prevSign = currentSign; if (debugAction != null) { debugAction(new RobotMAInnerState { maDifSign = maDifSign, maValueFast = queueFast.Average(), maValueSlow = queueSlow.Average(), lastCandle = candle, }, candle.timeClose); } } }
public void BuildSeries(ChartControl chart) { tooltipSeries.data.Clear(); averageRangeList.Clear(); var candles = chart.StockSeries.Data.Candles; var minimumCandles = PointsRange > 0 ? 5 : MaPeriod + 5; if (candles == null || candles.Count < minimumCandles) { return; } var lastRanges = new RestrictedQueue <float>(MaPeriod); float targetSize = PointsRange > 0 ? DalSpot.Instance.GetAbsValue(chart.Symbol, (float)PointsRange) : 0; var series = 0; for (var i = 0; i < candles.Count; i++) { var candle = candles[i]; var range = candle.high - candle.low; lastRanges.Add(range); if (lastRanges.Length < lastRanges.MaxQueueLength && PointsRange == 0) { averageRangeList.Add(0); continue; } var avgRange = lastRanges.Average(); averageRangeList.Add(avgRange); var candlePercent = range * 100 / avgRange; var isNarrow = PointsRange > 0 ? range < targetSize : candlePercent <= NarrowPercent; if (!isNarrow && series > 0) { if (series >= CandlesToSignal) { // отметить, сколько процентов данная свеча составила от обычной волатильности AddMark($"{candlePercent:F0}%", $"{candlePercent:F0}%", i, candle.open, false); } series = 0; continue; } if (!isNarrow) { continue; } series++; if (series >= CandlesToSignal) { AddMark($"{series}", $"{series}", i, candle.close, true); } } }
private void BuildIndi(Series.Series source) { if (source is IPriceQuerySeries == false) { return; } var curIndex = 0; var prevAccel = 0f; for (var j = 0; j < source.DataCount; j++) { var medianPrice = source is CandlestickSeries ? (((CandlestickSeries)source).Data.Candles[j].high + ((CandlestickSeries)source).Data.Candles[j].low) * 0.5f : (((IPriceQuerySeries)source).GetPrice(j) ?? 0); queueFast.Add(medianPrice); queueSlow.Add(medianPrice); // Если нечего рисовать то пропускаем var accelerator = 0f; /* А * добил график пустыми столбиками в начале, чтобы крайний столбик был там же, где крайняя свечка */ if (queueSlow.Length >= periodSlow) { var maSlow = queueSlow.Average(); var maFast = queueFast.Average(); //AO = SMA (MEDIAN PRICE, 5) — SMA (MEDIAN PRICE, 34) var awesome = maFast - maSlow; queueAwesomes.Add(awesome); // Собственно значение ускорения/замедления // AC = AO — SMA (AO, 5) if (queueAwesomes.Length == periodAwesome) { accelerator = awesome - queueAwesomes.Average(); } } //? Класс HistogramBar не описал но вроде он не хитрый и понятно по примеру как его юзать. Для документации достаточно описаь поля и предназначение имхо /* А * я чуть иначе раскрасил акселератор, канонически */ seriesDeltas.data.Add(new HistogramBar { color = accelerator >= prevAccel ? ClBarsPositive : ClBarsNegative, index = curIndex++, y = accelerator }); prevAccel = accelerator; } }
private float CalculateBrickSize() { if (lastPrices.Length < AutosizePeriod) { return(pointCost * brickSize); } if (VolatilityType == RencoVolatilityType.азмах) { return((lastPrices.Max(c => c.high) - lastPrices.Min(c => c.low)) * AutosizeScale); } //if (VolatilityType == RencoVolatilityType.ATR) return(lastPrices.Average(p => p.high - p.low) * AutosizeScale); }
private static double [] ReduceTrendMA(double [] data, int maPeriod) { var maData = new RestrictedQueue <double>(maPeriod); var outData = new List <double>(); for (var i = 0; i < data.Length; i++) { maData.Add(data[i]); if (maData.Length < maPeriod) { continue; } var ma = maData.Average(); outData.Add(data[i] / ma); } return(outData.ToArray()); }
// ReSharper disable InconsistentNaming private void BuildMA(Series.Series source) // ReSharper restore InconsistentNaming { series.Data.Clear(); float sum = 0; var frameLen = 0; for (var i = 0; i < source.DataCount; i++) { var price = GetPrice(source, i); // простая СС if (maType == MovAvgType.Простая) { sum += price; if (frameLen < period) { frameLen++; } else { sum -= GetPrice(source, i - period); } series.Data.Add(sum / frameLen); continue; } // сглаженная СС if (smmaPrev.HasValue) { var smma = ((period - 1) * smmaPrev.Value + price) / period; series.Data.Add(smma); smmaPrev = smma; } else { queue.Add(price); if (queue.Length == period) { smmaPrev = queue.Average(); } series.Data.Add(smmaPrev ?? price); } } }
private void BuildIndi(Series source) { if (source is CandlestickSeries == false) { return; } var candles = ((CandlestickSeries)source).Data.Candles; var currentSign = 0; // текущая операция (покупка/продажа) var curIndex = 0; foreach (var candle in candles) { var price = candle.GetPrice(priceType); // обновить средние queueFast.Add(price); queueSlow.Add(price); // посчитать профит var delta = 0f; if (currentSign != 0) { delta = (candle.close - candle.open) * currentSign; } seriesDeltas.data.Add(new HistogramBar { color = delta >= 0 ? ClBarsPositive : ClBarsNegative, index = curIndex++, y = delta }); // определить знак if (queueSlow.Length < periodSlow) { continue; } var maSlow = queueSlow.Average(); var maFast = queueFast.Average(); var newSign = maFast > maSlow ? 1 : maFast < maSlow ? -1 : 0; if (newSign != 0) { currentSign = newSign; } } }
public void BuildSeries(ChartControl chart) { tooltipSeries.data.Clear(); averageRangeList.Clear(); seriesUp.Data.Clear(); seriesDn.Data.Clear(); var candles = chart.StockSeries.Data.Candles; var minimumCandles = PointsRange > 0 ? 5 : MaPeriod + 5; if (candles == null || candles.Count < minimumCandles) { return; } var lastRanges = new RestrictedQueue <float>(MaPeriod); var lastHL = new RestrictedQueue <float>(MaPeriod); float targetSize = PointsRange > 0 ? DalSpot.Instance.GetAbsValue(chart.Symbol, (float)PointsRange) : 0; var curFrame = candles.Take(FrameLength).ToList(); lastRanges.Add(curFrame.Max(c => c.high) - curFrame.Min(c => c.low)); seriesUp.Data.Add(candles[0].high); seriesDn.Data.Add(candles[0].low); for (var i = 0; i < FrameLength; i++) { averageRangeList.Add(0); seriesUp.Data.Add(candles[i].high); seriesDn.Data.Add(candles[i].low); } var breakCandleLengthPercents = new List <float>(); var prevFrameIsNarrow = false; for (var i = FrameLength; i < candles.Count; i++) { var candle = candles[i]; curFrame.RemoveAt(0); curFrame.Add(candle); var high = curFrame[0].high; var low = curFrame[0].low; foreach (var c in curFrame.Skip(1)) { if (c.high > high) { high = c.high; } if (c.low < low) { low = c.low; } } var range = high - low; lastRanges.Add(range); lastHL.Add(candle.high - candle.low); if (lastRanges.Length < lastRanges.MaxQueueLength && PointsRange == 0) { averageRangeList.Add(0); seriesUp.Data.Add(candles[i].high); seriesDn.Data.Add(candles[i].low); continue; } var avgRange = lastRanges.Average(); averageRangeList.Add(avgRange); seriesUp.Data.Add(candles[i].close + range); seriesDn.Data.Add(candles[i].close - range); var avgHl = lastHL.Average(); var rangePercent = avgRange == 0 ? 0 : range * 100 / avgRange; var isNarrow = PointsRange > 0 ? range < targetSize : rangePercent <= NarrowPercent; if (prevFrameIsNarrow) // && !isNarrow) { // отметить, сколько процентов данная свеча составила от обычной волатильности var candlePercent = avgHl == 0 ? 0 : (candle.high - candle.low) * 100 / avgHl; if (candlePercent > 0) { breakCandleLengthPercents.Add(candlePercent); } AddMark($"{candlePercent:F0}%", $"{candlePercent:F0}%", i, candle.open, false); } prevFrameIsNarrow = isNarrow; } // вывести в лог среднее if (breakCandleLengthPercents.Count > 0) { avgBreakCandle = breakCandleLengthPercents.Average(); minBreakCandle = breakCandleLengthPercents.Min(); maxBreakCandle = breakCandleLengthPercents.Max(); breaksCount = breakCandleLengthPercents.Count; Logger.Info($"{breakCandleLengthPercents.Count}, от {minBreakCandle:F1} до {maxBreakCandle:F1}, среднее: {avgBreakCandle:F1}"); } }
private static double[] ReduceTrendMA(double []data, int maPeriod) { var maData = new RestrictedQueue<double>(maPeriod); var outData = new List<double>(); for (var i = 0; i < data.Length; i++) { maData.Add(data[i]); if (maData.Length < maPeriod) continue; var ma = maData.Average(); outData.Add(data[i] / ma); } return outData.ToArray(); }
public void BuildSeries(ChartControl chart) { seriesK.Data.Clear(); seriesD.Data.Clear(); seriesBounds.parts.Clear(); if (SeriesSources.Count == 0) { return; } var source = SeriesSources[0]; if (source.DataCount == 0) { return; } if (source is CandlestickSeries == false && source is LineSeries == false) { return; } // границы if (lowerBound > 0) { seriesBounds.parts.Add(new List <PartSeriesPoint> { new PartSeriesPoint(1, lowerBound), new PartSeriesPoint(source.DataCount, lowerBound) }); } if (upperBound > 0 && upperBound < 100) { seriesBounds.parts.Add(new List <PartSeriesPoint> { new PartSeriesPoint(1, upperBound), new PartSeriesPoint(source.DataCount, upperBound) }); } queue = new RestrictedQueue <float>(period); queueMA = new RestrictedQueue <float>(periodMA); for (var i = 0; i < source.DataCount; i++) { var price = source is CandlestickSeries ? ((CandlestickSeries)source).Data.Candles[i].close : ((LineSeries)source).GetPrice(i) ?? 0; queue.Add(price); if (queue.Length < period) { seriesK.Data.Add(50); seriesD.Data.Add(50); continue; } var minValue = float.MaxValue; var maxValue = float.MinValue; foreach (var p in queue) { if (p < minValue) { minValue = p; } if (p > maxValue) { maxValue = p; } } var range = maxValue - minValue; var k = range == 0 ? 50 : 100 * (price - minValue) / range; queueMA.Add(k); var d = queueMA.Length == periodMA?queueMA.Average() : k; seriesK.Data.Add(k); seriesD.Data.Add(d); } }
private void BuildBollinger(Series.Series source) { series.Data.Clear(); seriesUp.Data.Clear(); seriesDn.Data.Clear(); if (source is CandlestickSeries == false && source is LineSeries == false) { return; } float sum = 0; var frameLen = 0; for (var i = 0; i < source.DataCount; i++) { var price = GetPrice(source, i); // простая СС if (MaType == MovAvgType.Простая) { sum += price; if (frameLen < period) { frameLen++; } else { sum -= GetPrice(source, i - period); } series.Data.Add(sum / frameLen); } // сглаженная СС else { if (smmaPrev.HasValue) { var smma = ((period - 1) * smmaPrev.Value + price) / period; series.Data.Add(smma); smmaPrev = smma; } else { queue.Add(price); if (queue.Length == period) { smmaPrev = queue.Average(); } series.Data.Add(smmaPrev ?? price); } } // волатильность (СКО) // очередь из квадратов отклонений var lastMedian = series.Data[series.Data.Count - 1]; var dev = lastMedian - price; queueDisper.Add((float)(dev * dev)); var ssd = queueDisper.Length == period ? Math.Sqrt(queueDisper.Average()) : 0; seriesUp.Data.Add(lastMedian + KVolatile * ssd); seriesDn.Data.Add(lastMedian - KVolatile * ssd); } }
private void PlaceMarkers() { var candles = chart.chart.StockSeries.Data.Candles; if (candles.Count <= FrameLength + MaPeriod) { return; } var lastRanges = new RestrictedQueue <float>(MaPeriod); var curFrame = candles.Take(FrameLength).ToList(); lastRanges.Add(curFrame.Max(c => c.high)); var prevFrameIsNarrow = false; for (var i = FrameLength; i < candles.Count; i++) { var candle = candles[i]; curFrame.RemoveAt(0); curFrame.Add(candle); var high = curFrame[0].high; var low = curFrame[0].low; foreach (var c in curFrame.Skip(1)) { if (c.high > high) { high = c.high; } if (c.low < low) { low = c.low; } } var range = high - low; lastRanges.Add(range); if (lastRanges.Length < lastRanges.MaxQueueLength) { continue; } var avgRange = lastRanges.Average(); var rangePercent = avgRange == 0 ? 0 : range * 100 / avgRange; var isNarrow = rangePercent <= NarrowPercent; if (prevFrameIsNarrow) // && !isNarrow) { if (i <= MomentumPeriod) { continue; } var prevClose = candles[i - MomentumPeriod - 1].close; if (prevClose <= 0) { continue; } var momentum = candles[i - 1].close * 100 / prevClose; var side = momentum > 100 ? -1 : 1; AddMark(side, i, candles, $"M: {momentum:F1}"); } prevFrameIsNarrow = isNarrow; } }
public void BuildSeries(ChartControl chart) { seriesK.Data.Clear(); seriesD.Data.Clear(); seriesBounds.parts.Clear(); if (SeriesSources.Count == 0) return; var source = SeriesSources[0]; if (source.DataCount == 0) return; if (source is CandlestickSeries == false && source is LineSeries == false) return; // границы if (lowerBound > 0) seriesBounds.parts.Add(new List<PartSeriesPoint> { new PartSeriesPoint(1, lowerBound), new PartSeriesPoint(source.DataCount, lowerBound) }); if (upperBound > 0 && upperBound < 100) seriesBounds.parts.Add(new List<PartSeriesPoint> { new PartSeriesPoint(1, upperBound), new PartSeriesPoint(source.DataCount, upperBound) }); queue = new RestrictedQueue<float>(period); queueMA = new RestrictedQueue<float>(periodMA); for (var i = 0; i < source.DataCount; i++) { var price = source is CandlestickSeries ? ((CandlestickSeries) source).Data.Candles[i].close : ((LineSeries) source).GetPrice(i) ?? 0; queue.Add(price); if (queue.Length < period) { seriesK.Data.Add(50); seriesD.Data.Add(50); continue; } var minValue = float.MaxValue; var maxValue = float.MinValue; foreach (var p in queue) { if (p < minValue) minValue = p; if (p > maxValue) maxValue = p; } var range = maxValue - minValue; var k = range == 0 ? 50 : 100*(price - minValue)/range; queueMA.Add(k); var d = queueMA.Length == periodMA ? queueMA.Average() : k; seriesK.Data.Add(k); seriesD.Data.Add(d); } }