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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

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

namespace XFEEDToolBox.Core.Execution;

public static class AutomaticTradePlanner
{
    public static TradeOrder Select(IEnumerable<CommodityQuote> source,IEnumerable<CommodityQuote> destination,int capacity,
        decimal credits,decimal reserve,IReadOnlyCollection<string>? commodities=null)
    {
        if(capacity<=0||credits<0||reserve<0)throw new ArgumentOutOfRangeException(nameof(capacity));
        var targets=destination.Where(q=>!q.Illegal&&!q.Rare).GroupBy(q=>q.Commodity).ToDictionary(g=>g.Key,g=>g.OrderByDescending(q=>q.CollectedAt).First());
        var orders=new List<TradeOrder>();
        foreach(var quote in source.Where(q=>!q.Illegal&&!q.Rare))
        {
            if(!NativeTradeBatchPolicy.AllowsFormal(quote.Commodity)||commodities is {Count:>0}&&!commodities.Contains(quote.Commodity)
                ||!targets.TryGetValue(quote.Commodity,out var target)||quote.BuyPrice is <=0 or >NativeTradeBatchPolicy.MaximumUnitPrice
                ||target.SellPrice<=quote.BuyPrice||target.SellPrice>NativeTradeBatchPolicy.MaximumUnitPrice||quote.Supply<=0||target.Demand<=0)continue;
            var affordable=Math.Max(0,decimal.Floor((credits-reserve)/quote.BuyPrice));
            var quantity=(int)Math.Min(Math.Min(capacity,quote.Supply),Math.Min(target.Demand,affordable));
            if(quantity>0)orders.Add(new(quote.Commodity,quote.DisplayName,quantity,quote.BuyPrice,target.SellPrice));
        }
        return orders.OrderByDescending(o=>((decimal)o.SellPrice-o.BuyPrice)*o.Quantity).ThenBy(o=>o.Commodity,StringComparer.Ordinal).FirstOrDefault()
            ??throw new InvalidOperationException("当前两站的实时价格、库存、需求或保留资金不支持有利润的采购。");
    }
}