Start impl for genetic

This commit is contained in:
2025-07-10 21:33:24 +07:00
parent 0b4f2173e0
commit 2fc7a1d4bb
8 changed files with 302 additions and 134 deletions

View File

@@ -30,6 +30,7 @@ public class BacktestController : BaseController
private readonly IScenarioService _scenarioService; private readonly IScenarioService _scenarioService;
private readonly IAccountService _accountService; private readonly IAccountService _accountService;
private readonly IMoneyManagementService _moneyManagementService; private readonly IMoneyManagementService _moneyManagementService;
private readonly IGeneticService _geneticService;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BacktestController"/> class. /// Initializes a new instance of the <see cref="BacktestController"/> class.
@@ -39,12 +40,14 @@ public class BacktestController : BaseController
/// <param name="scenarioService">The service for managing scenarios.</param> /// <param name="scenarioService">The service for managing scenarios.</param>
/// <param name="accountService">The service for account management.</param> /// <param name="accountService">The service for account management.</param>
/// <param name="moneyManagementService">The service for money management strategies.</param> /// <param name="moneyManagementService">The service for money management strategies.</param>
/// <param name="geneticService">The service for genetic algorithm operations.</param>
public BacktestController( public BacktestController(
IHubContext<BotHub> hubContext, IHubContext<BotHub> hubContext,
IBacktester backtester, IBacktester backtester,
IScenarioService scenarioService, IScenarioService scenarioService,
IAccountService accountService, IAccountService accountService,
IMoneyManagementService moneyManagementService, IMoneyManagementService moneyManagementService,
IGeneticService geneticService,
IUserService userService) : base(userService) IUserService userService) : base(userService)
{ {
_hubContext = hubContext; _hubContext = hubContext;
@@ -52,6 +55,7 @@ public class BacktestController : BaseController
_scenarioService = scenarioService; _scenarioService = scenarioService;
_accountService = accountService; _accountService = accountService;
_moneyManagementService = moneyManagementService; _moneyManagementService = moneyManagementService;
_geneticService = geneticService;
} }
/// <summary> /// <summary>
@@ -266,8 +270,8 @@ public class BacktestController : BaseController
{ {
var user = await GetUser(); var user = await GetUser();
// Create genetic request using the Backtester service // Create genetic request using the GeneticService directly
var geneticRequest = _backtester.CreateGeneticRequest( var geneticRequest = _geneticService.CreateGeneticRequest(
user, user,
request.Ticker, request.Ticker,
request.Timeframe, request.Timeframe,
@@ -302,7 +306,7 @@ public class BacktestController : BaseController
public async Task<ActionResult<IEnumerable<GeneticRequest>>> GetGeneticRequests() public async Task<ActionResult<IEnumerable<GeneticRequest>>> GetGeneticRequests()
{ {
var user = await GetUser(); var user = await GetUser();
var geneticRequests = _backtester.GetGeneticRequestsByUser(user); var geneticRequests = _geneticService.GetGeneticRequestsByUser(user);
return Ok(geneticRequests); return Ok(geneticRequests);
} }
@@ -316,7 +320,7 @@ public class BacktestController : BaseController
public async Task<ActionResult<GeneticRequest>> GetGeneticRequest(string id) public async Task<ActionResult<GeneticRequest>> GetGeneticRequest(string id)
{ {
var user = await GetUser(); var user = await GetUser();
var geneticRequest = _backtester.GetGeneticRequestByIdForUser(user, id); var geneticRequest = _geneticService.GetGeneticRequestByIdForUser(user, id);
if (geneticRequest == null) if (geneticRequest == null)
{ {
@@ -336,7 +340,7 @@ public class BacktestController : BaseController
public async Task<ActionResult> DeleteGeneticRequest(string id) public async Task<ActionResult> DeleteGeneticRequest(string id)
{ {
var user = await GetUser(); var user = await GetUser();
_backtester.DeleteGeneticRequestByIdForUser(user, id); _geneticService.DeleteGeneticRequestByIdForUser(user, id);
return Ok(); return Ok();
} }

View File

@@ -2,7 +2,6 @@
using Managing.Domain.Bots; using Managing.Domain.Bots;
using Managing.Domain.Candles; using Managing.Domain.Candles;
using Managing.Domain.Users; using Managing.Domain.Users;
using static Managing.Common.Enums;
namespace Managing.Application.Abstractions.Services namespace Managing.Application.Abstractions.Services
{ {
@@ -50,25 +49,6 @@ namespace Managing.Application.Abstractions.Services
bool DeleteBacktestByUser(User user, string id); bool DeleteBacktestByUser(User user, string id);
bool DeleteBacktestsByUser(User user); bool DeleteBacktestsByUser(User user);
// Genetic algorithm request methods
GeneticRequest CreateGeneticRequest(
User user,
Ticker ticker,
Timeframe timeframe,
DateTime startDate,
DateTime endDate,
decimal balance,
int populationSize,
int generations,
double mutationRate,
string selectionMethod,
int elitismPercentage,
double maxTakeProfit,
List<IndicatorType> eligibleIndicators);
IEnumerable<GeneticRequest> GetGeneticRequestsByUser(User user);
GeneticRequest GetGeneticRequestByIdForUser(User user, string id);
void UpdateGeneticRequest(GeneticRequest geneticRequest);
void DeleteGeneticRequestByIdForUser(User user, string id);
} }
} }

View File

@@ -74,4 +74,11 @@ public interface IGeneticService
/// </summary> /// </summary>
/// <returns>Collection of pending genetic requests</returns> /// <returns>Collection of pending genetic requests</returns>
IEnumerable<GeneticRequest> GetPendingGeneticRequests(); IEnumerable<GeneticRequest> GetPendingGeneticRequests();
/// <summary>
/// Runs the genetic algorithm for a specific request
/// </summary>
/// <param name="request">The genetic request to process</param>
/// <returns>The genetic algorithm result</returns>
Task<GeneticAlgorithmResult> RunGeneticAlgorithm(GeneticRequest request);
} }

View File

@@ -69,8 +69,8 @@ public class GeneticAlgorithmWorker : BaseWorker<GeneticAlgorithmWorker>
request.Status = GeneticRequestStatus.Running; request.Status = GeneticRequestStatus.Running;
_geneticService.UpdateGeneticRequest(request); _geneticService.UpdateGeneticRequest(request);
// Run genetic algorithm // Run genetic algorithm using the service
var results = await RunGeneticAlgorithm(request, cancellationToken); var results = await _geneticService.RunGeneticAlgorithm(request);
// Update request with results // Update request with results
request.Status = GeneticRequestStatus.Completed; request.Status = GeneticRequestStatus.Completed;
@@ -101,31 +101,5 @@ public class GeneticAlgorithmWorker : BaseWorker<GeneticAlgorithmWorker>
} }
} }
private async Task<GeneticAlgorithmResult> RunGeneticAlgorithm(GeneticRequest request, CancellationToken cancellationToken)
{
// TODO: Implement the actual genetic algorithm
// This is where the genetic algorithm logic will be implemented
_logger.LogInformation("[GeneticAlgorithm] Placeholder: Would run genetic algorithm for request {RequestId}", request.RequestId);
// Simulate some processing time
await Task.Delay(1000, cancellationToken);
return new GeneticAlgorithmResult
{
BestFitness = 0.85,
BestIndividual = "placeholder_individual",
ProgressInfo = "{\"generation\": 10, \"best_fitness\": 0.85}"
};
}
}
/// <summary>
/// Result of a genetic algorithm run
/// </summary>
public class GeneticAlgorithmResult
{
public double BestFitness { get; set; }
public string BestIndividual { get; set; } = string.Empty;
public string ProgressInfo { get; set; } = string.Empty;
} }

View File

@@ -27,7 +27,6 @@ namespace Managing.Application.Backtesting
private readonly IScenarioService _scenarioService; private readonly IScenarioService _scenarioService;
private readonly IAccountService _accountService; private readonly IAccountService _accountService;
private readonly IMessengerService _messengerService; private readonly IMessengerService _messengerService;
private readonly IGeneticService _geneticService;
public Backtester( public Backtester(
IExchangeService exchangeService, IExchangeService exchangeService,
@@ -36,8 +35,7 @@ namespace Managing.Application.Backtesting
ILogger<Backtester> logger, ILogger<Backtester> logger,
IScenarioService scenarioService, IScenarioService scenarioService,
IAccountService accountService, IAccountService accountService,
IMessengerService messengerService, IMessengerService messengerService)
IGeneticService geneticService)
{ {
_exchangeService = exchangeService; _exchangeService = exchangeService;
_botFactory = botFactory; _botFactory = botFactory;
@@ -46,7 +44,6 @@ namespace Managing.Application.Backtesting
_scenarioService = scenarioService; _scenarioService = scenarioService;
_accountService = accountService; _accountService = accountService;
_messengerService = messengerService; _messengerService = messengerService;
_geneticService = geneticService;
} }
public Backtest RunSimpleBotBacktest(Workflow workflow, bool save = false) public Backtest RunSimpleBotBacktest(Workflow workflow, bool save = false)
@@ -460,82 +457,6 @@ namespace Managing.Application.Backtesting
} }
} }
/// <summary>
/// Creates a new genetic algorithm request
/// </summary>
/// <param name="user">The user creating the request</param>
/// <param name="ticker">The ticker to optimize for</param>
/// <param name="timeframe">The timeframe to use</param>
/// <param name="startDate">The start date for the backtest period</param>
/// <param name="endDate">The end date for the backtest period</param>
/// <param name="balance">The starting balance</param>
/// <param name="populationSize">The population size for the genetic algorithm</param>
/// <param name="generations">The number of generations to evolve</param>
/// <param name="mutationRate">The mutation rate (0.0 - 1.0)</param>
/// <param name="selectionMethod">The selection method to use</param>
/// <param name="elitismPercentage">The percentage of elite individuals to preserve</param>
/// <param name="maxTakeProfit">The maximum take profit percentage</param>
/// <param name="eligibleIndicators">The list of eligible indicators</param>
/// <returns>The created genetic request</returns>
public GeneticRequest CreateGeneticRequest(
User user,
Ticker ticker,
Timeframe timeframe,
DateTime startDate,
DateTime endDate,
decimal balance,
int populationSize,
int generations,
double mutationRate,
string selectionMethod,
int elitismPercentage,
double maxTakeProfit,
List<IndicatorType> eligibleIndicators)
{
return _geneticService.CreateGeneticRequest(
user, ticker, timeframe, startDate, endDate, balance,
populationSize, generations, mutationRate, selectionMethod,
elitismPercentage, maxTakeProfit, eligibleIndicators);
}
/// <summary>
/// Gets all genetic requests for a user
/// </summary>
/// <param name="user">The user to get requests for</param>
/// <returns>Collection of genetic requests</returns>
public IEnumerable<GeneticRequest> GetGeneticRequestsByUser(User user)
{
return _geneticService.GetGeneticRequestsByUser(user);
}
/// <summary>
/// Gets a specific genetic request by ID for a user
/// </summary>
/// <param name="user">The user</param>
/// <param name="id">The request ID</param>
/// <returns>The genetic request or null if not found</returns>
public GeneticRequest GetGeneticRequestByIdForUser(User user, string id)
{
return _geneticService.GetGeneticRequestByIdForUser(user, id);
}
/// <summary>
/// Updates a genetic request
/// </summary>
/// <param name="geneticRequest">The genetic request to update</param>
public void UpdateGeneticRequest(GeneticRequest geneticRequest)
{
_geneticService.UpdateGeneticRequest(geneticRequest);
}
/// <summary>
/// Deletes a genetic request by ID for a user
/// </summary>
/// <param name="user">The user</param>
/// <param name="id">The request ID</param>
public void DeleteGeneticRequestByIdForUser(User user, string id)
{
_geneticService.DeleteGeneticRequestByIdForUser(user, id);
}
} }
} }

