How to Backtest a CS2 Skin Trading Strategy
Updated
Backtest a CS2 skin strategy by matching its decision to the historical series that actually represents it, calculating signals only after each observation was available, and simulating the next possible buy or sale with fees and liquidity limits. Use cs2.sh's
POST /v1/prices/historyfor intraday listing and buy-order OHLC; use the CSFloat or Steam archives when the strategy depends on completed sales and sale volume.
Scope: This is a broad, high-level v0 guide, not a complete application. cs2.sh is designed as a flexible market-data layer for whatever you want to build on top of it, so adapt the implementation to your own product, strategy, and integrations.
A backtest output should contain trades, costs, profit, drawdown, exposure time, turnover, missing-data counts, and results split by item and liquidity class. A single return percentage cannot reveal a strategy that works only on one liquid case or on observations that were never tradable.
Choose history by the decision being tested
| Strategy question | Series | What the price means |
|---|---|---|
| Could a listing-price signal be observed intraday? | POST /v1/prices/history |
Ask or bid OHLC built from current marketplace snapshots |
| Where did CSFloat items sell each day? | POST /v1/archive/csfloat |
Daily arithmetic average completed-sale price and sale count |
| Where did Steam items sell over a long period? | POST /v1/archive/steam |
Steam median sale price and purchase count |
| How did long-term asks, bids, supply, and approximate volume change? | POST /v1/archive/history |
Last observed listing and buy-order values, approximate sales, and supply |
| What did BUFF items sell for, and how much supply existed? | POST /v1/archive/buff |
BUFF sale prices with daily total supply, without sale volume |
| What sale did Youpin expose in each sampling window? | POST /v1/archive/youpin |
One sale per independent 1h, 4h, or 12h series, without sale volume |
Listing OHLC and completed-sale archives are not interchangeable. An ask close shows the lowest sell listing at the end of an observation bucket. It does not prove a purchase occurred at that price. A daily completed-sale average shows actual CSFloat sales but cannot reproduce an intraday entry time.
Build a point-in-time dataset
Request one source and one interval at a time, in batches of at most 100 item names. Normalize each series into rows that retain the item, variant, source, bucket, price fields, observation times, sample_count, and volume fields that actually exist.
For prices/history, bucket is the UTC interval boundary, while open_time and close_time are the first and last real observations inside it. Calculate a close-based signal only after close_time. A safe simulation acts no earlier than the next observation that your rule permits.
Do not forward-fill across an unknown market gap without recording the assumption. A missing source object means no observation exists for that source and bucket. Filling it with zero creates artificial crashes; carrying it indefinitely creates artificial stability.
Prevent lookahead bias
The following rules remove the most common impossible results:
- Derive indicators from buckets whose
close_timeis at or before the decision time. - Enter on a later observation, not on the same bucket's favorable low after using its close to create the signal.
- Use only historical information to classify volume or liquidity. The current
GET /v1/liquidity/itemssnapshot cannot be applied backward as if its daily classification were known months earlier. - Compute rolling features with trailing windows. Centered averages and full-period normalization expose future prices.
- Freeze strategy parameters before evaluating the held-out time range or held-out item set.
If the strategy buys when an ask closes below a moving average, a conservative fill assumption is the next eligible ask plus purchase costs. If it sells into buyer demand, use a later eligible bid minus selling and withdrawal costs. Make the assumption explicit because OHLC observations are market state, not guaranteed fills.
Add fees, spread, and capacity
At minimum, model:
net_return = (sale_price - sell_costs - buy_price - buy_costs) / (buy_price + buy_costs)Use source-specific fees and fixed payment or withdrawal costs. Keep Steam Wallet strategies separate from cash-market strategies. A Steam sale price is denominated in USD by the API but still represents value that settles inside Steam.
Order counts and sale volume limit how much a historical result can reasonably scale. A strategy that assigns ten purchases to a day with one completed sale has exceeded the evidence. ask_volume and bid_volume in OHLC are last-observed active-order counts, while archive volume has endpoint-specific sale meaning. Do not substitute one for the other.
Full orderbook history can support more detailed capacity assumptions for Steam and CSFloat. Those endpoints return sampled books rather than OHLC, so each bucket contains the last complete orderbook collected inside the interval. They still do not prove that a hypothetical order would have been filled before another participant acted.
Handle variants and exact assets
Doppler, Gamma Doppler, and supported Case Hardened variants can have their own price history. Backtest the nested variant series directly and keep its version in every row. Falling back from a missing Phase 2 observation to the base Doppler price changes the asset being tested.
Steam archive history does not support variants. Exact paint seeds, stickers, and individual floats can also trade away from the generic or range-level market, so a generic historical series cannot validate an exact-asset strategy.
Report the evidence behind the result
For each run, store the endpoint, source, interval, requested time range, item universe, fee version, missing-data policy, fill assumption, and strategy parameters. Report trade count and item coverage beside returns.
Separate in-sample and out-of-sample results, and show performance after costs by item and time period. A strategy that loses its advantage after the bid-ask difference and fees are applied has no historical evidence of executable profit, even if its signal predicted the direction of listing prices.
References: OHLC price history, BUFF sale history, CSFloat completed sales, Steam sale history, archive history, and historical coverage.