Finite difference pricer adjustments
This branch is still a work in progress
This PR is for addressing the issues raised by @avhz in this comment - the code in question was initially merged from this PR to address issue 98.
There were some errors in implementation, namely:
- Call/Put boundary conditions had implemented $e^{-rT - t}$ instead of $e^{-r(T - t)}$
- The time steps were iterating
(1..self.time_steps)instead of(0..self.time_steps) - The boundary condition for the Crank-Nicolson method was implemented as
self.call_boundary(t + 1, T, delta_t) - self.call_boundary(t, T, delta_t)when it should beself.call_boundary(t + 1, T, delta_t) + self.call_boundary(t, T, delta_t)(same amendment made for put option boundary
Although the above amendments have been made, there are still issues with convergence in some places. For brevity, I have limited the cases to European call options.
The cases below were run with the following configuration:
strike_price: 10.0;
risk_free_rate: 0.05;
volatility: 0.5;
evaluation_date: Some(date!(2024 - 01 - 01));
expiration_date: date!(2025 - 01 - 01);
time_steps: 10000; // increased 10-fold for stability - see note below
price_steps: 100;
type_flag: TypeFlag::Call;
exercise_flag: ExerciseFlag::European;
with a distinct value for initial_price in each case:
At the money
initial_price: 10.0
Target: 2.179260421286683
| Method | Value | Error |
|---|---|---|
| Explicit | 2.1752124177257035 | 0.004048003560979563 |
| Implicit | 2.1751386801265697 | 0.004121741160113324 |
| Crank-Nicolson | 2.175175548933553 | 0.004084872353129931 |
In the money
initial_price: 15.0
Target: 6.064426504411616
| Method | Value | Error |
|---|---|---|
| Explicit | 6.064451145206732 | 0.00002464079511632633 |
| Implicit | 6.06441080128941 | 0.00001570312220611214 |
| Crank-Nicolson | 6.064430972254538 | 0.000004467842922295517 |
Out the money
initial_price: 1.0
Target: 1.0140475395001201e-6
| Method | Value | Error |
|---|---|---|
| Explicit | -1.0119200252625027 | 1.0119210393100422 |
| Implicit | -1.011847275245909 | 1.0118482892934484 |
| Crank-Nicolson | -1.011883645221771 | 1.0118846592693105 |
There is some level of convergence for the at/in the money cases, although I think there is room for improvement for accuracy. Whilst experimenting with different time step sizes, it seems that there is an issue with consistency i.e. $\Delta t \rightarrow 0 \Rightarrow \text{Error}\rightarrow 0$ - this suggests there is still an issue with implementation - Note that there is convergence, but just not to the target value. The out the money case further confirms that there is something wrong.
Stability issues
It was noticed that explicit() was blowing up in some configurations. It is likely that this indicates an issue with stability, which can be resolved by having a larger time steps, which in turn provides smaller $\Delta t$. Hence, in the config above, I changed the time step from 1000 to 10000 (though 5000 suffices for stability).