-
Notifications
You must be signed in to change notification settings - Fork 586
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
Discrete dividens for american options #70
Comments
@cyrilchim If possible you could provide some help? This should be something really trivial, but I am honestly missing how to do it with the library |
Hi @mlambelho Could you please share the full code you are running? |
@cyrilchim here it is. I included the quantlib code for comparison. https://colab.research.google.com/drive/1WiS1Lz259vv1UZZTYK5TBafG1MFir3WW?usp=sharing |
I looked in the colab. As I understand, you indeed need to adjust the grid by the dividend at the dividend date. You also need to adjust boundary conditions, @tf.function(autograph=True)
def american_option(number_grid_points,
strike,
volatility,
risk_free_rate,
expiry,
dividend_time,
dtype=tf.float64):
""" Computes American Call options prices."""
# Define the coordinate grid
s_min = 250
s_max = 700
grid = pde.grids.uniform_grid(minimums=[s_min],
maximums=[s_max],
sizes=[number_grid_points],
dtype=dtype)
# Define the values grid for the final condition
s = grid[0]
final_values_grid = tf.nn.relu(
s - 1.57 * tf.exp(-risk_free_rate * (dividend_time - expiry)) - strike)
# Define the PDE coefficient functions
def second_order_coeff_fn(t, grid):
#del t
#s = grid[0]
s = grid[0]
if t > dividend_time:
s = s - 1.57
del t
return [[volatility ** 2 * s ** 2 / 2]]
def first_order_coeff_fn(t, grid):
#del t
#s = grid[0]
s = grid[0]
if t > dividend_time:
s = s - 1.57
del t
return [risk_free_rate * s]
def zeroth_order_coeff_fn(t, grid):
del t, grid
return -risk_free_rate
# Define the boundary conditions
@pde.boundary_conditions.dirichlet
def lower_boundary_fn(t, grid):
del t, grid
return tf.constant(0.0, dtype=dtype)
@pde.boundary_conditions.dirichlet
def upper_boundary_fn(t, grid):
#del grid
if t > dividend_time:
res = tf.squeeze(s_max - strike * tf.exp(-risk_free_rate * (expiry - t))
- (1.57 * tf.exp(-risk_free_rate * (dividend_time - t))))
else:
res = tf.squeeze(s_max - strike * tf.exp(-risk_free_rate * (expiry - t)))
return res
# In order to price American option one needs to set option values to
# V(x) := max(V(x), max(x - strike, 0)) after each iteration
def values_transform_fn(t, grid, values):
s = grid[0]
# Ensure shape of t is the same on both branches
if t > dividend_time:
s = s - 1.57 * tf.exp(-risk_free_rate * (dividend_time - t))
else:
s = tf.broadcast_to(s, tf.shape(values))
values_floor = tf.nn.relu(s - strike)
return grid, tf.maximum(values, values_floor)
# Solve
estimate_values, estimate_grid, _, _ = \
pde.fd_solvers.solve_backward(
start_time=expiry_dte,
end_time=0,
values_transform_fn=values_transform_fn,
coord_grid=grid,
values_grid=final_values_grid,
num_steps=500,
boundary_conditions=[(lower_boundary_fn, upper_boundary_fn)],
second_order_coeff_fn=second_order_coeff_fn,
first_order_coeff_fn=first_order_coeff_fn,
zeroth_order_coeff_fn=zeroth_order_coeff_fn,
dtype=dtype
)
return estimate_values, estimate_grid[0] I estimate prices as prices = (estimate[:, spot_index + 1] + estimate[:, spot_index - 1]) / 2 and relative absolute error of Also, in your code you can try speeding up both pricing and Brent search with XLA compilation (I get 4x speed up for Brent and 6x for option pricing):
Hope this helps. |
Thank you for your help. If possible could you share the colab that you are running? jit_compile is much slower for me every time that I test it. Could this be expandable for more than one dividend at one point in time? So two or three dividends? On another note, is it possible to compute Greeks here? Things like theta, vega, gamma, delta? Thank you in advance |
Edited your colab. Please check it out (see the last section there). Delta and Gamma should be estimated with finite difference as you already have price estimate on the grid. I've added that too. You can try computing gradients directly though it will be quite slow. I've added theta and vega calculation using standard AD. I have changed how dividend date is incorporated. This should make it clear how to add multiple dividend dates. Hope this helps! |
Hi @cyrilchim thank you very very much for your reply, it is super helpful. A couple of questions though:
Apologize for the questions, thank you very much for your help. |
Hi @mlambelho
Please let me know if this helps! |
Thank you @cyrilchim, solutions 1 and 2 worked perfectly! Thank you! Regarding solution 3, I am sure your suggestion will also prove successful, I just can't seem to make it work. So, my understanding is that if we want to run in batches 20 options for example (10 puts and 10 calls), we need to provide a list with size 20 for s_min, s_max and sizes (where the number of grid points is always the same), correct? https://colab.research.google.com/drive/10Vu37T5f45E9yb1-LZNODFuAcTofmoy-?usp=sharing I provide here what I was able to accomplish for other people possibly watching this thread as well. So the pricing of the put/call options at the same time seems to work pretty well. I used both tf.where and tf.multiply to be able to make it work. However, in order to test the batch of grids, I converted the grid into three lists of 20 s_min, s_max and sizes (of course this does not make much sense since all batches have the same s_min and s_max, but that is not relevant for testing), but I am seeing other problems:
I am not exactly sure of what could be causing the problem. I tested changing multiple other things, but nothing seems to be working for me. Thank you in advance again for your help. Much appreciated! |
Sorry for the late reply. Typically shape error means there is some shape mismatch somewhere. The best way to debug that is to remove In your case, I simply printed out shapes for each of the input functions to the pde solver ( I noticed that
on line 104. There should be
This should be it. Also, where you get spot prices, you need a slice on the grid
Please let me know if this works (it seems to work for me!) |
Also, I think you need to adjust the boundary conditions for puts. The lower boundary for put is
and the upper boundary is zero. |
I was able to make the adjustments you recommended, you can see the changes below: https://colab.research.google.com/drive/10Vu37T5f45E9yb1-LZNODFuAcTofmoy-?usp=sharing I think the pricing of the put options looks ok now. If possible could you elaborate on how should I safely specify a grid? For example if a stock has spot 100, what should be the safest grid to specify that is neither too big nor too small? In my mind it would be possible to specify a grid just by knowing the current spot price. Also I am seeing an issue in the notebook above where the brent optimizer with jit compile seems to be way slower vs without jit compile. Finding this quite strange. Lastly, is it possible within this framework to price options at the same time with different dividends? I think it should be, the issue here is that I am not sure how is it possible to make each row of the tensor instead of using the same tf.where statement with the extra term, use a different tf.where statement per row (since each row would require different dividends). Again, thank you very much for all the help. |
You could use similar approach to QuantLib (ql). Under Black-Scholes the distribution of the terminal values
The grid can then be constructed as
That is about it! You will also need to update how prices and gradients are computed (see, e.g., here)
Let me know if this works (I get 9e-5 relative error compared to QL). |
@cyrilchim I am coming back to you this late because I was trying for so long to make the grid work as you suggested, however I am not able to get it to work. I think something really small must be going on... But can't figure out what is. from the error message https://colab.research.google.com/drive/1TsbOkm_LH_SgRRICaBUfh7nigEVyp6A8?usp=sharing Basically there are 3 things that I would like to do that I still wasn't able: I tried to prevent asking for help again, but really not figuring out what is happening. Many apologies |
Please let me know if that makes sense |
I have added a section on CPU utilization optimization. |
This was extremely helpful @cyrilchim. Thank you very much. If possible, I have one additional question regarding how to calculate the deltas where the stocks have different grids.
On this example, we are assuming that the spot_index is exactly at the center of the grid. What happens if that is not the case? How can deltas be computed if that is not the case? The formula to calculate the grid relies on volatilities values. Meaning that even on the same stock/same expiry, the grids will vary. In such a case how can deltas be computed with AD?
Uploading here my link where I try to calculate deltas for 20 options which although they are the same stock, they have slightly different grids due to the different volatility term in the quantile formula above. https://colab.research.google.com/drive/1WkFza_cfH1fC-B9jMT7RRQvi3vIaPZP9?usp=sharing The 'correct' deltas were computed in a url above, leaving it here as well: https://colab.research.google.com/drive/10Vu37T5f45E9yb1-LZNODFuAcTofmoy-?usp=sharing Deltas that I am observing now: I suspect the reason for this discrepancy is related with the spot_index that is not aligned in this new approach of calculating the grid. If you could help me how can I calculate the delta taking into account that the spot will assume a different position in the grid (i.e spot_index is not int(number_grid_points/2) ) it would be very appreciated. many many apologies again. thank you |
Sorry for the late reply. I think you just need to adjust |
Hi @cyrilchim, my problem was indeed that spot_index was not in the correct index. Here it is the correct final solution. https://colab.research.google.com/drive/1tfWjWQzP5ZS92x3zoMV7GhBbTlKUYeLk?usp=sharing Everything seems to be working as expected, however it seems that I am only able to price more than 1 option. If I try to run it with a single option the code is failing. ValueError: slice index 1 of dimension 0 out of bounds. for '{{node solve_backward/while/composite_scheme_step/strided_slice_6}} = StridedSlice[Index=DT_INT32, T=DT_DOUBLE, begin_mask=0, ellipsis_mask=0, end_mask=0, new_axis_mask=0, shrink_axis_mask=1](solve_backward/while/composite_scheme_step/sub, solve_backward/while/composite_scheme_step/strided_slice_6/stack, solve_backward/while/composite_scheme_step/strided_slice_6/stack_1, solve_backward/while/composite_scheme_step/strided_slice_6/stack_2)' with input shapes: [1,1022], [1], [1], [1] and with computed input tensors: input[1] = <1>, input[2] = <2>, input[3] = <1>. Do you think you could help? I have tried debugging it a lot, but not sure where the error is coming from. I bet is just an indexing in one of the statements but I can't seem to be able to find it. |
Looking into it. There is probably some bug. Will report back once investigate |
Ok, should be fixed now! Please check it out. Thank you for spotting this one |
Hi @cyrilchim . Thank you very much. It is working perfectly. The only issue that I am seeing is that some runs take a lot longer than other runs. I leave here a snippet as an example: https://colab.research.google.com/drive/1L4YXephA0tg3XkSVbX4GD5o8klAxXCa2?usp=sharing Is there any possibility to use dynamic shapes with the current structure of the code? Perhaps dynamic shapes might also make the first run go faster if the first run only has 1 option? I tried to use all of your suggestions across the code Sorry again to bother again, if you could provide a bit of help would be really appreciated. Thank you very much in advance |
Sorry for keeping you waiting. You need to add Could you please give more details on the use case? Seems like what you need is to wrap the pricer in In your case this will be something like
Save the model with Once that is done, use TF serving to start local server for your model. The link contains copy-pastable code to do that. Please let me know if that makes sense? |
Hi @cyrilchim is there any news on the American examples being formalized and made available? If not, what needs to be done as I am currently trying to piecemeal together a working example from this thread to get the IV, price and greeks for an american option with discrete dividends. |
My goal is to compute american option prices with discrete dividends on a specific date. The example provided for American options does not indicate how could this be done.
Given that discrete options happen at a specific date I thought it would be possible to change the value of spot (s) by the respective dividend after the ex_date occurs. Lets say that expiry = 0.8 and for example ex_date = 0.5, with a dividend of 1.5%, then the code would become:
Unfortunately, this approach does not yield proper results when compared with Quantlib. Does the tff library allow for the computation of option prices using discrete dividends? Ideally the only thing that it is required is to shift all spot prices based on the dividend starting at ex date.
Thank you in advance
The text was updated successfully, but these errors were encountered: