Closed chipkent closed 4 weeks ago
If the first block of code is changed to fix a typo and add casts:
""" Setup the risk management example. """
import math
import numpy as np
import numpy.typing as npt
import numba
from datetime import date, datetime, timedelta
from deephaven import time_table, empty_table, merge, dtypes as dht
from deephaven.table import Table
############################################################################################################
# Black-Scholes
#
# Write a Black-Scholes option pricing model in Python using Numba for vectorization.
############################################################################################################
@numba.vectorize(['float64(float64)'])
def norm_cdf(x):
""" Cumulative distribution function for the standard normal distribution """
return (1.0 + math.erf(x / math.sqrt(2.0))) / 2.0
@numba.vectorize(['float64(float64)'])
def norm_pdf(x):
""" Probability density function for the standard normal distribution """
return math.exp(-x**2 / 2.0) / math.sqrt(2.0 * math.pi)
@numba.vectorize(['float64(float64, float64, float64, float64, float64, boolean, boolean)'])
def black_scholes_price(s, k, r, t, vol, is_call, is_stock):
""" Calculates the Black-Scholes option price for a call/put """
if is_stock:
return s
d1 = (np.log(s / k) + (r + vol ** 2 / 2) * t) / (vol * np.sqrt(t))
d2 = d1 - vol * np.sqrt(t)
if is_call:
return s * norm_cdf(d1) - k * np.exp(-r * t) * norm_cdf(d2)
else:
return k * np.exp(-r * t) * norm_cdf(-d2) - s * norm_cdf(-d1)
@numba.vectorize(['float64(float64, float64, float64, float64, float64, boolean, boolean)'])
def black_scholes_delta(s, k, r, t, vol, is_call, is_stock):
""" Calculates the Black-Scholes option delta for a call/put """
if is_stock:
return 1.0
d1 = (np.log(s / k) + (r + vol ** 2 / 2) * t) / (vol * np.sqrt(t))
# d2 = d1 - vol * np.sqrt(T)
if is_call:
return norm_cdf(d1)
else:
return -norm_cdf(-d1)
@numba.vectorize(['float64(float64, float64, float64, float64, float64, boolean)'])
def black_scholes_gamma(s, k, r, t, vol, is_stock):
""" Calculates the Black-Scholes option gamma """
if is_stock:
return 0.0
d1 = (np.log(s / k) + (r + vol ** 2 / 2) * t) / (vol * np.sqrt(t))
# d2 = d1 - vol * np.sqrt(T)
return norm_pdf(d1) / (s * vol * np.sqrt(t))
@numba.vectorize(['float64(float64, float64, float64, float64, float64, boolean, boolean)'])
def black_scholes_theta(s, k, r, t, vol, is_call, is_stock):
""" Calculates the Black-Scholes option theta """
if is_stock:
return 0.0
d1 = (np.log(s / k) + (r + vol ** 2 / 2) * t) / (vol * np.sqrt(t))
d2 = d1 - vol * np.sqrt(t)
if is_call:
return - ((s * norm_pdf(d1) * vol) / (2 * np.sqrt(t))) - r * k * np.exp(-r * t) * norm_cdf(d2)
else:
return - ((s * norm_pdf(d1) * vol) / (2 * np.sqrt(t))) + r * k * np.exp(-r * t) * norm_cdf(-d2)
@numba.vectorize(['float64(float64, float64, float64, float64, float64, boolean)'])
def black_scholes_vega(s, k, r, t, vol, is_stock):
""" Calculates the Black-Scholes option vega """
if is_stock:
return 0.0
d1 = (np.log(s / k) + (r + vol ** 2 / 2) * t) / (vol * np.sqrt(t))
return s * np.sqrt(t) * norm_pdf(d1)
@numba.vectorize(['float64(float64, float64, float64, float64, float64, boolean, boolean)'])
def black_scholes_rho(s, k, r, t, vol, is_call, is_stock):
""" Calculates the Black-Scholes option rho """
if is_stock:
return 0.0
d1 = (np.log(s / k) + (r + vol ** 2 / 2) * t) / (vol * np.sqrt(t))
d2 = d1 - vol * np.sqrt(t)
if is_call:
return 0.01 * k * t * np.exp(-r * t) * norm_cdf(d2)
else:
return 0.01 * -k * t * np.exp(-r * t) * norm_cdf(-d2)
############################################################################################################
# Simulate market and trading data
############################################################################################################
# noinspection PyUnusedLocal
def simulate_market_data(usyms: list[str], rate_risk_free: float) -> tuple[Table, Table, Table, Table]:
""" Simulate market data for a set of underlying securities and options.
Args:
usyms: List of underlying symbols
rate_risk_free: The risk-free rate
Returns:
Tuple of tables containing the simulated securities, price history, trade history, betas
"""
############################################################################################################
# Underlying price simulation
#
# Simulate the price and volatility of a set of underlying securities
############################################################################################################
# noinspection PyUnusedLocal
usyms_array = dht.array(dht.string, usyms)
last_price = {s: round(np.abs(np.random.normal(100, 30.0)), 2) for s in usyms}
last_vol = {s: np.abs(np.random.normal(0.4, 0.2)) + 0.03 for s in usyms}
# noinspection PyUnusedLocal
def gen_sym() -> str:
""" Generate a random symbol """
return usyms[np.random.randint(0, len(usyms))]
# noinspection PyUnusedLocal
def gen_price(sym: str) -> float:
""" Generate a random price for a given symbol """
p = last_price[sym]
p += (np.random.random() - 0.5)
p = abs(p)
last_price[sym] = p
return round(p, 2)
# noinspection PyUnusedLocal
def gen_vol(sym: str) -> float:
""" Generate a random volatility for a given symbol """
v = last_vol[sym]
v += (np.random.random() - 0.5) * 0.01
v = abs(v)
last_vol[sym] = v
return v
_underlying_securities = empty_table(1) \
.update(["Type=`STOCK`", "USym = usyms_array"]) \
.ungroup() \
.update([
"Strike = NULL_DOUBLE",
"Expiry = (Instant) null",
"Parity = (String) null",
])
_underlying_prices = time_table("PT00:00:00.1") \
.update([
"Type = `STOCK`",
"USym = gen_sym()",
"Strike = NULL_DOUBLE",
"Expiry = (Instant) null",
"Parity = (String) null",
"UBid = gen_price(USym)",
"UAsk = UBid + randomInt(1, 10)*0.01",
"VolBid = gen_vol(USym)",
"VolAsk = VolBid + randomInt(1, 10)*0.01",
"Bid = UBid",
"Ask = UAsk",
])
############################################################################################################
# Option price simulation
#
# Simulate the price of a set of options based on the underlying securities
############################################################################################################
def compute_strikes(spot: float) -> npt.NDArray[np.float64]:
""" Compute the option strikes from a given underlying opening price """
ref = round(spot, 0)
start = ref - 5
stop = ref + 5
return np.arange(start, stop, step=1)
strikes = {s: compute_strikes(p) for s, p in last_price.items()}
# noinspection PyUnusedLocal
def get_strikes(sym: str) -> npt.NDArray[np.float64]:
""" Get the strikes for a given symbol """
return strikes[sym]
# noinspection PyUnusedLocal
expiry_array = dht.array(dht.Instant, [
datetime.combine(date.today() + timedelta(days=30), datetime.min.time()),
datetime.combine(date.today() + timedelta(days=60), datetime.min.time()),
])
_option_securities = empty_table(1) \
.update(["Type=`OPTION`", "USym = usyms_array"]) \
.ungroup() \
.update(["Strike = get_strikes(USym)"]) \
.ungroup() \
.update(["Expiry = expiry_array"]) \
.ungroup() \
.update(["Parity = new String[] {`CALL`, `PUT`}"]) \
.ungroup() \
.view(["Type", "USym", "Strike", "Expiry", "Parity"])
_option_prices = _underlying_prices \
.view(["Timestamp", "USym", "UBid", "UAsk", "VolBid", "VolAsk"]) \
.join(_option_securities, "USym") \
.view(["Timestamp", "Type", "USym", "Strike", "Expiry", "Parity", "UBid", "UAsk", "VolBid", "VolAsk"]) \
.update([
"DT = diffYearsAvg(Timestamp, Expiry)",
"IsStock = Type == `STOCK`",
"IsCall = Parity == `CALL`",
"Bid = black_scholes_price(UBid, Strike, (double) rate_risk_free, DT, VolBid, IsCall, IsStock)",
"Ask = black_scholes_price(UAsk, Strike, (double) rate_risk_free, DT, VolAsk, IsCall, IsStock)",
]) \
.drop_columns(["DT", "IsStock", "IsCall"])
############################################################################################################
# Securities
#
# Combine the underlying and option securities into a single table
############################################################################################################
securities = merge([_underlying_securities, _option_securities])
_underlying_securities = None
_option_securities = None
############################################################################################################
# Prices
#
# Combine the underlying and option prices into a single table
############################################################################################################
price_history = merge([_underlying_prices, _option_prices])
############################################################################################################
# Trade simulation
#
# Simulate a series of trades
############################################################################################################
# noinspection PyUnusedLocal
def get_random_strike(sym: str) -> float:
""" Get a random strike for a given underlying symbol """
return np.random.choice(strikes[sym])
trade_history = time_table("PT00:00:01") \
.update([
"Type = random() < 0.3 ? `STOCK` : `OPTION`",
"USym = usyms_array[randomInt(0, usyms_array.length)]",
"Strike = Type == `STOCK` ? NULL_DOUBLE : get_random_strike(USym)",
"Expiry = Type == `STOCK` ? null : _expiry_array[randomInt(0, _expiry_array.length)]",
"Parity = Type == `STOCK` ? null : random() < 0.5 ? `CALL` : `PUT`",
"TradeSize = randomInt(-1000, 1000)",
]) \
.aj(price_history, ["USym", "Strike", "Expiry", "Parity", "Timestamp"], ["Bid", "Ask"]) \
.update(["TradePrice = random() < 0.5 ? Bid : Ask"])
############################################################################################################
# Risk betas
############################################################################################################
betas = empty_table(1) \
.update(["USym = usyms_array"]) \
.ungroup() \
.update(["Beta = random() * 2 - 0.5"])
return securities, price_history, trade_history, betas
Then these warnings get spewed. I think they are incorrect for numba.
/opt/deephaven/venv/lib/python3.10/site-packages/deephaven/_udf.py:114: UserWarning:
numpy scalar type <class 'numpy.float64'> is used to annotate parameter '1'. Note that conversion of arguments to numpy scalar types is significantly slower than to Python built-in scalar types such as int, float, bool, etc. If possible, consider using Python built-in scalar types instead.
/opt/deephaven/venv/lib/python3.10/site-packages/deephaven/_udf.py:114: UserWarning:
numpy scalar type <class 'numpy.float64'> is used to annotate parameter '2'. Note that conversion of arguments to numpy scalar types is significantly slower than to Python built-in scalar types such as int, float, bool, etc. If possible, consider using Python built-in scalar types instead.
/opt/deephaven/venv/lib/python3.10/site-packages/deephaven/_udf.py:114: UserWarning:
numpy scalar type <class 'numpy.float64'> is used to annotate parameter '3'. Note that conversion of arguments to numpy scalar types is significantly slower than to Python built-in scalar types such as int, float, bool, etc. If possible, consider using Python built-in scalar types instead.
/opt/deephaven/venv/lib/python3.10/site-packages/deephaven/_udf.py:114: UserWarning:
numpy scalar type <class 'numpy.float64'> is used to annotate parameter '4'. Note that conversion of arguments to numpy scalar types is significantly slower than to Python built-in scalar types such as int, float, bool, etc. If possible, consider using Python built-in scalar types instead.
/opt/deephaven/venv/lib/python3.10/site-packages/deephaven/_udf.py:114: UserWarning:
numpy scalar type <class 'numpy.float64'> is used to annotate parameter '5'. Note that conversion of arguments to numpy scalar types is significantly slower than to Python built-in scalar types such as int, float, bool, etc. If possible, consider using Python built-in scalar types instead.
/opt/deephaven/venv/lib/python3.10/site-packages/deephaven/_udf.py:114: UserWarning:
numpy scalar type <class 'numpy.bool_'> is used to annotate parameter '6'. Note that conversion of arguments to numpy scalar types is significantly slower than to Python built-in scalar types such as int, float, bool, etc. If possible, consider using Python built-in scalar types instead.
/opt/deephaven/venv/lib/python3.10/site-packages/deephaven/_udf.py:114: UserWarning:
numpy scalar type <class 'numpy.bool_'> is used to annotate parameter '7'. Note that conversion of arguments to numpy scalar types is significantly slower than to Python built-in scalar types such as int, float, bool, etc. If possible, consider using Python built-in scalar types instead.
r-Scheduler-Serial-1 | .c.ConsoleServiceGrpcImpl | Error running script: java.lang.RuntimeException: Error in Python interpreter:
Adding casts just triggers anti-vectorization logic:
Having trouble with the following expression:
Full expression : black_scholes_price(UBid, Strike, (double) rate_risk_free, DT, VolBid, IsCall, IsStock)
Expression having trouble : black_scholes_price.call(UBid, Strike, doubleCast(rate_risk_free), DT, VolBid, IsCall, IsStock)
Exception type : io.deephaven.engine.table.impl.lang.QueryLanguageParser$PythonCallVectorizationFailure
Exception message : Invalid argument at index 2: Python vectorized function arguments can only be columns, variables, and constants: doubleCast(rate_risk_free)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.checkVectorizability(QueryLanguageParser.java:2725)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.prepareVectorization(QueryLanguageParser.java:2660)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.tryVectorizePythonCallable(QueryLanguageParser.java:2651)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.visit(QueryLanguageParser.java:2479)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.visit(QueryLanguageParser.java:133)
at com.github.javaparser.ast.expr.MethodCallExpr.accept(MethodCallExpr.java:116)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.visit(QueryLanguageParser.java:2387)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.visit(QueryLanguageParser.java:133)
at com.github.javaparser.ast.expr.MethodCallExpr.accept(MethodCallExpr.java:116)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.<init>(QueryLanguageParser.java:293)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.<init>(QueryLanguageParser.java:209)
at io.deephaven.engine.table.impl.select.codegen.FormulaAnalyzer.parseFormula(FormulaAnalyzer.java:209)
at io.deephaven.engine.table.impl.select.codegen.FormulaAnalyzer.parseFormula(FormulaAnalyzer.java:88)
at io.deephaven.engine.table.impl.select.DhFormulaColumn.initDef(DhFormulaColumn.java:196)
... 33 more
To make it function, a new column needs to be added.
There is another weird part to this problem. Here is the example code:
Run this first:
""" Setup the risk management example. """
import math
import numpy as np
import numpy.typing as npt
import numba
from datetime import date, datetime, timedelta
from deephaven import time_table, empty_table, merge, dtypes as dht
from deephaven.table import Table
############################################################################################################
# Black-Scholes
#
# Write a Black-Scholes option pricing model in Python using Numba for vectorization.
############################################################################################################
@numba.vectorize(['float64(float64)'])
def norm_cdf(x):
""" Cumulative distribution function for the standard normal distribution """
return (1.0 + math.erf(x / math.sqrt(2.0))) / 2.0
@numba.vectorize(['float64(float64)'])
def norm_pdf(x):
""" Probability density function for the standard normal distribution """
return math.exp(-x**2 / 2.0) / math.sqrt(2.0 * math.pi)
@numba.vectorize(['float64(float64, float64, float64, float64, float64, boolean, boolean)'])
def black_scholes_price(s, k, r, t, vol, is_call, is_stock):
""" Calculates the Black-Scholes option price for a call/put """
if is_stock:
return s
d1 = (np.log(s / k) + (r + vol ** 2 / 2) * t) / (vol * np.sqrt(t))
d2 = d1 - vol * np.sqrt(t)
if is_call:
return s * norm_cdf(d1) - k * np.exp(-r * t) * norm_cdf(d2)
else:
return k * np.exp(-r * t) * norm_cdf(-d2) - s * norm_cdf(-d1)
@numba.vectorize(['float64(float64, float64, float64, float64, float64, boolean, boolean)'])
def black_scholes_delta(s, k, r, t, vol, is_call, is_stock):
""" Calculates the Black-Scholes option delta for a call/put """
if is_stock:
return 1.0
d1 = (np.log(s / k) + (r + vol ** 2 / 2) * t) / (vol * np.sqrt(t))
# d2 = d1 - vol * np.sqrt(T)
if is_call:
return norm_cdf(d1)
else:
return -norm_cdf(-d1)
@numba.vectorize(['float64(float64, float64, float64, float64, float64, boolean)'])
def black_scholes_gamma(s, k, r, t, vol, is_stock):
""" Calculates the Black-Scholes option gamma """
if is_stock:
return 0.0
d1 = (np.log(s / k) + (r + vol ** 2 / 2) * t) / (vol * np.sqrt(t))
# d2 = d1 - vol * np.sqrt(T)
return norm_pdf(d1) / (s * vol * np.sqrt(t))
@numba.vectorize(['float64(float64, float64, float64, float64, float64, boolean, boolean)'])
def black_scholes_theta(s, k, r, t, vol, is_call, is_stock):
""" Calculates the Black-Scholes option theta """
if is_stock:
return 0.0
d1 = (np.log(s / k) + (r + vol ** 2 / 2) * t) / (vol * np.sqrt(t))
d2 = d1 - vol * np.sqrt(t)
if is_call:
return - ((s * norm_pdf(d1) * vol) / (2 * np.sqrt(t))) - r * k * np.exp(-r * t) * norm_cdf(d2)
else:
return - ((s * norm_pdf(d1) * vol) / (2 * np.sqrt(t))) + r * k * np.exp(-r * t) * norm_cdf(-d2)
@numba.vectorize(['float64(float64, float64, float64, float64, float64, boolean)'])
def black_scholes_vega(s, k, r, t, vol, is_stock):
""" Calculates the Black-Scholes option vega """
if is_stock:
return 0.0
d1 = (np.log(s / k) + (r + vol ** 2 / 2) * t) / (vol * np.sqrt(t))
return s * np.sqrt(t) * norm_pdf(d1)
@numba.vectorize(['float64(float64, float64, float64, float64, float64, boolean, boolean)'])
def black_scholes_rho(s, k, r, t, vol, is_call, is_stock):
""" Calculates the Black-Scholes option rho """
if is_stock:
return 0.0
d1 = (np.log(s / k) + (r + vol ** 2 / 2) * t) / (vol * np.sqrt(t))
d2 = d1 - vol * np.sqrt(t)
if is_call:
return 0.01 * k * t * np.exp(-r * t) * norm_cdf(d2)
else:
return 0.01 * -k * t * np.exp(-r * t) * norm_cdf(-d2)
############################################################################################################
# Simulate market and trading data
############################################################################################################
# noinspection PyUnusedLocal
def simulate_market_data(usyms: list[str], rate_risk_free: float, n_accounts: int = 5) -> tuple[Table, Table, Table, Table]:
""" Simulate market data for a set of underlying securities and options.
Args:
usyms: List of underlying symbols
rate_risk_free: The risk-free rate
n_accounts: The number of trading accounts to simulate
Returns:
Tuple of tables containing the simulated securities, price history, trade history, betas
"""
############################################################################################################
# Underlying price simulation
#
# Simulate the price and volatility of a set of underlying securities
############################################################################################################
# noinspection PyUnusedLocal
usyms_array = dht.array(dht.string, usyms)
last_price = {s: round(np.abs(np.random.normal(100, 30.0)), 2) for s in usyms}
last_vol = {s: np.abs(np.random.normal(0.4, 0.2)) + 0.03 for s in usyms}
# noinspection PyUnusedLocal
def gen_sym() -> str:
""" Generate a random symbol """
return usyms[np.random.randint(0, len(usyms))]
# noinspection PyUnusedLocal
def gen_price(sym: str) -> float:
""" Generate a random price for a given symbol """
p = last_price[sym]
p += (np.random.random() - 0.5)
p = abs(p)
last_price[sym] = p
return round(p, 2)
# noinspection PyUnusedLocal
def gen_vol(sym: str) -> float:
""" Generate a random volatility for a given symbol """
v = last_vol[sym]
v += (np.random.random() - 0.5) * 0.01
v = abs(v)
last_vol[sym] = v
return v
_underlying_securities = empty_table(1) \
.update(["Type=`STOCK`", "USym = usyms_array"]) \
.ungroup() \
.update([
"Strike = NULL_DOUBLE",
"Expiry = (Instant) null",
"Parity = (String) null",
])
_underlying_prices = time_table("PT00:00:00.1") \
.update([
"Type = `STOCK`",
"USym = gen_sym()",
"Strike = NULL_DOUBLE",
"Expiry = (Instant) null",
"Parity = (String) null",
"UBid = gen_price(USym)",
"UAsk = UBid + randomInt(1, 10)*0.01",
"VolBid = gen_vol(USym)",
"VolAsk = VolBid + randomInt(1, 10)*0.01",
"Bid = UBid",
"Ask = UAsk",
])
############################################################################################################
# Option price simulation
#
# Simulate the price of a set of options based on the underlying securities
############################################################################################################
def compute_strikes(spot: float) -> npt.NDArray[np.float64]:
""" Compute the option strikes from a given underlying opening price """
ref = round(spot, 0)
start = ref - 5
stop = ref + 5
return np.arange(start, stop, step=1)
strikes = {s: compute_strikes(p) for s, p in last_price.items()}
# noinspection PyUnusedLocal
def get_strikes(sym: str) -> npt.NDArray[np.float64]:
""" Get the strikes for a given symbol """
return strikes[sym]
# noinspection PyUnusedLocal
expiry_array = dht.array(dht.Instant, [
datetime.combine(date.today() + timedelta(days=30), datetime.min.time()),
datetime.combine(date.today() + timedelta(days=60), datetime.min.time()),
])
_option_securities = empty_table(1) \
.update(["Type=`OPTION`", "USym = usyms_array"]) \
.ungroup() \
.update(["Strike = get_strikes(USym)"]) \
.ungroup() \
.update(["Expiry = expiry_array"]) \
.ungroup() \
.update(["Parity = new String[] {`CALL`, `PUT`}"]) \
.ungroup() \
.view(["Type", "USym", "Strike", "Expiry", "Parity"])
_option_prices = _underlying_prices \
.view(["Timestamp", "USym", "UBid", "UAsk", "VolBid", "VolAsk"]) \
.join(_option_securities, "USym") \
.view(["Timestamp", "Type", "USym", "Strike", "Expiry", "Parity", "UBid", "UAsk", "VolBid", "VolAsk"]) \
.update([
"DT = diffYearsAvg(Timestamp, Expiry)",
"IsStock = Type == `STOCK`",
"IsCall = Parity == `CALL`",
"Rf = (double) rate_risk_free",
"Bid = black_scholes_price(UBid, Strike, Rf, DT, VolBid, IsCall, IsStock)",
"Ask = black_scholes_price(UAsk, Strike, Rf, DT, VolAsk, IsCall, IsStock)",
]) \
.drop_columns(["Rf", "DT", "IsStock", "IsCall"])
############################################################################################################
# Securities
#
# Combine the underlying and option securities into a single table
############################################################################################################
securities = merge([_underlying_securities, _option_securities])
_underlying_securities = None
_option_securities = None
############################################################################################################
# Prices
#
# Combine the underlying and option prices into a single table
############################################################################################################
price_history = merge([_underlying_prices, _option_prices])
############################################################################################################
# Trade simulation
#
# Simulate a series of trades
############################################################################################################
# noinspection PyUnusedLocal
def get_random_strike(sym: str) -> float:
""" Get a random strike for a given underlying symbol """
return np.random.choice(strikes[sym])
trade_history = time_table("PT00:00:01") \
.update([
"Account = randomInt(0, n_accounts)",
"Type = random() < 0.3 ? `STOCK` : `OPTION`",
"USym = usyms_array[randomInt(0, usyms_array.length)]",
"Strike = Type == `STOCK` ? NULL_DOUBLE : get_random_strike(USym)",
"Expiry = Type == `STOCK` ? null : expiry_array[randomInt(0, expiry_array.length)]",
"Parity = Type == `STOCK` ? null : random() < 0.5 ? `CALL` : `PUT`",
"TradeSize = randomInt(-1000, 1000)",
]) \
.aj(price_history, ["USym", "Strike", "Expiry", "Parity", "Timestamp"], ["Bid", "Ask"]) \
.update(["TradePrice = random() < 0.5 ? Bid : Ask"])
############################################################################################################
# Risk betas
############################################################################################################
betas = empty_table(1) \
.update(["USym = usyms_array"]) \
.ungroup() \
.update(["Beta = random() * 2 - 0.5"])
return securities, price_history, trade_history, betas
Run this next:
# from deephaven_server import Server
# _s = Server(port=10000, jvm_args=["-Xmx16g"])
# _s.start()
from deephaven import time_table, updateby as uby
# from setup_risk_management import simulate_market_data, black_scholes_price, black_scholes_delta, black_scholes_gamma, black_scholes_theta, black_scholes_vega, black_scholes_rho
usyms = ["AAPL", "GOOG", "MSFT", "AMZN", "FB", "TSLA", "NVDA", "INTC", "CSCO", "ADBE", "SPY", "QQQ", "DIA", "IWM", "GLD", "SLV", "USO", "UNG", "TLT", "IEF", "LQD", "HYG", "JNK"]
rate_risk_free = 0.05
securities, price_history, trade_history, betas = simulate_market_data(usyms, rate_risk_free)
price_current = price_history.last_by(["USym", "Strike", "Expiry", "Parity"])
############################################################################################################
# Greeks
#
# Calculate the greeks for the securites
############################################################################################################
greek_history = price_history \
.snapshot_when(time_table("PT00:00:05").drop_columns("Timestamp")) \
.update([
"UMid = (UBid + UAsk) / 2",
"VolMid = (VolBid + VolAsk) / 2",
"DT = diffYearsAvg(Timestamp, Expiry)",
"Rf = (double) rate_risk_free",
"IsStock = Type == `STOCK`",
"IsCall = Parity == `CALL`",
"Theo = black_scholes_price(UMid, Strike, Rf, DT, VolMid, IsCall, IsStock)",
"Delta = black_scholes_delta(UBid, Strike, Rf, DT, VolBid, IsCall, IsStock)",
"Gamma = black_scholes_gamma(UBid, Strike, Rf, DT, VolBid, IsStock)",
"Theta = black_scholes_theta(UBid, Strike, Rf, DT, VolBid, IsCall, IsStock)",
"Vega = black_scholes_vega(UBid, Strike, Rf, DT, VolBid, IsStock)",
"Rho = black_scholes_rho(UBid, Strike, Rf, DT, VolBid, IsCall, IsStock)",
"UMidUp10 = UMid * 1.1",
"UMidDown10 = UMid * 0.9",
"Up10 = black_scholes_price(UMidUp10, Strike, Rf, DT, VolMid, IsCall, IsStock)",
"Down10 = black_scholes_price(UMidDown10, Strike, Rf, DT, VolMid, IsCall, IsStock)",
"JumpUp10 = Up10 - Theo",
"JumpDown10 = Down10 - Theo",
]) \
.drop_columns(["UMidUp10", "UMidDown10", "Up10", "Down10"])
greek_current = greek_history.last_by(["USym", "Strike", "Expiry", "Parity"])
############################################################################################################
# Portfolio
#
# Calculate the current portfolio and history
############################################################################################################
portfolio_history = trade_history \
.update_by([uby.cum_sum("Position=TradeSize")], ["Account", "USym", "Strike", "Expiry", "Parity"])
portfolio_current = portfolio_history \
.last_by(["Account", "USym", "Strike", "Expiry", "Parity"]) \
.view(["Account", "USym", "Strike", "Expiry", "Parity", "Position"])
############################################################################################################
# Risk
#
# Calculate the risk for the portfolio in different ways
############################################################################################################
risk_all = greek_current \
.natural_join(portfolio_current, ["USym", "Strike", "Expiry", "Parity"]) \
.natural_join(betas, "USym") \
.update([
"Theo = Theo * Position",
"DollarDelta = UMid * Delta * Position",
"BetaDollarDelta = Beta * DollarDelta",
"GammaPercent = UMid * Gamma * Position",
"Theta = Theta * Position",
"VegaPercent = VolMid * Vega * Position",
"Rho = Rho * Position",
"JumpUp10 = JumpUp10 * Position",
"JumpDown10 = JumpDown10 * Position",
]) \
.view([
"Account",
"USym",
"Strike",
"Expiry",
"Parity",
"Theo",
"DollarDelta",
"BetaDollarDelta",
"GammaPercent",
"VegaPercent",
"Theta",
"Rho",
"JumpUp10",
"JumpDown10",
])
risk_ue = risk_all.drop_columns(["Strike", "Parity"]).sum_by(["Account", "USym", "Expiry"])
risk_u = risk_ue.drop_columns("Expiry").sum_by(["Account", "USym"])
risk_e = risk_ue.drop_columns("USym").sum_by(["Account", "Expiry"])
risk_net = risk_ue.drop_columns(["USym", "Expiry"]).sum_by("Account")
risk_firm = risk_net.drop_columns("Account").sum_by()
############################################################################################################
# Trade analysis
#
# Calculate the PnL for the trades with a 10 minute holding period
############################################################################################################
trade_pnl = trade_history \
.view(["Timestamp", "USym", "Strike", "Expiry", "Parity", "TradeSize", "TradePrice"]) \
.aj(price_history.update("Timestamp=Timestamp-'PT10m'"),
["USym", "Strike", "Expiry", "Parity", "Timestamp"],
["FutureBid=Bid", "FutureAsk=Ask"]) \
.update([
"FutureMid = (FutureBid + FutureAsk) / 2",
"PriceChange = FutureMid - TradePrice",
"PnL = TradeSize * PriceChange",
])
trade_pnl_by_sym = trade_pnl \
.view(["USym", "PnL"]) \
.sum_by("USym")
That should run fine. Now if the double cast in Rf = (double) rate_risk_free
is removed, it fails.
Value: table update operation failed. : black_scholes_price
Traceback (most recent call last):
File "/opt/deephaven/venv/lib/python3.10/site-packages/deephaven/table.py", line 764, in update
return Table(j_table=self.j_table.update(*formulas))
RuntimeError: io.deephaven.engine.table.impl.select.FormulaCompilationException: Formula compilation error for: black_scholes_price(UMid, Strike, Rf, DT, VolMid, IsCall, IsStock)
at io.deephaven.engine.table.impl.select.DhFormulaColumn.initDef(DhFormulaColumn.java:216)
at io.deephaven.engine.table.impl.select.SwitchColumn.initDef(SwitchColumn.java:64)
at io.deephaven.engine.table.impl.select.analyzers.SelectAndViewAnalyzer.create(SelectAndViewAnalyzer.java:124)
at io.deephaven.engine.table.impl.select.analyzers.SelectAndViewAnalyzer.create(SelectAndViewAnalyzer.java:73)
at io.deephaven.engine.table.impl.QueryTable.lambda$selectOrUpdate$32(QueryTable.java:1518)
at io.deephaven.engine.table.impl.perf.QueryPerformanceRecorder.withNugget(QueryPerformanceRecorder.java:369)
at io.deephaven.engine.table.impl.QueryTable.lambda$selectOrUpdate$33(QueryTable.java:1500)
at io.deephaven.engine.table.impl.QueryTable.memoizeResult(QueryTable.java:3639)
at io.deephaven.engine.table.impl.QueryTable.selectOrUpdate(QueryTable.java:1499)
at io.deephaven.engine.table.impl.QueryTable.update(QueryTable.java:1477)
at io.deephaven.engine.table.impl.QueryTable.update(QueryTable.java:100)
at io.deephaven.api.TableOperationsDefaults.update(TableOperationsDefaults.java:94)
at org.jpy.PyLib.executeCode(Native Method)
at org.jpy.PyObject.executeCode(PyObject.java:138)
at io.deephaven.engine.util.PythonEvaluatorJpy.evalScript(PythonEvaluatorJpy.java:73)
at io.deephaven.integrations.python.PythonDeephavenSession.lambda$evaluate$1(PythonDeephavenSession.java:205)
at io.deephaven.util.locks.FunctionalLock.doLockedInterruptibly(FunctionalLock.java:51)
at io.deephaven.integrations.python.PythonDeephavenSession.evaluate(PythonDeephavenSession.java:205)
at io.deephaven.engine.util.AbstractScriptSession.lambda$evaluateScript$0(AbstractScriptSession.java:163)
at io.deephaven.engine.context.ExecutionContext.lambda$apply$0(ExecutionContext.java:196)
at io.deephaven.engine.context.ExecutionContext.apply(ExecutionContext.java:207)
at io.deephaven.engine.context.ExecutionContext.apply(ExecutionContext.java:195)
at io.deephaven.engine.util.AbstractScriptSession.evaluateScript(AbstractScriptSession.java:163)
at io.deephaven.engine.util.DelegatingScriptSession.evaluateScript(DelegatingScriptSession.java:72)
at io.deephaven.engine.util.ScriptSession.evaluateScript(ScriptSession.java:75)
at io.deephaven.server.console.ConsoleServiceGrpcImpl.lambda$executeCommand$4(ConsoleServiceGrpcImpl.java:191)
at io.deephaven.server.session.SessionState$ExportBuilder.lambda$submit$2(SessionState.java:1537)
at io.deephaven.server.session.SessionState$ExportObject.doExport(SessionState.java:995)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at io.deephaven.server.runner.scheduler.SchedulerModule$ThreadFactory.lambda$newThread$0(SchedulerModule.java:100)
at java.base/java.lang.Thread.run(Thread.java:1583)
caused by io.deephaven.engine.table.impl.lang.QueryLanguageParser$QueryLanguageParseException:
Having trouble with the following expression:
Full expression : black_scholes_price(UMid, Strike, Rf, DT, VolMid, IsCall, IsStock)
Expression having trouble : black_scholes_price.call(UMid, Strike, Rf, DT, VolMid, IsCall, IsStock)
Exception type : java.lang.IllegalArgumentException
Exception message : black_scholes_price: Expected argument (3) to be either one of [double] or their compatible ones, got class java.lang.Double
at io.deephaven.engine.util.PyCallableWrapperJpyImpl.verifyArguments(PyCallableWrapperJpyImpl.java:331)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.verifyPyCallableArguments(QueryLanguageParser.java:2506)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.visit(QueryLanguageParser.java:2478)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.visit(QueryLanguageParser.java:133)
at com.github.javaparser.ast.expr.MethodCallExpr.accept(MethodCallExpr.java:116)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.visit(QueryLanguageParser.java:2387)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.visit(QueryLanguageParser.java:133)
at com.github.javaparser.ast.expr.MethodCallExpr.accept(MethodCallExpr.java:116)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.<init>(QueryLanguageParser.java:293)
at io.deephaven.engine.table.impl.lang.QueryLanguageParser.<init>(QueryLanguageParser.java:209)
at io.deephaven.engine.table.impl.select.codegen.FormulaAnalyzer.parseFormula(FormulaAnalyzer.java:209)
at io.deephaven.engine.table.impl.select.codegen.FormulaAnalyzer.parseFormula(FormulaAnalyzer.java:88)
at io.deephaven.engine.table.impl.select.DhFormulaColumn.initDef(DhFormulaColumn.java:196)
... 33 more
Line: 766
Namespace: update
File: /opt/deephaven/venv/lib/python3.10/site-packages/deephaven/table.py
Traceback (most recent call last):
File "<string>", line 25, in <module>
File "/opt/deephaven/venv/lib/python3.10/site-packages/deephaven/table.py", line 766, in update
at org.jpy.PyLib.executeCode(Native Method)
at org.jpy.PyObject.executeCode(PyObject.java:138)
at io.deephaven.engine.util.PythonEvaluatorJpy.evalScript(PythonEvaluatorJpy.java:73)
at io.deephaven.integrations.python.PythonDeephavenSession.lambda$evaluate$1(PythonDeephavenSession.java:205)
at io.deephaven.util.locks.FunctionalLock.doLockedInterruptibly(FunctionalLock.java:51)
at io.deephaven.integrations.python.PythonDeephavenSession.evaluate(PythonDeephavenSession.java:205)
at io.deephaven.engine.util.AbstractScriptSession.lambda$evaluateScript$0(AbstractScriptSession.java:163)
at io.deephaven.engine.context.ExecutionContext.lambda$apply$0(ExecutionContext.java:196)
at io.deephaven.engine.context.ExecutionContext.apply(ExecutionContext.java:207)
at io.deephaven.engine.context.ExecutionContext.apply(ExecutionContext.java:195)
at io.deephaven.engine.util.AbstractScriptSession.evaluateScript(AbstractScriptSession.java:163)
at io.deephaven.engine.util.DelegatingScriptSession.evaluateScript(DelegatingScriptSession.java:72)
at io.deephaven.engine.util.ScriptSession.evaluateScript(ScriptSession.java:75)
at io.deephaven.server.console.ConsoleServiceGrpcImpl.lambda$executeCommand$4(ConsoleServiceGrpcImpl.java:191)
at io.deephaven.server.session.SessionState$ExportBuilder.lambda$submit$2(SessionState.java:1537)
at io.deephaven.server.session.SessionState$ExportObject.doExport(SessionState.java:995)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at io.deephaven.server.runner.scheduler.SchedulerModule$ThreadFactory.lambda$newThread$0(SchedulerModule.java:100)
at java.base/java.lang.Thread.run(Thread.java:1583)
Python query string can not use a python
float
as adouble
in a query string.First run this code:
Next run this code:
Error: