def evaluate_contract_in_series( contract_valuation_id, contract_valuation_repo, market_simulation_repo, simulated_price_repo, call_requirement_repo, call_dependencies_repo, call_link_repo, call_result_repo, perturbation_dependencies_repo, simulated_price_dependencies_repo, is_double_sided_deltas): """ Computes value of contract by following the series execution order of its call dependency graph in directly evaluating each call in turn until the whole graph has been evaluated. """ # Get the contract valuation entity (it knows which call dependency graph and which market simualation to use). contract_valuation = contract_valuation_repo[contract_valuation_id] assert isinstance(contract_valuation, ContractValuation), contract_valuation # Get the contract specification ID. contract_specification_id = contract_valuation.contract_specification_id # Get the market simulation. market_simulation = market_simulation_repo[ contract_valuation.market_simulation_id] # Follow the execution order... for call_id in regenerate_execution_order(contract_specification_id, call_link_repo): # Get the call requirement entity. call_requirement = call_requirement_repo[call_id] # Get the perturbation requirements for this call. perturbation_dependencies = perturbation_dependencies_repo[call_id] # Get the simulated price requirements for this call. simulation_requirements = simulated_price_dependencies_repo[ call_id].requirements # Compute the call result. result_value, perturbed_values, involved_market_names = compute_call_result( contract_valuation=contract_valuation, call_requirement=call_requirement, market_simulation=market_simulation, perturbation_dependencies=perturbation_dependencies, call_dependencies_repo=call_dependencies_repo, call_result_repo=call_result_repo, simulated_price_repo=simulated_price_repo, simulation_requirements=simulation_requirements, is_double_sided_deltas=is_double_sided_deltas, ) # Register the result. register_call_result( call_id=call_id, result_value=result_value, perturbed_values=perturbed_values, contract_valuation_id=contract_valuation_id, contract_specification_id=contract_specification_id, involved_market_names=involved_market_names)
def evaluate_contract_in_series(contract_valuation_id, contract_valuation_repo, market_simulation_repo, simulated_price_repo, call_requirement_repo, call_dependencies_repo, call_link_repo, call_result_repo, perturbation_dependencies_repo, simulated_price_dependencies_repo, is_double_sided_deltas): """ Computes value of contract by following the series execution order of its call dependency graph in directly evaluating each call in turn until the whole graph has been evaluated. """ # Get the contract valuation entity (it knows which call dependency graph and which market simualation to use). contract_valuation = contract_valuation_repo[contract_valuation_id] assert isinstance(contract_valuation, ContractValuation), contract_valuation # Get the contract specification ID. contract_specification_id = contract_valuation.contract_specification_id # Get the market simulation. market_simulation = market_simulation_repo[contract_valuation.market_simulation_id] # Follow the execution order... for call_id in regenerate_execution_order(contract_specification_id, call_link_repo): # Get the call requirement entity. call_requirement = call_requirement_repo[call_id] # Get the perturbation requirements for this call. perturbation_dependencies = perturbation_dependencies_repo[call_id] # Get the simulated price requirements for this call. simulation_requirements = simulated_price_dependencies_repo[call_id].requirements # Compute the call result. result_value, perturbed_values, involved_market_names = compute_call_result( contract_valuation=contract_valuation, call_requirement=call_requirement, market_simulation=market_simulation, perturbation_dependencies=perturbation_dependencies, call_dependencies_repo=call_dependencies_repo, call_result_repo=call_result_repo, simulated_price_repo=simulated_price_repo, simulation_requirements=simulation_requirements, is_double_sided_deltas=is_double_sided_deltas, ) # Register the result. register_call_result( call_id=call_id, result_value=result_value, perturbed_values=perturbed_values, contract_valuation_id=contract_valuation_id, contract_specification_id=contract_specification_id, involved_market_names=involved_market_names )
def test_delete_result(self): # In this test, there are two "calls": call1 and call2. # It is supposed that call1 happens first, and call2 uses the result of call1. # Therefore call2 depends upon call1, call1 is a dependency of call2, and call2 is a dependent of call1. call1_id = 'call1' call2_id = 'call2' contract_valuation_id = 'val1' contract_specification_id = 'spec1' # call1_id = uuid4().hex # call2_id = uuid4().hex # contract_valuation_id = uuid4().hex # contract_specification_id = uuid4().hex register_call_dependencies(call2_id, [call1_id]) # Check the policy has the dependencies for call2. self.assertEqual(self.policy.dependencies[call2_id], [call1_id]) # Register dependents of call1, as call2. register_call_dependents(call1_id, [call2_id]) # Check the policy has the dependencies for call2. self.assertEqual(self.policy.dependents[call1_id], [call2_id]) # Register call result for call1. # - this should trigger deletion of call2 result call1_result = register_call_result(call1_id, 1.0, {}, contract_valuation_id, contract_specification_id, []) # Check the policy has the result for call1. self.assertTrue(call1_result.id in self.policy.result) # Check the result for call1 exists. self.assertTrue(call1_result.id in self.call_result_repo) # Register call result for call2. call2_result = register_call_result(call2_id, 1.0, {}, contract_valuation_id, contract_specification_id, []) # Check the policy has the result for call2. self.assertTrue(call2_result.id in self.policy.result) # Check the result for call2 exists. self.assertTrue(call2_result.id in self.call_result_repo) # Check the policy does not have the result for call1. self.assertFalse(call1_result.id in self.policy.result) # Check the result for call1 doesn't exist (because it's dependents have results). self.assertFalse(call1_result.id in self.call_result_repo)
def test_register_call_result(self): contract_specification_id = create_uuid4() contract_valuation_id = create_uuid4() call_id = create_uuid4() call_result_id = make_call_result_id(contract_valuation_id, call_id) self.assertRaises(KeyError, self.app.call_result_repo.__getitem__, call_result_id) register_call_result(call_id=call_id, result_value=123, perturbed_values={}, contract_valuation_id=contract_valuation_id, contract_specification_id=contract_specification_id, involved_market_names=[]) call_result = self.app.call_result_repo[call_result_id] assert isinstance(call_result, CallResult) self.assertEqual(call_result.result_value, 123)
def test_register_call_result(self): dependency_graph_id = create_uuid4() contract_valuation_id = create_uuid4() call_id = create_uuid4() call_result_id = make_call_result_id(contract_valuation_id, call_id) self.assertRaises(KeyError, self.app.call_result_repo.__getitem__, call_result_id) register_call_result(call_id=call_id, result_value=123, perturbed_values={}, contract_valuation_id=contract_valuation_id, dependency_graph_id=dependency_graph_id) call_result = self.app.call_result_repo[call_result_id] assert isinstance(call_result, CallResult) self.assertEqual(call_result.result_value, 123)
def evaluate_call_and_queue_next_calls( contract_valuation_id, contract_specification_id, call_id, contract_valuation_repo, call_requirement_repo, market_simulation_repo, call_dependencies_repo, call_result_repo, simulated_price_repo, perturbation_dependencies_repo, simulated_price_requirements_repo): # Get the contract valuation. contract_valuation = contract_valuation_repo[contract_valuation_id] assert isinstance(contract_valuation, ContractValuation) # Get the market simulation. market_simulation = market_simulation_repo[ contract_valuation.market_simulation_id] # Get the call requirement. call_requirement = call_requirement_repo[call_id] # Get the perturbation dependencies for this call. perturbation_dependencies = perturbation_dependencies_repo[call_id] assert isinstance(perturbation_dependencies, PerturbationDependencies) # Get the simulated price requirements for this call. simulation_requirements = simulated_price_requirements_repo[ call_id].requirements # Compute the call result. result_value, perturbed_values, involved_market_names = compute_call_result( contract_valuation=contract_valuation, call_requirement=call_requirement, market_simulation=market_simulation, perturbation_dependencies=perturbation_dependencies, call_dependencies_repo=call_dependencies_repo, call_result_repo=call_result_repo, simulated_price_repo=simulated_price_repo, simulation_requirements=simulation_requirements, is_double_sided_deltas=contract_valuation.is_double_sided_deltas, ) # Register the call result. register_call_result(call_id=call_id, result_value=result_value, perturbed_values=perturbed_values, contract_valuation_id=contract_valuation_id, contract_specification_id=contract_specification_id, involved_market_names=involved_market_names)
def evaluate_call_and_queue_next_calls(contract_valuation_id, contract_specification_id, call_id, contract_valuation_repo, call_requirement_repo, market_simulation_repo, call_dependencies_repo, call_result_repo, simulated_price_repo, perturbation_dependencies_repo, simulated_price_requirements_repo): # Get the contract valuation. contract_valuation = contract_valuation_repo[contract_valuation_id] assert isinstance(contract_valuation, ContractValuation) # Get the market simulation. market_simulation = market_simulation_repo[contract_valuation.market_simulation_id] # Get the call requirement. call_requirement = call_requirement_repo[call_id] # Get the perturbation dependencies for this call. perturbation_dependencies = perturbation_dependencies_repo[call_id] assert isinstance(perturbation_dependencies, PerturbationDependencies) # Get the simulated price requirements for this call. simulation_requirements = simulated_price_requirements_repo[call_id].requirements # Compute the call result. result_value, perturbed_values, involved_market_names = compute_call_result( contract_valuation=contract_valuation, call_requirement=call_requirement, market_simulation=market_simulation, perturbation_dependencies=perturbation_dependencies, call_dependencies_repo=call_dependencies_repo, call_result_repo=call_result_repo, simulated_price_repo=simulated_price_repo, simulation_requirements=simulation_requirements, is_double_sided_deltas=contract_valuation.is_double_sided_deltas, ) # Register the call result. register_call_result( call_id=call_id, result_value=result_value, perturbed_values=perturbed_values, contract_valuation_id=contract_valuation_id, contract_specification_id=contract_specification_id, involved_market_names=involved_market_names )
def test_register_call_result(self): contract_specification_id = create_uuid4() contract_valuation_id = create_uuid4() call_id = create_uuid4() call_result_id = make_call_result_id(contract_valuation_id, call_id) self.assertRaises(KeyError, self.app.call_result_repo.__getitem__, call_result_id) register_call_result( call_id=call_id, result_value=123, perturbed_values={}, contract_valuation_id=contract_valuation_id, contract_specification_id=contract_specification_id, involved_market_names=[]) call_result = self.app.call_result_repo[call_result_id] assert isinstance(call_result, CallResult) self.assertEqual(call_result.result_value, 123)
def generate_contract_valuation(self, dependency_graph_id, market_simulation): assert isinstance(dependency_graph_id, six.string_types), dependency_graph_id assert isinstance(market_simulation, MarketSimulation) v = self.register_contract_valuation(dependency_graph_id) for call_id in regenerate_execution_order(dependency_graph_id, self.call_link_repo): call = self.call_requirement_repo[call_id] assert isinstance(call, CallRequirement) # Evaluate the call requirement. dependency_values = get_dependency_values(call_id, self.call_dependencies_repo, self.call_result_repo) # - parse the expr stubbed_module = dsl_parse(call.dsl_source) assert isinstance(stubbed_module, Module), "Parsed stubbed expr string is not a module: %s" % stubbed_module # - build a namespace from the dependency values dsl_locals = DslNamespace(dependency_values) # - compile the parsed expr dsl_expr = stubbed_module.body[0].reduce(dsl_locals=dsl_locals, dsl_globals=DslNamespace()) assert isinstance(dsl_expr, DslExpression), dsl_expr # - evaluate the compiled expr first_market_name = market_simulation.market_names[0] if market_simulation.market_names else None evaluation_kwds = { 'simulated_price_repo': self.simulated_price_repo, 'simulation_id': market_simulation.id, 'interest_rate': market_simulation.interest_rate, 'present_time': call.effective_present_time or market_simulation.observation_date, 'first_market_name': first_market_name, } result_value = dsl_expr.evaluate(**evaluation_kwds) # - store the result register_call_result(call_id=call_id, result_value=result_value)
def evaluate_call_and_queue_next_calls(contract_valuation_id, dependency_graph_id, call_id, call_evaluation_queue, contract_valuation_repo, call_requirement_repo, market_simulation_repo, call_dependencies_repo, call_result_repo, simulated_price_repo, call_dependents_repo, perturbation_dependencies_repo, simulated_price_requirements_repo, call_result_lock, compute_pool=None, result_counters=None, usage_counters=None): # Get the contract valuation. contract_valuation = contract_valuation_repo[contract_valuation_id] assert isinstance(contract_valuation, ContractValuation) # Get the market simulation. market_simulation = market_simulation_repo[ contract_valuation.market_simulation_id] call_requirement = call_requirement_repo[call_id] perturbation_dependencies = perturbation_dependencies_repo[call_id] assert isinstance(perturbation_dependencies, PerturbationDependencies) # Get the simulated price requirements for this call. simulation_requirements = simulated_price_requirements_repo[ call_id].requirements result_value, perturbed_values = compute_call_result( contract_valuation=contract_valuation, call_requirement=call_requirement, market_simulation=market_simulation, perturbation_dependencies=perturbation_dependencies, call_dependencies_repo=call_dependencies_repo, call_result_repo=call_result_repo, simulated_price_repo=simulated_price_repo, perturbation_dependencies_repo=perturbation_dependencies_repo, simulation_requirements=simulation_requirements, compute_pool=compute_pool, ) # Lock the results. # - avoids race conditions when checking, after a result # has been written, if all results are now available, whilst # others are writing results. # - could perhaps do this with optimistic concurrency control, so # that result events can be collected by dependents # and then evaluated when all are received - to be robust against # concurrent operations causing concurrency exceptions, an accumulating # operation would require to be retried only as many times as there are # remaining dependents. if call_result_lock is not None: call_result_lock.acquire() try: # Register this result. # Todo: Retries on concurrency errors. register_call_result( call_id=call_id, result_value=result_value, perturbed_values=perturbed_values, contract_valuation_id=contract_valuation_id, dependency_graph_id=dependency_graph_id, ) # Find next calls. ready_generator = find_dependents_ready_to_be_evaluated( contract_valuation_id=contract_valuation_id, call_id=call_id, call_dependencies_repo=call_dependencies_repo, call_dependents_repo=call_dependents_repo, call_result_repo=call_result_repo, result_counters=result_counters, ) # Check requirements, and discard result when dependency has been fully used. if usage_counters is not None: call_dependencies = call_dependencies_repo[call_id] assert isinstance(call_dependencies, CallDependencies) for dependency_id in call_dependencies.dependencies: dependency_result_id = make_call_result_id( contract_valuation_id, dependency_id) try: usage_counters[dependency_result_id].pop( ) # Pop one off the array (atomic decrement). except (KeyError, IndexError): call_result = call_result_repo[dependency_result_id] # Todo: Maybe do discard operations after lock has been released? call_result.discard() # Need to remove from the cache if we are to save memory. try: del (call_result_repo._cache[dependency_result_id]) except: pass if call_result_lock is not None: # Make a list from the generator, if we are locking results. next_call_ids = list(ready_generator) else: # Otherwise put things directly on the queue. next_call_ids = [] for next_call_id in ready_generator: call_evaluation_queue.put( (dependency_graph_id, contract_valuation_id, next_call_id)) gevent.sleep(0) finally: # Unlock the results. if call_result_lock is not None: call_result_lock.release() # Queue the next calls (if there are any - see above). for next_call_id in next_call_ids: call_evaluation_queue.put( (dependency_graph_id, contract_valuation_id, next_call_id))
def register_call_result(self, call_id, result_value): return register_call_result(call_id=call_id, result_value=result_value)