using XFEEDToolBox.Core.Models; namespace XFEEDToolBox.Core.Execution; public static class AutomaticTradePlanner { public static TradeOrder Select(IEnumerable source,IEnumerable destination,int capacity, decimal credits,decimal reserve,IReadOnlyCollection? 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(); 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("当前两站的实时价格、库存、需求或保留资金不支持有利润的采购。"); } }