Martingaling Roobet's Crash

My friend put me onto this crypto casino game "Crash". The idea is simple, cash out before the asset crashes. What's more, is that Roobet assures us that the games are 'fair'. 'Fair' in this case means the house always wins, but the results of each individual round is predetermined. Anecdotally, they do label users internally as "high-rollers" and I've routinely seen "high-rollers" lose more frequently than non "high-rollers". Regardless, my buddy James wanted to believe that a machine could rig the game. This wasn't the case, some simple statistics and visualizations below prove to him why.

In [0]:
import pandas as pd
import numpy as np


import matplotlib.pyplot as plt

import seaborn as sns
In [0]:
 

Roobet game results can be produced here:

https://codesandbox.io/s/crash-gl2qb?from-embed

This is provided from there site where they claim to be "provably fair". ...

In [0]:
 
In [0]:
results = pd.read_csv('/content/Roobet_100_base.csv')

print(results.head(5))
   iter  value
0     0   5.46
1     1   1.25
2     2   4.44
3     3  21.26
4     4   2.40
In [0]:
print(results.columns)

reduce_by = 1.3

results['reduced'] = np.where(results.value > 20, 20, results.value) 
results['furtherReduced'] = np.where(results.value > reduce_by, reduce_by, results.value)

results['binary'] = np.where(results.value > reduce_by, 1, 0)

print(results['reduced'].head(5))


ax = sns.distplot(results.reduced,
                  kde=True,
                  bins=200,
                  color='skyblue',
                  hist_kws={"linewidth": 15,'alpha':1})
ax.set(xlabel='Round Results', ylabel='Frequency')
Index(['iter', 'value', 'reduced', 'furtherReduced', 'binary'], dtype='object')
0     5.46
1     1.25
2     4.44
3    20.00
4     2.40
Name: reduced, dtype: float64
Out[0]:
[Text(0, 0.5, 'Frequency'), Text(0.5, 0, 'Round Results')]

So the distribution of game results is well modelled by an exponential distribution. This suggests that the most likely outcome of any given crash game is 1.00. It is more than 50% likely that the game won't exceed 1.5.

These are just rough numbers from the tiny graph up there.

In [0]:
ax = sns.distplot(results.binary,
                  kde=True,
                  bins=40,
                  color='skyblue',
                  hist_kws={"linewidth": 15,'alpha':1})
ax.set(xlabel='Exponential Distribution', ylabel='Frequency')
Out[0]:
[Text(0, 0.5, 'Frequency'), Text(0.5, 0, 'Exponential Distribution')]

When evaluating if a statistical strategy may be used to outgame the game we can reduce the data to "winners" and "losers" in binary fashion.

Having done this it is clear that the game is very marginal. If anything above 1.5 is a winner, you'll find that that is nearly (but not quite) twice as likely as a crash below 1.5(loser.)

reducing that further to 1.3, or 30% gains on each bet, you find it is again ALMOST 3 times more likely than a bust. In any case, over time you'll lose money with these numbers.

But even these numbers should allow us to implement some kind of betting strategy that would keep us afloat over time.

Martingaling comes to mind. Perhaps we could run some rudimentary backtesting.

In [0]:
og_bankroll = 1000
og_size = 0.01


bankroll = og_bankroll #Arbitrary starting balance.
size = og_size #Bet 1% of our bankroll each time.

for value in results.furtherReduced :
  if bankroll < og_size * og_bankroll : # We went broke.
    print('Broke!')
    break 

  if value != reduce_by : 
    value = -1 # This was a loser. 
  
  bankroll = bankroll + (bankroll * size * value - bankroll * size)

  if value == -1 :
    size = size * 2

  if value != -1 :
    size = size * 0.2

print(bankroll);
1000.4838766203072
In [0]:
 

That looks incredibly promising. Gross. Roobet will probably take our money EVENTUALLY.

Note: Even if this worked, there is not way to implement a betting strategy of this nature with their autobetter. (They don't let you auto set the bets as a percentage of total bankroll like we've done here.)

In [0]:
og_bankroll = 1000
og_size = 0.01
og_exit_target = 1.3

exit_target = og_exit_target

bankroll = og_bankroll #Arbitrary starting balance.
size = og_size #Bet 1% of our bankroll each time.

for value in results.reduced :
  if bankroll < og_size * og_bankroll : # We went broke.
    print('Broke!')
    break 

  if value < exit_target : 
    value = -1 # This was a loser. 
  else :
    value = exit_target # This was a winner.
  
  bankroll = bankroll + (bankroll * size * value - bankroll * size) # Update our balance.

  if value == -1 : # If we lost that round...
    size = size * 4
    exit_target = exit_target * 0.75

  if value != -1 : # If we won that round...
    size = og_size
    exit_target = og_exit_target

print(bankroll); #How'd we do?!
82.27198801591186

The house always wins. Try any combination of exit strategies above. Nothing seems to be profitable over time.

The best I've been able to make by hand is something that only gradually loses over time. Most blow up within about 5000 rounds.

Good Coder, Bad Trader 2020