docker files fixes from liaqat
This commit is contained in:
51
src/Managing.Application.Tests/BaseTests.cs
Normal file
51
src/Managing.Application.Tests/BaseTests.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
408
src/Managing.Application.Tests/BotsTests.cs
Normal file
408
src/Managing.Application.Tests/BotsTests.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/Managing.Application.Tests/CandleHelpersTests.cs
Normal file
20
src/Managing.Application.Tests/CandleHelpersTests.cs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
24
src/Managing.Application.Tests/MathHelpersTests.cs
Normal file
24
src/Managing.Application.Tests/MathHelpersTests.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
src/Managing.Application.Tests/PositionTests.cs
Normal file
56
src/Managing.Application.Tests/PositionTests.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
251
src/Managing.Application.Tests/ProfitAndLossTests.cs
Normal file
251
src/Managing.Application.Tests/ProfitAndLossTests.cs
Normal 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", "")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
62
src/Managing.Application.Tests/RiskHelpersTests.cs
Normal file
62
src/Managing.Application.Tests/RiskHelpersTests.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
212
src/Managing.Application.Tests/StrategyTests.cs
Normal file
212
src/Managing.Application.Tests/StrategyTests.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
85
src/Managing.Application.Tests/TradingBaseTests.cs
Normal file
85
src/Managing.Application.Tests/TradingBaseTests.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/Managing.Application.Tests/WorkflowTests.cs
Normal file
54
src/Managing.Application.Tests/WorkflowTests.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user