View File

@@ -1,7 +1,13 @@
using System.Text.Json;
using GeneticSharp;
using Managing.Application.Abstractions.Repositories; using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services; using Managing.Application.Abstractions.Services;
using Managing.Domain.Backtests; using Managing.Domain.Backtests;
using Managing.Domain.Bots;
using Managing.Domain.MoneyManagements;
using Managing.Domain.Risk;
using Managing.Domain.Users; using Managing.Domain.Users;
using Microsoft.Extensions.Logging;
using static Managing.Common.Enums; using static Managing.Common.Enums;
namespace Managing.Application; namespace Managing.Application;
@@ -12,10 +18,17 @@ namespace Managing.Application;
public class GeneticService : IGeneticService public class GeneticService : IGeneticService
{ {
private readonly IGeneticRepository _geneticRepository; private readonly IGeneticRepository _geneticRepository;
private readonly IBacktester _backtester;
private readonly ILogger<GeneticService> _logger;
public GeneticService(IGeneticRepository geneticRepository) public GeneticService(
IGeneticRepository geneticRepository,
IBacktester backtester,
ILogger<GeneticService> logger)
{ {
_geneticRepository = geneticRepository; _geneticRepository = geneticRepository;
_backtester = backtester;
_logger = logger;
} }
public GeneticRequest CreateGeneticRequest( public GeneticRequest CreateGeneticRequest(
@@ -79,4 +92,245 @@ public class GeneticService : IGeneticService
{ {
return _geneticRepository.GetPendingGeneticRequests(); return _geneticRepository.GetPendingGeneticRequests();
} }
/// <summary>
/// Runs the genetic algorithm for a specific request
/// </summary>
/// <param name="request">The genetic request to process</param>
/// <returns>The genetic algorithm result</returns>
public async Task<GeneticAlgorithmResult> RunGeneticAlgorithm(GeneticRequest request)
{
try
{
_logger.LogInformation("Starting genetic algorithm for request {RequestId}", request.RequestId);
// Create chromosome for trading bot configuration
var chromosome = new TradingBotChromosome(request.EligibleIndicators);
// Create fitness function
var fitness = new TradingBotFitness(_backtester, request);
// Create genetic algorithm
var ga = new GeneticAlgorithm(
new Population(request.PopulationSize, request.PopulationSize, chromosome),
fitness,
GetSelection(request.SelectionMethod),
new UniformCrossover(),
GetMutation(request.MutationRate))
{
Termination = new GenerationNumberTermination(request.Generations)
};
// Run the genetic algorithm
ga.Start();
// Get the best chromosome
var bestChromosome = ga.BestChromosome as TradingBotChromosome;
var bestFitness = ga.BestChromosome.Fitness.Value;
_logger.LogInformation("Genetic algorithm completed for request {RequestId}. Best fitness: {Fitness}",
request.RequestId, bestFitness);
return new GeneticAlgorithmResult
{
BestFitness = bestFitness,
BestIndividual = bestChromosome?.ToString() ?? "unknown",
ProgressInfo = JsonSerializer.Serialize(new
{
generation = ga.GenerationsNumber,
best_fitness = bestFitness,
population_size = request.PopulationSize,
generations = request.Generations
})
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error running genetic algorithm for request {RequestId}", request.RequestId);
throw;
}
}
private ISelection GetSelection(string selectionMethod)
{
return selectionMethod.ToLower() switch
{
"tournament" => new TournamentSelection(),
"roulette" => new RouletteWheelSelection(),
"rank" => new RankSelection(),
_ => new TournamentSelection()
};
}
private IMutation GetMutation(double mutationRate)
{
return new UniformMutation(true);
}
}
/// <summary>
/// Chromosome representing a trading bot configuration
/// </summary>
public class TradingBotChromosome : ChromosomeBase
{
private readonly List<IndicatorType> _eligibleIndicators;
private readonly Random _random = new Random();
public TradingBotChromosome(List<IndicatorType> eligibleIndicators) : base(eligibleIndicators.Count + 5)
{
_eligibleIndicators = eligibleIndicators;
}
public override Gene GenerateGene(int geneIndex)
{
if (geneIndex < _eligibleIndicators.Count)
{
// Gene represents whether an indicator is selected (0 or 1)
return new Gene(_random.Next(2));
}
else
{
// Additional genes for other parameters
return geneIndex switch
{
var i when i == _eligibleIndicators.Count => new Gene(_random.Next(1, 11)), // Stop loss percentage
var i when i == _eligibleIndicators.Count + 1 => new Gene(_random.Next(1, 21)), // Take profit percentage
var i when i == _eligibleIndicators.Count + 2 => new Gene(_random.Next(1, 101)), // Position size percentage
var i when i == _eligibleIndicators.Count + 3 => new Gene(_random.Next(1, 51)), // Max positions
var i when i == _eligibleIndicators.Count + 4 => new Gene(_random.Next(1, 11)), // Risk level
_ => new Gene(0)
};
}
}
public override IChromosome CreateNew()
{
return new TradingBotChromosome(_eligibleIndicators);
}
public override IChromosome Clone()
{
var clone = new TradingBotChromosome(_eligibleIndicators);
clone.ReplaceGenes(0, GetGenes());
return clone;
}
public List<IndicatorType> GetSelectedIndicators()
{
var selected = new List<IndicatorType>();
for (int i = 0; i < _eligibleIndicators.Count; i++)
{
if (GetGene(i).Value.ToString() == "1")
{
selected.Add(_eligibleIndicators[i]);
}
}
return selected;
}
public TradingBotConfig GetTradingBotConfig(GeneticRequest request)
{
var selectedIndicators = GetSelectedIndicators();
var genes = GetGenes();
return new TradingBotConfig
{
Name = $"Genetic_{request.RequestId}",
AccountName = "genetic_account",
Ticker = request.Ticker,
Timeframe = request.Timeframe,
BotTradingBalance = request.Balance,
IsForBacktest = true,
IsForWatchingOnly = false,
CooldownPeriod = 0,
MaxLossStreak = 3,
FlipPosition = false,
FlipOnlyWhenInProfit = true,
MoneyManagement = new MoneyManagement
{
Name = $"Genetic_{request.RequestId}_MM",
Timeframe = request.Timeframe,
StopLoss = Convert.ToDecimal(genes[_eligibleIndicators.Count].Value),
TakeProfit = Convert.ToDecimal(genes[_eligibleIndicators.Count + 1].Value),
Leverage = 1.0m
},
RiskManagement = new RiskManagement
{
RiskTolerance = (RiskToleranceLevel)Convert.ToInt32(genes[_eligibleIndicators.Count + 4].Value)
}
};
}
}
/// <summary>
/// Fitness function for trading bot optimization
/// </summary>
public class TradingBotFitness : IFitness
{
private readonly IBacktester _backtester;
private readonly GeneticRequest _request;
public TradingBotFitness(IBacktester backtester, GeneticRequest request)
{
_backtester = backtester;
_request = request;
}
public double Evaluate(IChromosome chromosome)
{
try
{
var tradingBotChromosome = chromosome as TradingBotChromosome;
if (tradingBotChromosome == null)
return 0;
var config = tradingBotChromosome.GetTradingBotConfig(_request);
// Run backtest
var backtest = _backtester.RunTradingBotBacktest(
config,
_request.StartDate,
_request.EndDate,
_request.User,
false, // Don't save individual backtests
false // Don't include candles
).Result;
// Calculate fitness based on backtest results
var fitness = CalculateFitness(backtest);
return fitness;
}
catch (Exception)
{
// Return low fitness for failed backtests
return 0.1;
}
}
private double CalculateFitness(Backtest backtest)
{
if (backtest == null || backtest.Score == null)
return 0.1;
// Use the backtest score as the primary fitness metric
var baseFitness = backtest.Score;
// Apply additional factors
var tradeCount = backtest.Positions?.Count ?? 0;
var winRate = backtest.WinRate;
var finalPnl = backtest.FinalPnl;
// Penalize if no trades were made
if (tradeCount == 0)
return 0.1;
// Bonus for good win rate
var winRateBonus = winRate > 0.6 ? 10 : 0;
// Bonus for positive PnL
var pnlBonus = finalPnl > 0 ? 20 : 0;
return baseFitness + winRateBonus + pnlBonus;
}
} }

View File

@@ -14,6 +14,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="FluentValidation" Version="11.9.1" /> <PackageReference Include="FluentValidation" Version="11.9.1" />
<PackageReference Include="GeneticSharp" Version="3.1.4" />
<PackageReference Include="MediatR" Version="12.2.0" /> <PackageReference Include="MediatR" Version="12.2.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.1.0" /> <PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.1.0" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.1.0" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.1.0" />

View File

@@ -0,0 +1,27 @@
namespace Managing.Domain.Backtests;
/// <summary>
/// Represents the result of a genetic algorithm run
/// </summary>
public class GeneticAlgorithmResult
{
/// <summary>
/// The best fitness score achieved
/// </summary>
public double BestFitness { get; set; }
/// <summary>
/// The best individual (chromosome) found
/// </summary>
public string BestIndividual { get; set; } = string.Empty;
/// <summary>
/// Progress information as JSON string
/// </summary>
public string ProgressInfo { get; set; } = string.Empty;
/// <summary>
/// When the algorithm completed
/// </summary>
public DateTime CompletedAt { get; set; } = DateTime.UtcNow;
}