docker files fixes from liaqat

This commit is contained in:
alirehmani
2024-05-03 16:39:25 +05:00
commit 464a8730e8
587 changed files with 44288 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
using Managing.Application.Abstractions.Services;
using Managing.Application.Abstractions;
using Moq;
using Managing.Domain.MoneyManagements;
using static Managing.Common.Enums;
using Managing.Domain.Accounts;
namespace Managing.Application.Tests;
public class BaseTests
{
public readonly Mock<IMoneyManagementService> _moneyManagementService;
public readonly Mock<IAccountService> _accountService;
public readonly IExchangeService _exchangeService;
public readonly Mock<ITradingService> _tradingService;
public readonly Account Account;
public readonly MoneyManagement MoneyManagement;
public const string PublicAddress = "0x0425dEAb364E9121F7CA284129dA854FD5cF22eD";
public const string PrivateKey = "";
public BaseTests()
{
_accountService = new Mock<IAccountService>();
_moneyManagementService = new Mock<IMoneyManagementService>();
MoneyManagement = new MoneyManagement()
{
BalanceAtRisk = 0.30m,
Leverage = 2,
Timeframe = Timeframe.FifteenMinutes,
StopLoss = 0.008m,
TakeProfit = 0.02m,
Name = "Default MM"
};
_ =_moneyManagementService.Setup(m => m.GetMoneyMangement(It.IsAny<string>())).Returns(Task.FromResult(MoneyManagement));
Account = new Account()
{
Exchange = TradingExchanges.Evm,
Key = PublicAddress,
Secret = PrivateKey,
};
_accountService.Setup(a => a.GetAccount(It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>()))
.Returns(Task.FromResult(Account));
_tradingService = new Mock<ITradingService>();
_exchangeService = TradingBaseTests.GetExchangeService();
}
}

View File

