using XFEEDToolBox.Core.Execution;
using XFEEDToolBox.Core.Models;
using XFEExtension.NetCore.XUnit.Attributes;
namespace XFEEDToolBox.Test;
[TestFixture]
public class AutomaticTradeTests
{
private static Station Station(long id)=>new(){MarketId=id,Name="Station"+id,SystemName="System"+id};
private static CommodityQuote Quote(long market,string key,long buy,long sell,long stock=10000,long demand=10000)=>new()
{MarketId=market,Commodity=key,DisplayName=key,BuyPrice=buy,SellPrice=sell,Supply=stock,Demand=demand,CollectedAt=DateTimeOffset.UtcNow,Source=DataSource.Journal};
[Test]public void ArbitraryUserStationsAndAutomaticCapacityAreValid()
{
using var directory=new TemporaryDirectory();var value=new AutomaticTradeConfiguration{Source=Station(7),Destination=Station(98),WorkingDirectory=directory.Path};
value.Validate();(value with{CargoCapacity=752}).Validate();(value with{CargoCapacity=2048}).Validate();
Check.Throws<InvalidDataException>(()=>(value with{Destination=Station(7)}).Validate());
}
[Test]public void InterdictionDefaultsToImmediateExitAndKeepsLegacyRoutesCompatible()
{
using var directory=new TemporaryDirectory();var value=new AutomaticTradeConfiguration{Source=Station(7),Destination=Station(98),WorkingDirectory=directory.Path};
Check.That(value.ExitOnInterdiction&&!value.RequireInterdictionEscape);value.Validate();(value with{RequireInterdictionEscape=true}).Validate();
Check.Throws<InvalidDataException>(()=>(value with{ExitOnInterdiction=false}).Validate());
}
[Test]public void OrdinaryCargoCanUseNonCgCommoditiesAndAllOfTheLiveHold()
{
var order=AutomaticTradePlanner.Select([Quote(1,"gold",100,90)],[Quote(2,"gold",50,120)],752,1000000,2000);
Check.Equal("gold",order.Commodity);Check.Equal(752,order.Quantity);
}
[Test]public void ReserveStockDemandAndConfiguredCgGoodsAllBoundTheOrder()
{
var source=new[]{Quote(1,"gold",100,120,99),Quote(1,"uranium",10,20,1000)};
var target=new[]{Quote(2,"gold",1,900,demand:5),Quote(2,"uranium",1,20,demand:8)};
var ordinary=AutomaticTradePlanner.Select(source,target,752,1000000,1000);Check.Equal("gold",ordinary.Commodity);Check.Equal(5,ordinary.Quantity);
var cg=AutomaticTradePlanner.Select(source,target,752,1025,1000,["uranium"]);Check.Equal("uranium",cg.Commodity);Check.Equal(2,cg.Quantity);
Check.Throws<InvalidOperationException>(()=>AutomaticTradePlanner.Select(source,target,752,999,1000));
}
[Test]public void ExplicitInvestmentCapsEveryPurchaseAfterWalletProfitsIncrease()
{
using var directory=new TemporaryDirectory();
var config=new AutomaticTradeConfiguration{Source=Station(1),Destination=Station(2),WorkingDirectory=directory.Path,ReserveCredits=100,InvestmentCredits=300};
config.Validate();
var source=new[]{Quote(1,"gold",10,8)};var target=new[]{Quote(2,"gold",8,20)};
foreach(var wallet in new[]{1000m,2000m,10000m})
{
var protectedCredits=config.PurchaseReserve(wallet,50,20);
var order=AutomaticTradePlanner.Select(source,target,752,wallet,protectedCredits);
Check.Equal(28,order.Quantity);Check.That(order.Cost+20<=config.InvestmentCredits);
}
Check.Equal(920m,config.PurchaseReserve(1000,900,20));
}
[Test]public void AllFundsAndLegacyConfigurationKeepOperationalReservesWithoutAnInvestmentCap()
{
using var directory=new TemporaryDirectory();
var config=new AutomaticTradeConfiguration{Source=Station(1),Destination=Station(2),WorkingDirectory=directory.Path,ReserveCredits=100};
Check.That(config.InvestmentCredits is null);Check.Equal(120m,config.PurchaseReserve(1000,50,20));Check.Equal(120m,config.PurchaseReserve(2000,50,20));
Check.Throws<InvalidDataException>(()=>(config with{InvestmentCredits=0}).Validate());
Check.Throws<InvalidDataException>(()=>(config with{InvestmentCredits=-1}).Validate());
}
[Test]public void TradeCgSelectionUsesDestinationAndIdentityRatherThanOneFixedTitle()
{
var goal=new CommunityGoalInfo(42,"Other trade initiative","tradelist","trade",987,"Other system","Other station",DateTimeOffset.UtcNow.AddDays(1),true,true,10);
Check.Equal(42UL,CommunityGoalCatalogue.RequireDestinationTrade([goal],987,42,DateTimeOffset.UtcNow,true).Id);
Check.Throws<InvalidDataException>(()=>CommunityGoalCatalogue.RequireDestinationTrade([goal],986,42,DateTimeOffset.UtcNow,true));
Check.Throws<InvalidDataException>(()=>CommunityGoalCatalogue.RequireDestinationTrade([goal with{Subscribed=false}],987,42,DateTimeOffset.UtcNow,true));
}
}
using XFEEDToolBox.Core.Execution;
using XFEEDToolBox.Core.Models;
using XFEExtension.NetCore.XUnit.Attributes;
namespace XFEEDToolBox.Test;
[TestFixture]
public class AutomaticTradeTests
{
private static Station Station(long id)=>new(){MarketId=id,Name="Station"+id,SystemName="System"+id};
private static CommodityQuote Quote(long market,string key,long buy,long sell,long stock=10000,long demand=10000)=>new()
{MarketId=market,Commodity=key,DisplayName=key,BuyPrice=buy,SellPrice=sell,Supply=stock,Demand=demand,CollectedAt=DateTimeOffset.UtcNow,Source=DataSource.Journal};
[Test]public void ArbitraryUserStationsAndAutomaticCapacityAreValid()
{
using var directory=new TemporaryDirectory();var value=new AutomaticTradeConfiguration{Source=Station(7),Destination=Station(98),WorkingDirectory=directory.Path};
value.Validate();(value with{CargoCapacity=752}).Validate();(value with{CargoCapacity=2048}).Validate();
Check.Throws<InvalidDataException>(()=>(value with{Destination=Station(7)}).Validate());
}
[Test]public void InterdictionDefaultsToImmediateExitAndKeepsLegacyRoutesCompatible()
{
using var directory=new TemporaryDirectory();var value=new AutomaticTradeConfiguration{Source=Station(7),Destination=Station(98),WorkingDirectory=directory.Path};
Check.That(value.ExitOnInterdiction&&!value.RequireInterdictionEscape);value.Validate();(value with{RequireInterdictionEscape=true}).Validate();
Check.Throws<InvalidDataException>(()=>(value with{ExitOnInterdiction=false}).Validate());
}
[Test]public void OrdinaryCargoCanUseNonCgCommoditiesAndAllOfTheLiveHold()
{
var order=AutomaticTradePlanner.Select([Quote(1,"gold",100,90)],[Quote(2,"gold",50,120)],752,1000000,2000);
Check.Equal("gold",order.Commodity);Check.Equal(752,order.Quantity);
}
[Test]public void ReserveStockDemandAndConfiguredCgGoodsAllBoundTheOrder()
{
var source=new[]{Quote(1,"gold",100,120,99),Quote(1,"uranium",10,20,1000)};
var target=new[]{Quote(2,"gold",1,900,demand:5),Quote(2,"uranium",1,20,demand:8)};
var ordinary=AutomaticTradePlanner.Select(source,target,752,1000000,1000);Check.Equal("gold",ordinary.Commodity);Check.Equal(5,ordinary.Quantity);
var cg=AutomaticTradePlanner.Select(source,target,752,1025,1000,["uranium"]);Check.Equal("uranium",cg.Commodity);Check.Equal(2,cg.Quantity);
Check.Throws<InvalidOperationException>(()=>AutomaticTradePlanner.Select(source,target,752,999,1000));
}
[Test]public void ExplicitInvestmentCapsEveryPurchaseAfterWalletProfitsIncrease()
{
using var directory=new TemporaryDirectory();
var config=new AutomaticTradeConfiguration{Source=Station(1),Destination=Station(2),WorkingDirectory=directory.Path,ReserveCredits=100,InvestmentCredits=300};
config.Validate();
var source=new[]{Quote(1,"gold",10,8)};var target=new[]{Quote(2,"gold",8,20)};
foreach(var wallet in new[]{1000m,2000m,10000m})
{
var protectedCredits=config.PurchaseReserve(wallet,50,20);
var order=AutomaticTradePlanner.Select(source,target,752,wallet,protectedCredits);
Check.Equal(28,order.Quantity);Check.That(order.Cost+20<=config.InvestmentCredits);
}
Check.Equal(920m,config.PurchaseReserve(1000,900,20));
}
[Test]public void AllFundsAndLegacyConfigurationKeepOperationalReservesWithoutAnInvestmentCap()
{
using var directory=new TemporaryDirectory();
var config=new AutomaticTradeConfiguration{Source=Station(1),Destination=Station(2),WorkingDirectory=directory.Path,ReserveCredits=100};
Check.That(config.InvestmentCredits is null);Check.Equal(120m,config.PurchaseReserve(1000,50,20));Check.Equal(120m,config.PurchaseReserve(2000,50,20));
Check.Throws<InvalidDataException>(()=>(config with{InvestmentCredits=0}).Validate());
Check.Throws<InvalidDataException>(()=>(config with{InvestmentCredits=-1}).Validate());
}
[Test]public void TradeCgSelectionUsesDestinationAndIdentityRatherThanOneFixedTitle()
{
var goal=new CommunityGoalInfo(42,"Other trade initiative","tradelist","trade",987,"Other system","Other station",DateTimeOffset.UtcNow.AddDays(1),true,true,10);
Check.Equal(42UL,CommunityGoalCatalogue.RequireDestinationTrade([goal],987,42,DateTimeOffset.UtcNow,true).Id);
Check.Throws<InvalidDataException>(()=>CommunityGoalCatalogue.RequireDestinationTrade([goal],986,42,DateTimeOffset.UtcNow,true));
Check.Throws<InvalidDataException>(()=>CommunityGoalCatalogue.RequireDestinationTrade([goal with{Subscribed=false}],987,42,DateTimeOffset.UtcNow,true));
}
}