фиксация первой реализации уровней поддержки
parent
e57d67d5db
commit
167c2ba119
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace KLHZ.Trader.Core.Math.Declisions.Dtos
|
||||||
|
{
|
||||||
|
public class ConvolutionResult
|
||||||
|
{
|
||||||
|
public decimal Sum { get; set; }
|
||||||
|
public decimal Value { get;set; }
|
||||||
|
public int Shift { get; set; }
|
||||||
|
public decimal Leverage { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace KLHZ.Trader.Core.Math.Declisions.Dtos
|
||||||
|
{
|
||||||
|
public class HistItem
|
||||||
|
{
|
||||||
|
public decimal Value { get; set; }
|
||||||
|
public decimal Count { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
|
||||||
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
|
|
||||||
|
namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
{
|
{
|
||||||
public static class SignalProcessing
|
public static class SignalProcessing
|
||||||
{
|
{
|
||||||
|
@ -54,6 +57,103 @@
|
||||||
return (res2.ToArray(), res.ToArray());
|
return (res2.ToArray(), res.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ITradeDataItem[] InterpolateData(ITradeDataItem[] items, TimeSpan timeStep)
|
||||||
|
{
|
||||||
|
var result = new List<ITradeDataItem>();
|
||||||
|
|
||||||
|
var firstItem = items[0];
|
||||||
|
var startTime = new DateTime(firstItem.Time.Year, firstItem.Time.Month, firstItem.Time.Day, 0, 0, 0, DateTimeKind.Utc);
|
||||||
|
var dt = items[0].Time - startTime;
|
||||||
|
|
||||||
|
var totalSteps = System.Math.Ceiling((items[items.Length - 1].Time - firstItem.Time).TotalSeconds / timeStep.TotalSeconds);
|
||||||
|
var deltaSeconds = System.Math.Floor(dt.TotalSeconds / timeStep.TotalSeconds);
|
||||||
|
startTime = startTime.AddSeconds(deltaSeconds * timeStep.TotalSeconds);
|
||||||
|
|
||||||
|
var firstBound = startTime;
|
||||||
|
var secondBound = startTime + timeStep;
|
||||||
|
var boundD1 = 0;
|
||||||
|
var boundD2 = 0;
|
||||||
|
for (int i = 0; i < totalSteps; i++)
|
||||||
|
{
|
||||||
|
var countD1 = 0;
|
||||||
|
var sumD1 = 0m;
|
||||||
|
var cD1 = 0L;
|
||||||
|
|
||||||
|
var countD2 = 0;
|
||||||
|
var sumD2 = 0m;
|
||||||
|
var cD2 = 0L;
|
||||||
|
|
||||||
|
for (int i1 = boundD1; i1 < items.Length; i1++)
|
||||||
|
{
|
||||||
|
if (items[i1].Direction == 1)
|
||||||
|
{
|
||||||
|
if (items[i1].Time > firstBound && items[i1].Time <= secondBound)
|
||||||
|
{
|
||||||
|
countD1++;
|
||||||
|
sumD1 += items[i1].Price;
|
||||||
|
cD1 += items[i1].Count;
|
||||||
|
}
|
||||||
|
else if (countD1 != 0)
|
||||||
|
{
|
||||||
|
boundD1 = i1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i1 = boundD2; i1 < items.Length; i1++)
|
||||||
|
{
|
||||||
|
if (items[i1].Direction == 2)
|
||||||
|
{
|
||||||
|
if (items[i1].Time > firstBound && items[i1].Time <= secondBound)
|
||||||
|
{
|
||||||
|
countD2++;
|
||||||
|
sumD2 += items[i1].Price;
|
||||||
|
cD2 += items[i1].Count;
|
||||||
|
}
|
||||||
|
else if (countD2 != 0)
|
||||||
|
{
|
||||||
|
boundD2 = i1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (countD1 != 0)
|
||||||
|
{
|
||||||
|
result.Add(new TradeDataItem()
|
||||||
|
{
|
||||||
|
Figi = firstItem.Figi,
|
||||||
|
Ticker = firstItem.Ticker,
|
||||||
|
Price = sumD1 / countD1,
|
||||||
|
Time = secondBound,
|
||||||
|
IsHistoricalData = firstItem.IsHistoricalData,
|
||||||
|
Direction = 1,
|
||||||
|
Count = cD1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (countD2 != 0)
|
||||||
|
{
|
||||||
|
result.Add(new TradeDataItem()
|
||||||
|
{
|
||||||
|
Figi = firstItem.Figi,
|
||||||
|
Ticker = firstItem.Ticker,
|
||||||
|
Price = sumD2 / countD2,
|
||||||
|
Time = secondBound,
|
||||||
|
IsHistoricalData = firstItem.IsHistoricalData,
|
||||||
|
Direction = 2,
|
||||||
|
Count = cD2
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
firstBound += timeStep;
|
||||||
|
secondBound += timeStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return result.OrderBy(r => r.Time).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
public static decimal[] CalcDiffs(decimal[] values)
|
public static decimal[] CalcDiffs(decimal[] values)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
|
using KLHZ.Trader.Core.Math.Declisions.Dtos;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
{
|
{
|
||||||
|
@ -128,5 +129,91 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
}
|
}
|
||||||
return false;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
|
||||||
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
|
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Tests
|
namespace KLHZ.Trader.Core.Tests
|
||||||
|
@ -38,5 +40,30 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
Assert.IsTrue(r == 1);
|
Assert.IsTrue(r == 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public static void Test3()
|
||||||
|
{
|
||||||
|
var results = new List<ITradeDataItem>();
|
||||||
|
var times = new List<DateTime>();
|
||||||
|
var startDt = DateTime.UtcNow;
|
||||||
|
for (int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
startDt = startDt.AddSeconds(((double)(RandomNumberGenerator.GetInt32(1, 100))) / 100);
|
||||||
|
var t = new TradeDataItem()
|
||||||
|
{
|
||||||
|
Figi = "",
|
||||||
|
Ticker = "",
|
||||||
|
Time = startDt,
|
||||||
|
Count = 1,
|
||||||
|
Direction = RandomNumberGenerator.GetInt32(1, 3),
|
||||||
|
IsHistoricalData = true,
|
||||||
|
Price = (decimal)System.Math.Sin(0.01 * i) + (decimal)System.Math.Cos(0.01 * i),
|
||||||
|
};
|
||||||
|
results.Add(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = SignalProcessing.InterpolateData(results.ToArray(), TimeSpan.FromSeconds(5));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
|
||||||
|
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Tests
|
namespace KLHZ.Trader.Core.Tests
|
||||||
|
@ -6,7 +7,7 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
internal class StatisticTests
|
internal class StatisticTests
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public static void Test()
|
public static void Test1()
|
||||||
{
|
{
|
||||||
var data = new decimal[1000];
|
var data = new decimal[1000];
|
||||||
for (int i = 0; i < data.Length; i++)
|
for (int i = 0; i < data.Length; i++)
|
||||||
|
@ -21,5 +22,27 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
Assert.IsTrue(data.Length != res.Length);
|
Assert.IsTrue(data.Length != res.Length);
|
||||||
Assert.IsTrue(res[0] != 1000);
|
Assert.IsTrue(res[0] != 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public static void Test2()
|
||||||
|
{
|
||||||
|
var data = new decimal[1000];
|
||||||
|
for (int i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
data[i] = RandomNumberGenerator.GetInt32(-10, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var res = Statistics.CalcHistogram(data.Select(d=>new CachedValue()
|
||||||
|
{
|
||||||
|
Figi ="",
|
||||||
|
Ticker = "",
|
||||||
|
Direction =1,
|
||||||
|
Price = d/2,
|
||||||
|
Time = DateTime.UtcNow,
|
||||||
|
}).ToArray());
|
||||||
|
|
||||||
|
Statistics.CalcConvolution(res, 5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
var changeMods = TraderUtils.GetInitDict(0);
|
var changeMods = TraderUtils.GetInitDict(0);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
message = TraderUtils.FilterHighFreqValues(message, message.Direction == 1 ? pricesCache1 : pricesCache2);
|
//message = TraderUtils.FilterHighFreqValues(message, message.Direction == 1 ? pricesCache1 : pricesCache2);
|
||||||
|
|
||||||
#region Добавление данных в кеши.
|
#region Добавление данных в кеши.
|
||||||
if (message.Figi == "BBG004730N88" || message.Figi == "FUTIMOEXF000")
|
if (message.Figi == "BBG004730N88" || message.Figi == "FUTIMOEXF000")
|
||||||
|
|
|
@ -2,9 +2,12 @@ using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
|
||||||
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
|
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
|
||||||
using KLHZ.Trader.Core.DataLayer;
|
using KLHZ.Trader.Core.DataLayer;
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Orders;
|
using KLHZ.Trader.Core.DataLayer.Entities.Orders;
|
||||||
|
using KLHZ.Trader.Core.Math.Declisions.Dtos;
|
||||||
|
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
||||||
using KLHZ.Trader.Service.Models;
|
using KLHZ.Trader.Service.Models;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Service.Controllers
|
namespace KLHZ.Trader.Service.Controllers
|
||||||
{
|
{
|
||||||
|
@ -234,6 +237,126 @@ namespace KLHZ.Trader.Service.Controllers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task RunConvolution(double? shift = null)
|
||||||
|
{
|
||||||
|
var timeStep = 30;
|
||||||
|
var time = DateTime.UtcNow.AddDays(-shift ?? -7).Date;
|
||||||
|
while (time < DateTime.UtcNow)
|
||||||
|
{
|
||||||
|
var forSave = new List<Core.DataLayer.Entities.Prices.ProcessedPrice>();
|
||||||
|
time = time.AddMinutes(timeStep);
|
||||||
|
var figi1 = "FUTIMOEXF000";
|
||||||
|
//var figi1 = "BBG004730N88";
|
||||||
|
var figi2 = "FUTIMOEXF000";
|
||||||
|
//var figi2 = "FUTIMOEXF000";
|
||||||
|
var time2 = time;
|
||||||
|
//var time1 = new DateTime(2025, 9, 24, 11, 00, 0, DateTimeKind.Utc);
|
||||||
|
//var time2 = DateTime.UtcNow.AddMinutes(18);
|
||||||
|
using var context1 = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||||
|
|
||||||
|
var time1 = time2.AddHours(-3);
|
||||||
|
var prices = await context1.PriceChanges
|
||||||
|
.Where(c => (c.Figi == figi1 || c.Figi == figi2) && c.Time >= time1 && c.Time < time2 && c.Direction == 1)
|
||||||
|
.OrderBy(c => c.Time)
|
||||||
|
.Select(c => new TradeDataItem()
|
||||||
|
{
|
||||||
|
Figi = c.Figi,
|
||||||
|
Ticker = c.Ticker,
|
||||||
|
Time = c.Time,
|
||||||
|
Price = c.Price,
|
||||||
|
IsHistoricalData = true,
|
||||||
|
Direction = c.Direction,
|
||||||
|
Count = c.Count,
|
||||||
|
})
|
||||||
|
.ToArrayAsync();
|
||||||
|
var pricesToSave = prices.Where(p => p.Time < time && p.Time >= time.AddMinutes(-timeStep)).ToArray();
|
||||||
|
if (pricesToSave.Any())
|
||||||
|
{
|
||||||
|
var p1 = pricesToSave.Last();
|
||||||
|
forSave.Add(new Core.DataLayer.Entities.Prices.ProcessedPrice()
|
||||||
|
{
|
||||||
|
Figi = p1.Figi,
|
||||||
|
Processor = "support_level_calc",
|
||||||
|
Ticker = p1.Ticker,
|
||||||
|
Count = p1.Count,
|
||||||
|
Direction = p1.Direction,
|
||||||
|
Time = p1.Time,
|
||||||
|
Price = p1.Price,
|
||||||
|
});
|
||||||
|
var leverage = 3;
|
||||||
|
var hist = Statistics.CalcHistogram(prices);
|
||||||
|
var convs = Statistics.CalcConvolution(hist, leverage).ToList();
|
||||||
|
//var result = new List<ConvolutionResult>();
|
||||||
|
//Statistics.MergeConvolutionResults(convs, result);
|
||||||
|
var orderedConvs = convs.OrderByDescending(c => c.Sum).Take(5).ToList();
|
||||||
|
orderedConvs = orderedConvs.OrderBy(c => c.Value).ToList();
|
||||||
|
var values = new List<decimal>();
|
||||||
|
var shifts = new List<decimal>();
|
||||||
|
|
||||||
|
foreach (var c in orderedConvs)
|
||||||
|
{
|
||||||
|
var left = c.Value - 0.5m * (leverage - 1);
|
||||||
|
var right = c.Value + 0.5m * (leverage + 1);
|
||||||
|
|
||||||
|
if (values.Count > 0)
|
||||||
|
{
|
||||||
|
var last = values.Last();
|
||||||
|
if (last < left)
|
||||||
|
{
|
||||||
|
values.Add(left);
|
||||||
|
values.Add(right);
|
||||||
|
}
|
||||||
|
else if (last >= left && last < right)
|
||||||
|
{
|
||||||
|
values.Remove(last);
|
||||||
|
values.Add(right);
|
||||||
|
}
|
||||||
|
else if (last >= right)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
values.Add(left);
|
||||||
|
values.Add(right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pairs = new List<(decimal left, decimal right)>();
|
||||||
|
for (int i = 0; i < values.Count - 1; i += 2)
|
||||||
|
{
|
||||||
|
pairs.Add((values[i], values[i + 1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var price in pricesToSave)
|
||||||
|
{
|
||||||
|
foreach (var p in pairs)
|
||||||
|
{
|
||||||
|
if (price.Price >= p.left && price.Price <= p.right)
|
||||||
|
{
|
||||||
|
forSave.Add(new Core.DataLayer.Entities.Prices.ProcessedPrice()
|
||||||
|
{
|
||||||
|
Figi = price.Figi,
|
||||||
|
Processor = "support_level",
|
||||||
|
Ticker = price.Ticker,
|
||||||
|
Count = price.Count,
|
||||||
|
Direction = price.Direction,
|
||||||
|
Time = price.Time,
|
||||||
|
Price = price.Price,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await context1.ProcessedPrices.AddRangeAsync(forSave);
|
||||||
|
await context1.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue