Чистка кода

dev
vlad zverzhkhovskiy 2025-10-07 13:05:38 +03:00
parent 00de5bee2e
commit 64c702ebf1
17 changed files with 135 additions and 732 deletions

View File

@ -1,9 +1,15 @@
namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos
{ {
public class CachedValue public class CachedValue //: ICachedValue
{ {
public DateTime Time { get; init; } public DateTime Time { get; init; }
public decimal Count { get; init; } public long Count { get; init; }
public decimal Price { 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; }
} }
} }

View File

@ -0,0 +1,10 @@
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
namespace KLHZ.Trader.Core.Contracts.Declisions.Interfaces
{
internal interface ICachedValue : INewPrice
{
public decimal Value { get; }
public decimal Value2 { get; }
}
}

View File

@ -2,12 +2,12 @@
{ {
public interface INewPrice public interface INewPrice
{ {
public bool IsHistoricalData { get; set; } public bool IsHistoricalData { get; }
public decimal Value { get; set; } public decimal Price { get; }
public string Figi { get; set; } public string Figi { get; }
public string Ticker { get; set; } public string Ticker { get; }
public DateTime Time { get; set; } public DateTime Time { get; }
public long Count { get; set; } public long Count { get; }
public int Direction { get; set; } public int Direction { get; }
} }
} }

View File

@ -4,7 +4,7 @@ namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos
{ {
public class NewPriceMessage : INewPrice public class NewPriceMessage : INewPrice
{ {
public decimal Value { get; set; } public decimal Price { get; set; }
public required string Figi { get; set; } public required string Figi { get; set; }
public required string Ticker { get; set; } public required string Ticker { get; set; }
public DateTime Time { get; set; } public DateTime Time { get; set; }

View File

@ -111,7 +111,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
lock (_locker) lock (_locker)
{ {
_pointer++; _pointer++;
Prices[_pointer] = priceChange.Value; Prices[_pointer] = priceChange.Price;
Timestamps[_pointer] = priceChange.Time; Timestamps[_pointer] = priceChange.Time;
if (_length < CacheMaxLength) if (_length < CacheMaxLength)
{ {

View File

@ -94,9 +94,9 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
{ {
var tradevolume_diffMean = values.Mean(); var tradevolume_diffMean = values.Mean();
var dprice_diffMean = values.Mean2(); var dprice_diffMean = values.Mean2();
var sum1 = (double)values.Sum(d => (d.Count - tradevolume_diffMean) * (d.Price - dprice_diffMean)); var sum1 = (double)values.Sum(d => (d.Value2 - tradevolume_diffMean) * (d.Value - dprice_diffMean));
var sum2 = values.Sum(d => (d.Count - tradevolume_diffMean) * (d.Count - tradevolume_diffMean)); var sum2 = values.Sum(d => (d.Value2 - tradevolume_diffMean) * (d.Value2 - tradevolume_diffMean));
var sum3 = values.Sum(d => (d.Price - dprice_diffMean) * (d.Price - dprice_diffMean)); var sum3 = values.Sum(d => (d.Value - dprice_diffMean) * (d.Value - dprice_diffMean));
if (sum2 != 0 && sum3 != 0) if (sum2 != 0 && sum3 != 0)
{ {
result = (decimal)(sum1 / System.Math.Sqrt((double)(sum2 * sum3))); result = (decimal)(sum1 / System.Math.Sqrt((double)(sum2 * sum3)));

View File

@ -20,7 +20,7 @@ namespace KLHZ.Trader.Core.Tests
Ticker = figi + "_ticker", Ticker = figi + "_ticker",
Id = i, Id = i,
Time = startDt, Time = startDt,
Value = (decimal)(i + 0.5) Price = (decimal)(i + 0.5)
}; };
} }
} }
@ -53,7 +53,7 @@ namespace KLHZ.Trader.Core.Tests
Assert.That(data.timestamps.Length == count); Assert.That(data.timestamps.Length == count);
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
Assert.That((float)hist[i].Value, Is.EqualTo(data.prices[i])); Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i]));
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i])); Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
} }
} }
@ -72,7 +72,7 @@ namespace KLHZ.Trader.Core.Tests
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
Assert.That((float)hist[i].Value, Is.EqualTo(data.prices[i])); Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i]));
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i])); Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
} }
} }
@ -91,7 +91,7 @@ namespace KLHZ.Trader.Core.Tests
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
Assert.That((float)hist[i].Value, Is.EqualTo(data.prices[i])); Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i]));
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i])); Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
} }
} }
@ -114,7 +114,7 @@ namespace KLHZ.Trader.Core.Tests
var k = i + shift; var k = i + shift;
if (k < hist.Length) if (k < hist.Length)
{ {
Assert.That((float)hist[k].Value, Is.EqualTo(data.prices[i])); Assert.That((float)hist[k].Price, Is.EqualTo(data.prices[i]));
Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i])); Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i]));
} }
} }
@ -138,7 +138,7 @@ namespace KLHZ.Trader.Core.Tests
var k = i + shift; var k = i + shift;
if (k < hist.Length) if (k < hist.Length)
{ {
Assert.That((float)hist[k].Value, Is.EqualTo(data.prices[i])); Assert.That((float)hist[k].Price, Is.EqualTo(data.prices[i]));
Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i])); Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i]));
} }
} }
@ -162,7 +162,7 @@ namespace KLHZ.Trader.Core.Tests
var k = i + shift; var k = i + shift;
if (k < hist.Length) if (k < hist.Length)
{ {
Assert.That((float)hist[k].Value, Is.EqualTo(data.prices[i])); Assert.That((float)hist[k].Price, Is.EqualTo(data.prices[i]));
Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i])); Assert.That(hist[k].Time, Is.EqualTo(data.timestamps[i]));
} }
} }
@ -183,24 +183,24 @@ namespace KLHZ.Trader.Core.Tests
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
Assert.That((float)hist[i].Value, Is.EqualTo(data.prices[i])); Assert.That((float)hist[i].Price, Is.EqualTo(data.prices[i]));
Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i])); Assert.That(hist[i].Time, Is.EqualTo(data.timestamps[i]));
} }
var newData1 = new PriceChange() { Figi = figi, Ticker = figi, Value = 100500, Time = DateTime.UtcNow }; var newData1 = new PriceChange() { Figi = figi, Ticker = figi, Price = 100500, Time = DateTime.UtcNow };
cacheUnit.AddData(newData1); cacheUnit.AddData(newData1);
var data2 = cacheUnit.GetData().Result; var data2 = cacheUnit.GetData().Result;
Assert.IsTrue(data2.prices[data2.prices.Length - 1] == newData1.Value); Assert.IsTrue(data2.prices[data2.prices.Length - 1] == newData1.Price);
Assert.IsTrue(data2.timestamps[data2.timestamps.Length - 1] == newData1.Time); Assert.IsTrue(data2.timestamps[data2.timestamps.Length - 1] == newData1.Time);
var newData2 = new PriceChange() { Figi = figi, Ticker = figi, Value = 100501, Time = DateTime.UtcNow }; var newData2 = new PriceChange() { Figi = figi, Ticker = figi, Price = 100501, Time = DateTime.UtcNow };
cacheUnit.AddData(newData2); cacheUnit.AddData(newData2);
var data3 = cacheUnit.GetData().Result; var data3 = cacheUnit.GetData().Result;
Assert.IsTrue(data3.prices[data3.prices.Length - 1] == newData2.Value); Assert.IsTrue(data3.prices[data3.prices.Length - 1] == newData2.Price);
Assert.IsTrue(data3.timestamps[data3.timestamps.Length - 1] == newData2.Time); Assert.IsTrue(data3.timestamps[data3.timestamps.Length - 1] == newData2.Time);
} }
@ -210,7 +210,7 @@ namespace KLHZ.Trader.Core.Tests
var cacheUnit = new PriceHistoryCacheUnit2(""); var cacheUnit = new PriceHistoryCacheUnit2("");
for (int i = 0; i < 5 * PriceHistoryCacheUnit2.CacheMaxLength; i++) for (int i = 0; i < 5 * PriceHistoryCacheUnit2.CacheMaxLength; i++)
{ {
cacheUnit.AddData(new PriceChange() { Figi = "", Ticker = "", Value = i, Time = DateTime.UtcNow }); cacheUnit.AddData(new PriceChange() { Figi = "", Ticker = "", Price = i, Time = DateTime.UtcNow });
if (i >= PriceHistoryCacheUnit2.CacheMaxLength) if (i >= PriceHistoryCacheUnit2.CacheMaxLength)
{ {
var data = cacheUnit.GetData().Result; var data = cacheUnit.GetData().Result;
@ -237,11 +237,11 @@ namespace KLHZ.Trader.Core.Tests
Assert.That(data.prices.Length == length); Assert.That(data.prices.Length == length);
Assert.That(data.timestamps.Length == length); Assert.That(data.timestamps.Length == length);
Assert.That(data.prices.Last() == hist.Last().Value); Assert.That(data.prices.Last() == hist.Last().Price);
Assert.That(data.timestamps.Last() == hist.Last().Time); Assert.That(data.timestamps.Last() == hist.Last().Time);
for (var i = 1; i <= length; i++) for (var i = 1; i <= length; i++)
{ {
Assert.That(hist[hist.Length - i].Value, Is.EqualTo(data.prices[data.prices.Length - i])); Assert.That(hist[hist.Length - i].Price, Is.EqualTo(data.prices[data.prices.Length - i]));
Assert.That(hist[hist.Length - i].Time, Is.EqualTo(data.timestamps[data.prices.Length - i])); Assert.That(hist[hist.Length - i].Time, Is.EqualTo(data.timestamps[data.prices.Length - i]));
} }
} }
@ -267,7 +267,7 @@ namespace KLHZ.Trader.Core.Tests
Assert.That(data.prices.Length == length); Assert.That(data.prices.Length == length);
Assert.That(data.timestamps.Length == length); Assert.That(data.timestamps.Length == length);
Assert.That(data.prices.Last() == hist.Last().Value); Assert.That(data.prices.Last() == hist.Last().Price);
Assert.That(data.timestamps.Last() == hist.Last().Time); Assert.That(data.timestamps.Last() == hist.Last().Time);
} }

