def test_get_price_process(self): price_process = get_price_process(DEFAULT_PRICE_PROCESS_NAME) self.assertIsInstance(price_process, BlackScholesPriceProcess) # Test the error paths. # - can't import the Python module self.assertRaises(DslError, get_price_process, 'x' + DEFAULT_PRICE_PROCESS_NAME) # - can't find the price process class self.assertRaises(DslError, get_price_process, DEFAULT_PRICE_PROCESS_NAME + 'x')
def simulate_future_prices(market_simulation, market_calibration): assert isinstance(market_simulation, MarketSimulation), market_simulation assert isinstance(market_calibration, MarketCalibration), market_calibration price_process = get_price_process(market_calibration.price_process_name) assert isinstance(price_process, PriceProcess), price_process return price_process.simulate_future_prices( market_names=market_simulation.market_names, fixing_dates=market_simulation.fixing_dates, observation_date=market_simulation.observation_date, path_count=market_simulation.path_count, calibration_params=market_calibration.calibration_params)
def test_get_price_process(self): price_process = get_price_process(DEFAULT_PRICE_PROCESS_NAME) self.assertIsInstance(price_process, BlackScholesPriceProcess) # Test the error paths. # - can't import the Python module self.assertRaises(DslError, get_price_process, 'x' + DEFAULT_PRICE_PROCESS_NAME) # - can't find the price process class self.assertRaises(DslError, get_price_process, DEFAULT_PRICE_PROCESS_NAME + 'x')
def simulate_future_prices(market_simulation, market_calibration): assert isinstance(market_simulation, MarketSimulation), market_simulation if not market_simulation.requirements: return [] assert isinstance(market_calibration, MarketCalibration), market_calibration price_process = get_price_process(market_calibration.price_process_name) assert isinstance(price_process, PriceProcess), price_process return price_process.simulate_future_prices( observation_date=market_simulation.observation_date, requirements=market_simulation.requirements, path_count=market_simulation.path_count, calibration_params=market_calibration.calibration_params)
def simulate_future_prices(market_simulation, market_calibration): assert isinstance(market_simulation, MarketSimulation), market_simulation if not market_simulation.requirements: return [] assert isinstance(market_calibration, MarketCalibration), market_calibration price_process = get_price_process(market_calibration.price_process_name) assert isinstance(price_process, PriceProcess), price_process return price_process.simulate_future_prices( observation_date=market_simulation.observation_date, requirements=market_simulation.requirements, path_count=market_simulation.path_count, calibration_params=market_calibration.calibration_params)
def dsl_eval(dsl_source, filename='<unknown>', is_parallel=None, dsl_classes=None, compile_kwds=None, evaluation_kwds=None, price_process_name=None, is_multiprocessing=False, pool_size=0, is_verbose=False, is_show_source=False, **extra_evaluation_kwds): """ Returns the result of evaluating a compiled module (an expression, or a user defined function). An expression (with optional function defs) will evaluate to a simple value. A function def will evaluate to a DSL expression, will may then be evaluated (more than one function def without an expression is an error). """ if price_process_name is None: price_process_name = DEFAULT_PRICE_PROCESS_NAME if evaluation_kwds is None: evaluation_kwds = DslNamespace() assert isinstance(evaluation_kwds, dict) evaluation_kwds.update(extra_evaluation_kwds) if is_show_source: print_("Reading DSL source:") print_() print_('"""') print_(dsl_source.strip()) print_('"""') print_() if is_verbose: print_("Compiling DSL source, please wait...") print_() compile_start_time = datetime.datetime.now() # Compile the source into a primitive DSL expression, with optional dependency graph. dsl_expr = dsl_compile(dsl_source, filename=filename, is_parallel=is_parallel, dsl_classes=dsl_classes, compile_kwds=compile_kwds) # Measure the compile_dsl_module time. compile_time_delta = datetime.datetime.now() - compile_start_time # Check the result of the compilation. # Todo: This feels unnecessary? if is_parallel: assert isinstance(dsl_expr, DependencyGraph), type(dsl_expr) else: assert isinstance(dsl_expr, DslExpression), type(dsl_expr) if is_verbose: if isinstance(dsl_expr, DependencyGraph): print_("Compiled DSL source into %d partial expressions (root ID: %s)." % ( len(dsl_expr.stubbed_calls), dsl_expr.root_stub_id)) print_() print_("Duration of compilation: %s" % compile_time_delta) print_() if isinstance(dsl_expr, DependencyGraph): if is_show_source: print_("Expression stack:") for stubbed_exprData in dsl_expr.stubbed_calls: print_(" " + str(stubbed_exprData[0]) + ": " + str(stubbed_exprData[1])) print_() # If the expression has any stochastic elements, the evaluation kwds must have an 'observation_date' (datetime). if dsl_expr.has_instances(dsl_type=StochasticObject): observation_date = evaluation_kwds['observation_date'] assert isinstance(observation_date, datetime.date) if is_verbose: print_("Observation time: %s" % observation_date) print_() # Avoid any confusion with the internal 'present_time' variable. if 'present_time' in evaluation_kwds: msg = ("Don't set present_time here, set observation_date instead. " "Hint: Adjust effective present time with Fixing or Wait elements.") raise DslError(msg) # Initialise present_time as observation_date. evaluation_kwds['present_time'] = observation_date # If the expression has any Market elements, a market simulation is required if dsl_expr.has_instances(dsl_type=Market): # If a market simulation is required, evaluation kwds must have 'path_count' (integer). if 'path_count' not in evaluation_kwds: evaluation_kwds['path_count'] = DEFAULT_PATH_COUNT path_count = evaluation_kwds['path_count'] assert isinstance(path_count, int) # If a market simulation is required, evaluation_kwds must have 'market_calibration' (integer). market_calibration = evaluation_kwds['market_calibration'] assert isinstance(market_calibration, dict) # If a market simulation is required, generate the simulated prices using the price process. if not 'all_market_prices' in evaluation_kwds: if is_verbose: print_("Price process: %s" % price_process_name) print_() price_process = get_price_process(price_process_name) if is_verbose: print_("Path count: %d" % path_count) print_() if is_verbose: print_("Finding all Market names and Fixing dates...") print_() # Extract market names from the expression. # Todo: Avoid doing this on the dependency graph, when all the Market elements must be in the original. market_names = find_market_names(dsl_expr) # Extract fixing dates from the expression. # Todo: Perhaps collect the fixing dates? fixing_dates = list_fixing_dates(dsl_expr) if is_verbose: print_("Simulating future prices for Market%s '%s' from observation time %s through fixing dates: %s." % ( '' if len(market_names) == 1 else 's', ", ".join(market_names), "'%04d-%02d-%02d'" % (observation_date.year, observation_date.month, observation_date.day), # Todo: Only print first and last few, if there are loads. ", ".join(["'%04d-%02d-%02d'" % (d.year, d.month, d.day) for d in fixing_dates[:8]]) + \ (", [...]" if len(fixing_dates) > 9 else '') + \ ((", '%04d-%02d-%02d'" % (fixing_dates[-1].year, fixing_dates[-1].month, fixing_dates[-1].day)) if len(fixing_dates) > 8 else '') )) print_() # Simulate the future prices. all_market_prices = price_process.simulate_future_prices(market_names, fixing_dates, observation_date, path_count, market_calibration) # Add future price simulation to evaluation_kwds. evaluation_kwds['all_market_prices'] = all_market_prices # Initialise the evaluation timer variable (needed by showProgress thread). evalStartTime = None if is_parallel: if is_verbose: len_stubbed_exprs = len(dsl_expr.stubbed_calls) lenLeafIds = len(dsl_expr.leaf_ids) msg = "Evaluating %d expressions (%d %s) with " % (len_stubbed_exprs, lenLeafIds, 'leaf' if lenLeafIds == 1 else 'leaves') if is_multiprocessing and pool_size: msg += "a multiprocessing pool of %s workers" % pool_size else: msg += "a single thread" msg += ", please wait..." print_(msg) print_() # Define showProgress() thread. def showProgress(stop): progress = 0 movingRates = [] while progress < 100 and not stop.is_set(): time.sleep(0.3) if evalStartTime is None: continue # Avoid race condition. if not hasattr(dsl_expr, 'runner') or not hasattr(dsl_expr.runner, 'resultIds'): continue if stop.is_set(): break try: lenResults = len(dsl_expr.runner.resultIds) except IOError: break resultsTime = datetime.datetime.now() movingRates.append((lenResults, resultsTime)) if len(movingRates) >= 15: movingRates.pop(0) if len(movingRates) > 1: firstLenResults, firstTimeResults = movingRates[0] lastLenResults, lastTimeResults = movingRates[-1] lenDelta = lastLenResults - firstLenResults resultsTimeDelta = lastTimeResults - firstTimeResults timeDeltaSeconds = resultsTimeDelta.seconds + resultsTimeDelta.microseconds * 0.000001 rateStr = "%.2f expr/s" % (lenDelta / timeDeltaSeconds) else: rateStr = '' progress = 100.0 * lenResults / len_stubbed_exprs sys.stdout.write("\rProgress: %01.2f%% (%s/%s) %s " % (progress, lenResults, len_stubbed_exprs, rateStr)) sys.stdout.flush() sys.stdout.write("\r") sys.stdout.flush() stop = threading.Event() thread = threading.Thread(target=showProgress, args=(stop,)) # Start showProgress() thread. thread.start() # Start timing the evaluation. evalStartTime = datetime.datetime.now() try: # Evaluate the primitive DSL expression. if is_parallel: if is_multiprocessing: dependency_graph_runner_class = MultiProcessingDependencyGraphRunner else: dependency_graph_runner_class = SingleThreadedDependencyGraphRunner value = dsl_expr.evaluate(dependency_graph_runner_class=dependency_graph_runner_class, pool_size=pool_size, **evaluation_kwds) else: value = dsl_expr.evaluate(**evaluation_kwds) except: if is_parallel: if is_verbose: if thread.isAlive(): # print "Thread is alive..." stop.set() # print "Waiting to join with thread..." thread.join(timeout=1) # print "Joined with thread..." raise # Stop timing the evaluation. evalTimeDelta = datetime.datetime.now() - evalStartTime if isinstance(dsl_expr, DependencyGraph): if is_verbose: # Join with showProgress thread. thread.join(timeout=3) if is_verbose: timeDeltaSeconds = evalTimeDelta.seconds + evalTimeDelta.microseconds * 0.000001 if is_parallel: len_stubbed_exprs = len(dsl_expr.stubbed_calls) rateStr = "(%.2f expr/s)" % (len_stubbed_exprs / timeDeltaSeconds) else: rateStr = '' print_("Duration of evaluation: %s %s" % (evalTimeDelta, rateStr)) print_() # Prepare the result. import scipy if isinstance(value, scipy.ndarray): mean = value.mean() stderr = value.std() / math.sqrt(path_count) return { 'mean': mean, 'stderr': stderr } else: return value
def dsl_eval(dsl_source, filename='<unknown>', is_parallel=None, dsl_classes=None, compile_kwds=None, evaluation_kwds=None, price_process_name=None, is_multiprocessing=False, pool_size=0, is_verbose=False, is_show_source=False, **extra_evaluation_kwds): """ Returns the result of evaluating a compiled module (an expression, or a user defined function). An expression (with optional function defs) will evaluate to a simple value. A function def will evaluate to a DSL expression, will may then be evaluated (more than one function def without an expression is an error). """ if price_process_name is None: price_process_name = DEFAULT_PRICE_PROCESS_NAME if evaluation_kwds is None: evaluation_kwds = DslNamespace() assert isinstance(evaluation_kwds, dict) evaluation_kwds.update(extra_evaluation_kwds) if is_show_source: print_("Reading DSL source:") print_() print_('"""') print_(dsl_source.strip()) print_('"""') print_() if is_verbose: print_("Compiling DSL source, please wait...") print_() compile_start_time = datetime.datetime.now() # Compile the source into a primitive DSL expression, with optional dependency graph. dsl_expr = dsl_compile(dsl_source, filename=filename, is_parallel=is_parallel, dsl_classes=dsl_classes, compile_kwds=compile_kwds) # Measure the compile_dsl_module time. compile_time_delta = datetime.datetime.now() - compile_start_time # Check the result of the compilation. # Todo: This feels unnecessary? if is_parallel: assert isinstance(dsl_expr, DependencyGraph), type(dsl_expr) else: assert isinstance(dsl_expr, DslExpression), type(dsl_expr) if is_verbose: if isinstance(dsl_expr, DependencyGraph): print_( "Compiled DSL source into %d partial expressions (root ID: %s)." % (len(dsl_expr.stubbed_calls), dsl_expr.root_stub_id)) print_() print_("Duration of compilation: %s" % compile_time_delta) print_() if isinstance(dsl_expr, DependencyGraph): if is_show_source: print_("Expression stack:") for stubbed_exprData in dsl_expr.stubbed_calls: print_(" " + str(stubbed_exprData[0]) + ": " + str(stubbed_exprData[1])) print_() # If the expression has any stochastic elements, the evaluation kwds must have an 'observation_date' (datetime). if dsl_expr.has_instances(dsl_type=StochasticObject): observation_date = evaluation_kwds['observation_date'] assert isinstance(observation_date, datetime.date) if is_verbose: print_("Observation time: %s" % observation_date) print_() # Avoid any confusion with the internal 'present_time' variable. if 'present_time' in evaluation_kwds: msg = ( "Don't set present_time here, set observation_date instead. " "Hint: Adjust effective present time with Fixing or Wait elements." ) raise DslError(msg) # Initialise present_time as observation_date. evaluation_kwds['present_time'] = observation_date # If the expression has any Market elements, a market simulation is required if dsl_expr.has_instances(dsl_type=Market): # If a market simulation is required, evaluation kwds must have 'path_count' (integer). if 'path_count' not in evaluation_kwds: evaluation_kwds['path_count'] = DEFAULT_PATH_COUNT path_count = evaluation_kwds['path_count'] assert isinstance(path_count, int) # If a market simulation is required, evaluation_kwds must have 'market_calibration' (integer). market_calibration = evaluation_kwds['market_calibration'] assert isinstance(market_calibration, dict) # If a market simulation is required, generate the simulated prices using the price process. if not 'all_market_prices' in evaluation_kwds: if is_verbose: print_("Price process: %s" % price_process_name) print_() price_process = get_price_process(price_process_name) if is_verbose: print_("Path count: %d" % path_count) print_() if is_verbose: print_("Finding all Market names and Fixing dates...") print_() # Extract market names from the expression. # Todo: Avoid doing this on the dependency graph, when all the Market elements must be in the original. market_names = find_market_names(dsl_expr) # Extract fixing dates from the expression. # Todo: Perhaps collect the fixing dates? fixing_dates = list_fixing_dates(dsl_expr) if is_verbose: print_("Simulating future prices for Market%s '%s' from observation time %s through fixing dates: %s." % ( '' if len(market_names) == 1 else 's', ", ".join(market_names), "'%04d-%02d-%02d'" % (observation_date.year, observation_date.month, observation_date.day), # Todo: Only print first and last few, if there are loads. ", ".join(["'%04d-%02d-%02d'" % (d.year, d.month, d.day) for d in fixing_dates[:8]]) + \ (", [...]" if len(fixing_dates) > 9 else '') + \ ((", '%04d-%02d-%02d'" % (fixing_dates[-1].year, fixing_dates[-1].month, fixing_dates[-1].day)) if len(fixing_dates) > 8 else '') )) print_() # Simulate the future prices. all_market_prices = price_process.simulate_future_prices( market_names, fixing_dates, observation_date, path_count, market_calibration) # Add future price simulation to evaluation_kwds. evaluation_kwds['all_market_prices'] = all_market_prices # Initialise the evaluation timer variable (needed by showProgress thread). evalStartTime = None if is_parallel: if is_verbose: len_stubbed_exprs = len(dsl_expr.stubbed_calls) lenLeafIds = len(dsl_expr.leaf_ids) msg = "Evaluating %d expressions (%d %s) with " % ( len_stubbed_exprs, lenLeafIds, 'leaf' if lenLeafIds == 1 else 'leaves') if is_multiprocessing and pool_size: msg += "a multiprocessing pool of %s workers" % pool_size else: msg += "a single thread" msg += ", please wait..." print_(msg) print_() # Define showProgress() thread. def showProgress(stop): progress = 0 movingRates = [] while progress < 100 and not stop.is_set(): time.sleep(0.3) if evalStartTime is None: continue # Avoid race condition. if not hasattr(dsl_expr, 'runner') or not hasattr( dsl_expr.runner, 'resultIds'): continue if stop.is_set(): break try: lenResults = len(dsl_expr.runner.resultIds) except IOError: break resultsTime = datetime.datetime.now() movingRates.append((lenResults, resultsTime)) if len(movingRates) >= 15: movingRates.pop(0) if len(movingRates) > 1: firstLenResults, firstTimeResults = movingRates[0] lastLenResults, lastTimeResults = movingRates[-1] lenDelta = lastLenResults - firstLenResults resultsTimeDelta = lastTimeResults - firstTimeResults timeDeltaSeconds = resultsTimeDelta.seconds + resultsTimeDelta.microseconds * 0.000001 rateStr = "%.2f expr/s" % (lenDelta / timeDeltaSeconds) else: rateStr = '' progress = 100.0 * lenResults / len_stubbed_exprs sys.stdout.write( "\rProgress: %01.2f%% (%s/%s) %s " % (progress, lenResults, len_stubbed_exprs, rateStr)) sys.stdout.flush() sys.stdout.write("\r") sys.stdout.flush() stop = threading.Event() thread = threading.Thread(target=showProgress, args=(stop, )) # Start showProgress() thread. thread.start() # Start timing the evaluation. evalStartTime = datetime.datetime.now() try: # Evaluate the primitive DSL expression. if is_parallel: if is_multiprocessing: dependency_graph_runner_class = MultiProcessingDependencyGraphRunner else: dependency_graph_runner_class = SingleThreadedDependencyGraphRunner value = dsl_expr.evaluate( dependency_graph_runner_class=dependency_graph_runner_class, pool_size=pool_size, **evaluation_kwds) else: value = dsl_expr.evaluate(**evaluation_kwds) except: if is_parallel: if is_verbose: if thread.isAlive(): # print "Thread is alive..." stop.set() # print "Waiting to join with thread..." thread.join(timeout=1) # print "Joined with thread..." raise # Stop timing the evaluation. evalTimeDelta = datetime.datetime.now() - evalStartTime if isinstance(dsl_expr, DependencyGraph): if is_verbose: # Join with showProgress thread. thread.join(timeout=3) if is_verbose: timeDeltaSeconds = evalTimeDelta.seconds + evalTimeDelta.microseconds * 0.000001 if is_parallel: len_stubbed_exprs = len(dsl_expr.stubbed_calls) rateStr = "(%.2f expr/s)" % (len_stubbed_exprs / timeDeltaSeconds) else: rateStr = '' print_("Duration of evaluation: %s %s" % (evalTimeDelta, rateStr)) print_() # Prepare the result. import scipy if isinstance(value, scipy.ndarray): mean = value.mean() stderr = value.std() / math.sqrt(path_count) return {'mean': mean, 'stderr': stderr} else: return value