Применение соотношения продаж и покупок сбера

dev
vlad zverzhkhovskiy 2025-09-16 17:59:23 +03:00
parent 2b9f7cf332
commit efb6f8b64f
10 changed files with 130 additions and 56 deletions

View File

@ -4,7 +4,7 @@
{ {
None = 0, None = 0,
_1_Minute = 1, _1_Minute = 1,
_2_Minutes = 2, _5_Minutes = 2,
_15_Minutes = 15, _15_Minutes = 15,
} }
} }

View File

@ -10,7 +10,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Dtos.FFT.Enums
{ {
None = 0, None = 0,
Middle = 1, Middle = 1,
UpperThen20Decil = 10, UpperThen30Decil = 10,
LowerThenMediana = -50, LowerThenMediana = -50,
} }
} }

View File

@ -13,7 +13,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Dtos.FFT
public decimal Max { get; init; } public decimal Max { get; init; }
public decimal Min { get; init; } public decimal Min { get; init; }
public decimal Mediana { get; init; } public decimal Mediana { get; init; }
public decimal Upper20Decil { get; init; } public decimal Upper30Decil { get; init; }
public decimal Lower20Decil { get; init; } public decimal Lower20Decil { get; init; }
public DateTime LastTime { get; init; } public DateTime LastTime { get; init; }
public DateTime StartTime { get; init; } public DateTime StartTime { get; init; }

View File

@ -43,9 +43,9 @@ namespace KLHZ.Trader.Core.Math.Declisions.Dtos
{ {
switch (type) switch (type)
{ {
case TimeWindowCacheType._2_Minutes: case TimeWindowCacheType._5_Minutes:
{ {
return TimeSpan.FromMinutes(2); return TimeSpan.FromMinutes(5);
} }
case TimeWindowCacheType._15_Minutes: case TimeWindowCacheType._15_Minutes:
{ {

View File

@ -51,7 +51,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
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> _1_minTimeWindows = new(); private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _1_minTimeWindows = new();
private readonly ConcurrentDictionary<string, TimeWindowCacheItem> _2_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();
private int _length = 0; private int _length = 0;
@ -85,9 +85,9 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
{ {
switch (timeWindowCacheType) switch (timeWindowCacheType)
{ {
case TimeWindowCacheType._2_Minutes: case TimeWindowCacheType._5_Minutes:
{ {
return _2_minTimeWindows; return _5_minTimeWindows;
} }
case TimeWindowCacheType._15_Minutes: case TimeWindowCacheType._15_Minutes:
{ {

View File

@ -11,9 +11,9 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
{ {
var value = (decimal)CalcAmplitude(fftData.Harmonics, fftData.StartTime, timestamp); var value = (decimal)CalcAmplitude(fftData.Harmonics, fftData.StartTime, timestamp);
var value2 = (decimal)CalcExtremum(fftData.Harmonics, fftData.StartTime, timestamp); var value2 = (decimal)CalcExtremum(fftData.Harmonics, fftData.StartTime, timestamp);
if (value > fftData.Upper20Decil) if (value > fftData.Upper30Decil)
{ {
return ValueAmplitudePosition.UpperThen20Decil; return ValueAmplitudePosition.UpperThen30Decil;
} }
else if (value < fftData.Mediana) else if (value < fftData.Mediana)
{ {
@ -47,7 +47,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
LastTime = timestamps[timestamps.Length - 1], LastTime = timestamps[timestamps.Length - 1],
StartTime = startTime, StartTime = startTime,
Mediana = newValues[newValues.Length / 2], Mediana = newValues[newValues.Length / 2],
Upper20Decil = newValues[(int)(newValues.Length * 0.8)], Upper30Decil = newValues[(int)(newValues.Length * 0.7)],
Lower20Decil = newValues[(int)(newValues.Length * 0.2)], Lower20Decil = newValues[(int)(newValues.Length * 0.2)],
Max = newValues.Max(), Max = newValues.Max(),
Min = newValues.Min(), Min = newValues.Min(),

View File

@ -3,6 +3,8 @@
internal static class Constants internal static class Constants
{ {
internal const string _1minCacheKey = "1min"; internal const string _1minCacheKey = "1min";
internal const string _1minSellCacheKey = "1min_sell";
internal const string _1minBuyCacheKey = "1min_buy";
internal const string BigWindowCrossingAverageProcessor = "Trader_big"; internal const string BigWindowCrossingAverageProcessor = "Trader_big";
internal const string SmallWindowCrossingAverageProcessor = "Trader_small"; internal const string SmallWindowCrossingAverageProcessor = "Trader_small";
internal const string AreasRelationProcessor = "balancescalc30min"; internal const string AreasRelationProcessor = "balancescalc30min";

View File

@ -32,6 +32,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
private readonly TraderDataProvider _tradeDataProvider; private readonly TraderDataProvider _tradeDataProvider;
private readonly ILogger<Trader> _logger; private readonly ILogger<Trader> _logger;
private readonly ConcurrentDictionary<string, DateTime> LongOpeningStops = new(); private readonly ConcurrentDictionary<string, DateTime> LongOpeningStops = new();
private readonly ConcurrentDictionary<string, DateTime> LongClosingStops = new();
private readonly ConcurrentDictionary<string, DateTime> ShortClosingStops = new(); private readonly ConcurrentDictionary<string, DateTime> ShortClosingStops = new();
private readonly ConcurrentDictionary<string, InstrumentSettings> Leverages = new(); private readonly ConcurrentDictionary<string, InstrumentSettings> Leverages = new();
@ -105,9 +106,8 @@ namespace KLHZ.Trader.Core.Exchange.Services
else else
{ {
position = FFT.Check(fft, message.Time); position = FFT.Check(fft, message.Time);
if (position == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.UpperThen20Decil) if (position == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.UpperThen30Decil)
{ {
await LogPrice(message, "upper10percent", message.Value);
await LogPrice(message, "upper30percent", message.Value); await LogPrice(message, "upper30percent", message.Value);
} }
if (position == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.LowerThenMediana) if (position == Math.Declisions.Dtos.FFT.Enums.ValueAmplitudePosition.LowerThenMediana)
@ -155,6 +155,36 @@ namespace KLHZ.Trader.Core.Exchange.Services
list.Clear(); list.Clear();
} }
} }
if (message.Figi == "BBG004730N88")
{
if (message.Direction == 1)
{
await _tradeDataProvider.AddDataTo5MinuteWindowCache(message.Figi, Constants._1minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Value = (decimal)message.Count
});
}
if (message.Direction == 2)
{
await _tradeDataProvider.AddDataTo5MinuteWindowCache(message.Figi, Constants._1minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Value = (decimal)message.Count
});
}
var sberSells = await _tradeDataProvider.GetDataFrom5MinuteWindowCache("BBG004730N88", Constants._1minSellCacheKey);
var sberBuys = await _tradeDataProvider.GetDataFrom5MinuteWindowCache("BBG004730N88", Constants._1minBuyCacheKey);
var sells = sberSells.Sum(s => s.Value);
var buys = sberBuys.Sum(s => s.Value);
var su = sells + buys;
if (su != 0)
{
await LogPrice(message, "sellsbuysbalance", (sells / su - 0.5m)*2);
}
}
if (_tradingInstrumentsFigis.Contains(message.Figi)) if (_tradingInstrumentsFigis.Contains(message.Figi))
{ {
var currentTime = message.IsHistoricalData ? message.Time : DateTime.UtcNow; var currentTime = message.IsHistoricalData ? message.Time : DateTime.UtcNow;
@ -314,6 +344,14 @@ namespace KLHZ.Trader.Core.Exchange.Services
return; return;
} }
var sberSells = await _tradeDataProvider.GetDataFrom5MinuteWindowCache("BBG004730N88", Constants._1minSellCacheKey);
var sberBuys = await _tradeDataProvider.GetDataFrom5MinuteWindowCache("BBG004730N88", Constants._1minBuyCacheKey);
var sells = sberSells.Sum(s => s.Value);
var buys = sberBuys.Sum(s => s.Value);
var su = sells + buys;
var dsell = (sells / su - 0.5m) * 2;
var mavTask = CheckByWindowAverageMean(data, message, windowMaxSize); var mavTask = CheckByWindowAverageMean(data, message, windowMaxSize);
var mavTaskShorts = CheckByWindowAverageMeanForShotrs(data, message, windowMaxSize); var mavTaskShorts = CheckByWindowAverageMeanForShotrs(data, message, windowMaxSize);
var ltTask = CheckByLocalTrends(data, message, windowMaxSize); var ltTask = CheckByLocalTrends(data, message, windowMaxSize);
@ -328,7 +366,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
if ((res & TradingEvent.UptrendStart) == TradingEvent.UptrendStart if ((res & TradingEvent.UptrendStart) == TradingEvent.UptrendStart
&& !LongOpeningStops.ContainsKey(message.Figi) && !LongOpeningStops.ContainsKey(message.Figi)
&& trendTask.Result.HasValue && trendTask.Result.HasValue
&& System.Math.Abs(trendTask.Result.Value)<6 && trendTask.Result.Value > -5
&& state == ExchangeState.Open && state == ExchangeState.Open
&& areasTask.Result.HasValue && areasTask.Result.HasValue
&& (areasTask.Result.Value >= 20 && areasTask.Result.Value < 75) && (areasTask.Result.Value >= 20 && areasTask.Result.Value < 75)
@ -371,7 +409,9 @@ namespace KLHZ.Trader.Core.Exchange.Services
await LogDeclision(DeclisionTradeAction.OpenLong, message); await LogDeclision(DeclisionTradeAction.OpenLong, message);
} }
if ((res & TradingEvent.UptrendEnd) == TradingEvent.UptrendEnd && positionTask.Result != ValueAmplitudePosition.LowerThenMediana) if ((res & TradingEvent.UptrendEnd) == TradingEvent.UptrendEnd)
{
if (dsell < 0.1m)
{ {
if (!message.IsHistoricalData && BotModeSwitcher.CanSell()) if (!message.IsHistoricalData && BotModeSwitcher.CanSell())
{ {
@ -394,10 +434,10 @@ namespace KLHZ.Trader.Core.Exchange.Services
profit = TradingCalculator.CaclProfit(asset.BoughtPrice, message.Value, profit = TradingCalculator.CaclProfit(asset.BoughtPrice, message.Value,
GetComission(assetType), GetLeverage(message.Figi, asset.Count < 0), asset.Count < 0); GetComission(assetType), GetLeverage(message.Figi, asset.Count < 0), asset.Count < 0);
} }
var stoppingKey = message.Figi + asset.AccountId;
if (profit > 0) if (profit > 0)
{ {
//ClosingStops[stoppingKey] = DateTime.UtcNow.AddSeconds(30); LongClosingStops[message.Figi] = message.Time.AddSeconds(30);
var command = new TradeCommand() var command = new TradeCommand()
{ {
AccountId = asset.AccountId, AccountId = asset.AccountId,
@ -421,9 +461,11 @@ namespace KLHZ.Trader.Core.Exchange.Services
await LogDeclision(DeclisionTradeAction.CloseLong, message); await LogDeclision(DeclisionTradeAction.CloseLong, message);
} }
}
if ((mavTaskShorts.Result & TradingEvent.UptrendEnd) == TradingEvent.UptrendEnd) if ((mavTaskShorts.Result & TradingEvent.UptrendEnd) == TradingEvent.UptrendEnd)
{ {
if (trendTask.Result.HasValue && trendTask.Result.Value < -3) if (trendTask.Result.HasValue && trendTask.Result.Value < -4)
{ {
if (!message.IsHistoricalData) if (!message.IsHistoricalData)
{ {
@ -541,6 +583,13 @@ namespace KLHZ.Trader.Core.Exchange.Services
ShortClosingStops.TryRemove(message.Figi, out _); ShortClosingStops.TryRemove(message.Figi, out _);
} }
} }
if (LongClosingStops.TryGetValue(message.Figi, out var dt3))
{
if (dt3 < currentTime)
{
LongClosingStops.TryRemove(message.Figi, out _);
}
}
} }
private async Task LogPrice(INewPrice message, string processor, decimal value) private async Task LogPrice(INewPrice message, string processor, decimal value)

View File

@ -126,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 AddDataTo5MinuteWindowCache(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._5_Minutes);
}
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))
@ -135,6 +145,15 @@ namespace KLHZ.Trader.Core.Exchange.Services
return ValueTask.FromResult(Array.Empty<CachedValue>()); return ValueTask.FromResult(Array.Empty<CachedValue>());
} }
public ValueTask<CachedValue[]> GetDataFrom5MinuteWindowCache(string figi, string key)
{
if (_historyCash.TryGetValue(figi, out var cahcheItem))
{
return cahcheItem.GetDataFromTimeWindowCache(key, TimeWindowCacheType._5_Minutes);
}
return ValueTask.FromResult(Array.Empty<CachedValue>());
}
public async ValueTask AddOrderbook(IOrderbook orderbook) public async ValueTask AddOrderbook(IOrderbook orderbook)
{ {
if (!_historyCash.TryGetValue(orderbook.Figi, out var unit)) if (!_historyCash.TryGetValue(orderbook.Figi, out var unit))

View File

@ -26,20 +26,24 @@ namespace KLHZ.Trader.Service.Controllers
} }
[HttpGet] [HttpGet]
public async Task Run(string figi) public async Task Run()
{ {
try try
{ {
var figi1 = "FUTIMOEXF000";
//var figi1 = "BBG004730N88";
var figi2 = "BBG004730N88";
//var figi2 = "FUTIMOEXF000";
var time1 = DateTime.UtcNow.AddDays(-16.5); var time1 = DateTime.UtcNow.AddDays(-16.5);
//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 var data = await context1.PriceChanges
.Where(c => c.Figi == figi && c.Time >= time1) .Where(c => (c.Figi == figi1 || c.Figi == figi2) && c.Time >= time1)
.OrderBy(c => c.Time) .OrderBy(c => c.Time)
.Select(c => new NewPriceMessage() .Select(c => new NewPriceMessage()
{ {
Figi = figi, Figi = c.Figi,
Ticker = c.Ticker, Ticker = c.Ticker,
Time = c.Time, Time = c.Time,
Value = c.Value, Value = c.Value,