View File

@ -13,7 +13,7 @@ namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
public DateTime Time { get; set; } public DateTime Time { get; set; }
[Column("value")] [Column("value")]
public decimal Value { get; set; } public decimal Price { get; set; }
[Column("figi")] [Column("figi")]
public required string Figi { get; set; } public required string Figi { get; set; }

View File

@ -13,7 +13,7 @@ namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
public DateTime Time { get; set; } public DateTime Time { get; set; }
[Column("value")] [Column("value")]
public decimal Value { get; set; } public decimal Price { get; set; }
[Column("figi")] [Column("figi")]
public required string Figi { get; set; } public required string Figi { get; set; }

View File

@ -3,12 +3,10 @@
public class ExchangeConfig public class ExchangeConfig
{ {
public bool ExchangeDataRecievingEnabled { get; init; } public bool ExchangeDataRecievingEnabled { get; init; }
public decimal StopBuyLengthMinuts { get; init; }
public decimal FutureComission { get; init; } public decimal FutureComission { get; init; }
public decimal ShareComission { get; init; } public decimal ShareComission { get; init; }
public decimal AccountCashPart { get; init; } public decimal AccountCashPart { get; init; }
public decimal AccountCashPartFutures { get; init; } public decimal AccountCashPartFutures { get; init; }
public decimal DefaultBuyPartOfAccount { get; init; }
public string[] DataRecievingInstrumentsFigis { get; init; } = []; public string[] DataRecievingInstrumentsFigis { get; init; } = [];
public string[] TradingInstrumentsFigis { get; init; } = []; public string[] TradingInstrumentsFigis { get; init; } = [];
public string[] ManagingAccountNamePatterns { get; init; } = []; public string[] ManagingAccountNamePatterns { get; init; } = [];

View File

@ -149,7 +149,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
Figi = response.Trade.Figi, Figi = response.Trade.Figi,
Ticker = _tradeDataProvider.GetTickerByFigi(response.Trade.Figi), Ticker = _tradeDataProvider.GetTickerByFigi(response.Trade.Figi),
Time = response.Trade.Time.ToDateTime().ToUniversalTime(), Time = response.Trade.Time.ToDateTime().ToUniversalTime(),
Value = response.Trade.Price, Price = response.Trade.Price,
IsHistoricalData = false, IsHistoricalData = false,
Direction = (int)response.Trade.Direction, Direction = (int)response.Trade.Direction,
Count = response.Trade.Quantity, Count = response.Trade.Quantity,

View File

@ -39,11 +39,6 @@ namespace KLHZ.Trader.Core.Exchange.Services
private readonly ConcurrentDictionary<string, decimal> DPirsonValues = new(); private readonly ConcurrentDictionary<string, decimal> DPirsonValues = new();
private readonly ConcurrentDictionary<string, DateTime> LongOpeningStops = new();
private readonly ConcurrentDictionary<string, DateTime> ShortOpeningStops = new();
private readonly ConcurrentDictionary<string, DateTime> LongClosingStops = new();
private readonly ConcurrentDictionary<string, DateTime> ShortClosingStops = new();
private readonly Channel<INewPrice> _pricesChannel = Channel.CreateUnbounded<INewPrice>(); private readonly Channel<INewPrice> _pricesChannel = Channel.CreateUnbounded<INewPrice>();
private readonly Channel<ITradeCommand> _commands = Channel.CreateUnbounded<ITradeCommand>(); private readonly Channel<ITradeCommand> _commands = Channel.CreateUnbounded<ITradeCommand>();
private readonly Channel<IOrderbook> _orderbooks = Channel.CreateUnbounded<IOrderbook>(); private readonly Channel<IOrderbook> _orderbooks = Channel.CreateUnbounded<IOrderbook>();
@ -87,7 +82,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
if (command.CommandType == Contracts.Messaging.Dtos.Enums.TradeCommandType.OpenLong if (command.CommandType == Contracts.Messaging.Dtos.Enums.TradeCommandType.OpenLong
|| command.CommandType == Contracts.Messaging.Dtos.Enums.TradeCommandType.OpenShort) || command.CommandType == Contracts.Messaging.Dtos.Enums.TradeCommandType.OpenShort)
{ {
var fakeMessage = new NewPriceMessage() { Figi = command.Figi, Ticker = "", Count = command.Count, Direction = 1, IsHistoricalData = false, Time = DateTime.UtcNow, Value = command.RecomendPrice ?? 0m }; var fakeMessage = new NewPriceMessage() { Figi = command.Figi, Ticker = "", Count = command.Count, Direction = 1, IsHistoricalData = false, Time = DateTime.UtcNow, Price = command.RecomendPrice ?? 0m };
var positionType = command.CommandType == TradeCommandType.OpenLong ? PositionType.Long : PositionType.Short; var positionType = command.CommandType == TradeCommandType.OpenLong ? PositionType.Long : PositionType.Short;
var stops = GetStops(fakeMessage, positionType); var stops = GetStops(fakeMessage, positionType);
var accounts = _portfolioWrapper.Accounts var accounts = _portfolioWrapper.Accounts
@ -99,7 +94,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
} }
else else
{ {
var fakeMessage = new NewPriceMessage() { Figi = command.Figi, Ticker = "", Count = command.Count, Direction = 1, IsHistoricalData = false, Time = DateTime.UtcNow, Value = command.RecomendPrice ?? 0m }; var fakeMessage = new NewPriceMessage() { Figi = command.Figi, Ticker = "", Count = command.Count, Direction = 1, IsHistoricalData = false, Time = DateTime.UtcNow, Price = command.RecomendPrice ?? 0m };
var assetsForClose = _portfolioWrapper.Accounts var assetsForClose = _portfolioWrapper.Accounts
.SelectMany(a => a.Value.Assets.Values) .SelectMany(a => a.Value.Assets.Values)
.Where(a => a.Figi == fakeMessage.Figi) .Where(a => a.Figi == fakeMessage.Figi)
@ -109,7 +104,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Ошибка при выполнении команды.");
} }
} }
} }
@ -123,7 +118,6 @@ namespace KLHZ.Trader.Core.Exchange.Services
} }
} }
private async Task ProcessPrices() private async Task ProcessPrices()
{ {
var pricesCache1 = new Dictionary<string, List<INewPrice>>(); var pricesCache1 = new Dictionary<string, List<INewPrice>>();
@ -150,27 +144,27 @@ namespace KLHZ.Trader.Core.Exchange.Services
{ {
Time = message.Time, Time = message.Time,
Count = message.Count, Count = message.Count,
Price = message.Value, Price = message.Price,
}); });
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,
Count = (decimal)message.Count, Count = message.Count,
Price = message.Value, Price = message.Price,
}); });
await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, Constants._15minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue() await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, Constants._15minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue()
{ {
Time = message.Time, Time = message.Time,
Count = (decimal)message.Count, Count = message.Count,
Price = message.Value, Price = message.Price,
}); });
await _tradeDataProvider.AddDataTo1MinuteWindowCache(message.Figi, Constants._1minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue() await _tradeDataProvider.AddDataTo1MinuteWindowCache(message.Figi, Constants._1minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue()
{ {
Time = message.Time, Time = message.Time,
Count = (decimal)message.Count, Count = message.Count,
Price = message.Value, Price = message.Price,
}); });
} }
if (message.Direction == 2) if (message.Direction == 2)
@ -178,201 +172,101 @@ namespace KLHZ.Trader.Core.Exchange.Services
await _tradeDataProvider.AddDataTo5MinuteWindowCache(message.Figi, Constants._5minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue() await _tradeDataProvider.AddDataTo5MinuteWindowCache(message.Figi, Constants._5minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue()
{ {
Time = message.Time, Time = message.Time,
Count = (decimal)message.Count, Count = message.Count,
Price = message.Value, Price = message.Price,
}); });
await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, Constants._15minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue() await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, Constants._15minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue()
{ {
Time = message.Time, Time = message.Time,
Count = (decimal)message.Count, Count = message.Count,
Price = message.Value, Price = message.Price,
}); });
await _tradeDataProvider.AddDataTo1MinuteWindowCache(message.Figi, Constants._1minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue() await _tradeDataProvider.AddDataTo1MinuteWindowCache(message.Figi, Constants._1minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue()
{ {
Time = message.Time, Time = message.Time,
Count = (decimal)message.Count, Count = message.Count,
Price = message.Value, Price = message.Price,
}); });
await _tradeDataProvider.AddDataTo20SecondsWindowCache(message.Figi, "2", new Contracts.Declisions.Dtos.CachedValue() await _tradeDataProvider.AddDataTo20SecondsWindowCache(message.Figi, "2", new Contracts.Declisions.Dtos.CachedValue()
{ {
Time = message.Time, Time = message.Time,
Count = message.Count, Count = message.Count,
Price = message.Value, Price = message.Price,
}); });
} }
} }
#endregion #endregion
if (_exchangeConfig.TradingInstrumentsFigis.Contains(message.Figi) && (message.Figi == "FUTIMOEXF000" || message.Figi == "BBG004730N88") && message.Direction == 1) if (_exchangeConfig.TradingInstrumentsFigis.Contains(message.Figi) && message.Direction == 1)
{ {
var smallWindow = TimeSpan.FromSeconds(120); var smallWindow = TimeSpan.FromSeconds(120);
var bigWindow = TimeSpan.FromSeconds(240); var bigWindow = TimeSpan.FromSeconds(240);
var meanWindow = TimeSpan.FromSeconds(240); var meanWindow = TimeSpan.FromSeconds(240);
var currentTime = message.IsHistoricalData ? message.Time : DateTime.UtcNow; var currentTime = message.IsHistoricalData ? message.Time : DateTime.UtcNow;
try
{
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))
{
await LogPrice(message, "privcesDiff", pricesDiff);
await LogPrice(message, "tradevolume_diff", tradesDiff);
await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, "5min_diff", new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Count = tradesDiff,
Price = pricesDiff,
});
var diffs = await _tradeDataProvider.GetDataFrom15MinuteWindowCache(message.Figi, "5min_diff"); var buys = await _tradeDataProvider.GetDataFrom15MinuteWindowCache(message.Figi, Constants._15minBuyCacheKey);
if (diffs.TryCalcTimeWindowsDiff(bigWindow, smallWindow, (c) => c.Price, true, out var resdp) var sells = await _tradeDataProvider.GetDataFrom15MinuteWindowCache(message.Figi, Constants._15minSellCacheKey);
&& diffs.TryCalcTimeWindowsDiff(bigWindow, smallWindow, (c) => c.Count, true, out var resv)) 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))
{
await LogPrice(message, "privcesDiff", pricesDiff);
await LogPrice(message, "tradevolume_diff", tradesDiff);
await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, "5min_diff", new Contracts.Declisions.Dtos.CachedValue()
{
Time = message.Time,
Value2 = tradesDiff,
Value = pricesDiff,
});
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))
{
await LogPrice(message, "privcesDiffDiff", (decimal)resdp);
await LogPrice(message, "tradevolume_diff_diff", (decimal)resv);
if (diffs.TryCalcPirsonCorrelation(meanWindow, out var pirson))
{ {
await LogPrice(message, "privcesDiffDiff", (decimal)resdp); await LogPrice(message, "diffs_pirson", (decimal)pirson);
await LogPrice(message, "tradevolume_diff_diff", (decimal)resv); await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, "diffs_pirson", new Contracts.Declisions.Dtos.CachedValue()
await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, "5min_diff_diff", new Contracts.Declisions.Dtos.CachedValue()
{ {
Time = message.Time, Time = message.Time,
Count = resv, Value = (decimal)pirson,
Price = resdp,
}); });
if (diffs.TryCalcPirsonCorrelation(meanWindow, out var 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 LogPrice(message, "diffs_pirson", (decimal)pirson); if (DPirsonValues.TryGetValue(message.Figi, out var olddpirs))
await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, "diffs_pirson", new Contracts.Declisions.Dtos.CachedValue()
{ {
Time = message.Time, if (olddpirs < 0.5m && res > 0.5m)
Count = (decimal)pirson,
});
var diffs_pirson = await _tradeDataProvider.GetDataFrom15MinuteWindowCache(message.Figi, "diffs_pirson");
if (diffs_pirson.TryCalcTimeWindowsDiff(bigWindow, smallWindow, (c) => c.Count, true, out var res))
{
await LogPrice(message, "diffs_pirson_diff", (decimal)res);
if (DPirsonValues.TryGetValue(message.Figi, out var olddpirs))
{ {
if (olddpirs < 0 && res > 0) await LogPrice(message, "diffs_pirson_diff_point0.5", message.Price);
{
await LogPrice(message, "diffs_pirson_diff_point0", message.Value);
}
if (olddpirs < 0.25m && res > 0.25m)
{
await LogPrice(message, "diffs_pirson_diff_point0.25", message.Value);
}
if (olddpirs < 0.5m && res > 0.5m)
{
await LogPrice(message, "diffs_pirson_diff_point0.5", message.Value);
}
} }
DPirsonValues[message.Figi] = res;
} }
DPirsonValues[message.Figi] = res;
} }
} }
} }
if (timesCache.TryGetValue(message.Figi, out var dt))
{
if ((message.Time - dt).TotalSeconds > 10)
{
timesCache[message.Figi] = message.Time;
var newMod = await CalcTradingMode2(message);
if (TradingModes.TryGetValue(message.Figi, out var oldMod))
{
if ((oldMod == TradingMode.Growing || oldMod == TradingMode.Stable)
&& oldMod != newMod)
{
changeMods[TradingEvent.UptrendEnd] = Constants.PowerUppingCoefficient;
}
if ((oldMod == TradingMode.Dropping || oldMod == TradingMode.SlowDropping)
&& oldMod != newMod)
{
changeMods[TradingEvent.DowntrendEnd] = Constants.PowerUppingCoefficient;
}
if (newMod == TradingMode.Growing && newMod != oldMod && !LongOpeningStops.ContainsKey(message.Figi))
{
//changeMods[TradingEvent.UptrendStart] = Constants.PowerUppingCoefficient;
}
if (newMod == TradingMode.Dropping && newMod != oldMod && !ShortOpeningStops.ContainsKey(message.Figi))
{
//changeMods[TradingEvent.DowntrendStart] = Constants.PowerUppingCoefficient;
}
TradingModes[message.Figi] = newMod;
if (oldMod != newMod)
{
var accountForStopsChanging = _portfolioWrapper.Accounts
.Where(a => a.Value.Assets.ContainsKey(message.Figi))
.ToArray();
foreach (var account in accountForStopsChanging)
{
if (account.Value.Assets.TryGetValue(message.Figi, out var asset))
{
var stops = GetStops(message, asset.Count > 0 ? PositionType.Long : PositionType.Short);
if (!message.IsHistoricalData)
{
await account.Value.ResetStops(message.Figi, stops.stopLoss, stops.takeProfit);
if (asset.Count < 0)
{
await LogDeclision(DeclisionTradeAction.ResetStopsShort, asset.BoughtPrice - stops.takeProfit, message.Time.AddMilliseconds(-100), message);
await LogDeclision(DeclisionTradeAction.ResetStopsShort, asset.BoughtPrice + stops.stopLoss, message.Time.AddMilliseconds(100), message);
}
else
{
await LogDeclision(DeclisionTradeAction.ResetStopsLong, asset.BoughtPrice + stops.takeProfit, message.Time.AddMilliseconds(-100), message);
await LogDeclision(DeclisionTradeAction.ResetStopsLong, asset.BoughtPrice - stops.stopLoss, message.Time.AddMilliseconds(100), message);
}
}
}
}
}
}
}
}
else
{
timesCache[message.Figi] = message.Time;
}
}
catch (Exception ex)
{
}
if (TradingModes.TryGetValue(message.Figi, out var mode))
{
await LogPrice(message, "trading_mode", (decimal)mode);
}
try
{
if (message.Direction != 1) continue;
await _tradeDataProvider.AddData(message);
//ProcessStops(message, currentTime);
var windowMaxSize = 2000;
//var data = await _tradeDataProvider.GetData(message.Figi, windowMaxSize);
//var state = ExchangeScheduler.GetCurrentState(message.Time);
// await ProcessNewPriceIMOEXF3(data, state, message, windowMaxSize, changeMods.ToImmutableDictionary());
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка при боработке новой цены IMOEXF");
} }
} }
} }
catch (Exception e) catch (Exception ex)
{ {
_logger.LogError(ex, "Ошибка при боработке новой цены.");
} }
} }
} }
private async Task ClosePositions(Asset[] assets, INewPrice message, bool withProfitOnly = true) private async Task ClosePositions(Asset[] assets, INewPrice message, bool withProfitOnly = true)
{ {
var loggedDeclisions = 0; var loggedDeclisions = 0;
var assetType = _tradeDataProvider.GetAssetTypeByFigi(message.Figi); var assetType = _tradeDataProvider.GetAssetTypeByFigi(message.Figi);
var assetsForClose = new List<Asset>(); var assetsForClose = new List<Asset>();
var price = message.Value; var price = message.Price;
if (price == 0) if (price == 0)
{ {
price = await _tradeDataProvider.GetLastPrice(message.Figi); price = await _tradeDataProvider.GetLastPrice(message.Figi);
@ -381,6 +275,8 @@ namespace KLHZ.Trader.Core.Exchange.Services
var messages = new List<string>(); var messages = new List<string>();
foreach (var asset in assets) foreach (var asset in assets)
{ {
Asset? assetForClose = null;
string? mess = null;
if (withProfitOnly) if (withProfitOnly)
{ {
var profit = 0m; var profit = 0m;
@ -405,8 +301,8 @@ namespace KLHZ.Trader.Core.Exchange.Services
if (profit > 0) if (profit > 0)
{ {
profit = System.Math.Round(profit, 2); profit = System.Math.Round(profit, 2);
assetsForClose.Add(asset); assetForClose = asset;
messages.Add($"Закрываю позицию {asset.Figi} ({(asset.Count > 0 ? "лонг" : "шорт")}) на счёте {_portfolioWrapper.Accounts[asset.AccountId].AccountName}. Количество {(long)asset.Count}, цена ~{price}, профит {profit}"); mess = $"Закрываю позицию {asset.Figi} ({(asset.Count > 0 ? "лонг" : "шорт")}) на счёте {_portfolioWrapper.Accounts[asset.AccountId].AccountName}. Количество {(long)asset.Count}, цена ~{price}, профит {profit}";
if (loggedDeclisions == 0) if (loggedDeclisions == 0)
{ {
loggedDeclisions++; loggedDeclisions++;
@ -416,16 +312,15 @@ namespace KLHZ.Trader.Core.Exchange.Services
} }
else else
{ {
messages.Add($"Закрываю позицию {asset.Figi} ({(asset.Count > 0 ? "лонг" : "шорт")}) на счёте {_portfolioWrapper.Accounts[asset.AccountId].AccountName}. Количество {(long)asset.Count}, цена ~{price}"); mess = $"Закрываю позицию {asset.Figi} ({(asset.Count > 0 ? "лонг" : "шорт")}) на счёте {_portfolioWrapper.Accounts[asset.AccountId].AccountName}. Количество {(long)asset.Count}, цена ~{price}";
assetsForClose.Add(asset); assetForClose = asset;
} }
}
for (int i = 0; i < assetsForClose.Count; i++) if (assetForClose != null && mess != null)
{ {
var asset = assetsForClose[i]; await _portfolioWrapper.Accounts[asset.AccountId].ClosePosition(message.Figi);
await _portfolioWrapper.Accounts[asset.AccountId].ClosePosition(message.Figi); await _dataBus.Broadcast(new MessageForAdmin() { Text = mess });
await _dataBus.Broadcast(new MessageForAdmin() { Text = messages[i] }); }
} }
} }
@ -435,14 +330,14 @@ namespace KLHZ.Trader.Core.Exchange.Services
var sign = positionType == PositionType.Long ? 1 : 1; var sign = positionType == PositionType.Long ? 1 : 1;
foreach (var acc in accounts) foreach (var acc in accounts)
{ {
if (TraderUtils.IsOperationAllowed(acc, message.Value, 1, _exchangeConfig.AccountCashPartFutures, _exchangeConfig.AccountCashPart)) if (TraderUtils.IsOperationAllowed(acc, message.Price, 1, _exchangeConfig.AccountCashPartFutures, _exchangeConfig.AccountCashPart))
{ {
await acc.OpenPosition(message.Figi, positionType, stopLossShift, takeProfitShift, count); await acc.OpenPosition(message.Figi, positionType, stopLossShift, takeProfitShift, count);
await _dataBus.Broadcast(new MessageForAdmin() await _dataBus.Broadcast(new MessageForAdmin()
{ {
Text = $"Открываю позицию {message.Figi} ({(positionType == PositionType.Long ? "лонг" : "шорт")}) " + Text = $"Открываю позицию {message.Figi} ({(positionType == PositionType.Long ? "лонг" : "шорт")}) " +
$"на счёте {acc.AccountName}. Количество {(positionType == PositionType.Long ? "" : "-")}{count}, " + $"на счёте {acc.AccountName}. Количество {(positionType == PositionType.Long ? "" : "-")}{count}, " +
$"цена ~{System.Math.Round(message.Value, 2)}. Стоп лосс: {(positionType == PositionType.Long ? "-" : "+")}{stopLossShift}. " + $"цена ~{System.Math.Round(message.Price, 2)}. Стоп лосс: {(positionType == PositionType.Long ? "-" : "+")}{stopLossShift}. " +
$"Тейк профит: {(positionType == PositionType.Long ? "+" : "-")}{takeProfitShift}" $"Тейк профит: {(positionType == PositionType.Long ? "+" : "-")}{takeProfitShift}"
}); });
} }
@ -450,7 +345,6 @@ namespace KLHZ.Trader.Core.Exchange.Services
if (loggedDeclisions == 0) if (loggedDeclisions == 0)
{ {
await LogDeclision(DeclisionTradeAction.OpenLongReal, message); await LogDeclision(DeclisionTradeAction.OpenLongReal, message);
LongOpeningStops[message.Figi] = message.Time.AddMinutes(1);
loggedDeclisions++; loggedDeclisions++;
} }
} }
@ -460,7 +354,6 @@ namespace KLHZ.Trader.Core.Exchange.Services
{ {
var state = ExchangeScheduler.GetCurrentState(); var state = ExchangeScheduler.GetCurrentState();
if (result[TradingEvent.UptrendStart] >= Constants.UppingCoefficient if (result[TradingEvent.UptrendStart] >= Constants.UppingCoefficient
&& !LongOpeningStops.ContainsKey(message.Figi)
&& state == ExchangeState.Open && state == ExchangeState.Open
) )
{ {
@ -473,16 +366,14 @@ namespace KLHZ.Trader.Core.Exchange.Services
.Select(a => a.Value) .Select(a => a.Value)
.ToArray(); .ToArray();
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);
} }
await LogDeclision(DeclisionTradeAction.OpenLong, message.Value, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message); await LogDeclision(DeclisionTradeAction.OpenLong, message.Price, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message);
await LogDeclision(DeclisionTradeAction.ResetStopsLong, message.Value + stops.takeProfit, message.Time.AddMilliseconds(-RandomNumberGenerator.GetInt32(300, 1000)), message); await LogDeclision(DeclisionTradeAction.ResetStopsLong, message.Price + 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.Price - 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)
&& state == ExchangeState.Open && state == ExchangeState.Open
) )
{ {
var stops = GetStops(message, PositionType.Short); var stops = GetStops(message, PositionType.Short);
if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase()) if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase())
@ -493,12 +384,11 @@ namespace KLHZ.Trader.Core.Exchange.Services
.Select(a => a.Value) .Select(a => a.Value)
.ToArray(); .ToArray();
await OpenPositions(accounts, message, PositionType.Short, stops.stopLoss, stops.takeProfit, 1); await OpenPositions(accounts, message, PositionType.Short, stops.stopLoss, stops.takeProfit, 1);
ShortOpeningStops[message.Figi] = DateTime.UtcNow.AddMinutes(1);
} }
await LogDeclision(DeclisionTradeAction.OpenShort, message.Value, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message); await LogDeclision(DeclisionTradeAction.OpenShort, message.Price, 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.Price - 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.Price + stops.stopLoss, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(300, 1000)), message);
} }
if (result[TradingEvent.UptrendEnd] >= Constants.UppingCoefficient * 10) if (result[TradingEvent.UptrendEnd] >= Constants.UppingCoefficient * 10)
{ {
@ -510,7 +400,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
.ToArray(); .ToArray();
await ClosePositions(assetsForClose, message); await ClosePositions(assetsForClose, message);
} }
await LogDeclision(DeclisionTradeAction.CloseLong, message.Value, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message); await LogDeclision(DeclisionTradeAction.CloseLong, message.Price, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message);
} }
@ -524,43 +414,11 @@ namespace KLHZ.Trader.Core.Exchange.Services
.ToArray(); .ToArray();
await ClosePositions(assetsForClose, message); await ClosePositions(assetsForClose, message);
} }
await LogDeclision(DeclisionTradeAction.CloseShort, message.Value, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message); await LogDeclision(DeclisionTradeAction.CloseShort, message.Price, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message);
} }
} }
private void ProcessStops(INewPrice message, DateTime currentTime)
{
if (LongOpeningStops.TryGetValue(message.Figi, out var dt))
{
if (dt < currentTime)
{
LongOpeningStops.TryRemove(message.Figi, out _);
}
}
if (ShortClosingStops.TryGetValue(message.Figi, out var dt2))
{
if (dt2 < currentTime)
{
ShortClosingStops.TryRemove(message.Figi, out _);
}
}
if (LongClosingStops.TryGetValue(message.Figi, out var dt3))
{
if (dt3 < currentTime)
{
LongClosingStops.TryRemove(message.Figi, out _);
}
}
if (ShortOpeningStops.TryGetValue(message.Figi, out var dt4))
{
if (dt4 < currentTime)
{
ShortOpeningStops.TryRemove(message.Figi, out _);
}
}
}
private async Task LogPrice(INewPrice message, string processor, decimal value) private async Task LogPrice(INewPrice message, string processor, decimal value)
{ {
await _tradeDataProvider.LogPrice(new ProcessedPrice() await _tradeDataProvider.LogPrice(new ProcessedPrice()
@ -569,7 +427,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
Ticker = message.Ticker, Ticker = message.Ticker,
Processor = processor, Processor = processor,
Time = message.Time, Time = message.Time,
Value = value, Price = value,
}, false); }, false);
} }
@ -581,7 +439,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
Ticker = ticker, Ticker = ticker,
Processor = processor, Processor = processor,
Time = time, Time = time,
Value = value, Price = value,
}, false); }, false);
} }
@ -593,7 +451,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
Figi = message.Figi, Figi = message.Figi,
Ticker = message.Ticker, Ticker = message.Ticker,
Value = profit, Value = profit,
Price = message.Value, Price = message.Price,
Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow, Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow,
Action = action, Action = action,
}, false); }, false);
@ -645,135 +503,11 @@ namespace KLHZ.Trader.Core.Exchange.Services
return res; return res;
} }
private async ValueTask<TradingMode> CalcTradingMode2(INewPrice message)
{
var res = TradingMode.None;
var data = await _tradeDataProvider.GetData(message.Figi, TimeSpan.FromMinutes(20));
if (data.isFullIntervalExists)
{
if (LocalTrends.TryCalcTrendDiff(data.timestamps, data.prices, out var diff))
{
if (diff >= -2 && diff <= 2)
{
res = TradingMode.Stable;
}
else if (diff < -6)
{
res = TradingMode.Dropping;
}
else if (diff > 6)
{
res = TradingMode.Growing;
}
else if (diff < -2)
{
res = TradingMode.SlowDropping;
}
else if (diff > 2)
{
res = TradingMode.SlowGrowing;
}
}
}
return res;
}
private (decimal stopLoss, decimal takeProfit) GetStops(INewPrice message, PositionType type) private (decimal stopLoss, decimal takeProfit) GetStops(INewPrice message, PositionType type)
{ {
var mode = TradingModes[message.Figi];
decimal stopLossShift = 2m; decimal stopLossShift = 2m;
decimal takeProfitShift = 6; decimal takeProfitShift = 6;
if (mode == TradingMode.Growing && type == PositionType.Long)
{
takeProfitShift = 15;
//stopLossShift = 2;
}
if (mode == TradingMode.Growing && type == PositionType.Short)
{
//stopLossShift = 2;
takeProfitShift = 3m;
}
if (mode == TradingMode.Stable && type == PositionType.Long)
{
takeProfitShift = 3m;
}
if (mode == TradingMode.Stable && type == PositionType.Short)
{
takeProfitShift = 3m;
//stopLossShift = 10;
}
if (mode == TradingMode.SlowDropping && type == PositionType.Short)
{
}
if (mode == TradingMode.SlowDropping && type == PositionType.Long)
{
takeProfitShift = 1.5m;
//stopLossShift = 10;
}
if (mode == TradingMode.SlowGrowing && type == PositionType.Short)
{
//takeProfitShift = 2.5m;
//stopLossShift = 10;
}
if (mode == TradingMode.SlowGrowing && type == PositionType.Long)
{
}
if (mode == TradingMode.Dropping && type == PositionType.Short)
{
takeProfitShift = 15;
//stopLossShift = 2;
}
if (mode == TradingMode.Dropping && type == PositionType.Long)
{
//stopLossShift = 2;
takeProfitShift = 3m;
}
return (stopLossShift, takeProfitShift); return (stopLossShift, takeProfitShift);
} }
private Task<ImmutableDictionary<TradingEvent, decimal>> GetTradingModeMods(INewPrice message)
{
var res = TraderUtils.GetInitDict(1);
var mode = TradingModes[message.Figi];
if (mode == TradingMode.None)
{
//res[TradingEvent.UptrendEnd] = Constants.UppingCoefficient;
//res[TradingEvent.UptrendStart] = 1;
res[TradingEvent.DowntrendStart] = Constants.LowingCoefficient;
//res[TradingEvent.DowntrendEnd] = Constants.UppingCoefficient;
}
if (mode == TradingMode.Growing)
{
//res[TradingEvent.UptrendEnd] = Constants.LowingCoefficient;
res[TradingEvent.UptrendStart] = Constants.UppingCoefficient;
res[TradingEvent.DowntrendStart] = Constants.BlockingCoefficient;
res[TradingEvent.DowntrendEnd] = Constants.PowerUppingCoefficient;
}
if (mode == TradingMode.Stable)
{
//res[TradingEvent.UptrendEnd] = 1;
res[TradingEvent.UptrendStart] = Constants.LowingCoefficient;
//res[TradingEvent.DowntrendEnd] = Constants.UppingCoefficient;
res[TradingEvent.DowntrendStart] = Constants.LowingCoefficient;
}
if (mode == TradingMode.SlowDropping)
{
//res[TradingEvent.UptrendEnd] = Constants.PowerUppingCoefficient;
//res[TradingEvent.UptrendStart] = Constants.LowingCoefficient;
res[TradingEvent.DowntrendStart] = Constants.UppingCoefficient;
//res[TradingEvent.DowntrendEnd] = Constants.UppingCoefficient;
}
if (mode == TradingMode.Dropping)
{
res[TradingEvent.UptrendEnd] = Constants.PowerUppingCoefficient;
res[TradingEvent.UptrendStart] = Constants.BlockingCoefficient;
res[TradingEvent.DowntrendStart] = Constants.UppingCoefficient;
//res[TradingEvent.DowntrendEnd] = Constants.LowingCoefficient;
}
return Task.FromResult(res.ToImmutableDictionary());
}
} }
} }

View File

@ -9,7 +9,6 @@ using KLHZ.Trader.Core.DataLayer.Entities.Prices;
using KLHZ.Trader.Core.Exchange.Models.Configs; using KLHZ.Trader.Core.Exchange.Models.Configs;
using KLHZ.Trader.Core.Math.Declisions.Dtos.FFT; using KLHZ.Trader.Core.Math.Declisions.Dtos.FFT;
using KLHZ.Trader.Core.Math.Declisions.Services.Cache; using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
using KLHZ.Trader.Core.Math.Declisions.Utils;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -241,7 +240,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
Figi = c.Figi, Figi = c.Figi,
Ticker = c.Ticker, Ticker = c.Ticker,
Time = c.Time, Time = c.Time,
Value = c.Value, Price = c.Price,
IsHistoricalData = true, IsHistoricalData = true,
Direction = c.Direction, Direction = c.Direction,
Count = c.Count, Count = c.Count,
@ -251,15 +250,6 @@ namespace KLHZ.Trader.Core.Exchange.Services
foreach (var price in data) foreach (var price in data)
{ {
await AddData(price); await AddData(price);
var cachedData = await GetData(price.Figi);
if ((DateTime.UtcNow - price.Time).TotalMinutes < 5)
{
if (ShapeAreaCalculator.TryGetAreasRelation(cachedData.timestamps, cachedData.prices, price.Value, Constants.AreasRelationWindow, out var rel))
{
await AddDataTo1MinuteWindowCache(price.Figi, Constants._1minCacheKey, new CachedValue() { Time = price.Time, Count = (decimal)rel });
}
}
} }
} }

