фиксы новой стратегии
test / deploy_trader_prod (push) Successful in 5m11s Details

dev
vlad zverzhkhovskiy 2025-10-14 14:05:15 +03:00
parent c2612ba85a
commit 022cee9a19
9 changed files with 219 additions and 54 deletions

View File

@ -56,9 +56,10 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
} }
public static bool TryCalcTimeWindowsDiff(this ITradeDataItem[] values, TimeSpan boundLeft, TimeSpan boundRight, public static bool TryCalcTimeWindowsDiff(this ITradeDataItem[] values, TimeSpan boundLeft, TimeSpan boundRight,
Func<ITradeDataItem, decimal> fieldSelector, bool calcMean, out decimal result) Func<ITradeDataItem, decimal> fieldSelector, bool calcMean, out decimal result, out decimal resultRelative)
{ {
result = default; result = default;
resultRelative = default;
if (values.Length > 1) if (values.Length > 1)
{ {
var shiftTimeR = values.Last().Time - boundRight; var shiftTimeR = values.Last().Time - boundRight;
@ -78,6 +79,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
valOld = valOld / valuesOld.Length; valOld = valOld / valuesOld.Length;
} }
result = valNew - valOld; result = valNew - valOld;
resultRelative = (valNew - valOld) / valOld;
return true; return true;
} }
} }

View File

@ -15,10 +15,11 @@
internal const string AreasRelationProcessor = "balancescalc30min"; internal const string AreasRelationProcessor = "balancescalc30min";
internal readonly static TimeSpan AreasRelationWindow = TimeSpan.FromMinutes(15); internal readonly static TimeSpan AreasRelationWindow = TimeSpan.FromMinutes(15);
internal const decimal ForceExecuteCoefficient = 500000m;
internal const decimal PowerUppingCoefficient = 1.69m; internal const decimal PowerUppingCoefficient = 1.69m;
internal const decimal UppingCoefficient = 1.3m; internal const decimal UppingCoefficient = 1.3m;
internal const decimal LowingCoefficient = .76m; internal const decimal LowingCoefficient = .76m;
internal const decimal PowerLowingCoefficient = .59m; internal const decimal PowerLowingCoefficient = .59m;
internal const decimal BlockingCoefficient = 0m; internal const decimal BlockingCoefficient = 0.01m;
} }
} }

View File

@ -0,0 +1,14 @@
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
using System.Collections.Immutable;
namespace KLHZ.Trader.Core.Exchange.Models.Trading
{
internal class DeferredDeclision
{
public ImmutableDictionary<TradingEvent, decimal> Events { get; init; } = ImmutableDictionary<TradingEvent, decimal>.Empty;
public Stops Stops { get; init; }
public DateTime ExpirationTime { get; init; }
public required ITradeDataItem Message { get; init; }
}
}

View File

@ -6,5 +6,6 @@
public decimal Pirson { get; init; } public decimal Pirson { get; init; }
public decimal PriceDiff { get; init; } public decimal PriceDiff { get; init; }
public decimal TradesDiff { get; init; } public decimal TradesDiff { get; init; }
public decimal TradesDiffRelative { get; init; }
} }
} }

View File

@ -6,5 +6,6 @@
public decimal LowValue { get; init; } public decimal LowValue { get; init; }
public decimal HighValue { get; init; } public decimal HighValue { get; init; }
public DateTime? LastLevelTime { get; init; } public DateTime? LastLevelTime { get; init; }
public DateTime CalculatedAt { get; init; }
} }
} }

View File

@ -34,9 +34,14 @@ namespace KLHZ.Trader.Core.Exchange.Services
private readonly ConcurrentDictionary<string, TradingMode> TradingModes = new(); private readonly ConcurrentDictionary<string, TradingMode> TradingModes = new();
private readonly ConcurrentDictionary<string, DeferredDeclision> DeferredDeclisions = new();
private readonly ConcurrentDictionary<string, SupportLevel[]> SupportLevels = new(); private readonly ConcurrentDictionary<string, SupportLevel[]> SupportLevels = new();
private readonly ConcurrentDictionary<string, decimal> DPirsonValues = new(); private readonly ConcurrentDictionary<string, decimal> _pirsonValues = new();
private readonly ConcurrentDictionary<string, decimal> _dpirsonValues = new();
private readonly ConcurrentDictionary<string, DateTime> _supportLevelsCalculationTimes = new(); private readonly ConcurrentDictionary<string, DateTime> _supportLevelsCalculationTimes = new();
private readonly ConcurrentDictionary<string, DateTime> _usedSupportLevels = new();
private readonly ConcurrentDictionary<string, DateTime> _usedSupportLevelsForClosing = new();
private readonly ConcurrentDictionary<string, ITradeDataItem> _oldItems = new();
private readonly Channel<ITradeDataItem> _pricesChannel = Channel.CreateUnbounded<ITradeDataItem>(); private readonly Channel<ITradeDataItem> _pricesChannel = Channel.CreateUnbounded<ITradeDataItem>();
private readonly Channel<ITradeCommand> _commands = Channel.CreateUnbounded<ITradeCommand>(); private readonly Channel<ITradeCommand> _commands = Channel.CreateUnbounded<ITradeCommand>();
@ -161,44 +166,109 @@ namespace KLHZ.Trader.Core.Exchange.Services
{ {
if (message.Figi == "FUTIMOEXF000") if (message.Figi == "FUTIMOEXF000")
{ {
if (DeferredDeclisions.TryGetValue(message.Figi, out var dec))
{
if (dec.ExpirationTime < message.Time)
{
if (dec.Events[TradingEvent.OpenShort] > Constants.BlockingCoefficient)
{
if (dec.Message.Price < message.Price)
{
var stops2 = GetStops(message);
var declisionsStops2 = ProcessStops(stops2, 2m);
var e = TraderUtils.MergeResultsMult(dec.Events, declisionsStops2);
await ExecuteDeclisions(e.ToImmutableDictionary(), message, stops2, 1);
}
}
else if (dec.Events[TradingEvent.OpenLong] > Constants.BlockingCoefficient)
{
if (dec.Message.Price > message.Price)
{
var stops2 = GetStops(message);
var declisionsStops2 = ProcessStops(stops2, 2m);
var e = TraderUtils.MergeResultsMult(dec.Events, declisionsStops2);
await ExecuteDeclisions(e.ToImmutableDictionary(), message, stops2, 1);
}
}
else if (dec.Events[TradingEvent.CloseLong] > Constants.BlockingCoefficient
|| dec.Events[TradingEvent.CloseShort] > Constants.BlockingCoefficient)
{
await ExecuteDeclisions(dec.Events, dec.Message, dec.Stops, 1);
}
DeferredDeclisions.TryRemove(message.Figi, out _);
}
}
await CalcSupportLevels(message, 3, 5); await CalcSupportLevels(message, 3, 5);
var stops = GetStops(message); var stops = GetStops(message);
var pirson = await CalcPirson(message); var pirson = await CalcPirson(message);
var declisionPirson = await ProcessPirson(pirson, message); var declisionPirson = await ProcessPirson(pirson, message);
var declisionsSupportLevels = await ProcessSupportLevels(message); var declisionsSupportLevels = await ProcessSupportLevels(message);
var declisionsStops = ProcessStops(stops, 1.5m); var declisionsStops = ProcessStops(stops, 2m);
var res = TraderUtils.MergeResultsMult(declisionPirson, declisionsSupportLevels); var res = TraderUtils.MergeResultsMult(declisionPirson, declisionsSupportLevels);
res = TraderUtils.MergeResultsMult(res, declisionsStops); res = TraderUtils.MergeResultsMult(res, declisionsStops);
await ExecuteDeclisions(res.ToImmutableDictionary(), message, stops, 1); await ExecuteDeclisions(res.ToImmutableDictionary(), message, stops, 1);
var declision = new DeferredDeclision()
{
Message = message,
Stops = stops,
Events = res.ToImmutableDictionary(),
ExpirationTime = message.Time.AddSeconds(5)
};
if (declision.Events.Values.Any(v => v > Constants.BlockingCoefficient))
{
//DeferredDeclisions.TryAdd(message.Figi, declision);
}
_oldItems[message.Figi] = message;
} }
} }
private async Task<ImmutableDictionary<TradingEvent, decimal>> ProcessPirson(PirsonCalculatingResult pirson, ITradeDataItem message) private async Task<ImmutableDictionary<TradingEvent, decimal>> ProcessPirson(PirsonCalculatingResult pirson, ITradeDataItem message)
{ {
var res = TraderUtils.GetInitDict(0); var res = TraderUtils.GetInitDict(Constants.BlockingCoefficient);
if (pirson.Success && DPirsonValues.TryGetValue(message.Figi, out var olddpirs)) if (pirson.Success && _pirsonValues.TryGetValue(message.Figi, out var olddpirs))
{ {
if (olddpirs < -0.3m && pirson.Pirson > -0.3m && pirson.PriceDiff > 0 && (pirson.TradesDiff > 0)) var dpirson = pirson.Pirson - olddpirs;
if (olddpirs < 0 && pirson.Pirson > 0 && pirson.PriceDiff > 0 && (pirson.TradesDiffRelative > 0.2m))
{ {
res[TradingEvent.OpenLong] = Constants.PowerUppingCoefficient; 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)) //if (olddpirs < -0.7m && pirson.Pirson > -0.7m && pirson.PriceDiff > 0 && (pirson.TradesDiffRelative < -0.1m))
//{
// res[TradingEvent.OpenLong] = Constants.PowerUppingCoefficient;
//}
if (olddpirs > 0 && pirson.Pirson < 0 && pirson.PriceDiff < 0 && (pirson.TradesDiffRelative > 0.2m))
{ {
res[TradingEvent.OpenShort] = Constants.PowerUppingCoefficient; 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) //if (olddpirs > 0.3m && pirson.Pirson < 0.3m && pirson.PriceDiff < 0 && (pirson.TradesDiffRelative > 0.3m))
//{
// res[TradingEvent.OpenShort] = Constants.PowerUppingCoefficient;
//}
if (_dpirsonValues.TryGetValue(message.Figi, out var oldDprison))
{ {
// await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_long_out", message.Price); if (oldDprison > 0.02m && dpirson < -0.02m && pirson.Pirson > 0.7m && pirson.TradesDiffRelative < -0.2m)
} {
if (olddpirs < -0.7m && pirson.Pirson > -0.7m) res[TradingEvent.CloseLong] = Constants.PowerUppingCoefficient;
{ //await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_long_out", message.Price);
// await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_short_out", message.Price); }
if (oldDprison < 0 && dpirson > 0 && pirson.Pirson < -0.6m && pirson.TradesDiffRelative < -0.1m)
{
res[TradingEvent.CloseShort] = Constants.PowerUppingCoefficient;
// await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_short_out", message.Price);
}
} }
_dpirsonValues[message.Figi] = dpirson;
} }
DPirsonValues[message.Figi] = pirson.Pirson; _pirsonValues[message.Figi] = pirson.Pirson;
return res.ToImmutableDictionary(); return res.ToImmutableDictionary();
} }
@ -212,7 +282,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
var buys = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, cacheSize, selector: (i) => i.Direction == 1); var buys = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, cacheSize, selector: (i) => i.Direction == 1);
var trades = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, cacheSize); var trades = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, cacheSize);
if (trades.TryCalcTimeWindowsDiff(bigWindow, smallWindow, v => v.Count, false, out var tradesDiff) if (trades.TryCalcTimeWindowsDiff(bigWindow, smallWindow, v => v.Count, false, out var tradesDiff, out var tradesDiffRelative)
&& buys.TryCalcTimeDiff(bigWindow, smallWindow, v => v.Price, true, out var pricesDiff)) && buys.TryCalcTimeDiff(bigWindow, smallWindow, v => v.Price, true, out var pricesDiff))
{ {
await _tradeDataProvider.LogPrice(message, "privcesDiff", pricesDiff); await _tradeDataProvider.LogPrice(message, "privcesDiff", pricesDiff);
@ -243,6 +313,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
Pirson = res, Pirson = res,
PriceDiff = pricesDiff, PriceDiff = pricesDiff,
TradesDiff = tradesDiff, TradesDiff = tradesDiff,
TradesDiffRelative = tradesDiffRelative,
Success = true, Success = true,
}; };
} }
@ -276,10 +347,10 @@ namespace KLHZ.Trader.Core.Exchange.Services
var data = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, TimeSpan.FromHours(depthHours)); var data = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, TimeSpan.FromHours(depthHours));
if (data.Length > 0) if (data.Length > 0)
{ {
if (data[^1].Time - data[0].Time < TimeSpan.FromHours(depthHours - 1)) if (data[^1].Time - data[0].Time < TimeSpan.FromHours(0.5))
{ {
data = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, TimeSpan.FromHours(depthHours + 12)); data = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, TimeSpan.FromHours(depthHours + 12));
if (data[^1].Time - data[0].Time < TimeSpan.FromHours(depthHours - 1)) if (data[^1].Time - data[0].Time < TimeSpan.FromHours(0.5))
{ {
return; return;
} }
@ -305,6 +376,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
HighValue = high, HighValue = high,
LowValue = low, LowValue = low,
Value = c.Value, Value = c.Value,
CalculatedAt = message.Time,
}); });
} }
else if (last.HighValue >= low && last.HighValue < high) else if (last.HighValue >= low && last.HighValue < high)
@ -313,7 +385,8 @@ namespace KLHZ.Trader.Core.Exchange.Services
{ {
LowValue = last.LowValue, LowValue = last.LowValue,
HighValue = high, HighValue = high,
Value = last.LowValue + (high - last.LowValue) / 2 Value = last.LowValue + (high - last.LowValue) / 2,
CalculatedAt = message.Time,
}; };
} }
} }
@ -324,6 +397,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
HighValue = high, HighValue = high,
LowValue = low, LowValue = low,
Value = c.Value, Value = c.Value,
CalculatedAt = message.Time,
}); });
} }
} }
@ -346,6 +420,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
LowValue = level.LowValue, LowValue = level.LowValue,
Value = level.Value, Value = level.Value,
LastLevelTime = time, LastLevelTime = time,
CalculatedAt = message.Time,
}; };
i++; i++;
} }
@ -426,7 +501,7 @@ 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.Price, 1, _exchangeConfig.AccountCashPartFutures, _exchangeConfig.AccountCashPart)) if (TraderUtils.IsOperationAllowed(acc, message.Price, count, _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()
@ -448,7 +523,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
private async Task ExecuteDeclisions(ImmutableDictionary<TradingEvent, decimal> result, ITradeDataItem message, Stops st, int accountsForOpening = 1) private async Task ExecuteDeclisions(ImmutableDictionary<TradingEvent, decimal> result, ITradeDataItem message, Stops st, int accountsForOpening = 1)
{ {
var state = ExchangeScheduler.GetCurrentState(); var state = ExchangeScheduler.GetCurrentState(message.Time);
if (result[TradingEvent.OpenLong] >= Constants.UppingCoefficient if (result[TradingEvent.OpenLong] >= Constants.UppingCoefficient
&& state == ExchangeState.Open && state == ExchangeState.Open
) )
@ -464,15 +539,18 @@ namespace KLHZ.Trader.Core.Exchange.Services
.ToArray(); .ToArray();
await OpenPositions(accounts, message, PositionType.Long, stops.stopLoss, stops.takeProfit, 1); await OpenPositions(accounts, message, PositionType.Long, stops.stopLoss, stops.takeProfit, 1);
} }
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.OpenLong, message.Price, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message); var val = message.Price;
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsLong, message.Price + stops.takeProfit, message.Time.AddMilliseconds(-RandomNumberGenerator.GetInt32(300, 1000)), message); var valLow = message.Price - stops.stopLoss;
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsLong, message.Price - stops.stopLoss, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(300, 1000)), message); var valHigh = message.Price + stops.takeProfit;
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.OpenLong, val, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message);
//await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsLong, valHigh, message.Time.AddMilliseconds(-RandomNumberGenerator.GetInt32(300, 1000)), message);
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsLong, valLow, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(300, 1000)), message);
} }
if (result[TradingEvent.OpenShort] >= Constants.UppingCoefficient if (result[TradingEvent.OpenShort] >= Constants.UppingCoefficient
&& state == ExchangeState.Open && state == ExchangeState.Open
) )
{ {
var stops = st.GetStops(PositionType.Long); var stops = st.GetStops(PositionType.Short);
if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase()) if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase())
{ {
var accounts = _portfolioWrapper.Accounts var accounts = _portfolioWrapper.Accounts
@ -482,12 +560,14 @@ namespace KLHZ.Trader.Core.Exchange.Services
.ToArray(); .ToArray();
await OpenPositions(accounts, message, PositionType.Short, stops.stopLoss, stops.takeProfit, 1); await OpenPositions(accounts, message, PositionType.Short, stops.stopLoss, stops.takeProfit, 1);
} }
var val = message.Price;
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.OpenShort, message.Price, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message); var valLow = message.Price - stops.takeProfit;
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsShort, message.Price - stops.takeProfit, message.Time.AddMilliseconds(-RandomNumberGenerator.GetInt32(300, 1000)), message); var valHigh = message.Price + stops.stopLoss;
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsShort, message.Price + stops.stopLoss, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(300, 1000)), message); await _tradeDataProvider.LogDeclision(DeclisionTradeAction.OpenShort, val, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message);
//await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsShort, valLow, message.Time.AddMilliseconds(-RandomNumberGenerator.GetInt32(300, 1000)), message);
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsShort, valHigh, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(300, 1000)), message);
} }
if (result[TradingEvent.CloseLong] >= Constants.UppingCoefficient * 10) if (result[TradingEvent.CloseLong] >= Constants.UppingCoefficient)
{ {
if (!message.IsHistoricalData && BotModeSwitcher.CanSell()) if (!message.IsHistoricalData && BotModeSwitcher.CanSell())
{ {
@ -501,7 +581,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
} }
if (result[TradingEvent.CloseShort] >= Constants.UppingCoefficient * 10) if (result[TradingEvent.CloseShort] >= Constants.UppingCoefficient)
{ {
if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase()) if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase())
{ {
@ -550,10 +630,11 @@ namespace KLHZ.Trader.Core.Exchange.Services
private Stops GetStops(ITradeDataItem message) private Stops GetStops(ITradeDataItem message)
{ {
decimal longStopLossShift = message.Value * 0.99m; var additionalShift = message.Price * 0.001m;
decimal longTakeProfitShift = message.Value * 1.03m; var longStopLossShift = message.Price * 0.0025m;
decimal shortStopLossShift = message.Value * 0.99m; var longTakeProfitShift = message.Price * 0.02m;
decimal shortTakeProfitShift = message.Value * 1.03m; var shortStopLossShift = message.Price * 0.0025m;
var shortTakeProfitShift = message.Price * 0.02m;
if (SupportLevels.TryGetValue(message.Figi, out var levels)) if (SupportLevels.TryGetValue(message.Figi, out var levels))
{ {
if (levels.Length > 0) if (levels.Length > 0)
@ -564,16 +645,29 @@ namespace KLHZ.Trader.Core.Exchange.Services
if (message.Price >= levelsByTime[0].LowValue && message.Price < levelsByTime[0].HighValue) if (message.Price >= levelsByTime[0].LowValue && message.Price < levelsByTime[0].HighValue)
{ {
longStopLossShift = message.Price - levelsByTime[0].LowValue; longStopLossShift = message.Price - levelsByTime[0].LowValue;
shortStopLossShift = message.Price + levelsByTime[0].HighValue; shortStopLossShift = levelsByTime[0].HighValue - message.Price;
} }
else else
{ {
var levelsByDiff = levels.Where(l => l.LastLevelTime.HasValue) var levelsByDiffForLong = levels.Where(l => l.LastLevelTime.HasValue)
.OrderBy(l => System.Math.Abs(l.Value - message.Price)) .OrderBy(l => l.Value - message.Price)
.ToArray(); .ToArray();
var nearestLevel = levelsByDiff[0];
longStopLossShift = message.Price - nearestLevel.HighValue; var levelsByDiffForShort = levels.Where(l => l.LastLevelTime.HasValue)
shortStopLossShift = nearestLevel.LowValue - message.Price; .OrderByDescending(l => l.Value - message.Price)
.ToArray();
var nearestLevel = levelsByDiffForLong[0];
if (message.Price > nearestLevel.HighValue)
{
longStopLossShift = message.Price - nearestLevel.HighValue + additionalShift;
}
nearestLevel = levelsByDiffForShort[0];
if (message.Price < nearestLevel.LowValue)
{
shortStopLossShift = nearestLevel.LowValue - message.Price + additionalShift;
}
} }
} }
} }
@ -613,17 +707,69 @@ namespace KLHZ.Trader.Core.Exchange.Services
var levelsByTime = levels.Where(l => l.LastLevelTime.HasValue) var levelsByTime = levels.Where(l => l.LastLevelTime.HasValue)
.OrderByDescending(l => l.LastLevelTime) .OrderByDescending(l => l.LastLevelTime)
.ToArray(); .ToArray();
var levelByTime = levelsByTime[0];
if (message.Price >= levelsByTime[0].LowValue && message.Price < levelsByTime[0].HighValue) if (message.Price >= levelByTime.LowValue && message.Price < levelByTime.HighValue)
{ {
if (message.Price > levelsByTime[0].Value) if (message.Price > levelByTime.Value)
{ {
res[TradingEvent.OpenLong] = Constants.BlockingCoefficient; res[TradingEvent.OpenLong] = Constants.BlockingCoefficient;
} }
if (message.Price < levelsByTime[0].Value) if (message.Price < levelByTime.Value)
{ {
res[TradingEvent.OpenShort] = Constants.BlockingCoefficient; res[TradingEvent.OpenShort] = Constants.BlockingCoefficient;
} }
if (_oldItems.TryGetValue(message.Figi, out var old1))
{
var islevelUsed = false;
if (_usedSupportLevelsForClosing.TryGetValue(message.Figi, out var time))
{
if (time == levelByTime.CalculatedAt)
{
islevelUsed = true;
}
}
if (!islevelUsed)
{
if (old1.Price < levelByTime.LowValue || levelByTime.CalculatedAt == message.Time)
{
res[TradingEvent.CloseLong] = Constants.ForceExecuteCoefficient;
_usedSupportLevelsForClosing[message.Figi] = levelByTime.CalculatedAt;
}
if (old1.Price > levelByTime.HighValue || levelByTime.CalculatedAt == message.Time)
{
res[TradingEvent.CloseShort] = Constants.ForceExecuteCoefficient;
_usedSupportLevelsForClosing[message.Figi] = levelByTime.CalculatedAt;
}
}
}
}
else if (_oldItems.TryGetValue(message.Figi, out var old))
{
if (old.Price >= levelByTime.LowValue && old.Price < levelByTime.HighValue)
{
var islevelUsed = false;
if (_usedSupportLevels.TryGetValue(message.Figi, out var time))
{
if (time == levelByTime.CalculatedAt)
{
islevelUsed = true;
}
}
if (!islevelUsed)
{
if (message.Price < levelByTime.LowValue)
{
res[TradingEvent.OpenShort] = Constants.ForceExecuteCoefficient;
_usedSupportLevels[message.Figi] = levelByTime.CalculatedAt;
}
else if (message.Price > levelByTime.HighValue)
{
res[TradingEvent.OpenLong] = Constants.ForceExecuteCoefficient;
_usedSupportLevels[message.Figi] = levelByTime.CalculatedAt;
}
}
}
} }
} }
} }

View File

@ -24,6 +24,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory; private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
private readonly ILogger<TraderDataProvider> _logger; private readonly ILogger<TraderDataProvider> _logger;
private readonly string[] _instrumentsFigis = []; private readonly string[] _instrumentsFigis = [];
private readonly string[] _tradingInstrumentsFigis = [];
public readonly ConcurrentDictionary<string, IOrderbook> Orderbooks = new(); public readonly ConcurrentDictionary<string, IOrderbook> Orderbooks = new();
private readonly ConcurrentDictionary<string, string> _tickersCache = new(); private readonly ConcurrentDictionary<string, string> _tickersCache = new();
@ -40,6 +41,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_logger = logger; _logger = logger;
_instrumentsFigis = options.Value.DataRecievingInstrumentsFigis.ToArray(); _instrumentsFigis = options.Value.DataRecievingInstrumentsFigis.ToArray();
_tradingInstrumentsFigis = options.Value.TradingInstrumentsFigis.ToArray();
_isDataRecievingAllowed = options.Value.ExchangeDataRecievingEnabled; _isDataRecievingAllowed = options.Value.ExchangeDataRecievingEnabled;
} }
@ -142,11 +144,11 @@ namespace KLHZ.Trader.Core.Exchange.Services
if (_isDataRecievingAllowed) if (_isDataRecievingAllowed)
{ {
var time = DateTime.UtcNow.AddHours(-5); var time = DateTime.UtcNow.AddHours(-20);
using var context1 = await _dbContextFactory.CreateDbContextAsync(); using var context1 = await _dbContextFactory.CreateDbContextAsync();
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var data = await context1.PriceChanges var data = await context1.PriceChanges
.Where(c => _instrumentsFigis.Contains(c.Figi) && c.Time >= time) .Where(c => _tradingInstrumentsFigis.Contains(c.Figi) && c.Time >= time)
.OrderBy(c => c.Time) .OrderBy(c => c.Time)
.Select(c => new TradeDataItem() .Select(c => new TradeDataItem()
{ {
@ -262,6 +264,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
Action = action, Action = action,
}, false); }, false);
} }
private async Task WritePricesTask() private async Task WritePricesTask()
{ {
var buffer1 = new List<ProcessedPrice>(); var buffer1 = new List<ProcessedPrice>();

View File

@ -23,10 +23,7 @@ namespace KLHZ.Trader.Core.Exchange.Utils
var time = TimeOnly.FromDateTime(dt); var time = TimeOnly.FromDateTime(dt);
if (day == DayOfWeek.Sunday || day == DayOfWeek.Saturday) if (day == DayOfWeek.Sunday || day == DayOfWeek.Saturday)
{ {
if (time > _openTimeHoliday && time < _closeTimeHoliday) return ExchangeState.Close;
{
return ExchangeState.Open;
}
} }
else else
{ {

View File

@ -12,7 +12,7 @@
"Token": "", "Token": "",
"ManagingAccountNamePatterns": [ "автотрейд" ], "ManagingAccountNamePatterns": [ "автотрейд" ],
"DataRecievingInstrumentsFigis": [ "BBG004730N88", "FUTIMOEXF000", "FUTGMKN09250", "FUTBR1025000", "FUTNG0925000", "FUTNASD09250" ], "DataRecievingInstrumentsFigis": [ "BBG004730N88", "FUTIMOEXF000", "FUTGMKN09250", "FUTBR1025000", "FUTNG0925000", "FUTNASD09250" ],
"TradingInstrumentsFigis": [ "FUTIMOEXF000", "BBG004730N88" ], "TradingInstrumentsFigis": [ "FUTIMOEXF000"],
"FutureComission": 0.0025, "FutureComission": 0.0025,
"ShareComission": 0.0004, "ShareComission": 0.0004,
"AccountCashPart": 0.05, "AccountCashPart": 0.05,