рефакторинг + запись сделок по инструментам
test / deploy_trader_prod (push) Successful in 3m16s
Details
test / deploy_trader_prod (push) Successful in 3m16s
Details
parent
2d6bc10ed8
commit
cee02fe665
|
@ -5,7 +5,8 @@ namespace KLHZ.Trader.Core.Contracts.Declisions.Interfaces
|
|||
public interface IPriceHistoryCacheUnit
|
||||
{
|
||||
public string Figi { get; }
|
||||
public ValueTask AddData(INewPriceMessage priceChange);
|
||||
public int Length { get; }
|
||||
public ValueTask AddData(INewPrice priceChange);
|
||||
|
||||
public ValueTask<(DateTime[] timestamps, float[] prices)> GetData();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces
|
||||
{
|
||||
public interface INewPriceMessage
|
||||
public interface INewPrice
|
||||
{
|
||||
public bool IsHistoricalData { get; set; }
|
||||
public decimal Value { get; set; }
|
|
@ -0,0 +1,7 @@
|
|||
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces
|
||||
{
|
||||
public interface IProcessedPrice : INewPrice
|
||||
{
|
||||
public string Processor { get; set; }
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos
|
||||
{
|
||||
public class NewPriceMessage : INewPriceMessage
|
||||
public class NewPriceMessage : INewPrice
|
||||
{
|
||||
public decimal Value { get; set; }
|
||||
public required string Figi { get; set; }
|
||||
|
|
|
@ -6,12 +6,14 @@ namespace KLHZ.Trader.Core.Contracts.Messaging.Interfaces
|
|||
{
|
||||
public interface IDataBus
|
||||
{
|
||||
public bool AddChannel(string key, Channel<INewPriceMessage> channel);
|
||||
public bool AddChannel(string key, Channel<IProcessedPrice> channel);
|
||||
public bool AddChannel(string key, Channel<INewPrice> channel);
|
||||
public bool AddChannel(string key, Channel<TradeCommand> channel);
|
||||
public bool AddChannel(string key, Channel<IMessage> channel);
|
||||
public bool AddChannel(string key, Channel<INewCandle> channel);
|
||||
public Task BroadcastNewPrice(INewPriceMessage newPriceMessage);
|
||||
public Task BroadcastNewPrice(INewPrice newPriceMessage);
|
||||
public Task BroadcastCommand(TradeCommand command);
|
||||
public Task BroadcastNewCandle(INewCandle command);
|
||||
public Task BroadcastProcessedPrice(IProcessedPrice command);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
|
||||
|
||||
namespace KLHZ.Trader.Core.Math.Declisions.Dtos
|
||||
{
|
||||
public class ProcessedPrice : IProcessedPrice
|
||||
{
|
||||
public required string Processor { get; set; }
|
||||
public bool IsHistoricalData { get; set; }
|
||||
public decimal Value { get; set; }
|
||||
public required string Figi { get; set; }
|
||||
public required string Ticker { get; set; }
|
||||
public DateTime Time { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
|
||||
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
|
||||
using MathNet.Filtering.Kalman;
|
||||
using MathNet.Numerics.LinearAlgebra;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace KLHZ.Trader.Core.Math.Declisions.Dtos.Services
|
||||
{
|
||||
public class KalmanPredictor : IHostedService
|
||||
{
|
||||
private readonly double r = 1000.0; // Measurement covariance
|
||||
private readonly PriceHistoryCacheUnit _cache = new PriceHistoryCacheUnit("");
|
||||
private readonly Channel<INewPrice> _messages = Channel.CreateUnbounded<INewPrice>();
|
||||
private readonly IDataBus _dataBus;
|
||||
|
||||
private DiscreteKalmanFilter? _dkf;
|
||||
|
||||
public KalmanPredictor(IDataBus bus)
|
||||
{
|
||||
_dataBus = bus;
|
||||
bus.AddChannel(nameof(KalmanPredictor), _messages);
|
||||
_ = ProcessMessages();
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task Init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private async Task ProcessCalman(INewPrice message)
|
||||
{
|
||||
var H = Matrix<double>.Build.Dense(2, 2, new[] { 1d, 0d, 0d, 1d }); // Measurement matrix
|
||||
var length = _cache.Length;
|
||||
|
||||
if (length > 2 && _dkf != null)
|
||||
{
|
||||
var data = await _cache.GetData();
|
||||
|
||||
var dt = (data.timestamps[data.prices.Length - 1] - data.timestamps[data.prices.Length - 2]).TotalSeconds;
|
||||
if (dt < 0.0000001 || dt > 1800) return;
|
||||
var F = Matrix<double>.Build.Dense(2, 2, new[] { 1d, 0d, dt, 1 }); // State transition matrix
|
||||
var R = Matrix<double>.Build.Dense(2, 2, new[] { r, r / dt, r / dt, 2 * r / (dt * dt) });
|
||||
_dkf.Predict(F);
|
||||
|
||||
var state = _dkf.State;
|
||||
var value = state[0, 0] + dt * state[1, 0];
|
||||
if (!double.IsNaN(value))
|
||||
{
|
||||
await _dataBus.BroadcastProcessedPrice(new ProcessedPrice()
|
||||
{
|
||||
Figi = message.Figi,
|
||||
Processor = nameof(KalmanPredictor) + "_predict",
|
||||
Ticker = message.Ticker,
|
||||
IsHistoricalData = message.IsHistoricalData,
|
||||
Time = message.Time,
|
||||
Value = (decimal)value,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var dprice = data.prices[data.prices.Length - 1] - data.prices[data.prices.Length - 2];
|
||||
var k = dprice / dt;
|
||||
var b = data.prices[data.prices.Length - 2];
|
||||
var z = Matrix<double>.Build.Dense(2, 1, new[] { b, k });
|
||||
|
||||
_dkf.Update(z, H, R);
|
||||
|
||||
var state2 = _dkf.State;
|
||||
var value2 = state2[0, 0] + dt * state2[1, 0];
|
||||
if (!double.IsNaN(value))
|
||||
{
|
||||
await _dataBus.BroadcastProcessedPrice(new ProcessedPrice()
|
||||
{
|
||||
Figi = message.Figi,
|
||||
Processor = nameof(KalmanPredictor) + "_state",
|
||||
Ticker = message.Ticker,
|
||||
IsHistoricalData = message.IsHistoricalData,
|
||||
Time = message.Time,
|
||||
Value = (decimal)value,
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (length >= 2)
|
||||
{
|
||||
var data = await _cache.GetData();
|
||||
var dprice = data.prices[data.prices.Length - 1] - data.prices[data.prices.Length - 2];
|
||||
var dt = (data.timestamps[data.prices.Length - 1] - data.timestamps[data.prices.Length - 2]).TotalSeconds;
|
||||
if (dt < 0.0000001) return;
|
||||
var k = dprice / dt;
|
||||
var b = data.prices[data.prices.Length - 2];
|
||||
|
||||
|
||||
Matrix<double> x0 = Matrix<double>.Build.Dense(2, 1, new[] { b, k });
|
||||
Matrix<double> P0 = Matrix<double>.Build.Dense(2, 2, new[] { r, r / dt, r / dt, 2 * r / (dt * dt) });
|
||||
|
||||
_dkf = new DiscreteKalmanFilter(x0, P0);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessMovAv(INewPrice message, int window)
|
||||
{
|
||||
var data = await _cache.GetData();
|
||||
if (data.prices.Length < window) return;
|
||||
var sum = 0f;
|
||||
var count = 0;
|
||||
for (int i = 1; (i <= window); i++)
|
||||
{
|
||||
sum += data.prices[data.prices.Length - i];
|
||||
count++;
|
||||
}
|
||||
|
||||
await _dataBus.BroadcastProcessedPrice(new ProcessedPrice()
|
||||
{
|
||||
Figi = message.Figi,
|
||||
Processor = nameof(KalmanPredictor) + "_mov_av_window" + window.ToString(),
|
||||
Ticker = message.Ticker,
|
||||
IsHistoricalData = message.IsHistoricalData,
|
||||
Time = message.Time,
|
||||
Value = (decimal)(sum / count),
|
||||
});
|
||||
}
|
||||
|
||||
private async Task ProcessTimeWindow(INewPrice message, int window)
|
||||
{
|
||||
var data = await _cache.GetData();
|
||||
var sum = data.prices[data.prices.Length - 1];
|
||||
var count = 1;
|
||||
var startTime = data.timestamps[data.prices.Length - 1];
|
||||
for (int i = 2; (i < data.prices.Length && (startTime - data.timestamps[data.prices.Length - i]) < TimeSpan.FromSeconds(window)); i++)
|
||||
{
|
||||
sum += data.prices[data.prices.Length - i];
|
||||
count++;
|
||||
}
|
||||
|
||||
await _dataBus.BroadcastProcessedPrice(new ProcessedPrice()
|
||||
{
|
||||
Figi = message.Figi,
|
||||
Processor = nameof(KalmanPredictor) + "_timeWindow" + window.ToString(),
|
||||
Ticker = message.Ticker,
|
||||
IsHistoricalData = message.IsHistoricalData,
|
||||
Time = message.Time,
|
||||
Value = (decimal)(sum / count),
|
||||
});
|
||||
|
||||
var diffValue = System.Math.Abs((decimal)(sum / count) - message.Value);
|
||||
await _dataBus.BroadcastProcessedPrice(new ProcessedPrice()
|
||||
{
|
||||
Figi = message.Figi,
|
||||
Processor = nameof(KalmanPredictor) + "_diff" + window.ToString(),
|
||||
Ticker = message.Ticker,
|
||||
IsHistoricalData = message.IsHistoricalData,
|
||||
Time = message.Time,
|
||||
Value = diffValue > 3 ? diffValue : 0,
|
||||
});
|
||||
}
|
||||
|
||||
private async Task ProcessMessages()
|
||||
{
|
||||
|
||||
|
||||
while (await _messages.Reader.WaitToReadAsync())
|
||||
{
|
||||
var message = await _messages.Reader.ReadAsync();
|
||||
await _cache.AddData(message);
|
||||
try
|
||||
{
|
||||
//await ProcessCalman(message);
|
||||
await ProcessMovAv(message, 3);
|
||||
await ProcessTimeWindow(message, 5);
|
||||
await ProcessTimeWindow(message, 15);
|
||||
await ProcessTimeWindow(message, 120);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
|
||||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
|
||||
|
||||
namespace KLHZ.Trader.Core.Declisions.Services
|
||||
namespace KLHZ.Trader.Core.Math.Declisions.Dtos.Services
|
||||
{
|
||||
public class PriceHistoryCacheUnit : IPriceHistoryCacheUnit
|
||||
{
|
||||
|
@ -9,13 +9,24 @@ namespace KLHZ.Trader.Core.Declisions.Services
|
|||
|
||||
public string Figi { get; init; }
|
||||
|
||||
public int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly object _locker = new();
|
||||
private readonly float[] Prices = new float[ArrayMaxLength];
|
||||
private readonly DateTime[] Timestamps = new DateTime[ArrayMaxLength];
|
||||
|
||||
private int Length = 0;
|
||||
private int _length = 0;
|
||||
|
||||
public ValueTask AddData(INewPriceMessage priceChange)
|
||||
public ValueTask AddData(INewPrice priceChange)
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
|
@ -25,9 +36,9 @@ namespace KLHZ.Trader.Core.Declisions.Services
|
|||
Prices[Prices.Length - 1] = (float)priceChange.Value;
|
||||
Timestamps[Timestamps.Length - 1] = priceChange.Time;
|
||||
|
||||
if (Length < ArrayMaxLength)
|
||||
if (_length < ArrayMaxLength)
|
||||
{
|
||||
Length++;
|
||||
_length++;
|
||||
}
|
||||
}
|
||||
return ValueTask.CompletedTask;
|
||||
|
@ -35,17 +46,17 @@ namespace KLHZ.Trader.Core.Declisions.Services
|
|||
|
||||
public ValueTask<(DateTime[] timestamps, float[] prices)> GetData()
|
||||
{
|
||||
var prices = new float[Length];
|
||||
var timestamps = new DateTime[Length];
|
||||
lock (_locker)
|
||||
{
|
||||
Array.Copy(Prices, Prices.Length - Length, prices, 0, prices.Length);
|
||||
Array.Copy(Timestamps, Prices.Length - Length, timestamps, 0, timestamps.Length);
|
||||
var prices = new float[_length];
|
||||
var timestamps = new DateTime[_length];
|
||||
Array.Copy(Prices, Prices.Length - _length, prices, 0, prices.Length);
|
||||
Array.Copy(Timestamps, Prices.Length - _length, timestamps, 0, timestamps.Length);
|
||||
return ValueTask.FromResult((timestamps, prices));
|
||||
}
|
||||
}
|
||||
|
||||
public PriceHistoryCacheUnit(string figi, params INewPriceMessage[] priceChanges)
|
||||
public PriceHistoryCacheUnit(string figi, params INewPrice[] priceChanges)
|
||||
{
|
||||
Figi = figi;
|
||||
|
||||
|
@ -69,7 +80,7 @@ namespace KLHZ.Trader.Core.Declisions.Services
|
|||
Array.Copy(prices, 0, Prices, Prices.Length - prices.Length, prices.Length);
|
||||
Array.Copy(times, 0, Timestamps, Timestamps.Length - times.Length, times.Length);
|
||||
|
||||
Length = times.Length > ArrayMaxLength ? ArrayMaxLength : times.Length;
|
||||
_length = times.Length > ArrayMaxLength ? ArrayMaxLength : times.Length;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MathNet.Filtering.Kalman" Version="0.7.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.8" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\KLHZ.Trader.Core.Contracts\KLHZ.Trader.Core.Contracts.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,5 +1,4 @@
|
|||
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
||||
using KLHZ.Trader.Core.Declisions.Services;
|
||||
|
||||
namespace KLHZ.Trader.Core.Tests
|
||||
{
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
||||
using KLHZ.Trader.Core.Declisions.Services;
|
||||
using KLHZ.Trader.Core.Declisions.Utils;
|
||||
|
||||
namespace KLHZ.Trader.Core.Tests
|
||||
|
|
|
@ -10,15 +10,21 @@ namespace KLHZ.Trader.Core.Common.Messaging.Services
|
|||
{
|
||||
private readonly ConcurrentDictionary<string, Channel<IMessage>> _messagesChannels = new();
|
||||
private readonly ConcurrentDictionary<string, Channel<INewCandle>> _candlesChannels = new();
|
||||
private readonly ConcurrentDictionary<string, Channel<INewPriceMessage>> _priceChannels = new();
|
||||
private readonly ConcurrentDictionary<string, Channel<INewPrice>> _priceChannels = new();
|
||||
private readonly ConcurrentDictionary<string, Channel<IProcessedPrice>> _processedPricesChannels = new();
|
||||
private readonly ConcurrentDictionary<string, Channel<TradeCommand>> _commandChannels = new();
|
||||
|
||||
public bool AddChannel(string key, Channel<IProcessedPrice> channel)
|
||||
{
|
||||
return _processedPricesChannels.TryAdd(key, channel);
|
||||
}
|
||||
|
||||
public bool AddChannel(string key, Channel<IMessage> channel)
|
||||
{
|
||||
return _messagesChannels.TryAdd(key, channel);
|
||||
}
|
||||
|
||||
public bool AddChannel(string key, Channel<INewPriceMessage> channel)
|
||||
public bool AddChannel(string key, Channel<INewPrice> channel)
|
||||
{
|
||||
return _priceChannels.TryAdd(key, channel);
|
||||
}
|
||||
|
@ -33,7 +39,7 @@ namespace KLHZ.Trader.Core.Common.Messaging.Services
|
|||
return _commandChannels.TryAdd(key, channel);
|
||||
}
|
||||
|
||||
public async Task BroadcastNewPrice(INewPriceMessage newPriceMessage)
|
||||
public async Task BroadcastNewPrice(INewPrice newPriceMessage)
|
||||
{
|
||||
foreach (var channel in _priceChannels.Values)
|
||||
{
|
||||
|
@ -41,6 +47,14 @@ namespace KLHZ.Trader.Core.Common.Messaging.Services
|
|||
}
|
||||
}
|
||||
|
||||
public async Task BroadcastProcessedPrice(IProcessedPrice mess)
|
||||
{
|
||||
foreach (var channel in _processedPricesChannels.Values)
|
||||
{
|
||||
await channel.Writer.WriteAsync(mess);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task BroadcastNewCandle(INewCandle newPriceMessage)
|
||||
{
|
||||
foreach (var channel in _candlesChannels.Values)
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.ComponentModel.DataAnnotations.Schema;
|
|||
namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
||||
{
|
||||
[Table("price_changes")]
|
||||
public class PriceChange : INewPriceMessage
|
||||
public class PriceChange : INewPrice
|
||||
{
|
||||
[Column("id")]
|
||||
public long Id { get; set; }
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
|
||||
{
|
||||
[Table("processed_prices")]
|
||||
public class ProcessedPrice : IProcessedPrice
|
||||
{
|
||||
[Column("id")]
|
||||
public long Id { get; set; }
|
||||
|
||||
[Column("time")]
|
||||
public DateTime Time { get; set; }
|
||||
|
||||
[Column("value")]
|
||||
public decimal Value { get; set; }
|
||||
|
||||
[Column("figi")]
|
||||
public required string Figi { get; set; }
|
||||
|
||||
[Column("ticker")]
|
||||
public required string Ticker { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public bool IsHistoricalData { get; set; }
|
||||
|
||||
[Column("processor")]
|
||||
public required string Processor { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace KLHZ.Trader.Core.DataLayer.Entities.Trades
|
||||
{
|
||||
/// <summary>
|
||||
/// Обезличенная сделка с биржи над инструментом.
|
||||
/// </summary>
|
||||
[Table("instrument_trades")]
|
||||
public class InstrumentTrade
|
||||
{
|
||||
[Column("trade_id")]
|
||||
public long Id { get; set; }
|
||||
|
||||
[Column("bought_at")]
|
||||
public DateTime BoughtAt { get; set; }
|
||||
|
||||
[Column("figi")]
|
||||
public required string Figi { get; set; }
|
||||
|
||||
[Column("ticker")]
|
||||
public required string Ticker { get; set; }
|
||||
|
||||
[Column("price")]
|
||||
|
||||
public decimal Price { get; set; }
|
||||
|
||||
[Column("count")]
|
||||
public decimal Count { get; set; }
|
||||
|
||||
[Column("direction")]
|
||||
public TradeDirection Direction { get; set; }
|
||||
}
|
||||
}
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
namespace KLHZ.Trader.Core.DataLayer.Entities.Trades
|
||||
{
|
||||
/// <summary>
|
||||
/// Сделка, совершенная ботом.
|
||||
/// </summary>
|
||||
[Table("trades")]
|
||||
public class Trade
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
public enum TradeDirection
|
||||
{
|
||||
Unknown = 0,
|
||||
Income = 1,
|
||||
Outcome = 2
|
||||
Buy = 1,
|
||||
Sell = 2
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,10 @@ namespace KLHZ.Trader.Core.DataLayer
|
|||
public class TraderDbContext : DbContext
|
||||
{
|
||||
public DbSet<Trade> Trades { get; set; }
|
||||
public DbSet<InstrumentTrade> InstrumentTrades { get; set; }
|
||||
public DbSet<Declision> Declisions { get; set; }
|
||||
public DbSet<PriceChange> PriceChanges { get; set; }
|
||||
public DbSet<ProcessedPrice> ProcessedPrices { get; set; }
|
||||
public DbSet<Candle> Candles { get; set; }
|
||||
public TraderDbContext(DbContextOptions<TraderDbContext> options)
|
||||
: base(options)
|
||||
|
@ -30,6 +32,15 @@ namespace KLHZ.Trader.Core.DataLayer
|
|||
v => DateTime.SpecifyKind(v, DateTimeKind.Utc));
|
||||
});
|
||||
|
||||
modelBuilder.Entity<InstrumentTrade>(entity =>
|
||||
{
|
||||
entity.HasKey(e1 => e1.Id);
|
||||
entity.Property(e => e.BoughtAt)
|
||||
.HasConversion(
|
||||
v => v.ToUniversalTime(),
|
||||
v => DateTime.SpecifyKind(v, DateTimeKind.Utc));
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Declision>(entity =>
|
||||
{
|
||||
entity.HasKey(e1 => e1.Id);
|
||||
|
@ -49,6 +60,16 @@ namespace KLHZ.Trader.Core.DataLayer
|
|||
v => DateTime.SpecifyKind(v, DateTimeKind.Utc));
|
||||
});
|
||||
|
||||
modelBuilder.Entity<ProcessedPrice>(entity =>
|
||||
{
|
||||
entity.HasKey(e1 => e1.Id);
|
||||
entity.Ignore(e1 => e1.IsHistoricalData);
|
||||
entity.Property(e => e.Time)
|
||||
.HasConversion(
|
||||
v => v.ToUniversalTime(),
|
||||
v => DateTime.SpecifyKind(v, DateTimeKind.Utc));
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Candle>(entity =>
|
||||
{
|
||||
entity.HasKey(e1 => new { e1.Figi, e1.Time });
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
|
||||
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
|
||||
using KLHZ.Trader.Core.DataLayer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace KLHZ.Trader.Core.Declisions.Services
|
||||
{
|
||||
public class ProcessedPricesLogger : IHostedService
|
||||
{
|
||||
private readonly ILogger<ProcessedPricesLogger> _logger;
|
||||
private readonly IDataBus _dataBus;
|
||||
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
|
||||
|
||||
private readonly Channel<IProcessedPrice> _channel = Channel.CreateUnbounded<IProcessedPrice>();
|
||||
public ProcessedPricesLogger(IDataBus dataBus, IDbContextFactory<TraderDbContext> dbContextFactory, ILogger<ProcessedPricesLogger> logger)
|
||||
{
|
||||
_dataBus = dataBus;
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_logger = logger;
|
||||
_dataBus.AddChannel(nameof(ProcessedPricesLogger), _channel);
|
||||
_ = ProcessMessages();
|
||||
}
|
||||
|
||||
private async Task ProcessMessages()
|
||||
{
|
||||
using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||
var buffer = new List<DataLayer.Entities.Prices.ProcessedPrice>();
|
||||
var lastWrite = DateTime.UtcNow;
|
||||
while (await _channel.Reader.WaitToReadAsync())
|
||||
{
|
||||
try
|
||||
{
|
||||
var message = await _channel.Reader.ReadAsync();
|
||||
|
||||
buffer.Add(new DataLayer.Entities.Prices.ProcessedPrice()
|
||||
{
|
||||
Figi = message.Figi,
|
||||
Processor = message.Processor,
|
||||
Ticker = message.Ticker,
|
||||
IsHistoricalData = message.IsHistoricalData,
|
||||
Time = message.Time,
|
||||
Value = message.Value,
|
||||
});
|
||||
|
||||
if (buffer.Count > 10000 || (DateTime.UtcNow - lastWrite) > TimeSpan.FromSeconds(5) || _channel.Reader.Count == 0)
|
||||
{
|
||||
await context.AddRangeAsync(buffer);
|
||||
await context.SaveChangesAsync();
|
||||
buffer.Clear();
|
||||
lastWrite = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,11 +5,11 @@ using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
|
|||
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
|
||||
using KLHZ.Trader.Core.DataLayer;
|
||||
using KLHZ.Trader.Core.DataLayer.Entities.Declisions;
|
||||
using KLHZ.Trader.Core.Declisions.Utils;
|
||||
using KLHZ.Trader.Core.Exchange;
|
||||
using KLHZ.Trader.Core.Exchange.Extentions;
|
||||
using KLHZ.Trader.Core.Exchange.Models;
|
||||
using KLHZ.Trader.Core.Exchange.Services;
|
||||
using KLHZ.Trader.Core.Math.Declisions.Dtos.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
@ -29,7 +29,7 @@ namespace KLHZ.Trader.Core.Declisions.Services
|
|||
private readonly BotModeSwitcher _botModeSwitcher;
|
||||
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
|
||||
private readonly ConcurrentDictionary<string, ManagedAccount> Accounts = new();
|
||||
private readonly ConcurrentDictionary<string, PriceHistoryCacheUnit> _historyCash = new();
|
||||
private readonly ConcurrentDictionary<string, IPriceHistoryCacheUnit> _historyCash = new();
|
||||
private readonly ITradingEventsDetector _tradingEventsDetector;
|
||||
|
||||
|
||||
|
@ -40,7 +40,7 @@ namespace KLHZ.Trader.Core.Declisions.Services
|
|||
private readonly decimal _defaultBuyPartOfAccount;
|
||||
private readonly string[] _managedAccountsNamePatterns = [];
|
||||
|
||||
private readonly Channel<INewPriceMessage> _pricesChannel = Channel.CreateUnbounded<INewPriceMessage>();
|
||||
private readonly Channel<INewPrice> _pricesChannel = Channel.CreateUnbounded<INewPrice>();
|
||||
|
||||
public Trader(
|
||||
ITradingEventsDetector tradingEventsDetector,
|
||||
|
@ -89,7 +89,6 @@ namespace KLHZ.Trader.Core.Declisions.Services
|
|||
_ = ProcessMessages();
|
||||
}
|
||||
|
||||
|
||||
private async Task ProcessMessages()
|
||||
{
|
||||
while (await _pricesChannel.Reader.WaitToReadAsync())
|
||||
|
@ -97,7 +96,7 @@ namespace KLHZ.Trader.Core.Declisions.Services
|
|||
var message = await _pricesChannel.Reader.ReadAsync();
|
||||
if (_historyCash.TryGetValue(message.Figi, out var data))
|
||||
{
|
||||
data.AddData(message);
|
||||
await data.AddData(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -146,24 +145,6 @@ namespace KLHZ.Trader.Core.Declisions.Services
|
|||
}
|
||||
}
|
||||
|
||||
public async Task Preprocess(string figi)
|
||||
{
|
||||
if (_historyCash.TryGetValue(figi, out var unit))
|
||||
{
|
||||
var periodData1 = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, TimeSpan.FromSeconds(10));
|
||||
var periodData2 = unit.GetPriceDiffForTimeSpan(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30));
|
||||
if (Math.Abs(periodData1.PeriodDiff) <= 1 && periodData2.PeriodDiff > 2)
|
||||
{
|
||||
//можно покупать.
|
||||
}
|
||||
|
||||
if (Math.Abs(periodData1.PeriodDiff) <= 1 && periodData2.PeriodDiff < -2)
|
||||
{
|
||||
//можно продавать.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
|
||||
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
|
||||
using KLHZ.Trader.Core.DataLayer.Entities.Declisions;
|
||||
using KLHZ.Trader.Core.Declisions.Utils;
|
||||
|
||||
namespace KLHZ.Trader.Core.Declisions.Services
|
||||
|
@ -36,28 +35,6 @@ namespace KLHZ.Trader.Core.Declisions.Services
|
|||
var uptrendEnds2 = data.CheckLongClose(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(30), meanfullDiff, 15, 8);
|
||||
uptrendEnds |= uptrendEnds2;
|
||||
|
||||
//var uptrendEnds2 = data.CheckUptrendEnding(TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(20), meanfullDiff);
|
||||
|
||||
//var uptrendEnds = uptrendEnds1 || uptrendEnds2;
|
||||
|
||||
var declisionAction = DeclisionTradeAction.Unknown;
|
||||
|
||||
//if (downtrendStarts)
|
||||
//{
|
||||
// //declisionAction = DeclisionTradeAction.OpenShort;
|
||||
//}
|
||||
if (uptrendStarts)
|
||||
{
|
||||
declisionAction = DeclisionTradeAction.OpenLong;
|
||||
}
|
||||
//else if (downtrendEnds)
|
||||
//{
|
||||
// //declisionAction = DeclisionTradeAction.CloseShort;
|
||||
//}
|
||||
else if (uptrendEnds)
|
||||
{
|
||||
declisionAction = DeclisionTradeAction.CloseLong;
|
||||
}
|
||||
return new TradingEventsDto(uptrendEnds, uptrendStarts);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
|
||||
using KLHZ.Trader.Core.Declisions.Dtos;
|
||||
using KLHZ.Trader.Core.Declisions.Services;
|
||||
using KLHZ.Trader.Core.Math.Declisions.Dtos.Services;
|
||||
|
||||
namespace KLHZ.Trader.Core.Declisions.Utils
|
||||
{
|
||||
|
@ -66,32 +66,32 @@ namespace KLHZ.Trader.Core.Declisions.Utils
|
|||
|
||||
internal static bool CheckStable(this PeriodPricesInfoDto data, float meanfullDiff)
|
||||
{
|
||||
meanfullDiff = Math.Abs(meanfullDiff);
|
||||
return data.Success && Math.Abs(data.PeriodDiff) < 1.5 * meanfullDiff && Math.Abs(data.PeriodMax - data.PeriodMin) < 2 * meanfullDiff;
|
||||
meanfullDiff = System.Math.Abs(meanfullDiff);
|
||||
return data.Success && System.Math.Abs(data.PeriodDiff) < 1.5 * meanfullDiff && System.Math.Abs(data.PeriodMax - data.PeriodMin) < 2 * meanfullDiff;
|
||||
}
|
||||
|
||||
internal static bool CheckGrowing(this PeriodPricesInfoDto data, float meanfullDiff)
|
||||
{
|
||||
return meanfullDiff > 0 && data.Success && data.PeriodDiff > meanfullDiff && Math.Abs(data.PeriodMax - data.PeriodMin) < 3 * Math.Abs(data.PeriodDiff);
|
||||
return meanfullDiff > 0 && data.Success && data.PeriodDiff > meanfullDiff && System.Math.Abs(data.PeriodMax - data.PeriodMin) < 3 * System.Math.Abs(data.PeriodDiff);
|
||||
}
|
||||
|
||||
internal static bool CheckFalling(this PeriodPricesInfoDto data, float meanfullDiff)
|
||||
{
|
||||
meanfullDiff = -meanfullDiff;
|
||||
return meanfullDiff < 0 && data.Success && data.PeriodDiff < meanfullDiff && Math.Abs(data.PeriodMax - data.PeriodMin) < 3 * Math.Abs(data.PeriodDiff);
|
||||
return meanfullDiff < 0 && data.Success && data.PeriodDiff < meanfullDiff && System.Math.Abs(data.PeriodMax - data.PeriodMin) < 3 * System.Math.Abs(data.PeriodDiff);
|
||||
}
|
||||
|
||||
internal static float CalcTrendRelationAbs(PeriodPricesInfoDto first, PeriodPricesInfoDto second)
|
||||
{
|
||||
var k1 = Math.Abs(first.PeriodDiff) / Math.Abs(first.Period.TotalSeconds);
|
||||
var k2 = Math.Abs(second.PeriodDiff) / Math.Abs(second.Period.TotalSeconds);
|
||||
var k1 = System.Math.Abs(first.PeriodDiff) / System.Math.Abs(first.Period.TotalSeconds);
|
||||
var k2 = System.Math.Abs(second.PeriodDiff) / System.Math.Abs(second.Period.TotalSeconds);
|
||||
if (k2 == 0 && k1 != 0) return 1000;
|
||||
return (float)(k1 / k2);
|
||||
}
|
||||
internal static float CalcTrendRelationAbs(TwoPeriodsProcessingDto data)
|
||||
{
|
||||
var k1 = Math.Abs(data.DiffStart) / Math.Abs(data.PeriodStart.TotalSeconds);
|
||||
var k2 = Math.Abs(data.DiffEnd) / Math.Abs(data.PeriodEnd.TotalSeconds);
|
||||
var k1 = System.Math.Abs(data.DiffStart) / System.Math.Abs(data.PeriodStart.TotalSeconds);
|
||||
var k2 = System.Math.Abs(data.DiffEnd) / System.Math.Abs(data.PeriodEnd.TotalSeconds);
|
||||
if (k2 == 0 && k1 != 0) return 1000;
|
||||
return (float)(k1 / k2);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
|
||||
using KLHZ.Trader.Core.DataLayer;
|
||||
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
||||
using KLHZ.Trader.Core.DataLayer.Entities.Trades;
|
||||
using KLHZ.Trader.Core.Exchange.Extentions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
@ -10,7 +11,6 @@ using Microsoft.Extensions.Options;
|
|||
using System.Collections.Concurrent;
|
||||
using Tinkoff.InvestApi;
|
||||
using Tinkoff.InvestApi.V1;
|
||||
using Candle = KLHZ.Trader.Core.DataLayer.Entities.Prices.Candle;
|
||||
|
||||
namespace KLHZ.Trader.Core.Exchange.Services
|
||||
{
|
||||
|
@ -95,6 +95,12 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
SubscriptionAction = SubscriptionAction.Subscribe
|
||||
};
|
||||
|
||||
|
||||
var tradesRequest = new SubscribeTradesRequest
|
||||
{
|
||||
SubscriptionAction = SubscriptionAction.Subscribe
|
||||
};
|
||||
|
||||
foreach (var f in _instrumentsFigis)
|
||||
{
|
||||
request.Instruments.Add(
|
||||
|
@ -102,6 +108,12 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
{
|
||||
InstrumentId = f
|
||||
});
|
||||
|
||||
tradesRequest.Instruments.Add(
|
||||
new TradeInstrument()
|
||||
{
|
||||
InstrumentId = f
|
||||
});
|
||||
}
|
||||
|
||||
await stream.RequestStream.WriteAsync(new MarketDataRequest
|
||||
|
@ -109,6 +121,17 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
SubscribeLastPriceRequest = request,
|
||||
});
|
||||
|
||||
await stream.RequestStream.WriteAsync(new MarketDataRequest
|
||||
{
|
||||
SubscribeTradesRequest = tradesRequest,
|
||||
});
|
||||
|
||||
using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||
var pricesBuffer = new List<PriceChange>();
|
||||
var tradesBuffer = new List<InstrumentTrade>();
|
||||
var lastWritePrices = DateTime.UtcNow;
|
||||
var lastWriteTrades = DateTime.UtcNow;
|
||||
await foreach (var response in stream.ResponseStream.ReadAllAsync())
|
||||
{
|
||||
if (response.LastPrice != null)
|
||||
|
@ -122,63 +145,41 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
IsHistoricalData = false,
|
||||
};
|
||||
await _eventBus.BroadcastNewPrice(message);
|
||||
using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||
await context.PriceChanges.AddAsync(message);
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
pricesBuffer.Add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SubscribeCandles()
|
||||
{
|
||||
using var stream = _investApiClient.MarketDataStream.MarketDataStream();
|
||||
|
||||
var request = new SubscribeCandlesRequest
|
||||
{
|
||||
SubscriptionAction = SubscriptionAction.Subscribe,
|
||||
CandleSourceType = GetCandlesRequest.Types.CandleSource.Exchange
|
||||
};
|
||||
|
||||
foreach (var f in _instrumentsFigis)
|
||||
{
|
||||
request.Instruments.Add(
|
||||
new CandleInstrument()
|
||||
{
|
||||
InstrumentId = f,
|
||||
Interval = SubscriptionInterval.OneMinute
|
||||
});
|
||||
}
|
||||
|
||||
await stream.RequestStream.WriteAsync(new MarketDataRequest
|
||||
{
|
||||
SubscribeCandlesRequest = request,
|
||||
});
|
||||
|
||||
await foreach (var response in stream.ResponseStream.ReadAllAsync())
|
||||
{
|
||||
if (response.Candle != null)
|
||||
if (response.Trade != null)
|
||||
{
|
||||
var message = new Candle()
|
||||
var trade = new KLHZ.Trader.Core.DataLayer.Entities.Trades.InstrumentTrade()
|
||||
{
|
||||
Figi = response.Candle.Figi,
|
||||
Ticker = GetTickerByFigi(response.LastPrice.Figi),
|
||||
Time = response.Candle.Time.ToDateTime().ToUniversalTime(),
|
||||
Close = response.Candle.Close,
|
||||
Open = response.Candle.Open,
|
||||
Low = response.Candle.Low,
|
||||
High = response.Candle.High,
|
||||
Volume = response.Candle.Volume,
|
||||
IsHistoricalData = false,
|
||||
Figi = response.Trade.Figi,
|
||||
BoughtAt = response.Trade.Time.ToDateTime().ToUniversalTime(),
|
||||
Ticker = GetTickerByFigi(response.Trade.Figi),
|
||||
Price = response.Trade.Price,
|
||||
Count = response.Trade.Quantity,
|
||||
Direction = response.Trade.Direction == Tinkoff.InvestApi.V1.TradeDirection.Sell ? KLHZ.Trader.Core.DataLayer.Entities.Trades.TradeDirection.Sell : KLHZ.Trader.Core.DataLayer.Entities.Trades.TradeDirection.Buy,
|
||||
};
|
||||
await _eventBus.BroadcastNewCandle(message);
|
||||
using var context = await _dbContextFactory.CreateDbContextAsync();
|
||||
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||
await context.Candles.AddAsync(message);
|
||||
tradesBuffer.Add(trade);
|
||||
}
|
||||
|
||||
|
||||
//if (pricesBuffer.Count > 200 || (DateTime.UtcNow - lastWritePrices).TotalSeconds > 10)
|
||||
{
|
||||
lastWritePrices = DateTime.UtcNow;
|
||||
await context.PriceChanges.AddRangeAsync(pricesBuffer);
|
||||
pricesBuffer.Clear();
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
//if (tradesBuffer.Count > 200 || (DateTime.UtcNow - lastWriteTrades).TotalSeconds > 10)
|
||||
{
|
||||
lastWriteTrades = DateTime.UtcNow;
|
||||
await context.InstrumentTrades.AddRangeAsync(tradesBuffer);
|
||||
tradesBuffer.Clear();
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTickerByFigi(string figi)
|
||||
{
|
||||
return _tickersCache.TryGetValue(figi, out var ticker) ? ticker : string.Empty;
|
||||
|
|
|
@ -237,7 +237,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
Count = res.LotsExecuted,
|
||||
Price = res.ExecutedOrderPrice,
|
||||
Position = DataLayer.Entities.Trades.PositionType.Long,
|
||||
Direction = DataLayer.Entities.Trades.TradeDirection.Income,
|
||||
Direction = DataLayer.Entities.Trades.TradeDirection.Buy,
|
||||
Asset = DataLayer.Entities.Trades.AssetType.Common,
|
||||
};
|
||||
|
||||
|
|
|
@ -12,10 +12,12 @@
|
|||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
<PackageReference Include="Telegram.Bot" Version="22.6.0" />
|
||||
<PackageReference Include="Tinkoff.InvestApi" Version="0.6.17" />
|
||||
<PackageReference Include="MathNet.Filtering.Kalman" Version="0.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\KLHZ.Trader.Core.Contracts\KLHZ.Trader.Core.Contracts.csproj" />
|
||||
<ProjectReference Include="..\KLHZ.Trader.Core.Math\KLHZ.Trader.Core.Math.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -22,24 +22,31 @@ namespace KLHZ.Trader.Service.Controllers
|
|||
[HttpGet]
|
||||
public async Task Run(string figi)
|
||||
{
|
||||
using var context1 = await _dbContextFactory.CreateDbContextAsync();
|
||||
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||
var data = await context1.PriceChanges
|
||||
.Where(c => c.Figi == figi)
|
||||
.OrderBy(c => c.Time)
|
||||
.Select(c => new NewPriceMessage()
|
||||
{
|
||||
Figi = figi,
|
||||
Ticker = c.Ticker,
|
||||
Time = c.Time,
|
||||
Value = c.Value,
|
||||
IsHistoricalData = true
|
||||
})
|
||||
.ToArrayAsync();
|
||||
|
||||
foreach (var mess in data)
|
||||
try
|
||||
{
|
||||
await _dataBus.BroadcastNewPrice(mess);
|
||||
using var context1 = await _dbContextFactory.CreateDbContextAsync();
|
||||
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||
var data = await context1.PriceChanges
|
||||
.Where(c => c.Figi == figi)
|
||||
.OrderBy(c => c.Time)
|
||||
.Select(c => new NewPriceMessage()
|
||||
{
|
||||
Figi = figi,
|
||||
Ticker = c.Ticker,
|
||||
Time = c.Time,
|
||||
Value = c.Value,
|
||||
IsHistoricalData = true
|
||||
})
|
||||
.ToArrayAsync();
|
||||
|
||||
foreach (var mess in data)
|
||||
{
|
||||
await _dataBus.BroadcastNewPrice(mess);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,9 @@ builder.Services.AddDbContextFactory<TraderDbContext>(options =>
|
|||
builder.Services.AddHostedService<BotStarter>();
|
||||
builder.Services.AddHostedService<ExchangeDataReader>();
|
||||
builder.Services.AddHostedService<Trader>();
|
||||
//builder.Services.AddHostedService<ProcessedPricesLogger>();
|
||||
|
||||
//builder.Services.AddHostedService<KalmanPredictor>();
|
||||
|
||||
builder.Services.AddSingleton<IUpdateHandler, BotMessagesHandler>();
|
||||
builder.Services.AddSingleton<BotModeSwitcher>();
|
||||
|
|
|
@ -26,6 +26,7 @@ EndProject
|
|||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "postrgres", "postrgres", "{174A800A-6040-40CF-B331-8603E097CBAC}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
KLHZ.Trader.Infrastructure\postgres\init.sql = KLHZ.Trader.Infrastructure\postgres\init.sql
|
||||
migration1.sql = migration1.sql
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "loki", "loki", "{63D21DAF-FDF0-4F2D-A671-E9E59BB0CA5B}"
|
||||
|
@ -42,6 +43,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deploy", "deploy", "{9DE95D
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KLHZ.Trader.Core.Contracts", "KLHZ.Trader.Core.Contracts\KLHZ.Trader.Core.Contracts.csproj", "{C1ADC79B-ADDB-435D-A453-9D1623D144C4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KLHZ.Trader.Core.Math", "KLHZ.Trader.Core.Math\KLHZ.Trader.Core.Math.csproj", "{4C224F89-2C33-41FB-94CB-87368C86C2C3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -72,6 +75,10 @@ Global
|
|||
{C1ADC79B-ADDB-435D-A453-9D1623D144C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C1ADC79B-ADDB-435D-A453-9D1623D144C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C1ADC79B-ADDB-435D-A453-9D1623D144C4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4C224F89-2C33-41FB-94CB-87368C86C2C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4C224F89-2C33-41FB-94CB-87368C86C2C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4C224F89-2C33-41FB-94CB-87368C86C2C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4C224F89-2C33-41FB-94CB-87368C86C2C3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
create table processed_prices
|
||||
(
|
||||
id bigserial,
|
||||
time timestamp default current_timestamp,
|
||||
figi text not null,
|
||||
processor text not null,
|
||||
ticker text not null,
|
||||
value decimal not null,
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
CREATE INDEX processed_prices_index ON processed_prices USING btree(figi,processor, time);
|
Loading…
Reference in New Issue