@@ -0,0 +1,408 @@
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Application.Backtesting;
using Managing.Application.Bots.Base;
using Managing.Domain.MoneyManagements;
using Managing.Domain.Scenarios;
using Moq;
using System.Collections;
using System.Diagnostics;
using Xunit;
using static Managing.Common.Enums;
namespace Managing.Application.Tests
{
public class BotsTests : BaseTests
{
private readonly IBotFactory _botFactory;
private readonly IBacktester _backtester;
private readonly string _reportPath = "D:\\BacktestingReports\\backtesting.csv";
private string _analysePath = "D:\\BacktestingReports\\analyse";
private readonly string _errorsPath = "D:\\BacktestingReports\\errorsAnalyse.csv";
private readonly string _s = "|";
private List<double> _elapsedTimes { get; set; }
public BotsTests() : base ()
{
var backtestRepository = new Mock<IBacktestRepository>().Object;
var discordService = new Mock<IMessengerService>().Object;
var tradingBotLogger = TradingBaseTests.CreateTradingBotLogger();
var backtestLogger = TradingBaseTests.CreateBacktesterLogger();
_botFactory = new BotFactory(
_exchangeService,
tradingBotLogger,
_moneyManagementService.Object,
discordService,
_accountService.Object,
_tradingService.Object);
_backtester = new Backtester(_exchangeService, _botFactory, backtestRepository, backtestLogger);
_elapsedTimes = new List<double>();
}
[Theory]
[InlineData(Ticker.BTC, Timeframe.OneDay, -100)]
public void SwingBot_Should_Return_Positiv_Profit_For_Every_Position(Ticker ticker, Timeframe timeframe, int days)
{
// Arrange
var scenario = new Scenario("ScalpingScenario");
var strategy = ScenarioHelpers.BuildStrategy(StrategyType.RsiDivergence, timeframe, "RsiDiv", period: 14);
scenario.AddStrategy(strategy);
// Act
var backtestResult = _backtester.RunFlippingBotBacktest(Account, MoneyManagement, ticker, scenario, timeframe, Convert.ToDouble(days*2), 1000);
WriteCsvReport(backtestResult.GetStringReport());
// Assert
Assert.True(backtestResult.FinalPnl > 0);
Assert.True(backtestResult.WinRate >= 30);
Assert.True(backtestResult.GrowthPercentage > backtestResult.HodlPercentage);
}
[Theory]
//[InlineData(Enums.Exchanges.Binance, "ADAUSDT", Timeframe.ThirtyMinutes, -5)]
//[InlineData(Enums.Exchanges.Binance, "ADAUSDT", Timeframe.FifteenMinutes, -5)]
//[InlineData(Enums.Exchanges.Binance, "SOLUSDT", Timeframe.ThirtyMinutes, -4)]
//[InlineData(Enums.Exchanges.Binance, "SOLUSDT", Timeframe.FifteenMinutes, -4)]
//[InlineData(Enums.Exchanges.Binance, "BTCUSDT", Timeframe.ThirtyMinutes, -4)]
//[InlineData(Enums.Exchanges.Binance, "BTCUSDT", Timeframe.FifteenMinutes, -4)]
[InlineData(Ticker.BTC, Timeframe.FifteenMinutes, -14)]
public void ScalpingBot_Should_Return_Positiv_Profit_For_Every_Position(Ticker ticker, Timeframe timeframe, int days)
{
// Arrange
var scenario = new Scenario("ScalpingScenario");
var strategy = ScenarioHelpers.BuildStrategy(StrategyType.RsiDivergence, timeframe, "RsiDiv", period: 5);
scenario.AddStrategy(strategy);
// Act
var backtestResult = _backtester.RunScalpingBotBacktest(Account, MoneyManagement, ticker, scenario, timeframe, Convert.ToDouble(days), 1000);
//WriteCsvReport(backtestResult.GetStringReport());
// Assert
Assert.True(backtestResult.FinalPnl > 0);
Assert.True(backtestResult.WinRate >= 30);
Assert.True(backtestResult.GrowthPercentage > backtestResult.HodlPercentage);
}
[Theory]
[InlineData(Ticker.BTC, Timeframe.FifteenMinutes, -8)]
public void MacdCross_Should_Return_Positiv_Profit_For_Every_Position(Ticker ticker, Timeframe timeframe, int days)
{
// Arrange
var scenario = new Scenario("ScalpingScenario");
var strategy = ScenarioHelpers.BuildStrategy(StrategyType.MacdCross, timeframe, "RsiDiv", fastPeriods: 12, slowPeriods: 26, signalPeriods: 9);
scenario.AddStrategy(strategy);
var moneyManagement = new MoneyManagement()
{
BalanceAtRisk = 0.05m,
Leverage = 1,
Timeframe = timeframe,
StopLoss = 0.01m,
TakeProfit = 0.02m
};
// Act
var backtestResult = _backtester.RunScalpingBotBacktest(Account, moneyManagement, ticker, scenario, timeframe, Convert.ToDouble(days), 1000);
WriteCsvReport(backtestResult.GetStringReport());
// Assert
Assert.True(backtestResult.FinalPnl > 0);
Assert.True(backtestResult.WinRate >= 30);
Assert.True(backtestResult.GrowthPercentage > backtestResult.HodlPercentage);
}
[Theory]
[InlineData(Timeframe.FifteenMinutes, -6, StrategyType.RsiDivergenceConfirm, BotType.ScalpingBot)]
//[InlineData(Timeframe.FifteenMinutes, -6, Enums.StrategyType.RsiDivergenceConfirm, Enums.BotType.FlippingBot)]
public void GetBestPeriodRsiForDivergenceFlippingBot(Timeframe timeframe, int days, StrategyType strategyType, BotType botType)
{
var result = new List<Tuple<string, int, decimal, decimal, decimal, decimal>>();
var errors = new List<string>();
var options = new ParallelOptions()
{
MaxDegreeOfParallelism = 4
};
var periodRange = new List<int>() { 2, 7};
var stopLossRange = new List<decimal>() { 0.005m, 0.05m, 0.005m };
var takeProfitRange = new List<decimal>() { 0.01m, 0.1m, 0.02m };
var fileIdentifier = $"{strategyType}-{timeframe}";
var completedTest = 0;
var totalTests = GetTotalTrades(periodRange, stopLossRange, takeProfitRange) * Enum.GetNames(typeof(Ticker)).Length;
CleanAnalyseFile(fileIdentifier);
UpdateProgression(totalTests, completedTest);
Parallel.ForEach((Ticker[])Enum.GetValues(typeof(Ticker)), options, ticker => {
var candles = _exchangeService.GetCandles(Account, ticker, DateTime.Now.AddDays(Convert.ToDouble(days)), timeframe).Result;
if (candles == null || candles.Count == 0)
return;
Parallel.For(periodRange[0], periodRange[1], options, i => {
var scenario = new Scenario("ScalpingScenario");
var strategy = ScenarioHelpers.BuildStrategy(strategyType, timeframe, "RsiDiv", period: i);
scenario.AddStrategy(strategy);
// -0.5 to -5
for (decimal s = stopLossRange[0]; s < stopLossRange[1]; s += stopLossRange[2])
{
// +1% to +10% in 1%
for(decimal t = takeProfitRange[0]; t < takeProfitRange[1]; t += takeProfitRange[2])
{
var moneyManagement = new MoneyManagement()
{
BalanceAtRisk = 0.05m,
Leverage = 1,
Timeframe = timeframe,
StopLoss = s,
TakeProfit = t
};
try
{
var timer = new Stopwatch();
timer.Start();
var backtestResult = botType switch
{
BotType.SimpleBot => throw new NotImplementedException(),
BotType.ScalpingBot => _backtester.RunScalpingBotBacktest(Account, moneyManagement, scenario, timeframe, candles, 1000),
BotType.FlippingBot => _backtester.RunFlippingBotBacktest(Account, moneyManagement, scenario, timeframe, candles, 1000),
_ => throw new NotImplementedException(),
};
timer.Stop();
if (backtestResult.FinalPnl > 0
&& (backtestResult.GrowthPercentage - backtestResult.HodlPercentage) > 30
&& backtestResult.Statistics.MaxDrawdown < 3)
{
var currentResult = new Tuple<string, int, decimal, decimal, decimal, decimal>(ticker.ToString(), i,
backtestResult.FinalPnl, s, t, backtestResult.GrowthPercentage - backtestResult.HodlPercentage);
result.Add(currentResult);
}
completedTest++;
UpdateProgression(totalTests, completedTest, timer.Elapsed.TotalSeconds);
}
catch (Exception ex)
{
completedTest++;
errors.Add($"{ticker}{_s}{i}{_s}{s}{_s}{t}{_s}{ex.Message}");
}
}
}
});
});
foreach (var r in result)
{
WriteCsvAnalyse($"{r.Item1}{_s}{r.Item2}{_s}{r.Item3.ToString("0.000")}{_s}{r.Item4 * 100}{_s}{r.Item5 * 100}{_s}{r.Item6}");
}
foreach (var e in errors)
{
WriteCsvErrors(e);
}
var bestResult = result.OrderByDescending(b => b.Item3).FirstOrDefault();
WriteCsvAnalyse($"Best result : {bestResult.Item1} - Rsi Period : {bestResult.Item2} - {bestResult.Item3} - SL : {bestResult.Item4}% - TP : {bestResult.Item5}%");
Assert.True(result.Any());
}
[Theory]
[InlineData(Timeframe.OneHour, -30, StrategyType.MacdCross, BotType.FlippingBot)]
[InlineData(Timeframe.OneHour, -30, StrategyType.MacdCross, BotType.ScalpingBot)]
public void GetBestMMForMacdFlippingBot(Timeframe timeframe, int days, StrategyType strategyType, BotType botType)
{
var result = new List<Tuple<string, decimal, decimal, decimal, decimal>>();
var errors = new List<string>();
var options = new ParallelOptions()
{
MaxDegreeOfParallelism = 4
};
var stopLossRange = new List<decimal>() { 0.005m, 0.05m, 0.005m };
var takeProfitRange = new List<decimal>() { 0.01m, 0.1m, 0.02m };
var fileIdentifier = $"{strategyType}-{timeframe}-{botType}";
var completedTest = 0;
var totalTests = GetTotalTradeForStopLossTakeProfit(stopLossRange, takeProfitRange) * Enum.GetNames(typeof(Ticker)).Length;
CleanAnalyseFile(fileIdentifier);
UpdateProgression(totalTests, completedTest);
Parallel.ForEach((Ticker[])Enum.GetValues(typeof(Ticker)), options, ticker => {
var candles = _exchangeService.GetCandles(Account, ticker, DateTime.Now.AddDays(Convert.ToDouble(days)), timeframe).Result;
if (candles == null || candles.Count == 0)
return;
var scenario = new Scenario("ScalpingScenario");
var strategy = ScenarioHelpers.BuildStrategy(strategyType, timeframe, "RsiDiv", fastPeriods: 12, slowPeriods: 26, signalPeriods: 9);
scenario.AddStrategy(strategy);
// -0.5 to -5
for (decimal s = stopLossRange[0]; s < stopLossRange[1]; s += stopLossRange[2])
{
// +1% to +10% in 1%
for (decimal t = takeProfitRange[0]; t < takeProfitRange[1]; t += takeProfitRange[2])
{
var timer = new Stopwatch();
timer.Start();
try
{
var moneyManagement = new MoneyManagement()
{
BalanceAtRisk = 0.05m,
Leverage = 1,
Timeframe = timeframe,
StopLoss = s,
TakeProfit = t
};
var backtestResult = botType switch
{
BotType.SimpleBot => throw new NotImplementedException(),
BotType.ScalpingBot => _backtester.RunScalpingBotBacktest(Account, moneyManagement, scenario, timeframe, candles, 1000),
BotType.FlippingBot => _backtester.RunFlippingBotBacktest(Account, moneyManagement, scenario, timeframe, candles, 1000),
_ => throw new NotImplementedException(),
};
if (backtestResult.FinalPnl > 0
&& (backtestResult.GrowthPercentage - backtestResult.HodlPercentage) > 30
&& backtestResult.Statistics.MaxDrawdown < 3)
{
var currentResult = new Tuple<string, decimal, decimal, decimal, decimal>(ticker.ToString(),
backtestResult.FinalPnl, s, t, backtestResult.GrowthPercentage - backtestResult.HodlPercentage);
result.Add(currentResult);
}
completedTest++;
UpdateProgression(totalTests, completedTest, timer.Elapsed.TotalSeconds);
}
catch (Exception ex)
{
completedTest++;
errors.Add($"{ticker}{_s}{s}{_s}{t}{_s}{ex.Message}");
}
timer.Stop();
}
}
});
foreach (var r in result)
{
WriteCsvAnalyse($"{r.Item1}{_s}{r.Item2.ToString("0.000")}{_s}{r.Item3 * 100}{_s}{r.Item4 * 100}{_s}{r.Item5}");
}
foreach (var e in errors)
{
WriteCsvErrors(e);
}
var bestResult = result.OrderByDescending(b => b.Item3).FirstOrDefault();
WriteCsvAnalyse($"Best result : {bestResult.Item1} - Rsi Period : {bestResult.Item2} - {bestResult.Item3} - SL : {bestResult.Item4}% - TP : {bestResult.Item5}%");
Assert.True(result.Any());
}
private void WriteCsvReport(string line)
{
File.AppendAllLines(_reportPath, new[] { line });
}
private void WriteCsvAnalyse(string line, string fileIdentifier = null)
{
if (!string.IsNullOrEmpty(fileIdentifier))
_analysePath += $"-{fileIdentifier}-{DateTime.Now.ToString("dd-MM-HH-mm")}.csv";
File.AppendAllLines(_analysePath , new[] { line });
}
private void WriteCsvErrors(string line)
{
File.AppendAllLines(_errorsPath, new[] { line });
}
private void CleanAnalyseFile(string fileIdentifier)
{
WriteCsvAnalyse("", fileIdentifier);
}
private decimal GetTotalTrades(List<int> periodRange, List<decimal> stopLossRange, List<decimal> takeProfitRange)
{
var stopLossRangeTotalTest = stopLossRange[1] / stopLossRange[2];
var takeProfitRangeTotalTest = takeProfitRange[1] / takeProfitRange[2];
var totalTrades = GetTotalTradeForStopLossTakeProfit(stopLossRange, takeProfitRange) * stopLossRangeTotalTest * takeProfitRangeTotalTest;
return totalTrades;
}
private decimal GetTotalTradeForStopLossTakeProfit(List<decimal> stopLossRange, List<decimal> takeProfitRange)
{
var stopLossRangeTotalTest = stopLossRange[1] / stopLossRange[2];
var takeProfitRangeTotalTest = takeProfitRange[1] / takeProfitRange[2];
var totalTrades = stopLossRangeTotalTest * takeProfitRangeTotalTest;
return totalTrades;
}
private void UpdateProgression(decimal totalTest, int completedTest, double? elapsed = null)
{
var timeRemaining = "";
if (elapsed.HasValue && completedTest > 0)
{
//_elapsedTimes.Add((elapsed.Value / completedTest) * ((double)totalTest - completedTest));
_elapsedTimes.Add(elapsed.Value);
//var t = (_elapsedTimes.Average() / completedTest) * ((double)totalTest - completedTest);
var t = (_elapsedTimes.Average() * (double)(totalTest - completedTest));
timeRemaining = $" Remaining time: {t} seconds - Estimated end date: {DateTime.Now.AddSeconds(t)}";
}
ModifyFirstRow(_analysePath, $"{completedTest}/{totalTest}{timeRemaining}");
}
private void ModifyFirstRow(string filepath, string newValue)
{
ArrayList rows = new ArrayList();
using (StreamReader reader = new StreamReader(filepath))
{
string row = null;
while ((row = reader.ReadLine()) != null)
{
rows.Add(row);
}
}
// Determ if the file even contains rows.
if (rows.Count > 0)
{
// Replace the first row.
rows[0] = newValue;
}
else
{
// Add the new value because there
// where no rows found in the file.
rows.Add(newValue);
}
// Write the modified content to the file.
using (StreamWriter writer = new StreamWriter(filepath, false))
{
foreach (String row in rows)
{
writer.WriteLine(row);
}
}
}
}
}

