Pursue better loss function to acquire a plausable predictive model
import pymc as pm
import numpy as np
import matplotlib.pyplot as plt
def stock_loss(true_return, yhat, alpha=100.):
if true_return * yhat < 0:
return alpha * yhat ** 2 \
- np.sign(true_return) * yhat + abs(true_return)
else:
return abs(true_return - yhat)
pred = np.linspace(-.04, .12, 75)
for true_value in [.05, -.02]:
plt.plot(pred, [stock_loss(true_value, _p) for _p in pred],
lw=3, label='loss associated with\nprediction '
'if true value={:.2f}'.format(true_value))
plt.vlines(0, 0, .25, linestyles='--')
plt.xlabel('Prediction')
plt.ylabel('Loss')
plt.xlim(-.04, .12)
plt.ylim(0, .25)
plt.legend()
plt.title('Stock returns loss if true value = 0.05, -0.02')
Text(0.5, 1.0, 'Stock returns loss if true value = 0.05, -0.02')
Fitted simple least-squares model.
$$ R = \alpha + \beta x + \epsilon, $$where
$$ \epsilon ~ Normal(0, 1/\tau) $$N = 100
X = .025 * np.random.randn(N)
Y = .5 * X + .01 * np.random.randn(N)
ls_coef_ = np.cov(X, Y)[0, 1] / np.var(X)
ls_intercept_ = Y.mean() - ls_coef_ * X.mean()
plt.scatter(X, Y, c='k')
plt.plot(X, ls_coef_ * X + ls_intercept_,
label='least-squares line')
plt.xlim(X.min(), X.max())
plt.ylim(Y.min(), Y.max())
plt.xlabel('Trading signal')
plt.ylabel('Returns')
plt.title('Empirical returns versus trading signal')
plt.legend(loc='upper left')
<matplotlib.legend.Legend at 0x1570ec8cfd0>
std = pm.Uniform('std', 0, 100, trace=False)
@pm.deterministic
def prec(U=std):
return 1.0 / U ** 2
beta = pm.Normal('beta', 0, 0.0001)
alpha = pm.Normal('alpha', 0, 0.0001)
@pm.deterministic
def mean(X=X, alpha=alpha, beta=beta):
return alpha + beta * X
obs = pm.Normal('obs', mean, prec, value=Y, observed=True)
mcmc = pm.MCMC([obs, beta, alpha, std, prec])
mcmc.sample(100000, 80000)
[-----------------100%-----------------] 100000 of 100000 complete in 20.7 sec
Bayes action is a set of parameters that minimize the value of loss function after Bayes inference
from scipy.optimize import fmin
def stock_loss(price, pred, coef=500):
sol = np.zeros_like(price)
ix = price * pred < 0
sol[ix] = coef * pred ** 2. \
- np.sign(price[ix]) * pred + abs(price[ix])
sol[~ix] = abs(price[~ix] - pred)
return sol
tau_samples = mcmc.trace('prec')[:]
alpha_samples = mcmc.trace('alpha')[:]
beta_samples = mcmc.trace('beta')[:]
N = tau_samples.shape[0]
noise = 1. / np.sqrt(tau_samples) * np.random.randn(N)
possible_outcomes = lambda signal: \
alpha_samples + beta_samples * signal + noise
opt_predictions = np.zeros(50)
trading_signals = np.linspace(X.min(), X.max(), 50)
for i, _signal in enumerate(trading_signals):
_possible_outcomes = possible_outcomes(_signal)
tomin = lambda pred: stock_loss(_possible_outcomes, pred).mean()
opt_predictions[i] = fmin(tomin, 0, disp=False)
plt.plot(X, ls_coef_ * X + ls_intercept_,
label='least-squares prediction')
plt.plot(trading_signals, opt_predictions,
label='Bayes action prediction')
plt.xlim(X.min(), X.max())
plt.xlabel('Trading signal')
plt.ylabel('Prediction')
plt.title('Least-squares prediction versus Bayes action prediction')
plt.legend(loc='upper left')
<matplotlib.legend.Legend at 0x1570fd58400>