lib/mutually_exclusive_collection.rb in rodders-2.1.0 vs lib/mutually_exclusive_collection.rb in rodders-3.0.0

- old
+ new

@@ -1,32 +1,102 @@ -# A collection of mutually exclusive events. +# A collection of mutually exclusive odds for different outcomes. +# Includes methods for sports betting arbitrage. Due to the discrete nature +# of real-world monetary amounts, there may be small rounding errors. class MutuallyExclusiveCollection - # create a new collection with the given events - # @param [Array<FixedOdds>] events the events - def initialize(events) - @events = events.sort + # create a new collection with the given odds + # @param [Array<FixedOdds>] mutually_exclusive_outcome_odds the odds for all the mutually exclusive outcomes + def initialize(mutually_exclusive_outcome_odds) + @mutually_exclusive_outcome_odds = mutually_exclusive_outcome_odds.sort end - # the least likely of the events to occur - # @return [FixedOdds] the least likely event + # the least likely of the odds to occur + # @return [FixedOdds] the least likely odd def least_likely - @events.first + @mutually_exclusive_outcome_odds.first end - # the most likely of the events to occur - # @return [FixedOdds] the most likely event + # the most likely of the odds to occur + # @return [FixedOdds] the most likely odd def most_likely - @events.last + @mutually_exclusive_outcome_odds.last end - # the events in ascending order of probability - # @return [Array<FixedOdds>] events in ascending probability + # the odds in ascending order of probability + # @return [Array<FixedOdds>] odds in ascending probability def in_ascending_probability - @events + @mutually_exclusive_outcome_odds end - # the events in descending order of probability - # @return [Array<FixedOdds>] events in descending probability + # the odds in descending order of probability + # @return [Array<FixedOdds>] odds in descending probability def in_descending_probability - @events.reverse + @mutually_exclusive_outcome_odds.reverse end + + # tells if arbitrage is possible for a collection of odds + # @return [Boolean] true if profit can be made regardless + # of the outcome, false otherwise + def arbitrageable? + sum_inverse_outcome < 1 + end + + # the bookmaker's return rate + # @return [Number] the bookmaker's return rate as a percentage + def bookmakers_return_rate + fs = fractions + 1 - fs.reduce(:*) / fs.reduce(:+) + end + + # hash of the odds and what percentage of the total stake should go on each + # @return [Hash<FixedOdds, Number>] hash of odds to percentages + def percentages + hash = {} + @mutually_exclusive_outcome_odds.each {|odds| hash[odds] = 1 / odds.fractional_odds / sum_inverse_outcome } + hash + end + + # hash of the odds and what stakes to put on each given a total stake + # @param [Money] total_stake the money to distribute on the outcomes + # @return [Hash<FixedOdds, Money>] hash of odds to stakes + def stakes_for_total_stake total_stake + hash = {} + @mutually_exclusive_outcome_odds.each {|odds| hash[odds] = total_stake / odds.fractional_odds / sum_inverse_outcome } + hash + end + + # hash of the odds and the stakes needed to make the specified profit + # @param (see #stakes_for_total_stake) + # @return (see #stakes_for_total_stake) + def stakes_for_profit desired_profit + stakes_for_total_stake(stake_to_profit(desired_profit)) + end + + # the stake needed to win the desired profit + # @param [Money] desired_profit the profit to gain from arbitrage + # @return [Money] the stake necessary to realise the desired profit + def stake_to_profit desired_profit + desired_profit / profit_percentage + end + + # the profit won given the total stake to distribute + # @param (see #stakes_for_total_stake) + # @return [Money] the profit that can be made from a total stake + def profit_on_stake total_stake + total_stake * profit_percentage + end + + # profit percentage available on this arb + # @return [Number] the percentage profit available on the arb + def profit_percentage + sum = sum_inverse_outcome + (1 - sum) / sum + end + + private + def sum_inverse_outcome + fractions.reduce(0) {|sum, n| sum + Rational(1, n) } + end + + def fractions + @mutually_exclusive_outcome_odds.collect {|o| o.fractional_odds } + end end \ No newline at end of file