@@ -10,4 +10,4 @@ docker build -t managing.api -f Managing.Api/Dockerfile . --no-cache
|
||||
docker build -t managing.api.workers -f Managing.Api.Workers/Dockerfile . --no-cache
|
||||
|
||||
# Start up the project using docker-compose
|
||||
docker-compose -f Managing.Docker/docker-compose.yml -f Managing.Docker/docker-compose.sandbox.yml up -d
|
||||
docker compose -f Managing.Docker/docker-compose.yml -f Managing.Docker/docker-compose.local.yml up -d
|
||||
@@ -198,7 +198,7 @@ public class BotController : ControllerBase
|
||||
{
|
||||
Status = item.GetStatus(),
|
||||
Name = item.GetName(),
|
||||
Candles = item.Candles.ToList(),
|
||||
Candles = item.OptimizedCandles.ToList(),
|
||||
Positions = item.Positions,
|
||||
Signals = item.Signals.ToList(),
|
||||
WinRate = item.GetWinRate(),
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Managing.Application.Tests
|
||||
// Act
|
||||
foreach (var candle in candles)
|
||||
{
|
||||
rsiStrategy.Candles.Add(candle);
|
||||
rsiStrategy.Candles.Enqueue(candle);
|
||||
var signals = rsiStrategy.Run();
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Managing.Application.Tests
|
||||
// Act
|
||||
foreach (var candle in candles)
|
||||
{
|
||||
rsiStrategy.Candles.Add(candle);
|
||||
rsiStrategy.Candles.Enqueue(candle);
|
||||
var signals = rsiStrategy.Run();
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace Managing.Application.Tests
|
||||
// Act
|
||||
foreach (var candle in candles)
|
||||
{
|
||||
rsiStrategy.Candles.Add(candle);
|
||||
rsiStrategy.Candles.Enqueue(candle);
|
||||
var signals = rsiStrategy.Run();
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace Managing.Application.Tests
|
||||
// Act
|
||||
foreach (var candle in candles)
|
||||
{
|
||||
superTrendStrategy.Candles.Add(candle);
|
||||
superTrendStrategy.Candles.Enqueue(candle);
|
||||
var signals = superTrendStrategy.Run();
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace Managing.Application.Tests
|
||||
// Act
|
||||
foreach (var candle in candles)
|
||||
{
|
||||
chandelierExitStrategy.Candles.Add(candle);
|
||||
chandelierExitStrategy.Candles.Enqueue(candle);
|
||||
var signals = chandelierExitStrategy.Run();
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace Managing.Application.Tests
|
||||
// Act
|
||||
foreach (var candle in candles)
|
||||
{
|
||||
emaTrendSrategy.Candles.Add(candle);
|
||||
emaTrendSrategy.Candles.Enqueue(candle);
|
||||
var signals = emaTrendSrategy.Run();
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ namespace Managing.Application.Tests
|
||||
// Act
|
||||
foreach (var candle in candles)
|
||||
{
|
||||
stochRsiStrategy.Candles.Add(candle);
|
||||
stochRsiStrategy.Candles.Enqueue(candle);
|
||||
var signals = stochRsiStrategy.Run();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Core.FixedSizedQueue;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Strategies;
|
||||
@@ -11,6 +12,7 @@ namespace Managing.Application.Abstractions
|
||||
{
|
||||
HashSet<Signal> Signals { get; set; }
|
||||
List<Position> Positions { get; set; }
|
||||
FixedSizeQueue<Candle> OptimizedCandles { get; set; }
|
||||
HashSet<Candle> Candles { get; set; }
|
||||
Timeframe Timeframe { get; set; }
|
||||
HashSet<IStrategy> Strategies { get; set; }
|
||||
|
||||
@@ -142,10 +142,12 @@ namespace Managing.Application.Backtesting
|
||||
bot.WalletBalances.Add(candles.FirstOrDefault().Date, balance);
|
||||
foreach (var candle in candles)
|
||||
{
|
||||
bot.Candles.Add(candle);
|
||||
bot.OptimizedCandles.Enqueue(candle);
|
||||
bot.Run();
|
||||
}
|
||||
|
||||
bot.Candles = new HashSet<Candle>(candles);
|
||||
|
||||
var finalPnl = bot.GetProfitAndLoss();
|
||||
var winRate = bot.GetWinRate();
|
||||
var optimizedMoneyManagement = TradingBox.GetBestMoneyManagement(candles, bot.Positions, moneyManagement);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Trading;
|
||||
using Managing.Application.Trading.Commands;
|
||||
using Managing.Core.FixedSizedQueue;
|
||||
using Managing.Domain.Accounts;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.Candles;
|
||||
@@ -27,6 +28,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
|
||||
public Account Account { get; set; }
|
||||
public HashSet<IStrategy> Strategies { get; set; }
|
||||
public FixedSizeQueue<Candle> OptimizedCandles { get; set; }
|
||||
public HashSet<Candle> Candles { get; set; }
|
||||
public HashSet<Signal> Signals { get; set; }
|
||||
public List<Position> Positions { get; set; }
|
||||
@@ -80,6 +82,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
|
||||
Strategies = new HashSet<IStrategy>();
|
||||
Signals = new HashSet<Signal>();
|
||||
OptimizedCandles = new FixedSizeQueue<Candle>(600);
|
||||
Candles = new HashSet<Candle>();
|
||||
Positions = new List<Position>();
|
||||
WalletBalances = new Dictionary<DateTime, decimal>();
|
||||
@@ -160,13 +163,13 @@ public class TradingBot : Bot, ITradingBot
|
||||
Logger.LogInformation(
|
||||
$"Time : {DateTime.Now} - Server time {DateTime.Now.ToUniversalTime()} - Bot : {Name} - Type {BotType} - Ticker : {Ticker}");
|
||||
|
||||
var previousCandleCount = Candles.Count;
|
||||
var previousCandleCount = OptimizedCandles.Count;
|
||||
|
||||
if (!IsForBacktest)
|
||||
await UpdateCandles();
|
||||
|
||||
if (Candles.Count > previousCandleCount || IsForBacktest)
|
||||
await UpdateSignals(Candles);
|
||||
if (OptimizedCandles.Count > previousCandleCount || IsForBacktest)
|
||||
await UpdateSignals(OptimizedCandles);
|
||||
else
|
||||
Logger.LogInformation($"No need to update signals for {Ticker}");
|
||||
|
||||
@@ -177,35 +180,38 @@ public class TradingBot : Bot, ITradingBot
|
||||
SaveBackup();
|
||||
|
||||
await UpdateWalletBalances();
|
||||
Logger.LogInformation($"Candles : {Candles.Count}");
|
||||
if (OptimizedCandles.Count % 100 == 0) // Log every 10th execution
|
||||
{
|
||||
Logger.LogInformation($"Candle date : {OptimizedCandles.Last().Date:u}");
|
||||
Logger.LogInformation($"Signals : {Signals.Count}");
|
||||
Logger.LogInformation($"ExecutionCount : {ExecutionCount}");
|
||||
Logger.LogInformation($"Positions : {Positions.Count}");
|
||||
Logger.LogInformation("__________________________________________________");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PreloadCandles()
|
||||
{
|
||||
if (Candles.Any())
|
||||
if (OptimizedCandles.Any())
|
||||
return;
|
||||
|
||||
var candles = await ExchangeService.GetCandlesInflux(Account.Exchange, Ticker, PreloadSince, Timeframe);
|
||||
|
||||
foreach (var candle in candles.Where(c => c.Date < DateTime.Now.ToUniversalTime()))
|
||||
{
|
||||
if (!Candles.Any(c => c.Date == candle.Date))
|
||||
if (!OptimizedCandles.Any(c => c.Date == candle.Date))
|
||||
{
|
||||
Candles.Add(candle);
|
||||
await UpdateSignals(Candles);
|
||||
OptimizedCandles.Enqueue(candle);
|
||||
await UpdateSignals(OptimizedCandles);
|
||||
}
|
||||
}
|
||||
|
||||
PreloadedCandlesCount = Candles.Count();
|
||||
PreloadedCandlesCount = OptimizedCandles.Count();
|
||||
}
|
||||
|
||||
private async Task UpdateSignals(HashSet<Candle> candles)
|
||||
private async Task UpdateSignals(FixedSizeQueue<Candle> candles)
|
||||
{
|
||||
var signal = TradingBox.GetSignal(candles, Strategies, Signals);
|
||||
var signal = TradingBox.GetSignal(candles.ToHashSet(), Strategies, Signals);
|
||||
|
||||
if (signal == null) return;
|
||||
|
||||
@@ -236,15 +242,15 @@ public class TradingBot : Bot, ITradingBot
|
||||
|
||||
protected async Task UpdateCandles()
|
||||
{
|
||||
if (Candles.Count == 0 || ExecutionCount == 0)
|
||||
if (OptimizedCandles.Count == 0 || ExecutionCount == 0)
|
||||
return;
|
||||
|
||||
var lastCandle = Candles.Last();
|
||||
var lastCandle = OptimizedCandles.Last();
|
||||
var newCandle = await ExchangeService.GetCandlesInflux(Account.Exchange, Ticker, lastCandle.Date, Timeframe);
|
||||
|
||||
foreach (var candle in newCandle.Where(c => c.Date < DateTime.Now.ToUniversalTime()))
|
||||
{
|
||||
Candles.Add(candle);
|
||||
OptimizedCandles.Enqueue(candle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,17 +275,25 @@ public class TradingBot : Bot, ITradingBot
|
||||
|
||||
private async Task UpdateWalletBalances()
|
||||
{
|
||||
var lastCandle = OptimizedCandles.LastOrDefault();
|
||||
if (lastCandle == null) return;
|
||||
|
||||
var date = lastCandle.Date;
|
||||
|
||||
if (WalletBalances.Count == 0)
|
||||
{
|
||||
WalletBalances.Add(Candles.LastOrDefault().Date, await ExchangeService.GetBalance(Account, IsForBacktest));
|
||||
WalletBalances[date] = await ExchangeService.GetBalance(Account, IsForBacktest);
|
||||
return;
|
||||
}
|
||||
else if (!WalletBalances.Any(w => w.Key == Candles.LastOrDefault().Date))
|
||||
|
||||
if (!WalletBalances.ContainsKey(date))
|
||||
{
|
||||
var walletBalance = WalletBalances.FirstOrDefault().Value + GetProfitAndLoss();
|
||||
WalletBalances.Add(Candles.LastOrDefault().Date, walletBalance);
|
||||
var previousBalance = WalletBalances.First().Value;
|
||||
WalletBalances[date] = previousBalance + GetProfitAndLoss();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task UpdatePosition(Signal signal, Position positionForSignal)
|
||||
{
|
||||
try
|
||||
@@ -293,7 +307,6 @@ public class TradingBot : Bot, ITradingBot
|
||||
if (position.Status == (PositionStatus.Finished | PositionStatus.Flipped))
|
||||
{
|
||||
await HandleClosedPosition(positionForSignal);
|
||||
return;
|
||||
}
|
||||
else if (position.Status == PositionStatus.Filled)
|
||||
{
|
||||
@@ -301,7 +314,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
// check if position is still open
|
||||
// Check status, if still open update the status of the position
|
||||
var lastCandle = IsForBacktest
|
||||
? Candles.Last()
|
||||
? OptimizedCandles.Last()
|
||||
: ExchangeService.GetCandle(Account, Ticker, DateTime.UtcNow);
|
||||
|
||||
if (positionForSignal.OriginDirection == TradeDirection.Long)
|
||||
@@ -401,7 +414,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
&& p.SignalIdentifier != signal.Identifier);
|
||||
|
||||
var lastPrice = IsForBacktest
|
||||
? Candles.Last().Close
|
||||
? OptimizedCandles.Last().Close
|
||||
: ExchangeService.GetPrice(Account, Ticker, DateTime.UtcNow);
|
||||
|
||||
// If position open
|
||||
@@ -511,7 +524,7 @@ public class TradingBot : Bot, ITradingBot
|
||||
if (lastPosition == null)
|
||||
return true;
|
||||
|
||||
var tenCandleAgo = Candles.TakeLast(10).First();
|
||||
var tenCandleAgo = OptimizedCandles.TakeLast(10).First();
|
||||
var positionSignal = Signals.FirstOrDefault(s => s.Identifier == lastPosition.SignalIdentifier);
|
||||
|
||||
return positionSignal.Date < tenCandleAgo.Date;
|
||||
@@ -543,8 +556,8 @@ public class TradingBot : Bot, ITradingBot
|
||||
try
|
||||
{
|
||||
var closedPosition =
|
||||
await (new ClosePositionCommandHandler(ExchangeService, AccountService, TradingService))
|
||||
.Handle(command);
|
||||
(new ClosePositionCommandHandler(ExchangeService, AccountService, TradingService)
|
||||
.Handle(command)).Result;
|
||||
|
||||
if (closedPosition.Status == (PositionStatus.Finished | PositionStatus.Flipped))
|
||||
{
|
||||
|
||||
13
src/Managing.Core/FixedSizedQueue/FixedSizeQueue.cs
Normal file
13
src/Managing.Core/FixedSizedQueue/FixedSizeQueue.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace Managing.Core.FixedSizedQueue;
|
||||
|
||||
public class FixedSizeQueue<T> : Queue<T>
|
||||
{
|
||||
private readonly int _maxSize;
|
||||
public FixedSizeQueue(int maxSize) => _maxSize = maxSize;
|
||||
|
||||
public new void Enqueue(T item)
|
||||
{
|
||||
while (Count >= _maxSize) Dequeue();
|
||||
base.Enqueue(item);
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,10 @@ public static class TradingBox
|
||||
HashSet<Signal> previousSignal)
|
||||
{
|
||||
var signalOnCandles = new HashSet<Signal>();
|
||||
var limitedCandles = newCandles.ToList().TakeLast(600).ToList();
|
||||
foreach (var strategy in strategies)
|
||||
{
|
||||
strategy.UpdateCandles(newCandles);
|
||||
strategy.UpdateCandles(limitedCandles.ToHashSet());
|
||||
var signals = strategy.Run();
|
||||
|
||||
if (signals == null || signals.Count == 0) continue;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Core;
|
||||
using static Managing.Common.Enums;
|
||||
using Managing.Core.FixedSizedQueue;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Scenarios;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Strategies
|
||||
{
|
||||
@@ -11,14 +11,14 @@ namespace Managing.Domain.Strategies
|
||||
{
|
||||
Name = name;
|
||||
Timeframe = timeframe;
|
||||
Candles = new List<Candle>();
|
||||
Candles = new FixedSizeQueue<Candle>(600);
|
||||
Type = type;
|
||||
SignalType = ScenarioHelpers.GetSignalType(type);
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public Timeframe Timeframe { get; set; }
|
||||
public List<Candle> Candles { get; set; }
|
||||
public FixedSizeQueue<Candle> Candles { get; set; }
|
||||
public StrategyType Type { get; set; }
|
||||
public SignalType SignalType { get; set; }
|
||||
public int MinimumHistory { get; set; }
|
||||
@@ -49,7 +49,7 @@ namespace Managing.Domain.Strategies
|
||||
{
|
||||
if (Candles.All(c => c.Date != item.Date))
|
||||
{
|
||||
Candles.AddItem(item);
|
||||
Candles.Enqueue(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user