using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; using KLHZ.Trader.Core.Math.Declisions.Dtos; namespace KLHZ.Trader.Core.Math.Declisions.Utils { public static class Statistics { public static decimal MeanCount(this ITradeDataItem[] values) { return values.Sum(x => x.Count) / values.Length; } public static decimal MeanPrice(this ITradeDataItem[] values) { return values.Sum(x => x.Price) / values.Length; } private static (decimal mean, decimal std) CaclSigma(decimal[] values) { var mean = values.Sum() / values.Length; var data = new decimal[values.Length]; Array.Copy(values, data, data.Length); for (int i = 0; i < data.Length; i++) { var v = data[i] - mean; data[i] = v * v; } var std = System.Math.Pow((double)(data.Sum() / (data.Length - 1)), 0.5); return (mean, (decimal)std); } public static decimal[] ClearNSigmaReqursive(decimal[] values, int depth = 0, int sigmasCount = 3) { if (values.Length <= 1 || depth > 10) return values; var sigmaRes = CaclSigma(values); var std = sigmaRes.std; var mean = sigmaRes.mean; var forRes = new List(); var _3std = sigmasCount * std; foreach (var v in values) { if (System.Math.Abs(mean - v) < _3std) { forRes.Add(v); } } if (forRes.Count != values.Length) { return ClearNSigmaReqursive(forRes.ToArray(), depth + 1); } else { return forRes.ToArray(); } } public static bool TryCalcTimeWindowsDiff(this ITradeDataItem[] values, TimeSpan boundLeft, TimeSpan boundRight, Func fieldSelector, bool calcMean, out decimal result, out decimal resultRelative) { result = default; resultRelative = default; if (values.Length > 1) { var shiftTimeR = values.Last().Time - boundRight; var shiftTimeL = values.Last().Time - boundLeft; var valuesOld = values.Where(b => b.Time < shiftTimeR && b.Time >= shiftTimeL).ToArray(); var valuesNew = values.Where(b => b.Time >= shiftTimeR).ToArray(); if (valuesOld.Length > 0 && valuesNew.Length > 0) { var valNew = valuesNew.Sum(fieldSelector); var valOld = valuesOld.Sum(fieldSelector); if (calcMean) { valNew = valNew / valuesNew.Length; valOld = valOld / valuesOld.Length; } result = valNew - valOld; resultRelative = (valNew - valOld) / valOld; return true; } } return false; } public static bool TryCalcTimeDiff(this ITradeDataItem[] values, TimeSpan boundLeft, TimeSpan boundRight, Func fieldSelector, bool calcMean, out decimal result) { result = default; if (values.Length > 1) { var shiftTimeR = values.Last().Time - boundRight; var shiftTimeL = values.Last().Time - boundLeft; var valuesOld = values.Where(b => b.Time < shiftTimeR && b.Time >= shiftTimeL).ToArray(); var valuesNew = values.Where(b => b.Time >= shiftTimeR).ToArray(); if (valuesOld.Length > 0 && valuesNew.Length > 0) { var valNew = fieldSelector(valuesNew.Last()); var valOld = fieldSelector(valuesOld.Last()); result = valNew - valOld; return true; } } return false; } public static bool TryCalcPirsonCorrelation(this ITradeDataItem[] values, TimeSpan period, out decimal result) { result = default; if (values.Any()) { var shiftTimeDiffs1 = values.Last().Time - period; values = values.Where(b => b.Time >= shiftTimeDiffs1).ToArray(); if (values.Any()) { var tradevolume_diffMean = values.MeanCount(); var dprice_diffMean = values.MeanPrice(); var sum1 = (double)values.Sum(d => (d.Value2 - tradevolume_diffMean) * (d.Value - dprice_diffMean)); var sum2 = values.Sum(d => (d.Value2 - tradevolume_diffMean) * (d.Value2 - tradevolume_diffMean)); var sum3 = values.Sum(d => (d.Value - dprice_diffMean) * (d.Value - dprice_diffMean)); if (sum2 != 0 && sum3 != 0) { result = (decimal)(sum1 / System.Math.Sqrt((double)(sum2 * sum3))); return true; } } } return false; } public static HistItem[] CalcHistogram(ITradeDataItem[] values) { var result = new Dictionary(); foreach (var item in values) { if (!result.TryGetValue(item.Price, out var val)) { result[item.Price] = 0; } result[item.Price] = val + 1; } return result.Select(r => new HistItem() { Value = r.Key, Count = r.Value }).OrderBy(i => i.Value).ToArray(); } public static decimal[] GetParabolaCore(int leverageSize, decimal maxValue = 1) { var d = new double[2 * leverageSize + 1]; for (int i = 0; i < d.Length; i++) { d[i] = -System.Math.Pow((i - leverageSize), 2); } var min = d.Min(); var amin = System.Math.Abs(min); for (int i = 0; i < d.Length; i++) { d[i] = (d[i] - min) / amin * (double)maxValue; } return d.Select(i => (decimal)i).ToArray(); } public static ConvolutionResult[] CalcConvolution(HistItem[] hist, int leverageSize) { if (hist.Length > 2 * leverageSize + 1) { var results = new List(); var coreSize = 2 * leverageSize + 1; for (int shift = 0; shift < hist.Length - coreSize; shift++) { var s = 0m; var k = 0; for (int i = 0; i < 2 * leverageSize + 1; i++) { var core = GetParabolaCore(leverageSize, hist[i + shift].Count); s += (hist[i + shift].Count - core[i]) * (hist[i + shift].Count - core[i]); if (i == leverageSize) { k = i + shift; } } s = s / coreSize; s = (decimal)System.Math.Pow((double)s, 0.5d); //s /= coreSum; results.Add(new ConvolutionResult() { Leverage = leverageSize, Sum = s, Value = hist[k].Value, Shift = k, }); } return results.OrderByDescending(r => r.Value).ToArray(); } return Array.Empty(); } public static void MergeConvolutionResults(List results, List mergedResults) { if (results.Count == 0) { return; } var resultsOrdered = results.OrderBy(r => r.Sum).ToList(); var res = resultsOrdered[0]; var b1 = res.Shift - res.Leverage; var b2 = res.Shift + res.Leverage; var forMerge = results.Where(r => r.Shift >= b1 && r.Shift <= b2).ToList(); res.Sum = forMerge.Sum(r => r.Sum); foreach (var m in forMerge) { results.Remove(m); } mergedResults.Add(res); MergeConvolutionResults(results, mergedResults); } } }