private void BuildEratio() { if (signals.Count == 0) { return; } ReadConfig(); var signalStats = signals.OrderBy(s => s.Time).Select(s => new SignalStat(s, timeframe, timeframes)).ToList(); var actualStats = new List <SignalStat>(); var resultedStats = new List <SignalStat>(); using (var candleReader = new CandleStream(quoteFilePath)) { foreach (var candle in candleReader) { candle.time = candle.time.AddHours(quoteOffset); for (var i = 0; i < signalStats.Count; i++) { if (signalStats[i].time > candle.time) { break; } actualStats.Add(signalStats[i]); // выровнять цену входа signalStats[i].enter = (candle.o + candle.c) / 2; signalStats.RemoveAt(i--); } for (var i = 0; i < actualStats.Count; i++) { // обновить pros / cons actualStats[i].UpdateProsCons(candle, candle.time); if (actualStats[i].endTime > candle.time) { break; } resultedStats.Add(actualStats[i]); actualStats.RemoveAt(i--); } } } resultedStats.AddRange(actualStats); // свернуть статистику в график ReduceERatio(resultedStats); }
private void FindTimeframeOffest() { var sigByTime = signals.GroupBy(s => s.Time).ToDictionary(s => s.Key, s => s.First()); var mistakeByOffset = new Dictionary <int, OffsetMistake>(); for (var i = -MaxOffset; i <= MaxOffset; i++) { mistakeByOffset.Add(i, new OffsetMistake { offset = i }); } using (var candleReader = new CandleStream(quoteFilePath)) { foreach (var candle in candleReader) { for (var i = -MaxOffset; i <= MaxOffset; i++) { var time = candle.time.AddHours(i); if (!sigByTime.TryGetValue(time, out var sig)) { continue; } var delta = Math.Abs(100 * ((decimal)candle.o - sig.Enter) / sig.Enter); var mis = mistakeByOffset[i]; mis.mistakeSum += delta; mis.count++; } } } // выбрать смещение с наименьшей ошибкой var min = 0M; var offset = 0; foreach (var mis in mistakeByOffset.Values) { var av = mis.count == 0 ? 0 : mis.mistakeSum / mis.count; if (mis.count > 0 && av < min) { offset = mis.offset; min = av; } Console.WriteLine($"[Offset {mis.offset}]: avg. mistake is {av:f4}%, count {mis.count}"); } Console.WriteLine($"Offset should be {offset}"); }
/// <summary>Access the order book for the given pair</summary> public List <MarketChartData> this[CurrencyPair pair, EMarketPeriod period, UnixMSec time_beg, UnixMSec time_end, CancellationToken? cancel = null] // Worker thread context { get { var key = new PairAndTF { Pair = pair, TimeFrame = period }; try { var stream = (CandleStream)null; lock (Streams) { // Look for the stream for 'pair'. Create if not found. // Return a copy since any thread can call this function. if (!Streams.TryGetValue(key, out stream) || stream.Socket == null) { stream = Streams[key] = new CandleStream(pair, period, this); } } lock (stream.CandleData) { // If the range is outside the cached data range, fall back to the REST call if (stream.CandleData.Count == 0 || time_beg < stream.CandleData[0].Time) { return(Api.GetChartData(pair, period, time_beg, time_end, cancel).Result); } // Return the requested range var ibeg = stream.CandleData.BinarySearch(x => x.Time.CompareTo(time_beg), find_insert_position: true); var iend = stream.CandleData.BinarySearch(x => x.Time.CompareTo(time_end), find_insert_position: true); return(stream.CandleData.GetRange(ibeg, iend - ibeg)); } } catch (Exception ex) { BinanceApi.Log.Write(ELogLevel.Error, ex, $"Subscribing to candle data for {pair.Id}/{period} failed."); lock (Streams) Streams.Remove(key); return(new List <MarketChartData>()); } } }