Ok, it’s presidential election time, and some bored people on the trading floor are making markets. How to make money by betting against them ?
The principle is rather simple. Take the best quotes for each outcome, and process :
- if the sum of 1/quote_i is less than one, an arbitrage opportunity exists ;
- the amount to bet on each outcome is 100/quote.
Ok, here’s an example (assuming each trader doesn’t offer arbitrage opportunities within his own book).
Trader A : Obama wins @ 1.62 vs Trader B : McCain wins @ 2.72
1/1.62 + 1/2.72 = 0.98, which is less than 1, so you can arbitrage them.
Betting 100/1.62 at trader A, and 100/2.72 at trader B yields 1.50, whoever wins, or 2.44% if Obama wins, and 4.10% if McCain wins.
So, F# comes to the rescue to do the calculations, and here we go.
let findArb quotes = let res = quotes |> Array.fold (fun acc quote -> acc + (1.0 / quote)) 0.0 if res < 1.0 then printfn "----------------------------------" printfn "Bet" printfn "----------------------------------" let amountsToBet = quotes |> Array.map (fun quote -> 100.0 / quote) let totalBet = Array.reduce (+) amountsToBet printfn "Total bet : %0.2f" totalBet printfn "----------------------------------" let minGainPct = ref System.Double.MaxValue quotes |> Array.iteri (fun i quote -> let amt = amountsToBet.[i] let received = amt * quote - (totalBet - amt) let gainPct = 100.0 * (received - amt) / amt if gainPct < !minGainPct then minGainPct := gainPct printfn "quote:%0.2f \t amount to bet:%0.4f \t received* :%0.2f \t gain*:%0.2f p.c." quote amountsToBet.[i] received gainPct ) printfn "----------------------------------" printfn "minimum gain:%0.2f p.c." !minGainPct printfn "----------------------------------" printfn "* : if the outcome associated with this quote happens" else printfn "No arb opportunity : do not bet on this one" do //Example : Obama @ 1.62 vs McCain @ 2.72 findArb [|1.62; 2.72|] //Example : competition with 5 outcomes findArb [|4.77; 4.32; 5.67; 5.50; 5.10|]
Ok… there’s a catch ! Rounding numbers may result in losses. However, this seemingly only appears when there are many outcomes… So save it for the next grand-slam tennis final !
Alternatively, you may use the following function (although it might miss an opportunity since tweaking numbers works whith the following function will just round a real number to its nearest inferior integer).
let findArb2 quotes = let res = quotes |> Array.fold (fun acc quote -> acc + (1.0 / quote)) 0.0 if res < 1.0 then let amountsToBet = Array.map (fun quote -> floor <| 100.0 / quote) quotes let totalBet = Array.reduce (+) amountsToBet let received = Array.map2 (fun quote amt -> amt * quote - (totalBet - amt)) quotes amountsToBet let gainsPct = Array.map2 (fun amt received -> 100.0 * (received - amt) / amt) amountsToBet received let minGainPct = Array.min gainsPct if minGainPct > 0.0 then printfn "----------------------------------" printfn "Bet" printfn "----------------------------------" printfn "Total bet : %0.2f" totalBet printfn "----------------------------------" for i in 0 .. quotes.Length - 1 do printfn "quote:%0.2f \t amount to bet:%0.4f \t received* :%0.2f \t gain*:%0.2f p.c." quotes.[i] amountsToBet.[i] received.[i] gainsPct.[i] printfn "----------------------------------" printfn "minimum gain:%0.2f p.c." minGainPct printfn "----------------------------------" printfn "* : if the outcome associated with this quote happens" else printfn "Arb opportunity : but the automatic rounding screws it" else printfn "No arb opportunity : do not bet on this one" do //Example : Obama @ 1.62 vs McCain @ 2.72 findArb2 [|1.62; 2.72|] printfn "========================================" //Example : competition with 5 outcomes (under consideration) findArb2 [|4.77; 4.32; 5.67; 5.50; 5.10|]
[...] Julien Ortin’s Arbitraging bookmakers with F# [...]