-
-
Notifications
You must be signed in to change notification settings - Fork 166
Finite Difference Pricer using heap allocated vectors #211
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
…ros" representation of matrix
…atrix_without_zeros()
…e_trimmed_tridiagonal_matrix and tridiagonal_matrix_multiply_vector -> trimmed_tridiagonal_matrix_multiply_vector + formatting
…rix data structure
…e method to produce tridiagonal matrix
avhz
reviewed
Apr 15, 2024
avhz
reviewed
Apr 15, 2024
avhz
reviewed
Apr 15, 2024
avhz
reviewed
Apr 15, 2024
avhz
reviewed
Apr 15, 2024
avhz
reviewed
Apr 15, 2024
avhz
requested changes
Apr 15, 2024
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just have some questions.
…_boundary functions + minor formatting
…ps and price_steps attribute + import Duration for unit tests
Changes have been made as per suggestions from @avhz to this branch and the nalgebra branch |
…rix to use match + avoid indexing
…hanges on the branch
thanks for your work ! |
This was referenced Apr 25, 2024
Closed
Closed
Closed
Closed
This was referenced Jun 24, 2024
Closed
Closed
Merged
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Important Notes
This Pull Request is regarding issue #98 for the creation of a finite difference pricer.
I have made 2 PRs for this particular issue. Only one of them needs to be merged if the decision has been made to merge.
nalgebra vs heap allocated vectors
This PR uses heap allocated vectors to represent vectors and matrices with functions to handle the appropriate linear algebra calculations (+ matrix multiplication has been optimised for tridiagonal matrices). The other PR utilises the nalgebra package.
The way in which the objects and methods should be used are exactly the same for both PRs.
There are differences in speed performance between the two implementations.
An example with the following parameters:
We get the following runtimes when we run one instance of both implementations
We can immediately see that the explicit method is about
17 2224 times faster using vectors (this PR) than using the nalgebra packageand runtime for the Crank-Nicolson method is roughly halvedand runtime for the Crank-Nicolson method is 4 times faster and implicit method is roughly halved (This is at least the case for the machine I am running the code on).Contents of this PR
This PR implements the following features:
A struct,
FiniteDifferencePricer
in the moduleinstruments::options::finite_difference_pricer
, where the attributes are the parameters of an option, namely:initial_price: f64
strike_price: f64
risk_free_rate: f64
volatility: f64
evaluation_date: Option<Date>
,expiration_date: Date
,time_steps: u32
,price_steps: u32
,type_flag: TypeFlag
,exercise_flag: ExerciseFlag
where
TypeFlag
andExerciseFlag
are imported frominstruments::options::option
A constructor for the above
struct
with thenew()
method which takes in the above defined attributes as argumentsThree public methods implemented in the
FiniteDifferencePricer
for the three widely known finite difference methods, namelyexplicit()
,implicit()
andcrank_nicolson()
which returns the option price rounded to 2 decimal placesDemonstration by example
We can start off by defining the finite difference object by calling the
new()
constructorNow that the object has been defined, we can run any of the 3 methods to provide a numerical approximation of the option price:
Running the above code yields the following results:
Notes
Since I am using heap allocated vectors in this PR, I have had to manually calculate the inverse for the tridiagonal matrix for the implicit and Crank-Nicolson finite difference methods. I have done so by implementing the theorem outlined in this paper.
I have opted to solve this problem using a
struct
to define the option parameters and from there we can run each finite difference method from the object. Alternatively, I could amend the PR so that it can be purely functional.The code utilises the Dirichlet condition for the boundaries of the stock price extremities.
If we define the following:
Then Dirichlet boundary conditions for the call options are:
and for the put options:
EDIT 1: Optimisation amendmentsIn the code there are two private methods that seemingly do the same thing,create_tridiagonal_matrix()
andcreate_trimmed_tridiagonal_matrix()
. The difference between the two is that the former returns the matrx representation whole i.e. inclusive of all outer zeros, whilst the former only stores the the values of the three diagonals on a row by row basis.As such, two private methods for matrix multiplication had to be created to handle the two data structures, appropriately namedmatrix_multiply_vector()
andtrimmed_tridiagonal_matrix_multiply_vector()
.The reason both representation exist is because for explicit methods, only the diagonal values of the matrix are required to implement the approximation, whereas for implicit methods we require the whole matrix in order to calculate its inverse (though, this can be amended in a future PR).This change has fractionally increased speed performances forexplicit()
andcrank_nicolson()
.EDIT 2: Condensing methods + single data structure for tridiagonal matrices
Previously two distinct representations of a tridiagonal matrix were implemented:
An example for each case respectively:
Data structure 1:
Data structure 2:
This branch will now only utilise data structure 2 to represent tridiagonal matrices and operations.