View File

@@ -0,0 +1,20 @@
using Xunit;
namespace Managing.Application.Tests
{
public class CandleHelpersTests
{
[Fact]
public void Shoud_Result_Correct_Range_Date()
{
// Arrange
var expectedDate = DateTime.Now.AddMinutes(-15*5);
var currentCandleDate = DateTime.Now;
var previousCandleDate = DateTime.Now.AddMinutes(-15);
// Act
//var result = CandleHelpers.GetRangeDateFromTimeframe
// Assert
}
}
}

View File

@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<PlatformTarget>AnyCPU</PlatformTarget>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.10" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
<PackageReference Include="Microsoft.TestPlatform.AdapterUtilities" Version="17.7.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Managing.Api\Managing.Api.csproj" />
<ProjectReference Include="..\Managing.Application\Managing.Application.csproj" />
<ProjectReference Include="..\Managing.Common\Managing.Common.csproj" />
<ProjectReference Include="..\Managing.Core\Managing.Core.csproj" />
<ProjectReference Include="..\Managing.Domain\Managing.Domain.csproj" />
<ProjectReference Include="..\Managing.Infrastructure.Exchanges\Managing.Infrastructure.Exchanges.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,24 @@
using Managing.Core;
using Xunit;
namespace Managing.Application.Tests
{
public class MathHelpersTests
{
[Theory]
[InlineData(0.00010, 4)]
public void Should_Return_Correct_Precision(decimal n, int expectedValue)
{
var precision = MathHelpers.GetDecimalPlaces(n);
Assert.Equal(expectedValue, precision);
}
[Theory]
[InlineData("0.00010", 4)]
public void Should_Return_Correct_PrecisionTest(string s, int expectedValue)
{
var precision = MathHelpers.GetDecimalPlaces(s);
Assert.Equal(expectedValue, precision);
}
}
}

View File

@@ -0,0 +1,56 @@
using Managing.Application.Trading;
using Managing.Application.Trading.Commands;
using Managing.Domain.Trades;
using Moq;
using Xunit;
using static Managing.Common.Enums;
namespace Managing.Application.Tests;
public class PositionTests : BaseTests
{
public PositionTests() : base()
{
}
[Fact]
public async void Should_Open_Position()
{
var command = new OpenPositionRequest(
"test",
MoneyManagement,
TradeDirection.Short,
Ticker.BTC,
PositionInitiator.User,
DateTime.UtcNow,
isForPaperTrading: false);
var handler = new OpenPositionCommandHandler(
_exchangeService,
_accountService.Object,
_tradingService.Object);
var position = await handler.Handle(command);
Assert.NotNull(position);
}
[Fact]
public async void Shoud_Close_Position()
{
var openTrade = await _exchangeService.GetTrade(Account, "", Ticker.BTC);
var position = new Position("", TradeDirection.Long, Ticker.BTC, MoneyManagement, PositionInitiator.User, DateTime.UtcNow)
{
Open = openTrade
};
var command = new ClosePositionCommand(position);
_ = _tradingService.Setup(m => m.GetPositionByIdentifier(It.IsAny<string>())).Returns(position);
var handler = new ClosePositionCommandHandler(
_exchangeService,
_accountService.Object,
_tradingService.Object);
var closedPosition = await handler.Handle(command);
Assert.NotNull(closedPosition);
}
}

View File

@@ -0,0 +1,251 @@
using Managing.Domain.Trades;
using Xunit;
using static Managing.Common.Enums;
namespace Managing.Application.Tests
{
public class ProfitAndLossTests
{
[Theory]
[InlineData(1, 100, 110, 10)]
[InlineData(2, 100, 110, 20)]
public void Should_Return_Correct_ProfitAndLoss_Amount(decimal quantity, decimal price, decimal exitPrice, decimal expectedResult)
{
// Arrange
var init = new List<Tuple<decimal, decimal>>();
init.Add(new Tuple<decimal, decimal>(quantity, price));
var pnl = new ProfitAndLoss(init, TradeDirection.Long);
// Act
var result = pnl.FloatingForTheoriticalExit(exitPrice);
// Assert
Assert.Equal(expectedResult, result);
}
[Fact]
public void Should_Return_Correct_Pnl_For_Short_failed_Position_After_Took_One_Profit()
{
// Arrange
var position = GetFakeShortPosition();
// Setup Open and first take profit
var orders = new List<Tuple<decimal, decimal>>
{
new Tuple<decimal, decimal>(position.Open.Quantity, position.Open.Price),
new Tuple<decimal, decimal>(-position.TakeProfit1.Quantity, position.TakeProfit1.Price)
};
// Act
position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection);
// Trigger Stop Loss
position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, position.OriginDirection);
// Assert
Assert.Equal(20, position.ProfitAndLoss.Realized);
}
[Fact]
public void Should_Return_Correct_Pnl_For_Long_Solana_failed_Position_After_Took_One_Profit()
{
// Arrange
var position = GetSolanaLongPosition();
// Setup Open and first take profit
var orders = new List<Tuple<decimal, decimal>>
{
new Tuple<decimal, decimal>(position.Open.Quantity, position.Open.Price),
new Tuple<decimal, decimal>(-position.TakeProfit1.Quantity, position.TakeProfit1.Price)
};
// Act
position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection);
// Trigger Stop Loss
position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, position.OriginDirection);
// Assert
Assert.Equal(3.97005582759999752M, position.ProfitAndLoss.Realized);
}
[Fact]
public void Should_Return_Correct_Pnl_For_Long_Solana_failed_Position_After_Took_One_Profit2()
{
// Arrange
var position = GetSolanaLongPosition();
// Setup Open and first take profit
var orders = new List<Tuple<decimal, decimal>>
{
new Tuple<decimal, decimal>(position.Open.Quantity, position.Open.Price),
new Tuple<decimal, decimal>(-position.TakeProfit1.Quantity, position.TakeProfit1.Price),
new Tuple<decimal, decimal>(-position.TakeProfit2.Quantity, position.StopLoss.Price)
};
// Act
position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection);
// Assert
Assert.Equal(3.97005582759999752M, position.ProfitAndLoss.Realized);
}
[Fact]
public void Should_Return_Correct_Pnl_For_Short_failed_Position_After_Took_One_Profit2()
{
// Arrange
var position = GetFakeShortPosition();
// Setup Open and first take profit
var orders = new List<Tuple<decimal, decimal>>
{
new Tuple<decimal, decimal>(position.Open.Quantity, position.Open.Price),
new Tuple<decimal, decimal>(-position.TakeProfit1.Quantity, position.TakeProfit1.Price),
new Tuple<decimal, decimal>(-position.TakeProfit2.Quantity, position.StopLoss.Price)
};
// Act
position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection);
// Assert
Assert.Equal(20, position.ProfitAndLoss.Realized);
}
[Fact]
public void Should_Return_Correct_Pnl_For_Short_failed_Position_After_Took_One_Profit3()
{
// Arrange
var position = GetFakeShortPosition();
// Setup Open and first take profit
var orders = new List<Tuple<decimal, decimal>>
{
new Tuple<decimal, decimal>(position.Open.Quantity, position.Open.Price)
};
// Act
position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection);
position.ProfitAndLoss.AddFill(-position.TakeProfit1.Quantity, position.TakeProfit1.Price, TradeDirection.Short);
position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, TradeDirection.Short);
// Assert
Assert.Equal(20, position.ProfitAndLoss.Realized);
}
[Fact]
public void Should_Return_Correct_Pnl_For_Short_Succeeded_Position_After_Two_Take_Profit()
{
// Arrange
var position = GetFakeShortPosition();
// Setup Open and first take profit
var orders = new List<Tuple<decimal, decimal>>
{
new Tuple<decimal, decimal>(position.Open.Quantity, position.Open.Price),
new Tuple<decimal, decimal>(-position.TakeProfit1.Quantity, position.TakeProfit1.Price)
};
// Act
position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection);
// Trigger Stop Loss
position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.TakeProfit2.Price, TradeDirection.Short);
// Assert
Assert.Equal(120, position.ProfitAndLoss.Realized);
}
[Fact]
public void Should_Return_Correct_Pnl_For_Long_failed_Position_After_Took_One_Profit()
{
// Arrange
var position = GetFakeLongPosition();
// Setup Open and first take profit
var orders = new List<Tuple<decimal, decimal>>
{
new Tuple<decimal, decimal>(position.Open.Quantity, position.Open.Price),
new Tuple<decimal, decimal>(-position.TakeProfit1.Quantity, position.TakeProfit1.Price)
};
// Act
position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection);
// Trigger Stop Loss
position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.StopLoss.Price, TradeDirection.Long);
// Assert
Assert.Equal(20, position.ProfitAndLoss.Realized);
}
[Fact]
public void Should_Return_Correct_Pnl_For_Long_Succeeded_Position_After_Two_Take_Profit()
{
// Arrange
var position = GetFakeLongPosition();
// Setup Open and first take profit
var orders = new List<Tuple<decimal, decimal>>
{
new Tuple<decimal, decimal>(position.Open.Quantity, position.Open.Price),
new Tuple<decimal, decimal>(-position.TakeProfit1.Quantity, position.TakeProfit1.Price)
};
// Act
position.ProfitAndLoss = new ProfitAndLoss(orders, position.OriginDirection);
// Trigger Stop Loss
position.ProfitAndLoss.AddFill(-position.TakeProfit2.Quantity, position.TakeProfit2.Price, TradeDirection.Long);
// Assert
Assert.Equal(120, position.ProfitAndLoss.Realized);
}
private static Position GetFakeShortPosition()
{
return new Position("FakeAccount", TradeDirection.Short, Ticker.BTC, null, PositionInitiator.PaperTrading, DateTime.UtcNow)
{
Open = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.Filled,
TradeType.Market, Ticker.ADA, 10, 100, 1, "OpenOrderId", ""),
StopLoss = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen,
TradeType.StopMarket, Ticker.ADA, 10, 110, 1, "StopLossOrderId", ""),
TakeProfit1 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen,
TradeType.TakeProfitLimit, Ticker.ADA, 6, 90, 1, "TakeProfit1OrderId", ""),
TakeProfit2 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen,
TradeType.TakeProfitLimit, Ticker.ADA, 4, 85, 1, "TakeProfit1OrderId", "")
};
}
private static Position GetSolanaLongPosition()
{
return new Position("FakeAccount", TradeDirection.Long, Ticker.BTC, null, PositionInitiator.PaperTrading, DateTime.UtcNow)
{
Open = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.Filled,
TradeType.Market, Ticker.ADA, (decimal)6.0800904000245037980887037491, (decimal)81.6200, 1, "OpenOrderId", ""),
StopLoss = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.PendingOpen,
TradeType.StopMarket, Ticker.ADA, (decimal)3.6480542400147022788532222495, (decimal)79.987600, 1, "StopLossOrderId", ""),
TakeProfit1 = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.PendingOpen,
TradeType.TakeProfitLimit, Ticker.ADA, (decimal)2.4320361600098015192354814996, (decimal)85.701000, 1, "TakeProfit1OrderId", ""),
TakeProfit2 = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.PendingOpen,
TradeType.TakeProfitLimit, Ticker.ADA, (decimal)3.6480542400147022788532222495, (decimal)89.782000, 1, "TakeProfit1OrderId", "")
};
}
private static Position GetFakeLongPosition()
{
return new Position("FakeAccount", TradeDirection.Long, Ticker.BTC, null, PositionInitiator.PaperTrading, DateTime.UtcNow)
{
Open = new Trade(DateTime.Now, TradeDirection.Short, TradeStatus.Filled,
TradeType.Market, Ticker.ADA, 10, 100, 1, "OpenOrderId", ""),
StopLoss = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen,
TradeType.StopMarket, Ticker.ADA, 10, 90, 1, "StopLossOrderId", ""),
TakeProfit1 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen,
TradeType.TakeProfitLimit, Ticker.ADA, 6, 110, 1, "TakeProfit1OrderId", ""),
TakeProfit2 = new Trade(DateTime.Now, TradeDirection.Long, TradeStatus.PendingOpen,
TradeType.TakeProfitLimit, Ticker.ADA, 4, 115, 1, "TakeProfit1OrderId", "")
};
}
}
}

