добавлена обработка расписания торгов
parent
ccc44c0f57
commit
9749791b4c
|
@ -0,0 +1,63 @@
|
|||
using KLHZ.Trader.Core.Exchange.Utils;
|
||||
|
||||
namespace KLHZ.Trader.Core.Tests
|
||||
{
|
||||
public class ExchangeSchedulerTests
|
||||
{
|
||||
[Test]
|
||||
public void Test1()
|
||||
{
|
||||
var dt = new DateTime(2025, 9, 6, 0, 0, 0, DateTimeKind.Utc);
|
||||
var res = ExchangeScheduler.GetCurrentState(dt);
|
||||
Assert.IsTrue(res == Exchange.Models.ExchangeState.Close);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test2()
|
||||
{
|
||||
var dt = new DateTime(2025, 9, 5, 7, 0, 0, DateTimeKind.Utc);
|
||||
var res = ExchangeScheduler.GetCurrentState(dt);
|
||||
Assert.IsTrue(res == Exchange.Models.ExchangeState.Open);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test3()
|
||||
{
|
||||
var dt = new DateTime(2025, 9, 5, 6, 0, 0, DateTimeKind.Utc);
|
||||
var res = ExchangeScheduler.GetCurrentState(dt);
|
||||
Assert.IsTrue(res == Exchange.Models.ExchangeState.Close);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test4()
|
||||
{
|
||||
var dt = new DateTime(2025, 9, 5, 11, 0, 0, DateTimeKind.Utc);
|
||||
var res = ExchangeScheduler.GetCurrentState(dt);
|
||||
Assert.IsTrue(res == Exchange.Models.ExchangeState.ClearingTime);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test5()
|
||||
{
|
||||
var dt = new DateTime(2025, 9, 7, 11, 0, 0, DateTimeKind.Utc);
|
||||
var res = ExchangeScheduler.GetCurrentState(dt);
|
||||
Assert.IsTrue(res == Exchange.Models.ExchangeState.Open);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test6()
|
||||
{
|
||||
var dt = new DateTime(2025, 9, 5, 16, 0, 0, DateTimeKind.Utc);
|
||||
var res = ExchangeScheduler.GetCurrentState(dt);
|
||||
Assert.IsTrue(res == Exchange.Models.ExchangeState.ClearingTime);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test7()
|
||||
{
|
||||
var dt = new DateTime(2025, 9, 7, 15, 0, 0, DateTimeKind.Utc);
|
||||
var res = ExchangeScheduler.GetCurrentState(dt);
|
||||
Assert.IsTrue(res == Exchange.Models.ExchangeState.Open);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
{
|
||||
public class Asset
|
||||
{
|
||||
public long? TradeId { get; init; }
|
||||
public decimal BlockedItems { get; init; }
|
||||
public AssetType Type { get; init; }
|
||||
public PositionType Position { get; init; }
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
namespace KLHZ.Trader.Core.Exchange.Models
|
||||
{
|
||||
internal enum ExchangeState
|
||||
{
|
||||
None,
|
||||
Open,
|
||||
Close,
|
||||
ClearingTime
|
||||
}
|
||||
}
|
|
@ -128,6 +128,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
#pragma warning disable CS0612 // Тип или член устарел
|
||||
var asset = new Models.Assets.Asset()
|
||||
{
|
||||
TradeId = trade?.Id,
|
||||
AccountId = AccountId,
|
||||
Figi = position.Figi,
|
||||
Ticker = position.Ticker,
|
||||
|
|
|
@ -11,6 +11,7 @@ using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
|||
using KLHZ.Trader.Core.Exchange.Extentions;
|
||||
using KLHZ.Trader.Core.Exchange.Models;
|
||||
using KLHZ.Trader.Core.Exchange.Models.Assets;
|
||||
using KLHZ.Trader.Core.Exchange.Utils;
|
||||
using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
|
||||
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
@ -110,11 +111,14 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
var processedPrices = new List<ProcessedPrice>();
|
||||
while (await _pricesChannel.Reader.WaitToReadAsync())
|
||||
{
|
||||
|
||||
var bigWindowProcessor = nameof(Trader) + "_big";
|
||||
var smallWindowProcessor = nameof(Trader) + "_small";
|
||||
var message = await _pricesChannel.Reader.ReadAsync();
|
||||
|
||||
if (_tradingInstrumentsFigis.Contains(message.Figi))
|
||||
{
|
||||
var currentTime = message.IsHistoricalData ? message.Time : DateTime.UtcNow;
|
||||
if (_historyCash.TryGetValue(message.Figi, out var unit))
|
||||
{
|
||||
await unit.AddData(message);
|
||||
|
@ -128,12 +132,11 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
{
|
||||
if (message.Figi == "FUTIMOEXF000")
|
||||
{
|
||||
|
||||
DeferredTrade? longOpen;
|
||||
DeferredLongOpens.TryGetValue(message.Figi, out longOpen);
|
||||
if (longOpen != null)
|
||||
{
|
||||
var t = message.IsHistoricalData ? message.Time : DateTime.UtcNow;
|
||||
var t = currentTime;
|
||||
if (longOpen.Time <= t
|
||||
&& t - longOpen.Time < TimeSpan.FromMinutes(3))
|
||||
{
|
||||
|
@ -149,7 +152,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
DeferredLongCloses.TryGetValue(message.Figi, out longClose);
|
||||
if (longClose != null)
|
||||
{
|
||||
if (longClose.Time <= (message.IsHistoricalData ? message.Time : DateTime.UtcNow))
|
||||
if (longClose.Time <= currentTime)
|
||||
{
|
||||
DeferredLongCloses.TryRemove(message.Figi, out _);
|
||||
if (longClose.Price - message.Value < 1)
|
||||
|
@ -161,9 +164,18 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
|
||||
var windowMaxSize = 100;
|
||||
var data = await unit.GetData(windowMaxSize);
|
||||
var state = ExchangeScheduler.GetCurrentState(message.Time);
|
||||
|
||||
if (state == ExchangeState.ClearingTime
|
||||
&& data.timestamps.Length > 1
|
||||
&& (data.timestamps[data.timestamps.Length - 1] - data.timestamps[data.timestamps.Length - 2]) > TimeSpan.FromMinutes(3))
|
||||
{
|
||||
await UpdateFuturesPrice(message, data.prices[data.prices.Length - 2]);
|
||||
}
|
||||
|
||||
if (OpeningStops.TryGetValue(message.Figi, out var dt))
|
||||
{
|
||||
if (dt < (message.IsHistoricalData ? message.Time : DateTime.UtcNow))
|
||||
if (dt < currentTime)
|
||||
{
|
||||
OpeningStops.TryRemove(message.Figi, out _);
|
||||
}
|
||||
|
@ -171,7 +183,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
|
||||
if ((unit.BidsCount / unit.AsksCount) < 0.5m || (unit.BidsCount / unit.AsksCount) > 2m)
|
||||
{
|
||||
var stopTo = (message.IsHistoricalData ? message.Time : DateTime.UtcNow).AddMinutes(3);
|
||||
var stopTo = currentTime.AddMinutes(3);
|
||||
//OpeningStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo);
|
||||
//LogDeclision(declisionsForSave, DeclisionTradeAction.StopBuyShortTime, message);
|
||||
}
|
||||
|
@ -203,6 +215,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
|
||||
if ((res & TradingEvent.UptrendStart) == TradingEvent.UptrendStart
|
||||
&& !OpeningStops.TryGetValue(message.Figi, out _)
|
||||
&& state == ExchangeState.Open
|
||||
&& data.timestamps.Length > 1
|
||||
&& (data.timestamps[data.timestamps.Length - 1] - data.timestamps[data.timestamps.Length - 2] < TimeSpan.FromMinutes(1)))
|
||||
{
|
||||
|
@ -265,6 +278,18 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
}
|
||||
}
|
||||
|
||||
private async Task UpdateFuturesPrice(INewPrice newPrice, decimal newPriceValue)
|
||||
{
|
||||
using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
await context.Trades
|
||||
.Where(t => t.Figi == newPrice.Figi && t.ArchiveStatus == 0)
|
||||
.ExecuteUpdateAsync(t => t.SetProperty(tr => tr.Price, newPriceValue));
|
||||
foreach (var account in Accounts.Values)
|
||||
{
|
||||
await account.SyncPortfolio();
|
||||
}
|
||||
}
|
||||
|
||||
private static void LogPrice(List<ProcessedPrice> prices, INewPrice message, string processor, decimal value)
|
||||
{
|
||||
prices.Add(new ProcessedPrice()
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
using KLHZ.Trader.Core.Exchange.Models;
|
||||
|
||||
namespace KLHZ.Trader.Core.Exchange.Utils
|
||||
{
|
||||
internal static class ExchangeScheduler
|
||||
{
|
||||
private readonly static TimeOnly _openTimeMain = new(6, 10);
|
||||
private readonly static TimeOnly _closeTimeMain = new(20, 45);
|
||||
|
||||
private readonly static TimeOnly _openTimeHoliday = new(7, 10);
|
||||
private readonly static TimeOnly _closeTimeHoliday = new(17, 45);
|
||||
|
||||
private readonly static TimeOnly _firstClearingStart = new(10, 55);
|
||||
private readonly static TimeOnly _firstClearingEnd = new(11, 10);
|
||||
|
||||
private readonly static TimeOnly _mainClearingStart = new(15, 50);
|
||||
private readonly static TimeOnly _mainClearingEnd = new(16, 5);
|
||||
|
||||
internal static ExchangeState GetCurrentState(DateTime? currentDt = null)
|
||||
{
|
||||
var dt = currentDt ?? DateTime.UtcNow;
|
||||
var day = dt.DayOfWeek;
|
||||
|
||||
var time = TimeOnly.FromDateTime(dt);
|
||||
if (day == DayOfWeek.Sunday || day == DayOfWeek.Saturday)
|
||||
{
|
||||
if (time > _openTimeHoliday && time < _closeTimeHoliday)
|
||||
{
|
||||
return ExchangeState.Open;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (time > _openTimeMain && time < _closeTimeMain)
|
||||
{
|
||||
|
||||
if (time > _firstClearingStart && time < _firstClearingEnd || time > _mainClearingStart && time < _mainClearingEnd)
|
||||
{
|
||||
return ExchangeState.ClearingTime;
|
||||
}
|
||||
return ExchangeState.Open;
|
||||
}
|
||||
}
|
||||
|
||||
return ExchangeState.Close;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue