удалил калмана

main
vlad zverzhkhovskiy 2025-09-02 12:17:19 +03:00
parent 8d329dc069
commit d634cfac73
39 changed files with 297 additions and 438 deletions

View File

@ -0,0 +1,13 @@
namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums
{
[Flags]
public enum TradingEvent
{
None = 0,
StopBuy = 1,
LongOpen = 2,
ShortClose = 4,
LongClose = 8,
ShortOpen = 16,
}
}

View File

@ -1,6 +1,6 @@
namespace KLHZ.Trader.Core.Declisions.Dtos
namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos
{
internal readonly struct TwoPeriodsProcessingDto
public readonly struct TwoPeriodsResultDto
{
public readonly int Start;
public readonly int Bound;
@ -11,7 +11,7 @@
public readonly TimeSpan PeriodStart;
public readonly TimeSpan PeriodEnd;
public TwoPeriodsProcessingDto(bool success, float diffStart, float diffEnd, int start, int bound, int end,
public TwoPeriodsResultDto(bool success, float diffStart, float diffEnd, int start, int bound, int end,
TimeSpan periodStart, TimeSpan periodEnd)
{
Success = success;

View File

@ -1,4 +1,4 @@
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
namespace KLHZ.Trader.Core.Contracts.Declisions.Interfaces
{

View File

@ -1,9 +1,9 @@
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
namespace KLHZ.Trader.Core.Contracts.Declisions.Interfaces
{
public interface ITradingEventsDetector
{
public ValueTask<TradingEventsDto> Detect(IPriceHistoryCacheUnit unit);
public ValueTask<TradingEvent> Detect(IPriceHistoryCacheUnit unit);
}
}

View File

@ -1,4 +1,4 @@
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces
{
public interface IMessage
{

View File

@ -1,4 +1,4 @@
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces
{
public interface INewCandle
{

View File

@ -1,4 +1,4 @@
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces
{
public interface INewPrice
{

View File

@ -1,4 +1,4 @@
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces
{
public interface IProcessedPrice : INewPrice
{

View File

@ -1,4 +1,4 @@
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos
{

View File

@ -1,4 +1,4 @@
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
namespace KLHZ.Trader.Core.Contracts.Messaging.Dtos
{

View File

@ -1,5 +1,5 @@
using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
using System.Threading.Channels;
namespace KLHZ.Trader.Core.Contracts.Messaging.Interfaces

View File

@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;
//Тесты
[assembly: InternalsVisibleTo("KLHZ.Trader.Core.Tests")]

View File

@ -1,4 +1,4 @@
namespace KLHZ.Trader.Core.Math.Utils
namespace KLHZ.Trader.Core.Math.Common
{
public static class Lines
{
@ -14,7 +14,7 @@
{
var dtime = (float)(time2 - time1).TotalSeconds;
var dval1 = (val1_2 - val1_1);
var dval1 = val1_2 - val1_1;
var k1 = dval1 / dtime;
var b1 = val1_1;

View File

@ -1,4 +1,4 @@
namespace KLHZ.Trader.Core.Declisions.Dtos
namespace KLHZ.Trader.Core.Math.Declisions.Dtos
{
internal readonly struct PeriodPricesInfoDto
{

View File

@ -1,4 +1,4 @@
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
namespace KLHZ.Trader.Core.Math.Declisions.Dtos
{

View File

@ -1,7 +1,7 @@
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
namespace KLHZ.Trader.Core.Math.Declisions.Services
namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
{
public class PriceHistoryCacheUnit : IPriceHistoryCacheUnit
{

View File

@ -1,8 +1,7 @@
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
using System.Reflection;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
namespace KLHZ.Trader.Core.Math.Declisions.Services
namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
{
public class PriceHistoryCacheUnit2 : IPriceHistoryCacheUnit
{
@ -57,14 +56,14 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services
{
if (_pointer < 0)
{
return ValueTask.FromResult((Array.Empty<DateTime>(), Array.Empty <float>()));
return ValueTask.FromResult((Array.Empty<DateTime>(), Array.Empty<float>()));
}
else
{
var prices = new float[_length];
var timestamps = new DateTime[_length];
Array.Copy(Prices,1+ _pointer - _length, prices, 0, prices.Length);
Array.Copy(Timestamps,1+ _pointer - _length, timestamps, 0, timestamps.Length);
Array.Copy(Prices, 1 + _pointer - _length, prices, 0, prices.Length);
Array.Copy(Timestamps, 1 + _pointer - _length, timestamps, 0, timestamps.Length);
return ValueTask.FromResult((timestamps, prices));
}
}
@ -85,7 +84,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services
.Skip(priceChanges.Length - CacheMaxLength)
.ToArray();
foreach ( var pc in selectedPriceChanges)
foreach (var pc in selectedPriceChanges)
{
AddData(pc);
}

View File

@ -0,0 +1,14 @@
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
using KLHZ.Trader.Core.Math.Declisions.Utils;
namespace KLHZ.Trader.Core.Math.Declisions.Services.EventsDetection
{
public class IntervalsTradingEventsDetector : ITradingEventsDetector
{
public ValueTask<TradingEvent> Detect(IPriceHistoryCacheUnit unit)
{
return ValueTask.FromResult(TwoPeriods.Detect(unit));
}
}
}

View File

@ -1,319 +0,0 @@
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
using KLHZ.Trader.Core.Math.Declisions.Dtos;
using MathNet.Filtering.Kalman;
using MathNet.Numerics.LinearAlgebra;
using Microsoft.Extensions.Hosting;
using System.Threading.Channels;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace KLHZ.Trader.Core.Math.Declisions.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 static (DateTime time, float value) CalcTimeWindowAverageValue(DateTime[] timestamps, float[] values, int window, int shift = 0)
{
var sum = values[values.Length - 1 - shift];
var count = 1;
var startTime = timestamps[timestamps.Length - 1 - shift];
for (int i = 2; i < values.Length && startTime - timestamps[values.Length - i - shift] < TimeSpan.FromSeconds(window); i++)
{
sum += values[values.Length - i - shift];
count++;
}
return (startTime, sum / count);
}
private static TradingEventsDto CheckByWindowAverageMean(DateTime[] timestamps, float[] prices, int size, float meanfullStep = 3f)
{
var twav15s = new float[size];
var twav120s = new float[size];
var times = new DateTime[size];
for (int shift = 0; shift < size; shift++)
{
var twav15 = CalcTimeWindowAverageValue(timestamps, prices, 15, shift);
var twav120 = CalcTimeWindowAverageValue(timestamps, prices, 120, shift);
twav15s[size - 1 - shift] = twav15.value;
twav120s[size - 1 - shift] = twav120.value;
times[size - 1 - shift] = twav120.time;
if (shift > 0)
{
var isCrossing = Utils.Lines.IsLinesCrossing(
times[size - 1 - shift],
times[size - 2 - shift],
twav15s[size - 1 - shift],
twav15s[size - 2 - shift],
twav120s[size - 1 - shift],
twav120s[size - 2 - shift]);
if (shift == 1 && !isCrossing) //если нет пересечения скользящих средний с окном 120 и 15 секунд между
//текущей и предыдущей точкой - можно не продолжать выполнение.
{
break;
}
if (shift > 1 && isCrossing)
{
// если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта
if (twav120s[size - 1] <= twav15s[size - 1] && twav120s[size - 2] > twav15s[size - 2] )
{
if (twav15s[size - 1 - shift] - twav15s[size - 1] >= meanfullStep)
{
return new TradingEventsDto(false, true);
}
}
// если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта
if (twav15s[size - 1] <= twav120s[size - 1] && twav15s[size - 2] > twav120s[size - 2])
{
if (twav15s[size - 1 - shift] - twav15s[size - 1] <= - meanfullStep)
{
return new TradingEventsDto(true, false);
}
}
}
}
}
return new TradingEventsDto(false, false);
}
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
{
var data = await _cache.GetData();
var size = 50;
var twav15s = new float[size];
var twav120s = new float[size];
var times = new DateTime[size];
for (int shift = 0; shift < size; shift++)
{
var twav15 = CalcTimeWindowAverageValue(data.timestamps, data.prices, 15, shift);
var twav120 = CalcTimeWindowAverageValue(data.timestamps, data.prices, 120, shift);
twav15s[size - 1-shift] = twav15.value;
twav120s[size - 1 - shift] = twav120.value;
times[size - 1 - shift] = twav120.time;
if (shift>0)
{
var isCrossing = Utils.Lines.IsLinesCrossing(
times[size - 1 - shift],
times[size - 2 - shift],
twav15s[size - 1 - shift],
twav15s[size - 2 - shift],
twav120s[size - 1 - shift],
twav120s[size - 2 - shift]);
if (shift == 1 && !isCrossing) //если нет пересечения скользящих средний с окном 120 и 15 секунд между
//текущей и предыдущей точкой - можно не продолжать выполнение.
{
break;
}
if (shift>1 && isCrossing)
{
// если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга
if (twav120s[size - 1] <= twav15s[size - 1] && twav120s[size - 2] > twav15s[size - 2])
{
}
// если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и открытия шорта
if (twav15s[size - 1] <= twav120s[size - 1] && twav15s[size - 2] > twav120s[size - 2])
{
}
}
}
}
//if (isCrossing && twav120_2 <= twav15_2)// если филтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга
//{
// for (int i=shift;i<111; i++)
// {
// }
//}
//if (isCrossing && twav15_2 <= twav120_2)// если филтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга
//{
//}
//await ProcessCalman(message);
await ProcessMovAv(message, 3);
await ProcessTimeWindow(message, 5);
await ProcessTimeWindow(message, 15);
await ProcessTimeWindow(message, 120);
}
catch (Exception ex)
{
}
}
}
}
}

View File

@ -0,0 +1,76 @@
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
using KLHZ.Trader.Core.Math.Common;
namespace KLHZ.Trader.Core.Math.Declisions.Utils
{
public static class MovingAverage
{
private static (DateTime time, float value) CalcTimeWindowAverageValue(DateTime[] timestamps, float[] values, int window, int shift = 0)
{
var sum = values[values.Length - 1 - shift];
var count = 1;
var startTime = timestamps[timestamps.Length - 1 - shift];
for (int i = 2; i < values.Length && startTime - timestamps[values.Length - i - shift] < TimeSpan.FromSeconds(window); i++)
{
sum += values[values.Length - i - shift];
count++;
}
return (startTime, sum / count);
}
public static TradingEventsDto CheckByWindowAverageMean(DateTime[] timestamps, float[] prices, int size, float meanfullStep = 3f)
{
var twav15s = new float[size];
var twav120s = new float[size];
var times = new DateTime[size];
for (int shift = 0; shift < size; shift++)
{
var twav15 = CalcTimeWindowAverageValue(timestamps, prices, 15, shift);
var twav120 = CalcTimeWindowAverageValue(timestamps, prices, 120, shift);
twav15s[size - 1 - shift] = twav15.value;
twav120s[size - 1 - shift] = twav120.value;
times[size - 1 - shift] = twav120.time;
if (shift > 0)
{
var isCrossing = Lines.IsLinesCrossing(
times[size - 1 - shift],
times[size - 2 - shift],
twav15s[size - 1 - shift],
twav15s[size - 2 - shift],
twav120s[size - 1 - shift],
twav120s[size - 2 - shift]);
if (shift == 1 && !isCrossing) //если нет пересечения скользящих средний с окном 120 и 15 секунд между
//текущей и предыдущей точкой - можно не продолжать выполнение.
{
break;
}
if (shift > 1 && isCrossing)
{
// если фильтрация окном 120 наползает на окно 15 сверху, потенциальное время открытия лонга и закрытия шорта
if (twav120s[size - 1] <= twav15s[size - 1] && twav120s[size - 2] > twav15s[size - 2])
{
if (twav15s[size - 1 - shift] - twav15s[size - 1] >= meanfullStep)
{
return new TradingEventsDto(false, true);
}
}
// если фильтрация окном 15 наползает на окно 120 сверху, потенциальное время закрытия лонга и возможно открытия шорта
if (twav15s[size - 1] <= twav120s[size - 1] && twav15s[size - 2] > twav120s[size - 2])
{
if (twav15s[size - 1 - shift] - twav15s[size - 1] <= -meanfullStep)
{
return new TradingEventsDto(true, false);
}
}
}
}
}
return new TradingEventsDto(false, false);
}
}
}

View File

@ -1,10 +1,15 @@
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
using KLHZ.Trader.Core.Declisions.Dtos;
using KLHZ.Trader.Core.Math.Declisions.Services;
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
using KLHZ.Trader.Core.Math.Declisions.Dtos;
using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
namespace KLHZ.Trader.Core.Declisions.Utils
namespace KLHZ.Trader.Core.Math.Declisions.Utils
{
internal static class HistoryProcessingInstruments
/// <summary>
/// Обработка последних интервалов истории изменения цен.
/// </summary>
public static class TwoPeriods
{
internal static PeriodPricesInfoDto GetPriceDiffForTimeSpan(this IPriceHistoryCacheUnit unit, TimeSpan timeShift, TimeSpan timeSpan, int? pointsShift = null)
{
@ -25,7 +30,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils
int count = 0;
for (int i = times.Length - 1; i > -1; i--)
{
if ((times[i] <= intervalEnd || (pointsShift.HasValue && count < pointsShift.Value)) && intervaEndIndex < 0)
if ((times[i] <= intervalEnd || pointsShift.HasValue && count < pointsShift.Value) && intervaEndIndex < 0)
{
intervaEndIndex = i;
}
@ -88,7 +93,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils
if (k2 == 0 && k1 != 0) return 1000;
return (float)(k1 / k2);
}
internal static float CalcTrendRelationAbs(TwoPeriodsProcessingDto data)
internal static float CalcTrendRelationAbs(TwoPeriodsResultDto data)
{
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);
@ -127,7 +132,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils
var trendRelation = CalcTrendRelationAbs(startDiff, endDiff);
var isEndLocal = endDiff.PeriodDiff == 0;
var res = totalDiff.Success && isStartGrows && (isEndStable || isEndFalls) && (trendRelation >= 2 && !isEndLocal);
var res = totalDiff.Success && isStartGrows && (isEndStable || isEndFalls) && trendRelation >= 2 && !isEndLocal;
if (res)
{
@ -180,9 +185,9 @@ namespace KLHZ.Trader.Core.Declisions.Utils
return res;
}
internal static TwoPeriodsProcessingDto GetTwoPeriodsProcessingData(this (DateTime[] timestamps, float[] prices) data, TimeSpan shift, int shiftPointsStart, int shiftPointsEnd, TimeSpan firstPeriod, float meanfullDiff)
internal static TwoPeriodsResultDto GetTwoPeriodsProcessingData(this (DateTime[] timestamps, float[] prices) data, TimeSpan shift, int shiftPointsStart, int shiftPointsEnd, TimeSpan firstPeriod, float meanfullDiff)
{
var res = new TwoPeriodsProcessingDto(success: false, 0, 0, 0, 0, 0, TimeSpan.Zero, TimeSpan.Zero);
var res = new TwoPeriodsResultDto(success: false, 0, 0, 0, 0, 0, TimeSpan.Zero, TimeSpan.Zero);
var time = data.timestamps;
var prices = data.prices;
int count = -1;
@ -198,7 +203,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils
bound = i;
shift = lastTime - time[i];
}
if (((lastTime - time[i]) >= shift + firstPeriod))
if (lastTime - time[i] >= shift + firstPeriod)
{
start = i;
@ -212,12 +217,12 @@ namespace KLHZ.Trader.Core.Declisions.Utils
{
var diff1 = prices[bound] - prices[start];
var diff2 = prices[end] - prices[bound];
res = new TwoPeriodsProcessingDto(true, diff1, diff2, start, bound, end, time[bound] - time[start], time[end] - time[bound]);
res = new TwoPeriodsResultDto(true, diff1, diff2, start, bound, end, time[bound] - time[start], time[end] - time[bound]);
}
return res;
}
internal static bool CheckLongClose(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff, int pointsStart, int pointsEnd)
public static bool CheckLongClose(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff, int pointsStart, int pointsEnd)
{
var data = unit.GetData().Result;
var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, pointsStart, pointsEnd, firstPeriod, meanfullDiff);
@ -239,7 +244,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils
{
}
return isStartOk && isEndOk && (data.prices[periodStat.End] - data.prices[periodStat.Start] >= meanfullDiff);
return isStartOk && isEndOk && data.prices[periodStat.End] - data.prices[periodStat.Start] >= meanfullDiff;
}
internal static bool CheckUptrendStarting2(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff)
@ -293,7 +298,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils
}
internal static bool CheckLongOpen(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff, int pointsStart, int pointsEnd)
public static bool CheckLongOpen(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff, int pointsStart, int pointsEnd)
{
var data = unit.GetData().Result;
var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, pointsStart, pointsEnd, firstPeriod, meanfullDiff);
@ -315,7 +320,46 @@ namespace KLHZ.Trader.Core.Declisions.Utils
{
}
return isStartOk && isEndOk && (data.prices[periodStat.Start] - data.prices[periodStat.End] >= meanfullDiff);
return isStartOk && isEndOk && data.prices[periodStat.Start] - data.prices[periodStat.End] >= meanfullDiff;
}
public static TradingEvent Detect(IPriceHistoryCacheUnit data)
{
float meanfullDiff;
if (data.Figi == "BBG004730N88")
{
meanfullDiff = 0.05f;
}
else if (data.Figi == "FUTIMOEXF000")
{
meanfullDiff = 1f;
}
else
{
return TradingEvent.None;
}
var res = TradingEvent.None;
//var downtrendStarts = data.CheckDowntrendStarting(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(7), meanfullDiff);
var uptrendStarts = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(7), meanfullDiff, 8, 3);
var uptrendStarts2 = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(3), meanfullDiff, 15, 2);
var downtrendEnds = data.CheckLongOpen(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(10), meanfullDiff, 15, 5);
uptrendStarts |= downtrendEnds;
uptrendStarts |= uptrendStarts2;
if (uptrendStarts)
{
res |= TradingEvent.LongClose;
}
//var downtrendEnds = data.CheckDowntrendEnding(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(15), meanfullDiff);
var uptrendEnds = data.CheckLongClose(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(20), meanfullDiff * 1.5f, 8, 8);
var uptrendEnds2 = data.CheckLongClose(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(30), meanfullDiff, 15, 8);
uptrendEnds |= uptrendEnds2;
if (uptrendEnds)
{
res |= TradingEvent.LongOpen;
}
return res;
}
}
}

View File

@ -7,8 +7,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MathNet.Filtering.Kalman" Version="0.7.0" />
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.8" />
</ItemGroup>

View File

@ -1,5 +1,5 @@
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
using KLHZ.Trader.Core.Math.Declisions.Services;
using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
namespace KLHZ.Trader.Core.Tests
{
@ -208,7 +208,7 @@ namespace KLHZ.Trader.Core.Tests
public void Test9()
{
var cacheUnit = new PriceHistoryCacheUnit2("");
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 });
if (i >= 500)

View File

@ -1,5 +1,5 @@
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
using KLHZ.Trader.Core.Math.Declisions.Services;
using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
namespace KLHZ.Trader.Core.Tests
{

View File

@ -1,6 +1,6 @@
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
using KLHZ.Trader.Core.Declisions.Utils;
using KLHZ.Trader.Core.Math.Declisions.Services;
using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
using KLHZ.Trader.Core.Math.Declisions.Utils;
namespace KLHZ.Trader.Core.Tests
{
@ -45,7 +45,7 @@ namespace KLHZ.Trader.Core.Tests
var periodLength = 4;
var shift = 0;
var result = HistoryProcessingInstruments.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength));
var result = TwoPeriods.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength));
var maxValue = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength)));
var minValue = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift);
@ -79,7 +79,7 @@ namespace KLHZ.Trader.Core.Tests
var periodLength = 4;
var shift = 0;
var result = HistoryProcessingInstruments.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength));
var result = TwoPeriods.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength));
var maxValue = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength)));
var minValue = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift);
@ -114,7 +114,7 @@ namespace KLHZ.Trader.Core.Tests
var periodLength = 4;
var shift = 1;
var result = HistoryProcessingInstruments.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength));
var result = TwoPeriods.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength));
var maxValue = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength)));
var minValue = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift);
@ -149,7 +149,7 @@ namespace KLHZ.Trader.Core.Tests
var periodLength = 4;
var shift = 0;
var result = HistoryProcessingInstruments.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength));
var result = TwoPeriods.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength));
var maxValue = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength)));
var minValue = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift);
@ -184,7 +184,7 @@ namespace KLHZ.Trader.Core.Tests
var periodLength = 4;
var shift = 3;
var result = HistoryProcessingInstruments.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength));
var result = TwoPeriods.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength));
var maxValue = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength)));
var minValue = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift);
@ -219,7 +219,7 @@ namespace KLHZ.Trader.Core.Tests
var periodLength = 4;
var shift = 3;
var result = HistoryProcessingInstruments.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength));
var result = TwoPeriods.GetPriceDiffForTimeSpan(unit, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength));
var maxValue1 = startValue + (step > 0 ? (step * count) - step * shift : (step * count) - (step * (shift + periodLength)));
var minValue1 = startValue + (step > 0 ? (step * count) - (step * (shift + periodLength)) : (step * count) - step * shift);
@ -260,7 +260,7 @@ namespace KLHZ.Trader.Core.Tests
}
var result2 = HistoryProcessingInstruments.GetPriceDiffForTimeSpan(unit2, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength));
var result2 = TwoPeriods.GetPriceDiffForTimeSpan(unit2, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength));
var maxValue2 = 100;
var minValue2 = -100;

