Compare commits

..

5 Commits

Author SHA1 Message Date
vlad zverzhkhovskiy 7cc13d9ba0 обновление стратегии + фикс скидывания IMOEXF
test / deploy_trader_prod (push) Successful in 4m24s Details
2025-09-09 02:10:37 +03:00
vlad zverzhkhovskiy a2e58c9a12 фикс опреедения пересечений 2025-09-09 01:25:27 +03:00
vlad zverzhkhovskiy f76564ab27 фикс опреедения пересечений 2025-09-09 01:14:55 +03:00
vlad zverzhkhovskiy bbef0a757b фикс опреедения пересечений 2025-09-09 00:23:43 +03:00
vlad zverzhkhovskiy 9b0253b109 фиксация 2025-09-08 23:27:59 +03:00
11 changed files with 220 additions and 185 deletions

View File

@ -9,5 +9,6 @@ namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces
public decimal? RecomendPrice { get; } public decimal? RecomendPrice { get; }
public long Count { get; } public long Count { get; }
public string AccountId { get; } public string AccountId { get; }
public bool EnableMargin { get; }
} }
} }

View File

@ -10,5 +10,6 @@ namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos
public decimal? RecomendPrice { get; init; } public decimal? RecomendPrice { get; init; }
public long Count { get; init; } public long Count { get; init; }
public required string AccountId { get; init; } public required string AccountId { get; init; }
public bool EnableMargin { get; init; } = true;
} }
} }

View File

@ -10,10 +10,10 @@
return (x, y); return (x, y);
} }
public static bool IsLinesCrossing(DateTime time1, DateTime time2, decimal val1_1, decimal val1_2, decimal val2_1, decimal val2_2) public static (bool res,DateTime x, decimal y) IsLinesCrossing(DateTime time1, DateTime time2, decimal val1_1, decimal val1_2, decimal val2_1, decimal val2_2)
{ {
var dtime = (decimal)(time2 - time1).TotalSeconds; var dtime = (decimal)(time2 - time1).TotalSeconds;
if (dtime == 0) return false; if (dtime == 0) return (false, DateTime.MinValue,0);
var dval1 = val1_2 - val1_1; var dval1 = val1_2 - val1_1;
var k1 = dval1 / dtime; var k1 = dval1 / dtime;
var b1 = val1_1; var b1 = val1_1;
@ -28,10 +28,10 @@
if (cross.x >= 0 && cross.x <= dtime) if (cross.x >= 0 && cross.x <= dtime)
{ {
var crossingTimestamp = time1.AddSeconds((double)cross.x); var crossingTimestamp = time1.AddSeconds((double)cross.x);
return crossingTimestamp >= time1 && crossingTimestamp <= time2; return (crossingTimestamp >= time1 && crossingTimestamp <= time2, crossingTimestamp, cross.y);
} }
} }
return false; return (false, DateTime.MinValue, 0);
} }
} }
} }

View File

@ -29,11 +29,10 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
try try
{ {
var pricesForFinalComparison = new decimal[size]; var pricesForFinalComparison = new decimal[size];
var timesForFinalComparison = new DateTime[size];
var twavss = new decimal[size]; var twavss = new decimal[size];
var twavbs = new decimal[size]; var twavbs = new decimal[size];
var times = new DateTime[size]; var times = new DateTime[size];
var crossings = new List<int>();
for (int shift = 0; shift < size - 1 && shift < prices.Length - 1; shift++) for (int shift = 0; shift < size - 1 && shift < prices.Length - 1; shift++)
{ {
s = shift; s = shift;
@ -43,7 +42,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
var twavs = CalcTimeWindowAverageValue(timestamps, prices, smallWindow, shift); var twavs = CalcTimeWindowAverageValue(timestamps, prices, smallWindow, shift);
var twavb = CalcTimeWindowAverageValue(timestamps, prices, bigWindow, shift); var twavb = CalcTimeWindowAverageValue(timestamps, prices, bigWindow, shift);
pricesForFinalComparison[i2] = prices[prices.Length - 1 - shift]; pricesForFinalComparison[i2] = prices[prices.Length - 1 - shift];
timesForFinalComparison[i2] = timestamps[prices.Length - 1 - shift];
if (shift == 0) if (shift == 0)
{ {
bigWindowAv = twavb.value; bigWindowAv = twavb.value;
@ -52,11 +51,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
twavss[i2] = twavs.value; twavss[i2] = twavs.value;
twavbs[i2] = twavb.value; twavbs[i2] = twavb.value;
times[i2] = twavb.time; times[i2] = twavb.time;
if (System.Math.Abs(twavb.value - prices[prices.Length - 1]) > 2 * meanfullStep)
{
res |= TradingEvent.StopBuy;
return (res, bigWindowAv, smallWindowAv);
}
if (shift > 0) if (shift > 0)
{ {
var isCrossing = Lines.IsLinesCrossing( var isCrossing = Lines.IsLinesCrossing(
@ -67,33 +62,63 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
twavbs[i1 + 1], twavbs[i1 + 1],
twavbs[i2 + 1]); twavbs[i2 + 1]);
if (shift == 1 && !isCrossing) //если нет пересечения скользящих средний с окном 120 и 15 секунд между if (shift == 1 && !isCrossing.res) //если нет пересечения скользящих средний с окном 120 и 15 секунд между
//текущей и предыдущей точкой - можно не продолжать выполнение. //текущей и предыдущей точкой - можно не продолжать выполнение.
{ {
break; break;
} }
if (shift > 1 && isCrossing)
if (isCrossing.res)
{ {
// если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта crossings.Add(i2);
if (twavbs[size - 1] <= twavss[size - 1] && twavbs[size - 2] > twavss[size - 2]) if (crossings.Count == 4 || (shift + 1 == size - 1 || shift + 1 == prices.Length - 1))
{ {
if (pricesForFinalComparison[i2 + 1] - pricesForFinalComparison[size - 1] >= meanfullStep if ((shift + 1 == size - 1 || shift + 1 == prices.Length - 1))
&& timesForFinalComparison[size - 1] - timesForFinalComparison[i2 + 1] >= timeForUptreandStart)
{ {
res |= TradingEvent.UptrendStart; crossings.Add(shift);
}
var diffTotal = pricesForFinalComparison[crossings[0]] - pricesForFinalComparison[crossings[1]];
for (int crossingShift = 1; crossingShift < crossings.Count - 2; crossingShift++)
{
var diff = pricesForFinalComparison[crossings[crossingShift]] - pricesForFinalComparison[crossings[crossingShift + 1]];
if (diff >= 0)
{
diffTotal += diff;
}
else
{
break;
}
}
// если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта
if (twavss[size - 1] <= twavbs[size - 1] && twavss[size - 2] > twavbs[size - 2])
{
if (diffTotal >= meanfullStep
&& times[crossings[0]] - times[crossings[1]] >= timeForUptreandStart)
{
res |= TradingEvent.UptrendEnd;
}
break;
} }
break;
} }
// если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта if (crossings.Count == 2 || (shift + 1 == size - 1 || shift + 1 == prices.Length - 1))
if (twavss[size - 1] <= twavbs[size - 1] && twavss[size - 2] > twavbs[size - 2])
{ {
if (pricesForFinalComparison[i2 + 1] - pricesForFinalComparison[size - 1] <= -meanfullStep if ((shift + 1 == size - 1 || shift + 1 == prices.Length - 1))
&& timesForFinalComparison[size - 1] - timesForFinalComparison[i2 + 1] >= timeForUptreandStart)
{ {
res |= TradingEvent.UptrendEnd; crossings.Add(shift);
}
// если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта
if (twavss[size - 1] >= twavbs[size - 1] && twavss[size - 2] < twavbs[size - 2])
{
if (pricesForFinalComparison[crossings[0]] - pricesForFinalComparison[crossings[1]] <= - meanfullStep
&& times[crossings[0]] - times[crossings[1]] >= timeForUptreandStart)
{
res |= TradingEvent.UptrendStart;
}
break;
} }
break;
} }
} }
} }

View File

@ -0,0 +1,69 @@
using System;
namespace KLHZ.Trader.Core.Math.Declisions.Utils
{
public static class ShapeAreaCalculator
{
// Метод для расчёта площади треугольника
public static double CalculateTriangleArea(
DateTime pointA_X, decimal pointA_Y,
DateTime pointB_X, decimal pointB_Y,
DateTime pointC_X, decimal pointC_Y)
{
// Определяем самую позднюю точку
DateTime latestPoint = new[] { pointA_X, pointB_X, pointC_X }.Max();
// Смещённые координаты
double offsetA = GetOffsetInSeconds(latestPoint, pointA_X);
double offsetB = GetOffsetInSeconds(latestPoint, pointB_X);
double offsetC = GetOffsetInSeconds(latestPoint, pointC_X);
// Расчёт расстояний
double sideAB = DistanceBetweenPoints(offsetA, (double)pointA_Y, offsetB, (double)pointB_Y);
double sideBC = DistanceBetweenPoints(offsetB, (double)pointB_Y, offsetC, (double)pointC_Y);
double sideCA = DistanceBetweenPoints(offsetC, (double)pointC_Y, offsetA, (double)pointA_Y);
// Формула Герона
double semiPerimeter = (sideAB + sideBC + sideCA) / 2;
return System.Math.Sqrt(semiPerimeter * (semiPerimeter - sideAB) *
(semiPerimeter - sideBC) * (semiPerimeter - sideCA));
}
// Метод для расчёта площади четырёхугольника
public static double CalculateQuadrilateralArea(
DateTime pointA_X, decimal pointA_Y,
DateTime pointB_X, decimal pointB_Y,
DateTime pointC_X, decimal pointC_Y,
DateTime pointD_X, decimal pointD_Y)
{
// Определяем самую позднюю точку
DateTime latestPoint = new[] { pointA_X, pointB_X, pointC_X, pointD_X }.Max();
// Смещённые координаты
double offsetA = GetOffsetInSeconds(latestPoint, pointA_X);
double offsetB = GetOffsetInSeconds(latestPoint, pointB_X);
double offsetC = GetOffsetInSeconds(latestPoint, pointC_X);
double offsetD = GetOffsetInSeconds(latestPoint, pointD_X);
// Суммируем площади двух треугольников
double firstTriangleArea = CalculateTriangleArea(pointA_X, pointA_Y, pointB_X, pointB_Y, pointD_X, pointD_Y);
double secondTriangleArea = CalculateTriangleArea(pointB_X, pointB_Y, pointC_X, pointC_Y, pointD_X, pointD_Y);
return firstTriangleArea + secondTriangleArea;
}
// Вспомогательные методы
// Конвертация разницы времён в секунды
private static double GetOffsetInSeconds(DateTime referencePoint, DateTime targetPoint)
{
return (referencePoint - targetPoint).TotalSeconds;
}
// Евклидово расстояние между двумя точками
private static double DistanceBetweenPoints(double x1, double y1, double x2, double y2)
{
return System.Math.Sqrt(System.Math.Pow(x2 - x1, 2) + System.Math.Pow(y2 - y1, 2));
}
}
}

View File

@ -16,7 +16,7 @@ namespace KLHZ.Trader.Core.Tests
var val2_1 = -0.5m; var val2_1 = -0.5m;
var val2_2 = 0.5m; var val2_2 = 0.5m;
Assert.IsTrue(Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2)); Assert.IsTrue(Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2).res);
} }
[Test] [Test]
@ -31,7 +31,7 @@ namespace KLHZ.Trader.Core.Tests
var val2_1 = 0.5m; var val2_1 = 0.5m;
var val2_2 = -0.5m; var val2_2 = -0.5m;
Assert.IsFalse(Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2)); Assert.IsFalse(Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2).res);
} }
} }
} }

View File

@ -28,8 +28,6 @@ namespace KLHZ.Trader.Core.Exchange.Services
private readonly IDataBus _dataBus; private readonly IDataBus _dataBus;
private readonly TraderDataProvider _tradeDataProvider; private readonly TraderDataProvider _tradeDataProvider;
private readonly ILogger<Trader> _logger; private readonly ILogger<Trader> _logger;
private readonly ConcurrentDictionary<string, DeferredTrade> DeferredLongOpens = 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();
@ -91,8 +89,8 @@ namespace KLHZ.Trader.Core.Exchange.Services
try try
{ {
await _tradeDataProvider.AddData(message, TimeSpan.FromHours(7)); await _tradeDataProvider.AddData(message, TimeSpan.FromHours(7));
await ProcessDeferredLongOpens(message, currentTime); //await ProcessDeferredLongOpens(message, currentTime);
await ProcessDeferredLongCloses(message, currentTime); //await ProcessDeferredLongCloses(message, currentTime);
if (message.Figi == "FUTIMOEXF000") if (message.Figi == "FUTIMOEXF000")
{ {
var windowMaxSize = 1000; var windowMaxSize = 1000;
@ -131,7 +129,8 @@ namespace KLHZ.Trader.Core.Exchange.Services
{ {
AccountId = asset.AccountId, AccountId = asset.AccountId,
Figi = message.Figi, Figi = message.Figi,
CommandType = Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketSell, CommandType = asset.Count < 0? Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketBuy
: Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketSell,
Count = (long)asset.Count, Count = (long)asset.Count,
RecomendPrice = null, RecomendPrice = null,
}); });
@ -146,7 +145,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
INewPrice message, int windowMaxSize) INewPrice message, int windowMaxSize)
{ {
var res = TradingEvent.None; var res = TradingEvent.None;
var resultMoveAvFull = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 45, 180, TimeSpan.FromSeconds(30), 1m); var resultMoveAvFull = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 25, 120, TimeSpan.FromSeconds(20), 1.5m);
//var resultLongClose = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 15, 120, 1.5m).events; //var resultLongClose = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 15, 120, 1.5m).events;
//ar uptrendStarts = LocalTrends.CheckByLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(20), 1.5m, 15); //ar uptrendStarts = LocalTrends.CheckByLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(20), 1.5m, 15);
@ -172,15 +171,16 @@ namespace KLHZ.Trader.Core.Exchange.Services
&& !OpeningStops.TryGetValue(message.Figi, out _) && !OpeningStops.TryGetValue(message.Figi, out _)
&& state == ExchangeState.Open && state == ExchangeState.Open
&& data.timestamps.Length > 1 && data.timestamps.Length > 1
&& (data.timestamps[data.timestamps.Length - 1] - data.timestamps[data.timestamps.Length - 2] < TimeSpan.FromMinutes(1))) && (data.timestamps[data.timestamps.Length - 1] - data.timestamps[data.timestamps.Length - 2] < TimeSpan.FromMinutes(1))
)
{ {
var fullData = await _tradeDataProvider.GetData(message.Figi, TimeSpan.FromMinutes(60)); var fullData = await _tradeDataProvider.GetData(message.Figi, TimeSpan.FromMinutes(30));
if (fullData.isFullIntervalExists) if (fullData.isFullIntervalExists)
{ {
var max = fullData.prices.Max(); var max = fullData.prices.Max();
var min = fullData.prices.Min(); var min = fullData.prices.Min();
if (max - min < 15 && fullData.prices.Last() - fullData.prices.First() < 4) if (max - min < 15 && fullData.prices.Last() - fullData.prices.First() < 4 && fullData.prices.Last() - fullData.prices.First()>-4)
{ {
if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase()) if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase())
{ {
@ -209,7 +209,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
} }
else else
{ {
await LogDeclision(DeclisionTradeAction.OpenLong, message); await LogDeclision(DeclisionTradeAction.OpenLong, message);
} }
} }
} }
@ -281,114 +281,6 @@ namespace KLHZ.Trader.Core.Exchange.Services
} }
} }
private async Task ProcessDeferredLongOpens(INewPrice message, DateTime currentTime)
{
if (message.Figi == "FUTIMOEXF000")
{
DeferredTrade? longOpen;
DeferredLongOpens.TryGetValue(message.Figi, out longOpen);
if (longOpen != null)
{
var t = currentTime;
if (longOpen.Time <= t
&& t - longOpen.Time < TimeSpan.FromMinutes(3))
{
DeferredLongOpens.TryRemove(message.Figi, out _);
if (message.Value - longOpen.Price < 1)
{
if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase())
{
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))
{
if (RandomNumberGenerator.GetInt32(100) > 50)
{
await _dataBus.Broadcast(new TradeCommand()
{
AccountId = acc.Value.AccountId,
Figi = message.Figi,
CommandType = Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketBuy,
Count = 1,
RecomendPrice = null,
});
}
await LogDeclision(DeclisionTradeAction.OpenLong, message);
}
}
}
else
{
await LogDeclision(DeclisionTradeAction.OpenLong, message);
}
}
}
}
}
}
private async Task ProcessDeferredLongCloses(INewPrice message, DateTime currentTime)
{
if (message.Figi == "FUTIMOEXF000")
{
DeferredTrade? longClose;
DeferredLongCloses.TryGetValue(message.Figi, out longClose);
if (longClose != null)
{
if (longClose.Time <= currentTime)
{
DeferredLongCloses.TryRemove(message.Figi, out _);
if (longClose.Price - message.Value < 1)
{
var assetType = _tradeDataProvider.GetAssetTypeByFigi(message.Figi);
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,
});
await LogDeclision(DeclisionTradeAction.CloseLong, message, profit);
}
}
}
else
{
await LogDeclision(DeclisionTradeAction.CloseLong, message);
}
}
}
}
}
}
private async Task LogPrice(INewPrice message, string processor, decimal value) private async Task LogPrice(INewPrice message, string processor, decimal value)
{ {
await _tradeDataProvider.LogPrice(new ProcessedPrice() await _tradeDataProvider.LogPrice(new ProcessedPrice()

View File

@ -1,4 +1,5 @@
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces; using Google.Protobuf.WellKnownTypes;
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos; 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.DataLayer; using KLHZ.Trader.Core.DataLayer;
@ -108,7 +109,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
public async Task Init() public async Task Init()
{ {
await _initSemaphore.WaitAsync(TimeSpan.FromSeconds(15)); await _initSemaphore.WaitAsync(TimeSpan.FromSeconds(3));
try try
{ {
var shares = await _investApiClient.Instruments.SharesAsync(); var shares = await _investApiClient.Instruments.SharesAsync();
@ -416,5 +417,11 @@ namespace KLHZ.Trader.Core.Exchange.Services
} }
} }
} }
public ValueTask<Asset[]> GetAssetsByFigi(string figi)
{
var assets = Accounts.Values.SelectMany(a => a.Assets.Values.Where(aa => aa.Figi == figi)).ToArray();
return ValueTask.FromResult(assets);
}
} }
} }

View File

@ -63,7 +63,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
Direction = dir, Direction = dir,
OrderType = OrderType.Market, OrderType = OrderType.Market,
Quantity = tradeCommand.Count, Quantity = tradeCommand.Count,
ConfirmMarginTrade = true, ConfirmMarginTrade = tradeCommand.EnableMargin,
}; };
var res = await _investApiClient.Orders.PostOrderAsync(req); var res = await _investApiClient.Orders.PostOrderAsync(req);

View File

@ -2,6 +2,7 @@
using KLHZ.Trader.Core.Contracts.Messaging.Dtos; using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Enums; using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Enums;
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces; using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
using KLHZ.Trader.Core.Exchange.Services;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Collections.Immutable; using System.Collections.Immutable;
@ -17,8 +18,10 @@ namespace KLHZ.Trader.Core.TG.Services
private readonly ImmutableArray<long> _admins = []; private readonly ImmutableArray<long> _admins = [];
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) private readonly TraderDataProvider _traderDataProvider;
public BotMessagesHandler(IDataBus eventBus, IOptions<TgBotConfig> options, ILogger<BotMessagesHandler> logger, TraderDataProvider traderDataProvider)
{ {
_traderDataProvider = traderDataProvider;
_logger = logger; _logger = logger;
_eventBus = eventBus; _eventBus = eventBus;
_admins = ImmutableArray.CreateRange(options.Value.Admins); _admins = ImmutableArray.CreateRange(options.Value.Admins);
@ -74,15 +77,24 @@ namespace KLHZ.Trader.Core.TG.Services
} }
case "продать IMOEXF": case "продать IMOEXF":
{ {
var command = new TradeCommand()
var assets = await _traderDataProvider.GetAssetsByFigi("FUTIMOEXF000");
foreach(var asset in assets)
{ {
AccountId = "2274189208", if (asset.Count > 0)
CommandType = TradeCommandType.MarketSell, {
RecomendPrice = null, var command = new TradeCommand()
Figi = "FUTIMOEXF000", {
Count = 1, AccountId = asset.AccountId,
}; CommandType = TradeCommandType.MarketSell,
await _eventBus.Broadcast(command); RecomendPrice = null,
Figi = asset.Figi,
Count = (long)asset.Count,
};
await _eventBus.Broadcast(command);
}
}
break; break;
} }
case "купить IMOEXF": case "купить IMOEXF":

View File

@ -52,33 +52,61 @@ namespace KLHZ.Trader.Service.Controllers
} }
} }
[HttpGet] ////[HttpGet]
public async Task LoadTradesToHistory(string figi) //public async Task LoadTradesToHistory(string figi)
{ //{
try // try
{ // {
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.InstrumentTrades // var data = await context1.InstrumentTrades
.Where(c => c.Figi == figi) // .Where(c => c.Figi == figi)
.OrderBy(c => c.BoughtAt) // .OrderBy(c => c.BoughtAt)
.Select(c => new PriceChange() // .Select(c => new PriceChange()
{ // {
Figi = figi, // Figi = figi,
Ticker = c.Ticker, // Ticker = c.Ticker,
Time = c.BoughtAt, // Time = c.BoughtAt,
Value = c.Price, // Value = c.Price,
IsHistoricalData = true // IsHistoricalData = true
}) // })
.ToArrayAsync(); // .ToArrayAsync();
await context1.PriceChanges.Where(p => p.Figi == figi).ExecuteDeleteAsync(); // await context1.PriceChanges.Where(p => p.Figi == figi).ExecuteDeleteAsync();
await context1.PriceChanges.AddRangeAsync(data); // await context1.PriceChanges.AddRangeAsync(data);
await context1.SaveChangesAsync(); // await context1.SaveChangesAsync();
} // }
catch (Exception ex) // catch (Exception ex)
{ // {
} // }
} //}
////[HttpGet]
//public async Task LoadTradesToHistory(string figi)
//{
// try
// {
// using var context1 = await _dbContextFactory.CreateDbContextAsync();
// context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
// var data = await context1.InstrumentTrades
// .Where(c => c.Figi == figi)
// .OrderBy(c => c.BoughtAt)
// .Select(c => new PriceChange()
// {
// Figi = figi,
// Ticker = c.Ticker,
// Time = c.BoughtAt,
// Value = c.Price,
// IsHistoricalData = true
// })
// .ToArrayAsync();
// await context1.PriceChanges.Where(p => p.Figi == figi).ExecuteDeleteAsync();
// await context1.PriceChanges.AddRangeAsync(data);
// await context1.SaveChangesAsync();
// }
// catch (Exception ex)
// {
// }
//}
} }
} }