def trading_algorithm_2(): yield { b: formula.Constant(3.5), b_or_g: formula.Product(formula.Constant(2), formula.Price(if_g_then_r, 2)), } yield { b: formula.Constant(-1), if_g_then_r: formula.Max(formula.Price(b, 1), formula.Price(g, 2)), }
def trade_on_probability(sentence, day, p, slope=10): return formula.Min( formula.Constant(1), formula.Max( formula.Constant(-1), formula.Sum( formula.Constant(slope * p), formula.Product( formula.Constant(-slope), formula.Price(sentence, day) ) ) ) )
def make_s_curve(f, intercept, slope): return formula.Min( formula.Constant(1), formula.Max( formula.Constant(-1), formula.Sum( formula.Constant(slope * intercept), formula.Product( formula.Constant(-slope), f ) ) ) )
def test_find_credences_single(): credence_history = credence.History([]) # empty history trading_formulas = { # purchase sentence 1 in quantity 1 - 3 * credence 1: formula.Sum(formula.Constant(1), formula.Product(formula.Constant(-3), formula.Price(1, 1))), } new_credences = inductor.find_credences(trading_formulas, credence_history, 1e-5) # setting credence to 1/3 yields a trade quantity of zero, which satisfies assert_almost_equal(new_credences[1], 1 / 3)
def test_compute_budget_factor_already_overran_budget(): phi = sentence.Atom("ϕ") psi = sentence.Atom("Ψ") # there was one previous observation, which was "phi OR psi" past_observations = [sentence.Disjunction(phi, psi)] # on our one previous update, our credences were as follows past_credences = credence.History([{ phi: .6, psi: .7, }]) # on the previous update we purchased one token of psi past_trading_formulas = [{ psi: formula.Constant(10), }] # we observe psi in our most recent update latest_observation = sentence.Disjunction(phi, psi) # our trading formula says to always purchase 10 tokens of phi latest_trading_formulas = { phi: formula.Constant(10), } # our budget is $2, which means we can lose up to $2, or, in other words, # the value of our holdings is allowed to go as low as -$2 budget = 2 # compute the budget factor budget_factor = inductor.compute_budget_factor(budget, past_observations, latest_observation, past_trading_formulas, latest_trading_formulas, past_credences) # on our previous update we purchased one token of PSI for $7 without being # able to rule out the possibility that PHI could turn out to be false, in # which case we would have lost $7, which is more than our budget of $2, so # our budget factor should be the constant 0 which eliminates all further # trading assert_is_instance(budget_factor, formula.Constant) assert_equal(budget_factor.k, 0)
def test_find_credences_multiple(): credence_history = credence.History( []) # empty history; we are on the first update trading_formulas = { # purchase sentence 1 in quantity max(credence-of-sentence-1, credence-of-sentence-2) 1: formula.Max(formula.Price(1, 1), formula.Price(2, 1)), # purchase sentence 2 in quantity 1 - credence-of-sentence-1 - credence-of-sentence-2 2: formula.Sum( formula.Constant(1), formula.Sum( formula.Product(formula.Constant(-1), formula.Price(1, 1)), formula.Product(formula.Constant(-1), formula.Price(2, 1)))) } new_credences = inductor.find_credences(trading_formulas, credence_history, 1e-5) # setting credence[1] to 1 and credence[2] to 0 satisfies the conditions assert_almost_equal(new_credences[1], 1) assert_almost_equal(new_credences[2], 0)
def test_compute_budget_factor_simple(): phi = sentence.Atom("ϕ") # we are on the first update; our history is all empty past_credences = credence.History([]) # no past credences past_trading_formulas = [] # no past trading formulas past_observations = [] # no past observations # we observed phi in our most recent update latest_observation = phi # our trading formula says to always purchase 10 tokens of phi latest_trading_formulas = { phi: formula.Constant(10), } # our budget is $2, which means we can lose up to $2, or, in other words, # the value of our holdings is allowed to go as low as -$2 budget = 2 # compute the budget factor budget_factor = inductor.compute_budget_factor(budget, past_observations, latest_observation, past_trading_formulas, latest_trading_formulas, past_credences) assert_is_instance(budget_factor, formula.SafeReciprocal) # our world consists of only one base fact (phi), and we observed phi, so # the world where phi=true is only one world propositionally consistent with # our observations, and in this world we purchase 10 tokens of phi, which # could cost anywhere from $0 to $10 depending on the as-yet-unknown # credence for phi, and will have a value of exactly $10 in this world. In # no case will the value of our holdings drop below -$2, so we expect a # budget factor that evaluates to 1 for all credences in [0, 1]. assert_equal( budget_factor.evaluate(past_credences.with_next_update({phi: 0.})), 1.) assert_equal( budget_factor.evaluate(past_credences.with_next_update({phi: .2})), 1.) assert_equal( budget_factor.evaluate(past_credences.with_next_update({phi: .6})), 1.) assert_equal( budget_factor.evaluate(past_credences.with_next_update({phi: 1.})), 1.)
def test_combine_trading_algorithms_simple(): phi = sentence.Atom("ϕ") psi = sentence.Atom("Ψ") # in this test we are on the first update, so there is one trading # algorithm, one observation, and no historical credences trading_formula = {phi: formula.Constant(1)} trading_histories = [[trading_formula]] observation_history = [psi] credence_history = credence.History([]) # create the compound trader, which just has one internal trader compound_trader = inductor.combine_trading_algorithms( trading_histories, observation_history, credence_history, ) assert_equal(len(compound_trader), 1) assert_is_instance(compound_trader[phi], formula.Sum) assert_equal(len(compound_trader[phi].terms), 3)
def combine_trading_algorithms(trading_histories, observation_history, credence_history): """ Given: * A sequence of N generators over trading formulas (trading_algorithms) * A sequence of N sentences (observation_history) * A sequence of N-1 belief states (credence_history) Returns: * A single trading formula for day N that incorporates the wisdom from the N trading algorithms. The returned trading formula exploits any market that any of the N constituent trading algorithms exploits. This function implements TradingFirm as defined in 5.3.2 in the logical induction paper. """ n = len(credence_history) + 1 assert len(observation_history) == n assert len(trading_histories) == n # compute the terms that should be added together to produce the final # trading formula terms_by_sentence = collections.defaultdict(list) for k, trading_history in enumerate( trading_histories): # this is the loop over \Sum_{k<=n} # zero out the first k entries clipped_trading_history = [] for i, trading_formula in enumerate(trading_history): if i < k: clipped_trading_history.append({}) else: clipped_trading_history.append(trading_formula) # compute an upper bound on the net value for this trading history net_value_bound = 0 for trading_formula in clipped_trading_history: for sentence, trading_expr in trading_formula.items(): # compute an upper bound on the absolute value of trading_expr, # which is the quantity that we will purchase of this sentence quantity_bound = trading_expr.bound() # Let C = quantity_bound. We might spend up to $C purchasing # these tokens, and they might later be worth up to $C, so their # net value could be between -$C and $2C. We technically only # need a lower bound for the sum below but here we follow the # paper and compute a formal bound on the net value of this # trade by including the constant 2 below. See the last # paragraph of proof of 5.3.2 in the paper. net_value_bound += 2 * quantity_bound net_value_bound = math.ceil(net_value_bound) # TODO: we can compute a better bound by using the N-1 belief states # that we have already observed in credence_history for budget in range(1, net_value_bound + 1): budget_factor = compute_budget_factor(budget, observation_history[:-1], observation_history[-1], clipped_trading_history[:-1], clipped_trading_history[-1], credence_history) for sentence, trading_expr in clipped_trading_history[-1].items(): weight = 2**(-k - 1 - budget) terms_by_sentence[sentence].append( formula.Product(formula.Constant(weight), budget_factor, trading_expr)) for sentence, trading_expr in clipped_trading_history[-1].items(): weight = 2**(-k - 1 - net_value_bound) terms_by_sentence[sentence].append( formula.Product(formula.Constant(weight), trading_expr)) # create a trading formula for each sentence that is a sum of the terms # computed above return { sentence: formula.Sum(*terms) for sentence, terms in terms_by_sentence.items() }
def compute_budget_factor(budget, observation_history, next_observation, trading_history, next_trading_formulas, credence_history): """ Returns a trading formula representing a weight that can be multiplied with each formula in next_trading_formulas in order to guarantee that the trader's value-of-holdings will not fall below the given budget in any world. The worlds considered are those that are propositionally consistent with the sentences in observation_history and next_observation. The lists observation_history, trading_history, and credence_history all have the same length. next_observation is the most recently observed sentence. next_trading_formulas is a list of (sentence, formula) pairs that will be evaluated on whatever credences the logical inductor outputs when it updates its credences in light of next_observation. We do not get to see these credences when computing the budget factor because the budget factor is an input to the process by which the logical inductor updates its credences. """ assert budget > 0 history_length = len(observation_history) # compute the support for all trading formulas over all days support = union( set(trading_formulas.keys()) for trading_formulas in trading_history) # evaluate the "if" clause in (5.2.1) for i in range(history_length): observations_up_to_i = set(observation_history[:i + 1]) # go over the worlds consistent with the first N observations for world in worlds_consistent_with(observations_up_to_i, support): # calculate the accumulated value of the trader up to update N accumulated_value = 0 for cur_trading_formulas in trading_history[:i + 1]: accumulated_value += evaluate(cur_trading_formulas, credence_history, world) # if we have exceeded our budget on a previous update then we # have no more money to trade now if accumulated_value < -budget + 1e-7: return formula.Constant(0) # create a set of observations up to and including the most recent observations = set(observation_history) observations.add(next_observation) # add atoms for next_trading_formula to the support set support.update(set(next_trading_formulas.keys())) # if we got this far then we have not already exceeded our budget, so now # compute the budget factor budget_divisors = [] for world in worlds_consistent_with(observations, support): # compute our accumulated value in this world accumulated_value = 0 for cur_trading_formulas in trading_history: accumulated_value += evaluate(cur_trading_formulas, credence_history, world) # the money we have left to trade now is our original budget, plus # (resp. minus) any money we made (resp. lost) since the beginning of # time remaining_budget = budget + accumulated_value # this value should be positive given the check that we did above assert remaining_budget > 1e-8 remaining_budget_recip = 1. / remaining_budget # construct a trading formula representing the value of # next_trading_formulas in this world, as a function of the # yet-to-be-determined credences for the latest update value_of_holdings_terms = [] for sentence, trading_formula in next_trading_formulas.items(): # construct a trading formula that looks up the price of tokens for this sentence price = formula.Price(sentence, history_length + 1) # construct a trading formula that computes the value that this # sentence pays out in this world payout = formula.Constant(float(world[sentence])) # construct a trading formula that computes the net value of # purchasing one token of this sentence, which is the payout from the # token minus the price paid to purchase the token value = formula.Sum(payout, formula.Product(formula.Constant(-1), price)) # construct a trading formula that multiplies the number of tokens that we # purchase by their profitability value_of_holdings_terms.append( formula.Product(trading_formula, value)) # construct a trading formula representing the value of the trades # executed on this update in this world value_of_holdings = formula.Sum(*value_of_holdings_terms) # construct a trading formula representing the negation of the above neg_value_of_holdings = formula.Product(formula.Constant(-1), value_of_holdings) # construct a trading formula representing the value we would need to # divide our trades by in this world in order to make sure we do not # exceed our remaining budget divisor_in_this_world = formula.Product( formula.Constant(remaining_budget_recip), neg_value_of_holdings) # add the budget factor for this world to the list of terms budget_divisors.append(divisor_in_this_world) # the final budget divisor is the max of all the possible budget divisors. budget_divisor = formula.Max(*budget_divisors) # now take the safe reciprocal of the divisor, which turns it into a # multiplicative factor and also clips it to 1, so that we only scale # traders down, not up. This is what we want because if a trader is below # its budget then there is no need to scale it up until it uses all of its # remaining budget. budget_factor = formula.SafeReciprocal(budget_divisor) # and we are done! return budget_factor
def trading_algorithm(sentence, start=1, step=1): for quantity in enumerator.integers(start=start, step=step): yield {sentence: formula.Constant(quantity)}
def test_compute_budget_factor_two_base_facts(): phi = sentence.Atom("ϕ") psi = sentence.Atom("Ψ") # we are on the first update; our history is all empty past_credences = credence.History([]) # no past credences past_trading_formulas = [] # no past trading formulas past_observations = [] # no past observations # we observe psi in our most recent update latest_observation = sentence.Disjunction(phi, psi) # our trading formula says to always purchase 10 tokens of phi latest_trading_formulas = { phi: formula.Constant(10), } # our budget is $2, which means we can lose up to $2, or, in other words, # the value of our holdings is allowed to go as low as -$2 budget = 2 # compute the budget factor budget_factor = inductor.compute_budget_factor(budget, past_observations, latest_observation, past_trading_formulas, latest_trading_formulas, past_credences) assert_is_instance(budget_factor, formula.SafeReciprocal) print() print(budget_factor.tree()) # Our world consists of two base facts, phi and psi, and we observed "phi OR psi", so # there are three worlds consistent with this observation: # phi=True psi=True # phi=True psi=False # phi=False psi=True # # Our trading formula says to purchase 10 tokens of phi no matter what. This # could cost us between $0 and $10 depending on the credence for phi. The # value of these 10 tokens could turn out to be either $0 if phi=False or $10 # if phi=True: # phi=True psi=True -> value of 10 tokens of phi = $10, so net worth between $0 and $10 # phi=True psi=False -> value of 10 tokens of phi = $10, so net worth between $0 and $10 # phi=False psi=True -> value of 10 tokens of phi = $0, so net worth between -$10 and $0 # # In the third world, our net worth could drop below our budget for some # possible credences, so: # if the credence for phi were 1 then in the third world we would end up with a # net worth of -$10, so we should multiply our trading volume by 0.2 assert_almost_equal( budget_factor.evaluate(past_credences.with_next_update({phi: 1.})), .2) # if the credence for phi were 0.4 then in the third world we would end up with a # net worth of -$4, so we should multiply our trading volume by 0.5 assert_almost_equal( budget_factor.evaluate(past_credences.with_next_update({phi: .4})), .5) # if the credence for phi were 0.2 then in the third world we would end up with a # net worth of -$2, which is right on budget, so our scaling factor should be 1 assert_almost_equal( budget_factor.evaluate(past_credences.with_next_update({phi: .2})), 1.) # if the credence for phi were 0 then in the third world we would end up with a # net worth of $0, which is above budget, so our scaling factor should be 1 assert_almost_equal( budget_factor.evaluate(past_credences.with_next_update({phi: 0.})), 1.)