def add_butterfly(init_instruments, book, spread): dimension = book.instrument_dim if dimension != len(init_instruments): raise ValueError("wrong dimension of init_instruments.") for link, spot in enumerate(init_instruments): itm = derivatives.PutCall( book.maturity, spot - spread / 2, book.numeraire_simulator.rate, book.instrument_simulator.volatility[link], 1) atm = derivatives.PutCall( book.maturity, spot, book.numeraire_simulator.rate, book.instrument_simulator.volatility[link], 1) otm = derivatives.PutCall( book.maturity, spot + spread / 2, book.numeraire_simulator.rate, book.instrument_simulator.volatility[link], 1) book.add_derivative(itm, link, 1 / dimension) book.add_derivative(atm, link, -2 / dimension) book.add_derivative(otm, link, 1 / dimension)
def test_put_value_delta(self): maturity, strike, rate, volatility, theta = 1.3, 110, 0.02, 0.05, -1 putcall = derivatives.PutCall(maturity, strike, rate, volatility, theta) time = tf.constant([0, 0.41, maturity], FLOAT_DTYPE) instrument = tf.constant([[100, 110, 120], [110., 121, 85]], FLOAT_DTYPE) numeraire = tf.math.exp(rate * time) price_expected = tf.constant([[ 7.4973075500600146, 1.2255281792756278, 0. ], [1.3101071219942781, 0.014761684729265312, 25.]]) / numeraire delta_expected = tf.constant([[ -0.88244073147661295, -0.3442306041250397, 0. ], [-0.31398908314745166, -0.0077279609756593093, -1]]) / numeraire payoff_expected = price_expected[..., -1] price_result = putcall.value(time, instrument, numeraire) delta_result = putcall.delta(time, instrument, numeraire) payoff_result = putcall.payoff(time, instrument, numeraire) assert_near(price_result, price_expected) assert_near(delta_result, delta_expected) assert_near(payoff_result, payoff_expected)
def test_call(self): maturity, strike, rate, volatility, theta = 0.25, 90, 0.05, 0.2, 1 putcall = derivatives.PutCall(maturity, strike, rate, volatility, theta) time = tf.constant([0, 0.1, maturity], FLOAT_DTYPE) instrument = tf.constant([[100, 110, 91], [100., 121, 85]], FLOAT_DTYPE) numeraire = tf.math.exp(rate * time) price_expected = tf.constant([[ 11.670086691861101, 20.680949336572269, 1. ], [11.670086691861101, 31.672557545579522, 0.]]) / numeraire delta_expected = tf.constant([[ 0.89039005940552085, 0.99679661077351212, 1. ], [0.89039005940552085, 0.99996199608869929, 0.]]) / numeraire payoff_expected = price_expected[..., -1] adjoint_expected = tf.constant([[0.91, 91 / 110, 1.], [0., 0., 0.] ]) / numeraire[-1] price_result = putcall.value(time, instrument, numeraire) delta_result = putcall.delta(time, instrument, numeraire) payoff_result = putcall.payoff(time, instrument, numeraire) adjoint_result = putcall.adjoint(time, instrument, numeraire) assert_near(price_result, price_expected) assert_near(delta_result, delta_expected) assert_near(payoff_result, payoff_expected) assert_near(adjoint_result, adjoint_expected)
def add_calls(init_instruments, book): dimension = book.instrument_dim if dimension != len(init_instruments): raise ValueError("wrong dimension of init_instruments.") for link in tf.range(dimension): derivative = derivatives.PutCall( book.maturity, init_instruments[link], book.numeraire_simulator.rate, book.instrument_simulator.volatility[link], 1 ) book.add_derivative(derivative, link, 1 / dimension)
def test_call(self): drift = tf.constant([0.0191846154, 0.0171541667, 0.0045500000], FLOAT_DTYPE) volatility = tf.constant([0.2920484681, 0.2823561581, 0.2100000000], FLOAT_DTYPE) gs = tf.constant( [[398.1071705535, 297.8632906618, 484.5364955981, 455.9051533831], [450.6200285030, 510.0596651041, 505.0054119235, 396.7001896122]], FLOAT_DTYPE) scale = tf.constant( [[5.1753932172, 4.8402784733, 4.8453649560, 2.9012146124], [5.3255094278, 5.4799798730, 5.0500541192, 3.2669427380]], FLOAT_DTYPE) result_value = self.option.value(self.time, self.instrument, self.numeraire) result_delta = self.option.delta(self.time, self.instrument, self.numeraire) result_adjoint = self.option.adjoint(self.time, self.instrument, self.numeraire) for k, (d, v) in enumerate(zip(drift, volatility)): benchmark = derivatives.PutCall(self.maturity, self.strike, d, v, self.option.theta) expected_value = benchmark.value(self.time, gs, self.numeraire) expected_delta = benchmark.delta(self.time, gs, self.numeraire) \ * scale expected_adjoint = benchmark.adjoint(self.time, gs, self.numeraire) * scale tf.debugging.assert_near(result_value[..., k], expected_value[..., k]) tf.debugging.assert_near(result_delta[..., k], expected_delta[..., k]) tf.debugging.assert_near(result_adjoint[..., k], expected_adjoint[..., k])
def add_random_putcalls(init_instruments, book, number_of_derivatives=3): dimension = book.instrument_dim if dimension != len(init_instruments): raise ValueError("wrong dimension of init_instruments.") for link in tf.range(dimension): for _ in tf.range(number_of_derivatives): spot = init_instruments[link] strike = tf.random.uniform((1, ), minval=spot - 10.0, maxval=spot + 10.0) theta = tf.sign(tf.random.uniform((1, )) - 0.5) derivative = derivatives.PutCall( book.maturity, strike, book.numeraire_simulator.rate, book.instrument_simulator.volatility[link], theta ) book.add_derivative(derivative, link, 1 / dimension)
d = (instrument - self.strike) / vol_time return -d * self.delta(time, instrument, numeraire) cost = False spot = 1 strike = 1 timesteps = 14 sigma = 0.2 maturity = timesteps / 250 if cost: instrument_simulator = simulators.GBM(0.0, 0.0, [[sigma]]) derivative = derivatives.PutCall(maturity, strike, 0.0, sigma, 1) else: instrument_simulator = BrownianMotion(sigma) derivative = BachelierBinary(maturity, strike, sigma) numeraire_simulator = simulators.ConstantBankAccount(0.0) book = books.DerivativeBook(maturity, instrument_simulator, numeraire_simulator) book.add_derivative(derivative, 0, 1.0) init_instruments = tf.constant([spot], FLOAT_DTYPE) init_numeraire = tf.constant([1.0], FLOAT_DTYPE) driver = utils.HedgeDriver( timesteps=timesteps,
import derivatives import random_books import gradient_models import gradient_driver import hedge_models tf.get_logger().setLevel('ERROR') timesteps = 13 init_instruments, init_numeraire, book = random_books.random_empty_book( timesteps / 52, 1, 0.02, 0.05, 0.2) init_numeraire = tf.ones_like(init_numeraire) spread = 10 itm = derivatives.PutCall(book.maturity, init_instruments - spread / 2, book.instrument_simulator.rate, book.instrument_simulator.volatility, 1) atm = derivatives.PutCall(book.maturity, init_instruments, book.instrument_simulator.rate, book.instrument_simulator.volatility, 1) otm = derivatives.PutCall(book.maturity, init_instruments + spread / 2, book.instrument_simulator.rate, book.instrument_simulator.volatility, 1) book.add_derivative(itm, 0, 1) book.add_derivative(atm, 0, -2) book.add_derivative(otm, 0, 1) # ============================================================================== # === train gradient models warmup_train_size = int(sys.argv[1])
import random_books import utils import hedge_models import derivatives # ============================================================================== # === hyperparameters train_size, test_size, timesteps = int(2**10), int(2**20), 30 alpha = 0.95 # ============================================================================== # === sample data init_instruments, init_numeraire, book = random_books.random_empty_book( 1.0, 1, 0.05, 0.1, 0.2, seed=69) derivative = derivatives.PutCall(book.maturity, 105.0, book.numeraire_simulator.rate, book.instrument_simulator.volatility, 1) book.add_derivative(derivative, 0, 1.0) driver = utils.HedgeDriver( timesteps=timesteps, frequency=0, # no need for frequency init_instruments=init_instruments, init_numeraire=init_numeraire, book=book, cost=None, risk_neutral=False, learning_rate=1e-1) driver.add_testcase("delta", hedge_models.FeatureHedge(),