реализация новой стратегии

dev
vlad zverzhkhovskiy 2025-10-13 17:20:17 +03:00
parent f4fb12407a
commit c2612ba85a
12 changed files with 379 additions and 121 deletions

View File

@ -4,14 +4,9 @@
public enum TradingEvent
{
None = 0,
StopBuy = 1,
LongOpen = 2,
ShortClose = 4,
LongClose = 8,
ShortOpen = 16,
UptrendEnd = 32,
UptrendStart = 64,
DowntrendEnd = 128,
DowntrendStart = 256,
CloseLong = 1,
OpenLong = 2,
CloseShort = 4,
OpenShort = 8,
}
}

View File

@ -71,19 +71,19 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
var diff2 = y2_approximated[0] - y2_approximated[y2_approximated.Count - 1];
if (diff1 <= -meanfullDiff && diff2 >= meanfullDiff)
{
res |= TradingEvent.DowntrendEnd;
res |= TradingEvent.CloseShort;
}
else if (diff1 >= meanfullDiff && diff2 <= -meanfullDiff)
{
res |= TradingEvent.UptrendEnd;
res |= TradingEvent.CloseLong;
}
else if (diff1 <= -meanfullDiff && diff2 >= meanfullDiff)
{
res |= TradingEvent.DowntrendEnd;
res |= TradingEvent.CloseShort;
}
else if (diff1 >= 0 && diff2 <= -meanfullDiff)
{
res |= TradingEvent.DowntrendStart;
res |= TradingEvent.OpenShort;
}
success = true;
}

View File

@ -97,7 +97,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
if (diffTotal >= uptrendEndingDetectionMeanfullStep
&& times[crossings[0]] - times[crossings[1]] >= timeForUptreandStart)
{
res |= TradingEvent.UptrendEnd;
res |= TradingEvent.CloseLong;
}
break;
}
@ -115,7 +115,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
if (pricesForFinalComparison[crossings[0]] - pricesForFinalComparison[crossings[1]] <= uptrendStartingDetectionMeanfullStep
&& times[crossings[0]] - times[crossings[1]] >= timeForUptreandStart)
{
res |= TradingEvent.UptrendStart;
res |= TradingEvent.OpenLong;
}
break;
}
@ -193,7 +193,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
)
&& dt > TimeSpan.FromSeconds(10)))
{
res |= TradingEvent.UptrendEnd;
res |= TradingEvent.CloseLong;
}
break;
}
@ -204,7 +204,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
// || d2 <= uptrendStartingDetectionMeanfullStep
) && dt > TimeSpan.FromSeconds(10)))
{
res |= TradingEvent.UptrendStart;
res |= TradingEvent.OpenLong;
}
break;
}

View File

@ -33,7 +33,7 @@ namespace KLHZ.Trader.Core.Tests
if (LocalTrends.TryGetLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(49), TimeSpan.FromSeconds(49), 22, out var res))
{
Assert.That(res == Contracts.Declisions.Dtos.Enums.TradingEvent.UptrendEnd);
Assert.That(res == Contracts.Declisions.Dtos.Enums.TradingEvent.CloseLong);
}
else
{

View File

@ -3,8 +3,6 @@
public enum DeclisionTradeAction
{
Unknown = 0,
StopBuy = 1,
StopBuyShortTime = 2,
OpenLong = 100,
OpenLongReal = 101,
CloseLong = 200,

View File

@ -0,0 +1,10 @@
namespace KLHZ.Trader.Core.Exchange.Models.Trading
{
internal class PirsonCalculatingResult
{
public bool Success { get; init; }
public decimal Pirson { get; init; }
public decimal PriceDiff { get; init; }
public decimal TradesDiff { get; init; }
}
}

View File

@ -0,0 +1,32 @@
using KLHZ.Trader.Core.Contracts.Common.Enums;
namespace KLHZ.Trader.Core.Exchange.Models.Trading
{
public readonly struct Stops
{
public readonly decimal LongStopLossShift;
public readonly decimal LongTakeProfitShift;
public readonly decimal ShortStopLossShift;
public readonly decimal ShortTakeProfitShift;
public Stops(decimal longStopLossShift, decimal longTakeProfitShift, decimal shortStopLossShift, decimal shortTakeProfitShift)
{
LongStopLossShift = longStopLossShift;
LongTakeProfitShift = longTakeProfitShift;
ShortStopLossShift = shortStopLossShift;
ShortTakeProfitShift = shortTakeProfitShift;
}
public (decimal takeProfit, decimal stopLoss) GetStops(PositionType positionType)
{
if (positionType == PositionType.Short)
{
return (ShortTakeProfitShift, ShortStopLossShift);
}
else
{
return (LongTakeProfitShift, LongStopLossShift);
}
}
}
}

View File

@ -0,0 +1,10 @@
namespace KLHZ.Trader.Core.Exchange.Models.Trading
{
public class SupportLevel
{
public decimal Value { get; init; }
public decimal LowValue { get; init; }
public decimal HighValue { get; init; }
public DateTime? LastLevelTime { get; init; }
}
}

View File

@ -34,8 +34,9 @@ namespace KLHZ.Trader.Core.Exchange.Services
private readonly ConcurrentDictionary<string, TradingMode> TradingModes = new();
private readonly ConcurrentDictionary<string, SupportLevel[]> SupportLevels = new();
private readonly ConcurrentDictionary<string, decimal> DPirsonValues = new();
private readonly ConcurrentDictionary<string, decimal> DPricesValues = new();
private readonly ConcurrentDictionary<string, DateTime> _supportLevelsCalculationTimes = new();
private readonly Channel<ITradeDataItem> _pricesChannel = Channel.CreateUnbounded<ITradeDataItem>();
private readonly Channel<ITradeCommand> _commands = Channel.CreateUnbounded<ITradeCommand>();
@ -82,7 +83,8 @@ namespace KLHZ.Trader.Core.Exchange.Services
{
var fakeMessage = new TradeDataItem() { 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 stops = GetStops(fakeMessage, positionType);
var st = GetStops(fakeMessage);
var stops = st.GetStops(positionType);
var accounts = _portfolioWrapper.Accounts
.Where(a => !a.Value.Assets.ContainsKey(command.Figi))
.Take(1)
@ -120,7 +122,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
{
var pricesCache1 = new Dictionary<string, List<ITradeDataItem>>();
var pricesCache2 = new Dictionary<string, List<ITradeDataItem>>();
var timesCache = new Dictionary<string, DateTime>();
while (await _pricesChannel.Reader.WaitToReadAsync())
{
var message = await _pricesChannel.Reader.ReadAsync();
@ -128,86 +130,22 @@ namespace KLHZ.Trader.Core.Exchange.Services
{
continue;
}
var changeMods = TraderUtils.GetInitDict(0);
await CloseMarginPositionsIfNeed(message);
try
{
//message = TraderUtils.FilterHighFreqValues(message, message.Direction == 1 ? pricesCache1 : pricesCache2);
#region Добавление данных в кеши.
if (message.Figi == "BBG004730N88" || message.Figi == "FUTIMOEXF000")
if (message.IsHistoricalData)
{
await _tradeDataProvider.AddData(message);
message = TraderUtils.FilterHighFreqValues(message, message.Direction == 1 ? pricesCache1 : pricesCache2);
}
#endregion
if (_exchangeConfig.TradingInstrumentsFigis.Contains(message.Figi) && message.Direction == 1)
{
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;
await _tradeDataProvider.AddData(message);
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))
if (message.Figi == "FUTIMOEXF000")
{
await _tradeDataProvider.LogPrice(message, "privcesDiff", pricesDiff);
await _tradeDataProvider.LogPrice(message, "tradevolume_diff", tradesDiff);
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,
});
if (DPricesValues.TryGetValue(message.Figi, out var olddPrice))
{
if (olddPrice < 0m && pricesDiff > 0)
{
//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))
{
await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_long_in", message.Price);
}
if (olddpirs > 0.7m && res < 0.7m)
{
// 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;
}
await ProcessIMOEXF(message);
}
}
}
@ -219,6 +157,206 @@ namespace KLHZ.Trader.Core.Exchange.Services
}
}
private async Task ProcessIMOEXF(ITradeDataItem message)
{
if (message.Figi == "FUTIMOEXF000")
{
await CalcSupportLevels(message, 3, 5);
var stops = GetStops(message);
var pirson = await CalcPirson(message);
var declisionPirson = await ProcessPirson(pirson, message);
var declisionsSupportLevels = await ProcessSupportLevels(message);
var declisionsStops = ProcessStops(stops, 1.5m);
var res = TraderUtils.MergeResultsMult(declisionPirson, declisionsSupportLevels);
res = TraderUtils.MergeResultsMult(res, declisionsStops);
await ExecuteDeclisions(res.ToImmutableDictionary(), message, stops, 1);
}
}
private async Task<ImmutableDictionary<TradingEvent, decimal>> ProcessPirson(PirsonCalculatingResult pirson, ITradeDataItem message)
{
var res = TraderUtils.GetInitDict(0);
if (pirson.Success && DPirsonValues.TryGetValue(message.Figi, out var olddpirs))
{
if (olddpirs < -0.3m && pirson.Pirson > -0.3m && pirson.PriceDiff > 0 && (pirson.TradesDiff > 0))
{
res[TradingEvent.OpenLong] = Constants.PowerUppingCoefficient;
await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_long_in", message.Price);
}
if (olddpirs > 0.3m && pirson.Pirson < 0.3m && pirson.PriceDiff < 0 && (pirson.TradesDiff > 0))
{
res[TradingEvent.OpenShort] = Constants.PowerUppingCoefficient;
await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_short_in", message.Price);
}
if (olddpirs > 0.7m && pirson.Pirson < 0.7m)
{
// await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_long_out", message.Price);
}
if (olddpirs < -0.7m && pirson.Pirson > -0.7m)
{
// await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_short_out", message.Price);
}
}
DPirsonValues[message.Figi] = pirson.Pirson;
return res.ToImmutableDictionary();
}
private async Task<PirsonCalculatingResult> CalcPirson(ITradeDataItem message)
{
var cacheSize = TimeSpan.FromSeconds(400);
var smallWindow = TimeSpan.FromSeconds(180);
var bigWindow = TimeSpan.FromSeconds(360);
var meanWindowForCottelation = TimeSpan.FromSeconds(360);
var currentTime = message.IsHistoricalData ? message.Time : DateTime.UtcNow;
var buys = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, cacheSize, selector: (i) => i.Direction == 1);
var trades = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, cacheSize);
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.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.GetDataForTimeWindow(message.Figi, cacheSize, "5min_diff");
if (diffs.TryCalcPirsonCorrelation(meanWindowForCottelation, 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,
//});
return new PirsonCalculatingResult()
{
Pirson = res,
PriceDiff = pricesDiff,
TradesDiff = tradesDiff,
Success = true,
};
}
}
return new PirsonCalculatingResult()
{
Success = false,
};
}
private async Task CloseMarginPositionsIfNeed(ITradeDataItem message)
{
var state = ExchangeScheduler.GetCurrentState(message.Time);
if (!message.IsHistoricalData && state == ExchangeState.ClearingTime)
{
var futuresFigis = _portfolioWrapper.Accounts.Values.SelectMany(v => v.Assets.Values.Where(a => a.Type == AssetType.Futures)).ToArray();
await ClosePositions(futuresFigis, message, false);
}
}
private async Task CalcSupportLevels(ITradeDataItem message, int leverage, decimal supportLevelWidth, int depthHours = 3)
{
if (_supportLevelsCalculationTimes.TryGetValue(message.Figi, out var lastTime))
{
if ((message.Time - lastTime).TotalMinutes < 30)
{
return;
}
}
var data = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, TimeSpan.FromHours(depthHours));
if (data.Length > 0)
{
if (data[^1].Time - data[0].Time < TimeSpan.FromHours(depthHours - 1))
{
data = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, TimeSpan.FromHours(depthHours + 12));
if (data[^1].Time - data[0].Time < TimeSpan.FromHours(depthHours - 1))
{
return;
}
}
var hist = Statistics.CalcHistogram(data);
var convs = Statistics.CalcConvolution(hist, leverage).ToList();
var orderedConvs = convs.OrderByDescending(c => c.Sum).Take(5).ToList();
orderedConvs = [.. orderedConvs.OrderBy(c => c.Value)];
var levelsForAdd = new List<SupportLevel>();
foreach (var c in orderedConvs)
{
var low = c.Value - supportLevelWidth;
var high = c.Value + supportLevelWidth;
if (levelsForAdd.Count > 0)
{
var last = levelsForAdd.Last();
if (last.HighValue < low)
{
levelsForAdd.Add(new SupportLevel()
{
HighValue = high,
LowValue = low,
Value = c.Value,
});
}
else if (last.HighValue >= low && last.HighValue < high)
{
levelsForAdd[^1] = new SupportLevel()
{
LowValue = last.LowValue,
HighValue = high,
Value = last.LowValue + (high - last.LowValue) / 2
};
}
}
else
{
levelsForAdd.Add(new SupportLevel()
{
HighValue = high,
LowValue = low,
Value = c.Value,
});
}
}
var finalLevels = new SupportLevel[levelsForAdd.Count];
var i = 0;
foreach (var level in levelsForAdd)
{
DateTime? time = null;
foreach (var item in data)
{
if (item.Price >= level.LowValue && item.Price < level.HighValue)
{
time = item.Time;
}
}
finalLevels[i] = new SupportLevel()
{
HighValue = level.HighValue,
LowValue = level.LowValue,
Value = level.Value,
LastLevelTime = time,
};
i++;
}
SupportLevels[message.Figi] = finalLevels;
await _tradeDataProvider.LogPrice(message, "support_level_calc", message.Price);
}
_supportLevelsCalculationTimes[message.Figi] = message.Time;
}
private async Task ClosePositions(Asset[] assets, ITradeDataItem message, bool withProfitOnly = true)
{
var loggedDeclisions = 0;
@ -308,19 +446,20 @@ namespace KLHZ.Trader.Core.Exchange.Services
}
}
private async Task ExecuteDeclisions(ITradeDataItem message, ImmutableDictionary<TradingEvent, decimal> result)
private async Task ExecuteDeclisions(ImmutableDictionary<TradingEvent, decimal> result, ITradeDataItem message, Stops st, int accountsForOpening = 1)
{
var state = ExchangeScheduler.GetCurrentState();
if (result[TradingEvent.UptrendStart] >= Constants.UppingCoefficient
if (result[TradingEvent.OpenLong] >= Constants.UppingCoefficient
&& state == ExchangeState.Open
)
{
var stops = GetStops(message, PositionType.Long);
var stops = st.GetStops(PositionType.Long);
if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase())
{
var accounts = _portfolioWrapper.Accounts
.Where(a => !a.Value.Assets.ContainsKey(message.Figi))
.Take(1)
.Take(accountsForOpening)
.Select(a => a.Value)
.ToArray();
await OpenPositions(accounts, message, PositionType.Long, stops.stopLoss, stops.takeProfit, 1);
@ -329,11 +468,11 @@ namespace KLHZ.Trader.Core.Exchange.Services
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsLong, message.Price + stops.takeProfit, message.Time.AddMilliseconds(-RandomNumberGenerator.GetInt32(300, 1000)), message);
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsLong, message.Price - stops.stopLoss, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(300, 1000)), message);
}
if (result[TradingEvent.DowntrendStart] >= Constants.UppingCoefficient
if (result[TradingEvent.OpenShort] >= Constants.UppingCoefficient
&& state == ExchangeState.Open
)
{
var stops = GetStops(message, PositionType.Short);
var stops = st.GetStops(PositionType.Long);
if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase())
{
var accounts = _portfolioWrapper.Accounts
@ -348,7 +487,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsShort, message.Price - stops.takeProfit, message.Time.AddMilliseconds(-RandomNumberGenerator.GetInt32(300, 1000)), message);
await _tradeDataProvider.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.CloseLong] >= Constants.UppingCoefficient * 10)
{
if (!message.IsHistoricalData && BotModeSwitcher.CanSell())
{
@ -362,7 +501,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
}
if (result[TradingEvent.DowntrendEnd] >= Constants.UppingCoefficient * 10)
if (result[TradingEvent.CloseShort] >= Constants.UppingCoefficient * 10)
{
if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase())
{
@ -409,11 +548,86 @@ namespace KLHZ.Trader.Core.Exchange.Services
return res;
}
private (decimal stopLoss, decimal takeProfit) GetStops(ITradeDataItem message, PositionType type)
private Stops GetStops(ITradeDataItem message)
{
decimal stopLossShift = 2m;
decimal takeProfitShift = 6;
return (stopLossShift, takeProfitShift);
decimal longStopLossShift = message.Value * 0.99m;
decimal longTakeProfitShift = message.Value * 1.03m;
decimal shortStopLossShift = message.Value * 0.99m;
decimal shortTakeProfitShift = message.Value * 1.03m;
if (SupportLevels.TryGetValue(message.Figi, out var levels))
{
if (levels.Length > 0)
{
var levelsByTime = levels.Where(l => l.LastLevelTime.HasValue)
.OrderByDescending(l => l.LastLevelTime)
.ToArray();
if (message.Price >= levelsByTime[0].LowValue && message.Price < levelsByTime[0].HighValue)
{
longStopLossShift = message.Price - levelsByTime[0].LowValue;
shortStopLossShift = message.Price + levelsByTime[0].HighValue;
}
else
{
var levelsByDiff = levels.Where(l => l.LastLevelTime.HasValue)
.OrderBy(l => System.Math.Abs(l.Value - message.Price))
.ToArray();
var nearestLevel = levelsByDiff[0];
longStopLossShift = message.Price - nearestLevel.HighValue;
shortStopLossShift = nearestLevel.LowValue - message.Price;
}
}
}
return new Stops(longStopLossShift, longTakeProfitShift, shortStopLossShift, shortTakeProfitShift);
}
private static ImmutableDictionary<TradingEvent, decimal> ProcessStops(Stops stops, decimal meanfullLevel)
{
var res = TraderUtils.GetInitDict(1);
if (stops.LongTakeProfitShift < meanfullLevel || stops.LongStopLossShift < meanfullLevel)
{
res[TradingEvent.OpenLong] = Constants.BlockingCoefficient;
}
if (stops.ShortTakeProfitShift < meanfullLevel || stops.ShortStopLossShift < meanfullLevel)
{
res[TradingEvent.OpenShort] = Constants.BlockingCoefficient;
}
return res.ToImmutableDictionary();
}
private async Task<ImmutableDictionary<TradingEvent, decimal>> ProcessSupportLevels(ITradeDataItem message)
{
var res = TraderUtils.GetInitDict(1);
if (SupportLevels.TryGetValue(message.Figi, out var levels))
{
foreach (var lev in levels)
{
if (message.Price >= lev.LowValue && message.Price < lev.HighValue)
{
await _tradeDataProvider.LogPrice(message, "support_level", message.Price);
}
}
if (levels.Length > 0)
{
var levelsByTime = levels.Where(l => l.LastLevelTime.HasValue)
.OrderByDescending(l => l.LastLevelTime)
.ToArray();
if (message.Price >= levelsByTime[0].LowValue && message.Price < levelsByTime[0].HighValue)
{
if (message.Price > levelsByTime[0].Value)
{
res[TradingEvent.OpenLong] = Constants.BlockingCoefficient;
}
if (message.Price < levelsByTime[0].Value)
{
res[TradingEvent.OpenShort] = Constants.BlockingCoefficient;
}
}
}
}
return res.ToImmutableDictionary();
}
}
}

View File

@ -86,10 +86,12 @@ namespace KLHZ.Trader.Core.Exchange.Services
}
return ValueTask.FromResult(Array.Empty<ITradeDataItem>());
}
public ValueTask<ITradeDataItem[]> GetDataFrom20SecondsWindowCache2(string figi, string key)
{
return GetDataForTimeWindow(figi, TimeSpan.FromSeconds(20), key);
}
public ValueTask<ITradeDataItem[]> GetDataFrom1MinuteWindowCache2(string figi, string key)
{
return GetDataForTimeWindow(figi, TimeSpan.FromSeconds(60), key);
@ -140,7 +142,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
if (_isDataRecievingAllowed)
{
var time = DateTime.UtcNow.AddHours(-1.5);
var time = DateTime.UtcNow.AddHours(-5);
using var context1 = await _dbContextFactory.CreateDbContextAsync();
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var data = await context1.PriceChanges

View File

@ -4,33 +4,35 @@ using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
using KLHZ.Trader.Core.Exchange.Interfaces;
using KLHZ.Trader.Core.Exchange.Models.AssetsAccounting;
using System.Collections.Immutable;
namespace KLHZ.Trader.Core.Exchange.Utils
{
internal static class TraderUtils
{
internal static Dictionary<TradingEvent, decimal> MergeResultsMult(Dictionary<TradingEvent, decimal> res, ImmutableDictionary<TradingEvent, decimal> data)
internal static Dictionary<TradingEvent, decimal> MergeResultsMult(IDictionary<TradingEvent, decimal> result, IDictionary<TradingEvent, decimal> data)
{
foreach (var k in res.Keys)
var res = new Dictionary<TradingEvent, decimal>();
foreach (var k in result.Keys)
{
var valRes = res[k];
var valRes = result[k];
var valData = data[k];
res[k] = valRes * valData;
}
return res;
}
internal static Dictionary<TradingEvent, decimal> MergeResultsMax(Dictionary<TradingEvent, decimal> res, ImmutableDictionary<TradingEvent, decimal> data)
internal static Dictionary<TradingEvent, decimal> MergeResultsMax(IDictionary<TradingEvent, decimal> result, IDictionary<TradingEvent, decimal> data)
{
foreach (var k in res.Keys)
var res = new Dictionary<TradingEvent, decimal>();
foreach (var k in result.Keys)
{
var valRes = res[k];
var valData = data[k];
var valRes = result[k];
var valData = result[k];
res[k] = System.Math.Max(valRes, valData);
}
return res;
}
internal static Dictionary<TradingEvent, decimal> GetInitDict(decimal initValue)
{
var values = Enum.GetValues<TradingEvent>();

View File

@ -286,12 +286,9 @@ namespace KLHZ.Trader.Service.Controllers
var leverage = 3;
var hist = Statistics.CalcHistogram(prices);
var convs = Statistics.CalcConvolution(hist, leverage).ToList();
//var result = new List<ConvolutionResult>();
//Statistics.MergeConvolutionResults(convs, result);
var orderedConvs = convs.OrderByDescending(c => c.Sum).Take(5).ToList();
orderedConvs = orderedConvs.OrderBy(c => c.Value).ToList();
var values = new List<decimal>();
var shifts = new List<decimal>();
foreach (var c in orderedConvs)
{
@ -354,7 +351,5 @@ namespace KLHZ.Trader.Service.Controllers
}
}
}
}
}