How does betting work?

I’m trying to understand how betting works.
I post notes here. I suppose it will be useful for others too.

http://counterparty.io/docs/protocol_specification/ - See BROADCAST and BET
http://www.rubydoc.info/github/brighton36/counterparty_ruby/Counterparty/Bet

Oracle / Feed Operator
When an address makes a broadcast with value -1 it creates a feed (opens a bet).
The next broadcast from the same address settles the bet. The broadcast value determines the outcome. A settlement value must be non-negative. -2 cancels all open bets. -3 cancels all bet matches. Any other negative value is ignored.

'broadcast': ['source', 'fee_fraction', 'text', 'timestamp', 'value'],

The fee_fraction enables the oracle to take a cut. Eg at 1% the winner will get 99% of the escrowed amount and the oracle gets 1%.

Bet
Anyone can play on a feed. There are two kind of bets; CFDs and Bionomial (Equal/Not Equal). When you make a bet, the specified amount of XCP on your address will be escrowed. If someone else has made an opposite bet with equal or overlapping odds, your two bets will match. Your amounts will remain escrowed until the oracle determines the outcome (or if he doesn’t, you’ll both get your XCP amounts back two weeks after the deadline). If there is no matching bet, your bet will be pending until there is a match (and the oracle decides the outcome), the deadline or it expires.

 'bet': ['source', 'feed_address', 'bet_type','deadline', 'wager_quantity', 'counterwager_quantity', 'expiration', 'target_value', 'leverage'],

For two Bets to be matched, their leverage levels, deadlines and target values must be identical. Otherwise, they are matched the same way that orders are, except a Bet’s odds are the multiplicative inverse of an order’s price (odds = wager/counterwager)

Source is your own address.

Feed address is that of the operator/oracle (a oracle can only have one feed at a time)

Bet type can be 0 (Bullish CFD), 1 (Bearish CFD), 2 (Equal), 3 (Not Equal)

Deadline: Unix timestamp. If the operator has not settled the bet within 2016 blocks (~14 days) after the deadline, the bet will expire and escrow returned ???

Wager quantity ??? If you place a Bullish CFD or Equal bet, you will escrow this amount?

Counterwager quantity ??? If you place a Bearish CFD or Not Equal bet, you will escrow this amount?

The odds equal wager/counterwager. If you place Equal bet, wager = 1, counterwager = 2, the odds is 1 to 2. You have 1 XCP escrowed. Alice bets on Not equal, wager = 1, counterwager = 2. She gets 2 XCP escrowed. Since your two bets match (assuming leverage, deadline, target equal) the final outcome is settled by the oracle. If broadcast value = target value, you will receive the entire 3 XCP pot, else Alice will receive the 3 XCP (minus any oracle fee).

Expiration ??? Number of blocks until an unmatched bet expires?

Target value A non-negative number that sets the bet. For binomial bets, the Equal bet wins if the oracle broadcasts this value, otherwise the Not equal bet wins. For CFDs it is more complicated (can look at this later).

Leverage For binomial bets this must be 5040. It’s just a magic number. Does only have a meaning for CFDs. This I can look at later.

Bet transaction

A valid bet transaction:
http://blockscan.com/betInfo?q=11551745
https://www.blocktrail.com/BTC/tx/1b23d932cfb73a12dc0be593b95164c00524864ee21580c6bf799fd78ed9e045

The ‘feed_address’ is encoded by sending some btc dust to it (as with asset send). All other parameters are encoded in a single multisig. It should be possible also to encode as op_return.

CFDs are diabled

if bet_type in (0,1):   # BullCFD, BearCFD
        if block_index >= 312350:   # Protocol change.
            problems.append('CFDs temporarily disabled')

I did not plan to look into CFD quite yet anyway, but from this it is obviously disabled.

I’ve placed a few dozen bets on CP betting sites and also from the CLI, but I can’t recall - since all the sites and/or enhanced feeds aren’t available any more, how the bet deadline works.

deadline (integer, required): The time at which the bet should be decided/settled, in Unix time.

See: http://counterparty.io/docs/api/#create_bet

