Archive

Ineffective Theory

Links for August 2022

PredictIt loses its special status from the CFTC; will most likely be forced to shut down. This seems like it will do real harm to the epistemological environment around politics and elections. To the extent that you’re at all worried about election security and misinformation (rather than “election security” and “misinformation”), the regulation of prediction markets does harm.

DJB is suspicious of the PQC selection process at NIST, and has filed a FOIA lawsuit.

Robin Hanson on STEM progress and moral “progress”. The corollary is that STEM progress is moral progress, while moral research is not moral progress.

ACOUP writes a progress studies piece, sort of. As he says, “the key takeaway here is just how contingent the industrial revolution was”. I’m less convinced than I should be, I think. An inevitable event (thousands of different paths cause X) can appear contingent (“look how unusual and contingent the path to X was!"). It’s difficult to distinguish the two without being able to consider counterfactuals.

White House announces debt forgiveness. Not only is this a year of jubilee, but all years will be years of jubilee. “If you can’t say anything nice, say nothing at all”; nevertheless it seems appropriate for taxes to be raised specifically on people with college degrees. We could determine the taxation rate by polling graduates about what they think the correct taxation rate is… but that would just be cruel.

Links for July 2022

Caplan on human smuggling. Note in particular that “smuggling is not a frowned-upon practice among migrant and refugee communities”.

Scaling laws for neural language models.

Nate Silver and the 2016 election: a story in two parts.

Majorana zero modes, redux. A previous claim that Microsoft had observed Majorana zero modes was retracted, but now they’re back! As noted in the abstract, this is “a prerequisite for experiments involving fusion and braiding of Majorana zero modes”—in other words, a possible path to usable quantum computers.

Balaji Srinivasan’s new book is available. I have skimmed it only briefly; Vitalik Buterin writes an excellent review.

Automating arbitrage on Manifold markets

Here are two questions on Manifold markets:

  1. Will there be a federal mask requirement on US domestic flights on November 8, 2022?
  2. Will there be a federal mask requirement in place on domestic flights as of Nov. 8, 2022?

This is the obvious hazard of allowing anyone to create a market—people aren’t always rigorously careful to ensure that they’re not creating a duplicate.

This is also an opportunity: the first market has (as I write this) a probability of 12%, and the second of 8%. Since both creators are trustworthy, the market probabilities should agree more closely than that.

Of course one can do this by hand, but then I’d have to check in periodically to make sure the two markets still agree, and if not, make appropriate trades. Much better to automate the check and the trade. (Also, I’m invested in the idea of automating arbitrage on Manifold—literally.)


Manifold markets has a nice API available. To make it a bit easier to work with, I wrote a python wrapper vatic. Be warned, this is an ill-tested work-in-progress! It’s good enough for this arbitrage task, and probably nothing else.

This is not a difficult task. Since the markets to be arbitraged are already identified, the script only needs to get their probabilities, check that they’re sufficiently far apart for the trade to be worthwhile (using 2% as a crude threshold), and then buy some YES of one and some NO of the other.

from vatic import manifold

slug1 = 'will-there-be-a-federal-mask-requir-d236f8cd3553'
slug2 = 'will-there-be-a-federal-mask-requir'

mani = manifold.Manifold(auth='Key obviously-im-hiding-it')
mkt1 = mani._get_slug(slug1)
mkt2 = mani._get_slug(slug2)

if abs(mkt1.probability - mkt2.probability) > .02:
    print('Probabilities separated by more than 2%---arbitraging!')
    if mkt1.probability > mkt2.probability:
        mkt1,mkt2 = mkt2,mkt1
    # Buy some YES from mkt1; buy some NO from mkt2
    print(mkt1.bet(1, 'YES'))
    print(mkt2.bet(1, 'NO'))

Running repeatedly yields:

(env)$ ./arbitrage.py 
Probabilities separated by more than 2%---arbitraging!
{'betId': 'xskQpaEh9RnEfsnp3NsF'}
{'betId': 'LiWz0zkraXxkRlEynlQu'}
(env)$ ./arbitrage.py 
Probabilities separated by more than 2%---arbitraging!
{'betId': 'aLtAe8ivMQYNH0sBTCZt'}
{'betId': 'O5LjGUwJR53tEsclvKkW'}
(env)$ ./arbitrage.py 
Probabilities separated by more than 2%---arbitraging!
{'betId': 'j1IVf1Dod7qm6mc39CtK'}
{'betId': 'O36ddnYtgnKpM69zXwI1'}
(env)$ ./arbitrage.py 
Probabilities separated by more than 2%---arbitraging!
{'betId': 'IXTLdjQJM1h0tDrINNsL'}
{'betId': 'lIVkXN0UEaPKGXCK8CiY'}

At the end of which I landed myself 4 NO shares and 40 YES shares. So it “worked”!

On the other hand, I now realize that I’ve accumulated a net YES position, which was certainly not my intent! If I believe that the true probability is 10%, then in expectation this strategy turns a profit (at least ignoring trading fees), but I nevertheless expect the strategy to result in a loss. Not terrible, but not ideal. I’m a bit more risk-averse, and would like a guaranteed profit.

So, I sell everything and try again.


To guarantee outcome-independent profit, we want to end up with roughly the same number of YES and NO shares at the end. Ideally, then, we would buy a single share at a time, not a single M$ worth at time. Something like:

mkt1.bet(mkt1.probability, 'YES')
mkt2.bet(1-mkt2.probability, 'NO')

Unfortunately the manifold API does not currently allow arbitrarily small bets. The smallest possible bet size is M$1, and in cases where the total amount of M$ being bet is small and the probabilities are close to 0%, there’s no way to construct a sensible bet.

A “good enough” approach is to buy shares probabilistically. If we buy YES on the low market about 10% of the time, and buy NO on the high market about 90% of the time, then we’ll end up with a roughly equal number of share of each when that’s possible. (When it’s not possible, we’ll be back where we started—profit only in expectation.)

import random
from vatic import manifold

slug1 = 'will-there-be-a-federal-mask-requir-d236f8cd3553'
slug2 = 'will-there-be-a-federal-mask-requir'

mani = manifold.Manifold(auth='Key obviously-im-hiding-it')
mkt1 = mani._get_slug(slug1)
mkt2 = mani._get_slug(slug2)

if abs(mkt1.probability - mkt2.probability) > .02:
    if random.random() < mkt1.probability:
        print('Buying YES')
    if random.random() < 1-mkt2.probability:
        print('Buying NO')

This also has the advantage of being stateless, so that the script can make a single M$1 bet at a time, and then re-evaluate. This prevents the client from needing to do any complicated calculations about what probabilities will be after the bet. I just take the smallest step possible, and then re-evaluate.

Being only very slightly reckless:

(env)$ while true; do ./arbitrage.py; done
Buying NO
Buying YES
Buying NO
Buying NO
Buying NO
Buying NO
Buying NO
Buying NO
Buying NO
Buying NO
Buying NO
Buying NO
Buying NO
Buying NO
Buying NO
Buying YES
Buying NO

The end result: I spent M$17 to get 21.059 YES and 16.719 NO shares. This is not quite a guaranteed profit. In the event of NO, I lose M$.291. It is, however, much closer. The maximum loss is M$0.291, and the potential gain is M$4.059. Alternatively, trusting the 10% probability, the expected value of this position is M$17.15, so I gained $0.15 in expectation.

There’s more that should be tuned. For instance, a good bit was lost to creator fees in this process. I still managed to make a net (expected) profit, but presumably a bit more could be eked out by paying attention to fees.

There’s more to say, but this has already taken much more time than I expected, so I will simply close with one of those universally beloved exercises for the reader. Under what circumstances does the strategy above far underperform a human trader? (Can another trader, who knows I’m running this script, profit from that knowledge?)