чистка мусора + доработка вывода результатов
test / deploy_trader_prod (push) Successful in 11m24s Details

dev
vlad zverzhkhovskiy 2025-10-15 11:31:24 +03:00
parent b3b7807249
commit 6770d12640
10 changed files with 109 additions and 115 deletions

View File

@ -82,7 +82,6 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
return (resDt, resVs);
}
public static (DateTime[] timestamps, decimal[] values) TrimValues(DateTime[] timestamps, decimal[] values, TimeSpan leftBound, TimeSpan rightBound)
{
var resDt = new List<DateTime>();

View File

@ -232,7 +232,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
}
public static (TradingEvent events, decimal bigWindowAv, decimal smallWindowAv) CheckByWindowAverageMean2(ITradeDataItem[] data, int size, int smallWindow, int bigWindow,
decimal? uptrendStartingDetectionMeanfullStep = null, decimal? uptrendEndingDetectionMeanfullStep = null)
decimal? uptrendStartingDetectionMeanfullStep = null, decimal? uptrendEndingDetectionMeanfullStep = null)
{
var res = TradingEvent.None;
var bigWindowAv = 0m;

View File

@ -5,7 +5,6 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
{
public static class Statistics
{
public static decimal MeanCount(this ITradeDataItem[] values)
{
return values.Sum(x => x.Count) / values.Length;
@ -197,25 +196,5 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
}
return Array.Empty<ConvolutionResult>();
}
public static void MergeConvolutionResults(List<ConvolutionResult> results, List<ConvolutionResult> mergedResults)
{
if (results.Count == 0)
{
return;
}
var resultsOrdered = results.OrderBy(r => r.Sum).ToList();
var res = resultsOrdered[0];
var b1 = res.Shift - res.Leverage;
var b2 = res.Shift + res.Leverage;
var forMerge = results.Where(r => r.Shift >= b1 && r.Shift <= b2).ToList();
res.Sum = forMerge.Sum(r => r.Sum);
foreach (var m in forMerge)
{
results.Remove(m);
}
mergedResults.Add(res);
MergeConvolutionResults(results, mergedResults);
}
}
}

View File

@ -1,5 +1,6 @@
using KLHZ.Trader.Core.Contracts.Common.Enums;
using KLHZ.Trader.Core.Exchange.Models.AssetsAccounting;
using KLHZ.Trader.Core.Exchange.Models.Trading;
using System.Collections.Immutable;
namespace KLHZ.Trader.Core.Exchange.Interfaces
@ -14,8 +15,8 @@ namespace KLHZ.Trader.Core.Exchange.Interfaces
Task Init(string accountId, string? accountName = null);
Task LoadPortfolio();
ImmutableDictionary<string, Asset> Assets { get; }
public Task OpenPosition(string figi, PositionType positionType, decimal stopLossShift, decimal takeProfitShift, long count = 1);
public Task ClosePosition(string figi);
public Task<TradeResult> OpenPosition(string figi, PositionType positionType, decimal stopLossShift, decimal takeProfitShift, long count = 1, decimal pointPrice = 1);
public Task<TradeResult> ClosePosition(string figi);
public Task ResetStops(string figi, decimal stopLossShift, decimal takeProfitShift);
}
}

View File

@ -5,6 +5,6 @@
public required string Figi { get; init; }
public decimal ShortLeverage { get; init; }
public decimal LongLeverage { get; init; }
public decimal PriceToRubConvertationCoefficient { get; init; } = 1;
public decimal PointPriceRub { get; init; } = 1;
}
}

View File

@ -0,0 +1,10 @@
namespace KLHZ.Trader.Core.Exchange.Models.Trading
{
public class TradeResult
{
public bool Success { get; init; }
public decimal ExecutedPrice { get; init; }
public decimal Comission { get; init; }
public long Count { get; init; }
}
}

View File

@ -1,12 +0,0 @@
namespace KLHZ.Trader.Core.Exchange.Models.Trading
{
public enum TradingMode
{
None = 0,
Stable = 1,
SlowDropping = -1,
SlowGrowing = 2,
Growing = 3,
Dropping = -2,
}
}

View File

@ -4,6 +4,7 @@ using KLHZ.Trader.Core.DataLayer;
using KLHZ.Trader.Core.Exchange.Extentions;
using KLHZ.Trader.Core.Exchange.Interfaces;
using KLHZ.Trader.Core.Exchange.Models.Configs;
using KLHZ.Trader.Core.Exchange.Models.Trading;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -164,8 +165,9 @@ namespace KLHZ.Trader.Core.Exchange.Services
Balance = portfolio.TotalAmountCurrencies;
}
public async Task OpenPosition(string figi, PositionType positionType, decimal stopLossShift, decimal takeProfitShift, long count = 1)
public async Task<TradeResult> OpenPosition(string figi, PositionType positionType, decimal stopLossShift, decimal takeProfitShift, long count = 1, decimal pointPrice = 1)
{
TradeResult? result = null;
try
{
await _semaphore.WaitAsync2(_defaultLockTimeSpan);
@ -186,58 +188,54 @@ namespace KLHZ.Trader.Core.Exchange.Services
var res = await _investApiClient.Orders.PostOrderAsync(req);
_usedOrderIds.TryAdd(res.OrderId, DateTime.UtcNow);
var executedPrice = res.ExecutedOrderPrice / 10;
var executedPrice = res.ExecutedOrderPrice / pointPrice;
await Task.Delay(1000);
if (stopLossShift == 0)
if (stopLossShift != 0)
{
stopLossShift = 0.002m * executedPrice;
var pricesl = positionType == PositionType.Long ? executedPrice - stopLossShift : executedPrice + stopLossShift;
var slReq = new PostStopOrderRequest()
{
AccountId = AccountId,
ConfirmMarginTrade = false,
InstrumentId = figi,
Direction = stopOrdersDirection,
PriceType = PriceType.Point,
Quantity = count,
StopOrderType = StopOrderType.StopLoss,
StopPrice = pricesl,
ExchangeOrderType = ExchangeOrderType.Market,
ExpirationType = StopOrderExpirationType.GoodTillCancel,
};
var slOrderRes = await _investApiClient.StopOrders.PostStopOrderAsync(slReq);
}
if (takeProfitShift == 0)
if (takeProfitShift != 0)
{
takeProfitShift = 0.01m * executedPrice;
var pricetp = positionType == PositionType.Long ? executedPrice + takeProfitShift : executedPrice - takeProfitShift;
var tpReq = new PostStopOrderRequest()
{
AccountId = AccountId,
ConfirmMarginTrade = false,
InstrumentId = figi,
Direction = stopOrdersDirection,
PriceType = PriceType.Point,
Quantity = count,
StopOrderType = StopOrderType.TakeProfit,
StopPrice = pricetp,
ExchangeOrderType = ExchangeOrderType.Market,
ExpirationType = StopOrderExpirationType.GoodTillCancel,
};
var tpOrderRes = await _investApiClient.StopOrders.PostStopOrderAsync(tpReq);
}
takeProfitShift = takeProfitShift * 2;
takeProfitShift = System.Math.Round(takeProfitShift);
takeProfitShift = takeProfitShift / 2;
stopLossShift = stopLossShift * 2;
stopLossShift = System.Math.Round(stopLossShift);
stopLossShift = stopLossShift / 2;
var pricesl = positionType == PositionType.Long ? executedPrice - stopLossShift : executedPrice + stopLossShift;
var pricetp = positionType == PositionType.Long ? executedPrice + takeProfitShift : executedPrice - takeProfitShift;
var slReq = new PostStopOrderRequest()
{
AccountId = AccountId,
ConfirmMarginTrade = false,
InstrumentId = figi,
Direction = stopOrdersDirection,
PriceType = PriceType.Point,
Quantity = count,
StopOrderType = StopOrderType.StopLoss,
StopPrice = pricesl,
ExchangeOrderType = ExchangeOrderType.Market,
ExpirationType = StopOrderExpirationType.GoodTillCancel,
};
var slOrderRes = await _investApiClient.StopOrders.PostStopOrderAsync(slReq);
var tpReq = new PostStopOrderRequest()
{
AccountId = AccountId,
ConfirmMarginTrade = false,
InstrumentId = figi,
Direction = stopOrdersDirection,
PriceType = PriceType.Point,
Quantity = count,
StopOrderType = StopOrderType.TakeProfit,
StopPrice = pricetp,
ExchangeOrderType = ExchangeOrderType.Market,
ExpirationType = StopOrderExpirationType.GoodTillCancel,
};
var tpOrderRes = await _investApiClient.StopOrders.PostStopOrderAsync(tpReq);
await LoadPortfolioNolock();
result = new TradeResult()
{
Success = true,
Comission = res.ExecutedCommission,
Count = res.LotsExecuted,
ExecutedPrice = res.ExecutedOrderPrice,
};
}
}
catch (TaskCanceledException) { }
@ -247,6 +245,8 @@ namespace KLHZ.Trader.Core.Exchange.Services
}
_semaphore.Release();
result ??= new TradeResult();
return result;
}
public async Task ResetStops(string figi, decimal stopLossShift, decimal takeProfitShift)
@ -317,8 +317,9 @@ namespace KLHZ.Trader.Core.Exchange.Services
_semaphore.Release();
}
public async Task ClosePosition(string figi)
public async Task<TradeResult> ClosePosition(string figi)
{
TradeResult? result = null;
try
{
await _semaphore.WaitAsync2(_defaultLockTimeSpan);
@ -355,6 +356,13 @@ namespace KLHZ.Trader.Core.Exchange.Services
}
await LoadPortfolioNolock();
result = new TradeResult()
{
Success = true,
Comission = res.ExecutedCommission,
Count = res.LotsExecuted,
ExecutedPrice = res.ExecutedOrderPrice,
};
}
}
catch (TaskCanceledException) { }
@ -364,6 +372,8 @@ namespace KLHZ.Trader.Core.Exchange.Services
}
_semaphore.Release();
result ??= new TradeResult();
return result;
}
private async Task CyclingOperations()

View File

@ -32,12 +32,10 @@ namespace KLHZ.Trader.Core.Exchange.Services
private readonly ExchangeConfig _exchangeConfig;
private readonly ILogger<Trader> _logger;
private readonly ConcurrentDictionary<string, TradingMode> TradingModes = new();
private readonly ConcurrentDictionary<string, DeferredDeclision> DeferredDeclisions = new();
private readonly ConcurrentDictionary<string, SupportLevel[]> SupportLevels = new();
private readonly ConcurrentDictionary<string, decimal> _pirsonValues = new();
private readonly ConcurrentDictionary<string, decimal> _dpirsonValues = new();
private readonly ConcurrentDictionary<string, DateTime> _supportLevelsCalculationTimes = new();
private readonly ConcurrentDictionary<string, DateTime> _usedSupportLevels = new();
private readonly ConcurrentDictionary<string, DateTime> _usedSupportLevelsForClosing = new();
@ -59,10 +57,6 @@ namespace KLHZ.Trader.Core.Exchange.Services
_logger = logger;
_dataBus = dataBus;
_exchangeConfig = options.Value;
foreach (var f in _exchangeConfig.TradingInstrumentsFigis)
{
TradingModes[f] = TradingMode.None;
}
}
public Task StartAsync(CancellationToken cancellationToken)
@ -481,32 +475,27 @@ namespace KLHZ.Trader.Core.Exchange.Services
{
Asset? assetForClose = null;
string? mess = null;
var profit = 0m;
if (withProfitOnly)
{
var profit = 0m;
if (assetType == AssetType.Futures)
if (_tradeDataProvider.Orderbooks.TryGetValue(message.Figi, out var orderbook))
{
if (_tradeDataProvider.Orderbooks.TryGetValue(message.Figi, out var orderbook))
if (asset.Count < 0 && orderbook.Asks.Length > 0)
{
if (asset.Count < 0 && orderbook.Asks.Length > 0)
{
price = orderbook.Asks[0].Price;
}
else if (orderbook.Bids.Length > 0)
{
price = orderbook.Bids[0].Price;
}
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);
}
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);
assetForClose = asset;
mess = $"Закрываю позицию {asset.Figi} ({(asset.Count > 0 ? "лонг" : "шорт")}) на счёте {_portfolioWrapper.Accounts[asset.AccountId].AccountName}. Количество {(long)asset.Count}, цена ~{price}, профит {profit}";
if (loggedDeclisions == 0)
{
loggedDeclisions++;
@ -516,13 +505,23 @@ namespace KLHZ.Trader.Core.Exchange.Services
}
else
{
mess = $"Закрываю позицию {asset.Figi} ({(asset.Count > 0 ? "лонг" : "шорт")}) на счёте {_portfolioWrapper.Accounts[asset.AccountId].AccountName}. Количество {(long)asset.Count}, цена ~{price}";
assetForClose = asset;
}
if (assetForClose != null && mess != null)
if (assetForClose != null)
{
await _portfolioWrapper.Accounts[asset.AccountId].ClosePosition(message.Figi);
var settings = _exchangeConfig.InstrumentsSettings.FirstOrDefault(l => l.Figi == message.Figi);
var closingResult = await _portfolioWrapper.Accounts[asset.AccountId].ClosePosition(message.Figi);
if (closingResult.Success)
{
var profitText = profit == 0 ? string.Empty : ", профит {profit}";
mess = $"Закрываю позицию {asset.Figi} ({(asset.Count > 0 ? "лонг" : "шорт")}) на счёте {_portfolioWrapper.Accounts[asset.AccountId].AccountName}. Количество {(long)asset.Count}, цена ~{closingResult.ExecutedPrice / (settings?.PointPriceRub ?? 1)}, комиссия {closingResult.Comission}" + profitText;
}
else
{
mess = $"Закрытие позиции прошло с ошибками.";
}
await _dataBus.Broadcast(new MessageForAdmin() { Text = mess });
}
}
@ -537,12 +536,20 @@ namespace KLHZ.Trader.Core.Exchange.Services
{
if (TraderUtils.IsOperationAllowed(acc, message.Price, count, _exchangeConfig.AccountCashPartFutures, _exchangeConfig.AccountCashPart))
{
await acc.OpenPosition(message.Figi, positionType, stopLossShift, takeProfitShift, count);
var settings = _exchangeConfig.InstrumentsSettings.FirstOrDefault(l => l.Figi == message.Figi);
takeProfitShift = takeProfitShift * 2;
takeProfitShift = System.Math.Round(takeProfitShift);
takeProfitShift = takeProfitShift / 2;
stopLossShift = stopLossShift * 2;
stopLossShift = System.Math.Round(stopLossShift);
stopLossShift = stopLossShift / 2;
var openingResult = await acc.OpenPosition(message.Figi, positionType, stopLossShift, takeProfitShift, count, settings?.PointPriceRub ?? 1);
await _dataBus.Broadcast(new MessageForAdmin()
{
Text = $"Открываю позицию {message.Figi} ({(positionType == PositionType.Long ? "лонг" : "шорт")}) " +
$"на счёте {acc.AccountName}. Количество {(positionType == PositionType.Long ? "" : "-")}{count}, " +
$"цена ~{System.Math.Round(message.Price, 2)}. Стоп лосс: {(positionType == PositionType.Long ? "-" : "+")}{stopLossShift}. " +
$"на счёте {acc.AccountName}. Количество {(positionType == PositionType.Long ? "" : "-")}{openingResult.Count}, " +
$"цена ~{System.Math.Round(openingResult.ExecutedPrice / (settings?.PointPriceRub ?? 1), 2)}. Комиссия:{System.Math.Round(openingResult.Comission, 2)}. Стоп лосс: {(positionType == PositionType.Long ? "-" : "+")}{stopLossShift}. " +
$"Тейк профит: {(positionType == PositionType.Long ? "+" : "-")}{takeProfitShift}"
});
}

View File

@ -22,7 +22,7 @@
"Figi": "FUTIMOEXF000",
"LongLeverage": 10.3,
"ShortLeverage": 7.9,
"PriceToRubConvertationCoefficient" : 1
"PointPriceRub": 10
}
]
},