222 lines
8.7 KiB
C#
222 lines
8.7 KiB
C#
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<decimal>();
|
|
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<ITradeDataItem, decimal> 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<ITradeDataItem, decimal> 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<decimal, decimal>();
|
|
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<ConvolutionResult>();
|
|
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<ConvolutionResult>();
|
|
}
|
|
|
|
public static void MergeConvolutionResults(List<ConvolutionResult> results, List<ConvolutionResult> 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);
|
|
}
|
|
}
|
|
}
|