# Copyright 2003 Chris Hibbert under the terms of the MIT license # found at http://www.opensource.org/licenses/mit-license.html # Chris Hibbert can be reached at copyright@mydruthers.com ///// helper methods and values /////////////////////////////////////////////// // upcoming: // def EList := def EList := .asType() /** bind last value in a 3-argument lambda */ def curry3(func3, val) :any { def func2(box, index) :any { func3(box, index, val) } } /** bind last value in a 4-argument lambda */ def curry4(func4, arg2) :any { def func3(box, index, arg1) :any { func4(box, index, arg1, arg2) } } def Math := ///// functions to map over subregions of boxes /////////////////////////////// // Notice that these functions don't return values. /** store VALUE at INDEX in BOX */ def put(box, index, value) { box[index] := value } /** increment the value at INDEX in BOX by DELTA */ def incr(box, index, delta) { box[index] += delta } /** scale the value at INDEX in BOX by FACTOR */ def sc(box, index, factor) { box[index] *= factor } /** sum the value at INDEX in BOX into RES */ def add(box, index, res) { res[0] += box[index] } /** save the value at INDEX in BOX in RES if it is smaller */ def least(box, index, res) { if (res[0] == null || res[0] > box[index]) { res[0] := box[index] } } /** save the value at INDEX in BOX in RES if it is larger */ def most(box, index, res) { if (res[0] == null || res[0] < box[index]) { res[0] := box[index] } } /** add (value at pos in oneBox * log(value at pos in otherBox)) to res */ def sumTimesLog(oneBox, index, otherBox, res) :float64 { res[0] := res[0] + (otherBox[index] * Math.log(oneBox[index])) } /****************************************************************************** Abstract parent for a multidimensional box that can recursively map functions over specified sub-region.

required methods for subclasses: dimensions(), values() ******************************************************************************/ def AbstractBox(self) :any { /** private: used by map(fn, assumptions) */ def mapRecur(fn, dim, subBox, assumptions) { if (dim == 0) { for i in assumptions.liveOutcomesForIssue(0) { fn(subBox, i) } } else { for i in assumptions.liveOutcomesForIssue(dim) { mapRecur(fn, dim - 1, subBox[i], assumptions) } } } /** private: used by mapWith(fn, other, assumptions) */ def mapRecurWith(fn, dim, subBox, assumptions, other) { if (dim == 0) { for i in assumptions.liveOutcomesForIssue(0) { fn(subBox, i, other) } } else { for i in assumptions.liveOutcomesForIssue(dim) { mapRecurWith(fn, dim - 1, subBox[i], assumptions, other[i]) } } } def box { /** map a function over the subregion of the box */ to map(fn, assumptions) { mapRecur(fn, self.dimensions().size() - 1, self.values(), assumptions) } /** map a function over the subregion of this box and the other box */ to mapWith(fn, other, assumptions) { mapRecurWith(fn, self.dimensions().size() - 1, self.values(), assumptions, other.values()) } to set(value, assumptions) { self.map(curry3(put, value), assumptions) } to scale(value, assumptions) { self.map(curry3(sc, value), assumptions) } to sum(assumptions) :float64 { def res := [0].diverge() self.map(curry3(add, res), assumptions) res[0] } to sumOfThatTimesLogThis(other, assumptions) :float64 { def res := [0].diverge() self.mapWith(curry4(sumTimesLog, res), other, assumptions) res[0] } to min(assumptions) :float64 { def res := [null].diverge() self.map(curry3(least, res), assumptions) res[0] } to max(assumptions) :float64 { def res := [null].diverge() self.map(curry3(most, res), assumptions) res[0] } } } /** build a box given an array with the sizes of each dimension, and an inital value to be stored in all positions */ def buildBox(boxSizes, dim, initialValue) :EList { def quant := boxSizes.get(dim) if (dim == 0) { ([initialValue] * quant).diverge() } else { def nextBox := ([null] * quant).diverge() for i in 0..!quant { nextBox[i] := buildBox(boxSizes, dim - 1, initialValue) } nextBox } } /****************************************************************************** Build arrays representing positions in a MarketMaker's list of issues.

The coordinates of each cell represent particular positions on each of the issues, and the contents of the cell give a value corresponding to that combination of outcomes.

Probability boxes hold the MarketMaker's current probability of each outcome. The probability corresponding to a composite outcome can be found by summing the cells that represent the ways the outcome could come about. Asset Boxes hold an investor's assets, represented as 2**value in each cell.

When an issue is determined, the array can be reduced by eliminating the "rows" corresponding to the outcomes that didn't come true. ******************************************************************************/ def boxKit { /** build a box holding an individual's assets */ to makeAssetBox(sizes, initial) :any { def myValues := buildBox(sizes, sizes.size() - 1, initial) def assetBox extends AbstractBox(assetBox) { to dimensions() :any { sizes } to values() :any { myValues } } } /** build a box holding a market maker's probability asssignments. Assume even distribution of probability. */ to makeProbBox(sizes) :any { var cases := 1 for i in sizes { cases := cases * i } // count total cases def myValues := buildBox(sizes, sizes.size() - 1, 1/cases) def probabilityBox extends AbstractBox(probabilityBox) { to dimensions() :any { sizes } to values() :any { myValues } /** return the probability in the box of the issue having the value according to the assumptions. We want to know, out of the total probability according to the assumptions, how much of it correspondends to states in which the issue has the value */ to probGiven(issue, outcome, assumption) :float64 { def wholeProb := probabilityBox.sum(assumption) def [indexOfIssue, indexOfOutcome] := assumption.validateIssueOutcome(issue, outcome) if (assumption.outcomeIsDetermined(issue, outcome)) { def live := assumption.liveOutcomesForIssue(indexOfIssue) if (live.contains(indexOfOutcome)) { 1.0 } else { 0.0 } } else { def newAssumption := assumption.withAssumption(issue, outcome, true) if (wholeProb > 0) { def subProb := probabilityBox.sum(newAssumption) subProb / wholeProb } else { 0.0 } } } } } }