Skip to content

Commit

Permalink
Fixed README file. Fixed discounting period.
Browse files Browse the repository at this point in the history
Adding parameter to select approximate present value formula,
over slightly longer but accurate formula.
  • Loading branch information
johnbywater committed Sep 24, 2017
1 parent c659334 commit 1a2f4f9
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 23 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,9 @@ results = calc("Settlement('2111-1-1', 1000.0)",
interest_rate=2.5,
)

assert round(results.fair_value, 3) == 81.950, results.fair_value
raise Exception()
# assert round(results.fair_value, 3) == 84.647, results.fair_value
assert round(results.fair_value, 3) == 82.085, results.fair_value

```

If the effective present time of the `Settlement` is the same as the settlement date, there is no discounting.
Expand All @@ -301,7 +302,7 @@ results = calc("Fixing('2111-1-1', Settlement('2111-1-1', 10))",
observation_date='2011-1-1',
interest_rate=2.5,
)
assert_equal(results.fair_value, 10.0)
assert results.fair_value == 10.0
```

Similarly, if the `interest_rate` is `0.0`, there is no discounting.
Expand All @@ -311,10 +312,9 @@ results = calc("Settlement('2111-1-1', 10)",
observation_date='2011-1-1',
interest_rate=0,
)
assert_equal(results.fair_value, 10.0)
assert results.fair_value == 10.0
```


### Fixing

The `Fixing` element is used to condition the "effective present time" of its included expression. If a
Expand Down
11 changes: 7 additions & 4 deletions quantdsl/application/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,10 @@ def identify_simulation_requirements(self, contract_specification, observation_d
requirements,
periodisation)

def start_contract_valuation(self, contract_specification_id, market_simulation_id, periodisation):
return start_contract_valuation(contract_specification_id, market_simulation_id, periodisation)
def start_contract_valuation(self, contract_specification_id, market_simulation_id, periodisation,
approximate_discounting):
return start_contract_valuation(contract_specification_id, market_simulation_id, periodisation,
approximate_discounting)

def loop_on_evaluation_queue(self):
loop_on_evaluation_queue(
Expand Down Expand Up @@ -208,8 +210,9 @@ def simulate(self, contract_specification, price_process_name, calibration_param
)
return market_simulation

def evaluate(self, contract_specification_id, market_simulation_id, periodisation=None):
return self.start_contract_valuation(contract_specification_id, market_simulation_id, periodisation)
def evaluate(self, contract_specification_id, market_simulation_id, periodisation=None,
approximate_discounting=False):
return self.start_contract_valuation(contract_specification_id, market_simulation_id, periodisation, approximate_discounting)

def get_result(self, contract_valuation):
call_result_id = make_call_result_id(contract_valuation.id, contract_valuation.contract_specification_id)
Expand Down
11 changes: 9 additions & 2 deletions quantdsl/domain/model/contract_valuation.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ class Created(EventSourcedEntity.Created):
class Discarded(EventSourcedEntity.Discarded):
pass

def __init__(self, market_simulation_id, contract_specification_id, periodisation, **kwargs):
def __init__(self, market_simulation_id, contract_specification_id, periodisation, approximate_discounting,
**kwargs):
super(ContractValuation, self).__init__(**kwargs)
self._market_simulation_id = market_simulation_id
self._contract_specification_id = contract_specification_id
self._periodisation = periodisation
self._approximate_discounting = approximate_discounting

@property
def market_simulation_id(self):
Expand All @@ -29,14 +31,19 @@ def contract_specification_id(self):
def periodisation(self):
return self._periodisation

@property
def approximate_discounting(self):
return self._approximate_discounting


def start_contract_valuation(contract_specification_id, market_simulation_id, periodisation):
def start_contract_valuation(contract_specification_id, market_simulation_id, periodisation, approximate_discounting):
contract_valuation_id = create_contract_valuation_id()
contract_valuation_created = ContractValuation.Created(
entity_id=contract_valuation_id,
market_simulation_id=market_simulation_id,
contract_specification_id=contract_specification_id,
periodisation=periodisation,
approximate_discounting=approximate_discounting,
)
contract_valuation = ContractValuation.mutator(event=contract_valuation_created)
publish(contract_valuation_created)
Expand Down
8 changes: 6 additions & 2 deletions quantdsl/domain/services/contract_valuations.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,14 +231,17 @@ def compute_call_result(contract_valuation, call_requirement, market_simulation,
perturbation_dependencies.dependencies,
dependency_results, market_simulation.path_count,
market_simulation.perturbation_factor,
contract_valuation.periodisation)
contract_valuation.periodisation,
contract_valuation.approximate_discounting,
)

# Return the result.
return result_value, perturbed_values


def evaluate_dsl_expr(dsl_expr, first_commodity_name, simulation_id, interest_rate, present_time, simulated_value_dict,
perturbation_dependencies, dependency_results, path_count, perturbation_factor, periodisation):
perturbation_dependencies, dependency_results, path_count, perturbation_factor, periodisation,
approximate_discounting):
evaluation_kwds = {
'simulated_value_dict': simulated_value_dict,
'simulation_id': simulation_id,
Expand All @@ -248,6 +251,7 @@ def evaluate_dsl_expr(dsl_expr, first_commodity_name, simulation_id, interest_ra
'first_commodity_name': first_commodity_name,
'path_count': path_count,
'periodisation': periodisation,
'approximate_discounting': approximate_discounting,
}

result_value = None
Expand Down
18 changes: 14 additions & 4 deletions quantdsl/interfaces/calcandplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ def __init__(self, fair_value, periods):

def calc_print_plot(source_code, title='', observation_date=None, periodisation=None, interest_rate=0,
path_count=20000, perturbation_factor=0.01, price_process=None, supress_plot=False,
max_dependency_graph_size=DEFAULT_MAX_DEPENDENCY_GRAPH_SIZE, timeout=None, verbose=False):
max_dependency_graph_size=DEFAULT_MAX_DEPENDENCY_GRAPH_SIZE, timeout=None, verbose=False,
approximate_discounting=False):
# Calculate and print the results.
results = calc_print(source_code,
max_dependency_graph_size=max_dependency_graph_size,
Expand All @@ -49,6 +50,7 @@ def calc_print_plot(source_code, title='', observation_date=None, periodisation=
periodisation=periodisation,
timeout=timeout,
verbose=verbose,
approximate_discounting=approximate_discounting,
)

# Plot the results.
Expand All @@ -66,7 +68,7 @@ def calc_print_plot(source_code, title='', observation_date=None, periodisation=

def calc_print(source_code, observation_date=None, interest_rate=0, path_count=20000, perturbation_factor=0.01,
price_process=None, periodisation=None, max_dependency_graph_size=DEFAULT_MAX_DEPENDENCY_GRAPH_SIZE,
timeout=None, verbose=False):
timeout=None, verbose=False, approximate_discounting=False):
# Calculate the results.
results = calc(
source_code=source_code,
Expand All @@ -79,6 +81,7 @@ def calc_print(source_code, observation_date=None, interest_rate=0, path_count=2
max_dependency_graph_size=max_dependency_graph_size,
timeout=timeout,
verbose=verbose,
approximate_discounting=approximate_discounting,
)

# Print the results.
Expand All @@ -88,7 +91,7 @@ def calc_print(source_code, observation_date=None, interest_rate=0, path_count=2

def calc(source_code, observation_date=None, interest_rate=0, path_count=20000, perturbation_factor=0.01,
price_process=None, periodisation=None, max_dependency_graph_size=DEFAULT_MAX_DEPENDENCY_GRAPH_SIZE,
timeout=None, verbose=False):
timeout=None, verbose=False, approximate_discounting=False):
cmd = Calculate(
source_code=source_code,
observation_date=observation_date,
Expand All @@ -100,24 +103,30 @@ def calc(source_code, observation_date=None, interest_rate=0, path_count=20000,
max_dependency_graph_size=max_dependency_graph_size,
timeout=timeout,
verbose=verbose,
approximate_discounting=approximate_discounting,
)
return cmd.calculate()


class Calculate(object):
def __init__(self, source_code, observation_date=None, interest_rate=0, path_count=20000, perturbation_factor=0.01,
price_process=None, periodisation=None, max_dependency_graph_size=DEFAULT_MAX_DEPENDENCY_GRAPH_SIZE,
timeout=None, verbose=False):
timeout=None, verbose=False, approximate_discounting=False):
self.timeout = timeout
self.source_code = source_code
self.observation_date = observation_date
self.interest_rate = interest_rate
self.path_count = path_count
self.perturbation_factor = perturbation_factor
# Todo: Optional double or single sided deltas.
# self.double_sided_deltas = double_sided_deltas
self.price_process = price_process
self.periodisation = periodisation
self.approximate_discounting = approximate_discounting
self.max_dependency_graph_size = max_dependency_graph_size
self.verbose = verbose
# Todo: Repetitions - number of times the computation will be repeated (multiprocessing).
# self.repetitions = repetitions

def calculate(self):
self.node_evaluations_count = 0
Expand Down Expand Up @@ -200,6 +209,7 @@ def calculate(self):
contract_specification_id=contract_specification.id,
market_simulation_id=market_simulation.id,
periodisation=self.periodisation,
approximate_discounting=self.approximate_discounting,
)

# Wait for the result.
Expand Down
7 changes: 5 additions & 2 deletions quantdsl/priceprocess/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import datetime
from abc import ABCMeta, abstractmethod
import six
from dateutil.relativedelta import relativedelta

DAYS_PER_YEAR = 365
SECONDS_PER_DAY = 86400
Expand All @@ -29,10 +30,12 @@ def get_commodity_names_and_fixing_dates(self, observation_date, requirements):

def get_duration_years(start_date, end_date, days_per_year=DAYS_PER_YEAR):
try:
time_delta = datetime_from_date(end_date) - datetime_from_date(start_date)
r = relativedelta(end_date, start_date)
# time_delta = datetime_from_date(end_date) - datetime_from_date(start_date)
except TypeError as inst:
raise TypeError("%s: start: %s end: %s" % (inst, start_date, end_date))
return time_delta.total_seconds() / float(days_per_year * SECONDS_PER_DAY)
else:
return r.years + r.months / 12.0 + (r.days + r.hours / 24) / float(days_per_year)


def datetime_from_date(observation_date):
Expand Down
6 changes: 5 additions & 1 deletion quantdsl/semantics.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,11 @@ def evaluate(self, **kwds):
def discount(self, value, date, **kwds):
r = float(kwds['interest_rate']) / 100
T = get_duration_years(kwds['present_time'], date)
return value * math.exp(- r * T)
# Todo: Support proper discounting (it's 16% slower).
if True or kwds['approximate_discounting']:
return value * math.exp(- r * T)
else:
return value / (1 + r) ** T


class DslConstant(DslExpression):
Expand Down
7 changes: 4 additions & 3 deletions quantdsl/tests/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def assert_contract_value(self, specification, expected_value, expected_deltas=N
calibration_params=self.calibration_params,
observation_date=observation_date,
path_count=self.PATH_COUNT,
interest_rate='2.5',
interest_rate=2.5,
perturbation_factor=0.001,
periodisation=periodisation,
)
Expand Down Expand Up @@ -233,7 +233,8 @@ def test_bermudan_with_monthly_deltas(self):
Fixing(Date('2011-06-01'), Choice(Market('NBP') - 9,
Fixing(Date('2012-01-01'), Choice(Market('NBP') - 9, 0))))
"""
self.assert_contract_value(specification, 2.6093, expected_deltas={'NBP-2011-6': 0.2208},
self.assert_contract_value(specification, 2.6093,
expected_deltas={'NBP-2011-6': 0.105},
periodisation='monthly')

def test_identical_fixings(self):
Expand Down Expand Up @@ -347,7 +348,7 @@ def test_brownian_increments(self):
# NB: Expected value should be 0.0000. It is slightly
# off due to small path count, and consistently at the
# slightly negative value due to the seed being fixed.
self.assert_contract_value(specification, -0.01, expected_deltas={'#1': 0.00}, periodisation='alltime')
self.assert_contract_value(specification, 0.00, expected_deltas={'#1': 0.00}, periodisation='alltime')


class FunctionTests(ApplicationTestCase):
Expand Down

0 comments on commit 1a2f4f9

Please sign in to comment.