доработка стратегия входов

dev
vlad zverzhkhovskiy 2025-10-07 18:40:30 +03:00
parent 06cf8ddc19
commit e57d67d5db
5 changed files with 140 additions and 222 deletions

View File

@ -1,15 +1,26 @@
namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos
{
public class CachedValue
public class CachedValue : ITradeDataItem
{
public DateTime Time { get; init; }
public long Count { get; init; }
public decimal Price { get; init; }
public decimal Value { get; init; }
public decimal Value2 { get; init; }
//public bool IsHistoricalData { get; init; }
//public required string Figi { get; init; } = string.Empty;
//public required string Ticker { get; init; } = string.Empty;
//public int Direction { get; init; }
public CachedValue()
{
Figi = string.Empty;
Ticker = string.Empty;
Direction = 0;
IsHistoricalData = false;
}
public bool IsHistoricalData { get; init; }
public required string Figi { get; init; }
public required string Ticker { get; init; }
public int Direction { get; init; }
}
}

View File

@ -103,6 +103,20 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
return ValueTask.FromResult(res.ToArray());
}
public ValueTask<(DateTime time, decimal price)> GetLastValues(string? key = null)
{
key = key ?? string.Empty;
lock (_locker)
{
if (_items.TryGetValue(key, out var list) && list.First != null && list.Last != null)
{
return ValueTask.FromResult((list.Last.Value.Time, list.Last.Value.Price));
}
}
return ValueTask.FromResult((DateTime.MinValue, 0m));
}
public ValueTask<ITradeDataItem[]> GetData(TimeSpan shift, TimeSpan period, string? key = null, Func<ITradeDataItem, bool>? selector = null)
{
key = key ?? string.Empty;

View File

@ -1,16 +1,16 @@
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
namespace KLHZ.Trader.Core.Math.Declisions.Utils
{
public static class Statistics
{
public static decimal Mean(this CachedValue[] values)
public static decimal MeanCount(this ITradeDataItem[] values)
{
return values.Sum(x => x.Count) / values.Length;
}
public static decimal Mean2(this CachedValue[] values)
public static decimal MeanPrice(this ITradeDataItem[] values)
{
return values.Sum(x => x.Price) / values.Length;
}
@ -54,8 +54,8 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
}
}
public static bool TryCalcTimeWindowsDiff(this CachedValue[] values, TimeSpan boundLeft, TimeSpan boundRight,
Func<CachedValue, decimal> fieldSelector, bool calcMean, out decimal result)
public static bool TryCalcTimeWindowsDiff(this ITradeDataItem[] values, TimeSpan boundLeft, TimeSpan boundRight,
Func<ITradeDataItem, decimal> fieldSelector, bool calcMean, out decimal result)
{
result = default;
if (values.Length > 1)
@ -82,8 +82,30 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
}
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;
public static bool TryCalcPirsonCorrelation(this CachedValue[] values, TimeSpan period, out decimal result)
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())
@ -92,8 +114,8 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
values = values.Where(b => b.Time >= shiftTimeDiffs1).ToArray();
if (values.Any())
{
var tradevolume_diffMean = values.Mean();
var dprice_diffMean = values.Mean2();
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));

View File

