Пример #1
0
 def get_price(self, num_dt: int, dx: float, num_dx: int) -> float:
     dt = self.expiry / num_dt
     x_pts = 2 * num_dx + 1
     x_limit = dx * num_dx
     res = np.empty([num_dt + 1, x_pts])
     prices = np.linspace(-x_limit, x_limit, x_pts) + self.spot_price
     res[-1, :] = [max(self.payoff(self.expiry, p), 0.) for p in prices]
     for i in range(num_dt - 1, -1, -1):
         t = i * dt
         knots, coeffs, order = splrep(prices, res[i + 1, :], k=3)
         spline_func = BSpline(knots, coeffs, order)
         disc = np.exp(self.ir(t) - self.ir(t + dt))
         for j in range(x_pts):
             m, v = get_future_price_mean_var(prices[j], t, dt, self.ir,
                                              self.dispersion)
             stdev = np.sqrt(v)
             norm_dist = norm(loc=m, scale=stdev)
             sample_points = 201
             integr_func = lambda x: max(spline_func(x), 0.
                                         ) * norm_dist.pdf(x)
             low, high = (m - 4 * stdev, m + 4 * stdev)
             disc_exp_payoff = disc * trapz(
                 np.vectorize(integr_func)(np.linspace(
                     low, high, sample_points)),
                 dx=(high - low) /
                 (sample_points - 1)) / (norm_dist.cdf(high) -
                                         norm_dist.cdf(low))
             res[i, j] = max(self.payoff(t, prices[j]), disc_exp_payoff)
     return res[0, num_dx]
Пример #2
0
    def get_fqi_price(self, num_dt: int, num_paths: int,
                      feature_funcs: Sequence[Callable[[int, np.ndarray],
                                                       float]],
                      batch_size: int, model_prob_draws: int) -> float:
        features = len(feature_funcs)
        a_mat = np.zeros((features, features))
        b_vec = np.zeros(features)
        params = np.zeros(features)
        paths = self.get_all_paths(num_paths, num_dt + 1)
        dt = self.expiry / num_dt

        for path_num, path in enumerate(paths):

            for step in range(num_dt):
                t = step * dt
                disc = np.exp(self.ir(t) - self.ir(t + dt))
                phi_s = np.array(
                    [f(step, path[:(step + 1)]) for f in feature_funcs])
                if model_prob_draws > 1:
                    m, v = get_future_price_mean_var(path[step], t, dt,
                                                     self.lognormal, self.ir,
                                                     self.isig)
                    norm_draws = np.random.normal(m, np.sqrt(v),
                                                  model_prob_draws)
                    local_paths = [
                        np.append(paths[:(step + 1)], nd) for nd in
                        (np.exp(norm_draws) if self.lognormal else norm_draws)
                    ]
                else:
                    local_paths = [path[:(step + 2)]]

                all_max_val = np.zeros(len(local_paths))
                for i, local_path in enumerate(local_paths):
                    next_payoff = self.payoff(t + dt, local_path)
                    if step == num_dt - 1:
                        next_phi = np.zeros(features)
                    else:
                        next_phi = np.array(
                            [f(step + 1, local_path) for f in feature_funcs])
                    all_max_val[i] = max(next_payoff, params.dot(next_phi))

                max_val = np.mean(all_max_val)

                a_mat += np.outer(phi_s, phi_s)
                b_vec += phi_s * disc * max_val

            if (path_num + 1) % batch_size == 0:
                params = np.linalg.inv(a_mat).dot(b_vec)
                # print(params)
                a_mat = np.zeros((features, features))
                b_vec = np.zeros(features)

        return self.get_price_from_paths_and_params(paths, params, num_dt,
                                                    feature_funcs)
Пример #3
0
 def get_all_paths(self, num_paths: int, num_dt: int) -> np.ndarray:
     dt = self.expiry / num_dt
     paths = np.empty([num_paths, num_dt + 1])
     paths[:, 0] = self.spot_price
     for i in range(num_paths):
         price = self.spot_price
         for t in range(num_dt):
             m, v = get_future_price_mean_var(price, t, dt, self.ir,
                                              self.dispersion)
             price = np.random.normal(m, np.sqrt(v))
             paths[i, t + 1] = price
     return paths
