добавлена защита от потерь на падениях
test / deploy_trader_prod (push) Successful in 11m34s
Details
test / deploy_trader_prod (push) Successful in 11m34s
Details
parent
bd607b6c74
commit
32072e57f9
|
@ -63,5 +63,36 @@
|
|||
{
|
||||
return System.Math.Sqrt(System.Math.Pow(x2 - x1, 2) + System.Math.Pow(y2 - y1, 2));
|
||||
}
|
||||
|
||||
public static bool TryGetAreasRelation(DateTime[] times, decimal[] values, decimal currentValue, TimeSpan boundTimeSpan, out double relation)
|
||||
{
|
||||
var upArea = 0d;
|
||||
var downArea = 0d;
|
||||
var startTime = times[times.Length - 1];
|
||||
for (int i = 1; i < times.Length - 2; i++)
|
||||
{
|
||||
var k = values.Length - i;
|
||||
if (startTime - times[k] > boundTimeSpan)
|
||||
{
|
||||
break;
|
||||
}
|
||||
var point = (double)(values[k] - currentValue);
|
||||
var time = times[k];
|
||||
var timePrev = times[k - 1];
|
||||
var dt = (time - timePrev).TotalSeconds;
|
||||
var ar = dt * point;
|
||||
if (ar > 0)
|
||||
{
|
||||
upArea += ar;
|
||||
}
|
||||
else
|
||||
{
|
||||
downArea += System.Math.Abs(ar);
|
||||
}
|
||||
}
|
||||
var area = downArea + upArea;
|
||||
relation = area != 0 ? (double)upArea / area * 100 : 0;
|
||||
return area != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ namespace KLHZ.Trader.Core.Tests
|
|||
[Test]
|
||||
public void CalcProfitTest()
|
||||
{
|
||||
var profit = TradingCalculator.CaclProfit(2990, 2991.5m, 0.0025m, 10.3m, false);
|
||||
var profit = TradingCalculator.CaclProfit(2990, 2985m, 0.0025m, 10.3m, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
public static class BotModeSwitcher
|
||||
{
|
||||
private readonly static object _locker = new();
|
||||
private static bool _canSell = false;
|
||||
private static bool _canPurchase = false;
|
||||
private static bool _canSell = true;
|
||||
private static bool _canPurchase = true;
|
||||
|
||||
public static bool CanSell()
|
||||
{
|
||||
|
|
|
@ -77,10 +77,17 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
|
||||
private async Task ProcessPrices()
|
||||
{
|
||||
var buffer = new LinkedList<(DateTime, double)>();
|
||||
var tradesBufferBuys = new LinkedList<(DateTime, double)>();
|
||||
var tradesBufferSells = new LinkedList<(DateTime, double)>();
|
||||
var tradesRelBuffer = new LinkedList<(DateTime, decimal)>();
|
||||
while (await _pricesChannel.Reader.WaitToReadAsync())
|
||||
{
|
||||
var message = await _pricesChannel.Reader.ReadAsync();
|
||||
|
||||
if (message.IsHistoricalData)
|
||||
{
|
||||
await _tradeDataProvider.AddData(message, TimeSpan.FromHours(6));
|
||||
}
|
||||
if (_tradingInstrumentsFigis.Contains(message.Figi))
|
||||
{
|
||||
var currentTime = message.IsHistoricalData ? message.Time : DateTime.UtcNow;
|
||||
|
@ -92,14 +99,18 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
if (message.Figi == "FUTIMOEXF000")
|
||||
{
|
||||
var windowMaxSize = 1000;
|
||||
|
||||
await SellAssetsIfNeed(message);
|
||||
var data = await _tradeDataProvider.GetData(message.Figi, windowMaxSize);
|
||||
if (data.timestamps.Length <= 1)
|
||||
{
|
||||
buffer.Clear();
|
||||
}
|
||||
var state = ExchangeScheduler.GetCurrentState(message.Time);
|
||||
await ProcessClearing(data, state, message);
|
||||
//await SellOldAssetsIfCan(message);
|
||||
|
||||
ProcessOpeningStops(message, currentTime);
|
||||
await ProcessNewPriceIMOEXF(data, state, message, windowMaxSize);
|
||||
await ProcessNewPriceIMOEXF(data, state, message, windowMaxSize, buffer, tradesBufferBuys, tradesBufferSells, tradesRelBuffer);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -110,18 +121,19 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
}
|
||||
}
|
||||
|
||||
private async Task SellOldAssetsIfCan(INewPrice message)
|
||||
private async Task SellAssetsIfNeed(INewPrice message)
|
||||
{
|
||||
var accounts = _tradeDataProvider.Accounts.Values.ToArray();
|
||||
var assetType = _tradeDataProvider.GetAssetTypeByFigi(message.Figi);
|
||||
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).ToArray();
|
||||
foreach (var asset in assets)
|
||||
{
|
||||
var profit = TradingCalculator.CaclProfit(asset.BoughtPrice, message.Value,
|
||||
GetComission(assetType), GetLeverage(message.Figi, asset.Count < 0), asset.Count < 0);
|
||||
if (profit > 0)
|
||||
|
||||
if (message.Time - asset.BoughtAt > TimeSpan.FromMinutes(4) && profit<-66m)
|
||||
{
|
||||
await _dataBus.Broadcast(new TradeCommand()
|
||||
{
|
||||
|
@ -131,6 +143,23 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
: Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketSell,
|
||||
Count = (long)asset.Count,
|
||||
RecomendPrice = null,
|
||||
EnableMargin = false,
|
||||
});
|
||||
OpeningStops[message.Figi] = DateTime.UtcNow.AddMinutes(10);
|
||||
await LogDeclision(DeclisionTradeAction.CloseLong, message, profit);
|
||||
}
|
||||
|
||||
if (message.Time - asset.BoughtAt > TimeSpan.FromHours(4) && profit> 100)
|
||||
{
|
||||
await _dataBus.Broadcast(new TradeCommand()
|
||||
{
|
||||
AccountId = asset.AccountId,
|
||||
Figi = message.Figi,
|
||||
CommandType = asset.Count < 0 ? Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketBuy
|
||||
: Contracts.Messaging.Dtos.Enums.TradeCommandType.MarketSell,
|
||||
Count = (long)asset.Count,
|
||||
RecomendPrice = null,
|
||||
EnableMargin = false,
|
||||
});
|
||||
await LogDeclision(DeclisionTradeAction.CloseLong, message, profit);
|
||||
}
|
||||
|
@ -138,9 +167,11 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
}
|
||||
}
|
||||
|
||||
private async Task ProcessNewPriceIMOEXF((DateTime[] timestamps, decimal[] prices, decimal bidsCount, decimal asksCount) data,
|
||||
private async Task ProcessNewPriceIMOEXF((DateTime[] timestamps, decimal[] prices) data,
|
||||
ExchangeState state,
|
||||
INewPrice message, int windowMaxSize)
|
||||
INewPrice message, int windowMaxSize, LinkedList<(DateTime time, double val)> areasBuffer,
|
||||
LinkedList<(DateTime time, double val)> tradesBufferBuys, LinkedList<(DateTime time, double val)> tradesBufferSells,
|
||||
LinkedList<(DateTime time, decimal val)> tradesRelBufferSells)
|
||||
{
|
||||
var res = TradingEvent.None;
|
||||
var resultMoveAvFull = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices,
|
||||
|
@ -154,6 +185,70 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
await LogPrice(message, _smallWindowProcessor, resultMoveAvFull.smallWindowAv);
|
||||
}
|
||||
|
||||
//var oldTotalSales = (decimal)tradesBufferSells.Sum(s => s.val);
|
||||
//var oldTotalBuys = (decimal)tradesBufferBuys.Sum(s => s.val);
|
||||
//var oldTotalTrades = oldTotalSales + oldTotalBuys;
|
||||
|
||||
//if (message.Direction == 1)
|
||||
//{
|
||||
// tradesBufferBuys.AddLast((message.Time, message.Count));
|
||||
|
||||
// if (tradesBufferBuys.Last != null && tradesBufferBuys.First != null
|
||||
// && tradesBufferBuys.Last.Value.time - tradesBufferBuys.First.Value.time > TimeSpan.FromSeconds(60))
|
||||
// {
|
||||
// tradesBufferBuys.RemoveFirst();
|
||||
// }
|
||||
//}
|
||||
|
||||
//if (message.Direction == 2)
|
||||
//{
|
||||
// tradesBufferSells.AddLast((message.Time, message.Count));
|
||||
|
||||
// if (tradesBufferSells.Last != null && tradesBufferSells.First != null
|
||||
// && tradesBufferSells.Last.Value.time - tradesBufferSells.First.Value.time > TimeSpan.FromSeconds(60))
|
||||
// {
|
||||
// tradesBufferSells.RemoveFirst();
|
||||
// }
|
||||
//}
|
||||
|
||||
//var totalSales = (decimal)tradesBufferSells.Sum(s => s.val);
|
||||
//var totalBuys = (decimal)tradesBufferBuys.Sum(s => s.val);
|
||||
//var totalTrades = totalSales + totalBuys;
|
||||
//var tradesRelation = -100m;
|
||||
//var oldTradesRelation = -100m;
|
||||
//await LogPrice(message, "tradesvolume", totalTrades);
|
||||
|
||||
//if (totalTrades > 0 && oldTotalTrades > 0)
|
||||
//{
|
||||
// tradesRelation = (totalBuys - totalSales) / totalTrades;
|
||||
// oldTradesRelation = (oldTotalBuys - oldTotalSales) / oldTotalTrades;
|
||||
|
||||
// tradesRelBufferSells.AddLast((message.Time, tradesRelation - oldTradesRelation));
|
||||
|
||||
// if (tradesRelBufferSells.Last != null && tradesRelBufferSells.First != null
|
||||
// && tradesRelBufferSells.Last.Value.time - tradesRelBufferSells.First.Value.time > TimeSpan.FromSeconds(10))
|
||||
// {
|
||||
// tradesRelBufferSells.RemoveFirst();
|
||||
// }
|
||||
|
||||
// if (tradesRelBufferSells.Count > 0)
|
||||
// {
|
||||
// await LogPrice(message, "tradesrelation", tradesRelBufferSells.Sum(e => e.val) / tradesRelBufferSells.Count);
|
||||
// }
|
||||
//}
|
||||
|
||||
var areasRel = -1m;
|
||||
if (ShapeAreaCalculator.TryGetAreasRelation(data.timestamps, data.prices, message.Value, TimeSpan.FromMinutes(15), out var rel))
|
||||
{
|
||||
areasBuffer.AddLast((message.Time, rel));
|
||||
if (areasBuffer.Last != null && areasBuffer.First != null
|
||||
&& areasBuffer.Last.Value.time - areasBuffer.First.Value.time > TimeSpan.FromMinutes(1))
|
||||
{
|
||||
areasBuffer.RemoveFirst();
|
||||
}
|
||||
areasRel = (decimal)areasBuffer.Sum(a => a.val) / areasBuffer.Count;
|
||||
await LogPrice(message, "balancescalc30min", areasRel);
|
||||
}
|
||||
if ((res & TradingEvent.UptrendStart) == TradingEvent.UptrendStart
|
||||
&& !OpeningStops.TryGetValue(message.Figi, out _)
|
||||
&& state == ExchangeState.Open
|
||||
|
@ -161,8 +256,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
&& (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));
|
||||
if (fullData.isFullIntervalExists && fullData.prices.Last() - fullData.prices.First() > -8)
|
||||
if (areasRel >= 20 && areasRel < 75)
|
||||
{
|
||||
if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase())
|
||||
{
|
||||
|
@ -196,6 +290,10 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
}
|
||||
await LogDeclision(DeclisionTradeAction.OpenLong, message);
|
||||
}
|
||||
//else if (areasRel >=75)
|
||||
//{
|
||||
// await LogDeclision(DeclisionTradeAction.OpenShort, message);
|
||||
//}
|
||||
}
|
||||
|
||||
if ((res & TradingEvent.UptrendEnd) == TradingEvent.UptrendEnd)
|
||||
|
@ -245,7 +343,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
}
|
||||
}
|
||||
|
||||
private async Task ProcessClearing((DateTime[] timestamps, decimal[] prices, decimal bidsCount, decimal asksCount) data, ExchangeState state, INewPrice message)
|
||||
private async Task ProcessClearing((DateTime[] timestamps, decimal[] prices) data, ExchangeState state, INewPrice message)
|
||||
{
|
||||
if (state == ExchangeState.ClearingTime
|
||||
&& !message.IsHistoricalData
|
||||
|
|
|
@ -64,14 +64,14 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
return (Array.Empty<DateTime>(), Array.Empty<decimal>(), false);
|
||||
}
|
||||
|
||||
public async ValueTask<(DateTime[] timestamps, decimal[] prices, decimal bidsCount, decimal asksCount)> GetData(string figi, int? length = null)
|
||||
public async ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(string figi, int? length = null)
|
||||
{
|
||||
if (_historyCash.TryGetValue(figi, out var unit))
|
||||
{
|
||||
var res = await unit.GetData(length);
|
||||
return (res.timestamps, res.prices, unit.BidsCount, unit.AsksCount);
|
||||
return (res.timestamps, res.prices);
|
||||
}
|
||||
return (Array.Empty<DateTime>(), Array.Empty<decimal>(), 1, 1);
|
||||
return (Array.Empty<DateTime>(), Array.Empty<decimal>());
|
||||
}
|
||||
|
||||
public async ValueTask AddData(INewPrice message, TimeSpan? clearingInterval = null)
|
||||
|
|
|
@ -3,6 +3,7 @@ using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
|
|||
using KLHZ.Trader.Core.DataLayer;
|
||||
using KLHZ.Trader.Core.DataLayer.Entities.Orders;
|
||||
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
||||
using KLHZ.Trader.Core.Exchange.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
|
@ -12,13 +13,15 @@ namespace KLHZ.Trader.Service.Controllers
|
|||
[Route("[controller]/[action]")]
|
||||
public class PlayController : ControllerBase
|
||||
{
|
||||
private readonly TraderDataProvider _traderDataProvider;
|
||||
private readonly IDataBus _dataBus;
|
||||
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
|
||||
|
||||
public PlayController(IDataBus dataBus, IDbContextFactory<TraderDbContext> dbContextFactory)
|
||||
public PlayController(IDataBus dataBus, IDbContextFactory<TraderDbContext> dbContextFactory, TraderDataProvider traderDataProvider)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_dataBus = dataBus;
|
||||
_traderDataProvider = traderDataProvider;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
@ -26,8 +29,8 @@ namespace KLHZ.Trader.Service.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
var time1 = DateTime.UtcNow.AddDays(-7);
|
||||
var time2 = DateTime.UtcNow.AddMinutes(-30);
|
||||
var time1 = DateTime.UtcNow.AddDays(-30);
|
||||
var time2 = DateTime.UtcNow.AddMinutes(30);
|
||||
using var context1 = await _dbContextFactory.CreateDbContextAsync();
|
||||
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||
var data = await context1.PriceChanges
|
||||
|
@ -39,7 +42,9 @@ namespace KLHZ.Trader.Service.Controllers
|
|||
Ticker = c.Ticker,
|
||||
Time = c.Time,
|
||||
Value = c.Value,
|
||||
IsHistoricalData = true
|
||||
IsHistoricalData = true,
|
||||
Direction = c.Direction,
|
||||
Count = c.Count,
|
||||
})
|
||||
.ToArrayAsync();
|
||||
|
||||
|
@ -193,6 +198,95 @@ namespace KLHZ.Trader.Service.Controllers
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//[HttpGet]
|
||||
//public async Task GetBalance(string figi)
|
||||
//{
|
||||
// try
|
||||
// {
|
||||
// var time1 = DateTime.UtcNow.AddDays(-7);
|
||||
// using var context1 = await _dbContextFactory.CreateDbContextAsync();
|
||||
// context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||
// var data = await context1.PriceChanges
|
||||
// .Where(c => c.Figi == figi && c.Time >= time1)
|
||||
// .OrderBy(c => c.Time)
|
||||
// .Select(c => new NewPriceMessage()
|
||||
// {
|
||||
// Figi = figi,
|
||||
// Ticker = c.Ticker,
|
||||
// Time = c.Time,
|
||||
// Value = c.Value,
|
||||
// IsHistoricalData = true
|
||||
// })
|
||||
// .ToArrayAsync();
|
||||
// var buffer = new List<ProcessedPrice>();
|
||||
// var list = new LinkedList<(DateTime time, double value)>();
|
||||
// var previous = -1000d;
|
||||
// foreach (var mess in data)
|
||||
// {
|
||||
// await _traderDataProvider.AddData(mess, TimeSpan.FromHours(6));
|
||||
// var dataFromCache = await _traderDataProvider.GetData(figi, TimeSpan.FromMinutes(15));
|
||||
// if (dataFromCache.isFullIntervalExists)
|
||||
// {
|
||||
// if (ShapeAreaCalculator.TryGetAreasRelation(dataFromCache.timestamps, dataFromCache.prices,mess.Value,out var res))
|
||||
// {
|
||||
// if (list.Count > 0 && mess.Time - list.Last().time > TimeSpan.FromMinutes(5))
|
||||
// {
|
||||
// list.Clear();
|
||||
// }
|
||||
// list.AddLast((mess.Time, res));
|
||||
// if (list.Last().time - list.First().time > TimeSpan.FromMinutes(1))
|
||||
// {
|
||||
// list.RemoveFirst();
|
||||
// }
|
||||
// var newRes = (decimal)(list.Sum(i => i.value) / list.Count);
|
||||
// if (list.Count > 0)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// buffer.Add(new ProcessedPrice()
|
||||
// {
|
||||
// Figi = figi,
|
||||
// Processor = "balancescalc30min",
|
||||
// Ticker = mess.Ticker,
|
||||
// Time = mess.Time,
|
||||
// Value = newRes,
|
||||
// //Value = (decimal)res,
|
||||
// });
|
||||
// }
|
||||
// catch(Exception ex)
|
||||
// {
|
||||
|
||||
// }
|
||||
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// previous = -1d;
|
||||
// }
|
||||
// if (buffer.Count > 10000)
|
||||
// {
|
||||
// await context1.ProcessedPrices.AddRangeAsync(buffer);
|
||||
// await context1.SaveChangesAsync();
|
||||
// buffer.Clear();
|
||||
// }
|
||||
// }
|
||||
// if (buffer.Count > 0)
|
||||
// {
|
||||
// await context1.ProcessedPrices.AddRangeAsync(buffer);
|
||||
// await context1.SaveChangesAsync();
|
||||
// buffer.Clear();
|
||||
// }
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
|
||||
// }
|
||||
//}
|
||||
|
||||
////[HttpGet]
|
||||
//public async Task LoadTradesToHistory(string figi)
|
||||
//{
|
||||
|
|
Loading…
Reference in New Issue