@ -35,6 +35,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
private readonly ConcurrentDictionary<string, TradingMode> TradingModes = new();
private readonly ConcurrentDictionary<string, decimal> DPirsonValues = new();
private readonly ConcurrentDictionary<string, decimal> DPricesValues = new();
private readonly Channel<ITradeDataItem> _pricesChannel = Channel.CreateUnbounded<ITradeDataItem>();
private readonly Channel<ITradeCommand> _commands = Channel.CreateUnbounded<ITradeCommand>();
@ -135,117 +136,77 @@ namespace KLHZ.Trader.Core.Exchange.Services
#region Добавление данных в кеши.
if (message.Figi == "BBG004730N88" || message.Figi == "FUTIMOEXF000")
{
if (message.Direction == 1)
{
await _tradeDataProvider.AddDataTo20SecondsWindowCache(message.Figi, "1", new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Count = message.Count,
Price = message.Price,
});
await _tradeDataProvider.AddDataTo5MinuteWindowCache(message.Figi, Constants._5minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Count = message.Count,
Price = message.Price,
});
await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, Constants._15minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Count = message.Count,
Price = message.Price,
});
await _tradeDataProvider.AddDataTo1MinuteWindowCache(message.Figi, Constants._1minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Count = message.Count,
Price = message.Price,
});
}
if (message.Direction == 2)
{
await _tradeDataProvider.AddDataTo5MinuteWindowCache(message.Figi, Constants._5minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Count = message.Count,
Price = message.Price,
});
await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, Constants._15minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Count = message.Count,
Price = message.Price,
});
await _tradeDataProvider.AddDataTo1MinuteWindowCache(message.Figi, Constants._1minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Count = message.Count,
Price = message.Price,
});
await _tradeDataProvider.AddDataTo20SecondsWindowCache(message.Figi, "2", new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Count = message.Count,
Price = message.Price,
});
}
await _tradeDataProvider.AddData(message);
}
#endregion
if (_exchangeConfig.TradingInstrumentsFigis.Contains(message.Figi) && message.Direction == 1)
{
var smallWindow = TimeSpan.FromSeconds(120);
var bigWindow = TimeSpan.FromSeconds(240);
var meanWindow = TimeSpan.FromSeconds(240);
var _15minCacheSize = TimeSpan.FromSeconds(400);
var smallWindow = TimeSpan.FromSeconds(180);
var bigWindow = TimeSpan.FromSeconds(360);
var meanWindow = TimeSpan.FromSeconds(360);
var currentTime = message.IsHistoricalData ? message.Time : DateTime.UtcNow;
var buys = await _tradeDataProvider.GetDataFrom15MinuteWindowCache(message.Figi, Constants._15minBuyCacheKey);
var sells = await _tradeDataProvider.GetDataFrom15MinuteWindowCache(message.Figi, Constants._15minSellCacheKey);
var trades = buys.ToList();
trades.AddRange(sells);
var trades2 = trades.OrderBy(t => t.Time).ToArray();
if (trades2.TryCalcTimeWindowsDiff(bigWindow, smallWindow, v => v.Count, false, out var tradesDiff)
&& buys.TryCalcTimeWindowsDiff(bigWindow, smallWindow, v => v.Price, true, out var pricesDiff))
var buys = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, _15minCacheSize, selector: (i) => i.Direction == 1);
var trades = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, _15minCacheSize);
if (trades.TryCalcTimeWindowsDiff(bigWindow, smallWindow, v => v.Count, false, out var tradesDiff)
&& buys.TryCalcTimeDiff(bigWindow, smallWindow, v => v.Price, true, out var pricesDiff))
{
await _tradeDataProvider.LogPrice(message, "privcesDiff", pricesDiff);
await _tradeDataProvider.LogPrice(message, "tradevolume_diff", tradesDiff);
await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, "5min_diff", new Contracts.Declisions.Dtos.CachedValue()
await _tradeDataProvider.AddData(message.Figi, "5min_diff", new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Value2 = tradesDiff,
Value = pricesDiff,
Figi = message.Figi,
Ticker = message.Ticker,
});
var diffs = await _tradeDataProvider.GetDataFrom15MinuteWindowCache(message.Figi, "5min_diff");
if (diffs.TryCalcTimeWindowsDiff(bigWindow, smallWindow, (c) => c.Value, true, out var resdp)
&& diffs.TryCalcTimeWindowsDiff(bigWindow, smallWindow, (c) => c.Value2, true, out var resv))
if (DPricesValues.TryGetValue(message.Figi, out var olddPrice))
{
await _tradeDataProvider.LogPrice(message, "privcesDiffDiff", (decimal)resdp);
await _tradeDataProvider.LogPrice(message, "tradevolume_diff_diff", (decimal)resv);
if (diffs.TryCalcPirsonCorrelation(meanWindow, out var pirson))
if (olddPrice < 0m && pricesDiff >0)
{
await _tradeDataProvider.LogPrice(message, "diffs_pirson", (decimal)pirson);
await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, "diffs_pirson", new Contracts.Declisions.Dtos.CachedValue()
//await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_long_in", message.Price);
}
if (olddPrice > 0m && pricesDiff < 0m)
{
//await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_short_in", message.Price);
}
}
DPricesValues[message.Figi] = pricesDiff;
var diffs = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, _15minCacheSize, "5min_diff");
if (diffs.TryCalcPirsonCorrelation(meanWindow, out var pirson))
{
var res = pirson;
await _tradeDataProvider.LogPrice(message, "diffs_pirson", (decimal)pirson);
await _tradeDataProvider.AddData(message.Figi, "diffs_pirson", new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Value = (decimal)pirson,
Figi = message.Figi,
Ticker = message.Ticker,
});
if (DPirsonValues.TryGetValue(message.Figi, out var olddpirs))
{
if (olddpirs < -0.3m && res > -0.3m && pricesDiff>0 && (tradesDiff > 0))
{
Time = message.Time,
Value = (decimal)pirson,
});
var diffs_pirson = await _tradeDataProvider.GetDataFrom15MinuteWindowCache(message.Figi, "diffs_pirson");
if (diffs_pirson.TryCalcTimeWindowsDiff(bigWindow, smallWindow, (c) => c.Value, true, out var res))
await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_long_in", message.Price);
}
if (olddpirs > 0.7m && res < 0.7m)
{
if (DPirsonValues.TryGetValue(message.Figi, out var olddpirs))
{
if (olddpirs < 0.5m && res > 0.5m)
{
await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point0.5", message.Price);
}
}
DPirsonValues[message.Figi] = res;
// await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_long_out", message.Price);
}
if (olddpirs > 0.3m && res < 0.3m && pricesDiff < 0 && (tradesDiff > 0))
{
await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_short_in", message.Price);
}
if (olddpirs < -0.7m && res > -0.7m)
{
// await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_short_out", message.Price);
}
}
DPirsonValues[message.Figi] = res;
}
}
}