Пример #4
0
    def get_price(
        self, num_dt: int, num_paths: int,
        feature_funcs: Sequence[Callable[[float, np.ndarray],
                                         float]]) -> float:
        dt = self.expiry / num_dt
        paths = np.empty([num_paths, num_dt + 1])
        paths[:, 0] = self.spot_price
        for i in range(num_paths):
            price = self.spot_price
            for t in range(num_dt):
                m, v = get_future_price_mean_var(price, t, dt, self.ir,
                                                 self.dispersion)
                price = np.random.normal(m, np.sqrt(v))
                paths[i, t + 1] = price
        cashflow = np.array([
            max(self.payoff(self.expiry, paths[i, :]), 0.)
            for i in range(num_paths)
        ])
        for t in range(num_dt - 1, 0, -1):
            """
            For each time slice t
            Step 1: collect X as features of (t, [S_0,.., S_t]) for those paths
            for which payoff(t, [S_0, ...., S_t]) > 0, and corresponding Y as
            the time-t discounted future actual cash flow on those paths.
            Step 2: Do the (X,Y) regression. Denote Y^ as regression-prediction.
            Compare Y^ versus payoff(t, [S_0, ..., S_t]). If payoff is higher,
            set cashflow at time t on that path to be the payoff, else set 
            cashflow at time t on that path to be the time-t discounted future
            actual cash flow on that path.
            """
            disc = np.exp(self.ir(t) - self.ir(t + dt))
            cashflow = cashflow * disc
            payoff = np.array(
                [self.payoff(t, paths[i, :(t + 1)]) for i in range(num_paths)])
            indices = [i for i in range(num_paths) if payoff[i] > 0]
            if len(indices) > 0:
                x_vals = np.array(
                    [[f(t, paths[i, :(t + 1)]) for f in feature_funcs]
                     for i in indices])
                y_vals = np.array([cashflow[i] for i in indices])
                estimate = x_vals.dot(
                    np.linalg.lstsq(x_vals, y_vals, rcond=None)[0])
                # plt.scatter([paths[i, t] for i in indices], y_vals, c='r')
                # plt.scatter([paths[i, t] for i in indices], estimate, c='b')
                # plt.show()

                for i, ind in enumerate(indices):
                    if payoff[ind] > estimate[i]:
                        cashflow[ind] = payoff[ind]

        return max(self.payoff(0, np.array([self.spot_price])),
                   np.average(cashflow * np.exp(-self.ir(dt))))
Пример #5
0
 def state_reward_gen(self, state: StateType, action: ActionType,
                      num_dt: int) -> Tuple[StateType, float]:
     ind, price_arr = state
     delta_t = self.expiry / num_dt
     t = ind * delta_t
     reward = (np.exp(-self.ir(t)) * self.payoff(t, price_arr)) if\
         (action and ind <= num_dt) else 0.
     m, v = get_future_price_mean_var(price_arr[-1], t, delta_t, self.ir,
                                      self.dispersion)
     next_price = np.random.normal(m, np.sqrt(v))
     price1 = np.append(price_arr, next_price)
     next_ind = (num_dt if action else ind) + 1
     return (next_ind, price1), reward
Пример #6
0
 def get_all_paths(self, spot_pct_noise: float, num_paths: int,
                   num_dt: int) -> np.ndarray:
     dt = self.expiry / num_dt
     paths = np.empty([num_paths, num_dt + 1])
     spot = self.spot_price
     for i in range(num_paths):
         start = max(0.001, np.random.normal(spot, spot * spot_pct_noise))
         paths[i, 0] = start
         for t in range(num_dt):
             m, v = get_future_price_mean_var(paths[i, t], t, dt,
                                              self.lognormal, self.ir,
                                              self.isig)
             norm_draw = np.random.normal(m, np.sqrt(v))
             paths[i, t +
                   1] = np.exp(norm_draw) if self.lognormal else norm_draw
     return paths
Пример #7
0
    def get_price(self, num_dt: int, num_dx: int, center: float,
                  width: float) -> float:
        """
        :param num_dt: represents number of discrete time steps
        :param num_dx: represents number of discrete state-space steps
        (on each side of the center)
        :param center: represents the center of the state space grid. For
        the case of lognormal == True, it should be Mean[log(x_{expiry}].
        For the case of lognormal == False, it should be Mean[x_{expiry}].
        :param width: represents the width of the state space grid. For the
        case of lognormal == True, it should be a multiple of
        Stdev[log(x_{expiry})]. For the case of lognormal == True, it
        should be a multiple of Stdev[log(x_{expiry})].
        :return: the price of the American option (this is the discounted
        expected payoff at time 0 at current stock price.
        """
        dt = self.expiry / num_dt
        x_pts = 2 * num_dx + 1
        lsp = np.linspace(center - width, center + width, x_pts)
        prices = np.exp(lsp) if self.lognormal else lsp
        res = np.empty([num_dt, x_pts])
        res[-1, :] = [max(self.payoff(self.expiry, p), 0.) for p in prices]
        sample_points = 201
        for i in range(num_dt - 2, -1, -1):
            t = (i + 1) * dt
            knots, coeffs, order = splrep(prices, res[i + 1, :], k=3)
            spline_func = BSpline(knots, coeffs, order)
            disc = np.exp(self.ir(t) - self.ir(t + dt))
            for j in range(x_pts):
                m, v = get_future_price_mean_var(prices[j], t, dt,
                                                 self.lognormal, self.ir,
                                                 self.isig)
                stdev = np.sqrt(v)
                norm_dist = norm(loc=m, scale=stdev)

                # noinspection PyShadowingNames
                def integr_func(x: float,
                                spline_func=spline_func,
                                norm_dist=norm_dist) -> float:
                    val = np.exp(x) if self.lognormal else x
                    return max(spline_func(val), 0.) * norm_dist.pdf(x)

                low, high = (m - 4 * stdev, m + 4 * stdev)
                disc_exp_payoff = disc * trapz(
                    np.vectorize(integr_func)(np.linspace(
                        low, high, sample_points)),
                    dx=(high - low) /
                    (sample_points - 1)) / (norm_dist.cdf(high) -
                                            norm_dist.cdf(low))
                res[i, j] = max(self.payoff(t, prices[j]), disc_exp_payoff)

        knots, coeffs, order = splrep(prices, res[0, :], k=3)
        spline_func = BSpline(knots, coeffs, order)
        disc = np.exp(-self.ir(dt))
        m, v = get_future_price_mean_var(self.spot_price, 0., dt,
                                         self.lognormal, self.ir, self.isig)
        stdev = np.sqrt(v)
        norm_dist = norm(loc=m, scale=stdev)

        # noinspection PyShadowingNames
        def integr_func0(x: float,
                         spline_func=spline_func,
                         norm_dist=norm_dist) -> float:
            val = np.exp(x) if self.lognormal else x
            return max(spline_func(val), 0.) * norm_dist.pdf(x)

        low, high = (m - 4 * stdev, m + 4 * stdev)
        disc_exp_payoff = disc * trapz(
            np.vectorize(integr_func0)(np.linspace(low, high, sample_points)),
            dx=(high - low) /
            (sample_points - 1)) / (norm_dist.cdf(high) - norm_dist.cdf(low))
        return max(self.payoff(0., self.spot_price), disc_exp_payoff)
Пример #8
0
    from examples.american_pricing.bs_pricing import EuropeanBSPricing
    ebsp = EuropeanBSPricing(is_call=False,
                             spot_price=spot_price_val,
                             strike=strike_val,
                             expiry=expiry_val,
                             r=rr,
                             sigma=sigma_val)
    print(ebsp.option_price)
    # noinspection PyShadowingNames
    ir_func = lambda t, rr=rr: rr * t
    # noinspection PyShadowingNames
    isig_func = lambda t, sigma_val=sigma_val: sigma_val * sigma_val * t

    gp = GridPricing(
        spot_price=spot_price_val,
        payoff=payoff_func,
        expiry=expiry_val,
        lognormal=lognormal_val,
        ir=ir_func,
        isig=isig_func,
    )
    num_dt_val = 10
    num_dx_val = 100
    expiry_mean, expiry_var = get_future_price_mean_var(
        spot_price_val, 0., expiry_val, lognormal_val, ir_func, isig_func)
    print(
        gp.get_price(num_dt=num_dt_val,
                     num_dx=num_dx_val,
                     center=expiry_mean,
                     width=np.sqrt(expiry_var) * 4.))
Пример #9
0
def get_vanilla_american_price(
        is_call: bool, spot_price: float, strike: float, expiry: float,
        lognormal: bool, r: float, sigma: float, num_dt: int, num_paths: int,
        num_laguerre: int, params_bag: Mapping[str,
                                               Any]) -> Mapping[str, float]:
    opt_payoff = lambda _, x, is_call=is_call, strike=strike:\
        max(x - strike, 0.) if is_call else max(strike - x, 0.)
    # noinspection PyShadowingNames
    ir_func = lambda t, r=r: r * t
    isig_func = lambda t, sigma=sigma: sigma * sigma * t

    num_dx = 200
    expiry_mean, expiry_var = get_future_price_mean_var(
        spot_price, 0., expiry, lognormal, ir_func, isig_func)
    grid_price = GridPricing(spot_price=spot_price,
                             payoff=opt_payoff,
                             expiry=expiry,
                             lognormal=lognormal,
                             ir=ir_func,
                             isig=isig_func).get_price(
                                 num_dt=num_dt,
                                 num_dx=num_dx,
                                 center=expiry_mean,
                                 width=np.sqrt(expiry_var) * 4)

    gp = AmericanPricing(
        spot_price=spot_price,
        payoff=(lambda t, x, opt_payoff=opt_payoff: opt_payoff(t, x[-1])),
        expiry=expiry,
        lognormal=lognormal,
        ir=ir_func,
        isig=isig_func)
    ident = np.eye(num_laguerre)

    # noinspection PyShadowingNames
    def laguerre_feature_func(x: float,
                              i: int,
                              ident=ident,
                              strike=strike) -> float:
        # noinspection PyTypeChecker
        xp = x / strike
        return np.exp(-xp / 2) * lagval(xp, ident[i])

    ls_price = gp.get_ls_price(
        num_dt=num_dt,
        num_paths=num_paths,
        feature_funcs=[lambda _, x: 1.] +
        [(lambda _, x, i=i: laguerre_feature_func(x[-1], i))
         for i in range(num_laguerre)])

    # noinspection PyShadowingNames
    def rl_feature_func(ind: int,
                        x: float,
                        a: bool,
                        i: int,
                        num_laguerre: int = num_laguerre,
                        num_dt: int = num_dt,
                        expiry: float = expiry) -> float:
        dt = expiry / num_dt
        t = ind * dt
        if i < num_laguerre + 4:
            if ind < num_dt and not a:
                if i == 0:
                    ret = 1.
                elif i < num_laguerre + 1:
                    ret = laguerre_feature_func(x, i - 1)
                elif i == num_laguerre + 1:
                    if t >= expiry_val:
                        ret = 0.
                    else:
                        ret = np.sin(-t * np.pi / (2. * expiry) + np.pi / 2.)
                elif i == num_laguerre + 2:
                    if t >= expiry_val:
                        ret = -LARGENUM
                    else:
                        ret = np.log(expiry - t)
                else:
                    if t >= expiry_val:
                        ret = 1.
                    else:
                        rat = t / expiry
                        ret = rat * rat
            else:
                ret = 0.
        else:
            if ind <= num_dt and a:
                ret = np.exp(-r * (ind * dt)) * opt_payoff(ind * dt, x)
            else:
                ret = 0.

        return ret

    rl_price = gp.get_rl_fa_price(
        num_dt=num_dt,
        method=params_bag["method"],
        exploring_start=params_bag["exploring_start"],
        algorithm=params_bag["algorithm"],
        softmax=params_bag["softmax"],
        epsilon=params_bag["epsilon"],
        epsilon_half_life=params_bag["epsilon_half_life"],
        lambd=params_bag["lambda"],
        num_paths=num_paths,
        batch_size=params_bag["batch_size"],
        feature_funcs=[
            (lambda x, i=i: rl_feature_func(x[0][0], x[0][1][-1], x[1], i))
            for i in range(num_laguerre + 5)
        ],
        neurons=params_bag["neurons"],
        learning_rate=params_bag["learning_rate"],
        learning_rate_decay=params_bag["learning_rate_decay"],
        adam=params_bag["adam"],
        offline=params_bag["offline"])

    return {"Grid": grid_price, "LS": ls_price, "RL": rl_price}
