Compare commits

..

No commits in common. "83e1b0fe436fd75bbd0efd31ab4797ff7acf52a3" and "9114dda27f32bded2cea05a72037592cf2b45a42" have entirely different histories.

9 changed files with 72 additions and 211 deletions

View File

@ -21,7 +21,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
internal static TradingEvent CheckUptrendStart(DateTime[] times, decimal[] prices, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int boundIndex) internal static TradingEvent CheckUptrendStart(DateTime[] times, decimal[] prices, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int boundIndex)
{ {
var periodStat = GetTwoPeriodsProcessingData(times, prices, firstPeriod, secondPeriod, boundIndex, meanfullDiff); var periodStat = GetTwoPeriodsProcessingData(times, prices, firstPeriod, secondPeriod, boundIndex, meanfullDiff);
var isStartOk = periodStat.Success && periodStat.DiffStart < 0.5m * meanfullDiff; var isStartOk = periodStat.Success && periodStat.DiffStart < -meanfullDiff;
var isEndOk = periodStat.Success && periodStat.DiffEnd >= meanfullDiff; var isEndOk = periodStat.Success && periodStat.DiffEnd >= meanfullDiff;
return isStartOk && isEndOk && prices[periodStat.Start] - prices[periodStat.End] >= meanfullDiff ? TradingEvent.UptrendStart : TradingEvent.None; return isStartOk && isEndOk && prices[periodStat.Start] - prices[periodStat.End] >= meanfullDiff ? TradingEvent.UptrendStart : TradingEvent.None;
} }

View File

@ -12,7 +12,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" /> <PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="NUnit" Version="3.14.0" /> <PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" /> <PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" /> <PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />

View File

@ -1,92 +0,0 @@
using KLHZ.Trader.Core.Exchange.Models.AssetsAccounting;
namespace KLHZ.Trader.Core.Tests
{
public class TraderTests
{
[Test]
public void IsBuyAllowedTest1()
{
var account = new ManagedAccount("111");
account.Total = 10000;
account.Balance = 9000;
account.Assets["123"] = new Asset()
{
AccountId = account.AccountId,
Figi = "123",
Ticker = "123",
Type = AssetType.Futures,
};
Assert.IsTrue(KLHZ.Trader.Core.Exchange.Services.Trader.IsBuyAllowed(account, 3000, 1, 0.5m, 0.3m));
}
[Test]
public void IsBuyAllowedTest2()
{
var account = new ManagedAccount("111");
account.Total = 10000;
account.Balance = 5000;
account.Assets["123"] = new Asset()
{
AccountId = account.AccountId,
Figi = "123",
Ticker = "123",
Type = AssetType.Futures,
};
Assert.IsFalse(KLHZ.Trader.Core.Exchange.Services.Trader.IsBuyAllowed(account, 3000, 1, 0.5m, 0.3m));
}
[Test]
public void IsBuyAllowedTest3()
{
var account = new ManagedAccount("111");
account.Total = 10000;
account.Balance = 5000;
account.Assets["123"] = new Asset()
{
AccountId = account.AccountId,
Figi = "123",
Ticker = "123",
Type = AssetType.Futures,
};
Assert.IsFalse(KLHZ.Trader.Core.Exchange.Services.Trader.IsBuyAllowed(account, 1500, 2, 0.5m, 0.3m));
}
[Test]
public void IsBuyAllowedTest4()
{
var account = new ManagedAccount("111");
account.Total = 10000;
account.Balance = 3000;
account.Assets["123"] = new Asset()
{
AccountId = account.AccountId,
Figi = "123",
Ticker = "123",
Type = AssetType.Futures,
};
Assert.IsFalse(KLHZ.Trader.Core.Exchange.Services.Trader.IsBuyAllowed(account, 1500, 1, 0.5m, 0.3m));
}
[Test]
public void IsBuyAllowedTest5()
{
var account = new ManagedAccount("111");
account.Total = 10000;
account.Balance = 5000;
account.Assets["123"] = new Asset()
{
AccountId = account.AccountId,
Figi = "123",
Ticker = "123",
Type = AssetType.Common,
};
Assert.IsTrue(KLHZ.Trader.Core.Exchange.Services.Trader.IsBuyAllowed(account, 3000, 1, 0.5m, 0.1m));
}
}
}

View File

@ -1,42 +1,42 @@
namespace KLHZ.Trader.Core.Common namespace KLHZ.Trader.Core.Common
{ {
public static class BotModeSwitcher public class BotModeSwitcher
{ {
private readonly static object _locker = new(); private readonly object _locker = new();
private static bool _canSell = false; private bool _canSell = true;
private static bool _canPurchase = false; private bool _canPurchase = true;
public static bool CanSell() public bool CanSell()
{ {
lock (_locker) lock (_locker)
return _canSell; return _canSell;
} }
public static bool CanPurchase() public bool CanPurchase()
{ {
lock (_locker) lock (_locker)
return _canPurchase; return _canPurchase;
} }
public static void StopSelling() public void StopSelling()
{ {
lock (_locker) lock (_locker)
_canSell = false; _canSell = false;
} }
public static void StopPurchase() public void StopPurchase()
{ {
lock (_locker) lock (_locker)
_canPurchase = false; _canPurchase = false;
} }
public static void StartSelling() public void StartSelling()
{ {
lock (_locker) lock (_locker)
_canSell = true; _canSell = true;
} }
public static void StartPurchase() public void StartPurchase()
{ {
lock (_locker) lock (_locker)
_canPurchase = true; _canPurchase = true;

View File

@ -61,7 +61,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
if (_instrumentsFigis.Contains(future.Figi)) if (_instrumentsFigis.Contains(future.Figi))
{ {
_tickersCache.TryAdd(future.Figi, future.Ticker); _tickersCache.TryAdd(future.Figi, future.Ticker);
_assetTypesCache.TryAdd(future.Figi, AssetType.Futures); _assetTypesCache.TryAdd(future.Figi, AssetType.Common);
} }
} }

View File

@ -1,7 +1,6 @@
using KLHZ.Trader.Core.Common; using KLHZ.Trader.Core.Common;
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums; using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces; using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces; using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
using KLHZ.Trader.Core.DataLayer; using KLHZ.Trader.Core.DataLayer;
@ -28,11 +27,12 @@ namespace KLHZ.Trader.Core.Exchange.Services
public class Trader : IHostedService public class Trader : IHostedService
{ {
private readonly IDataBus _dataBus; private readonly IDataBus _dataBus;
private readonly BotModeSwitcher _botModeSwitcher;
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory; private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
private readonly TradeDataProvider _tradeDataProvider; private readonly TradeDataProvider _tradeDataProvider;
private readonly ILogger<Trader> _logger; private readonly ILogger<Trader> _logger;
internal readonly ConcurrentDictionary<string, DeferredTrade> DeferredLongOpens = new(); private readonly ConcurrentDictionary<string, DeferredTrade> DeferredLongOpens = new();
internal readonly ConcurrentDictionary<string, DeferredTrade> DeferredLongCloses = new(); private readonly ConcurrentDictionary<string, DeferredTrade> DeferredLongCloses = new();
private readonly ConcurrentDictionary<string, DateTime> OpeningStops = new(); private readonly ConcurrentDictionary<string, DateTime> OpeningStops = new();
private readonly ConcurrentDictionary<string, InstrumentSettings> Leverages = new(); private readonly ConcurrentDictionary<string, InstrumentSettings> Leverages = new();
private readonly ConcurrentDictionary<string, IPriceHistoryCacheUnit> _historyCash = new(); private readonly ConcurrentDictionary<string, IPriceHistoryCacheUnit> _historyCash = new();
@ -43,6 +43,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
private readonly decimal _shareComission; private readonly decimal _shareComission;
private readonly decimal _accountCashPart; private readonly decimal _accountCashPart;
private readonly decimal _accountCashPartFutures; private readonly decimal _accountCashPartFutures;
private readonly decimal _defaultBuyPartOfAccount;
private readonly string[] _tradingInstrumentsFigis = []; private readonly string[] _tradingInstrumentsFigis = [];
private readonly Channel<INewPrice> _pricesChannel = Channel.CreateUnbounded<INewPrice>(); private readonly Channel<INewPrice> _pricesChannel = Channel.CreateUnbounded<INewPrice>();
@ -50,6 +51,8 @@ namespace KLHZ.Trader.Core.Exchange.Services
private readonly CancellationTokenSource _cts = new(); private readonly CancellationTokenSource _cts = new();
public Trader( public Trader(
ILogger<Trader> logger, ILogger<Trader> logger,
BotModeSwitcher botModeSwitcher,
IServiceProvider provider,
IOptions<ExchangeConfig> options, IOptions<ExchangeConfig> options,
IDataBus dataBus, IDataBus dataBus,
IDbContextFactory<TraderDbContext> dbContextFactory, IDbContextFactory<TraderDbContext> dbContextFactory,
@ -58,12 +61,14 @@ namespace KLHZ.Trader.Core.Exchange.Services
{ {
_tradeDataProvider = tradeDataProvider; _tradeDataProvider = tradeDataProvider;
_logger = logger; _logger = logger;
_botModeSwitcher = botModeSwitcher;
_dataBus = dataBus; _dataBus = dataBus;
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_futureComission = options.Value.FutureComission; _futureComission = options.Value.FutureComission;
_shareComission = options.Value.ShareComission; _shareComission = options.Value.ShareComission;
_accountCashPart = options.Value.AccountCashPart; _accountCashPart = options.Value.AccountCashPart;
_accountCashPartFutures = options.Value.AccountCashPartFutures; _accountCashPartFutures = options.Value.AccountCashPartFutures;
_defaultBuyPartOfAccount = options.Value.DefaultBuyPartOfAccount;
_tradingInstrumentsFigis = options.Value.TradingInstrumentsFigis; _tradingInstrumentsFigis = options.Value.TradingInstrumentsFigis;
_buyStopLength = (double)options.Value.StopBuyLengthMinuts; _buyStopLength = (double)options.Value.StopBuyLengthMinuts;
@ -120,31 +125,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
DeferredLongOpens.TryRemove(message.Figi, out _); DeferredLongOpens.TryRemove(message.Figi, out _);
if (message.Value - longOpen.Price < 1) if (message.Value - longOpen.Price < 1)
{ {
if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase()) LogDeclision(declisionsForSave, DeclisionTradeAction.OpenLong, message);
{
var accounts = _tradeDataProvider.Accounts
.Where(a => !a.Value.Assets.ContainsKey(message.Figi))
.ToArray();
foreach (var acc in accounts)
{
if (IsBuyAllowed(acc.Value, message.Value, 1, _accountCashPartFutures, _accountCashPart))
{
await _dataBus.Broadcast(new TradeCommand()
{
AccountId = acc.Value.AccountId,
Figi = message.Figi,
CommandType = Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketBuy,
Count = 1,
RecomendPrice = null,
});
LogDeclision(declisionsForSave, DeclisionTradeAction.OpenLong, message);
}
}
}
else
{
LogDeclision(declisionsForSave, DeclisionTradeAction.OpenLong, message);
}
} }
} }
} }
@ -158,45 +139,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
DeferredLongCloses.TryRemove(message.Figi, out _); DeferredLongCloses.TryRemove(message.Figi, out _);
if (longClose.Price - message.Value < 1) if (longClose.Price - message.Value < 1)
{ {
var assetType = _tradeDataProvider.GetAssetTypeByFigi(message.Figi); LogDeclision(declisionsForSave, DeclisionTradeAction.CloseLong, message);
if (!message.IsHistoricalData && BotModeSwitcher.CanSell())
{
var assetsForClose = _tradeDataProvider.Accounts
.SelectMany(a => a.Value.Assets.Values)
.Where(a => a.Figi == message.Figi && a.Count > 0)
.ToArray();
foreach (var asset in assetsForClose)
{
var profit = 0m;
if (assetType == AssetType.Common && asset.Count > 0)
{
profit = TradingCalculator.CaclProfit(asset.BoughtPrice, message.Value,
GetComission(assetType), 1, false);
}
if (assetType == AssetType.Futures)
{
profit = TradingCalculator.CaclProfit(asset.BoughtPrice, message.Value,
GetComission(assetType), GetLeverage(message.Figi, asset.Count < 0), asset.Count < 0);
}
if (profit > 0)
{
await _dataBus.Broadcast(new TradeCommand()
{
AccountId = asset.AccountId,
Figi = message.Figi,
CommandType = Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketSell,
Count = (long)asset.Count,
RecomendPrice = null,
});
LogDeclision(declisionsForSave, DeclisionTradeAction.CloseLong, message);
}
}
}
else
{
LogDeclision(declisionsForSave, DeclisionTradeAction.CloseLong, message);
}
} }
} }
} }
@ -206,7 +149,6 @@ namespace KLHZ.Trader.Core.Exchange.Services
var state = ExchangeScheduler.GetCurrentState(message.Time); var state = ExchangeScheduler.GetCurrentState(message.Time);
if (state == ExchangeState.ClearingTime if (state == ExchangeState.ClearingTime
&& !message.IsHistoricalData
&& data.timestamps.Length > 1 && data.timestamps.Length > 1
&& (data.timestamps[data.timestamps.Length - 1] - data.timestamps[data.timestamps.Length - 2]) > TimeSpan.FromMinutes(3)) && (data.timestamps[data.timestamps.Length - 1] - data.timestamps[data.timestamps.Length - 2]) > TimeSpan.FromMinutes(3))
{ {
@ -232,27 +174,15 @@ namespace KLHZ.Trader.Core.Exchange.Services
var resultMoveAvFull = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 45, 180, 2.5m); var resultMoveAvFull = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 45, 180, 2.5m);
var resultLongClose = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 15, 120, 2.5m).events; var resultLongClose = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 15, 120, 2.5m).events;
var uptrendStarts = LocalTrends.CheckByLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(20), 1.5m, 15); var uptrendStarts = LocalTrends.CheckByLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(15), 2m, 10);
//var uptrendStarts2 = LocalTrends.CheckByLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(3), 1.5m, 2); //var uptrendStarts2 = LocalTrends.CheckByLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(3), 1.5m, 2);
res |= (uptrendStarts & TradingEvent.UptrendStart); res |= (uptrendStarts & TradingEvent.UptrendStart);
//res |= (uptrendStarts2 & TradingEvent.UptrendStart);
//res |= downtrendEnds;
res |= resultLongClose; res |= resultLongClose;
res |= resultMoveAvFull.events; res |= resultMoveAvFull.events;
//res = TradingEvent.None;
//if (!stopBuy &&RandomNumberGenerator.GetInt32(100) < 20)
//{
// res |= TradingEvent.UptrendStart;
//}
//else if (!stopSell && (RandomNumberGenerator.GetInt32(100) < 20))
//{
// res |= TradingEvent.UptrendEnd;
//}
if (resultMoveAvFull.bigWindowAv != 0) if (resultMoveAvFull.bigWindowAv != 0)
{ {
LogPrice(processedPrices, message, bigWindowProcessor, resultMoveAvFull.bigWindowAv); LogPrice(processedPrices, message, bigWindowProcessor, resultMoveAvFull.bigWindowAv);
@ -260,7 +190,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
} }
if ((resultLongClose & TradingEvent.StopBuy) == TradingEvent.StopBuy) if ((resultLongClose & TradingEvent.StopBuy) == TradingEvent.StopBuy)
{ {
var stopTo = (message.IsHistoricalData ? message.Time : DateTime.UtcNow).AddMinutes(_buyStopLength / 2); var stopTo = (message.IsHistoricalData ? message.Time : DateTime.UtcNow).AddMinutes(_buyStopLength);
OpeningStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo); OpeningStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo);
//LogDeclision(declisionsForSave, DeclisionTradeAction.StopBuy, message); //LogDeclision(declisionsForSave, DeclisionTradeAction.StopBuy, message);
} }
@ -403,32 +333,52 @@ namespace KLHZ.Trader.Core.Exchange.Services
} }
} }
private decimal GetLeverage(string figi, bool isShort) private decimal CalcProfit(string accountId, string figi, decimal closePrice)
{ {
var res = 1m; if (_tradeDataProvider.Accounts.TryGetValue(accountId, out var account))
if (Leverages.TryGetValue(figi, out var leverage))
{ {
res = isShort ? leverage.ShortLeverage : leverage.LongLeverage; if (account.Assets.TryGetValue(figi, out var asset))
{
var leverageValue = 1m;
var isShort = asset.Position == PositionType.Short;
if (Leverages.TryGetValue(figi, out var leverage))
{
if (asset.Type == AssetType.Futures && !isShort)
{
leverageValue = leverage.LongLeverage;
}
else if (isShort)
{
leverageValue = leverage.ShortLeverage;
}
}
return TradingCalculator.CaclProfit(asset.BoughtPrice, closePrice, GetComission(asset.Type), leverageValue, isShort);
}
} }
return res; return 0;
} }
internal static bool IsBuyAllowed(ManagedAccount account, decimal boutPrice, decimal count, private decimal GetCount(string accountId, decimal boutPrice)
decimal accountCashPartFutures, decimal accountCashPart)
{ {
if (!BotModeSwitcher.CanPurchase()) return false; var balance = _tradeDataProvider.Accounts[accountId].Balance;
return System.Math.Floor(balance * _defaultBuyPartOfAccount / boutPrice);
}
var balance = account.Balance; private bool IsBuyAllowed(string accountId, decimal boutPrice, decimal count, bool needBigCash)
var total = account.Total; {
if (!_botModeSwitcher.CanPurchase()) return false;
var futures = account.Assets.Values.FirstOrDefault(v => v.Type == AssetType.Futures); var balance = _tradeDataProvider.Accounts[accountId].Balance;
if (futures != null) var total = _tradeDataProvider.Accounts[accountId].Total;
var futures = _tradeDataProvider.Accounts[accountId].Assets.Values.FirstOrDefault(v => v.Type == AssetType.Futures);
if (futures != null || needBigCash)
{ {
if ((balance - boutPrice * count) / total < accountCashPartFutures) return false; if ((balance - boutPrice * count) / total < _accountCashPartFutures) return false;
} }
else else
{ {
if ((balance - boutPrice * count) / total < accountCashPart) return false; if ((balance - boutPrice * count) / total < _accountCashPart) return false;
} }
return true; return true;

View File

@ -10,11 +10,11 @@ namespace KLHZ.Trader.Core.Exchange.Utils
private readonly static TimeOnly _openTimeHoliday = new(7, 10); private readonly static TimeOnly _openTimeHoliday = new(7, 10);
private readonly static TimeOnly _closeTimeHoliday = new(17, 45); private readonly static TimeOnly _closeTimeHoliday = new(17, 45);
private readonly static TimeOnly _firstClearingStart = new(10, 45); private readonly static TimeOnly _firstClearingStart = new(10, 55);
private readonly static TimeOnly _firstClearingEnd = new(11, 10); private readonly static TimeOnly _firstClearingEnd = new(11, 10);
private readonly static TimeOnly _mainClearingStart = new(15, 45); private readonly static TimeOnly _mainClearingStart = new(15, 50);
private readonly static TimeOnly _mainClearingEnd = new(16, 10); private readonly static TimeOnly _mainClearingEnd = new(16, 5);
internal static ExchangeState GetCurrentState(DateTime? currentDt = null) internal static ExchangeState GetCurrentState(DateTime? currentDt = null)
{ {

View File

@ -15,11 +15,13 @@ namespace KLHZ.Trader.Core.TG.Services
public class BotMessagesHandler : IUpdateHandler public class BotMessagesHandler : IUpdateHandler
{ {
private readonly ImmutableArray<long> _admins = []; private readonly ImmutableArray<long> _admins = [];
private readonly BotModeSwitcher _botModeSwitcher;
private readonly IDataBus _eventBus; private readonly IDataBus _eventBus;
private readonly ILogger<BotMessagesHandler> _logger; private readonly ILogger<BotMessagesHandler> _logger;
public BotMessagesHandler(IDataBus eventBus, IOptions<TgBotConfig> options, ILogger<BotMessagesHandler> logger) public BotMessagesHandler(BotModeSwitcher botModeSwitcher, IDataBus eventBus, IOptions<TgBotConfig> options, ILogger<BotMessagesHandler> logger)
{ {
_logger = logger; _logger = logger;
_botModeSwitcher = botModeSwitcher;
_eventBus = eventBus; _eventBus = eventBus;
_admins = ImmutableArray.CreateRange(options.Value.Admins); _admins = ImmutableArray.CreateRange(options.Value.Admins);
} }
@ -50,25 +52,25 @@ namespace KLHZ.Trader.Core.TG.Services
} }
case Constants.BotCommandsButtons.EnableSelling: case Constants.BotCommandsButtons.EnableSelling:
{ {
BotModeSwitcher.StartSelling(); _botModeSwitcher.StartSelling();
await botClient.SendMessage(update.Message.Chat, "Продажи начаты!"); await botClient.SendMessage(update.Message.Chat, "Продажи начаты!");
break; break;
} }
case Constants.BotCommandsButtons.DisableSelling: case Constants.BotCommandsButtons.DisableSelling:
{ {
BotModeSwitcher.StopSelling(); _botModeSwitcher.StopSelling();
await botClient.SendMessage(update.Message.Chat, "Продажи остановлены!"); await botClient.SendMessage(update.Message.Chat, "Продажи остановлены!");
break; break;
} }
case Constants.BotCommandsButtons.EnablePurchases: case Constants.BotCommandsButtons.EnablePurchases:
{ {
BotModeSwitcher.StartPurchase(); _botModeSwitcher.StartPurchase();
await botClient.SendMessage(update.Message.Chat, "Покупки начаты!"); await botClient.SendMessage(update.Message.Chat, "Покупки начаты!");
break; break;
} }
case Constants.BotCommandsButtons.DisablePurchases: case Constants.BotCommandsButtons.DisablePurchases:
{ {
BotModeSwitcher.StopPurchase(); _botModeSwitcher.StopPurchase();
await botClient.SendMessage(update.Message.Chat, "Покупки остановлены!"); await botClient.SendMessage(update.Message.Chat, "Покупки остановлены!");
break; break;
} }

View File

@ -1,3 +1,4 @@
using KLHZ.Trader.Core.Common;
using KLHZ.Trader.Core.Common.Messaging.Services; using KLHZ.Trader.Core.Common.Messaging.Services;
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces; using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
using KLHZ.Trader.Core.DataLayer; using KLHZ.Trader.Core.DataLayer;
@ -52,6 +53,7 @@ builder.Services.AddHostedService<TradingCommandsExecutor>();
builder.Services.AddSingleton<IUpdateHandler, BotMessagesHandler>(); builder.Services.AddSingleton<IUpdateHandler, BotMessagesHandler>();
builder.Services.AddSingleton<TradeDataProvider>(); builder.Services.AddSingleton<TradeDataProvider>();
builder.Services.AddSingleton<BotModeSwitcher>();
builder.Services.AddSingleton<IDataBus, DataBus>(); builder.Services.AddSingleton<IDataBus, DataBus>();
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)