View File

@ -1,14 +1,10 @@
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
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.DataLayer;
using KLHZ.Trader.Core.DataLayer.Entities.Declisions;
using KLHZ.Trader.Core.DataLayer.Entities.Declisions.Enums;
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
using KLHZ.Trader.Core.Exchange.Models.Configs;
using KLHZ.Trader.Core.Math.Declisions.Dtos.FFT;
using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
@ -22,7 +18,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
{
public class TraderDataProvider
{
private readonly ConcurrentDictionary<string, IPriceHistoryCacheUnit> _historyCash = new();
private readonly ConcurrentDictionary<string, PriceHistoryCacheUnit3> _historyCash3 = new();
private readonly InvestApiClient _investApiClient;
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
@ -30,7 +26,6 @@ namespace KLHZ.Trader.Core.Exchange.Services
private readonly string[] _instrumentsFigis = [];
public readonly ConcurrentDictionary<string, IOrderbook> Orderbooks = new();
private readonly ConcurrentDictionary<string, FFTAnalyzeResult> _fftResults = new();
private readonly ConcurrentDictionary<string, string> _tickersCache = new();
private readonly ConcurrentDictionary<string, AssetType> _assetTypesCache = new();
@ -48,156 +43,71 @@ namespace KLHZ.Trader.Core.Exchange.Services
_isDataRecievingAllowed = options.Value.ExchangeDataRecievingEnabled;
}
public ValueTask<FFTAnalyzeResult> GetFFtResult(string figi)
{
if (_fftResults.TryGetValue(figi, out var res))
{
return ValueTask.FromResult(res);
}
return ValueTask.FromResult<FFTAnalyzeResult>(FFTAnalyzeResult.Empty);
}
public ValueTask SetFFtResult(FFTAnalyzeResult result)
{
_fftResults[result.Key] = result;
return ValueTask.CompletedTask;
}
public async ValueTask<decimal> GetLastPrice(string figi)
{
var res = 0m;
if (_historyCash.TryGetValue(figi, out var unit))
if (_historyCash3.TryGetValue(figi, out var unit))
{
res = (await unit.GetLastValues()).price;
}
return res;
}
public async ValueTask<(DateTime[] timestamps, decimal[] prices, bool isFullIntervalExists)> GetData(string figi, TimeSpan timeSpan)
{
if (_historyCash.TryGetValue(figi, out var unit))
{
var res = await unit.GetData(timeSpan);
return (res.timestamps, res.prices, res.isFullIntervalExists);
}
return (Array.Empty<DateTime>(), Array.Empty<decimal>(), false);
}
public async ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(string figi, int? length = null)
{
if (_historyCash.TryGetValue(figi, out var unit))
{
var res = await unit.GetData(length);
return (res.timestamps, res.prices);
}
return (Array.Empty<DateTime>(), Array.Empty<decimal>());
}
public async ValueTask AddData(ITradeDataItem message, TimeSpan? clearingInterval = null)
public async ValueTask AddData(ITradeDataItem message)
{
if (message.Direction != 1) return;
if (_historyCash.TryGetValue(message.Figi, out var unit))
if (_historyCash3.TryGetValue(message.Figi, out var unit))
{
if (clearingInterval.HasValue)
{
var lasts = await unit.GetLastValues();
if (message.Time - lasts.time > clearingInterval.Value)
{
unit = new PriceHistoryCacheUnit2(message.Figi);
_historyCash[message.Figi] = unit;
}
}
await unit.AddData(message);
}
else
{
unit = new PriceHistoryCacheUnit2(message.Figi, message);
_historyCash.TryAdd(message.Figi, unit);
unit = new PriceHistoryCacheUnit3(message.Figi, message);
_historyCash3.TryAdd(message.Figi, unit);
}
}
public async ValueTask AddDataTo1MinuteWindowCache(string figi, string key, CachedValue data)
public async ValueTask AddData(string figi, string key, ITradeDataItem data)
{
if (!_historyCash.TryGetValue(figi, out var unit))
if (!_historyCash3.TryGetValue(figi, out var item))
{
unit = new PriceHistoryCacheUnit2(figi);
_historyCash.TryAdd(figi, unit);
item = new PriceHistoryCacheUnit3(figi);
_historyCash3.TryAdd(figi, item);
}
await _historyCash[figi].AddDataToTimeWindowCache(key, data, TimeWindowCacheType._1_Minute);
await _historyCash3[figi].AddData(data, key);
}
public async ValueTask AddDataTo20SecondsWindowCache(string figi, string key, CachedValue data)
public ValueTask<ITradeDataItem[]> GetDataForTimeWindow(string figi, TimeSpan time, string? key = null, Func<ITradeDataItem, bool>? selector = null)
{
if (!_historyCash.TryGetValue(figi, out var unit))
if (_historyCash3.TryGetValue(figi, out var cahcheItem))
{
unit = new PriceHistoryCacheUnit2(figi);
_historyCash.TryAdd(figi, unit);
return cahcheItem.GetData(time, key: key, selector);
}
await _historyCash[figi].AddDataToTimeWindowCache(key, data, TimeWindowCacheType._20_Seconds);
return ValueTask.FromResult(Array.Empty<ITradeDataItem>());
}
public async ValueTask AddDataTo5MinuteWindowCache(string figi, string key, CachedValue data)
public ValueTask<ITradeDataItem[]> GetDataFrom20SecondsWindowCache2(string figi, string key)
{
if (!_historyCash.TryGetValue(figi, out var unit))
{
unit = new PriceHistoryCacheUnit2(figi);
_historyCash.TryAdd(figi, unit);
}
await _historyCash[figi].AddDataToTimeWindowCache(key, data, TimeWindowCacheType._5_Minutes);
return GetDataForTimeWindow(figi, TimeSpan.FromSeconds(20), key);
}
public async ValueTask AddDataTo15MinuteWindowCache(string figi, string key, CachedValue data)
public ValueTask<ITradeDataItem[]> GetDataFrom1MinuteWindowCache2(string figi, string key)
{
if (!_historyCash.TryGetValue(figi, out var unit))
{
unit = new PriceHistoryCacheUnit2(figi);
_historyCash.TryAdd(figi, unit);
}
await _historyCash[figi].AddDataToTimeWindowCache(key, data, TimeWindowCacheType._15_Minutes);
return GetDataForTimeWindow(figi, TimeSpan.FromSeconds(60), key);
}
public ValueTask<CachedValue[]> GetDataFrom20SecondsWindowCache(string figi, string key)
public ValueTask<ITradeDataItem[]> GetDataFrom5MinuteWindowCache2(string figi, string key)
{
if (_historyCash.TryGetValue(figi, out var cahcheItem))
{
return cahcheItem.GetDataFromTimeWindowCache(key, TimeWindowCacheType._20_Seconds);
}
return ValueTask.FromResult(Array.Empty<CachedValue>());
return GetDataForTimeWindow(figi, TimeSpan.FromMinutes(5), key);
}
public ValueTask<CachedValue[]> GetDataFrom1MinuteWindowCache(string figi, string key)
public ValueTask<ITradeDataItem[]> GetDataFrom15MinuteWindowCache2(string figi, string key)
{
if (_historyCash.TryGetValue(figi, out var cahcheItem))
{
return cahcheItem.GetDataFromTimeWindowCache(key, TimeWindowCacheType._1_Minute);
}
return ValueTask.FromResult(Array.Empty<CachedValue>());
return GetDataForTimeWindow(figi, TimeSpan.FromMinutes(15), key);
}
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 ValueTask<CachedValue[]> GetDataFrom15MinuteWindowCache(string figi, string key)
{
if (_historyCash.TryGetValue(figi, out var cahcheItem))
{
return cahcheItem.GetDataFromTimeWindowCache(key, TimeWindowCacheType._15_Minutes);
}
return ValueTask.FromResult(Array.Empty<CachedValue>());
}
public async ValueTask AddOrderbook(IOrderbook orderbook)
{
if (!_historyCash.TryGetValue(orderbook.Figi, out var unit))
if (!_historyCash3.TryGetValue(orderbook.Figi, out var unit))
{
unit = new PriceHistoryCacheUnit2(orderbook.Figi);
_historyCash.TryAdd(orderbook.Figi, unit);
unit = new PriceHistoryCacheUnit3(orderbook.Figi);
_historyCash3.TryAdd(orderbook.Figi, unit);
}
Orderbooks[orderbook.Figi] = orderbook;
await unit.AddOrderbook(orderbook);