feat(engine): add FinlyticEngine microservice with trade lifecycle, AI reasoning gate, composite scoring, and unit tests
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FinlyticCore.Dtos.TechnicalAnalysis;
|
||||
using FinlyticCore.Dtos.Trading;
|
||||
using FinlyticEngine.Database.Entities;
|
||||
|
||||
namespace FinlyticEngine.Tests.TestSupport;
|
||||
|
||||
/// <summary>
|
||||
/// Small builder helpers for the entities used across the TradeLifecycleService tenant-boundary tests, to
|
||||
/// keep individual test methods focused on the behavior under test rather than entity plumbing.
|
||||
/// </summary>
|
||||
public static class TestData
|
||||
{
|
||||
public static ExitPlan SimpleExitPlan(decimal stopLoss = 90m, decimal takeProfit = 110m) => new(
|
||||
StrategyType: ExitStrategyType.FixedSingleTarget,
|
||||
InitialStopLoss: stopLoss,
|
||||
TakeProfitStages: new List<TakeProfitStage>
|
||||
{
|
||||
new(1, takeProfit, 100m, 1m, "Test stage")
|
||||
});
|
||||
|
||||
public static AiValidationResultDto ApprovedAiValidation() => new(
|
||||
IsApproved: true,
|
||||
Confidence: 0.9m,
|
||||
Source: ValidationSource.Ai,
|
||||
ThesisSummary: "Test thesis",
|
||||
InvalidationReason: "",
|
||||
KeyCatalysts: new List<string>(),
|
||||
IdentifiedRisks: new List<string>());
|
||||
|
||||
/// <summary>
|
||||
/// Builds an active, non-expired trade proposal ("system-wide opportunity") ready to be accepted.
|
||||
/// </summary>
|
||||
public static EngineTradeProposalEntity ActiveProposal(
|
||||
string isin = "US0378331005",
|
||||
decimal entryPrice = 100m,
|
||||
decimal stopLoss = 90m,
|
||||
bool isActive = true,
|
||||
DateTime? expiresAtUtc = null)
|
||||
{
|
||||
return new EngineTradeProposalEntity
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UnderlyingIsin = isin,
|
||||
Symbol = "AAPL",
|
||||
StrategyKey = "TestStrategy",
|
||||
Direction = SignalDirection.Buy,
|
||||
QualityScore = 80m,
|
||||
CompositeScore = 80m,
|
||||
CurrentPrice = entryPrice,
|
||||
EntryPrice = entryPrice,
|
||||
StopLoss = stopLoss,
|
||||
TakeProfit1 = entryPrice * 1.1m,
|
||||
RiskRewardRatio = 2m,
|
||||
ExitPlan = SimpleExitPlan(stopLoss, entryPrice * 1.1m),
|
||||
SelectedDerivative = null,
|
||||
AiValidation = ApprovedAiValidation(),
|
||||
IsActive = isActive,
|
||||
CreatedAtUtc = DateTime.UtcNow,
|
||||
ExpiresAtUtc = expiresAtUtc ?? DateTime.UtcNow.AddHours(24)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds an active trade owned by <paramref name="userId"/>, optionally linked to a proposal.
|
||||
/// </summary>
|
||||
public static EngineTradeEntity ActiveTrade(
|
||||
Guid userId,
|
||||
Guid? proposalId = null,
|
||||
string isin = "US0378331005",
|
||||
decimal averageBuyIn = 100m,
|
||||
decimal stopLoss = 90m,
|
||||
TradeStatus status = TradeStatus.Active)
|
||||
{
|
||||
return new EngineTradeEntity
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId,
|
||||
ProposalId = proposalId ?? Guid.Empty,
|
||||
UnderlyingIsin = isin,
|
||||
Symbol = "AAPL",
|
||||
ExecutionMode = ExecutionMode.ManualTradeRepublic,
|
||||
InstrumentType = InstrumentCategoryType.Stock,
|
||||
Direction = SignalDirection.Buy,
|
||||
Status = status,
|
||||
AverageBuyIn = averageBuyIn,
|
||||
TotalQuantity = 1m,
|
||||
InitialStopLoss = stopLoss,
|
||||
CurrentStopLoss = stopLoss,
|
||||
CurrentPrice = averageBuyIn,
|
||||
TakeProfit1 = averageBuyIn * 1.1m,
|
||||
TakeProfit2 = averageBuyIn * 1.2m,
|
||||
ExitPlan = SimpleExitPlan(stopLoss, averageBuyIn * 1.1m),
|
||||
OpenedAtUtc = DateTime.UtcNow,
|
||||
LastUpdatedAtUtc = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user