Пример #10
0
    def get_price(
        self,
        num_dt: int,
        num_dx: int,
        center: float,
        width: float
    ) -> float:
        """
        :param num_dt: represents number of discrete time steps
        :param num_dx: represents number of discrete state-space steps
        (on each side of the center)
        :param center: represents the center of the state space grid. For
        the case of lognormal == True, it should be Mean[log(x_{expiry}].
        For the case of lognormal == False, it should be Mean[x_{expiry}].
        :param width: represents the width of the state space grid. For the
        case of lognormal == True, it should be a multiple of
        Stdev[log(x_{expiry})].
        :return: the price of the American option (this is the discounted
        expected payoff at time 0 at current stock price.
        """
        dt = self.expiry / num_dt
        x_pts = 2 * num_dx + 1
        lsp = np.linspace(center - width, center + width, x_pts)
        prices = np.exp(lsp) if self.lognormal else lsp
        res = np.empty([num_dt, x_pts])
        res[-1, :] = [max(self.payoff(self.expiry, p), 0.) for p in prices]
        sample_points = 201

        final = [(p, max(self.payoff(self.expiry, p), 0.)) for p in prices]
        ex_boundary = [max(p for p, e in final if e > 0)]

        for i in range(num_dt - 2, -1, -1):
            t = (i + 1) * dt
            knots, coeffs, order = splrep(prices, res[i + 1, :], k=3)
            spline_func = BSpline(knots, coeffs, order)
            disc = np.exp(self.ir(t) - self.ir(t + dt))
            stprcs = []
            cp = []
            ep = []
            for j in range(x_pts):
                m, v = get_future_price_mean_var(
                    prices[j],
                    t,
                    dt,
                    self.lognormal,
                    self.ir,
                    self.isig
                )
                stdev = np.sqrt(v)
                norm_dist = norm(loc=m, scale=stdev)

                # noinspection PyShadowingNames
                def integr_func(
                    x: float,
                    spline_func=spline_func,
                    norm_dist=norm_dist
                ) -> float:
                    val = np.exp(x) if self.lognormal else x
                    return max(spline_func(val), 0.) * norm_dist.pdf(x)

                low, high = (m - 4 * stdev, m + 4 * stdev)
                disc_exp_payoff = disc * trapz(
                    np.vectorize(integr_func)(np.linspace(low, high, sample_points)),
                    dx=(high - low) / (sample_points - 1)
                ) / (norm_dist.cdf(high) - norm_dist.cdf(low))
                if prices[j] < 100:
                    stprcs.append(prices[j])
                    cp.append(disc_exp_payoff)
                    ep.append(max(self.payoff(t, prices[j]), 0.))
                res[i, j] = max(self.payoff(t, prices[j]), disc_exp_payoff)

            ex_boundary.append(max(p for p, c, e in zip(stprcs, cp, ep) if e > c))

            # if i == int(num_dt / 10) or i == num_dt - int(num_dt / 10) \
            #         or i == int(num_dt / 2):
            #     # print(list(zip(stprcs, cp, ep)))
            #     plt.title("Grid Time = %.3f" % t)
            #     plt.plot(stprcs, cp, 'r', stprcs, ep, 'b')
            #     plt.show()

        # plt.plot([t * dt for t in range(1, num_dt + 1)], ex_boundary[::-1])
        # plt.title("Grid Boundary")
        # plt.savefig(str(Path.home()) + "/Downloads/GridBoundary.png")

        knots, coeffs, order = splrep(prices, res[0, :], k=3)
        spline_func = BSpline(knots, coeffs, order)
        disc = np.exp(-self.ir(dt))
        m, v = get_future_price_mean_var(
            self.spot_price,
            0.,
            dt,
            self.lognormal,
            self.ir,
            self.isig
        )
        stdev = np.sqrt(v)
        norm_dist = norm(loc=m, scale=stdev)

        # noinspection PyShadowingNames
        def integr_func0(
            x: float,
            spline_func=spline_func,
            norm_dist=norm_dist
        ) -> float:
            val = np.exp(x) if self.lognormal else x
            return max(spline_func(val), 0.) * norm_dist.pdf(x)

        low, high = (m - 4 * stdev, m + 4 * stdev)
        disc_exp_payoff = disc * trapz(
            np.vectorize(integr_func0)(np.linspace(low, high, sample_points)),
            dx=(high - low) / (sample_points - 1)
        ) / (norm_dist.cdf(high) - norm_dist.cdf(low))
        return max(self.payoff(0., self.spot_price), disc_exp_payoff)