View File

@@ -0,0 +1,62 @@
using Managing.Domain.MoneyManagements;
using Managing.Domain.Shared.Helpers;
using Xunit;
using static Managing.Common.Enums;
namespace Managing.Application.Tests
{
public class RiskHelpersTests
{
private readonly MoneyManagement _moneyManagement;
public RiskHelpersTests()
{
_moneyManagement = new MoneyManagement()
{
BalanceAtRisk = 0.05m,
Leverage = 1,
Timeframe = Timeframe.OneDay,
StopLoss = 0.008m,
TakeProfit = 0.02m
};
}
[Theory]
[InlineData(1000, 992)]
public void Should_Return_Correct_Stop_Loss_Price_For_Long(decimal price, decimal expectedResult)
{
var stopLossPrice = RiskHelpers.GetStopLossPrice(TradeDirection.Long, price, _moneyManagement);
Assert.Equal(expectedResult, stopLossPrice);
}
[Theory]
[InlineData(1000, 1008)]
public void Should_Return_Correct_Stop_Loss_Price_For_Short(decimal price, decimal expectedResult)
{
var stopLossPrice = RiskHelpers.GetStopLossPrice(TradeDirection.Short, price, _moneyManagement);
Assert.Equal(expectedResult, stopLossPrice);
}
[Theory]
[InlineData(1000, 980)]
public void Should_Return_Correct_Take_Profit_Price_For_Short(decimal price, decimal expectedResult)
{
var stopLossPrice = RiskHelpers.GetTakeProfitPrice(TradeDirection.Short, price, _moneyManagement);
Assert.Equal(expectedResult, stopLossPrice);
}
[Theory]
[InlineData(1000, 1020)]
public void Should_Return_Correct_Take_Profit_Price_For_Long(decimal price, decimal expectedResult)
{
var stopLossPrice = RiskHelpers.GetTakeProfitPrice(TradeDirection.Long, price, _moneyManagement);
Assert.Equal(expectedResult, stopLossPrice);
}
[Fact]
public void Test()
{
const decimal test = (decimal)34523.4352;
Assert.Equal((decimal)34523.4, Math.Round(test, 1));
}
}
}

View File

@@ -0,0 +1,212 @@
using Managing.Application.Abstractions.Services;
using Managing.Domain.Accounts;
using Managing.Domain.Strategies;
using Xunit;
using static Managing.Common.Enums;
namespace Managing.Application.Tests
{
public class StrategyTests
{
private readonly IExchangeService _exchangeService;
public StrategyTests()
{
_exchangeService = TradingBaseTests.GetExchangeService();
}
[Theory]
[InlineData(TradingExchanges.Binance, Ticker.ADA, Timeframe.OneDay)]
[InlineData(TradingExchanges.Binance, Ticker.ADA, Timeframe.OneDay)]
[InlineData(TradingExchanges.Binance, Ticker.ADA, Timeframe.OneDay)]
public void Shoud_Return_Signal_On_Rsi_BullishDivergence2(TradingExchanges exchange, Ticker ticker,
Timeframe timeframe)
{
var account = GetAccount(exchange);
// Arrange
var rsiStrategy = new RSIDivergenceStrategy("unittest", timeframe, 5);
var candles = _exchangeService.GetCandles(account, ticker, DateTime.Now.AddDays(-50), timeframe).Result;
var resultSignal = new List<Signal>();
// Act
foreach (var candle in candles)
{
rsiStrategy.Candles.Add(candle);
var signals = rsiStrategy.Run();
}
if (rsiStrategy.Signals != null && rsiStrategy.Signals.Count > 0)
resultSignal.AddRange(rsiStrategy.Signals);
// Assert
Assert.IsType<List<Signal>>(resultSignal);
Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Long);
}
private static Account GetAccount(TradingExchanges exchange)
{
return new Account()
{
Exchange = exchange
};
}
[Theory]
[InlineData(TradingExchanges.Binance, Ticker.ADA, Timeframe.OneDay)]
public void Shoud_Return_Signal_On_Rsi_BearishDivergence(TradingExchanges exchange, Ticker ticker,
Timeframe timeframe)
{
// Arrange
var account = GetAccount(exchange);
var rsiStrategy = new RSIDivergenceStrategy("unittest", timeframe, 5);
var candles = _exchangeService.GetCandles(account, ticker, DateTime.Now.AddDays(-50), timeframe).Result;
var resultSignal = new List<Signal>();
// Act
foreach (var candle in candles)
{
rsiStrategy.Candles.Add(candle);
var signals = rsiStrategy.Run();
}
if (rsiStrategy.Signals != null && rsiStrategy.Signals.Count > 0)
resultSignal.AddRange(rsiStrategy.Signals);
// Assert
Assert.IsType<List<Signal>>(resultSignal);
Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Short);
}
[Theory]
[InlineData(TradingExchanges.Ftx, Ticker.ADA, Timeframe.OneDay, -500)]
public void Shoud_Return_Signal_On_Macd_Cross(TradingExchanges exchange, Ticker ticker, Timeframe timeframe, int days)
{
// Arrange
var account = GetAccount(exchange);
var rsiStrategy = new MACDCrossStrategy("unittest", timeframe, 12, 26, 9);
var candles = _exchangeService.GetCandles(account, ticker, DateTime.Now.AddDays(days), timeframe).Result;
var resultSignal = new List<Signal>();
// Act
foreach (var candle in candles)
{
rsiStrategy.Candles.Add(candle);
var signals = rsiStrategy.Run();
}
if (rsiStrategy.Signals != null && rsiStrategy.Signals.Count > 0)
resultSignal.AddRange(rsiStrategy.Signals);
// Assert
Assert.IsType<List<Signal>>(resultSignal);
Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Short);
Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Long);
}
[Theory]
[InlineData(TradingExchanges.Ftx, Ticker.ADA, Timeframe.OneDay, -500)]
public void Shoud_Return_Signal_On_SuperTrend(TradingExchanges exchange, Ticker ticker, Timeframe timeframe, int days)
{
// Arrange
var account = GetAccount(exchange);
var superTrendStrategy = new SuperTrendStrategy("unittest", timeframe, 10, 3);
var candles = _exchangeService.GetCandles(account, ticker, DateTime.Now.AddDays(days), timeframe).Result;
var resultSignal = new List<Signal>();
// Act
foreach (var candle in candles)
{
superTrendStrategy.Candles.Add(candle);
var signals = superTrendStrategy.Run();
}
if (superTrendStrategy.Signals != null && superTrendStrategy.Signals.Count > 0)
resultSignal.AddRange(superTrendStrategy.Signals);
// Assert
Assert.IsType<List<Signal>>(resultSignal);
Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Short);
Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Long);
}
[Theory]
[InlineData(TradingExchanges.Ftx, Ticker.ADA, Timeframe.OneDay, -500)]
public void Shoud_Return_Signal_On_ChandelierExist(TradingExchanges exchange, Ticker ticker, Timeframe timeframe, int days)
{
// Arrange
var account = GetAccount(exchange);
var chandelierExitStrategy = new ChandelierExitStrategy("unittest", timeframe, 22, 3);
var candles = _exchangeService.GetCandles(account, ticker, DateTime.Now.AddDays(days), timeframe).Result;
var resultSignal = new List<Signal>();
// Act
foreach (var candle in candles)
{
chandelierExitStrategy.Candles.Add(candle);
var signals = chandelierExitStrategy.Run();
}
if (chandelierExitStrategy.Signals != null && chandelierExitStrategy.Signals.Count > 0)
resultSignal.AddRange(chandelierExitStrategy.Signals);
// Assert
Assert.IsType<List<Signal>>(resultSignal);
Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Short);
Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Long);
}
[Theory]
[InlineData(TradingExchanges.Ftx, Ticker.ADA, Timeframe.OneDay, -500)]
public void Shoud_Return_Signal_On_EmaTrend(TradingExchanges exchange, Ticker ticker, Timeframe timeframe, int days)
{
// Arrange
var account = GetAccount(exchange);
var emaTrendSrategy = new EmaTrendStrategy("unittest", timeframe, 200);
var candles = _exchangeService.GetCandles(account, ticker, DateTime.Now.AddDays(days), timeframe).Result;
var resultSignal = new List<Signal>();
// Act
foreach (var candle in candles)
{
emaTrendSrategy.Candles.Add(candle);
var signals = emaTrendSrategy.Run();
}
if (emaTrendSrategy.Signals != null && emaTrendSrategy.Signals.Count > 0)
resultSignal.AddRange(emaTrendSrategy.Signals);
// Assert
Assert.IsType<List<Signal>>(resultSignal);
Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Short);
Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Long);
}
[Theory]
[InlineData(TradingExchanges.Ftx, Ticker.ADA, Timeframe.OneDay, -500)]
public void Shoud_Return_Signal_On_StochRsi(TradingExchanges exchange, Ticker ticker, Timeframe timeframe, int days)
{
// Arrange
var account = GetAccount(exchange);
var stochRsiStrategy = new StochRsiTrendStrategy("unittest", timeframe, 14, 14, 3, 1);
var candles = _exchangeService.GetCandles(account, ticker, DateTime.Now.AddDays(days), timeframe).Result;
var resultSignal = new List<Signal>();
// Act
foreach (var candle in candles)
{
stochRsiStrategy.Candles.Add(candle);
var signals = stochRsiStrategy.Run();
}
if (stochRsiStrategy.Signals != null && stochRsiStrategy.Signals.Count > 0)
resultSignal.AddRange(stochRsiStrategy.Signals);
// Assert
Assert.IsType<List<Signal>>(resultSignal);
Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Short);
Assert.Contains(resultSignal, s => s.Direction == TradeDirection.Long);
}
}
}

