Compare commits

...

4 Commits

Author SHA1 Message Date
vlad zverzhkhovskiy 89f4ec6126 обновление бота
test / deploy_trader_prod (push) Successful in 7m2s Details
2025-09-29 13:24:13 +03:00
vlad zverzhkhovskiy a0aaff5360 Фикс пропажи точек 2025-09-29 13:12:59 +03:00
vlad zverzhkhovskiy ac9cb53525 Добавил точек сброса активов 2025-09-25 12:58:07 +03:00
vlad zverzhkhovskiy cdc179b3a5 фиксация экспериментов с FFT 2025-09-25 10:03:04 +03:00
13 changed files with 577 additions and 80 deletions

View File

@ -3,6 +3,7 @@
public enum TimeWindowCacheType public enum TimeWindowCacheType
{ {
None = 0, None = 0,
_20_Seconds = 2001,
_1_Minute = 1, _1_Minute = 1,
_5_Minutes = 2, _5_Minutes = 2,
_15_Minutes = 15, _15_Minutes = 15,

View File

@ -5,6 +5,10 @@
None = 0, None = 0,
Middle = 1, Middle = 1,
UpperThen30Decil = 10, UpperThen30Decil = 10,
LowerThenMediana = -50, LowerThen30Decil = -0,
LowerThenMedianaGrowing = -50,
LowerThenMedianaFalling = -51,
Growing = -512,
Falling = -513,
} }
} }

View File

@ -8,7 +8,7 @@
public decimal Min { get; init; } public decimal Min { get; init; }
public decimal Mediana { get; init; } public decimal Mediana { get; init; }
public decimal Upper30Decil { get; init; } public decimal Upper30Decil { get; init; }
public decimal Lower20Decil { get; init; } public decimal Lower30Decil { get; init; }
public int Length { get; init; } public int Length { get; init; }
public DateTime LastTime { get; init; } public DateTime LastTime { get; init; }
public DateTime StartTime { get; init; } public DateTime StartTime { get; init; }

View File

@ -51,6 +51,10 @@ namespace KLHZ.Trader.Core.Math.Declisions.Dtos
{ {
return TimeSpan.FromMinutes(15); return TimeSpan.FromMinutes(15);
} }
case TimeWindowCacheType._20_Seconds:
{
return TimeSpan.FromSeconds(20);
}
default: default:
{ {
return TimeSpan.FromMinutes(1); return TimeSpan.FromMinutes(1);

View File

@ -50,6 +50,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
private readonly object _locker = new(); private readonly object _locker = new();
private readonly decimal[] Prices = new decimal[_arrayMaxLength]; private readonly decimal[] Prices = new decimal[_arrayMaxLength];
private readonly DateTime[] Timestamps = new DateTime[_arrayMaxLength]; private readonly DateTime[] Timestamps = new DateTime[_arrayMaxLength];
private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _20_secTimeWindows = new();
private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _1_minTimeWindows = new(); private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _1_minTimeWindows = new();
private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _5_minTimeWindows = new(); private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _5_minTimeWindows = new();
private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _15_minTimeWindows = new(); private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _15_minTimeWindows = new();
@ -93,6 +94,10 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
{ {
return _15_minTimeWindows; return _15_minTimeWindows;
} }
case TimeWindowCacheType._20_Seconds:
{
return _20_secTimeWindows;
}
default: default:
{ {
return _1_minTimeWindows; ; return _1_minTimeWindows; ;

View File

@ -2,7 +2,6 @@
using KLHZ.Trader.Core.Math.Declisions.Dtos.FFT.Enums; using KLHZ.Trader.Core.Math.Declisions.Dtos.FFT.Enums;
using MathNet.Numerics; using MathNet.Numerics;
using MathNet.Numerics.IntegralTransforms; using MathNet.Numerics.IntegralTransforms;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace KLHZ.Trader.Core.Math.Declisions.Utils namespace KLHZ.Trader.Core.Math.Declisions.Utils
{ {
@ -18,7 +17,47 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
} }
else if (value < fftData.Mediana && System.Math.Sign(value2) >= 0) else if (value < fftData.Mediana && System.Math.Sign(value2) >= 0)
{ {
return ValueAmplitudePosition.LowerThenMediana; return ValueAmplitudePosition.LowerThenMedianaGrowing;
}
else if (value < fftData.Mediana && System.Math.Sign(value2) < 0)
{
return ValueAmplitudePosition.LowerThenMedianaFalling;
}
else
{
return ValueAmplitudePosition.Middle;
}
}
public static ValueAmplitudePosition CheckExtremums(FFTAnalyzeResult fftData, DateTime timestamp)
{
var value = (decimal)CalcAmplitude(fftData.Harmonics, fftData.StartTime, timestamp);
var value2 = (decimal)CalcExtremum(fftData.Harmonics, fftData.StartTime, timestamp);
if (value > fftData.Upper30Decil && System.Math.Sign(value2) <= 0)
{
return ValueAmplitudePosition.UpperThen30Decil;
}
else if (value < fftData.Lower30Decil && System.Math.Sign(value2) >= 0)
{
return ValueAmplitudePosition.LowerThen30Decil;
}
else
{
return ValueAmplitudePosition.Middle;
}
}
public static ValueAmplitudePosition CheckSign(FFTAnalyzeResult fftData, DateTime timestamp)
{
var value = (decimal)CalcAmplitude(fftData.Harmonics, fftData.StartTime, timestamp);
var value2 = (decimal)CalcExtremum(fftData.Harmonics, fftData.StartTime, timestamp);
if (System.Math.Sign(value2) <= 0)
{
return ValueAmplitudePosition.Falling;
}
else if (System.Math.Sign(value2) >= 0)
{
return ValueAmplitudePosition.Growing;
} }
else else
{ {
@ -49,7 +88,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
StartTime = startTime, StartTime = startTime,
Mediana = newValues[newValues.Length / 2], Mediana = newValues[newValues.Length / 2],
Upper30Decil = newValues[(int)(newValues.Length * 0.7)], Upper30Decil = newValues[(int)(newValues.Length * 0.7)],
Lower20Decil = newValues[(int)(newValues.Length * 0.2)], Lower30Decil = newValues[(int)(newValues.Length * 0.3)],
Max = newValues.Max(), Max = newValues.Max(),
Min = newValues.Min(), Min = newValues.Min(),
Length = values.Length, Length = values.Length,
@ -91,7 +130,60 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
StartTime = result.StartTime, StartTime = result.StartTime,
Mediana = newValues[newValues.Length / 2], Mediana = newValues[newValues.Length / 2],
Upper30Decil = newValues[(int)(newValues.Length * 0.7)], Upper30Decil = newValues[(int)(newValues.Length * 0.7)],
Lower20Decil = newValues[(int)(newValues.Length * 0.2)], Lower30Decil = newValues[(int)(newValues.Length * 0.3)],
Max = newValues.Max(),
Min = newValues.Min(),
};
}
public static FFTAnalyzeResult ReAnalyze(FFTAnalyzeResult result, string key, float energyPart, bool include)
{
var tmp = new List<Harmonic>();
var symmEnergy = result.Harmonics.Sum(h => h.Magnitude);
var tmpSumEnergy = 0f;
for (int i = 0; i < result.Harmonics.Length; i++)
{
tmpSumEnergy += result.Harmonics[i].Magnitude;
if (include)
{
if (tmpSumEnergy/ symmEnergy < energyPart)
{
tmp.Add(result.Harmonics[i]);
}
}
else
{
if (tmpSumEnergy / symmEnergy >= energyPart)
{
tmp.Add(result.Harmonics[i]);
}
}
}
var harms = tmp.ToArray();
var newValues = new decimal[result.Length];
var newValues2 = new decimal[result.Length];
var time = result.StartTime;
var dt = (result.LastTime - result.StartTime).TotalSeconds / result.Length;
for (int i = 0; i < result.Length; i++)
{
var currentTime = time.AddSeconds(i * dt);
newValues[i] = (decimal)CalcAmplitude(harms, result.StartTime, currentTime);
newValues2[i] = (decimal)CalcExtremum(harms, result.StartTime, currentTime);
}
newValues = newValues.Order().ToArray();
var ma = newValues2.Max();
var mi = newValues2.Min();
return new FFTAnalyzeResult()
{
Key = key,
Harmonics = harms,
LastTime = result.LastTime,
StartTime = result.StartTime,
Mediana = newValues[newValues.Length / 2],
Upper30Decil = newValues[(int)(newValues.Length * 0.7)],
Lower30Decil = newValues[(int)(newValues.Length * 0.3)],
Max = newValues.Max(), Max = newValues.Max(),
Min = newValues.Min(), Min = newValues.Min(),
}; };

View File

@ -20,8 +20,10 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
} }
var x1 = new List<double>(); var x1 = new List<double>();
var y1 = new List<double>(); var y1 = new List<double>();
var x1d = new List<DateTime>();
var x2 = new List<double>(); var x2 = new List<double>();
var y2 = new List<double>(); var y2 = new List<double>();
var x2d = new List<DateTime>();
var y1_approximated = new List<double>(); var y1_approximated = new List<double>();
var y2_approximated = new List<double>(); var y2_approximated = new List<double>();
@ -37,11 +39,13 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
if (dt1 <= lastPeriod) if (dt1 <= lastPeriod)
{ {
x2.Add((times[times.Length - i] - firstTime).TotalSeconds); x2.Add((times[times.Length - i] - firstTime).TotalSeconds);
x2d.Add(times[times.Length - i]);
y2.Add((double)prices[times.Length - i]); y2.Add((double)prices[times.Length - i]);
} }
else if (dt1 <= fullPeriod) else if (dt1 <= fullPeriod)
{ {
x1.Add((times[times.Length - i] - firstTime).TotalSeconds); x1.Add((times[times.Length - i] - firstTime).TotalSeconds);
x1d.Add(times[times.Length - i]);
y1.Add((double)prices[times.Length - i]); y1.Add((double)prices[times.Length - i]);
} }
else else
@ -67,13 +71,13 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
var diff2 = y2_approximated[0] - y2_approximated[y2_approximated.Count - 1]; var diff2 = y2_approximated[0] - y2_approximated[y2_approximated.Count - 1];
if (diff1 <= -meanfullDiff && diff2 >= meanfullDiff) if (diff1 <= -meanfullDiff && diff2 >= meanfullDiff)
{ {
res |= TradingEvent.UptrendStart; res |= TradingEvent.DowntrendEnd;
} }
else if (diff1 >= meanfullDiff && diff2 <= 0) else if (diff1 >= meanfullDiff && diff2 <= -meanfullDiff)
{ {
res |= TradingEvent.UptrendEnd; res |= TradingEvent.UptrendEnd;
} }
else if (diff1 <= -meanfullDiff && diff2 >= 0) else if (diff1 <= -meanfullDiff && diff2 >= meanfullDiff)
{ {
res |= TradingEvent.DowntrendEnd; res |= TradingEvent.DowntrendEnd;
} }

View File

@ -144,7 +144,8 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
s = shift; s = shift;
var i2 = size - 1 - shift; var i2 = size - 1 - shift;
var i1 = size - 2 - shift; var i1 = size - 2 - shift;
var debugT = timestamps.Reverse().ToArray();
var debugV = prices.Reverse().ToArray();
var twavs = CalcTimeWindowAverageValue(timestamps, prices, smallWindow, shift); var twavs = CalcTimeWindowAverageValue(timestamps, prices, smallWindow, shift);
var twavb = CalcTimeWindowAverageValue(timestamps, prices, bigWindow, shift); var twavb = CalcTimeWindowAverageValue(timestamps, prices, bigWindow, shift);
pricesForFinalComparison[i2] = prices[prices.Length - 1 - shift]; pricesForFinalComparison[i2] = prices[prices.Length - 1 - shift];
@ -182,7 +183,8 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
// если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта // если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта
if (twavss[size - 1] <= twavbs[size - 1] && twavss[size - 2] > twavbs[size - 2]) if (twavss[size - 1] <= twavbs[size - 1] && twavss[size - 2] > twavbs[size - 2])
{ {
//if (pricesForFinalComparison[crossings[0]] - pricesForFinalComparison[crossings[1]] >= uptrendEndingDetectionMeanfullStep) var d = pricesForFinalComparison[crossings[0]] - pricesForFinalComparison[crossings[1]];
//if (d >= uptrendEndingDetectionMeanfullStep)
{ {
res |= TradingEvent.UptrendEnd; res |= TradingEvent.UptrendEnd;
} }
@ -191,7 +193,8 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
// если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта // если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта
if (twavss[size - 1] >= twavbs[size - 1] && twavss[size - 2] < twavbs[size - 2]) if (twavss[size - 1] >= twavbs[size - 1] && twavss[size - 2] < twavbs[size - 2])
{ {
//if (pricesForFinalComparison[crossings[0]] - pricesForFinalComparison[crossings[1]] <= uptrendStartingDetectionMeanfullStep) var d = pricesForFinalComparison[crossings[0]] - pricesForFinalComparison[crossings[1]];
//if (d <= -uptrendStartingDetectionMeanfullStep)
{ {
res |= TradingEvent.UptrendStart; res |= TradingEvent.UptrendStart;
} }

View File

@ -156,7 +156,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
Count = response.Trade.Quantity, Count = response.Trade.Quantity,
}; };
await _tradeDataProvider.AddData(message, TimeSpan.FromHours(7)); //await _tradeDataProvider.AddData(message, TimeSpan.FromHours(7));
await _eventBus.Broadcast(message); await _eventBus.Broadcast(message);
var exchangeState = ExchangeScheduler.GetCurrentState(); var exchangeState = ExchangeScheduler.GetCurrentState();

View File

@ -19,6 +19,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Threading.Channels; using System.Threading.Channels;
using Tinkoff.InvestApi; using Tinkoff.InvestApi;
@ -92,9 +93,9 @@ namespace KLHZ.Trader.Core.Exchange.Services
return Task.CompletedTask; return Task.CompletedTask;
} }
public async ValueTask<(DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists)> GetData(INewPrice message) public async ValueTask<(DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists)> GetData(INewPrice message, TimeSpan? windowSize = null)
{ {
var data2 = await _tradeDataProvider.GetData(message.Figi, TimeSpan.FromHours(1.5)); var data2 = await _tradeDataProvider.GetData(message.Figi, windowSize??TimeSpan.FromHours(1.5));
if (!data2.isFullIntervalExists) if (!data2.isFullIntervalExists)
{ {
data2 = await _tradeDataProvider.GetData(message.Figi, TimeSpan.FromHours(1)); data2 = await _tradeDataProvider.GetData(message.Figi, TimeSpan.FromHours(1));
@ -106,35 +107,106 @@ namespace KLHZ.Trader.Core.Exchange.Services
return data2; return data2;
} }
private async ValueTask<(decimal smallWindow, decimal bigWindow)> GetWindowsSizes(INewPrice message)
{
var fftFull = await _tradeDataProvider.GetFFtResult(message.Figi + "_full");
if (!fftFull.IsEmpty)
{
var harms = fftFull.Harmonics.Skip(1).Take(fftFull.Harmonics.Length - 3).ToArray();
var sum = harms.Sum(h => h.Magnitude);
var sumtmp = 0f;
foreach (var h in harms)
{
sumtmp += h.Magnitude;
if (sumtmp / sum > 0.7f)
{
return ((decimal)(h.Period.TotalSeconds / 4), (decimal)(h.Period.TotalSeconds));
}
}
}
return (30m, 180m);
}
private async ValueTask<ValueAmplitudePosition> CheckHarmonicPosition((DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists) data, INewPrice message) private async ValueTask<ValueAmplitudePosition> CheckHarmonicPosition((DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists) data, INewPrice message)
{ {
var currentTime = message.IsHistoricalData ? message.Time : DateTime.UtcNow; var currentTime = message.IsHistoricalData ? message.Time : DateTime.UtcNow;
var position = ValueAmplitudePosition.None; var position = ValueAmplitudePosition.None;
var fft = await _tradeDataProvider.GetFFtResult(message.Figi); var fft = await _tradeDataProvider.GetFFtResult(message.Figi);
var step = message.IsHistoricalData ? 5 : 5; var fftFull = await _tradeDataProvider.GetFFtResult(message.Figi + "_full");
//var highFreq = await _tradeDataProvider.GetFFtResult(message.Figi + "_high_freq");
//var lowFreq = await _tradeDataProvider.GetFFtResult(message.Figi + "_low_freq");
var step = message.IsHistoricalData ? 40 : 5;
if (fft.IsEmpty || (currentTime - fft.LastTime).TotalSeconds > step) if (fft.IsEmpty || (currentTime - fft.LastTime).TotalSeconds > step)
{ {
if (data.isFullIntervalExists) if (data.isFullIntervalExists)
{ {
var interpolatedData = SignalProcessing.InterpolateData(data.timestamps, data.prices, TimeSpan.FromSeconds(5)); var interpolatedData = SignalProcessing.InterpolateData(data.timestamps, data.prices, TimeSpan.FromSeconds(5));
var fftFull = FFT.Analyze(interpolatedData.timestamps, interpolatedData.values, message.Figi+"_full", TimeSpan.FromSeconds(15), TimeSpan.FromHours(24)); fftFull = FFT.Analyze(interpolatedData.timestamps, interpolatedData.values, message.Figi+"_full", TimeSpan.FromSeconds(30), TimeSpan.FromHours(24));
fft = FFT.ReAnalyze(fftFull, message.Figi, TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(40));
fft = FFT.ReAnalyze(fftFull, message.Figi, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(60));
//highFreq = FFT.ReAnalyze(fftFull, message.Figi + "_low_freq", TimeSpan.FromMinutes(20), TimeSpan.FromMinutes(60));
//lowFreq = FFT.ReAnalyze(fftFull, message.Figi + "_high_freq", TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(20));
await _tradeDataProvider.SetFFtResult(fft); await _tradeDataProvider.SetFFtResult(fft);
await _tradeDataProvider.SetFFtResult(fftFull); await _tradeDataProvider.SetFFtResult(fftFull);
//await _tradeDataProvider.SetFFtResult(lowFreq);
//await _tradeDataProvider.SetFFtResult(highFreq);
} }
//var highFreqData = await GetData(message, TimeSpan.FromMinutes(120));
//if (highFreqData.isFullIntervalExists)
//{
// var interpolatehighFreqData = SignalProcessing.InterpolateData(data.timestamps, data.prices, TimeSpan.FromSeconds(5));
// highFreq = FFT.Analyze(interpolatehighFreqData.timestamps, interpolatehighFreqData.values, message.Figi + "_high_freq", TimeSpan.FromSeconds(20), TimeSpan.FromMinutes(120));
// await _tradeDataProvider.SetFFtResult(highFreq);
//}
} }
else
position = FFT.Check(fft, message.Time);
if (position == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.UpperThen30Decil)
{ {
position = FFT.Check(fft, message.Time); await LogPrice(message, "upper30percent", message.Value);
if (position == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.UpperThen30Decil)
{
await LogPrice(message, "upper30percent", message.Value);
}
if (position == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.LowerThenMediana)
{
await LogPrice(message, "lower30percent", message.Value);
}
} }
if (position == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.LowerThenMedianaGrowing)
{
await LogPrice(message, "lower30percent", message.Value);
}
//var hposition = FFT.CheckExtremums(highFreq, message.Time);
//if (hposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.UpperThen30Decil)
//{
// await LogPrice(message, "high_freq_high", message.Value);
//}
//if (hposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.LowerThen30Decil)
//{
// await LogPrice(message, "high_freq_low", message.Value);
//}
//var gposition = FFT.CheckSign(highFreq, message.Time);
//if (gposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.Growing)
//{
// await LogPrice(message, "growing", message.Value);
//}
//if (gposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.Falling)
//{
// await LogPrice(message, "falling", message.Value);
//}
//var lposition = FFT.CheckExtremums(lowFreq, message.Time);
//if (lposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.UpperThen30Decil)
//{
// await LogPrice(message, "low_freq_high", message.Value);
//}
//if (lposition == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.LowerThen30Decil)
//{
// await LogPrice(message, "low_freq_low", message.Value);
//}
return position; return position;
} }
@ -187,13 +259,9 @@ namespace KLHZ.Trader.Core.Exchange.Services
var changeMods = GetInitDict(0); var changeMods = GetInitDict(0);
try try
{ {
if (message.IsHistoricalData)
{
await _tradeDataProvider.AddData(message, TimeSpan.FromHours(6));
}
#region Ускорение обработки исторических данных при отладке #region Ускорение обработки исторических данных при отладке
if (message.Direction == 1) if (message.Direction == 1)
{ {
if (!pricesCache1.TryGetValue(message.Figi, out var list)) if (!pricesCache1.TryGetValue(message.Figi, out var list))
{ {
list = new List<INewPrice>(); list = new List<INewPrice>();
@ -223,6 +291,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
} }
if (message.Direction == 2) if (message.Direction == 2)
{ {
if (!pricesCache2.TryGetValue(message.Figi, out var list)) if (!pricesCache2.TryGetValue(message.Figi, out var list))
{ {
list = new List<INewPrice>(); list = new List<INewPrice>();
@ -251,6 +320,9 @@ namespace KLHZ.Trader.Core.Exchange.Services
} }
} }
#endregion #endregion
#region Подсчёт торгового баланса по сберу и IMOEXF #region Подсчёт торгового баланса по сберу и IMOEXF
@ -258,6 +330,11 @@ namespace KLHZ.Trader.Core.Exchange.Services
{ {
if (message.Direction == 1) if (message.Direction == 1)
{ {
await _tradeDataProvider.AddDataTo20SecondsWindowCache(message.Figi, "1", new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Value = message.Count
});
await _tradeDataProvider.AddDataTo5MinuteWindowCache(message.Figi, Constants._5minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue() await _tradeDataProvider.AddDataTo5MinuteWindowCache(message.Figi, Constants._5minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue()
{ {
Time = message.Time, Time = message.Time,
@ -281,7 +358,76 @@ namespace KLHZ.Trader.Core.Exchange.Services
Time = message.Time, Time = message.Time,
Value = (decimal)message.Count Value = (decimal)message.Count
}); });
await _tradeDataProvider.AddDataTo20SecondsWindowCache(message.Figi, "2", new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Value = message.Count
});
} }
var buys = await _tradeDataProvider.GetDataFrom20SecondsWindowCache(message.Figi, "1");
var sells = await _tradeDataProvider.GetDataFrom20SecondsWindowCache(message.Figi, "2");
var buysSpeed = buys.Sum(p => p.Value) / 20;
var sellsSpeed = sells.Sum(p => p.Value) / 20;
var orderBook = _tradeDataProvider.Orderbooks[message.Figi];
if (orderBook.Asks.Length>3 && orderBook.Bids.Length>3)
{
var asks = (decimal)(orderBook.Asks[0].Count + orderBook.Asks[1].Count + orderBook.Asks[2].Count);
//var asks = (decimal)(orderBook.Asks[0].Count + orderBook.Asks[1].Count + orderBook.Asks[2].Count + orderBook.Asks[3].Count);
var bids = (decimal)(orderBook.Bids[0].Count + orderBook.Bids[1].Count + orderBook.Bids[2].Count);
//var bids = (decimal)(orderBook.Bids[0].Count + orderBook.Bids[1].Count + orderBook.Bids[2].Count + orderBook.Bids[3].Count);
if (buysSpeed > 0 && sellsSpeed > 0)
{
await LogPrice(message, "speed_relation", (sellsSpeed / (sellsSpeed + buysSpeed)));
}
//var diff = buysSpeed - sellsSpeed;
//await LogPrice(message, "speed_diff", diff);
//await LogPrice(message, "stabling", (asks+bids)/(sellsSpeed+buysSpeed));
//if (buysSpeed > 0)
//{
// var asksLifetime = asks / buysSpeed;
// if (asksLifetime > 600) asksLifetime = 600;
// var asksLifetime2 = diff > 0?System.Math.Abs( asks / diff):0;
// await LogPrice(message, "asks_lifetime", asksLifetime);
// await LogPrice(message, "asks_lifetime2", asksLifetime2);
// await LogPrice(message, "asks_lifetime2", asksLifetime2);
// await LogPrice(message, "buys_speed", buysSpeed);
//}
//if (sellsSpeed > 0)
//{
// var bidsLifetime = bids / sellsSpeed;
// if (bidsLifetime > 600) bidsLifetime = 600;
// var bidsLifetime2 = diff < 0 ? System.Math.Abs(bids / diff) : 0;
// await LogPrice(message, "bids_lifetime", bidsLifetime);
// await LogPrice(message, "bids_lifetime2", System.Math.Abs(bidsLifetime2));
// await LogPrice(message, "sells_speed", sellsSpeed);
//}
//var buys5min = await _tradeDataProvider.GetDataFrom5MinuteWindowCache(message.Figi, Constants._5minBuyCacheKey);
//var sells5min = await _tradeDataProvider.GetDataFrom5MinuteWindowCache(message.Figi, Constants._5minSellCacheKey);
//var buysSpeed5min = buys5min.Sum(p => p.Value) / 300;
//var sellsSpeed5min = sells5min.Sum(p => p.Value) / 300;
//var diff5min = buysSpeed5min - sellsSpeed5min;
//await LogPrice(message, "speed_diff_5min", diff5min);
//var buys1min = await _tradeDataProvider.GetDataFrom1MinuteWindowCache(message.Figi, Constants._1minBuyCacheKey);
//var sells1min = await _tradeDataProvider.GetDataFrom1MinuteWindowCache(message.Figi, Constants._1minSellCacheKey);
//var buysSpeed1min = buys1min.Sum(p => p.Value) / 60;
//var sellsSpeed1min = sells1min.Sum(p => p.Value) / 60;
//var diff1min = buysSpeed1min - sellsSpeed1min;
//await LogPrice(message, "speed_diff_1min", diff1min);
}
} }
#endregion #endregion
if (_tradingInstrumentsFigis.Contains(message.Figi) && message.Figi == "FUTIMOEXF000" && message.Direction == 1) if (_tradingInstrumentsFigis.Contains(message.Figi) && message.Figi == "FUTIMOEXF000" && message.Direction == 1)
@ -363,6 +509,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
try try
{ {
if (message.Direction != 1) continue; if (message.Direction != 1) continue;
await _tradeDataProvider.AddData(message);
ProcessStops(message, currentTime); ProcessStops(message, currentTime);
var windowMaxSize = 2000; var windowMaxSize = 2000;
var data = await _tradeDataProvider.GetData(message.Figi, windowMaxSize); var data = await _tradeDataProvider.GetData(message.Figi, windowMaxSize);
@ -383,6 +530,35 @@ namespace KLHZ.Trader.Core.Exchange.Services
} }
} }
private async Task<ImmutableDictionary<TradingEvent, decimal>> GetSpeedResultantMods(INewPrice message)
{
var res = GetInitDict(1);
var buys5min = await _tradeDataProvider.GetDataFrom5MinuteWindowCache(message.Figi, Constants._5minBuyCacheKey);
var sells5min = await _tradeDataProvider.GetDataFrom5MinuteWindowCache(message.Figi, Constants._5minSellCacheKey);
var buysSpeed5min = buys5min.Sum(p => p.Value) / 300;
var sellsSpeed5min = sells5min.Sum(p => p.Value) / 300;
var diff5min = buysSpeed5min - sellsSpeed5min;
await LogPrice(message, "speed_diff_5min", diff5min);
if (diff5min < 0)
{
res[TradingEvent.UptrendStart] = Constants.BlockingCoefficient;
}
if (diff5min > 0)
{
res[TradingEvent.DowntrendStart] = Constants.BlockingCoefficient;
}
if (diff5min > 6)
{
res[TradingEvent.UptrendEnd] = Constants.BlockingCoefficient;
}
if (diff5min < -6)
{
res[TradingEvent.DowntrendEnd] = Constants.BlockingCoefficient;
}
return res.ToImmutableDictionary();
}
private async Task<Dictionary<TradingEvent, decimal>> GetWindowAverageStartData((DateTime[] timestamps, decimal[] prices) data, int smallWindow, int bigWindow, private async Task<Dictionary<TradingEvent, decimal>> GetWindowAverageStartData((DateTime[] timestamps, decimal[] prices) data, int smallWindow, int bigWindow,
INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullStep = 0m, decimal uptrendEndingDetectionMeanfullStep = 3m) INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullStep = 0m, decimal uptrendEndingDetectionMeanfullStep = 3m)
{ {
@ -520,15 +696,18 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt
{ {
return; return;
} }
var windows = await GetWindowsSizes(message);
//var resTask1 = GetWindowAverageStartData(data, 30, 180, message, windowMaxSize, -2m, 2m,3); //var resTask1 = GetWindowAverageStartData(data, 30, 180, message, windowMaxSize, -2m, 2m,3);
var resTask1 = GetWindowAverageStartData(data, 30, 180, message, windowMaxSize, 0m, 0.5m); var resTask1 = GetWindowAverageStartData(data, (int)windows.smallWindow, (int)windows.bigWindow, message, windowMaxSize, -0.5m, 0.5m);
//var resTask3 = GetWindowAverageStartData(data, 30, 180, message, windowMaxSize, 0, 0,0.7m); ////var resTask3 = GetWindowAverageStartData(data, 30, 180, message, windowMaxSize, 0, 0,0.7m);
var getFFTModsTask = GetFFTMods(message); var getFFTModsTask = GetFFTMods(message);
//var getAreasModsTask = GetAreasMods(data, message); var getLocalTrendsModsTask = GetLocalTrendsMods(data, message);
var getSellsDiffsModsTask = GetSellsDiffsMods(message); var getSellsDiffsModsTask = GetSellsDiffsMods(message);
var getTradingModeModsTask = GetTradingModeMods(message); var getTradingModeModsTask = GetTradingModeMods(message);
var getSpeedResultantModsTask = GetSpeedResultantMods(message);
await Task.WhenAll(resTask1, getFFTModsTask, getSellsDiffsModsTask, getTradingModeModsTask); await Task.WhenAll(resTask1, getFFTModsTask, getSellsDiffsModsTask, getTradingModeModsTask, getLocalTrendsModsTask, getSpeedResultantModsTask);
//var assetType = _tradeDataProvider.GetAssetTypeByFigi(message.Figi); //var assetType = _tradeDataProvider.GetAssetTypeByFigi(message.Figi);
//if (resTask1.Result[TradingEvent.UptrendStart] >= 1) //if (resTask1.Result[TradingEvent.UptrendStart] >= 1)
//{ //{
@ -540,10 +719,11 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt
//result = MergeResults(result, resTask2.Result.ToImmutableDictionary()); //result = MergeResults(result, resTask2.Result.ToImmutableDictionary());
//result = MergeResults(result, resTask3.Result.ToImmutableDictionary()); //result = MergeResults(result, resTask3.Result.ToImmutableDictionary());
result = MergeResultsMax(result, changeModeData); result = MergeResultsMax(result, changeModeData);
result = MergeResultsMax(result, getLocalTrendsModsTask.Result);
result = MergeResultsMult(result, getFFTModsTask.Result); result = MergeResultsMult(result, getFFTModsTask.Result);
//////result = MergeResults(result, getAreasModsTask.Result); result = MergeResultsMult(result, getSellsDiffsModsTask.Result);
//result = MergeResultsMult(result, getSellsDiffsModsTask.Result);
result = MergeResultsMult(result, getTradingModeModsTask.Result); result = MergeResultsMult(result, getTradingModeModsTask.Result);
result = MergeResultsMult(result, getSpeedResultantModsTask.Result);
if (result[TradingEvent.UptrendStart] >= Constants.UppingCoefficient if (result[TradingEvent.UptrendStart] >= Constants.UppingCoefficient
&& !LongOpeningStops.ContainsKey(message.Figi) && !LongOpeningStops.ContainsKey(message.Figi)
@ -561,12 +741,11 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt
await OpenPositions(accounts, message, PositionType.Long, stops.stopLoss, stops.takeProfit, 1); await OpenPositions(accounts, message, PositionType.Long, stops.stopLoss, stops.takeProfit, 1);
LongOpeningStops[message.Figi] = DateTime.UtcNow.AddMinutes(1); LongOpeningStops[message.Figi] = DateTime.UtcNow.AddMinutes(1);
} }
await LogDeclision(DeclisionTradeAction.OpenLong, message.Value, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message);
await LogDeclision(DeclisionTradeAction.OpenLong, message);
await LogDeclision(DeclisionTradeAction.ResetStopsLong, message.Value + stops.takeProfit, message.Time.AddMilliseconds(-RandomNumberGenerator.GetInt32(300, 1000)), message); await LogDeclision(DeclisionTradeAction.ResetStopsLong, message.Value + stops.takeProfit, message.Time.AddMilliseconds(-RandomNumberGenerator.GetInt32(300, 1000)), message);
await LogDeclision(DeclisionTradeAction.ResetStopsLong, message.Value - stops.stopLoss, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(300, 1000)), message); await LogDeclision(DeclisionTradeAction.ResetStopsLong, message.Value - stops.stopLoss, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(300, 1000)), message);
} }
if (result[TradingEvent.DowntrendStart] > Constants.UppingCoefficient if (result[TradingEvent.DowntrendStart] >= Constants.UppingCoefficient
&& !ShortOpeningStops.ContainsKey(message.Figi) && !ShortOpeningStops.ContainsKey(message.Figi)
&& state == ExchangeState.Open && state == ExchangeState.Open
) )
@ -583,11 +762,11 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt
ShortOpeningStops[message.Figi] = DateTime.UtcNow.AddMinutes(1); ShortOpeningStops[message.Figi] = DateTime.UtcNow.AddMinutes(1);
} }
await LogDeclision(DeclisionTradeAction.OpenShort, message); await LogDeclision(DeclisionTradeAction.OpenShort, message.Value, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message);
await LogDeclision(DeclisionTradeAction.ResetStopsShort, message.Value - stops.takeProfit, message.Time.AddMilliseconds(-RandomNumberGenerator.GetInt32(300, 1000)), message); await LogDeclision(DeclisionTradeAction.ResetStopsShort, message.Value - stops.takeProfit, message.Time.AddMilliseconds(-RandomNumberGenerator.GetInt32(300, 1000)), message);
await LogDeclision(DeclisionTradeAction.ResetStopsShort, message.Value + stops.stopLoss, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(300, 1000)), message); await LogDeclision(DeclisionTradeAction.ResetStopsShort, message.Value + stops.stopLoss, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(300, 1000)), message);
} }
if (result[TradingEvent.UptrendEnd] > Constants.UppingCoefficient) if (result[TradingEvent.UptrendEnd] >= Constants.UppingCoefficient)
{ {
if (!message.IsHistoricalData && BotModeSwitcher.CanSell()) if (!message.IsHistoricalData && BotModeSwitcher.CanSell())
{ {
@ -597,10 +776,11 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt
.ToArray(); .ToArray();
await ClosePositions(assetsForClose, message); await ClosePositions(assetsForClose, message);
} }
await LogDeclision(DeclisionTradeAction.CloseLong, message); await LogDeclision(DeclisionTradeAction.CloseLong, message.Value, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message);
} }
if (result[TradingEvent.DowntrendEnd] > Constants.UppingCoefficient) if (result[TradingEvent.DowntrendEnd] >= Constants.UppingCoefficient)
{ {
if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase()) if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase())
{ {
@ -610,7 +790,8 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt
.ToArray(); .ToArray();
await ClosePositions(assetsForClose, message); await ClosePositions(assetsForClose, message);
} }
await LogDeclision(DeclisionTradeAction.CloseShort, message); await LogDeclision(DeclisionTradeAction.CloseShort, message.Value, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message);
} }
} }
@ -720,19 +901,9 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt
private async Task<TradingMode> CalcTradingMode(string figi) private async Task<TradingMode> CalcTradingMode(string figi)
{ {
var res = TradingMode.None; var res = TradingMode.None;
var largeData = await _tradeDataProvider.GetData(figi, TimeSpan.FromMinutes(90)); var largeData = await _tradeDataProvider.GetData(figi, TimeSpan.FromMinutes(45));
var smallData = await _tradeDataProvider.GetData(figi, TimeSpan.FromMinutes(15)); var smallData = await _tradeDataProvider.GetData(figi, TimeSpan.FromMinutes(10));
if (!largeData.isFullIntervalExists && smallData.isFullIntervalExists)
{
largeData = await _tradeDataProvider.GetData(figi, TimeSpan.FromMinutes(45));
smallData = await _tradeDataProvider.GetData(figi, TimeSpan.FromMinutes(10));
}
if (!largeData.isFullIntervalExists && smallData.isFullIntervalExists)
{
largeData = await _tradeDataProvider.GetData(figi, TimeSpan.FromMinutes(15));
smallData = await _tradeDataProvider.GetData(figi, TimeSpan.FromMinutes(7));
}
if (largeData.isFullIntervalExists && smallData.isFullIntervalExists) if (largeData.isFullIntervalExists && smallData.isFullIntervalExists)
{ {
if (LocalTrends.TryCalcTrendDiff(largeData.timestamps, largeData.prices, out var largeDataRes) if (LocalTrends.TryCalcTrendDiff(largeData.timestamps, largeData.prices, out var largeDataRes)
@ -821,19 +992,19 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt
var res = GetInitDict(1); var res = GetInitDict(1);
var position = await CheckHarmonicPosition(message); var position = await CheckHarmonicPosition(message);
if (position == ValueAmplitudePosition.LowerThenMediana) if (position == ValueAmplitudePosition.LowerThenMedianaGrowing)
{ {
//res[TradingEvent.UptrendStart] = Constants.UppingCoefficient; //res[TradingEvent.UptrendStart] = Constants.UppingCoefficient;
res[TradingEvent.DowntrendEnd] = Constants.UppingCoefficient; res[TradingEvent.DowntrendEnd] = Constants.UppingCoefficient;
//res[TradingEvent.UptrendEnd] = Constants.LowingCoefficient; //res[TradingEvent.UptrendEnd] = Constants.LowingCoefficient;
res[TradingEvent.DowntrendStart] = Constants.BlockingCoefficient; res[TradingEvent.DowntrendStart] = Constants.LowingCoefficient;
} }
if (position == ValueAmplitudePosition.UpperThen30Decil) if (position == ValueAmplitudePosition.UpperThen30Decil)
{ {
res[TradingEvent.UptrendStart] = Constants.BlockingCoefficient; res[TradingEvent.UptrendStart] = Constants.LowingCoefficient;
//res[TradingEvent.DowntrendEnd] = Constants.LowingCoefficient; //res[TradingEvent.DowntrendEnd] = Constants.LowingCoefficient;
//res[TradingEvent.UptrendEnd] = Constants.UppingCoefficient; //res[TradingEvent.UptrendEnd] = Constants.UppingCoefficient;
res[TradingEvent.DowntrendStart] = Constants.UppingCoefficient; res[TradingEvent.DowntrendEnd] = Constants.UppingCoefficient;
} }
return res.ToImmutableDictionary(); return res.ToImmutableDictionary();
} }
@ -853,6 +1024,23 @@ INewPrice message, int windowMaxSize, decimal uptrendStartingDetectionMeanfullSt
} }
return res.ToImmutableDictionary(); return res.ToImmutableDictionary();
} }
private Task<ImmutableDictionary<TradingEvent, decimal>> GetLocalTrendsMods((DateTime[] timestamps, decimal[] prices) data, INewPrice message)
{
var res = GetInitDict(0);
if (LocalTrends.TryGetLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15), 0.5, out var localTrends))
{
if ((localTrends & TradingEvent.UptrendEnd) == TradingEvent.UptrendEnd)
{
res[TradingEvent.UptrendEnd] = Constants.PowerUppingCoefficient;
}
if ((localTrends & TradingEvent.DowntrendEnd) == TradingEvent.DowntrendEnd)
{
res[TradingEvent.DowntrendEnd] = Constants.PowerUppingCoefficient;
}
}
return Task.FromResult(res.ToImmutableDictionary());
}
private async Task<ImmutableDictionary<TradingEvent, decimal>> GetSellsDiffsMods(INewPrice message) private async Task<ImmutableDictionary<TradingEvent, decimal>> GetSellsDiffsMods(INewPrice message)
{ {

View File

@ -29,6 +29,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
private readonly ILogger<TraderDataProvider> _logger; private readonly ILogger<TraderDataProvider> _logger;
private readonly string[] _instrumentsFigis = []; private readonly string[] _instrumentsFigis = [];
public readonly ConcurrentDictionary<string, IOrderbook> Orderbooks = new();
private readonly ConcurrentDictionary<string, FFTAnalyzeResult> _fftResults = new(); private readonly ConcurrentDictionary<string, FFTAnalyzeResult> _fftResults = new();
private readonly ConcurrentDictionary<string, string> _tickersCache = new(); private readonly ConcurrentDictionary<string, string> _tickersCache = new();
private readonly ConcurrentDictionary<string, AssetType> _assetTypesCache = new(); private readonly ConcurrentDictionary<string, AssetType> _assetTypesCache = new();
@ -125,6 +126,16 @@ namespace KLHZ.Trader.Core.Exchange.Services
await _historyCash[figi].AddDataToTimeWindowCache(key, data, TimeWindowCacheType._1_Minute); await _historyCash[figi].AddDataToTimeWindowCache(key, data, TimeWindowCacheType._1_Minute);
} }
public async ValueTask AddDataTo20SecondsWindowCache(string figi, string key, CachedValue data)
{
if (!_historyCash.TryGetValue(figi, out var unit))
{
unit = new PriceHistoryCacheUnit2(figi);
_historyCash.TryAdd(figi, unit);
}
await _historyCash[figi].AddDataToTimeWindowCache(key, data, TimeWindowCacheType._20_Seconds);
}
public async ValueTask AddDataTo5MinuteWindowCache(string figi, string key, CachedValue data) public async ValueTask AddDataTo5MinuteWindowCache(string figi, string key, CachedValue data)
{ {
if (!_historyCash.TryGetValue(figi, out var unit)) if (!_historyCash.TryGetValue(figi, out var unit))
@ -135,6 +146,15 @@ namespace KLHZ.Trader.Core.Exchange.Services
await _historyCash[figi].AddDataToTimeWindowCache(key, data, TimeWindowCacheType._5_Minutes); await _historyCash[figi].AddDataToTimeWindowCache(key, data, TimeWindowCacheType._5_Minutes);
} }
public ValueTask<CachedValue[]> GetDataFrom20SecondsWindowCache(string figi, string key)
{
if (_historyCash.TryGetValue(figi, out var cahcheItem))
{
return cahcheItem.GetDataFromTimeWindowCache(key, TimeWindowCacheType._20_Seconds);
}
return ValueTask.FromResult(Array.Empty<CachedValue>());
}
public ValueTask<CachedValue[]> GetDataFrom1MinuteWindowCache(string figi, string key) public ValueTask<CachedValue[]> GetDataFrom1MinuteWindowCache(string figi, string key)
{ {
if (_historyCash.TryGetValue(figi, out var cahcheItem)) if (_historyCash.TryGetValue(figi, out var cahcheItem))
@ -160,7 +180,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
unit = new PriceHistoryCacheUnit2(orderbook.Figi); unit = new PriceHistoryCacheUnit2(orderbook.Figi);
_historyCash.TryAdd(orderbook.Figi, unit); _historyCash.TryAdd(orderbook.Figi, unit);
} }
Orderbooks[orderbook.Figi] = orderbook;
await unit.AddOrderbook(orderbook); await unit.AddOrderbook(orderbook);
} }

