Sharedpmarket / MarketObject.ipynbOpen in CoCalc
MarketObject

Protocol for a distributed synthetic market

This is a protocol for matching state contingent contracts that give a payoff linear between an upper and lower bound and at a particular expiry.

For example the contract might pay $1 for each 10mm of rainfall above 30mm. If the amount of rainfall is above 30mm the payoff is $1 and if the outcome is below 30mm the outcome is zero.

These contracts can be combined to construct standard forward and option contracts or event markets (payoffs depending on a discrete set of outcomes).

The protocol uses a chain of signatures to organize buy and sell orders for these contracts. Orders are accepted if they have a valid signature and the trader's positions satisfiy a collateral check across all possible worst outcomes. To maintain an appropriate record of the order book, trades can only be added, and the validity of any trade can be checked by anyone using public keys provided by each participant.

Markets are settled by the market owner adding a new upper and lower bound for a market equal to a single number. For example, the lower bound for each market is the highest lower bound for itself of any sub-market or any previous bound on the same market. Market bounds telescope, making a market the underlying asset of its sub-markets.

Data structures

A market

A market is defined by:

  • 'marketRootId': (integer) market id
  • 'marketBranchId': (integer) sub-markets >1 (sub-markets bounded by super-markets)
  • 'marketMin' - (float) minimum possible outcome
  • 'marketMax' - (float) maximum possible outcome
  • 'traderId' - (integer) market owner trader ID
  • 'signatureMsg' - (string) message for signature
  • 'signature' - (binary) signed message

E.g.

Market with (root = 3, branch = 1); market bounded between (0, 1); owned by trader 1 and signed with signature 'sig1'.


testMarket = struct('marketRootId', 3, 'marketBranchId', 1,...
                                'marketMin', 0, 'marketMax', 1,...
                                'traderId', 1, 'signatureMsg','sigmsg1',...
                                'signature', 'sig1')
  • Sub markets have marketMin/marketMax bounded by superMarkets.
  • Any amount of valid markets with the same root/branch can be added and the market will take the highest minimum and the lowest maximum.

An order

An order in a market is defined by:

  • 'tradeRootId': (integer) trade r noot id
  • 'tradeBranchId': (integer) subtrades (dependent trades such as offsets and partials)
  • 'isMatched': (boolean) is trade matched (adding an unmatched trade requries adding an offsetting trade and adding an equal sized matched trade from cache)
  • 'price': (float) price of trade
  • 'quantity': (float) quanitity of trade (positive quantity for bids, negative for offers)
  • 'marketRootId' : (integer) market id
  • 'marketBranchId': (integer) sub-markets > 1 (sub-markets bounded by super-markets)
  • 'traderId': (integer) trade owner trader ID
  • 'tradeType': (integer) Primary = 1, pArtial = 2, Offset = 3, Reduction = 4
  • 'signatureMsg' - (string) message for signature
  • 'signature' - (binary) signed message

E.g.

Unmatched primary trade (price = 0.5, quantity = 10) on market (root = 1, branch = 1)


testTrade = struct('traderId', 1, 'tradeRootId', 1, 'tradeBranchId', 1,...
                'isMatched', 0, 'price', 0.5,...
                'quantity', 10, 'marketRootId', 3,...
                'marketBranchId', 1,'signatureMsg',...
                'tradeType, 1,'sigMsg', 'signature', 'sig');

Trades can be added to the order book but not removed or changed except for the 'tradeType' field which determines if the trade should be considered in collateral calculations.

Geometry of an order

Since orders can only be added to the order book and not subtracted, creating a matched order requires an offset of the unmatched order and a new matched order of equal size.

Consider an order '(p=0.5, q=0.5)'. For the trade to successfully be matched it requires at minimum:

  • Primary (p=0.5, q=10, isMatched = 0)
  • Offset (p=0.5, q=-10, isMatched = 0)
  • Match (p=0.5, q=10, isMatchced = 1)

Optionally, the trade an also include a reduction trade for the market to use to kill an existing order to make way for a new order. These reduction trades don't need offsets.

  • Reduction (p=0.5, q=10*-0.1=-1, isMatched=1)

Geometrically, this set of trades can be represented in (p,q) space:

It is useful to allow partial fills (lower quantity and weakly better price). Each partial trade only needs a matched version and not an offset.

Any non-primary trade that is picked up for a match is set as primary by the system so it can be picked up in future collateral calculations (this is the only changable field in the order book).

Geometry of signature chain

Each trade is chained to a previous trade according to some rule. The order book will require a valid signature (valid signature for message and valid previous node) and a collateral check. Primary and partial orders are chained to a previous valid order, while offset and reductions are chained to the primary trade.

The figure is a bid for 10 contracts at 0.5 with associated offset, match, and reduce trades.

If the order is matched by a sell contract at 0.5 the the offset and match contracts are taken from cache and added to the order book.

Example sequence of orders

Trader 1:
Primary: traderId=1, tradeRoot = 1, tradeBranch = 1, p=0.5, q=10, isMatched=0
Offset: traderId=1, tradeRoot = 1, tradeBranch = 2, p=0.5, q=-10, isMatched=0
Match: traderId=1, tradeRoot = 1, tradeBranch = 3, p=0.5, q=10, isMatched=1

Trader 2:
Primary: traderId=2, tradeRoot = 2, tradeBranch = 1, p=0.5, q=-10, isMatched=0
Offset: traderId=2, tradeRoot = 2, tradeBranch = 2, p=0.5, q=10, isMatched=0
Match: traderId=2, tradeRoot = 2, tradeBranch = 3, p=0.5, q=10, isMatched=1

Methods

[methods]

1+2

- Match trade (10,0.5) with all signature chain and cache
- Some idea for which last signature to use
- How to prevent malicious use of cache orders? 

df = pd.DataFrame({'prices': [1, 2, 3]},
                  [dt(2014, 1, 1), dt(2014, 1, 2), dt(2014, 1, 3)])