View File

@@ -0,0 +1,85 @@
using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services;
using Managing.Application.Backtesting;
using Managing.Application.Bots;
using Managing.Infrastructure.Databases;
using Managing.Infrastructure.Databases.InfluxDb;
using Managing.Infrastructure.Databases.InfluxDb.Models;
using Managing.Infrastructure.Evm;
using Managing.Infrastructure.Evm.Abstractions;
using Managing.Infrastructure.Evm.Services;
using Managing.Infrastructure.Evm.Subgraphs;
using Managing.Infrastructure.Exchanges;
using Managing.Infrastructure.Exchanges.Abstractions;
using Managing.Infrastructure.Exchanges.Exchanges;
using Microsoft.Extensions.Logging;
using Moq;
using static Managing.Common.Enums;
namespace Managing.Application.Tests
{
public static class TradingBaseTests
{
public static IExchangeService GetExchangeService()
{
ILoggerFactory loggerFactory = new Microsoft.Extensions.Logging.Abstractions.NullLoggerFactory();
var ChainlinkGmx = new ChainlinkGmx(SubgraphService.GetSubgraphClient(SubgraphProvider.ChainlinkGmx));
var Chainlink = new Chainlink(SubgraphService.GetSubgraphClient(SubgraphProvider.ChainlinkPrice));
var GbcFeed = new Gbc(SubgraphService.GetSubgraphClient(SubgraphProvider.Gbc));
var Subgraphs = new List<ISubgraphPrices>
{
ChainlinkGmx,
Chainlink,
GbcFeed
};
var evmManager = new EvmManager(Subgraphs);
var evmProcessor = new EvmProcessor(new Mock<ILogger<EvmProcessor>>().Object, evmManager);
var exchangeProcessors = new List<IExchangeProcessor>()
{
//new Mock<FtxProcessor>().Object,
//new Mock<BinanceProcessor>().Object,
evmProcessor
};
return new ExchangeService(loggerFactory.CreateLogger<ExchangeService>(), GetCandleRepository(), exchangeProcessors);
}
public static ILogger<TradingBot> CreateTradingBotLogger()
{
ILoggerFactory loggerFactory = new Microsoft.Extensions.Logging.Abstractions.NullLoggerFactory();
return loggerFactory.CreateLogger<TradingBot>();
}
public static ILogger<Backtester> CreateBacktesterLogger()
{
ILoggerFactory loggerFactory = new Microsoft.Extensions.Logging.Abstractions.NullLoggerFactory();
return loggerFactory.CreateLogger<Backtester>();
}
public static ILogger<CandleRepository> CreateCandleRepositoryLogger()
{
ILoggerFactory loggerFactory = new Microsoft.Extensions.Logging.Abstractions.NullLoggerFactory();
return loggerFactory.CreateLogger<CandleRepository>();
}
public static ICandleRepository GetCandleRepository()
{
var settings = new InfluxDbSettings()
{
Url = "http://localhost:8086/",
Token = "6b-OjFNaZRprYroZEx8zeLScvPqvOp9la1lEksXl8xRT0d96UyuN18iKpB6jKYFt8JJEX1NaxVMXhk-Sgy8sgg==",
Organization = "managing-org"
};
var influxdb = new InfluxDbRepository(settings);
var candleRepository = new CandleRepository(influxdb, CreateCandleRepositoryLogger());
return candleRepository;
}
}
}

View File

@@ -0,0 +1,54 @@
using Managing.Application.Workflows.Flows.Feeds;
using Managing.Domain.Workflows;
using Xunit;
using static Managing.Common.Enums;
namespace Managing.Application.Tests;
public class WorkflowTests : BaseTests
{
[Fact]
public async void Should_Create_Workflow_with_Feed_Ticker_Flow()
{
// Arrange
var workflow = new Workflow
{
Name = "Bot trading",
Usage = WorkflowUsage.Trading,
Description = "Basic trading Workflow",
Flows = new List<IFlow>()
};
// var rsiDivFlow = new RsiDiv()
// {
// Parameters = "{\"Period\": 14,\"Timeframe\":1}",
// Children = new List<IFlow>(),
// };
// var tickerFlow = new FeedTicker(_exchangeService)
// {
// Parameters = "{\"Exchange\": 3,\"Ticker\":9,\"Timeframe\":1}",
// Children = new List<IFlow>()
// {
// rsiDivFlow
// }
// };
// workflow.Flows.Add(tickerFlow);
// Act
await workflow.Execute();
// Assert
foreach (var f in workflow.Flows)
{
Assert.False(string.IsNullOrEmpty(f.Output));
}
Assert.NotNull(workflow);
Assert.NotNull(workflow.Flows);
Assert.Single(workflow.Flows);
Assert.Equal("Feed Ticker", workflow.Name);
Assert.Equal(WorkflowUsage.Trading, workflow.Usage);
Assert.Equal("Basic trading Workflow", workflow.Description);
}
}