обновление логики + обновление механизма логирования сделок
test / deploy_trader_prod (push) Successful in 7m30s
Details
test / deploy_trader_prod (push) Successful in 7m30s
Details
parent
c2105ad019
commit
50766b6f20
|
@ -6,5 +6,6 @@
|
||||||
|
|
||||||
MarketBuy = 1,
|
MarketBuy = 1,
|
||||||
MarketSell = 101,
|
MarketSell = 101,
|
||||||
|
LimitBuy = 200,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,5 +7,6 @@
|
||||||
public string Figi { get; set; }
|
public string Figi { get; set; }
|
||||||
public string Ticker { get; set; }
|
public string Ticker { get; set; }
|
||||||
public DateTime Time { get; set; }
|
public DateTime Time { get; set; }
|
||||||
|
public bool IsSellPrice { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos
|
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos
|
||||||
{
|
{
|
||||||
|
@ -9,5 +10,7 @@ namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos
|
||||||
public required string Ticker { get; set; }
|
public required string Ticker { get; set; }
|
||||||
public DateTime Time { get; set; }
|
public DateTime Time { get; set; }
|
||||||
public bool IsHistoricalData { get; set; }
|
public bool IsHistoricalData { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public bool IsSellPrice { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
twavbs[i2 + 1]);
|
twavbs[i2 + 1]);
|
||||||
|
|
||||||
if (shift == 1 && !isCrossing.res) //если нет пересечения скользящих средний с окном 120 и 15 секунд между
|
if (shift == 1 && !isCrossing.res) //если нет пересечения скользящих средний с окном 120 и 15 секунд между
|
||||||
//текущей и предыдущей точкой - можно не продолжать выполнение.
|
//текущей и предыдущей точкой - можно не продолжать выполнение.
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,7 +211,7 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
for (int i = 0; i < 5 * PriceHistoryCacheUnit2.CacheMaxLength; i++)
|
for (int i = 0; i < 5 * PriceHistoryCacheUnit2.CacheMaxLength; i++)
|
||||||
{
|
{
|
||||||
cacheUnit.AddData(new PriceChange() { Figi = "", Ticker = "", Value = i, Time = DateTime.UtcNow });
|
cacheUnit.AddData(new PriceChange() { Figi = "", Ticker = "", Value = i, Time = DateTime.UtcNow });
|
||||||
if (i >= 500)
|
if (i >= PriceHistoryCacheUnit2.CacheMaxLength)
|
||||||
{
|
{
|
||||||
var data = cacheUnit.GetData().Result;
|
var data = cacheUnit.GetData().Result;
|
||||||
Assert.IsTrue(data.prices.Length == PriceHistoryCacheUnit2.CacheMaxLength);
|
Assert.IsTrue(data.prices.Length == PriceHistoryCacheUnit2.CacheMaxLength);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using KLHZ.Trader.Core.Common;
|
using KLHZ.Trader.Core.Common;
|
||||||
using KLHZ.Trader.Core.Exchange.Models.AssetsAccounting;
|
using KLHZ.Trader.Core.Exchange.Models.AssetsAccounting;
|
||||||
|
using KLHZ.Trader.Core.Exchange.Utils;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Tests
|
namespace KLHZ.Trader.Core.Tests
|
||||||
{
|
{
|
||||||
|
@ -89,5 +90,11 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
};
|
};
|
||||||
Assert.IsTrue(KLHZ.Trader.Core.Exchange.Services.Trader.IsBuyAllowed(account, 3000, 1, 0.5m, 0.1m));
|
Assert.IsTrue(KLHZ.Trader.Core.Exchange.Services.Trader.IsBuyAllowed(account, 3000, 1, 0.5m, 0.1m));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CalcProfitTest()
|
||||||
|
{
|
||||||
|
var profit = TradingCalculator.CaclProfit(2990, 2991.5m, 0.0025m, 10.3m, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,5 +9,6 @@
|
||||||
BidsSummary10 = 4,
|
BidsSummary10 = 4,
|
||||||
AsksSummary4 = 5,
|
AsksSummary4 = 5,
|
||||||
BidsSummary4 = 6,
|
BidsSummary4 = 6,
|
||||||
|
BidsAsksSummary4 = 7,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,5 +23,7 @@ namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public bool IsHistoricalData { get; set; }
|
public bool IsHistoricalData { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public bool IsSellPrice { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,12 @@ namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
||||||
[Column("ticker")]
|
[Column("ticker")]
|
||||||
public required string Ticker { get; set; }
|
public required string Ticker { get; set; }
|
||||||
|
|
||||||
[NotMapped]
|
|
||||||
public bool IsHistoricalData { get; set; }
|
public bool IsHistoricalData { get; set; }
|
||||||
|
|
||||||
[Column("processor")]
|
[Column("processor")]
|
||||||
public required string Processor { get; set; }
|
public required string Processor { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public bool IsSellPrice { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Tinkoff.InvestApi;
|
using Tinkoff.InvestApi;
|
||||||
using Tinkoff.InvestApi.V1;
|
using Tinkoff.InvestApi.V1;
|
||||||
using static Google.Rpc.Context.AttributeContext.Types;
|
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Exchange.Services
|
namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
{
|
{
|
||||||
|
@ -28,6 +27,8 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
private readonly CancellationTokenSource _cts = new();
|
private readonly CancellationTokenSource _cts = new();
|
||||||
private readonly IDataBus _eventBus;
|
private readonly IDataBus _eventBus;
|
||||||
private readonly bool _exchangeDataRecievingEnabled;
|
private readonly bool _exchangeDataRecievingEnabled;
|
||||||
|
private readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
public ExchangeDataReader(InvestApiClient investApiClient, IDataBus eventBus, TraderDataProvider tradeDataProvider,
|
public ExchangeDataReader(InvestApiClient investApiClient, IDataBus eventBus, TraderDataProvider tradeDataProvider,
|
||||||
IOptions<ExchangeConfig> options, IDbContextFactory<TraderDbContext> dbContextFactory,
|
IOptions<ExchangeConfig> options, IDbContextFactory<TraderDbContext> dbContextFactory,
|
||||||
ILogger<ExchangeDataReader> logger)
|
ILogger<ExchangeDataReader> logger)
|
||||||
|
@ -72,28 +73,30 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
|
|
||||||
private async Task SubscribeMyTrades()
|
private async Task SubscribeMyTrades()
|
||||||
{
|
{
|
||||||
var req = new TradesStreamRequest();
|
var req = new PortfolioStreamRequest();
|
||||||
foreach (var a in _tradeDataProvider.Accounts)
|
foreach (var a in _tradeDataProvider.Accounts)
|
||||||
{
|
{
|
||||||
req.Accounts.Add(a.Key);
|
req.Accounts.Add(a.Key);
|
||||||
}
|
}
|
||||||
using var stream = _investApiClient.OrdersStream.TradesStream(req);
|
|
||||||
|
using var stream = _investApiClient.OperationsStream.PortfolioStream(req);
|
||||||
await foreach (var response in stream.ResponseStream.ReadAllAsync())
|
await foreach (var response in stream.ResponseStream.ReadAllAsync())
|
||||||
{
|
{
|
||||||
if (response.OrderTrades != null)
|
if (response.Portfolio?.Positions != null)
|
||||||
{
|
{
|
||||||
foreach(var t in response.OrderTrades.Trades)
|
if (_semaphoreSlim.CurrentCount == 1)
|
||||||
{
|
{
|
||||||
//await _tradeDataProvider.LogDeal(new Models.AssetsAccounting.DealResult()
|
try
|
||||||
//{
|
{
|
||||||
// AccountId = response.OrderTrades.AccountId,
|
await _semaphoreSlim.WaitAsync(TimeSpan.FromSeconds(5));
|
||||||
// Figi = response.OrderTrades.Figi,
|
await _tradeDataProvider.SyncPortfolio(response.Portfolio.AccountId);
|
||||||
// Count = t.Quantity,
|
}
|
||||||
// Price = t.Price,
|
catch (Exception ex)
|
||||||
// Direction = Models.AssetsAccounting.DealDirection
|
{
|
||||||
//})
|
|
||||||
|
}
|
||||||
|
_semaphoreSlim.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,6 +155,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
Time = response.Trade.Time.ToDateTime().ToUniversalTime(),
|
Time = response.Trade.Time.ToDateTime().ToUniversalTime(),
|
||||||
Value = response.Trade.Price,
|
Value = response.Trade.Price,
|
||||||
IsHistoricalData = false,
|
IsHistoricalData = false,
|
||||||
|
IsSellPrice = response.Trade.Direction == TradeDirection.Sell
|
||||||
};
|
};
|
||||||
await _eventBus.Broadcast(message);
|
await _eventBus.Broadcast(message);
|
||||||
|
|
||||||
|
|
|
@ -118,7 +118,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
var assetType = _tradeDataProvider.GetAssetTypeByFigi(message.Figi);
|
var assetType = _tradeDataProvider.GetAssetTypeByFigi(message.Figi);
|
||||||
foreach (var acc in accounts)
|
foreach (var acc in accounts)
|
||||||
{
|
{
|
||||||
var assets = acc.Assets.Values.Where(a => a.Figi == message.Figi && DateTime.UtcNow - a.BoughtAt > TimeSpan.FromHours(4)).ToArray();
|
var assets = acc.Assets.Values.Where(a => a.Figi == message.Figi && (DateTime.UtcNow - a.BoughtAt > TimeSpan.FromHours(4))).ToArray();
|
||||||
foreach (var asset in assets)
|
foreach (var asset in assets)
|
||||||
{
|
{
|
||||||
var profit = TradingCalculator.CaclProfit(asset.BoughtPrice, message.Value,
|
var profit = TradingCalculator.CaclProfit(asset.BoughtPrice, message.Value,
|
||||||
|
|
|
@ -17,6 +17,7 @@ using Tinkoff.InvestApi;
|
||||||
using Tinkoff.InvestApi.V1;
|
using Tinkoff.InvestApi.V1;
|
||||||
using Asset = KLHZ.Trader.Core.Exchange.Models.AssetsAccounting.Asset;
|
using Asset = KLHZ.Trader.Core.Exchange.Models.AssetsAccounting.Asset;
|
||||||
using AssetType = KLHZ.Trader.Core.Exchange.Models.AssetsAccounting.AssetType;
|
using AssetType = KLHZ.Trader.Core.Exchange.Models.AssetsAccounting.AssetType;
|
||||||
|
using PositionType = KLHZ.Trader.Core.Exchange.Models.AssetsAccounting.PositionType;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Exchange.Services
|
namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
{
|
{
|
||||||
|
@ -181,6 +182,14 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
return _assetTypesCache.TryGetValue(figi, out var t) ? t : AssetType.Unknown;
|
return _assetTypesCache.TryGetValue(figi, out var t) ? t : AssetType.Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal async Task SyncPortfolio(string accountId)
|
||||||
|
{
|
||||||
|
if (Accounts.TryGetValue(accountId, out var account))
|
||||||
|
{
|
||||||
|
await SyncPortfolio(account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal async Task SyncPortfolio(ManagedAccount account)
|
internal async Task SyncPortfolio(ManagedAccount account)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -231,6 +240,24 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
#pragma warning restore CS0612 // Тип или член устарел
|
#pragma warning restore CS0612 // Тип или член устарел
|
||||||
account.Assets.AddOrUpdate(asset.Figi, asset, (k, v) => asset);
|
account.Assets.AddOrUpdate(asset.Figi, asset, (k, v) => asset);
|
||||||
oldAssets.Remove(asset.Figi);
|
oldAssets.Remove(asset.Figi);
|
||||||
|
if (trade == null && position.InstrumentType != "currency")
|
||||||
|
{
|
||||||
|
trade = new DataLayer.Entities.Trades.Trade()
|
||||||
|
{
|
||||||
|
AccountId = account.AccountId,
|
||||||
|
Figi = position.Figi,
|
||||||
|
Ticker = position.Ticker,
|
||||||
|
ArchiveStatus = 0,
|
||||||
|
Asset = (DataLayer.Entities.Trades.Enums.AssetType)(int)GetAssetTypeByFigi(position.Figi),
|
||||||
|
BoughtAt = DateTime.UtcNow,
|
||||||
|
Count = position.Quantity,
|
||||||
|
Direction = position.Quantity > 0 ? DataLayer.Entities.Trades.Enums.TradeDirection.Buy : DataLayer.Entities.Trades.Enums.TradeDirection.Sell,
|
||||||
|
Position = position.Quantity > 0 ? DataLayer.Entities.Trades.Enums.PositionType.Long : DataLayer.Entities.Trades.Enums.PositionType.Short,
|
||||||
|
Price = price
|
||||||
|
};
|
||||||
|
await context.Trades.AddAsync(trade);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
account.Total = portfolio.TotalAmountPortfolio;
|
account.Total = portfolio.TotalAmountPortfolio;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
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.Exchange.Models.AssetsAccounting;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
|
@ -42,18 +41,21 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var dir = OrderDirection.Unspecified;
|
var dir = OrderDirection.Unspecified;
|
||||||
var dealDirection = DealDirection.Unknown;
|
var orderType = OrderType.Unspecified;
|
||||||
var sign = 1;
|
|
||||||
if (tradeCommand.CommandType == Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketBuy)
|
if (tradeCommand.CommandType == Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketBuy)
|
||||||
{
|
{
|
||||||
dir = OrderDirection.Buy;
|
dir = OrderDirection.Buy;
|
||||||
dealDirection = DealDirection.Buy;
|
orderType = OrderType.Market;
|
||||||
}
|
}
|
||||||
else if (tradeCommand.CommandType == Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketSell)
|
else if (tradeCommand.CommandType == Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketSell)
|
||||||
{
|
{
|
||||||
sign = -1;
|
|
||||||
dir = OrderDirection.Sell;
|
dir = OrderDirection.Sell;
|
||||||
dealDirection = DealDirection.Sell;
|
orderType = OrderType.Market;
|
||||||
|
}
|
||||||
|
else if (tradeCommand.CommandType == Contracts.Messaging.Dtos.Enums.TradeCommandType.LimitBuy && tradeCommand.RecomendPrice.HasValue)
|
||||||
|
{
|
||||||
|
dir = OrderDirection.Buy;
|
||||||
|
orderType = OrderType.Limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
var req = new PostOrderRequest()
|
var req = new PostOrderRequest()
|
||||||
|
@ -61,23 +63,24 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
AccountId = tradeCommand.AccountId,
|
AccountId = tradeCommand.AccountId,
|
||||||
InstrumentId = tradeCommand.Figi,
|
InstrumentId = tradeCommand.Figi,
|
||||||
Direction = dir,
|
Direction = dir,
|
||||||
OrderType = OrderType.Market,
|
Price = tradeCommand.RecomendPrice ?? 0,
|
||||||
|
OrderType = orderType,
|
||||||
Quantity = tradeCommand.Count,
|
Quantity = tradeCommand.Count,
|
||||||
ConfirmMarginTrade = tradeCommand.EnableMargin,
|
ConfirmMarginTrade = tradeCommand.EnableMargin,
|
||||||
};
|
};
|
||||||
|
|
||||||
var res = await _investApiClient.Orders.PostOrderAsync(req);
|
var res = await _investApiClient.Orders.PostOrderAsync(req);
|
||||||
|
|
||||||
var result = new DealResult
|
//var result = new DealResult
|
||||||
{
|
//{
|
||||||
Count = sign * res.LotsExecuted,
|
// Count = sign * res.LotsExecuted,
|
||||||
Price = res.ExecutedOrderPrice,
|
// Price = res.ExecutedOrderPrice,
|
||||||
Success = true,
|
// Success = true,
|
||||||
Direction = dealDirection,
|
// Direction = dealDirection,
|
||||||
AccountId = tradeCommand.AccountId,
|
// AccountId = tradeCommand.AccountId,
|
||||||
Figi = tradeCommand.Figi,
|
// Figi = tradeCommand.Figi,
|
||||||
};
|
//};
|
||||||
await _tradeDataProvider.LogDeal(result);
|
//await _tradeDataProvider.LogDeal(result);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue