This commit is contained in:
bob
2026-06-02 13:31:27 -05:00
parent 15310f0fd0
commit edc649fc36
33 changed files with 6407 additions and 8 deletions

View File

@@ -0,0 +1,122 @@
namespace BlueLaminate.Core.Options;
/// <summary>
/// Which StatTrak universes the finder searches. The two input pools are disjoint and
/// never mix in a contract: non-ST inputs (normal souvenir) produce a normal output,
/// ST inputs produce an ST output.
/// </summary>
public enum StatTrakMode
{
/// <summary>Search both the non-ST and ST universes (default).</summary>
Both,
/// <summary>Only the non-ST universe (normal + souvenir inputs → normal output).</summary>
NonStatTrakOnly,
/// <summary>Only the StatTrak universe (ST inputs → ST output).</summary>
StatTrakOnly,
}
/// <summary>
/// How to rank surviving candidates.
/// </summary>
public enum TradeupRanking
{
/// <summary>By worst-case (minimum across outputs) net profit — low variance.</summary>
WorstCaseProfit,
/// <summary>By expected net profit across the output distribution.</summary>
ExpectedProfit,
}
/// <summary>
/// Tuning for the tradeup finder, bound from the <c>Tradeups</c> configuration section.
/// Defaults are sensible for CS2 marketplaces (15% sell fee) and a conservative v1
/// (guaranteed-profit only). Everything here is economics/policy — none of it lives in
/// the CLI.
/// </summary>
public sealed class TradeupOptions
{
public const string SectionName = "Tradeups";
/// <summary>Number of inputs per contract. v1 supports 10-input weapon tradeups only.</summary>
public int ContractSize { get; set; } = 10;
/// <summary>
/// Fraction of the sale price taken as marketplace commission when selling an output
/// (0.15 = 15%). Applied to the realised sell price.
/// </summary>
public decimal SellFeeRate { get; set; } = 0.15m;
/// <summary>
/// Fraction shaved off the lowest active ask to model undercutting it for a quick
/// sale (0.01 = list 1% under the cheapest competitor). Applied before the fee.
/// </summary>
public decimal UndercutRate { get; set; } = 0.01m;
/// <summary>
/// Bucket width used to discretise normalised input fractions for the
/// cardinality-constrained selection DP. Smaller = finer output-float resolution at
/// higher cost. 0.005 resolves the wear boundaries to within 0.005 of output float.
/// </summary>
public decimal FractionBucket { get; set; } = 0.005m;
/// <summary>
/// When true (v1 default) only contracts whose worst-case output still clears input
/// cost survive — a guaranteed profit. When false, any positive-EV contract survives.
/// </summary>
public bool GuaranteedOnly { get; set; } = true;
/// <summary>Minimum net profit (in the listing currency) for a candidate to be reported.</summary>
public decimal MinProfit { get; set; } = 0m;
/// <summary>How surviving candidates are ordered.</summary>
public TradeupRanking Ranking { get; set; } = TradeupRanking.WorstCaseProfit;
/// <summary>Which StatTrak universes to search.</summary>
public StatTrakMode StatTrak { get; set; } = StatTrakMode.Both;
/// <summary>
/// Currency listings must be in to be comparable. The finder ignores listings in
/// other currencies rather than converting (v1 keeps a single money space).
/// </summary>
public string Currency { get; set; } = "USD";
/// <summary>
/// When a proposed output has fewer than this many active listings in our data, its
/// stored lowest-ask is fragile, so the finder re-prices it from the live CSFloat API.
/// </summary>
public int CsFloatThinOutputThreshold { get; set; } = 10;
/// <summary>
/// Enables the live CSFloat re-pricing of thin outputs. Silently inert when no CSFloat
/// API key is configured.
/// </summary>
public bool UseCsFloatForThinOutputs { get; set; } = true;
/// <summary>
/// Hard cap on live CSFloat lookups per search, so the re-pricing pass can't blow the
/// API rate-limit budget. Distinct (skin, ST, wear band) lookups are cached within a run.
/// </summary>
public int CsFloatMaxLookups { get; set; } = 120;
/// <summary>
/// Enables the multi-collection search: alongside the single-collection pass, it mixes
/// inputs from any collections at a rarity tier to maximise expected profit. Off keeps the
/// finder single-collection only.
/// </summary>
public bool MultiCollection { get; set; } = true;
/// <summary>
/// Step of the output-float target grid the multi-collection search sweeps. Each grid
/// point is an independent, parallelised chunk (one knapsack over the tier's pool), so a
/// finer grid is more thorough but does more work. 0.02 ≈ 50 chunks per tier × ST.
/// </summary>
public decimal MultiCollectionFloatGrid { get; set; } = 0.02m;
/// <summary>
/// How many distinct multi-collection contracts to keep per (rarity tier, StatTrak), best
/// expected-profit first, after de-duplicating by collection mix. Caps result volume.
/// </summary>
public int MultiCollectionPerTier { get; set; } = 8;
}