/// <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); } } }
/// <summary> /// Прорисовка кнопки на элементе "Graphics" контрола "QuoteTradeControl" /// </summary> /// <param name="graphics">Объект, на котором будт осущесвлятся прорисовка</param> public void Draw(Graphics graphics) { scaleMatrix = new Matrix(); scaleMatrix.Scale(ScaleX, ScaleY); Shape = QuotePartButtonFigure.instanceFigure.Clone() as GraphicsPath; Shape.Transform(translateMatrix); Shape.Transform(scaleMatrix); graphics.FillPath(currentBrush, Shape); graphics.DrawPath(pensStor.GetPen(Color.FromArgb(255, 64, 66, 64)), Shape); if (buySellIndicator != null) { if (Value != null) { // Запоминаем значение для вычсления направления стрелки тренда if (buySellIndicator.Volume == Value) { trandDeltaValues.Add(0); } else { trandDeltaValues.Add(buySellIndicator.Volume > Value ? -1 : 1); } buySellIndicator.Volume = Value; } buySellIndicator.ScaleX = ScaleX; buySellIndicator.ScaleY = ScaleY; buySellIndicator.Draw(graphics, brushesStor); } if (arrowTrend != null) { arrowTrend.Sx = ScaleX; arrowTrend.Sy = ScaleY; arrowTrend.DirectionAngle = trandDeltaValues.GetItemByIndex(0, true) * 1 + trandDeltaValues.GetItemByIndex(1, true) * 2 + trandDeltaValues.GetItemByIndex(2, true) * 4; arrowTrend.Draw(graphics); } }
/// <summary> /// Этот метод вызывается каждый раз при новых данных котировки. Метод реализует основную логику торговли робота. /// Здесь принимается решение о открытии нового ордера и закрытии открытых сделок /// Совершение сделок будет поисходить на закрытии свечи. При закрытии свечки метод UpdateCandle объекта packer вернёт эту свечу, иначе Null. /// В случае если свеча закрылась, тогда методом CalculateMA расчитываем значение скользящей средней для текущей котировки. /// Если быстрая и медленная скользящие седние пересекаются, а так же если текущая котировка не является "исторической", тогда можно /// принимать решение об открытии новой сделки. /// </summary> /// <param name="quotes"></param> /// <param name="isHistoryStartOff">Флаг, показывающий, является текущая котировка взятой из истории, или это значение катировки на текущий момент на рынке</param> /// <param name="names"></param> public override List <string> OnQuotesReceived(string[] names, CandleDataBidAsk[] quotes, bool isHistoryStartOff) { events = lastMessages.ToList(); lastMessages.Clear(); #region Получение текущей свечи // Массивы "names" и "quotes" всегда содержат одинаковое количество элементов (по одному). Фактически это пары ключ/значение if (string.IsNullOrEmpty(ticker)) { var accountId = 0; if (robotContext != null && robotContext.AccountInfo != null) { accountId = robotContext.AccountInfo.ID; } Logger.ErrorFormat("Название текущего инструмента ({0}) для робота \"{1}\" (#{2}) задано не корректно", ticker ?? "", TypeName, accountId); return(events); } var tickerIndex = -1; for (var i = 0; i < names.Length; i++) { if (names[i] == ticker) { tickerIndex = i; break; } } if (tickerIndex < 0) { //Logger.InfoFormat("Не удалось получить котировку для робота {0}", TypeName); return(events); } //Выбираем из всего массива текущих катировкок, катировку для того инструмента, которым торгует робот var quote = quotes[tickerIndex]; var candle = packer.UpdateCandle(quote); #endregion // Если свеча закрылась, тогда candle != null (сделки соверши) if (candle == null) { return(events); } queueSlow.Add(candle.close); queueFast.Add(candle.close); if (queueSlow.Length < queueSlow.MaxQueueLength) { return(events); } CandlesAnalysisAndTrade(candle, isHistoryStartOff); return(events); }
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; } }
public override List <string> OnQuotesReceived(string[] names, CandleDataBidAsk[] quotes, bool isHistoryStartOff) { var events = new List <string>(); var candle = UpdateCurrentCandle(names, quotes); if (candle == null) { return(events); } // проверить Каймана CandleData cayCandle; if (!caymanCandles.TryGetValue(candle.timeOpen, out cayCandle)) { return(events); } // знак Каймана var sign = cayCandle.close >= 50 ? -1 : 1; caymanLastSigns.Add(sign); if (caymanLastSigns.Length < caymanLastSigns.MaxQueueLength) { return(events); } // последняя сделка List <MarketOrder> orders; GetMarketOrders(out orders); var lastOrder = orders.LastOrDefault(); // закрыть ордер if (lastOrder != null && sign != lastOrder.Side) { CloseMarketOrder(lastOrder.ID); lastOrder = null; } // войти в рынок? if (lastOrder != null) { return(events); } if (caymanLastSigns.Any(s => s != sign)) { return(events); } OpenDeal(candle.close, sign); return(events); }
private int GetRencoSignal(CandleData candle) { var brickSizeAbs = brickSize * pointCost; // автомасштаб... if (BrickSizeAuto) { lastPrices.Add(candle); brickSizeAbs = CalculateBrickSize(); } // проверить - добавлен ли новый кирпич? сменился ли знак кирпича? if (lastBrick == null) { var brickDir = candle.close < candle.open ? -1 : 1; lastBrick = new RencoRobotBrick(candle.open, candle.close, brickDir); return(0); } var deltaClose = (int)((candle.close - lastBrick.priceClose) / brickSizeAbs); var deltaOpen = (int)((candle.close - lastBrick.priceOpen) / brickSizeAbs); if (deltaClose > 0 && lastBrick.direction > 0) { lastBrick = new RencoRobotBrick( lastBrick.priceClose + (deltaClose - 1) * brickSizeAbs, lastBrick.priceClose + deltaClose * brickSizeAbs, 1); return(0); } if (deltaOpen < 0 && lastBrick.direction > 0) { lastBrick = new RencoRobotBrick( lastBrick.priceOpen + (deltaOpen + 1) * brickSizeAbs, lastBrick.priceOpen + deltaOpen * brickSizeAbs, -1); return(-1); } if (deltaClose < 0 && lastBrick.direction < 0) { lastBrick = new RencoRobotBrick( lastBrick.priceClose + (deltaClose + 1) * brickSizeAbs, lastBrick.priceClose + deltaClose * brickSizeAbs, -1); return(0); } if (deltaOpen > 0 && lastBrick.direction < 0) { lastBrick = new RencoRobotBrick( lastBrick.priceOpen + (deltaOpen - 1) * brickSizeAbs, lastBrick.priceOpen + deltaOpen * brickSizeAbs, 1); return(1); } return(0); }
/// <summary> /// Проверяет, является ли средний элемент в "queue" локальным экстремумом /// </summary> private void CheckExtremum(RestrictedQueue <CandleData> queue) { bool isMax = true, isMin = true; // Проверяемая на экстремум свеча var checkingCandle = queue.ElementAt(ExtremumRange); for (var i = 0; i < queue.MaxQueueLength; i++) { if (i == ExtremumRange) { continue; } var candle = queue.ElementAt(i); if (checkingCandle.close <= candle.close) { isMax = false; } if (checkingCandle.close >= candle.close) { isMin = false; } if (!isMax && !isMin) { return; } } if (isMax) { extremumQueue.Add(new Cortege3 <float, bool, DateTime>(checkingCandle.close, true, checkingCandle.timeClose)); } if (isMin) { extremumQueue.Add(new Cortege3 <float, bool, DateTime>(checkingCandle.close, false, checkingCandle.timeClose)); } }
public void DetermineSentimentColorIndex(float bid) { colorCurrent = colorNeutral; if (prevBids.Length > 0) { signCurrent = Math.Sign(bid - prevBids.Last); if (signCurrent != 0) { var sum = prevBids.Sum(pBid => Math.Sign(bid - pBid)); var index = signCurrent == Math.Sign(sum) ? Math.Abs(sum) - 1 : 0; colorCurrent = signCurrent > 0 ? colorsGrowth[index] : colorsFall[index]; } } prevBids.Add(bid); }
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; } } }
private string BuildSeries() { var indi = chart.indicators.FirstOrDefault(i => i.GetType() == typeof(IndicatorExternSeries)); if (indi == null) { return("Нет данных для построения (данные из файла)"); } var indiData = indi.SeriesResult[0] as CandlestickSeries; if (indiData.DataCount == 0) { return("Индикатор пуст"); } var candles = chart.chart.StockSeries.Data.Candles; var max = Math.Min(candles.Count, indiData.DataCount); var lastSign = 0; var lastSigns = new RestrictedQueue <int>(skippedCandles); var lines = new List <TrendLine>(); TrendLine trendLine = null; for (var i = 0; i < max; i++) { var candle = indiData.Data[i]; var chartCandle = candles[i]; var thisSign = GetCaymanSign(candle); lastSigns.Add(thisSign); // растянуть регион if (trendLine != null) { trendLine.AddPoint(i, chartCandle.close); if (thisSign == lastSign) { continue; } } lastSign = thisSign; // завершить регион if (trendLine != null) { trendLine = null; continue; } if (lastSigns.Any(s => s != lastSign) || lastSigns.Length < skippedCandles) { continue; } // новая линия trendLine = new TrendLine { Comment = CommentSpecName, Magic = LineMagic, LineColor = thisSign > 0 ? colorSell : ColorBuy }; trendLine.AddPoint(i, chartCandle.close); lines.Add(trendLine); } MakeChartGraph(lines); return("Построено " + lines.Count + " областей"); }
public override List <string> OnQuotesReceived(string[] names, QuoteData[] quotes, bool isHistoryStartOff) { if (formulaResolver == null || packer == null) { return(null); } curTime = quotes[0].time; // обновить табличку цен for (var i = 0; i < names.Length; i++) { if (lastBids.ContainsKey(names[i])) { lastBids[names[i]] = quotes[i].bid; } else { lastBids.Add(names[i], quotes[i].bid); } } QuoteData curQuote = null; for (var i = 0; i < names.Length; i++) { if (names[i] == ticker) { curQuote = quotes[i]; break; } } if (curQuote == null) { return(null); // нет торгуемой котировки } // обновить свечки var candle = packer.UpdateCandle(curQuote.bid, curQuote.time); Dictionary <string, double> varValues = null; if (candle != null) { candles.Add(candle); countCandles++; // обновить очереди (для индекса, переменные вида usdjpy#15) if (lastBidLists.Count > 0) { foreach (var listTicker in lastBidLists) { float price; if (!lastBids.TryGetValue(listTicker.Key, out price)) { price = 0; } listTicker.Value.Add(price); } } // посчитать индекс double index; varValues = GetVariableValues(); if (formulaResolver.Calculate(varValues, out index)) { lastIndexValue = double.IsNaN(index) ? 0 : (float)index; } lastIndicies.Add(lastIndexValue ?? 0); } // если это период "разгона" конвейера if (isHistoryStartOff) { return(null); } RobotHint hint = null; // получить знак дивергенции if (candle != null) { string commentOnDivergence = string.Empty; var divergenceSign = GetDivergenceSign(out commentOnDivergence); List <MarketOrder> orders; robotContext.GetMarketOrders(robotContext.accountInfo.ID, out orders); if (divergenceSign != 0) { var hintText = new StringBuilder(); hintText.AppendLine(commentOnDivergence); hintText.AppendLine("Переменные:"); // ReSharper disable PossibleNullReferenceException) foreach (var pair in varValues) // ReSharper restore PossibleNullReferenceException { hintText.AppendLine(string.Format("{1}{0}{2:f4}", (char)9, pair.Key, pair.Value)); } hint = new RobotHint(Graphics[0].a, Graphics[0].b.ToString(), hintText.ToString(), divergenceSign > 0 ? "BUY" : "SELL", "e", curQuote.bid) { Time = candle.timeOpen, //curTime ColorFill = divergenceSign > 0 ? Color.Green : Color.Red, ColorLine = Color.Black, RobotHintType = divergenceSign > 0 ? RobotHint.HintType.Покупка : RobotHint.HintType.Продажа }; // если получен сигнал на покупку - купить, закрыв продажи // наоборот, если получен сигнал на продажу - продать, закрыв покупки var ordersToClose = orders.FindAll(o => o.Side != divergenceSign); foreach (var order in ordersToClose) { robotContext.SendCloseRequest(CurrentProtectedContext.Instance.MakeProtectedContext(), robotContext.accountInfo.ID, order.ID, PositionExitReason.ClosedByRobot); } // открыть позу в направлении знака дивера decimal?stop = StopLossPoints == 0 ? (decimal?)null : (decimal)curQuote.bid - divergenceSign * DalSpot.Instance.GetAbsValue(ticker, (decimal)StopLossPoints); decimal?take = TakeProfitPoints == 0 ? (decimal?)null : (decimal)curQuote.bid + divergenceSign * DalSpot.Instance.GetAbsValue(ticker, (decimal)TakeProfitPoints); robotContext.SendNewOrderRequest(CurrentProtectedContext.Instance.MakeProtectedContext(), RequestUniqueId.Next(), robotContext.accountInfo.ID, Magic, ticker, Volume, divergenceSign, OrderType.Market, 0, 0, stop, take, null, null, string.Empty, "OscillatorBasedRobot"); } } return(hint != null ? new List <string> { hint.ToString() } : null); }
private string BuildSeries() { var indi = chart.indicators.FirstOrDefault(i => i.GetType() == typeof (IndicatorExternSeries)); if (indi == null) return "Нет данных для построения (данные из файла)"; var indiData = indi.SeriesResult[0] as CandlestickSeries; if (indiData.DataCount == 0) return "Индикатор пуст"; var candles = chart.chart.StockSeries.Data.Candles; var max = Math.Min(candles.Count, indiData.DataCount); var lastSign = 0; var lastSigns = new RestrictedQueue<int>(skippedCandles); var lines = new List<TrendLine>(); TrendLine trendLine = null; for (var i = 0; i < max; i++) { var candle = indiData.Data[i]; var chartCandle = candles[i]; var thisSign = GetCaymanSign(candle); lastSigns.Add(thisSign); // растянуть регион if (trendLine != null) { trendLine.AddPoint(i, chartCandle.close); if (thisSign == lastSign) continue; } lastSign = thisSign; // завершить регион if (trendLine != null) { trendLine = null; continue; } if (lastSigns.Any(s => s != lastSign) || lastSigns.Length < skippedCandles) continue; // новая линия trendLine = new TrendLine { Comment = CommentSpecName, Magic = LineMagic, LineColor = thisSign > 0 ? colorSell : ColorBuy }; trendLine.AddPoint(i, chartCandle.close); lines.Add(trendLine); } MakeChartGraph(lines); return "Построено " + lines.Count + " областей"; }
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}"); } }
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 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 override List <string> OnQuotesReceived(string[] names, CandleDataBidAsk[] quotes, bool isHistoryStartOff) { var events = new List <string>(); #region получить candle из quote if (string.IsNullOrEmpty(ticker)) { return(events); } var tickerIndex = -1; for (var i = 0; i < names.Length; i++) { if (names[i] == ticker) { tickerIndex = i; break; } } if (tickerIndex < 0) { return(events); } var quote = quotes[tickerIndex]; var candle = packer.UpdateCandle(quote); if (candle == null) { return(events); } #endregion extremumRangeQueue.Add(candle); if (extremumRangeQueue.Length != extremumRangeQueue.MaxQueueLength) { return(events); } // Если очередь заполнена полностью, значит можно проверять её среднее значение на локальный экстремум CheckExtremum(extremumRangeQueue); // Все ли 4 экстремума сейчас имеются для анализа if (extremumQueue.Length != extremumQueue.MaxQueueLength) { return(events); } var extrArray = extremumQueue.ToArray(); // Теперь можно проверить - чередуются ли экстремумы. var boofSign = !extrArray[0].b; foreach (var extremum in extrArray) { if (extremum.b == boofSign) { return(events); // Это значит что два максимума или два минимума идут друг за другом } boofSign = !boofSign; } // Экстремумы чередуются друг за другом. // Теперь проверяем, больше ли по модулю "а", чем "b", "с" и "d" и close. var priceA = extrArray[0].a; var currentSide = extrArray[0].b ? 1 : -1; for (var i = 1; i < extrArray.Length; i++) { if (currentSide != Math.Sign(priceA - extrArray[i].a)) { return(events); } } if (currentSide != Math.Sign(priceA - candle.close)) { return(events); } // соотнесение "плеч" var ab = Math.Abs(extrArray[0].a - extrArray[1].a); var bc = Math.Abs(extrArray[1].a - extrArray[2].a); var cd = Math.Abs(extrArray[2].a - extrArray[3].a); var dClose = Math.Abs(extrArray[3].a - candle.close); var bcCd = cd == 0 ? float.MaxValue : bc / cd; if (bcCd < MinShoulder || bcCd > MaxShoulder) { return(events); } if (ab <= bc || ab <= cd || ab <= dClose) { return(events); } // Первый экстремум является большим по модулю, чем остальные - можно совершать сделку if (!isHistoryStartOff) // линии скользящих средних пересеклись причём не на истории. Значит пора торговать!!! { // коментарий var extrNames = new [] { "A", "B", "C", "D" }; var nameIndex = 0; // ReSharper disable LoopCanBeConvertedToQuery foreach (var extr in extrArray) // ReSharper restore LoopCanBeConvertedToQuery { events.Add(new RobotHint(ticker, Graphics[0].b.ToString(), extrNames[nameIndex], extrNames[nameIndex], extrNames[nameIndex++], extr.a) { Time = extr.c, RobotHintType = RobotHint.HintType.Линия //extr.b ? RobotHint.HintType.Покупка : RobotHint.HintType.Продажа }.ToString()); } // закрыть противонаправленные, если автозакрытие включено пользователем if (CloseDeal) { List <MarketOrder> orders; robotContext.GetMarketOrders(robotContext.AccountInfo.ID, out orders); foreach ( var order in orders.Where(o => o.Side != currentSide && o.Magic == Magic && o.Symbol == ticker).ToList()) { robotContext.SendCloseRequest(protectedContext.MakeProtectedContext(), robotContext.AccountInfo.ID, order.ID, PositionExitReason.ClosedByRobot); } } OpenDeal(candle.close, currentSide); } return(events); }
protected string BuildSeries() { var indi = chart.indicators.FirstOrDefault(i => i.GetType() == typeof(IndicatorExternSeries)); if (indi == null) { return("Нет данных для построения (данные из файла)"); } var indiData = indi.SeriesResult[0] as CandlestickSeries; if (indiData.DataCount == 0) { return("Индикатор пуст"); } var candles = chart.chart.StockSeries.Data.Candles; var max = Math.Min(candles.Count, indiData.DataCount); var lines = new List <TrendLine>(); TrendLine trendLine = null; var dealSign = 0; var caymanSigns = new RestrictedQueue <int>(skippedCandles); for (var i = 0; i < max; i++) { var candle = indiData.Data[i]; var chartCandle = candles[i]; var extremumSign = candle.close <lowerMargin ? -1 : candle.close> upperMargin ? 1 : 0; caymanSigns.Add(extremumSign); if (dealSign != 0) { trendLine.AddPoint(i, chartCandle.close); if ((dealSign < 0 && candle.close > 50) || (dealSign > 0 && candle.close < 50)) { trendLine = null; dealSign = 0; } continue; } dealSign = caymanSigns.Last; if (dealSign == 0) { continue; } if (caymanSigns.Any(s => s != dealSign)) { dealSign = 0; continue; } trendLine = new TrendLine { Comment = CommentSpecName, Magic = LineMagic, LineColor = dealSign > 0 ? colorSell : ColorBuy }; trendLine.AddPoint(i, chartCandle.close); trendLine.AddPoint(i, chartCandle.close); lines.Add(trendLine); } MakeChartGraph(lines); return("Построено " + lines.Count + " областей"); }
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; } }
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); } }
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); } }