View File

@ -1,12 +1,17 @@
using Grpc.Core;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos; using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
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.DataLayer.Entities.Prices; using KLHZ.Trader.Core.DataLayer.Entities.Prices;
using KLHZ.Trader.Core.Exchange.Services; using KLHZ.Trader.Core.Exchange.Services;
using KLHZ.Trader.Core.Math.Declisions.Utils; using KLHZ.Trader.Core.Math.Declisions.Utils;
using KLHZ.Trader.Service.Models;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Telegram.Bot.Types;
using Tinkoff.InvestApi.V1;
namespace KLHZ.Trader.Service.Controllers namespace KLHZ.Trader.Service.Controllers
{ {
@ -39,25 +44,184 @@ namespace KLHZ.Trader.Service.Controllers
//var time2 = DateTime.UtcNow.AddMinutes(18); //var time2 = DateTime.UtcNow.AddMinutes(18);
using var context1 = await _dbContextFactory.CreateDbContextAsync(); using var context1 = await _dbContextFactory.CreateDbContextAsync();
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var data = await context1.PriceChanges
.Where(c => (c.Figi == figi1 || c.Figi == figi2) && c.Time >= time1)
.OrderBy(c => c.Time)
.Select(c => new NewPriceMessage()
{
Figi = c.Figi,
Ticker = c.Ticker,
Time = c.Time,
Value = c.Value,
IsHistoricalData = true,
Direction = c.Direction,
Count = c.Count,
})
.ToArrayAsync();
foreach (var mess in data)
while (time1 < DateTime.UtcNow)
{ {
await _dataBus.Broadcast(mess); var data = new List<TimeSeriesData>();
var data2 = new List<TimeSeriesData>();
var time2 = time1.AddHours(1);
var orderbooks = await context1.OrderbookItems
.Where(oi => (oi.Figi == figi1 || oi.Figi == figi2) && oi.Time >= time1 && oi.Time<time2)
.OrderBy(c => c.Time)
.ToArrayAsync();
var prices = await context1.PriceChanges
.Where(c => (c.Figi == figi1 || c.Figi == figi2) && c.Time >= time1 && c.Time < time2)
.OrderBy(c => c.Time)
.Select(c => new NewPriceMessage()
{
Figi = c.Figi,
Ticker = c.Ticker,
Time = c.Time,
Value = c.Value,
IsHistoricalData = true,
Direction = c.Direction,
Count = c.Count,
})
.ToArrayAsync();
var lookupImoexf = orderbooks.Where(o=>o.Figi== "FUTIMOEXF000").ToLookup(o => o.Time);
var lookupSber = orderbooks.Where(o=>o.Figi== "BBG004730N88").ToLookup(o => o.Time);
foreach (var item in lookupImoexf)
{
var asks = item
.Where(i => i.ItemType == Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.Ask)
.OrderBy(o => o.Price)
.ToList();
var bids = item
.Where(i => i.ItemType == Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.Bid)
.OrderByDescending(o => o.Price)
.ToList();
var forRemove = new List<OrderbookItem>();
if (asks.Count > 4 || bids.Count > 4)
{
}
foreach (var bid in bids)
{
var bids2 = bids.Where(b=>b.Price==bid.Price).ToList();
var summCount = bids2.Sum(b => b.Count);
var b = bids2.First();
b.Count = summCount;
bids2.Remove(b);
forRemove.AddRange(bids2);
}
foreach (var ask in asks)
{
var asks2 = asks.Where(b => b.Price == ask.Price).ToList();
var summCount = asks2.Sum(b => b.Count);
var b = asks2.First();
b.Count = summCount;
asks2.Remove(b);
forRemove.AddRange(asks2);
}
asks.RemoveAll(a => forRemove.Contains(a));
bids.RemoveAll(a => forRemove.Contains(a));
var orderbook = new NewOrderbookMessage() { Figi = "FUTIMOEXF000", Ticker = "IMOEXF", Asks = asks.ToArray(), Bids = bids.ToArray(), AsksCount = asks.Sum(a => a.Count), BidsCount = bids.Sum(a => a.Count), Time = item.Key };
var wrapper = new TimeSeriesData()
{
Figi = orderbook.Figi,
Orderbook = orderbook,
Time = item.Key,
};
data.Add(wrapper);
}
foreach (var item in lookupSber)
{
var asks = item
.Where(i => i.ItemType == Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.Ask)
.OrderBy(o => o.Price)
.ToList();
var bids = item
.Where(i => i.ItemType == Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.Bid)
.OrderByDescending(o => o.Price)
.ToList();
var forRemove = new List<OrderbookItem>();
if (asks.Count > 4 || bids.Count > 4)
{
}
foreach (var bid in bids)
{
var bids2 = bids.Where(b => b.Price == bid.Price).ToList();
var summCount = bids2.Sum(b => b.Count);
var b = bids2.First();
b.Count = summCount;
bids2.Remove(b);
forRemove.AddRange(bids2);
}
foreach (var ask in asks)
{
var asks2 = asks.Where(b => b.Price == ask.Price).ToList();
var summCount = asks2.Sum(b => b.Count);
var b = asks2.First();
b.Count = summCount;
asks2.Remove(b);
forRemove.AddRange(asks2);
}
asks.RemoveAll(a => forRemove.Contains(a));
bids.RemoveAll(a => forRemove.Contains(a));
var orderbook = new NewOrderbookMessage() { Figi = "BBG004730N88", Ticker = "SBER", Asks = asks.ToArray(), Bids = bids.ToArray(), AsksCount = asks.Sum(a => a.Count), BidsCount = bids.Sum(a => a.Count), Time = item.Key };
var wrapper = new TimeSeriesData()
{
Figi = orderbook.Figi,
Orderbook = orderbook,
Time = item.Key,
};
data.Add(wrapper);
}
time1 = time2;
foreach (var price in prices)
{
var wrapper = new TimeSeriesData()
{
Figi = price.Figi,
NewPrice = price,
Time = price.Time,
};
data.Add(wrapper);
}
data = data.OrderBy(d => d.Time).ToList();
for(int i = 0; i < data.Count; i++)
{
if (data[i].NewPrice != null && i>0)
{
for (int i1=i-1; i1 >=0; i1--)
{
var ob = data[i1].Orderbook;
if (data[i1].Figi == data[i].Figi && ob != null)
{
var d =new TimeSeriesData()
{
Figi = ob.Figi,
Orderbook = ob,
Time = data[i].Time,
NewPrice = data[i].NewPrice,
};
data2.Add(d);
break;
}
}
}
}
foreach (var mess in data2)
{
if (mess.Orderbook != null)
{
await _traderDataProvider.AddOrderbook(mess.Orderbook);
}
if (mess.NewPrice != null)
{
//await _traderDataProvider.AddData(mess.NewPrice, TimeSpan.FromHours(6));
await _dataBus.Broadcast(mess.NewPrice);
}
}
data.Clear();
data2.Clear();
} }
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -0,0 +1,12 @@
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
namespace KLHZ.Trader.Service.Models
{
public class TimeSeriesData
{
public required string Figi { get; set;}
public DateTime Time { get; set; }
public INewPrice? NewPrice { get; set; }
public IOrderbook? Orderbook { get; set; }
}
}