.asType()
def round(n :float64, digits :int) :Twine {
def str := "" + n
def point :int := str.lastIndexOf(".")
def scientific :int := str.lastIndexOf("E")
if (scientific > 0) {
str
} else {
if (str.size() < point + digits) {
str(0, str.size())
} else {
str(0, point + digits)
}
}
}
/******************************************************************************
Participants can buy and sell positions in the MarketMaker's markets. Their
assets are represented in terms of the asset matrix that the MarketMaker
supports. Participants can have assumptions that represent a current
sub-region of the market that is of interest for trading and evaluation.
******************************************************************************/
def makeParticipant(theMarket, myAssets) :any {
var myAssumptions := theMarket.issues().newAssumptions()
def participant {
/** buy conditional assets. Scale the MarketMaker's probabilities to
reflect that the probability of OUTCOME on ISSUE is VALUE, and
scale my payoffs by the same amounts. */
to trade(issue, outcome, value :float64) {
theMarket.trade(issue, outcome, value, myAssumptions, myAssets)
}
///// Assets /////////////////////////////////////////////////////////////////
to assets() :any { myAssets }
/** return my maximum assets under the assumption. */
to maxAssets(assumps) :float64 {
theMarket.expToAsset(myAssets.max(assumps))
}
/** return my minimum assets under the assumption. */
to minAssets(assumps) :float64 {
theMarket.expToAsset(myAssets.min(assumps))
}
/** return my average assets (weighted by the MarketMaker's
probabilities) under the assumption. */
to aveAssets(assumps) :float64 {
theMarket.aveAssets(myAssets, assumps)
}
/** Print a description of the value of my assets given the possible
outcomes of the ISSUE and my current assumptions. */
to showDependentAssetValues(w :TextWriter, issue) {
participant.showDependentAssetValues(w, issue, myAssumptions)
}
/** Print a description of the value of my assets given the possible
outcomes of the ISSUE and the ASSUMPTION. */
to showDependentAssetValues(w :TextWriter, issue, assumps) {
def index := assumps.validateIssue(issue)
if (assumps.issueIsDetermined(issue)) {
def theOutcome := assumps.liveOutcomesForIssue(index)[0]
def position := issue.positions()[theOutcome]
w.print(" According to assumption, the value of <" +
issue.desc() + "> is <" + position + ">\n")
} else {
w.print(`$issue$\n`+
`$\toutcome$\tmax assets$\tave assets$\tmin assets$\n`)
for i in assumps.liveOutcomesForIssue(index) {
def outcome := issue.positions()[i]
def out := assumps.withAssumption(issue, outcome, true)
def max := round(participant.maxAssets(out), 5)
def ave := round(participant.aveAssets(out), 5)
def min := round(participant.minAssets(out), 5)
w.print(`$\t$outcome$\t$max$\t$ave$\t$min$\n`)
}
}
}
///// Assumptions ////////////////////////////////////////////////////////////
/** Show my Assumptions. */
to assumptions() :any { myAssumptions }
/** Change my assumptions. */
to setAssumptions(newAssumption) { myAssumptions := newAssumption }
/** Reset my assumptions to be blank. */
to resetAssumptions() {
myAssumptions := theMarket.issues().newAssumptions()
}
/** Drop one of the assumptions I currently have. */
to dropAssumption(issue, outcome) {
myAssumptions := myAssumptions.withoutAssumption(issue, outcome)
}
/** Add an assumption to those I currently have. */
to addAssumption(issue, outcome, value) {
myAssumptions :=
myAssumptions.withAssumption(issue, outcome, value)
}
to printAssumptions(w :TextWriter) { myAssumptions.printOn(w) }
}
}
/******************************************************************************
The MarketMaker decides what issues to support, and what the value of an
investment will be. Participants are able to deposit any multiple of the
MarketMaker's initial subsidy. Assets are represented as 2^(value/subsidy).
******************************************************************************/
def makeMarketMaker(issueSet, subsidy :int) :any {
def probCube := makeBox.makeProbBox(issueSet.cardinalities())
def marketMaker {
to issues() :any { issueSet }
/** return the average assets under the assumption weighted by
my probabilities). */
to aveAssets(assetBox, assum) :float64 {
def p := probCube.sum(assum)
require(p >= 0, "internal error: some probabilities are negative")
if (p == 0) {
0
} else {
def sumOfLogs :=
assetBox.sumOfThatTimesLogThis(probCube, assum)
marketMaker.scale() * (sumOfLogs / p) / Math.log(2)
}
}
/** the scale of the marketMaker's subsidy. */
to scale() :int { subsidy }
/** Print a description of the probabilities of the possible outcomes
of the issue. */
to showChances(w :TextWriter, issue, assumps) {
def index := issueSet.indexOf(issue)
if (assumps.issueIsDetermined(issue)) {
def theOutcome := assumps.liveOutcomesForIssue(index)[0]
w.print(` According to assumption, the value \
of <${issue.desc()}> is <${issue.positions()[theOutcome]}>$\n`)
} else {
w.print(
` Probabilities for outcomes of <$issue>:$\n$\toutcome$\tprobability$\n`)
for i in assumps.liveOutcomesForIssue(index) {
def outcome := issue.positions()[i]
def prob :=
round(probCube.probGiven(issue, outcome, assumps), 5)
w.print(`$\t${outcome}$\t${prob}$\n`)
}
}
}
/** Transform the internal representation (in exponential form) of a
Player's holdings to a monetary value. */
to expToAsset(exp :float64) :float64 {
subsidy * Math.log(exp) / Math.log(2)
}
to tradeFactors(issue, outcome, targetProb :float64, assumps) :EList {
require(targetProb <= 1 && targetProb >= 0,
"new price must be within [0,1].")
require(! assumps.outcomeIsDetermined(issue, outcome),
"There's already an assumption about that outcome.")
def assumeTrue := assumps.withAssumption(issue, outcome, true)
def pTrue := probCube.sum(assumeTrue)
def assumeFalse := assumps.withAssumption(issue, outcome, false)
def pFalse := probCube.sum(assumeFalse)
require(0 != pTrue && 0 != pFalse,
"Variable's targetProb has already been determined.")
def newPTrue := targetProb * (pTrue + pFalse)
def trueFactor := newPTrue/pTrue
def newPFalse := pFalse + pTrue - newPTrue
def falseFactor := newPFalse/pFalse
[trueFactor, falseFactor, assumeTrue, assumeFalse]
}
to quote(issue, outcome, targetProb :float64, assumps) :EList {
def [trueFactor, falseFactor, ignoreA, ignoreB] :=
marketMaker.tradeFactors(issue, outcome, targetProb, assumps)
def truePrice := round(- marketMaker.expToAsset(trueFactor), 5)
def falsePrice := round(marketMaker.expToAsset(falseFactor), 5)
if (trueFactor < 1) {
[truePrice, falsePrice]
} else {
[falsePrice, truePrice]
}
}
to quote(issue, outcome, targetProb :float64, assumps, w :TextWriter) {
def [cost, gain] :=
marketMaker.quote(issue, outcome, targetProb, assumps)
w.print(`To set P($outcome) on $issue to $targetProb
it will cost $cost if wrong, and gain $gain if right.`)
}
/** A Player wants to set the probability of Outcome (given
Assumptions) on Issue to Value. Change the trader's assets and the
market maker's current probabilities to represent that.
Calculate the current relative probability of Outcome (given
Assumptions) according to the ratio of the total values in the true
and false sub-regions of probBox. Calculate factors to be applied
to the true and false sub-regions which will make the ratio of their
sums have the target value. Scale the values in each sub-region by
the appropriate factor. Then scale the Player's assets in the
sub-regions by the same amounts. */
to trade(issue, outcome, targetProb :float64, assumps, assets) {
def [trueFactor, falseFactor, assumeTrue, assumeFalse] :=
marketMaker.tradeFactors(issue, outcome, targetProb, assumps)
def sufficientAssets :=
if (trueFactor < 1) {
1/trueFactor < assets.min(assumeTrue)
} else {
1/falseFactor < assets.min(assumeFalse)
}
require(sufficientAssets, "Not enough assets to pay for trade.")
probCube.scale(trueFactor, assumeTrue)
probCube.scale(falseFactor, assumeFalse)
assets.scale(trueFactor, assumeTrue)
assets.scale(falseFactor, assumeFalse)
}
/** create a new Player in the market. */
to makeParticipant(deposit :int) :any {
require(deposit %% subsidy == 0,
`deposit must be a multiple of $subsidy.`)
def asset := 2 ** (deposit / subsidy)
def assets := makeBox.makeAssetBox(issueSet.cardinalities(), asset)
makeParticipant(marketMaker, assets)
}
}
}