feat(Core): update DTOs and shared models
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace FinlyticCore.Models.Analyzer;
|
||||
|
||||
public class AssetRecommendationDto
|
||||
{
|
||||
[JsonPropertyName("mode")]
|
||||
public string Mode { get; set; } = "AUTO_SCREENER";
|
||||
|
||||
[JsonPropertyName("timestamp")]
|
||||
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||||
|
||||
[JsonPropertyName("recommended_asset")]
|
||||
public RecommendedAssetInfo RecommendedAsset { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("rationale")]
|
||||
public RecommendationRationaleInfo Rationale { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("action_required")]
|
||||
public string ActionRequired { get; set; } = "PROMPT_USER_FOR_MANUAL_TRADE"; // "PROMPT_USER_FOR_MANUAL_TRADE" | "NO_ACTION"
|
||||
}
|
||||
|
||||
public class RecommendedAssetInfo
|
||||
{
|
||||
[JsonPropertyName("symbol")]
|
||||
public string Symbol { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("company_name")]
|
||||
public string CompanyName { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("isin")]
|
||||
public string Isin { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("market")]
|
||||
public string Market { get; set; } = "US_EQUITIES";
|
||||
|
||||
[JsonPropertyName("bias")]
|
||||
public string Bias { get; set; } = "BULLISH"; // "BULLISH" | "BEARISH" | "NEUTRAL"
|
||||
|
||||
[JsonPropertyName("confidence_score")]
|
||||
public double ConfidenceScore { get; set; }
|
||||
|
||||
[JsonPropertyName("timeframe")]
|
||||
public string Timeframe { get; set; } = "1D";
|
||||
}
|
||||
|
||||
public class RecommendationRationaleInfo
|
||||
{
|
||||
[JsonPropertyName("pattern_detected")]
|
||||
public string PatternDetected { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("vix_context")]
|
||||
public string VixContext { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("key_technical_levels")]
|
||||
public KeyTechnicalLevelsInfo KeyTechnicalLevels { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("summary")]
|
||||
public string Summary { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class KeyTechnicalLevelsInfo
|
||||
{
|
||||
[JsonPropertyName("support")]
|
||||
public List<double> Support { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("resistance")]
|
||||
public List<double> Resistance { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using FinlyticCore.Models.Trades;
|
||||
|
||||
namespace FinlyticCore.Models.Analyzer;
|
||||
|
||||
/// <summary>
|
||||
/// Response payload for manual AI analysis trigger RPC.
|
||||
/// </summary>
|
||||
public class ManualAnalysisResponseDto
|
||||
{
|
||||
[JsonPropertyName("analysisId")]
|
||||
public string AnalysisId { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("isTradeProposed")]
|
||||
public bool IsTradeProposed { get; set; }
|
||||
|
||||
[JsonPropertyName("status")]
|
||||
public string Status { get; set; } = "Success";
|
||||
|
||||
[JsonPropertyName("recommendation")]
|
||||
public string Recommendation { get; set; } = "RECOMMENDED";
|
||||
|
||||
[JsonPropertyName("n8nResponse")]
|
||||
public N8nAnalysisResponseDto? N8nResponse { get; set; }
|
||||
|
||||
[JsonPropertyName("proposal")]
|
||||
public TradeProposalDto? Proposal { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FinlyticCore.Models.Analyzer;
|
||||
|
||||
public class TargetAssetInfo
|
||||
{
|
||||
public string Symbol { get; set; } = string.Empty; // e.g. "AAPL"
|
||||
public string Name { get; set; } = string.Empty; // e.g. "Apple Inc."
|
||||
public string Isin { get; set; } = string.Empty;
|
||||
public string Sector { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class MarketContextInfo
|
||||
{
|
||||
public decimal Vix { get; set; }
|
||||
public string MarketRegime { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class FilterContextInfo
|
||||
{
|
||||
public double ImpactScore { get; set; }
|
||||
public string RawNewsHeadline { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class UserPreferencesInfo
|
||||
{
|
||||
public int RiskScore { get; set; } = 50; // 0 to 100
|
||||
public string RiskTolerance { get; set; } = "Balanced";
|
||||
public int MinTimeframeValue { get; set; } = 1;
|
||||
public int MaxTimeframeValue { get; set; } = 7;
|
||||
public string TimeframeUnit { get; set; } = "Tage"; // "Stunden", "Tage", "Wochen", "Monate"
|
||||
public string TimeframeFormatted { get; set; } = "1-7 Tage";
|
||||
public string InstrumentType { get; set; } = "Stock"; // "Stock", "KnockOut", "Option", "CFD", "Future"
|
||||
public string UserNotes { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class TradeFeedbackInfo
|
||||
{
|
||||
public int TotalAssetTrades { get; set; }
|
||||
public double AssetWinRate { get; set; }
|
||||
public double AvgReturnPercent { get; set; }
|
||||
public string LastTradeResult { get; set; } = "NONE"; // "WIN", "LOSS", "NONE"
|
||||
}
|
||||
|
||||
public class PatternContextInfo
|
||||
{
|
||||
public string PatternName { get; set; } = string.Empty;
|
||||
public string? BreakoutDirection { get; set; }
|
||||
public double? TargetPrice { get; set; }
|
||||
public double? PotentialPercent { get; set; }
|
||||
}
|
||||
|
||||
public class TechnicalContextInfo
|
||||
{
|
||||
public string Rsi { get; set; } = "N/A";
|
||||
public string SupertrendStatus { get; set; } = "N/A";
|
||||
public string Atr { get; set; } = "N/A";
|
||||
public double? Sma50 { get; set; }
|
||||
public double? Sma200 { get; set; }
|
||||
public List<PatternContextInfo> DetectedPatterns { get; set; } = new();
|
||||
}
|
||||
|
||||
public class SentimentContextInfo
|
||||
{
|
||||
public double AssetSentimentScore { get; set; }
|
||||
public double SectorSentimentScore { get; set; }
|
||||
public string NewsSentimentSummary { get; set; } = "Neutral";
|
||||
}
|
||||
|
||||
public class FundamentalContextInfo
|
||||
{
|
||||
public double? PeRatio { get; set; }
|
||||
public double? ForwardPeRatio { get; set; }
|
||||
public double? PegRatio { get; set; }
|
||||
public double? MarketCap { get; set; }
|
||||
public double? DebtToEquity { get; set; }
|
||||
public double? GrossMargin { get; set; }
|
||||
public double? NetProfitMargin { get; set; }
|
||||
public double? ReturnOnEquity { get; set; }
|
||||
public double? DividendYield { get; set; }
|
||||
public double? ShortPercentOfFloat { get; set; }
|
||||
public double? AnalystTargetMedian { get; set; }
|
||||
public double? EvToEbitda { get; set; }
|
||||
}
|
||||
|
||||
public class N8nAnalysisRequestDto
|
||||
{
|
||||
public string RequestId { get; set; } = string.Empty;
|
||||
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||||
public string TriggerType { get; set; } = "AutomatedNews"; // "Manual" | "AutomatedNews"
|
||||
|
||||
public TargetAssetInfo TargetAsset { get; set; } = new();
|
||||
public MarketContextInfo MarketContext { get; set; } = new();
|
||||
public FilterContextInfo FilterContext { get; set; } = new();
|
||||
public UserPreferencesInfo UserPreferences { get; set; } = new();
|
||||
public TradeFeedbackInfo TradeFeedback { get; set; } = new();
|
||||
public TechnicalContextInfo TechnicalContext { get; set; } = new();
|
||||
public SentimentContextInfo SentimentContext { get; set; } = new();
|
||||
public FundamentalContextInfo FundamentalContext { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FinlyticCore.Models.Analyzer;
|
||||
|
||||
public class N8nAnalysisResponseDto
|
||||
{
|
||||
public string RequestId { get; set; } = string.Empty;
|
||||
public double EvalScore { get; set; } // 0.00 to 1.00
|
||||
public string AiDecision { get; set; } = "Proceed"; // "Proceed" | "Reject" | "Hold"
|
||||
public string SuggestedDirection { get; set; } = "Long"; // "Long" | "Short"
|
||||
public string AiReasoning { get; set; } = string.Empty;
|
||||
public string SuggestedTimeframe { get; set; } = "Intraday"; // "Scalp" | "Intraday" | "Swing"
|
||||
public string SuggestedRisk { get; set; } = "Medium"; // "Low" | "Medium" | "High"
|
||||
|
||||
public ExecutionPlanInfo? ExecutionPlan { get; set; }
|
||||
public DetailedAnalysisInfo? DetailedAnalysis { get; set; }
|
||||
}
|
||||
|
||||
public class ExecutionPlanInfo
|
||||
{
|
||||
public EntryZoneInfo? EntryZone { get; set; }
|
||||
public decimal StopLoss { get; set; }
|
||||
public List<decimal>? TakeProfitTargets { get; set; }
|
||||
public decimal RiskRewardRatio { get; set; }
|
||||
public decimal MaxLeverage { get; set; }
|
||||
}
|
||||
|
||||
public class EntryZoneInfo
|
||||
{
|
||||
public decimal Min { get; set; }
|
||||
public decimal Max { get; set; }
|
||||
}
|
||||
|
||||
public class DetailedAnalysisInfo
|
||||
{
|
||||
public string TechnicalRationale { get; set; } = string.Empty;
|
||||
public string FundamentalRationale { get; set; } = string.Empty;
|
||||
public string RiskWarning { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace FinlyticCore.Models.Analyzer;
|
||||
|
||||
/// <summary>
|
||||
/// Market volatility regime derived from VIX / VDAX index level.
|
||||
/// </summary>
|
||||
public enum VixMarketRegime
|
||||
{
|
||||
LowVol = 0, // VIX < 15
|
||||
Normal = 1, // VIX 15 - 20
|
||||
HighVol = 2, // VIX 20 - 30
|
||||
Panic = 3 // VIX > 30
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FinlyticCore.Models.Auth;
|
||||
|
||||
public class AuthResponseDto
|
||||
{
|
||||
public string Token { get; set; } = string.Empty;
|
||||
public Guid UserId { get; set; }
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string FullName { get; set; } = string.Empty;
|
||||
public string Role { get; set; } = "User";
|
||||
public List<string> FcmTokens { get; set; } = new();
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
public bool RequiresPasswordChange { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace FinlyticCore.Models.Auth;
|
||||
|
||||
public class CreateUserRequestDto
|
||||
{
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string Password { get; set; } = string.Empty;
|
||||
public string FullName { get; set; } = string.Empty;
|
||||
public string Role { get; set; } = "User"; // "User" | "Admin"
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using FinlyticCore.Models.Trades;
|
||||
|
||||
namespace FinlyticCore.Models.Auth;
|
||||
|
||||
/// <summary>
|
||||
/// Strongly typed SignalR client interface for real-time WebSocket/SSE streaming.
|
||||
/// </summary>
|
||||
public interface ITradeClient
|
||||
{
|
||||
Task OnTradeProposed(TradeProposalDto proposal);
|
||||
Task OnTradeUpdated(TradeHourlyUpdateDto update);
|
||||
Task OnTradeClosed(string tradeId, decimal exitPrice, string reason);
|
||||
Task OnNewsReceived(object newsItem);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FinlyticCore.Models.Auth;
|
||||
|
||||
public class LoginRequestDto
|
||||
{
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string Password { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace FinlyticCore.Models.Auth;
|
||||
|
||||
/// <summary>
|
||||
/// DTO representing a request for self-registration by a new user.
|
||||
/// </summary>
|
||||
public class RegisterRequestDto
|
||||
{
|
||||
/// <summary>
|
||||
/// User email address.
|
||||
/// </summary>
|
||||
public string Email { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// User plain-text password.
|
||||
/// </summary>
|
||||
public string Password { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// User full name.
|
||||
/// </summary>
|
||||
public string FullName { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FinlyticCore.Models.Auth;
|
||||
|
||||
public class UpdateFcmTokenRequestDto
|
||||
{
|
||||
public string FcmToken { get; set; } = string.Empty;
|
||||
public string DeviceName { get; set; } = "MobileDevice";
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace FinlyticCore.Models.Auth;
|
||||
|
||||
/// <summary>
|
||||
/// DTO for updating user role or active status by an admin.
|
||||
/// </summary>
|
||||
public class UpdateUserRequestDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Updated user role (User, Premium, Admin).
|
||||
/// </summary>
|
||||
public string? Role { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updated active state of user.
|
||||
/// </summary>
|
||||
public bool? IsActive { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updated full name.
|
||||
/// </summary>
|
||||
public string? FullName { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FinlyticCore.Models.Auth;
|
||||
|
||||
public class UserDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string FullName { get; set; } = string.Empty;
|
||||
public string Role { get; set; } = "User";
|
||||
public bool IsActive { get; set; } = true;
|
||||
public List<string> FcmTokens { get; set; } = new();
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? LastLoginAt { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
namespace FinlyticCore.Models.TradeRepublic;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
// 1. Der Response-Wrapper
|
||||
public record TradeRepublicAssetResponse(
|
||||
[property: JsonPropertyName("correlationId")] string CorrelationId,
|
||||
[property: JsonPropertyName("resultCount")] int ResultCount,
|
||||
[property: JsonPropertyName("results")] IList<TradeRepublicAsset> Results
|
||||
);
|
||||
|
||||
// 2. Das Tag-Objekt
|
||||
public record TradeRepublicTag
|
||||
{
|
||||
[JsonPropertyName("id")] public string Id { get; init; } = "";
|
||||
[JsonPropertyName("name")] public string Name { get; init; } = "";
|
||||
[JsonPropertyName("type")] public string Type { get; init; } = "";
|
||||
}
|
||||
|
||||
// 3. Die Basisklasse MIT UNSEREM CUSTOM CONVERTER (Kein [JsonPolymorphic] mehr!)
|
||||
[JsonConverter(typeof(TradeRepublicAssetConverter))]
|
||||
public record TradeRepublicAsset
|
||||
{
|
||||
[JsonPropertyName("isin")] public string Isin { get; init; } = "";
|
||||
[JsonPropertyName("name")] public string Name { get; init; } = "";
|
||||
[JsonPropertyName("type")] public string Type { get; init; } = "";
|
||||
[JsonPropertyName("instrumentCategory")] public string InstrumentCategory { get; init; } = "";
|
||||
[JsonPropertyName("hasCfd")] public bool HasCfd { get; init; }
|
||||
[JsonPropertyName("imageId")] public string? ImageId { get; init; }
|
||||
|
||||
[JsonPropertyName("tags")]
|
||||
public IReadOnlyList<TradeRepublicTag> Tags { get; init; } = Array.Empty<TradeRepublicTag>();
|
||||
}
|
||||
|
||||
// 4. Die spezifischen Klassen (inklusive Bond und Derivative aus deinem JSON!)
|
||||
|
||||
public record TradeRepublicStock : TradeRepublicAsset
|
||||
{
|
||||
[JsonPropertyName("derivativeProductCategories")]
|
||||
public IReadOnlyList<string> DerivativeProductCategories { get; init; } = Array.Empty<string>();
|
||||
}
|
||||
|
||||
public record TradeRepublicCrypto : TradeRepublicAsset
|
||||
{
|
||||
[JsonPropertyName("subtitle")] public string Subtitle { get; init; } = "";
|
||||
[JsonPropertyName("searchSubtitle")] public string SearchSubtitle { get; init; } = "";
|
||||
}
|
||||
|
||||
public record TradeRepublicEtf : TradeRepublicAsset
|
||||
{
|
||||
[JsonPropertyName("derivativeProductCategories")]
|
||||
public IReadOnlyList<string> DerivativeProductCategories { get; init; } = Array.Empty<string>();
|
||||
[JsonPropertyName("etfDescription")] public string EtfDescription { get; init; } = "";
|
||||
[JsonPropertyName("mappedEtfIndexName")] public string MappedEtfIndexName { get; init; } = "";
|
||||
[JsonPropertyName("subtitle")] public string Subtitle { get; init; } = "";
|
||||
[JsonPropertyName("searchSubtitle")] public string SearchSubtitle { get; init; } = "";
|
||||
}
|
||||
|
||||
public record TradeRepublicSynthetic : TradeRepublicAsset
|
||||
{
|
||||
[JsonPropertyName("derivativeProductCategories")]
|
||||
public IReadOnlyList<string> DerivativeProductCategories { get; init; } = Array.Empty<string>();
|
||||
}
|
||||
|
||||
// NEU: Anleihen
|
||||
public record TradeRepublicBond : TradeRepublicAsset
|
||||
{
|
||||
[JsonPropertyName("bondIssuerName")] public string BondIssuerName { get; init; } = "";
|
||||
[JsonPropertyName("searchSubtitle")] public string SearchSubtitle { get; init; } = "";
|
||||
}
|
||||
|
||||
// NEU: Derivate (Hebeleffekte etc.)
|
||||
public record TradeRepublicDerivative : TradeRepublicAsset
|
||||
{
|
||||
[JsonPropertyName("derivativeProductCategories")]
|
||||
public IReadOnlyList<string> DerivativeProductCategories { get; init; } = Array.Empty<string>();
|
||||
|
||||
[JsonIgnore]
|
||||
public string? UnderlyingIsin
|
||||
{
|
||||
get
|
||||
{
|
||||
// Wenn die ImageId z.B. "logos/US0378331005/v2" ist...
|
||||
if (!string.IsNullOrEmpty(ImageId) && ImageId.StartsWith("logos/"))
|
||||
{
|
||||
var parts = ImageId.Split('/');
|
||||
if (parts.Length >= 2)
|
||||
{
|
||||
return parts[1]; // Gibt "US0378331005" zurück
|
||||
}
|
||||
}
|
||||
return null; // Falls das Format mal anders ist
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Der Custom Converter - Die Maschine, die das JSON scannt und verteilt
|
||||
public class TradeRepublicAssetConverter : JsonConverter<TradeRepublicAsset>
|
||||
{
|
||||
public override TradeRepublicAsset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
using var doc = JsonDocument.ParseValue(ref reader);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// Wir scannen nach instrumentType, egal wo im JSON es steht!
|
||||
string? instrumentType = null;
|
||||
if (root.TryGetProperty("instrumentType", out var typeElement))
|
||||
{
|
||||
instrumentType = typeElement.GetString();
|
||||
}
|
||||
|
||||
// Wir werfen das JSON gezielt in die richtige Klasse
|
||||
TradeRepublicAsset? result = instrumentType switch
|
||||
{
|
||||
"stock" => JsonSerializer.Deserialize<TradeRepublicStock>(root.GetRawText(), options),
|
||||
"crypto" => JsonSerializer.Deserialize<TradeRepublicCrypto>(root.GetRawText(), options),
|
||||
"fund" => JsonSerializer.Deserialize<TradeRepublicEtf>(root.GetRawText(), options),
|
||||
"synthetic" => JsonSerializer.Deserialize<TradeRepublicSynthetic>(root.GetRawText(), options),
|
||||
"bond" => JsonSerializer.Deserialize<TradeRepublicBond>(root.GetRawText(), options),
|
||||
"derivative" => JsonSerializer.Deserialize<TradeRepublicDerivative>(root.GetRawText(), options),
|
||||
|
||||
// Wenn TR einen Typ schickt, den wir noch nicht kennen: Fallback nutzen!
|
||||
_ => JsonSerializer.Deserialize<TradeRepublicAssetFallback>(root.GetRawText(), options)
|
||||
};
|
||||
|
||||
return result ?? new TradeRepublicAssetFallback();
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, TradeRepublicAsset value, JsonSerializerOptions options)
|
||||
{
|
||||
JsonSerializer.Serialize(writer, value, value.GetType(), options);
|
||||
}
|
||||
}
|
||||
|
||||
// Ein reiner Fallback-Record, der nur intern vom Converter genutzt wird
|
||||
file record TradeRepublicAssetFallback : TradeRepublicAsset;
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace FinlyticCore.Models.TradeRepublic;
|
||||
|
||||
public record TradeRepublicConnectRequest(
|
||||
[property: JsonPropertyName("clientId")] string ClientId = "app.traderepublic.com",
|
||||
[property: JsonPropertyName("clientVersion")] string ClientVersion = "15.65.6",
|
||||
[property: JsonPropertyName("locale")] string Locale = "en",
|
||||
[property: JsonPropertyName("platformId")] string PlatformId = "webtrading",
|
||||
[property: JsonPropertyName("platformVersion")] string PlatformVersion = "chrome - 149.0.0",
|
||||
TradeRepublicHeaders? Headers = null
|
||||
)
|
||||
{
|
||||
[JsonPropertyName("__headers")]
|
||||
public TradeRepublicHeaders Headers { get; init; } = Headers ?? new TradeRepublicHeaders();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using FinlyticCore.Util;
|
||||
|
||||
namespace FinlyticCore.Models.TradeRepublic;
|
||||
|
||||
public record TradeRepublicHeaders(
|
||||
[property: JsonPropertyName("traceparent")] string Traceparent
|
||||
)
|
||||
{
|
||||
public TradeRepublicHeaders() : this(StringCodeGenerator.GenerateTraceparent())
|
||||
{}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace FinlyticCore.Models.TradeRepublic;
|
||||
|
||||
public record TradeRepublicFilter(
|
||||
[property: JsonPropertyName("key")] string Key,
|
||||
[property: JsonPropertyName("value")] string Value
|
||||
);
|
||||
|
||||
public record TradeRepublicSearchData(
|
||||
[property: JsonPropertyName("q")] string Query = "",
|
||||
[property: JsonPropertyName("page")] int Page = 1,
|
||||
[property: JsonPropertyName("pageSize")] int PageSize = 50,
|
||||
IReadOnlyList<TradeRepublicFilter>? Filter = null
|
||||
)
|
||||
{
|
||||
[JsonPropertyName("filter")]
|
||||
public IReadOnlyList<TradeRepublicFilter> Filter { get; init; } = Filter ?? Array.Empty<TradeRepublicFilter>();
|
||||
}
|
||||
|
||||
public record TradeRepublicSearchRequest(
|
||||
[property: JsonPropertyName("data")] TradeRepublicSearchData Data,
|
||||
[property: JsonPropertyName("type")] string Type = "neonSearch",
|
||||
TradeRepublicHeaders? Headers = null
|
||||
)
|
||||
{
|
||||
[JsonPropertyName("__headers")]
|
||||
public TradeRepublicHeaders Headers { get; init; } = Headers ?? new TradeRepublicHeaders();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace FinlyticCore.Models.TradeRepublic;
|
||||
|
||||
public record TradeRepublicTickerRequest(
|
||||
[property: JsonPropertyName("id")] string Id, // e.g. "US5398301094.TIB"
|
||||
[property: JsonPropertyName("type")] string Type = "ticker",
|
||||
TradeRepublicHeaders? Headers = null
|
||||
)
|
||||
{
|
||||
[JsonPropertyName("__headers")]
|
||||
public TradeRepublicHeaders Headers { get; init; } = Headers ?? new TradeRepublicHeaders();
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace FinlyticCore.Models.TradeRepublic;
|
||||
|
||||
public record TradeRepublicPriceTick(
|
||||
[property: JsonPropertyName("time")] long Time,
|
||||
[property: JsonPropertyName("price")] string Price,
|
||||
[property: JsonPropertyName("size")] decimal Size
|
||||
)
|
||||
{
|
||||
public decimal PriceValue => decimal.TryParse(Price, NumberStyles.Any, CultureInfo.InvariantCulture, out var v) ? v : 0m;
|
||||
public DateTime DateTimeUtc => DateTimeOffset.FromUnixTimeMilliseconds(Time).UtcDateTime;
|
||||
}
|
||||
|
||||
public record TradeRepublicTickerResponse(
|
||||
[property: JsonPropertyName("bid")] TradeRepublicPriceTick? Bid,
|
||||
[property: JsonPropertyName("ask")] TradeRepublicPriceTick? Ask,
|
||||
[property: JsonPropertyName("last")] TradeRepublicPriceTick? Last,
|
||||
[property: JsonPropertyName("pre")] TradeRepublicPriceTick? Pre,
|
||||
[property: JsonPropertyName("open")] TradeRepublicPriceTick? Open,
|
||||
[property: JsonPropertyName("qualityId")] string? QualityId,
|
||||
[property: JsonPropertyName("leverage")] decimal? Leverage,
|
||||
[property: JsonPropertyName("delta")] decimal? Delta
|
||||
);
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace FinlyticCore.Models.Trades;
|
||||
|
||||
/// <summary>
|
||||
/// Request payload for manually closing an active trade via REST API.
|
||||
/// </summary>
|
||||
public class CloseTradeRequest
|
||||
{
|
||||
public decimal UserExitPrice { get; set; }
|
||||
public DateTime? UserExitTimestamp { get; set; }
|
||||
public string CloseReason { get; set; } = "ManualClosure"; // "TakeProfitHit", "StopLossHit", "ManualClosure", "TimeExpired"
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace FinlyticCore.Models.Trades;
|
||||
|
||||
public class TradeAcceptanceDto
|
||||
{
|
||||
public string TradeId { get; set; } = string.Empty;
|
||||
public string AnalysisId { get; set; } = string.Empty;
|
||||
public string Isin { get; set; } = string.Empty;
|
||||
public string? UserId { get; set; } = "default_user";
|
||||
|
||||
public decimal? ActualEntryPrice { get; set; }
|
||||
public decimal? PositionSize { get; set; }
|
||||
public decimal? LeverageUsed { get; set; } = 1;
|
||||
public decimal? EntryFee { get; set; } = 0;
|
||||
public decimal? ExitFee { get; set; } = 0;
|
||||
|
||||
public string? Symbol { get; set; }
|
||||
public string? SignalType { get; set; }
|
||||
public decimal? EntryPrice { get; set; }
|
||||
public decimal? StopLoss { get; set; }
|
||||
public decimal? TakeProfit { get; set; }
|
||||
public string? InstrumentType { get; set; }
|
||||
public string? Timeframe { get; set; }
|
||||
public string? Reasoning { get; set; }
|
||||
|
||||
public DateTime? ExecutionTimestamp { get; set; }
|
||||
public decimal? Quantity { get; set; }
|
||||
public decimal? KnockoutThreshold { get; set; }
|
||||
public bool IsRecurring { get; set; } = false;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using FinlyticCore.Models.Analyzer;
|
||||
|
||||
namespace FinlyticCore.Models.Trades;
|
||||
|
||||
/// <summary>
|
||||
/// Structured closed trade record exported to JSON/Parquet for AI win-rate calibration feedback loops.
|
||||
/// </summary>
|
||||
public class TradeFeedbackRecord
|
||||
{
|
||||
public string TradeId { get; set; } = string.Empty;
|
||||
public string AnalysisId { get; set; } = string.Empty;
|
||||
public string Sector { get; set; } = string.Empty;
|
||||
public string Symbol { get; set; } = string.Empty;
|
||||
public string Isin { get; set; } = string.Empty;
|
||||
|
||||
public decimal EntryPrice { get; set; }
|
||||
public decimal StopLoss { get; set; }
|
||||
public decimal TakeProfit { get; set; }
|
||||
public decimal UserExitPrice { get; set; }
|
||||
|
||||
public decimal PnlAbsolute { get; set; }
|
||||
public decimal PnlPercent { get; set; }
|
||||
public bool IsWin { get; set; }
|
||||
|
||||
public string CloseReason { get; set; } = string.Empty;
|
||||
public VixMarketRegime VixRegime { get; set; }
|
||||
public decimal VixValue { get; set; }
|
||||
|
||||
public double ReactionDelayMinutes { get; set; }
|
||||
public decimal SlippagePercent { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime ClosedAt { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace FinlyticCore.Models.Trades;
|
||||
|
||||
/// <summary>
|
||||
/// Hourly AI recommendation update for an active trade.
|
||||
/// </summary>
|
||||
public class TradeHourlyUpdateDto
|
||||
{
|
||||
public string TradeId { get; set; } = string.Empty;
|
||||
public string Recommendation { get; set; } = "Hold"; // "Hold", "AdjustSL", "AdjustTP", "Close"
|
||||
|
||||
public decimal CurrentPrice { get; set; }
|
||||
public decimal? SuggestedStopLoss { get; set; }
|
||||
public decimal? SuggestedTakeProfit { get; set; }
|
||||
public decimal VixValue { get; set; }
|
||||
|
||||
public string Reasoning { get; set; } = string.Empty;
|
||||
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FinlyticCore.Models.Analyzer;
|
||||
|
||||
namespace FinlyticCore.Models.Trades;
|
||||
|
||||
/// <summary>
|
||||
/// Trade proposal generated by FinlyticAnalyzer and dispatched via MQTT QoS 2.
|
||||
/// </summary>
|
||||
public class TradeProposalDto
|
||||
{
|
||||
public string TradeId { get; set; } = string.Empty;
|
||||
public string? UserId { get; set; }
|
||||
public bool IsGlobalProposal { get; set; } = true;
|
||||
public string Status { get; set; } = "Proposed";
|
||||
|
||||
public string AnalysisId { get; set; } = string.Empty;
|
||||
public string EventId { get; set; } = string.Empty;
|
||||
public string Sector { get; set; } = string.Empty;
|
||||
public string Symbol { get; set; } = string.Empty;
|
||||
public string Isin { get; set; } = string.Empty;
|
||||
public string CompanyName { get; set; } = string.Empty;
|
||||
|
||||
public decimal EntryPrice { get; set; }
|
||||
public decimal StopLoss { get; set; }
|
||||
public decimal TakeProfit { get; set; }
|
||||
|
||||
public string SignalType { get; set; } = "BUY"; // "BUY", "SELL"
|
||||
public string RiskTolerance { get; set; } = "Moderate"; // "Conservative", "Moderate", "Aggressive"
|
||||
public string Timeframe { get; set; } = "1D"; // "1H", "4H", "1D", "1W"
|
||||
public string InstrumentType { get; set; } = "Stock"; // "Stock", "Option", "CFD", "Crypto"
|
||||
|
||||
public double WinRate { get; set; }
|
||||
public VixMarketRegime VixRegime { get; set; }
|
||||
public decimal VixValue { get; set; }
|
||||
|
||||
public int TtlMinutes { get; set; } = 60;
|
||||
public string Reasoning { get; set; } = string.Empty;
|
||||
|
||||
// --- New Fields for Detailed Execution & Rationale ---
|
||||
public decimal? EntryZoneMin { get; set; }
|
||||
public decimal? EntryZoneMax { get; set; }
|
||||
public List<decimal>? TakeProfitTargets { get; set; }
|
||||
public decimal? RiskRewardRatio { get; set; }
|
||||
public decimal? MaxLeverage { get; set; }
|
||||
|
||||
public string TechnicalRationale { get; set; } = string.Empty;
|
||||
public string FundamentalRationale { get; set; } = string.Empty;
|
||||
public string RiskWarning { get; set; } = string.Empty;
|
||||
|
||||
// --- Real Trade Execution Data ---
|
||||
public decimal? ActualEntryPrice { get; set; }
|
||||
public decimal? PositionSize { get; set; }
|
||||
public decimal? LeverageUsed { get; set; }
|
||||
public decimal? EntryFee { get; set; }
|
||||
public decimal? ExitFee { get; set; }
|
||||
public DateTime? ExecutionTimestamp { get; set; }
|
||||
public decimal? Quantity { get; set; }
|
||||
public decimal? KnockoutThreshold { get; set; }
|
||||
public bool IsRecurring { get; set; } = false;
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace FinlyticCore.Models.Trades;
|
||||
|
||||
/// <summary>
|
||||
/// Status of a proposed/active trade lifecycle.
|
||||
/// </summary>
|
||||
public enum TradeStatus
|
||||
{
|
||||
Proposed = 0,
|
||||
Active = 1,
|
||||
Closed = 2,
|
||||
Expired = 3,
|
||||
Rejected = 4,
|
||||
Invalidated = 5
|
||||
}
|
||||
Reference in New Issue
Block a user