View File

@ -1,3 +1,5 @@
using KLHZ.Trader.Core.Math.Common;
namespace KLHZ.Trader.Core.Tests
{
public class LinesProcessingTest
@ -14,7 +16,7 @@ namespace KLHZ.Trader.Core.Tests
var val2_1 = -0.5f;
var val2_2 = 0.5f;
Assert.IsTrue(KLHZ.Trader.Core.Math.Utils.Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2));
Assert.IsTrue(Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2));
}
[Test]
@ -29,7 +31,7 @@ namespace KLHZ.Trader.Core.Tests
var val2_1 = 0.5f;
var val2_2 = -0.5f;
Assert.IsFalse(KLHZ.Trader.Core.Math.Utils.Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2));
Assert.IsFalse(Lines.IsLinesCrossing(time1, time2, val1_1, val1_2, val2_1, val2_2));
}
}
}

View File

@ -1,5 +1,5 @@
using KLHZ.Trader.Core.Contracts.Messaging.Dtos;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
using System.Collections.Concurrent;
using System.Threading.Channels;

View File

@ -1,4 +1,4 @@
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
using KLHZ.Trader.Core.DataLayer;
using Microsoft.EntityFrameworkCore;
@ -6,7 +6,7 @@ using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Threading.Channels;
namespace KLHZ.Trader.Core.Declisions.Services
namespace KLHZ.Trader.Core.Common.Messaging.Services
{
public class ProcessedPricesLogger : IHostedService
{
@ -44,7 +44,7 @@ namespace KLHZ.Trader.Core.Declisions.Services
Value = message.Value,
});
if (buffer.Count > 10000 || (DateTime.UtcNow - lastWrite) > TimeSpan.FromSeconds(5) || _channel.Reader.Count == 0)
if (buffer.Count > 10000 || DateTime.UtcNow - lastWrite > TimeSpan.FromSeconds(5) || _channel.Reader.Count == 0)
{
lastWrite = DateTime.UtcNow;
using var context = await _dbContextFactory.CreateDbContextAsync();

View File

@ -1,4 +1,4 @@
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
using System.ComponentModel.DataAnnotations.Schema;
namespace KLHZ.Trader.Core.DataLayer.Entities.Prices

View File

@ -1,4 +1,4 @@
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
using System.ComponentModel.DataAnnotations.Schema;
namespace KLHZ.Trader.Core.DataLayer.Entities.Prices

View File

@ -1,4 +1,4 @@
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
using System.ComponentModel.DataAnnotations.Schema;
namespace KLHZ.Trader.Core.DataLayer.Entities.Prices

View File

@ -1,41 +0,0 @@
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
using KLHZ.Trader.Core.Declisions.Utils;
namespace KLHZ.Trader.Core.Declisions.Services
{
public class TradingEventsDetector : ITradingEventsDetector
{
public async ValueTask<TradingEventsDto> Detect(IPriceHistoryCacheUnit data)
{
await Task.Delay(0);
float meanfullDiff;
if (data.Figi == "BBG004730N88")
{
meanfullDiff = 0.05f;
}
else if (data.Figi == "FUTIMOEXF000")
{
meanfullDiff = 1f;
}
else
{
return TradingEventsDto.Empty;
}
//var downtrendStarts = data.CheckDowntrendStarting(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(7), meanfullDiff);
var uptrendStarts = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(7), meanfullDiff, 8, 3);
var uptrendStarts2 = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(3), meanfullDiff, 15, 2);
var downtrendEnds = data.CheckLongOpen(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(10), meanfullDiff, 15, 5);
uptrendStarts |= downtrendEnds;
uptrendStarts |= uptrendStarts2;
//var downtrendEnds = data.CheckDowntrendEnding(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(15), meanfullDiff);
var uptrendEnds = data.CheckLongClose(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(20), meanfullDiff * 1.5f, 8, 8);
var uptrendEnds2 = data.CheckLongClose(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(30), meanfullDiff, 15, 8);
uptrendEnds |= uptrendEnds2;
return new TradingEventsDto(uptrendEnds, uptrendStarts);
}
}
}

View File

@ -8,7 +8,8 @@
public decimal AccountCashPart { get; set; }
public decimal AccountCashPartFutures { get; set; }
public decimal DefaultBuyPartOfAccount { get; set; }
public string[] AllowedInstrumentsFigis { get; set; } = [];
public string[] DataRecievingInstrumentsFigis { get; set; } = [];
public string[] TradingInstrumentsFigis { get; set; } = [];
public string[] ManagingAccountNamePatterns { get; set; } = [];
}
}

View File

@ -33,7 +33,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
_eventBus = eventBus;
_dbContextFactory = dbContextFactory;
_investApiClient = investApiClient;
_instrumentsFigis = options.Value.AllowedInstrumentsFigis.ToArray();
_instrumentsFigis = options.Value.DataRecievingInstrumentsFigis.ToArray();
_logger = logger;
_managedAccountNamePatterns = options.Value.ManagingAccountNamePatterns.ToArray();
}

View File

@ -1,25 +1,25 @@
using KLHZ.Trader.Core.Common;
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Enums;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
using KLHZ.Trader.Core.DataLayer;
using KLHZ.Trader.Core.DataLayer.Entities.Declisions;
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.Services;
using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Collections.Concurrent;
using System.Threading.Channels;
using Tinkoff.InvestApi;
using AssetType = KLHZ.Trader.Core.Exchange.Models.AssetType;
namespace KLHZ.Trader.Core.Declisions.Services
namespace KLHZ.Trader.Core.Exchange.Services
{
public class Trader : IHostedService
{
@ -31,6 +31,7 @@ namespace KLHZ.Trader.Core.Declisions.Services
private readonly ConcurrentDictionary<string, ManagedAccount> Accounts = new();
private readonly ConcurrentDictionary<string, IPriceHistoryCacheUnit> _historyCash = new();
private readonly ITradingEventsDetector _tradingEventsDetector;
private readonly ILogger<Trader> _logger;
private readonly decimal _futureComission;
@ -107,7 +108,7 @@ namespace KLHZ.Trader.Core.Declisions.Services
try
{
if (result.LongOpen)
if ((result & TradingEvent.LongOpen) == TradingEvent.LongOpen)
{
using var context = await _dbContextFactory.CreateDbContextAsync();
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
@ -122,7 +123,7 @@ namespace KLHZ.Trader.Core.Declisions.Services
});
await context.SaveChangesAsync();
}
if (result.LongClose)
if ((result & TradingEvent.LongClose) == TradingEvent.LongClose)
{
using var context = await _dbContextFactory.CreateDbContextAsync();
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

View File

@ -1,4 +1,4 @@
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Intarfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;

View File

@ -0,0 +1,66 @@
using Grpc.Core;
using Microsoft.AspNetCore.Mvc;
using Tinkoff.InvestApi;
using Tinkoff.InvestApi.V1;
namespace KLHZ.Trader.Service.Controllers
{
[ApiController]
[Route("[controller]/[action]")]
public class ExchangeDataController : ControllerBase
{
private readonly InvestApiClient _investApiClient;
public ExchangeDataController(InvestApiClient investApiClient)
{
_investApiClient = investApiClient;
}
[HttpGet]
public async Task<string?> GetFigi([FromQuery] string ticker, [FromQuery] string? classCode = "TQBR")
{
var req = new InstrumentRequest()
{
ClassCode = classCode,
IdType = InstrumentIdType.Ticker,
Id = ticker,
};
try
{
var res = await _investApiClient.Instruments.GetInstrumentByAsync(req);
return res.Instrument.Figi;
}
catch (RpcException ex)
{
if (ex.StatusCode == Grpc.Core.StatusCode.NotFound && classCode == "TQBR")
{
return await GetFigi(ticker, "SPBFUT");
}
}
return null;
}
[HttpGet]
public async Task<string?> GetTicker([FromQuery] string figi)
{
var req = new InstrumentRequest()
{
ClassCode = "figi",
IdType = InstrumentIdType.Figi,
Id = figi,
};
try
{
var res = await _investApiClient.Instruments.GetInstrumentByAsync(req);
return res.Instrument.Ticker;
}
catch (Exception ex)
{
}
return null;
}
}
}

View File

@ -3,9 +3,9 @@ using KLHZ.Trader.Core.Common.Messaging.Services;
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
using KLHZ.Trader.Core.Contracts.Messaging.Interfaces;
using KLHZ.Trader.Core.DataLayer;
using KLHZ.Trader.Core.Declisions.Services;
using KLHZ.Trader.Core.Exchange;
using KLHZ.Trader.Core.Exchange.Services;
using KLHZ.Trader.Core.Math.Declisions.Services.EventsDetection;
using KLHZ.Trader.Core.TG;
using KLHZ.Trader.Core.TG.Services;
using KLHZ.Trader.Service.Infrastructure;
@ -53,7 +53,7 @@ builder.Services.AddHostedService<Trader>();
builder.Services.AddSingleton<IUpdateHandler, BotMessagesHandler>();
builder.Services.AddSingleton<BotModeSwitcher>();
builder.Services.AddSingleton<IDataBus, DataBus>();
builder.Services.AddSingleton<ITradingEventsDetector, TradingEventsDetector>();
builder.Services.AddSingleton<ITradingEventsDetector, IntervalsTradingEventsDetector>();
for (int i = 0; i < 10; i++)
{

View File

@ -11,7 +11,8 @@
"ExchangeDataRecievingEnabled": true,
"Token": "",
"ManagingAccountNamePatterns": [ "автотрейд 1" ],
"AllowedInstrumentsFigis": [ "BBG004730N88", "FUTIMOEXF000" ],
"DataRecievingInstrumentsFigis": [ "BBG004730N88", "FUTIMOEXF000", "FUTGMKN09250", "FUTBR1025000" ],
"TradingInstrumentsFigis": [ "FUTIMOEXF000" ],
"FutureComission": 0.0025,
"ShareComission": 0.0004,
"AccountCashPart": 0.05,