using KLHZ.Trader.Core.Common; using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums; using KLHZ.Trader.Core.Contracts.Messaging.Dtos; using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Enums; using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces; using KLHZ.Trader.Core.Contracts.Messaging.Interfaces; using KLHZ.Trader.Core.DataLayer.Entities.Declisions; using KLHZ.Trader.Core.DataLayer.Entities.Declisions.Enums; using KLHZ.Trader.Core.DataLayer.Entities.Prices; using KLHZ.Trader.Core.Exchange.Interfaces; using KLHZ.Trader.Core.Exchange.Models.Configs; using KLHZ.Trader.Core.Exchange.Models.Trading; using KLHZ.Trader.Core.Exchange.Utils; using KLHZ.Trader.Core.Math.Declisions.Utils; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Collections.Concurrent; using System.Collections.Immutable; using System.Security.Cryptography; using System.Threading.Channels; using Tinkoff.InvestApi; using Asset = KLHZ.Trader.Core.Exchange.Models.AssetsAccounting.Asset; using AssetType = KLHZ.Trader.Core.Exchange.Models.AssetsAccounting.AssetType; using PositionType = KLHZ.Trader.Core.Exchange.Models.AssetsAccounting.PositionType; namespace KLHZ.Trader.Core.Exchange.Services { public class Trader : IHostedService { private readonly IDataBus _dataBus; private readonly TraderDataProvider _tradeDataProvider; private readonly PortfolioWrapper _portfolioWrapper; private readonly ExchangeConfig _exchangeConfig; private readonly ILogger _logger; private readonly ConcurrentDictionary TradingModes = new(); private readonly ConcurrentDictionary DPirsonValues = new(); private readonly ConcurrentDictionary LongOpeningStops = new(); private readonly ConcurrentDictionary ShortOpeningStops = new(); private readonly ConcurrentDictionary LongClosingStops = new(); private readonly ConcurrentDictionary ShortClosingStops = new(); private readonly Channel _pricesChannel = Channel.CreateUnbounded(); private readonly Channel _commands = Channel.CreateUnbounded(); private readonly Channel _orderbooks = Channel.CreateUnbounded(); public Trader( ILogger logger, IOptions options, IDataBus dataBus, PortfolioWrapper portfolioWrapper, TraderDataProvider tradeDataProvider, InvestApiClient investApiClient) { _portfolioWrapper = portfolioWrapper; _tradeDataProvider = tradeDataProvider; _logger = logger; _dataBus = dataBus; _exchangeConfig = options.Value; foreach (var f in _exchangeConfig.TradingInstrumentsFigis) { TradingModes[f] = TradingMode.None; } } public Task StartAsync(CancellationToken cancellationToken) { _dataBus.AddChannel(nameof(Trader), _pricesChannel); _dataBus.AddChannel(nameof(Trader), _orderbooks); _dataBus.AddChannel(nameof(Trader), _commands); _ = ProcessPrices(); _ = ProcessOrderbooks(); _ = ProcessCommands(); return Task.CompletedTask; } private async Task ProcessCommands() { while (await _commands.Reader.WaitToReadAsync()) { var command = await _commands.Reader.ReadAsync(); try { if (command.CommandType == Contracts.Messaging.Dtos.Enums.TradeCommandType.OpenLong || 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 positionType = command.CommandType == TradeCommandType.OpenLong ? PositionType.Long : PositionType.Short; var stops = GetStops(fakeMessage, positionType); var accounts = _portfolioWrapper.Accounts .Where(a => !a.Value.Assets.ContainsKey(command.Figi)) .Take(1) .Select(a => a.Value) .ToArray(); await OpenPositions(accounts, fakeMessage, positionType, stops.stopLoss, stops.takeProfit, System.Math.Abs(command.Count)); } else { var fakeMessage = new NewPriceMessage() { Figi = command.Figi, Ticker = "", Count = command.Count, Direction = 1, IsHistoricalData = false, Time = DateTime.UtcNow, Value = command.RecomendPrice ?? 0m }; var assetsForClose = _portfolioWrapper.Accounts .SelectMany(a => a.Value.Assets.Values) .Where(a => a.Figi == fakeMessage.Figi) .ToArray(); await ClosePositions(assetsForClose, fakeMessage, false); } } catch (Exception ex) { } } } private async Task ProcessOrderbooks() { while (await _orderbooks.Reader.WaitToReadAsync()) { var message = await _orderbooks.Reader.ReadAsync(); await _tradeDataProvider.AddOrderbook(message); } } private async Task ProcessPrices() { var pricesCache1 = new Dictionary>(); var pricesCache2 = new Dictionary>(); var timesCache = new Dictionary(); while (await _pricesChannel.Reader.WaitToReadAsync()) { var message = await _pricesChannel.Reader.ReadAsync(); if (!message.IsHistoricalData && DateTime.UtcNow - message.Time > TimeSpan.FromMinutes(1)) { continue; } var changeMods = TraderUtils.GetInitDict(0); try { message = TraderUtils.FilterHighFreqValues(message, message.Direction == 1 ? pricesCache1 : pricesCache2); #region Добавление данных в кеши. if (message.Figi == "BBG004730N88" || message.Figi == "FUTIMOEXF000") { if (message.Direction == 1) { await _tradeDataProvider.AddDataTo20SecondsWindowCache(message.Figi, "1", new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, Count = message.Count, Price = message.Value, }); await _tradeDataProvider.AddDataTo5MinuteWindowCache(message.Figi, Constants._5minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, Count = (decimal)message.Count, Price = message.Value, }); await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, Constants._15minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, Count = (decimal)message.Count, Price = message.Value, }); await _tradeDataProvider.AddDataTo1MinuteWindowCache(message.Figi, Constants._1minBuyCacheKey, new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, Count = (decimal)message.Count, Price = message.Value, }); } if (message.Direction == 2) { await _tradeDataProvider.AddDataTo5MinuteWindowCache(message.Figi, Constants._5minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, Count = (decimal)message.Count, Price = message.Value, }); await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, Constants._15minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, Count = (decimal)message.Count, Price = message.Value, }); await _tradeDataProvider.AddDataTo1MinuteWindowCache(message.Figi, Constants._1minSellCacheKey, new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, Count = (decimal)message.Count, Price = message.Value, }); await _tradeDataProvider.AddDataTo20SecondsWindowCache(message.Figi, "2", new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, Count = message.Count, Price = message.Value, }); } } #endregion if (_exchangeConfig.TradingInstrumentsFigis.Contains(message.Figi) && (message.Figi == "FUTIMOEXF000" || message.Figi == "BBG004730N88") && message.Direction == 1) { var smallWindow = TimeSpan.FromSeconds(120); var bigWindow = TimeSpan.FromSeconds(240); var meanWindow = TimeSpan.FromSeconds(240); 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"); if (diffs.TryCalcTimeWindowsDiff(bigWindow, smallWindow, (c) => c.Price, true, out var resdp) && diffs.TryCalcTimeWindowsDiff(bigWindow, smallWindow, (c) => c.Count, true, out var resv)) { await LogPrice(message, "privcesDiffDiff", (decimal)resdp); await LogPrice(message, "tradevolume_diff_diff", (decimal)resv); await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, "5min_diff_diff", new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, Count = resv, Price = resdp, }); if (diffs.TryCalcPirsonCorrelation(meanWindow, out var pirson)) { await LogPrice(message, "diffs_pirson", (decimal)pirson); await _tradeDataProvider.AddDataTo15MinuteWindowCache(message.Figi, "diffs_pirson", new Contracts.Declisions.Dtos.CachedValue() { Time = message.Time, 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", 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; } } } } 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) { } } } private async Task ClosePositions(Asset[] assets, INewPrice message, bool withProfitOnly = true) { var loggedDeclisions = 0; var assetType = _tradeDataProvider.GetAssetTypeByFigi(message.Figi); var assetsForClose = new List(); var price = message.Value; if (price == 0) { price = await _tradeDataProvider.GetLastPrice(message.Figi); } price = System.Math.Round(price, 2); var messages = new List(); foreach (var asset in assets) { if (withProfitOnly) { var profit = 0m; if (assetType == AssetType.Futures) { if (_tradeDataProvider.Orderbooks.TryGetValue(message.Figi, out var orderbook)) { if (asset.Count < 0 && orderbook.Asks.Length > 0) { price = orderbook.Asks[0].Price; } else if (orderbook.Bids.Length > 0) { price = orderbook.Bids[0].Price; } } profit = TradingCalculator.CaclProfit(asset.BoughtPrice, price, GetComission(assetType), GetLeverage(message.Figi, asset.Count < 0), asset.Count < 0); } if (profit > 0) { profit = System.Math.Round(profit, 2); assetsForClose.Add(asset); messages.Add($"Закрываю позицию {asset.Figi} ({(asset.Count > 0 ? "лонг" : "шорт")}) на счёте {_portfolioWrapper.Accounts[asset.AccountId].AccountName}. Количество {(long)asset.Count}, цена ~{price}, профит {profit}"); if (loggedDeclisions == 0) { loggedDeclisions++; await LogDeclision(asset.Count < 0 ? DeclisionTradeAction.CloseShortReal : DeclisionTradeAction.CloseLongReal, message, profit); } } } else { messages.Add($"Закрываю позицию {asset.Figi} ({(asset.Count > 0 ? "лонг" : "шорт")}) на счёте {_portfolioWrapper.Accounts[asset.AccountId].AccountName}. Количество {(long)asset.Count}, цена ~{price}"); assetsForClose.Add(asset); } } for (int i = 0; i < assetsForClose.Count; i++) { var asset = assetsForClose[i]; await _portfolioWrapper.Accounts[asset.AccountId].ClosePosition(message.Figi); await _dataBus.Broadcast(new MessageForAdmin() { Text = messages[i] }); } } private async Task OpenPositions(IManagedAccount[] accounts, INewPrice message, PositionType positionType, decimal stopLossShift, decimal takeProfitShift, long count = 1) { var loggedDeclisions = 0; var sign = positionType == PositionType.Long ? 1 : 1; foreach (var acc in accounts) { if (TraderUtils.IsOperationAllowed(acc, message.Value, 1, _exchangeConfig.AccountCashPartFutures, _exchangeConfig.AccountCashPart)) { await acc.OpenPosition(message.Figi, positionType, stopLossShift, takeProfitShift, count); await _dataBus.Broadcast(new MessageForAdmin() { Text = $"Открываю позицию {message.Figi} ({(positionType == PositionType.Long ? "лонг" : "шорт")}) " + $"на счёте {acc.AccountName}. Количество {(positionType == PositionType.Long ? "" : "-")}{count}, " + $"цена ~{System.Math.Round(message.Value, 2)}. Стоп лосс: {(positionType == PositionType.Long ? "-" : "+")}{stopLossShift}. " + $"Тейк профит: {(positionType == PositionType.Long ? "+" : "-")}{takeProfitShift}" }); } if (loggedDeclisions == 0) { await LogDeclision(DeclisionTradeAction.OpenLongReal, message); LongOpeningStops[message.Figi] = message.Time.AddMinutes(1); loggedDeclisions++; } } } private async Task ExecuteDeclisions(INewPrice message, ImmutableDictionary result) { var state = ExchangeScheduler.GetCurrentState(); if (result[TradingEvent.UptrendStart] >= Constants.UppingCoefficient && !LongOpeningStops.ContainsKey(message.Figi) && state == ExchangeState.Open ) { var stops = GetStops(message, PositionType.Long); if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase()) { var accounts = _portfolioWrapper.Accounts .Where(a => !a.Value.Assets.ContainsKey(message.Figi)) .Take(1) .Select(a => a.Value) .ToArray(); 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.ResetStopsLong, message.Value + 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); } if (result[TradingEvent.DowntrendStart] >= Constants.UppingCoefficient && !ShortOpeningStops.ContainsKey(message.Figi) && state == ExchangeState.Open ) { var stops = GetStops(message, PositionType.Short); if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase()) { var accounts = _portfolioWrapper.Accounts .Where(a => !a.Value.Assets.ContainsKey(message.Figi)) .Take(1) .Select(a => a.Value) .ToArray(); 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.ResetStopsShort, message.Value - 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); } if (result[TradingEvent.UptrendEnd] >= Constants.UppingCoefficient * 10) { if (!message.IsHistoricalData && BotModeSwitcher.CanSell()) { var assetsForClose = _portfolioWrapper.Accounts .SelectMany(a => a.Value.Assets.Values) .Where(a => a.Figi == message.Figi && a.Count > 0) .ToArray(); await ClosePositions(assetsForClose, message); } await LogDeclision(DeclisionTradeAction.CloseLong, message.Value, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(-100, 100)), message); } if (result[TradingEvent.DowntrendEnd] >= Constants.UppingCoefficient * 10) { if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase()) { var assetsForClose = _portfolioWrapper.Accounts .SelectMany(a => a.Value.Assets.Values) .Where(a => a.Figi == message.Figi && a.Count < 0) .ToArray(); await ClosePositions(assetsForClose, message); } await LogDeclision(DeclisionTradeAction.CloseShort, message.Value, 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) { await _tradeDataProvider.LogPrice(new ProcessedPrice() { Figi = message.Figi, Ticker = message.Ticker, Processor = processor, Time = message.Time, Value = value, }, false); } private async Task LogPrice(string figi, string ticker, DateTime time, decimal value, string processor) { await _tradeDataProvider.LogPrice(new ProcessedPrice() { Figi = figi, Ticker = ticker, Processor = processor, Time = time, Value = value, }, false); } private async Task LogDeclision(DeclisionTradeAction action, INewPrice message, decimal? profit = null) { await _tradeDataProvider.LogDeclision(new Declision() { AccountId = string.Empty, Figi = message.Figi, Ticker = message.Ticker, Value = profit, Price = message.Value, Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow, Action = action, }, false); } private async Task LogDeclision(DeclisionTradeAction action, decimal price, DateTime time, INewPrice message) { await _tradeDataProvider.LogDeclision(new Declision() { AccountId = string.Empty, Figi = message.Figi, Ticker = message.Ticker, Value = price, Price = price, Time = time, Action = action, }, false); } public Task StopAsync(CancellationToken cancellationToken) { return Task.CompletedTask; } private decimal GetComission(AssetType assetType) { if (assetType == AssetType.Common) { return _exchangeConfig.ShareComission; } else if (assetType == AssetType.Futures) { return _exchangeConfig.FutureComission; } else { return 0; } } private decimal GetLeverage(string figi, bool isShort) { var res = 1m; var leverage = _exchangeConfig.InstrumentsSettings.FirstOrDefault(l => l.Figi == figi); if (leverage != null) { res = isShort ? leverage.ShortLeverage : leverage.LongLeverage; } return res; } private async ValueTask 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) { var mode = TradingModes[message.Figi]; decimal stopLossShift = 2m; 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); } private Task> 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()); } } }