XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Text.Json;
using XFEEDToolBox.Core.Models;

namespace XFEEDToolBox.Core.Execution;

public sealed record AutomaticTradeRunSession(string Id,DateTimeOffset StartedAt,decimal BaselineNetProfit,bool Active)
{
    public decimal Profit(decimal confirmedTotal)=>confirmedTotal-BaselineNetProfit;
    public static AutomaticTradeRunSession Open(string root,decimal confirmedTotal)
    {
        var path=Path.Combine(root,"run-session.json");
        if(File.Exists(path)&&JsonSerializer.Deserialize<AutomaticTradeRunSession>(File.ReadAllText(path),ToolboxSettings.JsonOptions) is {Active:true} existing)return existing;
        var created=new AutomaticTradeRunSession(Guid.NewGuid().ToString("N"),DateTimeOffset.UtcNow,confirmedTotal,true);Save(root,created);return created;
    }
    public static void Finish(string root)
    {
        var path=Path.Combine(root,"run-session.json");if(!File.Exists(path))return;
        var current=JsonSerializer.Deserialize<AutomaticTradeRunSession>(File.ReadAllText(path),ToolboxSettings.JsonOptions);if(current is not null)Save(root,current with{Active=false});
    }
    private static void Save(string root,AutomaticTradeRunSession value)
    {Directory.CreateDirectory(root);var path=Path.Combine(root,"run-session.json");File.WriteAllText(path+".tmp",JsonSerializer.Serialize(value,ToolboxSettings.JsonOptions));File.Move(path+".tmp",path,true);}
}

public sealed record AutomaticTradeEstimate(decimal NetProfit,double Minutes,string Basis)
{
    public decimal Hourly=>Minutes>0?NetProfit*60/(decimal)Minutes:0;
    public static AutomaticTradeEstimate For(TradeLeg leg,string root,long? ship)
    {
        var samples=new List<(DateTimeOffset At,double Minutes,decimal Costs)>();
        foreach(var directory in Directory.EnumerateDirectories(root,"journey-*"))
        {
            var r=Path.Combine(directory,"result.json");var p=Path.Combine(directory,"leg.json");if(!File.Exists(r)||!File.Exists(p))continue;
            var result=JsonSerializer.Deserialize<NativeTradeJourneyResult>(File.ReadAllText(r),ToolboxSettings.JsonOptions);
            if(result?.Passed!=true||result.Checkpoint.ShipId!=ship)continue;
            var prior=JsonSerializer.Deserialize<TradeLeg>(File.ReadAllText(p),ToolboxSettings.JsonOptions)!;var duration=(result.Finished-result.Checkpoint.StartedAt).TotalMinutes;
            if(prior.From.MarketId==leg.From.MarketId&&prior.To.MarketId==leg.To.MarketId&&duration is >=1 and <=30)samples.Add((result.Finished,duration,result.Profit.Expenses));
        }
        var recent=samples.OrderByDescending(s=>s.At).Take(5).ToArray();
        var fallback=2+1.5+1+Math.Sqrt(Math.Max(0,leg.To.DistanceToArrivalLs??1000))/32;
        var minutes=recent.Length>0?recent.Average(s=>s.Minutes):leg.EstimatedMinutes>0?leg.EstimatedMinutes:fallback;
        var costs=recent.Length>0?recent.Average(s=>s.Costs):leg.OperatingCost;
        return new(leg.GrossProfit-costs,minutes,recent.Length>0?"按同船同航段近期实测估算,实际利润以成交及费用回执为准。":"按路线参数估算,尚无同船完整实测;实际费用以回执为准。");
    }
}