Compare commits
No commits in common. "7cc13d9ba089b21ef7d190d922276c69efaa26c6" and "8bfcb1968d98b7525c5dcfecff2848d1875fa931" have entirely different histories.
7cc13d9ba0
...
8bfcb1968d
|
@ -9,6 +9,5 @@ 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; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,5 @@ 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
return (x, y);
|
return (x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
public static bool 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, DateTime.MinValue,0);
|
if (dtime == 0) return false;
|
||||||
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, crossingTimestamp, cross.y);
|
return crossingTimestamp >= time1 && crossingTimestamp <= time2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (false, DateTime.MinValue, 0);
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,10 +29,11 @@ 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;
|
||||||
|
@ -42,7 +43,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;
|
||||||
|
@ -51,7 +52,11 @@ 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(
|
||||||
|
@ -62,63 +67,33 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
twavbs[i1 + 1],
|
twavbs[i1 + 1],
|
||||||
twavbs[i2 + 1]);
|
twavbs[i2 + 1]);
|
||||||
|
|
||||||
if (shift == 1 && !isCrossing.res) //если нет пересечения скользящих средний с окном 120 и 15 секунд между
|
if (shift == 1 && !isCrossing) //если нет пересечения скользящих средний с окном 120 и 15 секунд между
|
||||||
//текущей и предыдущей точкой - можно не продолжать выполнение.
|
//текущей и предыдущей точкой - можно не продолжать выполнение.
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (shift > 1 && isCrossing)
|
||||||
if (isCrossing.res)
|
|
||||||
{
|
{
|
||||||
crossings.Add(i2);
|
// если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта
|
||||||
if (crossings.Count == 4 || (shift + 1 == size - 1 || shift + 1 == prices.Length - 1))
|
if (twavbs[size - 1] <= twavss[size - 1] && twavbs[size - 2] > twavss[size - 2])
|
||||||
{
|
{
|
||||||
if ((shift + 1 == size - 1 || shift + 1 == prices.Length - 1))
|
if (pricesForFinalComparison[i2 + 1] - pricesForFinalComparison[size - 1] >= meanfullStep
|
||||||
|
&& timesForFinalComparison[size - 1] - timesForFinalComparison[i2 + 1] >= timeForUptreandStart)
|
||||||
{
|
{
|
||||||
crossings.Add(shift);
|
res |= TradingEvent.UptrendStart;
|
||||||
}
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (crossings.Count == 2 || (shift + 1 == size - 1 || shift + 1 == prices.Length - 1))
|
// если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта
|
||||||
|
if (twavss[size - 1] <= twavbs[size - 1] && twavss[size - 2] > twavbs[size - 2])
|
||||||
{
|
{
|
||||||
if ((shift + 1 == size - 1 || shift + 1 == prices.Length - 1))
|
if (pricesForFinalComparison[i2 + 1] - pricesForFinalComparison[size - 1] <= -meanfullStep
|
||||||
|
&& timesForFinalComparison[size - 1] - timesForFinalComparison[i2 + 1] >= timeForUptreandStart)
|
||||||
{
|
{
|
||||||
crossings.Add(shift);
|
res |= TradingEvent.UptrendEnd;
|
||||||
}
|
|
||||||
// если фильтрация окном 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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).res);
|
Assert.IsTrue(Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
[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).res);
|
Assert.IsFalse(Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -28,6 +28,8 @@ 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();
|
||||||
|
|
||||||
|
@ -89,8 +91,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;
|
||||||
|
@ -129,8 +131,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
{
|
{
|
||||||
AccountId = asset.AccountId,
|
AccountId = asset.AccountId,
|
||||||
Figi = message.Figi,
|
Figi = message.Figi,
|
||||||
CommandType = asset.Count < 0? Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketBuy
|
CommandType = Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketSell,
|
||||||
: Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketSell,
|
|
||||||
Count = (long)asset.Count,
|
Count = (long)asset.Count,
|
||||||
RecomendPrice = null,
|
RecomendPrice = null,
|
||||||
});
|
});
|
||||||
|
@ -145,7 +146,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, 25, 120, TimeSpan.FromSeconds(20), 1.5m);
|
var resultMoveAvFull = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 45, 180, TimeSpan.FromSeconds(30), 1m);
|
||||||
//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);
|
||||||
|
@ -171,16 +172,15 @@ 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(30));
|
var fullData = await _tradeDataProvider.GetData(message.Figi, TimeSpan.FromMinutes(60));
|
||||||
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 && fullData.prices.Last() - fullData.prices.First()>-4)
|
if (max - min < 15 && 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,6 +281,114 @@ 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()
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using Google.Protobuf.WellKnownTypes;
|
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;
|
||||||
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;
|
||||||
|
@ -109,7 +108,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
|
|
||||||
public async Task Init()
|
public async Task Init()
|
||||||
{
|
{
|
||||||
await _initSemaphore.WaitAsync(TimeSpan.FromSeconds(3));
|
await _initSemaphore.WaitAsync(TimeSpan.FromSeconds(15));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var shares = await _investApiClient.Instruments.SharesAsync();
|
var shares = await _investApiClient.Instruments.SharesAsync();
|
||||||
|
@ -417,11 +416,5 @@ 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = tradeCommand.EnableMargin,
|
ConfirmMarginTrade = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var res = await _investApiClient.Orders.PostOrderAsync(req);
|
var res = await _investApiClient.Orders.PostOrderAsync(req);
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
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;
|
||||||
|
@ -18,10 +17,8 @@ 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;
|
||||||
private readonly TraderDataProvider _traderDataProvider;
|
public BotMessagesHandler(IDataBus eventBus, IOptions<TgBotConfig> options, ILogger<BotMessagesHandler> logger)
|
||||||
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);
|
||||||
|
@ -77,24 +74,15 @@ 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)
|
|
||||||
{
|
{
|
||||||
if (asset.Count > 0)
|
AccountId = "2274189208",
|
||||||
{
|
CommandType = TradeCommandType.MarketSell,
|
||||||
var command = new TradeCommand()
|
RecomendPrice = null,
|
||||||
{
|
Figi = "FUTIMOEXF000",
|
||||||
AccountId = asset.AccountId,
|
Count = 1,
|
||||||
CommandType = TradeCommandType.MarketSell,
|
};
|
||||||
RecomendPrice = null,
|
await _eventBus.Broadcast(command);
|
||||||
Figi = asset.Figi,
|
|
||||||
Count = (long)asset.Count,
|
|
||||||
};
|
|
||||||
await _eventBus.Broadcast(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "купить IMOEXF":
|
case "купить IMOEXF":
|
||||||
|
|
|
@ -52,61 +52,33 @@ 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)
|
|
||||||
// {
|
|
||||||
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue