обновление алгоритма принятия решений
test / deploy_trader_prod (push) Successful in 2m0s
Details
test / deploy_trader_prod (push) Successful in 2m0s
Details
parent
21833412d8
commit
76bde7c853
|
@ -11,5 +11,6 @@
|
||||||
ShortOpen = 16,
|
ShortOpen = 16,
|
||||||
UptrendEnd = 32,
|
UptrendEnd = 32,
|
||||||
UptrendStart = 64,
|
UptrendStart = 64,
|
||||||
|
HorisontTrend = 128,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace KLHZ.Trader.Core.Contracts.Declisions.Interfaces
|
||||||
public string Figi { get; }
|
public string Figi { get; }
|
||||||
public int Length { get; }
|
public int Length { get; }
|
||||||
public ValueTask AddData(INewPrice priceChange);
|
public ValueTask AddData(INewPrice priceChange);
|
||||||
public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData();
|
public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(int? length = null);
|
||||||
public ValueTask AddOrderbook(IOrderbook orderbook);
|
public ValueTask AddOrderbook(IOrderbook orderbook);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
namespace KLHZ.Trader.Core.Math.Declisions.Dtos
|
|
||||||
{
|
|
||||||
internal readonly struct PeriodPricesInfoDto
|
|
||||||
{
|
|
||||||
public readonly int Start;
|
|
||||||
public readonly int End;
|
|
||||||
public readonly decimal LastPrice;
|
|
||||||
public readonly decimal FirstPrice;
|
|
||||||
public readonly decimal PeriodDiff;
|
|
||||||
public readonly decimal PeriodMax;
|
|
||||||
public readonly decimal PeriodMin;
|
|
||||||
public readonly bool Success;
|
|
||||||
public readonly TimeSpan Period;
|
|
||||||
|
|
||||||
public PeriodPricesInfoDto(bool success, decimal firstPrice, decimal lastPrice, decimal periodDiff, decimal periodMin, decimal periodMax, TimeSpan period, int start, int end)
|
|
||||||
{
|
|
||||||
Success = success;
|
|
||||||
LastPrice = lastPrice;
|
|
||||||
FirstPrice = firstPrice;
|
|
||||||
PeriodDiff = periodDiff;
|
|
||||||
PeriodMax = periodMax;
|
|
||||||
PeriodMin = periodMin;
|
|
||||||
Period = period;
|
|
||||||
Start = start;
|
|
||||||
End = end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace KLHZ.Trader.Core.Contracts.Declisions.Dtos
|
namespace KLHZ.Trader.Core.Math.Declisions.Dtos
|
||||||
{
|
{
|
||||||
public readonly struct TwoPeriodsResultDto
|
internal readonly struct TwoLocalTrendsResultDto
|
||||||
{
|
{
|
||||||
public readonly int Start;
|
public readonly int Start;
|
||||||
public readonly int Bound;
|
public readonly int Bound;
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
public readonly TimeSpan PeriodStart;
|
public readonly TimeSpan PeriodStart;
|
||||||
public readonly TimeSpan PeriodEnd;
|
public readonly TimeSpan PeriodEnd;
|
||||||
|
|
||||||
public TwoPeriodsResultDto(bool success, decimal diffStart, decimal diffEnd, int start, int bound, int end,
|
public TwoLocalTrendsResultDto(bool success, decimal diffStart, decimal diffEnd, int start, int bound, int end,
|
||||||
TimeSpan periodStart, TimeSpan periodEnd)
|
TimeSpan periodStart, TimeSpan periodEnd)
|
||||||
{
|
{
|
||||||
Success = success;
|
Success = success;
|
|
@ -47,7 +47,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
|
||||||
return ValueTask.CompletedTask;
|
return ValueTask.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData()
|
public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(int? length = null)
|
||||||
{
|
{
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,7 +76,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
|
||||||
return ValueTask.CompletedTask;
|
return ValueTask.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData()
|
public ValueTask<(DateTime[] timestamps, decimal[] prices)> GetData(int? length = null)
|
||||||
{
|
{
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
|
@ -86,10 +86,12 @@ namespace KLHZ.Trader.Core.Math.Declisions.Services.Cache
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var prices = new decimal[_length];
|
var dataLength = length.HasValue ? System.Math.Min(length.Value, _length) : _length;
|
||||||
var timestamps = new DateTime[_length];
|
var prices = new decimal[dataLength];
|
||||||
Array.Copy(Prices, 1 + _pointer - _length, prices, 0, prices.Length);
|
var timestamps = new DateTime[dataLength];
|
||||||
Array.Copy(Timestamps, 1 + _pointer - _length, timestamps, 0, timestamps.Length);
|
var index = 1 + _pointer - dataLength;
|
||||||
|
Array.Copy(Prices, index, prices, 0, prices.Length);
|
||||||
|
Array.Copy(Timestamps, index, timestamps, 0, timestamps.Length);
|
||||||
return ValueTask.FromResult((timestamps, prices));
|
return ValueTask.FromResult((timestamps, prices));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos;
|
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Dtos.Enums;
|
|
||||||
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
|
using KLHZ.Trader.Core.Contracts.Declisions.Interfaces;
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Dtos;
|
using KLHZ.Trader.Core.Math.Declisions.Dtos;
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
|
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
{
|
{
|
||||||
|
@ -11,199 +9,48 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class LocalTrends
|
public static class LocalTrends
|
||||||
{
|
{
|
||||||
internal static PeriodPricesInfoDto GetPriceDiffForTimeSpan(this IPriceHistoryCacheUnit unit, TimeSpan timeShift, TimeSpan timeSpan, int? pointsShift = null)
|
public static TradingEvent CheckByLocalTrends(DateTime[] times, decimal[] prices, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int boundIndex)
|
||||||
{
|
{
|
||||||
var res = new PeriodPricesInfoDto(false, 0, 0, 0, 0, 0, timeSpan, 0, 0);
|
var res = TradingEvent.None;
|
||||||
var data = unit.GetData().Result;
|
res |= CheckUptrendStart(times, prices, firstPeriod, secondPeriod, meanfullDiff, boundIndex);
|
||||||
var times = data.timestamps;
|
res |= CheckUptrendEnd(times, prices, firstPeriod, secondPeriod, meanfullDiff, boundIndex);
|
||||||
var prices = data.prices;
|
|
||||||
if (times.Length < 2) return res;
|
|
||||||
var lastPriceTime = times[times.Length - 1];
|
|
||||||
var intervalEnd = lastPriceTime - timeShift;
|
|
||||||
var intervalStart = intervalEnd - timeSpan;
|
|
||||||
var max = decimal.MinValue;
|
|
||||||
var min = decimal.MaxValue;
|
|
||||||
|
|
||||||
var intervaEndIndex = -1;
|
|
||||||
var intervaStartIndex = -1;
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
for (int i = times.Length - 1; i > -1; i--)
|
|
||||||
{
|
|
||||||
if ((times[i] <= intervalEnd || pointsShift.HasValue && count < pointsShift.Value) && intervaEndIndex < 0)
|
|
||||||
{
|
|
||||||
intervaEndIndex = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prices[i] > max && intervaEndIndex >= 0)
|
|
||||||
{
|
|
||||||
max = prices[i];
|
|
||||||
}
|
|
||||||
if (prices[i] < min && intervaEndIndex >= 0)
|
|
||||||
{
|
|
||||||
min = prices[i];
|
|
||||||
}
|
|
||||||
if (times[i] <= intervalStart && intervaStartIndex < 0)
|
|
||||||
{
|
|
||||||
intervaStartIndex = i;
|
|
||||||
if (intervaStartIndex != intervaEndIndex && intervaEndIndex >= 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intervaStartIndex >= 0 && intervaEndIndex >= 0)
|
|
||||||
{
|
|
||||||
res = new PeriodPricesInfoDto(
|
|
||||||
true,
|
|
||||||
prices[intervaStartIndex],
|
|
||||||
prices[intervaEndIndex],
|
|
||||||
prices[intervaEndIndex] - prices[intervaStartIndex],
|
|
||||||
min,
|
|
||||||
max,
|
|
||||||
timeSpan,
|
|
||||||
intervaStartIndex,
|
|
||||||
intervaEndIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool CheckStable(this PeriodPricesInfoDto data, decimal meanfullDiff)
|
internal static TradingEvent CheckUptrendStart(DateTime[] times, decimal[] prices, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int boundIndex)
|
||||||
{
|
{
|
||||||
meanfullDiff = System.Math.Abs(meanfullDiff);
|
var periodStat = GetTwoPeriodsProcessingData(times, prices, firstPeriod, secondPeriod, boundIndex, meanfullDiff);
|
||||||
return data.Success && System.Math.Abs(data.PeriodDiff) < 1.5m * meanfullDiff && System.Math.Abs(data.PeriodMax - data.PeriodMin) < 2 * meanfullDiff;
|
var isStartOk = periodStat.Success && periodStat.DiffStart < -meanfullDiff;
|
||||||
|
var isEndOk = periodStat.Success && periodStat.DiffEnd >= meanfullDiff;
|
||||||
|
return isStartOk && isEndOk && prices[periodStat.Start] - prices[periodStat.End] >= meanfullDiff ? TradingEvent.UptrendStart : TradingEvent.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool CheckGrowing(this PeriodPricesInfoDto data, decimal meanfullDiff)
|
internal static TradingEvent CheckUptrendEnd(DateTime[] times, decimal[] prices, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int boundIndex)
|
||||||
{
|
{
|
||||||
return meanfullDiff > 0 && data.Success && data.PeriodDiff > meanfullDiff && System.Math.Abs(data.PeriodMax - data.PeriodMin) < 3 * System.Math.Abs(data.PeriodDiff);
|
var periodStat = GetTwoPeriodsProcessingData(times, prices, firstPeriod, secondPeriod, boundIndex, meanfullDiff);
|
||||||
|
var isStartOk = periodStat.Success && periodStat.DiffStart > 0 && periodStat.DiffStart > 1.5m * meanfullDiff;
|
||||||
|
var isEndOk = periodStat.Success && periodStat.DiffEnd < meanfullDiff;
|
||||||
|
return isStartOk && isEndOk && prices[periodStat.End] - prices[periodStat.Start] >= meanfullDiff ? TradingEvent.UptrendEnd : TradingEvent.None; ;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool CheckFalling(this PeriodPricesInfoDto data, decimal meanfullDiff)
|
internal static TwoLocalTrendsResultDto GetTwoPeriodsProcessingData(DateTime[] times, decimal[] prices, TimeSpan firstPeriod, TimeSpan lastPeriod, int boundIndex, decimal meanfullDiff)
|
||||||
{
|
{
|
||||||
meanfullDiff = -meanfullDiff;
|
var res = new TwoLocalTrendsResultDto(success: false, 0, 0, 0, 0, 0, TimeSpan.Zero, TimeSpan.Zero);
|
||||||
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 = System.Math.Abs(first.PeriodDiff) / System.Math.Abs((decimal)first.Period.TotalSeconds);
|
|
||||||
var k2 = System.Math.Abs(second.PeriodDiff) / System.Math.Abs((decimal)second.Period.TotalSeconds);
|
|
||||||
if (k2 == 0 && k1 != 0) return 1000;
|
|
||||||
return (float)(k1 / k2);
|
|
||||||
}
|
|
||||||
internal static float CalcTrendRelationAbs(TwoPeriodsResultDto data)
|
|
||||||
{
|
|
||||||
var k1 = System.Math.Abs(data.DiffStart) / System.Math.Abs((decimal)data.PeriodStart.TotalSeconds);
|
|
||||||
var k2 = System.Math.Abs(data.DiffEnd) / System.Math.Abs((decimal)data.PeriodEnd.TotalSeconds);
|
|
||||||
if (k2 == 0 && k1 != 0) return 1000;
|
|
||||||
return (float)(k1 / k2);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool CheckDowntrendEnding(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff)
|
|
||||||
{
|
|
||||||
var totalDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, firstPeriod + secondPeriod);
|
|
||||||
var startDiff = unit.GetPriceDiffForTimeSpan(secondPeriod, firstPeriod);
|
|
||||||
var endDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, secondPeriod);
|
|
||||||
var isEndStable = endDiff.CheckStable(meanfullDiff);
|
|
||||||
var isEndGrown = endDiff.CheckGrowing(meanfullDiff);
|
|
||||||
var isStartFalls = startDiff.CheckFalling(meanfullDiff);
|
|
||||||
var isTotalFalls = totalDiff.CheckFalling(meanfullDiff);
|
|
||||||
|
|
||||||
var trendRelation = CalcTrendRelationAbs(startDiff, endDiff);
|
|
||||||
var res = totalDiff.Success && isStartFalls && (isEndStable || isEndGrown) && trendRelation >= 2;
|
|
||||||
|
|
||||||
if (startDiff.Success)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool CheckUptrendEnding(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff)
|
|
||||||
{
|
|
||||||
var totalDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, firstPeriod + secondPeriod);
|
|
||||||
var startDiff = unit.GetPriceDiffForTimeSpan(secondPeriod, firstPeriod);
|
|
||||||
var endDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, secondPeriod);
|
|
||||||
var isEndStable = endDiff.CheckStable(meanfullDiff);
|
|
||||||
var isEndFalls = endDiff.CheckFalling(meanfullDiff);
|
|
||||||
var isStartGrows = startDiff.CheckGrowing(meanfullDiff);
|
|
||||||
|
|
||||||
var trendRelation = CalcTrendRelationAbs(startDiff, endDiff);
|
|
||||||
var isEndLocal = endDiff.PeriodDiff == 0;
|
|
||||||
var res = totalDiff.Success && isStartGrows && (isEndStable || isEndFalls) && trendRelation >= 2 && !isEndLocal;
|
|
||||||
|
|
||||||
if (res)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool CheckDowntrendStarting(this PriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff)
|
|
||||||
{
|
|
||||||
var totalDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, firstPeriod + secondPeriod);
|
|
||||||
var startDiff = unit.GetPriceDiffForTimeSpan(secondPeriod, firstPeriod);
|
|
||||||
var endDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, secondPeriod);
|
|
||||||
|
|
||||||
|
|
||||||
var isEndFalls = endDiff.CheckFalling(meanfullDiff);
|
|
||||||
var isStartStable = startDiff.CheckStable(meanfullDiff);
|
|
||||||
var isStartGrows = startDiff.CheckGrowing(meanfullDiff);
|
|
||||||
|
|
||||||
var trendRelation = CalcTrendRelationAbs(endDiff, startDiff);
|
|
||||||
return totalDiff.Success && (isStartStable || isStartGrows) && isEndFalls && trendRelation >= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool CheckUptrendStarting(this PriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff)
|
|
||||||
{
|
|
||||||
var totalDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, firstPeriod + secondPeriod);
|
|
||||||
var startDiff = unit.GetPriceDiffForTimeSpan(secondPeriod, firstPeriod);
|
|
||||||
var endDiff = unit.GetPriceDiffForTimeSpan(TimeSpan.Zero, secondPeriod);
|
|
||||||
|
|
||||||
|
|
||||||
var isEndGrows = endDiff.CheckGrowing(meanfullDiff);
|
|
||||||
var isStartStable = startDiff.CheckStable(meanfullDiff);
|
|
||||||
var isStartFalls = startDiff.CheckStable(meanfullDiff);
|
|
||||||
|
|
||||||
var trendRelation = CalcTrendRelationAbs(endDiff, startDiff);
|
|
||||||
var res = totalDiff.Success && (isStartStable || isStartFalls) && isEndGrows && endDiff.PeriodDiff > meanfullDiff;
|
|
||||||
|
|
||||||
if (isStartStable)
|
|
||||||
{
|
|
||||||
res &= trendRelation >= 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
if (res)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static TwoPeriodsResultDto GetTwoPeriodsProcessingData(this (DateTime[] timestamps, decimal[] prices) data, TimeSpan shift, int shiftPointsStart, int shiftPointsEnd, TimeSpan firstPeriod, decimal meanfullDiff)
|
|
||||||
{
|
|
||||||
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;
|
int count = -1;
|
||||||
var lastTime = time[time.Length - 1];
|
var lastTime = times[times.Length - 1];
|
||||||
var bound = -1;
|
var bound = -1;
|
||||||
var start = -1;
|
var start = -1;
|
||||||
var end = time.Length - 1;
|
var end = times.Length - 1;
|
||||||
|
|
||||||
for (int i = time.Length - 1; i > -1; i--)
|
for (int i = times.Length - 1; i > -1; i--)
|
||||||
{
|
{
|
||||||
if (count > 0 && bound < 0 && (count == shiftPointsEnd || lastTime - time[i] >= shift))
|
if (count > 0 && bound < 0 && (count == boundIndex || lastTime - times[i] >= lastPeriod))
|
||||||
{
|
{
|
||||||
bound = i;
|
bound = i;
|
||||||
shift = lastTime - time[i];
|
lastPeriod = lastTime - times[i];
|
||||||
}
|
}
|
||||||
if (lastTime - time[i] >= shift + firstPeriod)
|
if (lastTime - times[i] >= lastPeriod + firstPeriod)
|
||||||
{
|
{
|
||||||
start = i;
|
start = i;
|
||||||
|
|
||||||
|
@ -217,105 +64,37 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
{
|
{
|
||||||
var diff1 = prices[bound] - prices[start];
|
var diff1 = prices[bound] - prices[start];
|
||||||
var diff2 = prices[end] - prices[bound];
|
var diff2 = prices[end] - prices[bound];
|
||||||
res = new TwoPeriodsResultDto(true, diff1, diff2, start, bound, end, time[bound] - time[start], time[end] - time[bound]);
|
res = new TwoLocalTrendsResultDto(true, diff1, diff2, start, bound, end, times[bound] - times[start], times[end] - times[bound]);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
internal static bool CheckLongOpen(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int boundIndex)
|
||||||
public static bool CheckLongClose(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int pointsStart, int pointsEnd)
|
|
||||||
{
|
{
|
||||||
var data = unit.GetData().Result;
|
var data = unit.GetData().Result;
|
||||||
var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, pointsStart, pointsEnd, firstPeriod, meanfullDiff);
|
var periodStat = GetTwoPeriodsProcessingData(data.timestamps, data.prices, firstPeriod, secondPeriod, boundIndex, meanfullDiff);
|
||||||
var trendRelation = CalcTrendRelationAbs(periodStat);
|
|
||||||
var isStartOk = periodStat.Success && periodStat.DiffStart > 0 && periodStat.DiffStart > 1.5m * meanfullDiff;
|
|
||||||
var isEndOk = periodStat.Success && periodStat.DiffEnd < meanfullDiff;
|
|
||||||
|
|
||||||
if (isEndOk)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isStartOk)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEndOk && isStartOk)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
return isStartOk && isEndOk && data.prices[periodStat.End] - data.prices[periodStat.Start] >= meanfullDiff;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool CheckUptrendStarting2(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff)
|
|
||||||
{
|
|
||||||
var data = unit.GetData().Result;
|
|
||||||
var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, 15, 2, firstPeriod, meanfullDiff);
|
|
||||||
var trendRelation = CalcTrendRelationAbs(periodStat);
|
|
||||||
var isStartOk = periodStat.Success && periodStat.DiffStart < -meanfullDiff;
|
|
||||||
var isEndOk = periodStat.Success && periodStat.DiffEnd >= meanfullDiff;
|
|
||||||
|
|
||||||
if (isEndOk)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isStartOk)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEndOk && isStartOk)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
return isStartOk && isEndOk;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool _CheckUptrendStarting2(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff)
|
|
||||||
{
|
|
||||||
var data = unit.GetData().Result;
|
|
||||||
var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, 15, 1, firstPeriod, meanfullDiff);
|
|
||||||
var trendRelation = CalcTrendRelationAbs(periodStat);
|
|
||||||
var isStartOk = periodStat.Success && periodStat.DiffStart < -meanfullDiff;
|
|
||||||
var isEndOk = periodStat.Success && periodStat.DiffEnd >= meanfullDiff;
|
|
||||||
|
|
||||||
if (isEndOk)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isStartOk)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEndOk && isStartOk)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
return isStartOk && isEndOk;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static bool CheckLongOpen(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int pointsStart, int pointsEnd)
|
|
||||||
{
|
|
||||||
var data = unit.GetData().Result;
|
|
||||||
var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, pointsStart, pointsEnd, firstPeriod, meanfullDiff);
|
|
||||||
var isStartOk = periodStat.Success && periodStat.DiffStart < -meanfullDiff;
|
var isStartOk = periodStat.Success && periodStat.DiffStart < -meanfullDiff;
|
||||||
var isEndOk = periodStat.Success && periodStat.DiffEnd >= meanfullDiff;
|
var isEndOk = periodStat.Success && periodStat.DiffEnd >= meanfullDiff;
|
||||||
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)
|
internal static bool CheckLongClose(this IPriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, decimal meanfullDiff, int boundIndex)
|
||||||
|
{
|
||||||
|
var data = unit.GetData().Result;
|
||||||
|
var periodStat = GetTwoPeriodsProcessingData(data.timestamps, data.prices, firstPeriod, secondPeriod, boundIndex, meanfullDiff);
|
||||||
|
var isStartOk = periodStat.Success && periodStat.DiffStart > 0 && periodStat.DiffStart > 1.5m * meanfullDiff;
|
||||||
|
var isEndOk = periodStat.Success && periodStat.DiffEnd < meanfullDiff;
|
||||||
|
return isStartOk && isEndOk && data.prices[periodStat.End] - data.prices[periodStat.Start] >= meanfullDiff;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static TradingEvent Detect(IPriceHistoryCacheUnit data)
|
||||||
{
|
{
|
||||||
decimal meanfullDiff = 1m;
|
decimal meanfullDiff = 1m;
|
||||||
|
|
||||||
var res = TradingEvent.None;
|
var res = TradingEvent.None;
|
||||||
//var downtrendStarts = data.CheckDowntrendStarting(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(7), meanfullDiff);
|
//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 uptrendStarts = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(7), meanfullDiff, 3);
|
||||||
var uptrendStarts2 = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(3), meanfullDiff, 15, 2);
|
var uptrendStarts2 = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(3), meanfullDiff, 2);
|
||||||
var downtrendEnds = data.CheckLongOpen(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(10), meanfullDiff, 15, 5);
|
var downtrendEnds = data.CheckLongOpen(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(10), meanfullDiff, 5);
|
||||||
uptrendStarts |= downtrendEnds;
|
uptrendStarts |= downtrendEnds;
|
||||||
uptrendStarts |= uptrendStarts2;
|
uptrendStarts |= uptrendStarts2;
|
||||||
if (uptrendStarts)
|
if (uptrendStarts)
|
||||||
|
@ -324,8 +103,8 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
||||||
}
|
}
|
||||||
//var downtrendEnds = data.CheckDowntrendEnding(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(15), meanfullDiff);
|
//var downtrendEnds = data.CheckDowntrendEnding(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(15), meanfullDiff);
|
||||||
|
|
||||||
var uptrendEnds = data.CheckLongClose(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(20), meanfullDiff * 1.5m, 8, 8);
|
var uptrendEnds = data.CheckLongClose(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(20), meanfullDiff * 1.5m, 8);
|
||||||
var uptrendEnds2 = data.CheckLongClose(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(30), meanfullDiff, 15, 8);
|
var uptrendEnds2 = data.CheckLongClose(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(30), meanfullDiff, 8);
|
||||||
uptrendEnds |= uptrendEnds2;
|
uptrendEnds |= uptrendEnds2;
|
||||||
if (uptrendEnds)
|
if (uptrendEnds)
|
||||||
{
|
{
|
||||||
|
|
|
@ -219,5 +219,28 @@ namespace KLHZ.Trader.Core.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test10()
|
||||||
|
{
|
||||||
|
var length = 77;
|
||||||
|
var shift = 334;
|
||||||
|
var count = PriceHistoryCacheUnit.CacheMaxLength + shift;
|
||||||
|
var figi = "figi";
|
||||||
|
var hist = GetHistory(count, figi);
|
||||||
|
var cacheUnit = new PriceHistoryCacheUnit2(figi, hist);
|
||||||
|
var data = cacheUnit.GetData(length).Result;
|
||||||
|
|
||||||
|
Assert.That(data.prices.Length == length);
|
||||||
|
Assert.That(data.timestamps.Length == length);
|
||||||
|
|
||||||
|
Assert.That(data.prices.Last() == hist.Last().Value);
|
||||||
|
Assert.That(data.timestamps.Last() == hist.Last().Time);
|
||||||
|
for (var i = 1; i <= length; i++)
|
||||||
|
{
|
||||||
|
Assert.That(hist[hist.Length - i].Value, Is.EqualTo(data.prices[data.prices.Length - i]));
|
||||||
|
Assert.That(hist[hist.Length - i].Time, Is.EqualTo(data.timestamps[data.prices.Length - i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,275 +0,0 @@
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
|
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Tests
|
|
||||||
{
|
|
||||||
public class HistoryProcessingInstrumentsTests
|
|
||||||
{
|
|
||||||
private static PriceChange[] GetHistory(int count, string figi, DateTime startDt, decimal startValue, decimal step)
|
|
||||||
{
|
|
||||||
var res = new PriceChange[count];
|
|
||||||
if (count != 0)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
startValue += step;
|
|
||||||
startDt = startDt.AddSeconds(1);
|
|
||||||
res[i] = new PriceChange()
|
|
||||||
{
|
|
||||||
Figi = figi,
|
|
||||||
Ticker = figi + "_ticker",
|
|
||||||
Id = i,
|
|
||||||
Time = startDt,
|
|
||||||
Value = startValue,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test0()
|
|
||||||
{
|
|
||||||
var figi = "figi";
|
|
||||||
var startDate = new DateTime(2020, 1, 1, 1, 0, 0, DateTimeKind.Utc);
|
|
||||||
var count = 100;
|
|
||||||
var step = 0;
|
|
||||||
var startValue = 10;
|
|
||||||
var unit = new PriceHistoryCacheUnit(figi, GetHistory(count, figi, startDate, startValue, step));
|
|
||||||
|
|
||||||
var data = unit.GetData().Result;
|
|
||||||
var endDate = startDate.AddSeconds(count);
|
|
||||||
Assert.IsTrue(data.timestamps.Last() == endDate);
|
|
||||||
Assert.IsTrue(data.prices.Last() == startValue + step * count);
|
|
||||||
|
|
||||||
var periodLength = 4;
|
|
||||||
var shift = 0;
|
|
||||||
var result = LocalTrends.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);
|
|
||||||
var firstPrice = startValue + (step * count) - (step * (shift + periodLength));
|
|
||||||
var lastPrice = startValue + (step * count) - step * shift;
|
|
||||||
|
|
||||||
var diff = firstPrice - lastPrice;
|
|
||||||
|
|
||||||
|
|
||||||
Assert.IsTrue(result.LastPrice == lastPrice);
|
|
||||||
Assert.IsTrue(result.FirstPrice == firstPrice);
|
|
||||||
Assert.IsTrue(result.PeriodMax == maxValue);
|
|
||||||
Assert.IsTrue(result.PeriodMin == minValue);
|
|
||||||
Assert.IsTrue(result.PeriodDiff == diff);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test1()
|
|
||||||
{
|
|
||||||
var figi = "figi";
|
|
||||||
var startDate = new DateTime(2020, 1, 1, 1, 0, 0, DateTimeKind.Utc);
|
|
||||||
var count = 100;
|
|
||||||
var step = 0.5m;
|
|
||||||
var startValue = 10;
|
|
||||||
var unit = new PriceHistoryCacheUnit(figi, GetHistory(count, figi, startDate, startValue, step));
|
|
||||||
|
|
||||||
var data = unit.GetData().Result;
|
|
||||||
var endDate = startDate.AddSeconds(count);
|
|
||||||
Assert.IsTrue(data.timestamps.Last() == endDate);
|
|
||||||
Assert.IsTrue(data.prices.Last() == startValue + step * count);
|
|
||||||
|
|
||||||
var periodLength = 4;
|
|
||||||
var shift = 0;
|
|
||||||
var result = LocalTrends.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);
|
|
||||||
var firstPrice = startValue + (step * count) - (step * (shift + periodLength));
|
|
||||||
var lastPrice = startValue + (step * count) - step * shift;
|
|
||||||
|
|
||||||
var diff = lastPrice - firstPrice;
|
|
||||||
|
|
||||||
|
|
||||||
Assert.IsTrue(result.LastPrice == lastPrice);
|
|
||||||
Assert.IsTrue(result.FirstPrice == firstPrice);
|
|
||||||
Assert.IsTrue(result.PeriodMax == maxValue);
|
|
||||||
Assert.IsTrue(result.PeriodMin == minValue);
|
|
||||||
Assert.IsTrue(result.PeriodDiff == diff);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test2()
|
|
||||||
{
|
|
||||||
var figi = "figi";
|
|
||||||
var startDate = new DateTime(2020, 1, 1, 1, 0, 0, DateTimeKind.Utc);
|
|
||||||
var count = 100;
|
|
||||||
var step = 0.5m;
|
|
||||||
var startValue = 10;
|
|
||||||
|
|
||||||
var unit = new PriceHistoryCacheUnit(figi, GetHistory(count, figi, startDate, startValue, step));
|
|
||||||
|
|
||||||
var data = unit.GetData().Result;
|
|
||||||
var endDate = startDate.AddSeconds(count);
|
|
||||||
Assert.IsTrue(data.timestamps.Last() == endDate);
|
|
||||||
Assert.IsTrue(data.prices.Last() == startValue + step * count);
|
|
||||||
|
|
||||||
var periodLength = 4;
|
|
||||||
var shift = 1;
|
|
||||||
var result = LocalTrends.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);
|
|
||||||
var firstPrice = startValue + (step * count) - (step * (shift + periodLength));
|
|
||||||
var lastPrice = startValue + (step * count) - step * shift;
|
|
||||||
|
|
||||||
var diff = lastPrice - firstPrice;
|
|
||||||
|
|
||||||
|
|
||||||
Assert.IsTrue(result.LastPrice == lastPrice);
|
|
||||||
Assert.IsTrue(result.FirstPrice == firstPrice);
|
|
||||||
Assert.IsTrue(result.PeriodMax == maxValue);
|
|
||||||
Assert.IsTrue(result.PeriodMin == minValue);
|
|
||||||
Assert.IsTrue(result.PeriodDiff == diff);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test3()
|
|
||||||
{
|
|
||||||
var figi = "figi";
|
|
||||||
var startDate = new DateTime(2020, 1, 1, 1, 0, 0, DateTimeKind.Utc);
|
|
||||||
var count = 100;
|
|
||||||
var step = -0.5m;
|
|
||||||
var startValue = 10;
|
|
||||||
|
|
||||||
var unit = new PriceHistoryCacheUnit(figi, GetHistory(count, figi, startDate, startValue, step));
|
|
||||||
|
|
||||||
var data = unit.GetData().Result;
|
|
||||||
var endDate = startDate.AddSeconds(count);
|
|
||||||
Assert.IsTrue(data.timestamps.Last() == endDate);
|
|
||||||
Assert.IsTrue(data.prices.Last() == startValue + step * count);
|
|
||||||
|
|
||||||
var periodLength = 4;
|
|
||||||
var shift = 0;
|
|
||||||
var result = LocalTrends.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);
|
|
||||||
var firstPrice = startValue + (step * count) - (step * (shift + periodLength));
|
|
||||||
var lastPrice = startValue + (step * count) - step * shift;
|
|
||||||
|
|
||||||
var diff = lastPrice - firstPrice;
|
|
||||||
|
|
||||||
|
|
||||||
Assert.IsTrue(result.LastPrice == lastPrice);
|
|
||||||
Assert.IsTrue(result.FirstPrice == firstPrice);
|
|
||||||
Assert.IsTrue(result.PeriodMax == maxValue);
|
|
||||||
Assert.IsTrue(result.PeriodMin == minValue);
|
|
||||||
Assert.IsTrue(result.PeriodDiff == diff);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test4()
|
|
||||||
{
|
|
||||||
var figi = "figi";
|
|
||||||
var startDate = new DateTime(2020, 1, 1, 1, 0, 0, DateTimeKind.Utc);
|
|
||||||
var count = 100;
|
|
||||||
var step = -0.5m;
|
|
||||||
var startValue = 10;
|
|
||||||
|
|
||||||
var unit = new PriceHistoryCacheUnit(figi, GetHistory(count, figi, startDate, startValue, step));
|
|
||||||
|
|
||||||
var data = unit.GetData().Result;
|
|
||||||
var endDate = startDate.AddSeconds(count);
|
|
||||||
Assert.IsTrue(data.timestamps.Last() == endDate);
|
|
||||||
Assert.IsTrue(data.prices.Last() == startValue + step * count);
|
|
||||||
|
|
||||||
var periodLength = 4;
|
|
||||||
var shift = 3;
|
|
||||||
var result = LocalTrends.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);
|
|
||||||
var firstPrice = startValue + (step * count) - (step * (shift + periodLength));
|
|
||||||
var lastPrice = startValue + (step * count) - step * shift;
|
|
||||||
|
|
||||||
var diff = lastPrice - firstPrice;
|
|
||||||
|
|
||||||
|
|
||||||
Assert.IsTrue(result.LastPrice == lastPrice);
|
|
||||||
Assert.IsTrue(result.FirstPrice == firstPrice);
|
|
||||||
Assert.IsTrue(result.PeriodMax == maxValue);
|
|
||||||
Assert.IsTrue(result.PeriodMin == minValue);
|
|
||||||
Assert.IsTrue(result.PeriodDiff == diff);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Test5()
|
|
||||||
{
|
|
||||||
var figi = "figi";
|
|
||||||
var startDate = new DateTime(2020, 1, 1, 1, 0, 0, DateTimeKind.Utc);
|
|
||||||
var count = 100;
|
|
||||||
var step = -0.5m;
|
|
||||||
var startValue = 10;
|
|
||||||
|
|
||||||
var unit = new PriceHistoryCacheUnit(figi, GetHistory(count, figi, startDate, startValue, step));
|
|
||||||
|
|
||||||
var data = unit.GetData().Result;
|
|
||||||
var endDate = startDate.AddSeconds(count);
|
|
||||||
Assert.IsTrue(data.timestamps.Last() == endDate);
|
|
||||||
Assert.IsTrue(data.prices.Last() == startValue + step * count);
|
|
||||||
|
|
||||||
var periodLength = 4;
|
|
||||||
var shift = 3;
|
|
||||||
var result = LocalTrends.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);
|
|
||||||
var firstPrice1 = startValue + (step * count) - (step * (shift + periodLength));
|
|
||||||
var lastPrice1 = startValue + (step * count) - step * shift;
|
|
||||||
|
|
||||||
var diff1 = lastPrice1 - firstPrice1;
|
|
||||||
|
|
||||||
|
|
||||||
Assert.IsTrue(result.LastPrice == lastPrice1);
|
|
||||||
Assert.IsTrue(result.FirstPrice == firstPrice1);
|
|
||||||
Assert.IsTrue(result.PeriodMax == maxValue1);
|
|
||||||
Assert.IsTrue(result.PeriodMin == minValue1);
|
|
||||||
Assert.IsTrue(result.PeriodDiff == diff1);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var unit2 = new PriceHistoryCacheUnit(figi);
|
|
||||||
var data2 = unit.GetData().Result;
|
|
||||||
for (int i = 0; i < data2.prices.Length; i++)
|
|
||||||
{
|
|
||||||
var value = (decimal)data2.prices[i];
|
|
||||||
if (i == data2.prices.Length - 5)
|
|
||||||
{
|
|
||||||
value = 100;
|
|
||||||
}
|
|
||||||
else if (i == data2.prices.Length - 6)
|
|
||||||
{
|
|
||||||
value = -100;
|
|
||||||
}
|
|
||||||
unit2.AddData(new PriceChange()
|
|
||||||
{
|
|
||||||
Figi = figi,
|
|
||||||
Ticker = figi,
|
|
||||||
Time = data2.timestamps[i],
|
|
||||||
Value = value
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var result2 = LocalTrends.GetPriceDiffForTimeSpan(unit2, TimeSpan.FromSeconds(shift), TimeSpan.FromSeconds(periodLength));
|
|
||||||
|
|
||||||
var maxValue2 = 100;
|
|
||||||
var minValue2 = -100;
|
|
||||||
|
|
||||||
Assert.IsTrue(result2.LastPrice == result.LastPrice);
|
|
||||||
Assert.IsTrue(result2.FirstPrice == result.FirstPrice);
|
|
||||||
Assert.IsTrue(result2.PeriodMax == maxValue2);
|
|
||||||
Assert.IsTrue(result2.PeriodMin == minValue2);
|
|
||||||
Assert.IsTrue(result2.PeriodDiff == result.PeriodDiff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
using KLHZ.Trader.Core.Exchange.Models;
|
using KLHZ.Trader.Core.Exchange.Models.Assets;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Exchange.Extentions
|
namespace KLHZ.Trader.Core.Exchange.Extentions
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace KLHZ.Trader.Core.Exchange.Models
|
namespace KLHZ.Trader.Core.Exchange.Models.Assets
|
||||||
{
|
{
|
||||||
public class Asset
|
public class Asset
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace KLHZ.Trader.Core.Exchange.Models
|
namespace KLHZ.Trader.Core.Exchange.Models.Assets
|
||||||
{
|
{
|
||||||
public enum AssetType
|
public enum AssetType
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
namespace KLHZ.Trader.Core.Exchange.Models
|
namespace KLHZ.Trader.Core.Exchange.Models.Assets
|
||||||
{
|
{
|
||||||
public enum PositionType
|
public enum PositionType
|
||||||
{
|
{
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace KLHZ.Trader.Core.Exchange.Models
|
||||||
|
{
|
||||||
|
internal class DeferredTrade
|
||||||
|
{
|
||||||
|
public required string Figi { get; set; }
|
||||||
|
public decimal Price { get; set; }
|
||||||
|
public DateTime Time { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ using System.Collections.Concurrent;
|
||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
using Tinkoff.InvestApi;
|
using Tinkoff.InvestApi;
|
||||||
using Tinkoff.InvestApi.V1;
|
using Tinkoff.InvestApi.V1;
|
||||||
using PositionType = KLHZ.Trader.Core.Exchange.Models.PositionType;
|
using PositionType = KLHZ.Trader.Core.Exchange.Models.Assets.PositionType;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Exchange.Services
|
namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
{
|
{
|
||||||
|
@ -58,7 +58,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal readonly ConcurrentDictionary<string, Models.Asset> Assets = new();
|
internal readonly ConcurrentDictionary<string, Models.Assets.Asset> Assets = new();
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
price = position.AveragePositionPrice;
|
price = position.AveragePositionPrice;
|
||||||
}
|
}
|
||||||
#pragma warning disable CS0612 // Тип или член устарел
|
#pragma warning disable CS0612 // Тип или член устарел
|
||||||
var asset = new Models.Asset()
|
var asset = new Models.Assets.Asset()
|
||||||
{
|
{
|
||||||
AccountId = AccountId,
|
AccountId = AccountId,
|
||||||
Figi = position.Figi,
|
Figi = position.Figi,
|
||||||
|
|
|
@ -10,6 +10,7 @@ using KLHZ.Trader.Core.DataLayer.Entities.Declisions.Enums;
|
||||||
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
||||||
using KLHZ.Trader.Core.Exchange.Extentions;
|
using KLHZ.Trader.Core.Exchange.Extentions;
|
||||||
using KLHZ.Trader.Core.Exchange.Models;
|
using KLHZ.Trader.Core.Exchange.Models;
|
||||||
|
using KLHZ.Trader.Core.Exchange.Models.Assets;
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
|
using KLHZ.Trader.Core.Math.Declisions.Services.Cache;
|
||||||
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
@ -20,7 +21,7 @@ using Microsoft.Extensions.Options;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
using Tinkoff.InvestApi;
|
using Tinkoff.InvestApi;
|
||||||
using AssetType = KLHZ.Trader.Core.Exchange.Models.AssetType;
|
using AssetType = KLHZ.Trader.Core.Exchange.Models.Assets.AssetType;
|
||||||
|
|
||||||
namespace KLHZ.Trader.Core.Exchange.Services
|
namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
{
|
{
|
||||||
|
@ -31,6 +32,8 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
private readonly IDataBus _dataBus;
|
private readonly IDataBus _dataBus;
|
||||||
private readonly BotModeSwitcher _botModeSwitcher;
|
private readonly BotModeSwitcher _botModeSwitcher;
|
||||||
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
|
private readonly IDbContextFactory<TraderDbContext> _dbContextFactory;
|
||||||
|
private readonly ConcurrentDictionary<string, DeferredTrade> DeferredLongOpens = new();
|
||||||
|
private readonly ConcurrentDictionary<string, DeferredTrade> DeferredLongCloses = new();
|
||||||
private readonly ConcurrentDictionary<string, DateTime> OpeningStops = new();
|
private readonly ConcurrentDictionary<string, DateTime> OpeningStops = new();
|
||||||
private readonly ConcurrentDictionary<string, ManagedAccount> Accounts = new();
|
private readonly ConcurrentDictionary<string, ManagedAccount> Accounts = new();
|
||||||
private readonly ConcurrentDictionary<string, IPriceHistoryCacheUnit> _historyCash = new();
|
private readonly ConcurrentDictionary<string, IPriceHistoryCacheUnit> _historyCash = new();
|
||||||
|
@ -125,7 +128,37 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
{
|
{
|
||||||
if (message.Figi == "FUTIMOEXF000")
|
if (message.Figi == "FUTIMOEXF000")
|
||||||
{
|
{
|
||||||
var data = await unit.GetData();
|
|
||||||
|
DeferredTrade? longOpen;
|
||||||
|
DeferredLongOpens.TryGetValue(message.Figi, out longOpen);
|
||||||
|
if (longOpen != null)
|
||||||
|
{
|
||||||
|
if (longOpen.Time <= (message.IsHistoricalData ? message.Time : DateTime.UtcNow))
|
||||||
|
{
|
||||||
|
DeferredLongOpens.TryRemove(message.Figi, out _);
|
||||||
|
if (message.Value - longOpen.Price < 1)
|
||||||
|
{
|
||||||
|
LogDeclision(declisionsForSave, DeclisionTradeAction.OpenLong, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeferredTrade? longClose;
|
||||||
|
DeferredLongCloses.TryGetValue(message.Figi, out longClose);
|
||||||
|
if (longClose != null)
|
||||||
|
{
|
||||||
|
if (longClose.Time <= (message.IsHistoricalData ? message.Time : DateTime.UtcNow))
|
||||||
|
{
|
||||||
|
DeferredLongCloses.TryRemove(message.Figi, out _);
|
||||||
|
if (longClose.Price - message.Value < 1)
|
||||||
|
{
|
||||||
|
LogDeclision(declisionsForSave, DeclisionTradeAction.CloseLong, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var windowMaxSize = 100;
|
||||||
|
var data = await unit.GetData(windowMaxSize);
|
||||||
if (OpeningStops.TryGetValue(message.Figi, out var dt))
|
if (OpeningStops.TryGetValue(message.Figi, out var dt))
|
||||||
{
|
{
|
||||||
if (dt < (message.IsHistoricalData ? message.Time : DateTime.UtcNow))
|
if (dt < (message.IsHistoricalData ? message.Time : DateTime.UtcNow))
|
||||||
|
@ -137,34 +170,59 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
||||||
if ((unit.BidsCount / unit.AsksCount) < 0.5m || (unit.BidsCount / unit.AsksCount) > 2m)
|
if ((unit.BidsCount / unit.AsksCount) < 0.5m || (unit.BidsCount / unit.AsksCount) > 2m)
|
||||||
{
|
{
|
||||||
var stopTo = (message.IsHistoricalData ? message.Time : DateTime.UtcNow).AddMinutes(3);
|
var stopTo = (message.IsHistoricalData ? message.Time : DateTime.UtcNow).AddMinutes(3);
|
||||||
OpeningStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo);
|
//OpeningStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo);
|
||||||
LogDeclision(declisionsForSave, DeclisionTradeAction.StopBuyShortTime, message);
|
//LogDeclision(declisionsForSave, DeclisionTradeAction.StopBuyShortTime, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
var resultLongOpen = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, 100, 45, 180, 2.5m);
|
var res = TradingEvent.None;
|
||||||
var resultLongClose = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, 100, 15, 120, 2.5m);
|
var resultMoveAvFull = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 45, 180, 2.5m);
|
||||||
|
var resultLongClose = MovingAverage.CheckByWindowAverageMean(data.timestamps, data.prices, windowMaxSize, 15, 120, 2.5m).events;
|
||||||
|
|
||||||
if (resultLongOpen.bigWindowAv != 0)
|
var uptrendStarts = LocalTrends.CheckByLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(7), 1.5m, 3);
|
||||||
|
var uptrendStarts2 = LocalTrends.CheckByLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(3), 1.5m, 2);
|
||||||
|
var downtrendEnds = LocalTrends.CheckByLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(10), 1.5m, 5);
|
||||||
|
|
||||||
|
|
||||||
|
res |= uptrendStarts;
|
||||||
|
res |= uptrendStarts2;
|
||||||
|
//res |= downtrendEnds;
|
||||||
|
res |= resultLongClose;
|
||||||
|
res |= resultMoveAvFull.events;
|
||||||
|
if (resultMoveAvFull.bigWindowAv != 0)
|
||||||
{
|
{
|
||||||
LogPrice(processedPrices, message, bigWindowProcessor, resultLongOpen.bigWindowAv);
|
LogPrice(processedPrices, message, bigWindowProcessor, resultMoveAvFull.bigWindowAv);
|
||||||
LogPrice(processedPrices, message, smallWindowProcessor, resultLongOpen.smallWindowAv);
|
LogPrice(processedPrices, message, smallWindowProcessor, resultMoveAvFull.smallWindowAv);
|
||||||
}
|
}
|
||||||
if ((resultLongClose.events & TradingEvent.StopBuy) == TradingEvent.StopBuy)
|
if ((resultLongClose & TradingEvent.StopBuy) == TradingEvent.StopBuy)
|
||||||
{
|
{
|
||||||
var stopTo = (message.IsHistoricalData ? message.Time : DateTime.UtcNow).AddMinutes(_buyStopLength);
|
var stopTo = (message.IsHistoricalData ? message.Time : DateTime.UtcNow).AddMinutes(_buyStopLength);
|
||||||
OpeningStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo);
|
OpeningStops.AddOrUpdate(message.Figi, stopTo, (k, v) => stopTo);
|
||||||
//LogDeclision(declisionsForSave, DeclisionTradeAction.StopBuy, message);
|
//LogDeclision(declisionsForSave, DeclisionTradeAction.StopBuy, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((resultLongOpen.events & TradingEvent.UptrendStart) == TradingEvent.UptrendStart
|
if ((res & TradingEvent.UptrendStart) == TradingEvent.UptrendStart
|
||||||
&& !OpeningStops.TryGetValue(message.Figi, out _))
|
&& !OpeningStops.TryGetValue(message.Figi, out _)
|
||||||
|
&& data.timestamps.Length > 1
|
||||||
|
&& (data.timestamps[data.timestamps.Length - 1] - data.timestamps[data.timestamps.Length - 2] < TimeSpan.FromMinutes(1)))
|
||||||
{
|
{
|
||||||
LogDeclision(declisionsForSave, DeclisionTradeAction.OpenLong, message);
|
var trade = new DeferredTrade()
|
||||||
|
{
|
||||||
|
Figi = message.Figi,
|
||||||
|
Price = message.Value,
|
||||||
|
Time = message.Time.AddSeconds(15)
|
||||||
|
};
|
||||||
|
DeferredLongOpens[message.Figi] = trade;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((resultLongClose.events & TradingEvent.UptrendEnd) == TradingEvent.UptrendEnd)
|
if ((res & TradingEvent.UptrendEnd) == TradingEvent.UptrendEnd)
|
||||||
{
|
{
|
||||||
LogDeclision(declisionsForSave, DeclisionTradeAction.CloseLong, message);
|
var trade = new DeferredTrade()
|
||||||
|
{
|
||||||
|
Figi = message.Figi,
|
||||||
|
Price = message.Value,
|
||||||
|
Time = message.Time.AddSeconds(15)
|
||||||
|
};
|
||||||
|
DeferredLongCloses[message.Figi] = trade;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if ((resultLongOpen.events & TradingEvent.ShortOpen) == TradingEvent.ShortOpen
|
//if ((resultLongOpen.events & TradingEvent.ShortOpen) == TradingEvent.ShortOpen
|
||||||
|
|
Loading…
Reference in New Issue