I find this confusing because there’s a deadline in enhanced feed info (More here: http://counterparty.io/docs/enhanced_feed_info/). Since this deadline is also about the approximate time the feed should be decided, it seems in bet you could simply pre-populate it with the deadline value from broadcast.
If you bet and provide a deadline that ends before the broadcast’s deadline, then what happens? If your bet has been matched, it’s not like you can do anything about it. And neither can the feed operator decide earlier for you just because you set a ludicrous deadline in your bet. So this seems both unnecessary and complicated.

Wager/Counterwager: it’s very simple (if you look into this for over year, like I did) - say my bet’s wager is 5 and counterwager 15. What does that mean? That means that I and sending 5 XCP to the feed address and I accept to be matched against anyone betting the other way if, and only if, they offer the odds of 3:1 or better.

Example: J. Bush will win 2016 Rep nomination (Y=1, N=0)

  • Alice thinks his chances are 50/50, so she wagers 10 (wager) and accepts as little as 10 (counterwager), but she doesn’t mind to get more (11 or 111)
  • Bob thinks Jeb has no chance, he is extremely sure Jeb’s chances are 20% at best. So he bets 100 and accepts counterwagers of 20 (or more).

Now they cast their bets and like on the DEx (XCP/BTC vs. BTC/XCP), matching is done.
Alice’s 10 is matched with Bob’s 10 (the minimum that Bob can spend to match Alice’s counterwager criterium).
Bob’s remainder (90) remains in a matching pool similar to the DEx.
Donald sees there’s 90 left in the pool and the odds are ridiculous - he’s more than sure Trump will win. He needs to wager only 90/5 (Bob offered 5:1). He quickly wagers 18 and that gets matched with Bob’s remaining wager of 90.

Yes:

expiration (integer, required): The number of blocks after which the bet expires if it’s still unmatched.
( http://counterparty.io/docs/api/#create_bet )

So this is like expiration on the DEx. But then you can’t help and again wonder what happens if you set the deadline (in the bet) that comes due before the expiration and what if deadline the enhanced feed is yet different… It’d be good to write a loop that creates couple of meaningless scenarios to see what happens in each.

Although it should be added here that the deadline in the enhanced feed JSON file is out there in the world, not on the blockchain, so it can be changed back and forth as someone pleases. I guess that deadline in JSON file is only for display in CW and the protocol doesn’t care about it for any other purpose. It can be checked in CW source code but I’m too tired to search for that now.

Yep, didn’t work well and not many people were using it anyway.

If we manage to get this going and get some feedback and experience with this, we could submit a CIP to fix/improve this feature. I’ve been wondering if a hash value of the JSON file could be stored in the DB because even though enhanced feed info can be changed and the value updated, at least you could know whether any given file has been changed or not (and if yes, how many times and when, and whether some particular file corresponds to this or that version).

To the fun part; reverse engineer counterparty tx data.

@loon3 helped me with this:
http://blockscan.com/betInfo?q=11539662
https://www.blocktrail.com/BTC/tx/b505248579f583758be198496c92ffffdec01939903d972d995fd26b6e19db42
https://jsfiddle.net/perb6vpd/

32434e54525052545900000028000254a08b70000000000098968000000000004c4b373ff0000000000000000013b0000000090000000000000000000000

The next challenge is to convert the details below to the string above :slightly_smiling:

Feed Address: 16AnpRp8EDATwB27Fdoy62F2aXXXXytmRT (not in string, separate output)
Bet_Type: Equal (2)
Deadline: 2014-12-28 23:00:00 (1419807600 => hex 54A08B70)
Wager Quantity: 0.1 XCP (10000000 => hex 989680)
Counterwager Quantity: 0.04999991 XCP (4999991 => hex 4C4B37)
Expiration: 9
Target Value: 1 (double 1 => hex 3ff0000000000000)
Leverage: 5040 (hex 13B0)

Transaction ID = int 40 = hex 28

2 Likes

Here’s a function which encodes a bet. It relies on other libraries.
See https://github.com/Jpja/CounterTools/blob/master/lib/chrome-wallet-mod/js/xcp-js/transactions.js

function create_bet_data(betType, deadline, wager, counterwager, expiration, target) {
    
    //OP_RETURN output
    //betType = 2 for Equal or 3 for Not Equal
    //deadline is unix timestamp
    //wager is quantity XCP
    //counterwager is is quantity XCP
    //expiration is number of blocks (integer)
    //target value is number (double)
    var cntrprtyPrefix = "434e545250525459";
    var transIdHex = "00000028"; //id for bet is int 40 = hex 28
    var leverageHex = "000013b0"; //always 5040 (hex 13B0) - was only used for CFDs, now deprecated
    
    var betTypeHex = '';
    if (betType == 2) {
        betTypeHex = "0002";
    } else if (betType == 3) {
        betTypeHex = "0003";
    } else {
        return 'error';
    }
    
    var deadlineInt = Math.round(deadline); //force integer
    var deadlineHex = padprefix(deadlineInt.toString(16),8);
    
    var wagerInt = parseFloat(wager).toFixed(8) * 100000000;
    wagerInt = Math.round(wagerInt);
    var wagerHex = padprefix(wagerInt.toString(16),16);
    
    var counterwagerInt = parseFloat(counterwager).toFixed(8) * 100000000;
    counterwagerInt = Math.round(counterwagerInt);
    var counterwagerHex = padprefix(counterwagerInt.toString(16),16);
    
    var expirationInt = Math.round(expiration);
    var expirationHex = padprefix(expiration.toString(16),8);
    
    var target_binary = toIEEE754Double(parseFloat(target));
    var target_hex_array = new Array();
    for (var i = 0; i < target_binary.length; ++i) {
        target_hex_array[i] = padprefix(target_binary[i].toString(16),2);
    }
    var targetHex = target_hex_array.join("");
    
   var bet_tx_data = cntrprtyPrefix + transIdHex + betTypeHex + deadlineHex + wagerHex + counterwagerHex + targetHex + leverageHex + expirationHex;
    
    if (bet_tx_data.length == 100) { //catches only error where some input is too long.
        return bet_tx_data;
    } 
    
    return 'error';
    
}
1 Like

Understand Wager & Counterwager and Odds

Alice operates a ‘dice roll’ feed. Whenever she rolls a dice, she broadcasts the outcome.

CASE I

Bob bets on this feed. He bets 10 XCP that the outcome will be six. He is risk neutral and knows the probability of rolling six is 1/6. Therefore he wagers 10 XCP and demands a counterwager of at least 50 XCP.

Charlie bets the roll dice will not equal six. He wagers 50 XCP and demands counterwager of 10 XCP.

Alice rolls six. Bob gets the total pot of 60 XCP and Charlie is left with nothing.

CASE II

Bob is risk seeking. He wagers 10 XCP and is okay with a counterwager of only 45 XCP.

Charlie bets, as before, against Bob with wager 50 XCP and counterwager 10 XCP.

The bets match. 10 XCP is escrowed from Bob and 45 XCP from Charlie.

Alice rolls six. Bob gets the total pot of 55 XCP and Charlie is left with nothing.

CASE III

Charlie bets first. Like last time he wagers 50 XCP with counterwager 10 XCP.

Then Bob places his bet with wager 10 XCP and counterwager 45 XCP.

The bets match. 10 XCP is escrowed from Bob and 50 XCP from Charlie.

Alice rolls six. Bob gets the total pot of 60 XCP and Charlie is left with nothing.

Decimal odds

When Bob wagers 10 XCP (and demands 45 XCP counterwager) to potentially win 55 XCP the odds can be displayed as 5.5. The formula is odds = 1 + counterwager / wager.

Charlie takes the other side of the bet, and to make the odds comparable the formula is inverse; odds = 1 + wager / counterwager. When he wagers 50 XCP with counterwager 10 XCP the odds are 6.0.

A binomial bet should be asked as a question. Bob’s side of the bet can be called Equal, Yes or True. Charlie’s side is Not Equal, No or False.

Bob will want Charlie to place odds that are as high as possible. Charlie will like Bob to place low odds.

Compare it to a stock market. Think of the odds as the price. Bob is the seller at 5.5. Then comes Charlie, the buyer, at 6.0. The bets match at 5.5.

Fractional odds

fractional odds = decimal odds - 1 and displayed as a fraction.
1.01 => 0.01 = 1/100
1.33 => 0.33 ~ 1/3
2.00 => 1.00 = 1/1
6.00 => 5.00 = 5/1

Moneyline (American) odds

For fractional odds lower than 1/1 ( the same as decimal odds lower than 2.0):
moneline odds = - 100 / fractional odds
For fractional odds higher than 1/1:
moneline odds = + 100 * fractional odds

1.01 => 1/100 => - 100
1.33 => 1/3 => - 300
2.00 => 1/1 => +/- 100
6.00 => 5/1 => + 500

1 Like