реализация новой стратегии
parent
f4fb12407a
commit
c2612ba85a
|
@ -4,14 +4,9 @@
|
|||
public enum TradingEvent
|
||||
{
|
||||
None = 0,
|
||||
StopBuy = 1,
|
||||
LongOpen = 2,
|
||||
ShortClose = 4,
|
||||
LongClose = 8,
|
||||
ShortOpen = 16,
|
||||
UptrendEnd = 32,
|
||||
UptrendStart = 64,
|
||||
DowntrendEnd = 128,
|
||||
DowntrendStart = 256,
|
||||
CloseLong = 1,
|
||||
OpenLong = 2,
|
||||
CloseShort = 4,
|
||||
OpenShort = 8,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,19 +71,19 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
|||
var diff2 = y2_approximated[0] - y2_approximated[y2_approximated.Count - 1];
|
||||
if (diff1 <= -meanfullDiff && diff2 >= meanfullDiff)
|
||||
{
|
||||
res |= TradingEvent.DowntrendEnd;
|
||||
res |= TradingEvent.CloseShort;
|
||||
}
|
||||
else if (diff1 >= meanfullDiff && diff2 <= -meanfullDiff)
|
||||
{
|
||||
res |= TradingEvent.UptrendEnd;
|
||||
res |= TradingEvent.CloseLong;
|
||||
}
|
||||
else if (diff1 <= -meanfullDiff && diff2 >= meanfullDiff)
|
||||
{
|
||||
res |= TradingEvent.DowntrendEnd;
|
||||
res |= TradingEvent.CloseShort;
|
||||
}
|
||||
else if (diff1 >= 0 && diff2 <= -meanfullDiff)
|
||||
{
|
||||
res |= TradingEvent.DowntrendStart;
|
||||
res |= TradingEvent.OpenShort;
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
|||
if (diffTotal >= uptrendEndingDetectionMeanfullStep
|
||||
&& times[crossings[0]] - times[crossings[1]] >= timeForUptreandStart)
|
||||
{
|
||||
res |= TradingEvent.UptrendEnd;
|
||||
res |= TradingEvent.CloseLong;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
|||
if (pricesForFinalComparison[crossings[0]] - pricesForFinalComparison[crossings[1]] <= uptrendStartingDetectionMeanfullStep
|
||||
&& times[crossings[0]] - times[crossings[1]] >= timeForUptreandStart)
|
||||
{
|
||||
res |= TradingEvent.UptrendStart;
|
||||
res |= TradingEvent.OpenLong;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
|||
)
|
||||
&& dt > TimeSpan.FromSeconds(10)))
|
||||
{
|
||||
res |= TradingEvent.UptrendEnd;
|
||||
res |= TradingEvent.CloseLong;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ namespace KLHZ.Trader.Core.Math.Declisions.Utils
|
|||
// || d2 <= uptrendStartingDetectionMeanfullStep
|
||||
) && dt > TimeSpan.FromSeconds(10)))
|
||||
{
|
||||
res |= TradingEvent.UptrendStart;
|
||||
res |= TradingEvent.OpenLong;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace KLHZ.Trader.Core.Tests
|
|||
|
||||
if (LocalTrends.TryGetLocalTrends(data.timestamps, data.prices, TimeSpan.FromSeconds(49), TimeSpan.FromSeconds(49), 22, out var res))
|
||||
{
|
||||
Assert.That(res == Contracts.Declisions.Dtos.Enums.TradingEvent.UptrendEnd);
|
||||
Assert.That(res == Contracts.Declisions.Dtos.Enums.TradingEvent.CloseLong);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
public enum DeclisionTradeAction
|
||||
{
|
||||
Unknown = 0,
|
||||
StopBuy = 1,
|
||||
StopBuyShortTime = 2,
|
||||
OpenLong = 100,
|
||||
OpenLongReal = 101,
|
||||
CloseLong = 200,
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
namespace KLHZ.Trader.Core.Exchange.Models.Trading
|
||||
{
|
||||
internal class PirsonCalculatingResult
|
||||
{
|
||||
public bool Success { get; init; }
|
||||
public decimal Pirson { get; init; }
|
||||
public decimal PriceDiff { get; init; }
|
||||
public decimal TradesDiff { get; init; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using KLHZ.Trader.Core.Contracts.Common.Enums;
|
||||
|
||||
namespace KLHZ.Trader.Core.Exchange.Models.Trading
|
||||
{
|
||||
public readonly struct Stops
|
||||
{
|
||||
public readonly decimal LongStopLossShift;
|
||||
public readonly decimal LongTakeProfitShift;
|
||||
public readonly decimal ShortStopLossShift;
|
||||
public readonly decimal ShortTakeProfitShift;
|
||||
|
||||
public Stops(decimal longStopLossShift, decimal longTakeProfitShift, decimal shortStopLossShift, decimal shortTakeProfitShift)
|
||||
{
|
||||
LongStopLossShift = longStopLossShift;
|
||||
LongTakeProfitShift = longTakeProfitShift;
|
||||
ShortStopLossShift = shortStopLossShift;
|
||||
ShortTakeProfitShift = shortTakeProfitShift;
|
||||
}
|
||||
|
||||
public (decimal takeProfit, decimal stopLoss) GetStops(PositionType positionType)
|
||||
{
|
||||
if (positionType == PositionType.Short)
|
||||
{
|
||||
return (ShortTakeProfitShift, ShortStopLossShift);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (LongTakeProfitShift, LongStopLossShift);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
namespace KLHZ.Trader.Core.Exchange.Models.Trading
|
||||
{
|
||||
public class SupportLevel
|
||||
{
|
||||
public decimal Value { get; init; }
|
||||
public decimal LowValue { get; init; }
|
||||
public decimal HighValue { get; init; }
|
||||
public DateTime? LastLevelTime { get; init; }
|
||||
}
|
||||
}
|
|
@ -34,8 +34,9 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
|
||||
private readonly ConcurrentDictionary<string, TradingMode> TradingModes = new();
|
||||
|
||||
private readonly ConcurrentDictionary<string, SupportLevel[]> SupportLevels = new();
|
||||
private readonly ConcurrentDictionary<string, decimal> DPirsonValues = new();
|
||||
private readonly ConcurrentDictionary<string, decimal> DPricesValues = new();
|
||||
private readonly ConcurrentDictionary<string, DateTime> _supportLevelsCalculationTimes = new();
|
||||
|
||||
private readonly Channel<ITradeDataItem> _pricesChannel = Channel.CreateUnbounded<ITradeDataItem>();
|
||||
private readonly Channel<ITradeCommand> _commands = Channel.CreateUnbounded<ITradeCommand>();
|
||||
|
@ -82,7 +83,8 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
{
|
||||
var fakeMessage = new TradeDataItem() { Figi = command.Figi, Ticker = "", Count = command.Count, Direction = 1, IsHistoricalData = false, Time = DateTime.UtcNow, Price = command.RecomendPrice ?? 0m };
|
||||
var positionType = command.CommandType == TradeCommandType.OpenLong ? PositionType.Long : PositionType.Short;
|
||||
var stops = GetStops(fakeMessage, positionType);
|
||||
var st = GetStops(fakeMessage);
|
||||
var stops = st.GetStops(positionType);
|
||||
var accounts = _portfolioWrapper.Accounts
|
||||
.Where(a => !a.Value.Assets.ContainsKey(command.Figi))
|
||||
.Take(1)
|
||||
|
@ -120,7 +122,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
{
|
||||
var pricesCache1 = new Dictionary<string, List<ITradeDataItem>>();
|
||||
var pricesCache2 = new Dictionary<string, List<ITradeDataItem>>();
|
||||
var timesCache = new Dictionary<string, DateTime>();
|
||||
|
||||
while (await _pricesChannel.Reader.WaitToReadAsync())
|
||||
{
|
||||
var message = await _pricesChannel.Reader.ReadAsync();
|
||||
|
@ -128,86 +130,22 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
{
|
||||
continue;
|
||||
}
|
||||
var changeMods = TraderUtils.GetInitDict(0);
|
||||
await CloseMarginPositionsIfNeed(message);
|
||||
|
||||
try
|
||||
{
|
||||
//message = TraderUtils.FilterHighFreqValues(message, message.Direction == 1 ? pricesCache1 : pricesCache2);
|
||||
|
||||
#region Добавление данных в кеши.
|
||||
if (message.Figi == "BBG004730N88" || message.Figi == "FUTIMOEXF000")
|
||||
if (message.IsHistoricalData)
|
||||
{
|
||||
await _tradeDataProvider.AddData(message);
|
||||
message = TraderUtils.FilterHighFreqValues(message, message.Direction == 1 ? pricesCache1 : pricesCache2);
|
||||
}
|
||||
#endregion
|
||||
|
||||
if (_exchangeConfig.TradingInstrumentsFigis.Contains(message.Figi) && message.Direction == 1)
|
||||
{
|
||||
var _15minCacheSize = TimeSpan.FromSeconds(400);
|
||||
var smallWindow = TimeSpan.FromSeconds(180);
|
||||
var bigWindow = TimeSpan.FromSeconds(360);
|
||||
var meanWindow = TimeSpan.FromSeconds(360);
|
||||
var currentTime = message.IsHistoricalData ? message.Time : DateTime.UtcNow;
|
||||
await _tradeDataProvider.AddData(message);
|
||||
|
||||
var buys = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, _15minCacheSize, selector: (i) => i.Direction == 1);
|
||||
var trades = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, _15minCacheSize);
|
||||
if (trades.TryCalcTimeWindowsDiff(bigWindow, smallWindow, v => v.Count, false, out var tradesDiff)
|
||||
&& buys.TryCalcTimeDiff(bigWindow, smallWindow, v => v.Price, true, out var pricesDiff))
|
||||
if (message.Figi == "FUTIMOEXF000")
|
||||
{
|
||||
await _tradeDataProvider.LogPrice(message, "privcesDiff", pricesDiff);
|
||||
await _tradeDataProvider.LogPrice(message, "tradevolume_diff", tradesDiff);
|
||||
await _tradeDataProvider.AddData(message.Figi, "5min_diff", new Contracts.Declisions.Dtos.CachedValue()
|
||||
{
|
||||
Time = message.Time,
|
||||
Value2 = tradesDiff,
|
||||
Value = pricesDiff,
|
||||
Figi = message.Figi,
|
||||
Ticker = message.Ticker,
|
||||
});
|
||||
|
||||
if (DPricesValues.TryGetValue(message.Figi, out var olddPrice))
|
||||
{
|
||||
if (olddPrice < 0m && pricesDiff > 0)
|
||||
{
|
||||
//await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_long_in", message.Price);
|
||||
}
|
||||
if (olddPrice > 0m && pricesDiff < 0m)
|
||||
{
|
||||
//await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_short_in", message.Price);
|
||||
}
|
||||
}
|
||||
DPricesValues[message.Figi] = pricesDiff;
|
||||
var diffs = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, _15minCacheSize, "5min_diff");
|
||||
if (diffs.TryCalcPirsonCorrelation(meanWindow, out var pirson))
|
||||
{
|
||||
var res = pirson;
|
||||
await _tradeDataProvider.LogPrice(message, "diffs_pirson", (decimal)pirson);
|
||||
await _tradeDataProvider.AddData(message.Figi, "diffs_pirson", new Contracts.Declisions.Dtos.CachedValue()
|
||||
{
|
||||
Time = message.Time,
|
||||
Value = (decimal)pirson,
|
||||
Figi = message.Figi,
|
||||
Ticker = message.Ticker,
|
||||
});
|
||||
if (DPirsonValues.TryGetValue(message.Figi, out var olddpirs))
|
||||
{
|
||||
if (olddpirs < -0.3m && res > -0.3m && pricesDiff > 0 && (tradesDiff > 0))
|
||||
{
|
||||
await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_long_in", message.Price);
|
||||
}
|
||||
if (olddpirs > 0.7m && res < 0.7m)
|
||||
{
|
||||
// await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_long_out", message.Price);
|
||||
}
|
||||
if (olddpirs > 0.3m && res < 0.3m && pricesDiff < 0 && (tradesDiff > 0))
|
||||
{
|
||||
await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_short_in", message.Price);
|
||||
}
|
||||
if (olddpirs < -0.7m && res > -0.7m)
|
||||
{
|
||||
// await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_short_out", message.Price);
|
||||
}
|
||||
}
|
||||
DPirsonValues[message.Figi] = res;
|
||||
}
|
||||
await ProcessIMOEXF(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -219,6 +157,206 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
}
|
||||
}
|
||||
|
||||
private async Task ProcessIMOEXF(ITradeDataItem message)
|
||||
{
|
||||
if (message.Figi == "FUTIMOEXF000")
|
||||
{
|
||||
await CalcSupportLevels(message, 3, 5);
|
||||
var stops = GetStops(message);
|
||||
var pirson = await CalcPirson(message);
|
||||
var declisionPirson = await ProcessPirson(pirson, message);
|
||||
var declisionsSupportLevels = await ProcessSupportLevels(message);
|
||||
var declisionsStops = ProcessStops(stops, 1.5m);
|
||||
var res = TraderUtils.MergeResultsMult(declisionPirson, declisionsSupportLevels);
|
||||
res = TraderUtils.MergeResultsMult(res, declisionsStops);
|
||||
|
||||
await ExecuteDeclisions(res.ToImmutableDictionary(), message, stops, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ImmutableDictionary<TradingEvent, decimal>> ProcessPirson(PirsonCalculatingResult pirson, ITradeDataItem message)
|
||||
{
|
||||
var res = TraderUtils.GetInitDict(0);
|
||||
if (pirson.Success && DPirsonValues.TryGetValue(message.Figi, out var olddpirs))
|
||||
{
|
||||
if (olddpirs < -0.3m && pirson.Pirson > -0.3m && pirson.PriceDiff > 0 && (pirson.TradesDiff > 0))
|
||||
{
|
||||
res[TradingEvent.OpenLong] = Constants.PowerUppingCoefficient;
|
||||
await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_long_in", message.Price);
|
||||
}
|
||||
if (olddpirs > 0.3m && pirson.Pirson < 0.3m && pirson.PriceDiff < 0 && (pirson.TradesDiff > 0))
|
||||
{
|
||||
res[TradingEvent.OpenShort] = Constants.PowerUppingCoefficient;
|
||||
await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_short_in", message.Price);
|
||||
}
|
||||
if (olddpirs > 0.7m && pirson.Pirson < 0.7m)
|
||||
{
|
||||
// await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_long_out", message.Price);
|
||||
}
|
||||
if (olddpirs < -0.7m && pirson.Pirson > -0.7m)
|
||||
{
|
||||
// await _tradeDataProvider.LogPrice(message, "diffs_pirson_diff_point_short_out", message.Price);
|
||||
}
|
||||
}
|
||||
DPirsonValues[message.Figi] = pirson.Pirson;
|
||||
return res.ToImmutableDictionary();
|
||||
}
|
||||
|
||||
private async Task<PirsonCalculatingResult> CalcPirson(ITradeDataItem message)
|
||||
{
|
||||
var cacheSize = TimeSpan.FromSeconds(400);
|
||||
var smallWindow = TimeSpan.FromSeconds(180);
|
||||
var bigWindow = TimeSpan.FromSeconds(360);
|
||||
var meanWindowForCottelation = TimeSpan.FromSeconds(360);
|
||||
var currentTime = message.IsHistoricalData ? message.Time : DateTime.UtcNow;
|
||||
|
||||
var buys = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, cacheSize, selector: (i) => i.Direction == 1);
|
||||
var trades = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, cacheSize);
|
||||
if (trades.TryCalcTimeWindowsDiff(bigWindow, smallWindow, v => v.Count, false, out var tradesDiff)
|
||||
&& buys.TryCalcTimeDiff(bigWindow, smallWindow, v => v.Price, true, out var pricesDiff))
|
||||
{
|
||||
await _tradeDataProvider.LogPrice(message, "privcesDiff", pricesDiff);
|
||||
await _tradeDataProvider.LogPrice(message, "tradevolume_diff", tradesDiff);
|
||||
await _tradeDataProvider.AddData(message.Figi, "5min_diff", new Contracts.Declisions.Dtos.CachedValue()
|
||||
{
|
||||
Time = message.Time,
|
||||
Value2 = tradesDiff,
|
||||
Value = pricesDiff,
|
||||
Figi = message.Figi,
|
||||
Ticker = message.Ticker,
|
||||
});
|
||||
|
||||
var diffs = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, cacheSize, "5min_diff");
|
||||
if (diffs.TryCalcPirsonCorrelation(meanWindowForCottelation, out var pirson))
|
||||
{
|
||||
var res = pirson;
|
||||
await _tradeDataProvider.LogPrice(message, "diffs_pirson", (decimal)pirson);
|
||||
//await _tradeDataProvider.AddData(message.Figi, "diffs_pirson", new Contracts.Declisions.Dtos.CachedValue()
|
||||
//{
|
||||
// Time = message.Time,
|
||||
// Value = (decimal)pirson,
|
||||
// Figi = message.Figi,
|
||||
// Ticker = message.Ticker,
|
||||
//});
|
||||
return new PirsonCalculatingResult()
|
||||
{
|
||||
Pirson = res,
|
||||
PriceDiff = pricesDiff,
|
||||
TradesDiff = tradesDiff,
|
||||
Success = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
return new PirsonCalculatingResult()
|
||||
{
|
||||
Success = false,
|
||||
};
|
||||
}
|
||||
|
||||
private async Task CloseMarginPositionsIfNeed(ITradeDataItem message)
|
||||
{
|
||||
var state = ExchangeScheduler.GetCurrentState(message.Time);
|
||||
if (!message.IsHistoricalData && state == ExchangeState.ClearingTime)
|
||||
{
|
||||
var futuresFigis = _portfolioWrapper.Accounts.Values.SelectMany(v => v.Assets.Values.Where(a => a.Type == AssetType.Futures)).ToArray();
|
||||
await ClosePositions(futuresFigis, message, false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CalcSupportLevels(ITradeDataItem message, int leverage, decimal supportLevelWidth, int depthHours = 3)
|
||||
{
|
||||
if (_supportLevelsCalculationTimes.TryGetValue(message.Figi, out var lastTime))
|
||||
{
|
||||
if ((message.Time - lastTime).TotalMinutes < 30)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var data = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, TimeSpan.FromHours(depthHours));
|
||||
if (data.Length > 0)
|
||||
{
|
||||
if (data[^1].Time - data[0].Time < TimeSpan.FromHours(depthHours - 1))
|
||||
{
|
||||
data = await _tradeDataProvider.GetDataForTimeWindow(message.Figi, TimeSpan.FromHours(depthHours + 12));
|
||||
if (data[^1].Time - data[0].Time < TimeSpan.FromHours(depthHours - 1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var hist = Statistics.CalcHistogram(data);
|
||||
var convs = Statistics.CalcConvolution(hist, leverage).ToList();
|
||||
var orderedConvs = convs.OrderByDescending(c => c.Sum).Take(5).ToList();
|
||||
orderedConvs = [.. orderedConvs.OrderBy(c => c.Value)];
|
||||
var levelsForAdd = new List<SupportLevel>();
|
||||
foreach (var c in orderedConvs)
|
||||
{
|
||||
var low = c.Value - supportLevelWidth;
|
||||
var high = c.Value + supportLevelWidth;
|
||||
|
||||
if (levelsForAdd.Count > 0)
|
||||
{
|
||||
var last = levelsForAdd.Last();
|
||||
if (last.HighValue < low)
|
||||
{
|
||||
levelsForAdd.Add(new SupportLevel()
|
||||
{
|
||||
HighValue = high,
|
||||
LowValue = low,
|
||||
Value = c.Value,
|
||||
});
|
||||
}
|
||||
else if (last.HighValue >= low && last.HighValue < high)
|
||||
{
|
||||
levelsForAdd[^1] = new SupportLevel()
|
||||
{
|
||||
LowValue = last.LowValue,
|
||||
HighValue = high,
|
||||
Value = last.LowValue + (high - last.LowValue) / 2
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
levelsForAdd.Add(new SupportLevel()
|
||||
{
|
||||
HighValue = high,
|
||||
LowValue = low,
|
||||
Value = c.Value,
|
||||
});
|
||||
}
|
||||
}
|
||||
var finalLevels = new SupportLevel[levelsForAdd.Count];
|
||||
var i = 0;
|
||||
foreach (var level in levelsForAdd)
|
||||
{
|
||||
|
||||
DateTime? time = null;
|
||||
foreach (var item in data)
|
||||
{
|
||||
if (item.Price >= level.LowValue && item.Price < level.HighValue)
|
||||
{
|
||||
time = item.Time;
|
||||
}
|
||||
}
|
||||
finalLevels[i] = new SupportLevel()
|
||||
{
|
||||
HighValue = level.HighValue,
|
||||
LowValue = level.LowValue,
|
||||
Value = level.Value,
|
||||
LastLevelTime = time,
|
||||
};
|
||||
i++;
|
||||
}
|
||||
|
||||
SupportLevels[message.Figi] = finalLevels;
|
||||
await _tradeDataProvider.LogPrice(message, "support_level_calc", message.Price);
|
||||
}
|
||||
|
||||
_supportLevelsCalculationTimes[message.Figi] = message.Time;
|
||||
}
|
||||
|
||||
private async Task ClosePositions(Asset[] assets, ITradeDataItem message, bool withProfitOnly = true)
|
||||
{
|
||||
var loggedDeclisions = 0;
|
||||
|
@ -308,19 +446,20 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
}
|
||||
}
|
||||
|
||||
private async Task ExecuteDeclisions(ITradeDataItem message, ImmutableDictionary<TradingEvent, decimal> result)
|
||||
private async Task ExecuteDeclisions(ImmutableDictionary<TradingEvent, decimal> result, ITradeDataItem message, Stops st, int accountsForOpening = 1)
|
||||
{
|
||||
var state = ExchangeScheduler.GetCurrentState();
|
||||
if (result[TradingEvent.UptrendStart] >= Constants.UppingCoefficient
|
||||
if (result[TradingEvent.OpenLong] >= Constants.UppingCoefficient
|
||||
&& state == ExchangeState.Open
|
||||
)
|
||||
{
|
||||
var stops = GetStops(message, PositionType.Long);
|
||||
var stops = st.GetStops(PositionType.Long);
|
||||
|
||||
if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase())
|
||||
{
|
||||
var accounts = _portfolioWrapper.Accounts
|
||||
.Where(a => !a.Value.Assets.ContainsKey(message.Figi))
|
||||
.Take(1)
|
||||
.Take(accountsForOpening)
|
||||
.Select(a => a.Value)
|
||||
.ToArray();
|
||||
await OpenPositions(accounts, message, PositionType.Long, stops.stopLoss, stops.takeProfit, 1);
|
||||
|
@ -329,11 +468,11 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsLong, message.Price + stops.takeProfit, message.Time.AddMilliseconds(-RandomNumberGenerator.GetInt32(300, 1000)), message);
|
||||
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsLong, message.Price - stops.stopLoss, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(300, 1000)), message);
|
||||
}
|
||||
if (result[TradingEvent.DowntrendStart] >= Constants.UppingCoefficient
|
||||
if (result[TradingEvent.OpenShort] >= Constants.UppingCoefficient
|
||||
&& state == ExchangeState.Open
|
||||
)
|
||||
{
|
||||
var stops = GetStops(message, PositionType.Short);
|
||||
var stops = st.GetStops(PositionType.Long);
|
||||
if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase())
|
||||
{
|
||||
var accounts = _portfolioWrapper.Accounts
|
||||
|
@ -348,7 +487,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsShort, message.Price - stops.takeProfit, message.Time.AddMilliseconds(-RandomNumberGenerator.GetInt32(300, 1000)), message);
|
||||
await _tradeDataProvider.LogDeclision(DeclisionTradeAction.ResetStopsShort, message.Price + stops.stopLoss, message.Time.AddMilliseconds(RandomNumberGenerator.GetInt32(300, 1000)), message);
|
||||
}
|
||||
if (result[TradingEvent.UptrendEnd] >= Constants.UppingCoefficient * 10)
|
||||
if (result[TradingEvent.CloseLong] >= Constants.UppingCoefficient * 10)
|
||||
{
|
||||
if (!message.IsHistoricalData && BotModeSwitcher.CanSell())
|
||||
{
|
||||
|
@ -362,7 +501,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
|
||||
}
|
||||
|
||||
if (result[TradingEvent.DowntrendEnd] >= Constants.UppingCoefficient * 10)
|
||||
if (result[TradingEvent.CloseShort] >= Constants.UppingCoefficient * 10)
|
||||
{
|
||||
if (!message.IsHistoricalData && BotModeSwitcher.CanPurchase())
|
||||
{
|
||||
|
@ -409,11 +548,86 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
return res;
|
||||
}
|
||||
|
||||
private (decimal stopLoss, decimal takeProfit) GetStops(ITradeDataItem message, PositionType type)
|
||||
private Stops GetStops(ITradeDataItem message)
|
||||
{
|
||||
decimal stopLossShift = 2m;
|
||||
decimal takeProfitShift = 6;
|
||||
return (stopLossShift, takeProfitShift);
|
||||
decimal longStopLossShift = message.Value * 0.99m;
|
||||
decimal longTakeProfitShift = message.Value * 1.03m;
|
||||
decimal shortStopLossShift = message.Value * 0.99m;
|
||||
decimal shortTakeProfitShift = message.Value * 1.03m;
|
||||
if (SupportLevels.TryGetValue(message.Figi, out var levels))
|
||||
{
|
||||
if (levels.Length > 0)
|
||||
{
|
||||
var levelsByTime = levels.Where(l => l.LastLevelTime.HasValue)
|
||||
.OrderByDescending(l => l.LastLevelTime)
|
||||
.ToArray();
|
||||
if (message.Price >= levelsByTime[0].LowValue && message.Price < levelsByTime[0].HighValue)
|
||||
{
|
||||
longStopLossShift = message.Price - levelsByTime[0].LowValue;
|
||||
shortStopLossShift = message.Price + levelsByTime[0].HighValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
var levelsByDiff = levels.Where(l => l.LastLevelTime.HasValue)
|
||||
.OrderBy(l => System.Math.Abs(l.Value - message.Price))
|
||||
.ToArray();
|
||||
var nearestLevel = levelsByDiff[0];
|
||||
longStopLossShift = message.Price - nearestLevel.HighValue;
|
||||
shortStopLossShift = nearestLevel.LowValue - message.Price;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Stops(longStopLossShift, longTakeProfitShift, shortStopLossShift, shortTakeProfitShift);
|
||||
}
|
||||
|
||||
private static ImmutableDictionary<TradingEvent, decimal> ProcessStops(Stops stops, decimal meanfullLevel)
|
||||
{
|
||||
var res = TraderUtils.GetInitDict(1);
|
||||
if (stops.LongTakeProfitShift < meanfullLevel || stops.LongStopLossShift < meanfullLevel)
|
||||
{
|
||||
res[TradingEvent.OpenLong] = Constants.BlockingCoefficient;
|
||||
}
|
||||
if (stops.ShortTakeProfitShift < meanfullLevel || stops.ShortStopLossShift < meanfullLevel)
|
||||
{
|
||||
res[TradingEvent.OpenShort] = Constants.BlockingCoefficient;
|
||||
}
|
||||
|
||||
return res.ToImmutableDictionary();
|
||||
}
|
||||
|
||||
private async Task<ImmutableDictionary<TradingEvent, decimal>> ProcessSupportLevels(ITradeDataItem message)
|
||||
{
|
||||
var res = TraderUtils.GetInitDict(1);
|
||||
if (SupportLevels.TryGetValue(message.Figi, out var levels))
|
||||
{
|
||||
foreach (var lev in levels)
|
||||
{
|
||||
if (message.Price >= lev.LowValue && message.Price < lev.HighValue)
|
||||
{
|
||||
await _tradeDataProvider.LogPrice(message, "support_level", message.Price);
|
||||
}
|
||||
}
|
||||
|
||||
if (levels.Length > 0)
|
||||
{
|
||||
var levelsByTime = levels.Where(l => l.LastLevelTime.HasValue)
|
||||
.OrderByDescending(l => l.LastLevelTime)
|
||||
.ToArray();
|
||||
|
||||
if (message.Price >= levelsByTime[0].LowValue && message.Price < levelsByTime[0].HighValue)
|
||||
{
|
||||
if (message.Price > levelsByTime[0].Value)
|
||||
{
|
||||
res[TradingEvent.OpenLong] = Constants.BlockingCoefficient;
|
||||
}
|
||||
if (message.Price < levelsByTime[0].Value)
|
||||
{
|
||||
res[TradingEvent.OpenShort] = Constants.BlockingCoefficient;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res.ToImmutableDictionary();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,10 +86,12 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
}
|
||||
return ValueTask.FromResult(Array.Empty<ITradeDataItem>());
|
||||
}
|
||||
|
||||
public ValueTask<ITradeDataItem[]> GetDataFrom20SecondsWindowCache2(string figi, string key)
|
||||
{
|
||||
return GetDataForTimeWindow(figi, TimeSpan.FromSeconds(20), key);
|
||||
}
|
||||
|
||||
public ValueTask<ITradeDataItem[]> GetDataFrom1MinuteWindowCache2(string figi, string key)
|
||||
{
|
||||
return GetDataForTimeWindow(figi, TimeSpan.FromSeconds(60), key);
|
||||
|
@ -140,7 +142,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
|
|||
|
||||
if (_isDataRecievingAllowed)
|
||||
{
|
||||
var time = DateTime.UtcNow.AddHours(-1.5);
|
||||
var time = DateTime.UtcNow.AddHours(-5);
|
||||
using var context1 = await _dbContextFactory.CreateDbContextAsync();
|
||||
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||
var data = await context1.PriceChanges
|
||||
|
|
|
@ -4,33 +4,35 @@ using KLHZ.Trader.Core.Contracts.Messaging.Dtos.Interfaces;
|
|||
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
|
||||
using KLHZ.Trader.Core.Exchange.Interfaces;
|
||||
using KLHZ.Trader.Core.Exchange.Models.AssetsAccounting;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace KLHZ.Trader.Core.Exchange.Utils
|
||||
{
|
||||
internal static class TraderUtils
|
||||
{
|
||||
internal static Dictionary<TradingEvent, decimal> MergeResultsMult(Dictionary<TradingEvent, decimal> res, ImmutableDictionary<TradingEvent, decimal> data)
|
||||
internal static Dictionary<TradingEvent, decimal> MergeResultsMult(IDictionary<TradingEvent, decimal> result, IDictionary<TradingEvent, decimal> data)
|
||||
{
|
||||
foreach (var k in res.Keys)
|
||||
var res = new Dictionary<TradingEvent, decimal>();
|
||||
foreach (var k in result.Keys)
|
||||
{
|
||||
var valRes = res[k];
|
||||
var valRes = result[k];
|
||||
var valData = data[k];
|
||||
res[k] = valRes * valData;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
internal static Dictionary<TradingEvent, decimal> MergeResultsMax(Dictionary<TradingEvent, decimal> res, ImmutableDictionary<TradingEvent, decimal> data)
|
||||
internal static Dictionary<TradingEvent, decimal> MergeResultsMax(IDictionary<TradingEvent, decimal> result, IDictionary<TradingEvent, decimal> data)
|
||||
{
|
||||
foreach (var k in res.Keys)
|
||||
var res = new Dictionary<TradingEvent, decimal>();
|
||||
foreach (var k in result.Keys)
|
||||
{
|
||||
var valRes = res[k];
|
||||
var valData = data[k];
|
||||
var valRes = result[k];
|
||||
var valData = result[k];
|
||||
res[k] = System.Math.Max(valRes, valData);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
internal static Dictionary<TradingEvent, decimal> GetInitDict(decimal initValue)
|
||||
{
|
||||
var values = Enum.GetValues<TradingEvent>();
|
||||
|
|
|
@ -286,12 +286,9 @@ namespace KLHZ.Trader.Service.Controllers
|
|||
var leverage = 3;
|
||||
var hist = Statistics.CalcHistogram(prices);
|
||||
var convs = Statistics.CalcConvolution(hist, leverage).ToList();
|
||||
//var result = new List<ConvolutionResult>();
|
||||
//Statistics.MergeConvolutionResults(convs, result);
|
||||
var orderedConvs = convs.OrderByDescending(c => c.Sum).Take(5).ToList();
|
||||
orderedConvs = orderedConvs.OrderBy(c => c.Value).ToList();
|
||||
var values = new List<decimal>();
|
||||
var shifts = new List<decimal>();
|
||||
|
||||
foreach (var c in orderedConvs)
|
||||
{
|
||||
|
@ -354,7 +351,5 @@ namespace KLHZ.Trader.Service.Controllers
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue