La realizzazione di un software per le surebet è molto complessa e di seguito analizzeremo un caso d’uso applicato di una piattaforma che raccolga tutti gli eventi e calcoli i guadagni a seconda degli esiti fissati dai bookmakers.
Prima di scendere nel dettaglio dell’implementazione usata per la creazione della piattaforma è necessario un chiarimento sul significato del termine “surebet”.

Che cosa sono le Surebet?

Le surebet sono delle scommesse sicure con una probabilità di vincita del 100%: consistono nello scegliere delle scommesse che, effettuate in un certo modo, non danno altro esito che la vittoria.

Esempio pratico di surebet

Ipotizziamo una partita di calcio in cui, sul gol, la quota under del bookmaker “A” è di 1,70 , mentre la quota over del bookmaker “B” è di 2,50 . Siamo nel caso in cui con due sole possibilità (sopra o sotto un certo risultato di gol) abbiamo concluso tutte le varie alternative per questa data scommessa.
Calcoliamo ora la quota inversa in questa maniera:

scommessa under: 1 / 1,70 = 0,58
scommessa over: 1 / 2,50 = 0,4

La somma di queste due quote inverse è 0,98. Ecco, abbiamo trovato la nostra scommessa sicura, la surebet.

Quindi per una scommessa di 100 Euro:

giocata sulla scommessa under: (1 / 1,70) / 0,98 = 0,60 x 100 = 60 euro giocata sulla scommessa over: (1 / 2,50) / 0,98 = 0,40 x 100 = 40 euro

Ecco infine un riepilogo delle somme incassate dopo la vittoria di una delle due opzioni:

vittoria sulla scommessa under: 60 x 1,70 = 102 euro
vittoria sulla scommessa over: 40 x 2,50 = 100 euro

Dettagli di implementazione della piattaforma

Abbiamo realizzato alcuni mesi fa un aggregatore di eventi surebet con i relativi filtri: essendo eventi di tutto il mondo dovevamo implementare dei filtri per scremare gli eventi a seconda del range di data, del range di profitto minimo e massimo e a seconda dei bookmakers da visualizzare associati agli eventi. Inoltre andavano realizzati dei calcolatori per i profitti in base al cambio della quota dei bookmakers disponibili per quell’evento. La sorgente dati era a disposizione su un DB MySql popolato da uno script-parser realizzato in Java.

E’ stata quindi progettata una query iniziale per comporre tutti gli eventi con i profitti massimi che l’utente avrebbe visto sulla home della piattaforma.
La prima questione emersa è che il DB conteneva una grande quantità di dati: aveva molte tabelle e una di queste aveva tre campi con più di 1 milione di record. Tuttavia con la query che dovevamo realizzare, i dati si sarebbero ridotti in media a 400000.

Nella progettazione della query è emerso che dovevano essere eseguiti vari join tra più tabelle, niente di strano visto che su MySql è una cosa normale. Realizzato il codice Sql, abbiamo installato il DB su un RDS di AWS (Amazon Web Services) e abbiamo stimato che una query per estrarre gli eventi impiegava un tempo medio di 3 secondi che rappresentava un tempo accettabile.
Per l’interfaccia della piattaforma si è utilizzato Bootstrap e per realizzare una visualizzazione ad albero degli eventi si è impiegata la libreria datatables.js opportunamente personalizzata per la creazione dell’albero all’interno della piattaforma web.
Qui sotto riporto il codice per realizzare una visualizzazione ad albero nelle datatables:


$('#DT tbody').on('click', 'td.details-control', function () {
   var tr = $(this).closest('tr');
   var row = table.row(tr);

   if (row.child.isShown()) {
      // Questa riga è aperta e la chiudiamo
      row.child.hide();
      tr.removeClass('shown');
   } else {
      // format è la funzione che fa l’append dei risultati
      row.child(format(row.data()), 'no-padding').show();
      tr.addClass('shown');
      $('div.slider', row.child()).slideDown();
   }
});

La libreria Javascript prendeva in input un JSON creato dai risultati della query dal framework Laravel con PHP. La visualizzazione ad albero era importante perché la query associata al singolo evento restituiva le combinazioni dei sottoeventi con profitto decrescente (la query ordinava il tutto). Ci sono vari sottoeventi perché le surebet possono essere scommesse solo una volta e quindi, i dati vengono aggiornati spesso nel DB MySql e ricaricati nella view.

Nella fase di creazione dei filtri per le combinazioni degli eventi, sono stati riscontrati dei limiti in MySql: dovevamo realizzare dei filtri che, in determinate condizioni, rimuovevano un dato ed eseguivano un’altra query per prendere un altro evento di profitto immediatamente inferiore. Ovviamente non potendo fare n query, è stata impostata una query generale all’inizio del codice con tutti i dati delle varie combinazioni delle surebet; tuttavia ciò ha fatto crescere la dimensione dell’array PHP (con i risultati della query) appesantendo tutte le elaborazioni, perché esso doveva essere trasformato in JSON per la datatable.

Il risultato era che dalla query iniziale che aveva un tempo di 3 secondi, il tempo medio di attesa per restituire il JSON (quindi query e tutte le elaborazioni dei filtri) era aumentato a più di 10 secondi nel caso medio.
Un tempo molto più alto, considerato che potevano capitare molti più record anche intorno a 600000 eventi.

Oltre a questo, PHP doveva elaborare ancora i dati risultanti perché la query non restituiva i dati definitivi e quindi c’era anche un tempo di computazione del server con CPU al 98% in media per 5 secondi. Questo poteva diventare un problema nel caso ci fossero molti utenti collegati alla macchina che eseguivano filtri sulle surebet.

Constatando l’elevata quantità di tempo, si è reso necessario cercare una possibile soluzione per diminuire il tempo di query e di elaborazione dati. D’altronde gli utenti registrati devono pagare una quota mensile per la consultazione dei dati e quindi non aspetterebbero mai più di 10 secondi per fare apparire i dati da usare per le loro scommesse.

Abbiamo iniziato a valutare l’uso di un DB non relazionale, avendo dei dati frammentati in varie tabelle MySql e valutando alcuni prodotti sul mercato. Abbiamo fatto alcuni test sfruttando MongoDB, un database noSql basato su documents.

Dall’utilizzo di questa nuova architettura di DB abbiamo constatato che la sintassi delle query è molto più semplice rispetto a Mysql. Abbiamo inoltre deciso di cambiare anche il motore di popolamento del DB (realizzato in Java) in modo da avere in MongoDB i dati completi per le datatables.

Provando alcune query all’interno di MongoDB, è risultato che i filtri delle surebet avevano un tempo medio di attesa di 2 secondi: un notevole miglioramento considerando il tempo precedente di MySql.

Rimaneva però la perplessità che MongoDB non si appoggiasse bene su PHP per due ragioni: da un lato PHP è un motore non noto per la sua leggerezza; dall’altro la combinazione tra PHP e MongoDB è inusuale nello sviluppo web.

Tali iniziali perplessità sono state presto superate utilizzando Laravel come framework PHP e avendo scoperto su GitHub un ottimo driver per MongoDB su Laravel.

Lo sviluppatore Jenssengers su GitHub a questo link https://github.com/jenssegers/laravel- mongodb ha integrato benissimo Eloquent (l’ORM di Laravel) e le query builder in modo da interfacciarle sulle query di MongoDB. Per usare Laravel con MongoDB basta eseguire Composer con il seguente comando:


composer require jenssegers/mongodb

Questo farà sì che venga clonata tutta la libreria per MongoDB all’interno della cartella vendor di Laravel. Per usarla dobbiamo però integrarla aggiungendo in /config/app.php la seguente linea di codice:


Jenssegers\Mongodb\MongodbServiceProvider::class

Questa riga farà sì che venga importata la definizione della classe MongodbServiceProvider per usarla all’interno del framework. L’operatore :: in PHP è l’operatore di risoluzione dello scope e serve per accedere alle proprietà, ai metodi e alle costanti di una classe PHP.

Fatto questo in Laravel andrà cambiata la connessione di default (se non ne avete altre) all’interno del file config/database.php in questo modo:


'default' => env('DB_CONNECTION', 'mongodb')

La funzione env di Laravel prende dal file .env (il file di configurazione del framework) il valore DB_CONNECTION e, se non è impostato, lo setta dandogli il valore “mongodb”. Il valore “mongodb” andrà creato di seguito impostando una nuova connessione per MongoDB con il seguente codice nella chiave connections dell’array nel file config/database.php:


'mongodb' => [
   'driver' => 'mongodb',
   'host' => env('DB_HOST', 'localhost'),
   'port' => env('DB_PORT', 27017),
   'database' => env('DB_DATABASE'),
   'username' => env('DB_USERNAME'),
   'password' => env('DB_PASSWORD'),
   'options' => [
      'database' => 'admin' //imposta il database di autenticazione di MongoDB
   ]
]

Ora, per eseguire una query su MongoDB con Laravel abbiamo due strade: usare il queryBuilder:


$user = DB::collection('users')->where('name', 'John')->first();

oppure usare Eloquent (ORM di Laravel), di seguito alcuni esempi su un model User (integrato con la libreria di Jenssengers per MongoDB):


$user = User::find('517c43667db388101e00000f');
$users = User::where('votes', '>', 100)->where('name', '=', 'John')->get();
$users = User::where('votes', '>', 100)->orWhere('name', 'John')->get()

Un esempio di query per filtrare i dati per un range di data nell’applicativo è stato:


$start = new \DateTime($this->sqlDate($request->mindate)); $end = new \DateTime($this->sqlDate($request->maxdate));
$res = DB::collection('Combinazioni')->whereBetween('date', [$start, $end]);

Adattando quindi la logica di gestione dati del software che era stato sviluppato alla nuova architettura di dati, abbiamo constatato che PHP 7.3 si comportava adeguatamente, facendo il parsing del formato BSON da MongoDB (un JSON esteso) e effettuando alcune piccole elaborazioni sullo stesso.

Fatto questo è stato installato MongoDB su AWS: installato su un EC2 gratuito di Amazon con Ubuntu Server, le prestazioni sono state da subito ottime, essendo la macchina simile all’hardware di un Raspberry (1 GB Ram e 3.3 GHz Intel 1 core).

Infine siamo riusciti a testare la piattaforma e a ottenere tempi di attesa e carichi sulla CPU veramente vantaggiosi rispetto a MySql (neanche il 35% in un secondo applicando un filtro sui bookmakers, rispetto ai 10 secondi al 98% della vecchia implementazione).

Considerazioni finali

Quando si creano prodotti personalizzati e di nicchia (vedi scommesse surebet in questo caso) non si conoscono mai a fondo i comportamenti reali di un determinato prodotto anche con molta esperienza sul campo: in questo caso molto è dipeso dal tipo di dato e dalle elaborazioni su di esso. A volte la miglior soluzione è abbinare tecnologie che non avremmo mai pensato di installare insieme.

Sarebbe impossibile poter testare tutti i prodotti sul mercato e capirne effettivamente i limiti in determinati casi, i pro e i contro e il come lavorano insieme determinati componenti ma sicuramente il testing sul campo è ancora la miglior via per scegliere i prodotti da utilizzare e il come abbinarli.