| jupytext | kernelspec | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
(bcg_complete_mkts_final)=
<div id="qe-notebook-header" align="right" style="text-align:right;">
<a href="https://quantecon.org/" title="quantecon.org">
<img style="width:250px;display:inline;" width="250px" src="https://assets.quantecon.org/img/qe-menubar-logo.svg" alt="QuantEcon">
</a>
</div>
In addition to what's in Anaconda, this lecture will need the following libraries:
---
tags: [hide-output]
---
!pip install --upgrade quantecon
!conda install -y -c plotly plotly plotly-orca
This is a prolegomenon to another lecture {doc}Equilibrium Capital Structures with Incomplete Markets <BCG_incomplete_mkts> about a model with
incomplete markets authored by Bisin, Clementi, and Gottardi {cite}BCG_2018.
We adopt specifications of preferences and technologies very close to Bisin, Clemente, and Gottardi’s but unlike them assume that there are complete markets in one-period Arrow securities.
This simplification of BCG’s setup helps us by
- creating a benchmark economy to compare with outcomes in BCG’s incomplete markets economy
- creating a good guess for initial values of some equilibrium objects to be computed in BCG’s incomplete markets economy via an iterative algorithm
- illustrating classic complete markets outcomes that include
- indeterminacy of consumers’ portfolio choices
- indeterminacy of firms' financial structures that underlies a
Modigliani-Miller theorem {cite}
Modigliani_Miller_1958
- introducing
Big K, little kissues in a simple context that will recur in the BCG incomplete markets environment
A Big K, little k analysis also played roles in this quantecon lecture as well as
here and {doc}here <dyn_stack>.
The economy lasts for two periods,
There are two types of consumers named
A scalar random variable
- the return in period
$1$ from investing$k \geq 0$ in physical capital in period$0$ . - exogenous period
$1$ endowments of the consumption good for agents of types$i =1$ and$i=2$ .
Type
We discuss two arrangements:
- a command economy in which a benevolent planner chooses
$k$ and allocates goods to the two types of consumers in each period and each random second period state - a competitive equilibrium with markets in claims on physical capital
and a complete set (possibly a continuum) of one-period Arrow
securities that pay period
$1$ consumption goods contingent on the realization of random variable$\epsilon$ .
There is a single consumption good in period
Economy-wide endowments in periods
Soon we’ll explain how aggregate endowments are divided between
type
We don’t need to do that in order to describe a social planning problem.
Where
A consumer of type
Following BCG, we shall employ the following parameterizations:
Sometimes instead of asuming
The planner’s objective function is
where
We form the following Lagrangian for the planner’s problem:
First-order necessary optimality conditions for the planning problem are:
The first four equations imply that
These together with the fifth first-order condition for the planner imply the following equation that determines an optimal choice of capital
for
Evidently,
and
where it is to be understood that this equation holds for
With the same understanding, it follows that
Let
It follows from the preceding equation that
where
Consequently, we can write the planner’s first-order condition for
which is one equation to be solved for
Anticipating a Big K, little k idea widely used in macroeconomics,
to be discussed in detail below, let
:label: focke
1 = \beta \alpha A K^{\alpha -1} \int \left( \frac{w_1(\epsilon) + A K^\alpha e^\epsilon}
{w_0 - K } \right)^{-\gamma} g(\epsilon) e^\epsilon d \epsilon
The associated optimal consumption allocation is
where
The relative Pareto weight parameter focke that determines
Neither does it influence
The role of
Thus, the planner’s choice of
We now describe a competitive equilibrium for an economy that has specifications of consumer preferences, technology, and aggregate endowments that are identical to those in the preceding planning problem.
While prices do not appear in the planning problem – only quantities do – prices play an important role in a competitive equilibrium.
To understand how the planning economy is related to a competitive
equilibrium, we now turn to the Big K, little k distinction.
We follow BCG in assuming that there are unit measures of
- consumers of type
$i=1$ - consumers of type
$i=2$ - firms with access to the production technology that converts
$k$ units of time$0$ good into$A k^\alpha e^\epsilon$ units of the time$1$ good in random state$\epsilon$
Thus, let
Then define Big
In the same spirit, let
The assumption that there are continua of our three types of agents plays an important role making each individual agent into a powerless price taker:
- an individual consumer chooses its own (infinesimal) part
$c^i(\omega)$ of$C^i$ taking prices as given - an individual firm chooses its own (infinitesmimal) part
$k(\zeta)$ of$K$ taking prices as - equilibrium prices depend on the
Big K, Big Cobjects$K$ and$C$
Nevertheless, in equilibrium,
The assumption about measures of agents is thus a powerful device for making a host of competitive agents take as given equilibrium prices that are determined by the independent decisions of hosts of agents who behave just like they do.
Consumers of type
where
Consumers also own shares in a firm that operates the technology for converting
nonnegative amounts of the time
Consumers of types
At time
- equities (also known as stocks) issued by firms
- one-period Arrow securities that pay one unit of consumption at time
$1$ when the shock$\epsilon$ assumes a particular value
Later, we’ll allow the firm to issue bonds too, but not now.
Let
-
$a^i(\epsilon)$ be consumer$i$ ’s purchases of claims on time$1$ consumption in state$\epsilon$ -
$q(\epsilon)$ be a pricing kernel for one-period Arrow securities -
$\theta_0^i \geq 0$ be consumer$i$ 's intial share of the firm,$\sum_i \theta_0^i =1$ -
$\theta^i$ be the fraction of a firm’s shares purchased by consumer$i$ at time$t=0$ -
$V$ be the value of the representative firm -
$\tilde V$ be the value of equity issued by the representative firm -
$K, C_0$ be two scalars and$C_1(\epsilon)$ a function that we use to construct a guess about an equilibrium pricing kernel for Arrow securities
We proceed to describe constrained optimum problems faced by consumers and a representative firm in a competitive equilibrium.
A representative firm takes Arrow security prices
The firm purchases capital
The firm produces time earnings to owners of its
equity.
The value of a firm's equity at time
Owners of a firm want it to choose
The firm's first-order necessary condition for an optimal
$$
- 1 + \alpha A k^{\alpha -1} \int e^\epsilon q(\epsilon) d \epsilon = 0 $$
The time
The right side equals the value of equity minus the cost of the time
We now pose a consumer’s problem in a competitive equilibrium.
As a price taker, each consumer faces a given Arrow securities pricing kernel
If we evaluate consumer
:label: debtlimit
-\bar a^i(\epsilon;\theta^i) = w_1^i(\epsilon) +\theta^i A k^\alpha e^\epsilon
The quantity
Notice that debtlimit depends on
- his endowment
$w_1^i(\epsilon)$ at time$1$ in state$\epsilon$ - his share
$\theta^i$ of a representive firm's dividends
These constitute two sources of collateral that back the consumer's issues of Arrow securities that pay off in state
Consumer
subject to time
Attach Lagrange multiplier
Off corners, first-order necessary conditions for an optimum with respect to
These equations imply that consumer
:label: qgeqn
q(\epsilon) = \beta \left( \frac{u'(c_1^i(\epsilon))}{u'(c_0^i)} \right) g(\epsilon)
To deduce a restriction on equilibrium prices, we
solve the period
then substitute the expression on the right side into the time
:label: noarb
w_0^i + \theta_0^i V + \int w_1^i(\epsilon) q(\epsilon) d \epsilon + \theta^i \left[ A k^\alpha \int e^\epsilon q(\epsilon) d \epsilon - \tilde V \right]
\geq c_0^i + \int c_1^i(\epsilon) q(\epsilon) d \epsilon
The right side of inequality {eq}noarb is the present value
of consumer
From inequality {eq}noarb, we deduce two
findings.
1. No arbitrage profits condition:
Unless
:label: tilde
\tilde V = A k^\alpha \int e^\epsilon q (\epsilon) d \epsilon
an arbitrage opportunity would be open.
If
the consumer could afford an arbitrarily high present value of consumption by setting
If
the consumer could afford an arbitrarily high present value of
consumption by setting
Since resources are finite, there can exist no such arbitrage opportunity in a competitive equilibrium.
Therefore, it must be true that the following no arbitrage condition prevails:
:label: tildeV20
\tilde V = \int A k^\alpha e^\epsilon q(\epsilon;K) d \epsilon
Equation {eq}tildeV20 asserts that the value of equity
equals the value of the state-contingent dividends
We'll say more about this equation later.
2. Indeterminacy of portfolio
When the no-arbitrage pricing equation {eq}tildeV20
prevails, a consumer of type
Consumer of type
Having computed an allocation that solves the planning problem, we can
readily compute a competitive equilibrium via the following steps that,
as we’ll see, relies heavily on the Big K, little k,
Big C, little c logic mentioned earlier:
- a competitive equilbrium allocation equals the allocation chosen by the planner
- competitive equilibrium prices and the value of a firm’s equity are encoded in shadow prices from the planning problem that
depend on Big
$K$ and Big$C$ .
To substantiate that this procedure is valid, we proceed as follows.
With
:label: arrowprices
q(\epsilon;K) = \beta \left( \frac{u'\left( w_1(\epsilon) + A K^\alpha e^\epsilon\right)} {u'(w_0 - K )} \right)^{-\gamma}
To confirm the guess, we begin by considering its consequences for the firm’s choice of
With Arrow securities prices {eq}arrowprices, the firm’s
first-order necessary condition for choosing
:label: kK
-1 + \alpha A k^{\alpha -1} \int e^\epsilon q(\epsilon;K) d \epsilon = 0
which can be verified to be satisfied if the firm sets
because by setting kK becomes
equivalent with the planner’s first-order condition
{eq}focke for setting
To pose a consumer’s problem in a competitive equilibrium, we require
not only the above guess for the Arrow securities pricing kernel
:label: tildeV2
\tilde V = \int A K^\alpha e^\epsilon q(\epsilon;K) d \epsilon
Let arrowprices and formula
{eq}tildeV2.
At the Arrow securities prices arrowprices
and equity value tildeV2,
consumer
It can be verified directly that the following choices satisfy these equations
for an
Remark: Multiple arrangements of endowments
Think about the portfolio indeterminacy finding above.
We now allow a firm to issue both bonds and equity.
Payouts from equity and bonds, respectively, are
Thus, one unit of the bond pays one unit of consumption at time
The value of the firm is now the sum of equity plus the value of bonds, which we denote
where
We continue to assume that there are complete markets in Arrow
securities with pricing kernel
A version of the no-arbitrage-in-equilibrium argument that we presented earlier implies that the value of equity and the price of bonds are
Consequently, the value of the firm is
which is the same expression that we obtained above when we assumed that the firm issued only equity.
We thus obtain a version of the celebrated Modigliani-Miller theorem {cite}Modigliani_Miller_1958
about firms’ finance:
Modigliani-Miller theorem:
- The value of a firm is independent the mix of equity and bonds that it uses to finance its physical capital.
- The firms’s decision about how much physical capital to purchase does not depend on whether it finances those purchases by issuing bonds or equity
- The firm’s choice of whether to finance itself by issuing equity or bonds is indeterminant
Please note the role of the assumption of complete markets in Arrow securities in substantiating these claims.
In {doc}Equilibrium Capital Structures with Incomplete Markets <BCG_incomplete_mkts>, we will assume that markets are (very)
incomplete – we’ll shut down markets in almost all Arrow securities.
That will pull the rug from underneath the Modigliani-Miller theorem.
We create a class object BCG_complete_markets to compute
equilibrium allocations of the complete market BCG model given a list
of parameter values.
It consists of 4 functions that do the following things:
-
opt_kcomputes the planner's optimal capital$K$ -
First, create a grid for capital.
-
Then for each value of capital stock in the grid, compute the left side of the planner's first-order necessary condition for
$k$ , that is,$$ \beta \alpha A K^{\alpha -1} \int \left( \frac{w_1(\epsilon) + A K^\alpha e^\epsilon}{w_0 - K } \right)^{-\gamma} e^\epsilon g(\epsilon) d \epsilon - 1 =0 $$
-
Find
$k$ that solves this equation.
-
-
qcomputes Arrow security prices as a function of the productivity shock$\epsilon$ and capital$K$ :$$ q(\epsilon;K) = \beta \left( \frac{u'\left( w_1(\epsilon) + A K^\alpha e^\epsilon\right)} {u'(w_0 - K )} \right) $$
-
Vsolves for the firm value given capital$k$ :$$ V = - k + \int A k^\alpha e^\epsilon q(\epsilon; K) d \epsilon $$
-
opt_ccomputes optimal consumptions$c^i_0$ , and$c^i(\epsilon)$ :-
The function first computes weight
$\eta$ using the budget constraint for agent 1:$$ w_0^1 + \theta_0^1 V + \int w_1^1(\epsilon) q(\epsilon) d \epsilon = c_0^1 + \int c_1^1(\epsilon) q(\epsilon) d \epsilon = \eta \left( C_0 + \int C_1(\epsilon) q(\epsilon) d \epsilon \right) $$ where
$$ \begin{aligned} C_0 & = w_0 - K \cr C_1(\epsilon) & = w_1(\epsilon) + A K^\alpha e^\epsilon \cr \end{aligned} $$
-
It computes consumption for each agent as
$$ \begin{aligned} c_0^1 & = \eta C_0 \cr c_0^2 & = (1 - \eta) C_0 \cr c_1^1(\epsilon) & = \eta C_1 (\epsilon) \cr c_1^2 (\epsilon) & = (1 - \eta) C_1(\epsilon) \end{aligned} $$
-
The list of parameters includes:
-
$\chi_1$ ,$\chi_2$ : Correlation parameters for agents 1 and 2. Default values are 0 and 0.9, respectively. -
$w^1_0$ ,$w^2_0$ : Initial endowments. Default values are 1. -
$\theta^1_0$ ,$\theta^2_0$ : Consumers’ initial shares of a representative firm. Default values are 0.5. -
$\psi$ : CRRA risk parameter. Default value is 3. -
$\alpha$ : Returns to scale production function parameter. Default value is 0.6. -
$A$ : Productivity of technology. Default value is 2.5. -
$\mu$ ,$\sigma$ : Mean and standard deviation of the log of the shock. Default values are -0.025 and 0.4, respectively. -
$\beta$ : time preference discount factor. Default value is .96. -
nb_points_integ: number of points used for integration through Gauss-Hermite quadrature: default value is 10
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
from numba import njit, prange
from quantecon.optimize import root_finding
#=========== Class: BCG for complete markets ===========#
class BCG_complete_markets:
# init method or constructor
def __init__(self,
𝜒1 = 0,
𝜒2 = 0.9,
w10 = 1,
w20 = 1,
𝜃10 = 0.5,
𝜃20 = 0.5,
𝜓 = 3,
𝛼 = 0.6,
A = 2.5,
𝜇 = -0.025,
𝜎 = 0.4,
𝛽 = 0.96,
nb_points_integ = 10):
#=========== Setup ===========#
# Risk parameters
self.𝜒1 = 𝜒1
self.𝜒2 = 𝜒2
# Other parameters
self.𝜓 = 𝜓
self.𝛼 = 𝛼
self.A = A
self.𝜇 = 𝜇
self.𝜎 = 𝜎
self.𝛽 = 𝛽
# Utility
self.u = lambda c: (c**(1-𝜓)) / (1-𝜓)
# Production
self.f = njit(lambda k: A * (k ** 𝛼))
self.Y = lambda 𝜖, k: np.exp(𝜖) * self.f(k)
# Initial endowments
self.w10 = w10
self.w20 = w20
self.w0 = w10 + w20
# Initial holdings
self.𝜃10 = 𝜃10
self.𝜃20 = 𝜃20
# Endowments at t=1
w11 = njit(lambda 𝜖: np.exp(-𝜒1*𝜇 - 0.5*(𝜒1**2)*(𝜎**2) + 𝜒1*𝜖))
w21 = njit(lambda 𝜖: np.exp(-𝜒2*𝜇 - 0.5*(𝜒2**2)*(𝜎**2) + 𝜒2*𝜖))
self.w11 = w11
self.w21 = w21
self.w1 = njit(lambda 𝜖: w11(𝜖) + w21(𝜖))
# Normal PDF
self.g = lambda x: norm.pdf(x, loc=𝜇, scale=𝜎)
# Integration
x, self.weights = np.polynomial.hermite.hermgauss(nb_points_integ)
self.points_integral = np.sqrt(2) * 𝜎 * x + 𝜇
self.k_foc = k_foc_factory(self)
#=========== Optimal k ===========#
# Function: solve for optimal k
def opt_k(self, plot=False):
w0 = self.w0
# Grid for k
kgrid = np.linspace(1e-4, w0-1e-4, 100)
# get FONC values for each k in the grid
kfoc_list = [];
for k in kgrid:
kfoc = self.k_foc(k, self.𝜒1, self.𝜒2)
kfoc_list.append(kfoc)
# Plot FONC for k
if plot:
fig, ax = plt.subplots(figsize=(8,7))
ax.plot(kgrid, kfoc_list, color='blue', label=r'FONC for k')
ax.axhline(0, color='red', linestyle='--')
ax.legend()
ax.set_xlabel(r'k')
plt.show()
# Find k that solves the FONC
kk = root_finding.newton_secant(self.k_foc, 1e-2, args=(self.𝜒1, self.𝜒2)).root
return kk
#=========== Arrow security price ===========#
# Function: Compute Arrow security price
def q(self,𝜖,k):
𝛽 = self.𝛽
𝜓 = self.𝜓
w0 = self.w0
w1 = self.w1
fk = self.f(k)
g = self.g
return 𝛽 * ((w1(𝜖) + np.exp(𝜖)*fk) / (w0 - k))**(-𝜓)
#=========== Firm value V ===========#
# Function: compute firm value V
def V(self, k):
q = self.q
fk = self.f(k)
weights = self.weights
integ = lambda 𝜖: np.exp(𝜖) * fk * q(𝜖, k)
return -k + np.sum(weights * integ(self.points_integral)) / np.sqrt(np.pi)
#=========== Optimal c ===========#
# Function: Compute optimal consumption choices c
def opt_c(self, k=None, plot=False):
w1 = self.w1
w0 = self.w0
w10 = self.w10
w11 = self.w11
𝜃10 = self.𝜃10
Y = self.Y
q = self.q
V = self.V
weights = self.weights
if k is None:
k = self.opt_k()
# Solve for the ratio of consumption 𝜂 from the intertemporal B.C.
fk = self.f(k)
c1 = lambda 𝜖: (w1(𝜖) + np.exp(𝜖)*fk)*q(𝜖,k)
denom = np.sum(weights * c1(self.points_integral)) / np.sqrt(np.pi) + (w0 - k)
w11q = lambda 𝜖: w11(𝜖)*q(𝜖,k)
num = w10 + 𝜃10 * V(k) + np.sum(weights * w11q(self.points_integral)) / np.sqrt(np.pi)
𝜂 = num / denom
# Consumption choices
c10 = 𝜂 * (w0 - k)
c20 = (1-𝜂) * (w0 - k)
c11 = lambda 𝜖: 𝜂 * (w1(𝜖)+Y(𝜖,k))
c21 = lambda 𝜖: (1-𝜂) * (w1(𝜖)+Y(𝜖,k))
return c10, c20, c11, c21
def k_foc_factory(model):
𝜓 = model.𝜓
f = model.f
𝛽 = model.𝛽
𝛼 = model.𝛼
A = model.A
𝜓 = model.𝜓
w0 = model.w0
𝜇 = model.𝜇
𝜎 = model.𝜎
weights = model.weights
points_integral = model.points_integral
w11 = njit(lambda 𝜖, 𝜒1, : np.exp(-𝜒1*𝜇 - 0.5*(𝜒1**2)*(𝜎**2) + 𝜒1*𝜖))
w21 = njit(lambda 𝜖, 𝜒2: np.exp(-𝜒2*𝜇 - 0.5*(𝜒2**2)*(𝜎**2) + 𝜒2*𝜖))
w1 = njit(lambda 𝜖, 𝜒1, 𝜒2: w11(𝜖, 𝜒1) + w21(𝜖, 𝜒2))
@njit
def integrand(𝜖, 𝜒1, 𝜒2, k=1e-4):
fk = f(k)
return (w1(𝜖, 𝜒1, 𝜒2) + np.exp(𝜖) * fk) ** (-𝜓) * np.exp(𝜖)
@njit
def k_foc(k, 𝜒1, 𝜒2):
int_k = np.sum(weights * integrand(points_integral, 𝜒1, 𝜒2, k=k)) / np.sqrt(np.pi)
mul = 𝛽 * 𝛼 * A * k ** (𝛼 - 1) / ((w0 - k) ** (-𝜓))
val = mul * int_k - 1
return val
return k_foc
Below we provide some examples of how to use BCG_complete markets.
In the first example, we set up instances of BCG complete markets models.
We can use either default parameter values or set parameter values as we want.
The two instances of the BCG complete markets model, mdl1 and
mdl2, represent the model with default parameter settings and with agent 2’s income correlation altered to be
# Example: BCG model for complete markets
mdl1 = BCG_complete_markets()
mdl2 = BCG_complete_markets(𝜒2=-0.9)
Let’s plot the agents’ time-1 endowments with respect to shocks to see the difference in the two models:
#==== Figure 1: HH endowments and firm productivity ====#
# Realizations of innovation from -3 to 3
epsgrid = np.linspace(-1,1,1000)
fig, ax = plt.subplots(1,2,figsize=(14,6))
ax[0].plot(epsgrid, mdl1.w11(epsgrid), color='black', label=r'Agent 1\'s endowment')
ax[0].plot(epsgrid, mdl1.w21(epsgrid), color='blue', label=r'Agent 2\'s endowment')
ax[0].plot(epsgrid, mdl1.Y(epsgrid,1), color='red', label=r'Production with $k=1$')
ax[0].set_xlim([-1,1])
ax[0].set_ylim([0,7])
ax[0].set_xlabel(r'$\epsilon$',fontsize=12)
ax[0].set_title(r'Model with $\chi_1 = 0$, $\chi_2 = 0.9$')
ax[0].legend()
ax[0].grid()
ax[1].plot(epsgrid, mdl2.w11(epsgrid), color='black', label=r'Agent 1\'s endowment')
ax[1].plot(epsgrid, mdl2.w21(epsgrid), color='blue', label=r'Agent 2\'s endowment')
ax[1].plot(epsgrid, mdl2.Y(epsgrid,1), color='red', label=r'Production with $k=1$')
ax[1].set_xlim([-1,1])
ax[1].set_ylim([0,7])
ax[1].set_xlabel(r'$\epsilon$',fontsize=12)
ax[1].set_title(r'Model with $\chi_1 = 0$, $\chi_2 = -0.9$')
ax[1].legend()
ax[1].grid()
plt.show()
Let’s also compare the optimal capital stock,
# Print optimal k
kk_1 = mdl1.opt_k()
kk_2 = mdl2.opt_k()
print('The optimal k for model 1: {:.5f}'.format(kk_1))
print('The optimal k for model 2: {:.5f}'.format(kk_2))
# Print optimal time-0 consumption for agent 2
c20_1 = mdl1.opt_c(k=kk_1)[1]
c20_2 = mdl2.opt_c(k=kk_2)[1]
print('The optimal c20 for model 1: {:.5f}'.format(c20_1))
print('The optimal c20 for model 2: {:.5f}'.format(c20_2))
In the second example, we illustrate how the optimal choice of
We will need to install the plotly package for 3D illustration. See
https://plotly.com/python/getting-started/ for further instructions.
# Mesh grid of 𝜒
N = 30
𝜒1grid, 𝜒2grid = np.meshgrid(np.linspace(-1,1,N),
np.linspace(-1,1,N))
k_foc = k_foc_factory(mdl1)
# Create grid for k
kgrid = np.zeros_like(𝜒1grid)
w0 = mdl1.w0
@njit(parallel=True)
def fill_k_grid(kgrid):
# Loop: Compute optimal k and
for i in prange(N):
for j in prange(N):
X1 = 𝜒1grid[i, j]
X2 = 𝜒2grid[i, j]
k = root_finding.newton_secant(k_foc, 1e-2, args=(X1, X2)).root
kgrid[i, j] = k
%%time
fill_k_grid(kgrid)
%%time
# Second-run
fill_k_grid(kgrid)
#=== Example: Plot optimal k with different correlations ===#
from IPython.display import Image
# Import plotly
import plotly.graph_objs as go
# Plot optimal k
fig = go.Figure(data=[go.Surface(x=𝜒1grid, y=𝜒2grid, z=kgrid)])
fig.update_layout(scene = dict(xaxis_title='x - 𝜒1',
yaxis_title='y - 𝜒2',
zaxis_title='z - k',
aspectratio=dict(x=1,y=1,z=1)))
fig.update_layout(width=500,
height=500,
margin=dict(l=50, r=50, b=65, t=90))
fig.update_layout(scene_camera=dict(eye=dict(x=2, y=-2, z=1.5)))
# Export to PNG file
Image(fig.to_image(format="png"))
# fig.show() will provide interactive plot when running
# notebook locally