2,500,000 Cents
From 2013–2015 I ran an arbitrage strategy on the Steam Community Market. I would buy slightly under-priced items (mostly re-skinned weapons for CS:GO, Dota 2, and TF2) on the market, resell them for a few cents more, and repeat this up to 10,000 times per day. In the end, I traded over $1M, made around 25k after fees and taxes, and perhaps most amusingly, I made Valve over 150k in transaction fees.
Before I describe what I was doing and some of the challenges involved, let me paint a more detailed picture with stats from one account I used. The account traded $190,160 in Steam store credit over 587,906 trades. It’d buy a typical item for $0.307 and resell it for $0.334 (after Valves’s fees). These trades generated a profit of $6,035 in Steam credit, which could then be converted to about $4,800 actual USD by selling items off-market at a ~20% discount. So at the end of the day, the typical arbitrage opportunity was worth about $0.02.
Here are the distributions of the prices of buy and sell orders (there are some interesting patterns):
This account traded 5,429 different assets. The most profitable asset made a profit of $45.38. Most assets were much less profitable than this:
The maximum number of times I traded any given item was 4,227.
Disclaimer: automation is a violation of Steam’s terms of service. This eventually led to my accounts getting banned. This strategy also does not work well anymore.
Background
One summer night in 2013, I noticed that the price history of certain items on the market basically looked like white noise (independent samples from a fixed distribution), even over weeks. This suggested a simple strategy: buy items listed below the mean price and relist them just above it. Steam took a hearty ~15% fee from the seller, though, so the price difference had to be pretty high. One other thing to note is that the market was one-sided, which strongly incentivized automation.
Was this viable? I quickly wrote a script to buy a mis-priced item, and to my surprise, it worked. I was able to sell the item, making a couple cents. The main challenge now was the following: there are tens (now hundreds) of thousands of items on the market. How do you find those whose price “looks like white noise”, at least in the “short term”?
The problem of identifying time series that behave like white noise is answered using standard statistical tests (I found the Ljung-Box test to work well). The issue is figuring out what “short term” should mean. Intuitively, the longer you define “short” to be, the less risky the strategy should be, but the more opportunities you will miss. For instance, the price of the most profitable item I traded did not behave at all like white noise over the entire period when I was trading it (but if you could zoom in far enough, it would):
Some difficulties
There were many issues that came up along the way. Here are some of the main ones:
-
After a while, Steam started using Akamai for rate limiting and caching. This was obviously bad for me: I needed a constant flow of up-to-date prices. At first both issues were circumvented by making requests to “origin.steam.com”. After that stopped working, I used proxies to avoid rate limiting, and I appended random junk to the requests to fix the caching issue.
-
At some point, Steam required mobile authentication to list an item on the market. This was fixed by reverse-engineering their mobile authentication app and simulating the appropriate requests.
-
In 2014, Steam implemented a two-sided market for certain items, and so I had to rewrite most of my code. One big disadvantage was caused by the way that Steam converted between currencies: two buy orders might appear to me to be for $0.20, but one of them might have higher priority if it was made for slightly more in rubles, for instance.