View File

@ -82,7 +82,7 @@ namespace KLHZ.Trader.Core.Exchange.Utils
Direction = message.Direction, Direction = message.Direction,
IsHistoricalData = message.IsHistoricalData, IsHistoricalData = message.IsHistoricalData,
Time = message.Time, Time = message.Time,
Value = list.Sum(l => l.Value) / list.Count Price = list.Sum(l => l.Price) / list.Count
}; };
list.Clear(); list.Clear();
return message; return message;

View File

@ -2,9 +2,6 @@ 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.DataLayer.Entities.Prices;
using KLHZ.Trader.Core.Exchange.Services;
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;
@ -15,15 +12,13 @@ namespace KLHZ.Trader.Service.Controllers
[Route("[controller]/[action]")] [Route("[controller]/[action]")]
public class PlayController : ControllerBase public class PlayController : ControllerBase
{ {
private readonly TraderDataProvider _traderDataProvider;
private readonly IDataBus _dataBus; private readonly IDataBus _dataBus;
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory; private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
public PlayController(IDataBus dataBus, IDbContextFactory<TraderDbContext> dbContextFactory, TraderDataProvider traderDataProvider) public PlayController(IDataBus dataBus, IDbContextFactory<TraderDbContext> dbContextFactory)
{ {
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_dataBus = dataBus; _dataBus = dataBus;
_traderDataProvider = traderDataProvider;
} }
[HttpGet] [HttpGet]
@ -59,7 +54,7 @@ namespace KLHZ.Trader.Service.Controllers
Figi = c.Figi, Figi = c.Figi,
Ticker = c.Ticker, Ticker = c.Ticker,
Time = c.Time, Time = c.Time,
Value = c.Value, Price = c.Price,
IsHistoricalData = true, IsHistoricalData = true,
Direction = c.Direction, Direction = c.Direction,
Count = c.Count, Count = c.Count,
@ -239,334 +234,6 @@ namespace KLHZ.Trader.Service.Controllers
} }
} }
[HttpGet]
public async Task CalcOrderbookMeanav(string figi)
{
try
{
var t = DateTime.UtcNow.AddHours(-4);
using var context1 = await _dbContextFactory.CreateDbContextAsync();
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var data = await context1.OrderbookItems
.Where(i => i.Time > t && i.Figi == figi && (i.ItemType == Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.BidsSummary4 || i.ItemType == Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.AsksSummary4))
.OrderBy(i => i.Time)
.ToArrayAsync();
var bids = new LinkedList<OrderbookItem>();
var asks = new LinkedList<OrderbookItem>();
var buffer = new List<OrderbookItem>();
var dt = TimeSpan.FromMinutes(1);
var q = data.ToLookup(d => d.Time);
foreach (var d in q)
{
var pair = d.DistinctBy(www => www.ItemType).OrderBy(www => www.ItemType).ToArray();
if (pair.Length == 2)
{
bids.AddLast(pair[1]);
if (bids.Last().Time - bids.First().Time > dt)
{
bids.RemoveFirst();
}
if (pair[0].Count != 0)
{
pair[1].Price = ((decimal)pair[1].Count) / ((decimal)pair[0].Count);
buffer.Add(new OrderbookItem()
{
Figi = pair[1].Figi,
Ticker = pair[1].Ticker,
Count = 1,
ItemType = Core.DataLayer.Entities.Orders.Enums.OrderbookItemType.BidsAsksSummary4_2min,
Price = bids.Sum(b => b.Price) / bids.Count,
Time = pair[1].Time,
});
}
if (buffer.Count > 10000)
{
await context1.OrderbookItems.AddRangeAsync(buffer);
await context1.SaveChangesAsync();
buffer.Clear();
}
}
else
{
}
}
if (buffer.Count > 0)
{
await context1.OrderbookItems.AddRangeAsync(buffer);
await context1.SaveChangesAsync();
buffer.Clear();
}
}
catch (Exception ex)
{
}
}
[HttpGet]
public async Task CalcTradesMeanav(string figi)
{
try
{
var t = DateTime.UtcNow.AddHours(-4);
using var context1 = await _dbContextFactory.CreateDbContextAsync();
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var data = await context1.PriceChanges
.Where(i => i.Time > t && i.Figi == figi)
.OrderBy(i => i.Time)
.ToArrayAsync();
var sells = new LinkedList<PriceChange>();
var buys = new LinkedList<PriceChange>();
var buffer = new List<ProcessedPrice>();
var dt = TimeSpan.FromMinutes(1);
foreach (var d in data)
{
if (d.Direction == 1)
{
if (buys.Last().Time - buys.First().Time > dt)
{
buys.RemoveFirst();
}
buys.AddLast(d);
}
if (d.Direction == 2)
{
sells.AddLast(d);
if (sells.Last().Time - sells.First().Time > dt)
{
sells.RemoveFirst();
}
sells.AddLast(d);
}
if (sells.Count > 0 && buys.Count > 0)
{
var meanS = ((decimal)sells.Sum(s => s.Count)) / sells.Count;
var meanB = ((decimal)buys.Sum(s => s.Count)) / buys.Count;
if (meanS != 0)
{
buffer.Add(new ProcessedPrice()
{
Figi = d.Figi,
Processor = "tradesbalance",
Ticker = d.Ticker,
Count = 1,
Direction = 0,
Time = d.Time,
Value = meanB / meanS
});
}
}
if (buffer.Count > 10000)
{
await context1.ProcessedPrices.AddRangeAsync(buffer);
await context1.SaveChangesAsync();
buffer.Clear();
}
}
if (buffer.Count > 0)
{
await context1.ProcessedPrices.AddRangeAsync(buffer);
await context1.SaveChangesAsync();
buffer.Clear();
}
}
catch (Exception ex)
{
}
}
[HttpGet]
public async Task TestInmterpolate(string figi)
{
try
{
var t1 = DateTime.UtcNow.AddHours(-4);
var t2 = DateTime.UtcNow.AddHours(1);
//t1 = new DateTime(2025, 9, 15, 10, 1, 0, DateTimeKind.Utc);
//t2 = new DateTime(2025, 9, 15, 10, 1, 50, DateTimeKind.Utc);
using var context1 = await _dbContextFactory.CreateDbContextAsync();
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var data = await context1.PriceChanges
.Where(i => i.Time >= t1 && i.Time <= t2 && i.Figi == figi)
.OrderBy(i => i.Time)
.ToArrayAsync();
var buffer = new List<ProcessedPrice>();
var res = SignalProcessing.InterpolateData(data.Select(d => d.Time).ToArray(), data.Select(d => d.Value).ToArray(),
TimeSpan.FromSeconds(1));
for (int i = 0; i < res.Item1.Length; i++)
{
buffer.Add(new ProcessedPrice()
{
Figi = figi,
Processor = "1secinterpol",
Ticker = data[0].Ticker,
Count = 1,
Direction = 0,
Time = res.Item1[i],
Value = (decimal)res.Item2[i]
});
}
await context1.ProcessedPrices.AddRangeAsync(buffer);
await context1.SaveChangesAsync();
}
catch (Exception ex)
{
}
}
[HttpGet]
public async Task TestFFT(string figi)
{
try
{
var t1 = new DateTime(2025, 9, 15, 6, 30, 0, DateTimeKind.Utc);
var t2 = new DateTime(2025, 9, 15, 7, 50, 50, DateTimeKind.Utc);
using var context1 = await _dbContextFactory.CreateDbContextAsync();
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var data = await context1.PriceChanges
.Where(i => i.Time >= t1 && i.Time <= t2 && i.Figi == figi)
.OrderBy(i => i.Time)
.ToArrayAsync();
var buffer = new List<ProcessedPrice>();
var values = data.Select(d => d.Value).ToArray();
var times = data.Select(d => d.Time).ToArray();
var res = SignalProcessing.InterpolateData(times, values,
TimeSpan.FromSeconds(10));
//FFT.GetMainHarmonictPeriod(res.Item2, res.Item1.Last() - res.Item1.First());
}
catch (Exception ex)
{
}
}
//[HttpGet]
//public async Task GetBalance(string figi)
//{
// try
// {
// var time1 = DateTime.UtcNow.AddDays(-7);
// using var context1 = await _dbContextFactory.CreateDbContextAsync();
// context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
// var data = await context1.PriceChanges
// .Where(c => c.Figi == figi && c.Time >= time1)
// .OrderBy(c => c.Time)
// .Select(c => new NewPriceMessage()
// {
// Figi = figi,
// Ticker = c.Ticker,
// Time = c.Time,
// Value = c.Value,
// IsHistoricalData = true
// })
// .ToArrayAsync();
// var buffer = new List<ProcessedPrice>();
// var list = new LinkedList<(DateTime time, double value)>();
// var previous = -1000d;
// foreach (var mess in data)
// {
// await _traderDataProvider.AddData(mess, TimeSpan.FromHours(6));
// var dataFromCache = await _traderDataProvider.GetData(figi, TimeSpan.FromMinutes(15));
// if (dataFromCache.isFullIntervalExists)
// {
// if (ShapeAreaCalculator.TryGetAreasRelation(dataFromCache.timestamps, dataFromCache.prices,mess.Value,out var res))
// {
// if (list.Count > 0 && mess.Time - list.Last().time > TimeSpan.FromMinutes(5))
// {
// list.Clear();
// }
// list.AddLast((mess.Time, res));
// if (list.Last().time - list.First().time > TimeSpan.FromMinutes(1))
// {
// list.RemoveFirst();
// }
// var newRes = (decimal)(list.Sum(i => i.value) / list.Count);
// if (list.Count > 0)
// {
// try
// {
// buffer.Add(new ProcessedPrice()
// {
// Figi = figi,
// Processor = "balancescalc30min",
// Ticker = mess.Ticker,
// Time = mess.Time,
// Value = newRes,
// //Value = (decimal)res,
// });
// }
// catch(Exception ex)
// {
// }
// }
// }
// }
// else
// {
// previous = -1d;
// }
// if (buffer.Count > 10000)
// {
// await context1.ProcessedPrices.AddRangeAsync(buffer);
// await context1.SaveChangesAsync();
// buffer.Clear();
// }
// }
// if (buffer.Count > 0)
// {
// await context1.ProcessedPrices.AddRangeAsync(buffer);
// await context1.SaveChangesAsync();
// buffer.Clear();
// }
// }
// catch (Exception ex)
// {
// }
//}
////[HttpGet]
//public async Task LoadTradesToHistory(string figi)
//{
// try
// {
// using var context1 = await _dbContextFactory.CreateDbContextAsync();
// context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
// var data = await context1.InstrumentTrades
// .Where(c => c.Figi == figi)
// .OrderBy(c => c.BoughtAt)
// .Select(c => new PriceChange()
// {
// Figi = figi,
// Ticker = c.Ticker,
// Time = c.BoughtAt,
// Value = c.Price,
// IsHistoricalData = true
// })
// .ToArrayAsync();
// await context1.PriceChanges.Where(p => p.Figi == figi).ExecuteDeleteAsync();
// await context1.PriceChanges.AddRangeAsync(data);
// await context1.SaveChangesAsync();
// }
// catch (Exception ex)
// {
// }
//}
} }
} }

View File

@ -2,7 +2,7 @@
namespace KLHZ.Trader.Service.Models namespace KLHZ.Trader.Service.Models
{ {
public class TimeSeriesData internal class TimeSeriesData
{ {
public required string Figi { get; set; } public required string Figi { get; set; }
public DateTime Time { get; set; } public DateTime Time { get; set; }

View File

@ -8,7 +8,6 @@
}, },
"LokiUrl": "", "LokiUrl": "",
"ExchangeConfig": { "ExchangeConfig": {
"StopBuyLengthMinuts": 15,
"ExchangeDataRecievingEnabled": true, "ExchangeDataRecievingEnabled": true,
"Token": "", "Token": "",
"ManagingAccountNamePatterns": [ "автотрейд" ], "ManagingAccountNamePatterns": [ "автотрейд" ],
@ -18,7 +17,6 @@
"ShareComission": 0.0004, "ShareComission": 0.0004,
"AccountCashPart": 0.05, "AccountCashPart": 0.05,
"AccountCashPartFutures": 0.5, "AccountCashPartFutures": 0.5,
"DefaultBuyPartOfAccount": 0.3333,
"InstrumentsSettings": [ "InstrumentsSettings": [
{ {
"Figi": "FUTIMOEXF000", "Figi": "FUTIMOEXF000",