Initial import from Docker volume
This commit is contained in:
428
modules/risk/myvar.py
Executable file
428
modules/risk/myvar.py
Executable file
@@ -0,0 +1,428 @@
|
||||
import stdnum.exceptions
|
||||
from sql import Column, Literal
|
||||
import datetime
|
||||
from sql.aggregate import Count, Max, Min, Sum, Avg, BoolOr
|
||||
from sql.conditionals import Case
|
||||
from stdnum import get_cc_module
|
||||
from sql.functions import CharLength, CurrentTimestamp, DateTrunc, Extract
|
||||
from trytond.i18n import gettext
|
||||
from trytond.model import (
|
||||
DeactivableMixin, Index, ModelSQL, ModelView, MultiValueMixin, Unique,
|
||||
ValueMixin, convert_from, fields, sequence_ordered)
|
||||
from trytond.model.exceptions import AccessError
|
||||
from trytond.pool import Pool
|
||||
from trytond.pyson import Bool, Eval
|
||||
from trytond.tools import is_full_text, lstrip_wildcard
|
||||
from trytond.transaction import Transaction, inactive_records
|
||||
from trytond.wizard import Button, StateTransition, StateView, Wizard
|
||||
from decimal import Decimal
|
||||
from var import VaR, load_data
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import math
|
||||
import logging
|
||||
import datetime
|
||||
import io
|
||||
import contextlib
|
||||
import warnings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
VARTYPE = [
|
||||
('historical', 'Historical'),
|
||||
('parametric', 'Parametric'),
|
||||
('montecarlo', 'Monté Carlo'),
|
||||
('garch', 'GARCH'),
|
||||
]
|
||||
VARMODEL = [
|
||||
(None, ''),
|
||||
('norm', 'Normal'),
|
||||
('lognorm', 'Log Normal'),
|
||||
('t', 'T student'),
|
||||
('uniform', 'Uniform'),
|
||||
('expon', 'Exponential'),
|
||||
('exponpaw', 'Exp. Pow'),
|
||||
('powerlaw', 'Power law'),
|
||||
('rayleigh', 'Rayleigh'),
|
||||
('chauchy', 'Cauchy'),
|
||||
('gamma', 'Gamma'),
|
||||
('chi2', 'Chi2'),
|
||||
('f', 'F'),
|
||||
('gumbel_r', 'Gumbel_r'),
|
||||
]
|
||||
|
||||
class Var(ModelSQL, ModelView):
|
||||
"Value at risk"
|
||||
__name__ = 'risk.var'
|
||||
|
||||
name = fields.Char("Name")
|
||||
horizon = fields.Integer("Time horizon")
|
||||
confidence = fields.Numeric("Confidence")
|
||||
type = fields.Selection(VARTYPE,"Type")
|
||||
from_date = fields.Date("From")
|
||||
to_date = fields.Date("To")
|
||||
nb_days = fields.Integer("Nb days")
|
||||
lines = fields.One2Many('risk.var.line','var',"Lines")
|
||||
values = fields.One2Many('risk.var.values','var',"Values")
|
||||
product = fields.Many2One('product.product',"Product")
|
||||
party = fields.Many2One('party.party',"Trader")
|
||||
execute = fields.Boolean("Schedule to position")
|
||||
result = fields.Text("Result")
|
||||
market = fields.Selection([
|
||||
(None, ''),
|
||||
("Coffee ICE US", "Coffee ICE US"),
|
||||
("Cocoa ICE US", "Cocoa ICE US"),
|
||||
("Coffee ICE EU", "Coffee ICE EU"),
|
||||
("Cocoa ICE EU", "Cocoa ICE EU")
|
||||
],"Market")
|
||||
model = fields.Selection(VARMODEL,"Model")
|
||||
var_his = fields.Numeric("Historical")
|
||||
var_par = fields.Numeric("Parametric")
|
||||
var_mc = fields.Numeric("Monte Carlo")
|
||||
var_gar = fields.Numeric("GARCH")
|
||||
cvar_his = fields.Numeric("")
|
||||
cvar_par = fields.Numeric("")
|
||||
cvar_mc = fields.Numeric("")
|
||||
cvar_gar = fields.Numeric("")
|
||||
cdar_his = fields.Numeric("")
|
||||
cdar_par = fields.Numeric("")
|
||||
cdar_mc = fields.Numeric("")
|
||||
cdar_gar = fields.Numeric("")
|
||||
|
||||
@classmethod
|
||||
def default_model(cls):
|
||||
return 'norm'
|
||||
|
||||
class VarLine(ModelSQL, ModelView):
|
||||
"VaR line"
|
||||
__name__ = 'risk.var.line'
|
||||
|
||||
var = fields.Many2One('risk.var',"Var")
|
||||
curve = fields.Many2One('price.price',"Curve")
|
||||
model = fields.Selection(VARMODEL,"Model")
|
||||
weight = fields.Numeric("Weight")
|
||||
nb_days = fields.Integer("Backtest days")
|
||||
nb_days_past = fields.Integer("Historical days")
|
||||
mean = fields.Numeric("Mean")
|
||||
std = fields.Numeric("Std")
|
||||
simul_date = fields.Date("Date")
|
||||
dfn = fields.Numeric("Dfn")
|
||||
dfn = fields.Numeric("Dfd")
|
||||
perf = fields.One2Many('risk.perf','line',"Perf")
|
||||
perf_comput = fields.Function(fields.One2Many('risk.perf','line',
|
||||
"VaR computation",
|
||||
order=[
|
||||
('date', 'ASC'),
|
||||
],),'get_perfs')
|
||||
perf_past = fields.Function(fields.One2Many('risk.perf','line',
|
||||
"Historical data",
|
||||
order=[
|
||||
('date', 'ASC'),
|
||||
],),'get_perfs_past')
|
||||
fit_text = fields.Text("Fit result")
|
||||
fit_text_dyn = fields.Function(fields.Text("Fit result"),'get_fit_info')
|
||||
fit = fields.Boolean("Fit distribution")
|
||||
simul_comput = fields.Selection(VARMODEL,"Simulate with")
|
||||
gen_simul_comput = fields.Boolean("Generate")
|
||||
simul_past = fields.Selection(VARMODEL,"Simulate with")
|
||||
gen_simul_past = fields.Boolean("Generate")
|
||||
link = fields.Boolean("Link position")
|
||||
month = fields.Char("Month")
|
||||
future_market = fields.Char("Market")
|
||||
whatif = fields.Numeric("What if (%)")
|
||||
simul_return = fields.Numeric("Return")
|
||||
simul_return_past = fields.Numeric("Return")
|
||||
|
||||
@classmethod
|
||||
def default_simul_return(cls):
|
||||
return 0.2
|
||||
|
||||
@classmethod
|
||||
def default_simul_return_past(cls):
|
||||
return 0.2
|
||||
|
||||
@classmethod
|
||||
def default_nb_days(cls):
|
||||
return 100
|
||||
|
||||
@classmethod
|
||||
def default_nb_days_past(cls):
|
||||
return 100
|
||||
|
||||
@classmethod
|
||||
def default_link(cls):
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def default_simul_date(cls):
|
||||
Date = Pool().get('ir.date')
|
||||
return Date.today()
|
||||
|
||||
def get_fit_info(self,name):
|
||||
return self.fit_text
|
||||
|
||||
def get_perfs_ordered(self,from_date,to_date):
|
||||
perfs = []
|
||||
Perf = Pool().get('risk.perf')
|
||||
perfs.extend(Perf.search([('line','=',self.id),('date','>=',from_date),('date','<=',to_date)],order=[('date','ASC')]))
|
||||
return perfs
|
||||
|
||||
def get_perfs_past(self, name):
|
||||
pastdate = self.simul_date - datetime.timedelta(days=(self.nb_days_past+self.nb_days))
|
||||
pastdate2 = self.simul_date - datetime.timedelta(days=self.nb_days)
|
||||
return self.get_perfs_ordered(pastdate,pastdate2)
|
||||
|
||||
def get_perfs(self, name):
|
||||
pastdate = self.simul_date - datetime.timedelta(days=self.nb_days)
|
||||
return self.get_perfs_ordered(pastdate,self.simul_date)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super(VarLine, cls).write(*args)
|
||||
to_write = []
|
||||
varlines = sum(args[::2], [])
|
||||
for vl in varlines:
|
||||
if vl.gen_simul_comput:
|
||||
end_date = vl.simul_date
|
||||
num_days = vl.nb_days
|
||||
start_date = end_date - datetime.timedelta(num_days)
|
||||
total_return = vl.simul_return
|
||||
if vl.simul_comput == 'norm':
|
||||
daily_returns = np.random.normal(loc=total_return / num_days, scale=0.01, size=num_days)
|
||||
elif vl.simul_comput == 'lognormal':
|
||||
daily_returns = np.random.lognormal(mean=total_return / num_days, sigma=0.01, size=num_days)
|
||||
elif vl.simul_comput == 't':
|
||||
daily_returns = np.random.standard_t(df=10, size=num_days) * (total_return / num_days)
|
||||
else:
|
||||
daily_returns = np.random.normal(loc=total_return / num_days, scale=0.01, size=num_days)
|
||||
Perf = Pool().get('risk.perf')
|
||||
to_delete = Perf.search([('line','=',vl.id),('date','>=',start_date)])
|
||||
Perf.delete(to_delete)
|
||||
for i in range(num_days):
|
||||
p = Perf()
|
||||
p.line = vl.id
|
||||
p.price_index = vl.curve
|
||||
p.date = start_date + datetime.timedelta(days=i)
|
||||
p.d_perf = daily_returns[i]
|
||||
Perf.save([p])
|
||||
to_write.extend(([vl], {
|
||||
'gen_simul_comput': False,
|
||||
}))
|
||||
if vl.gen_simul_past:
|
||||
end_date = vl.simul_date - datetime.timedelta(vl.nb_days)
|
||||
num_days = vl.nb_days_past
|
||||
start_date = end_date - datetime.timedelta(num_days)
|
||||
total_return = vl.simul_return_past
|
||||
if vl.simul_past == 'norm':
|
||||
daily_returns = np.random.normal(loc=total_return / num_days, scale=0.01, size=num_days)
|
||||
elif vl.simul_past == 'lognormal':
|
||||
daily_returns = np.random.lognormal(mean=total_return / num_days, sigma=0.01, size=num_days)
|
||||
elif vl.simul_past == 't':
|
||||
daily_returns = np.random.standard_t(df=10, size=num_days) * (total_return / num_days)
|
||||
else:
|
||||
daily_returns = np.random.normal(loc=total_return / num_days, scale=0.01, size=num_days)
|
||||
Perf = Pool().get('risk.perf')
|
||||
to_delete = Perf.search([('line','=',vl.id),('date','>=',start_date),('date','<',end_date)])
|
||||
Perf.delete(to_delete)
|
||||
for i in range(num_days):
|
||||
p = Perf()
|
||||
p.line = vl.id
|
||||
p.price_index = vl.curve
|
||||
p.date = start_date + datetime.timedelta(days=i)
|
||||
p.d_perf = daily_returns[i]
|
||||
Perf.save([p])
|
||||
to_write.extend(([vl], {
|
||||
'gen_simul_past': False,
|
||||
}))
|
||||
|
||||
if to_write:
|
||||
cls.write(*to_write)
|
||||
|
||||
@classmethod
|
||||
def validate(cls, varlines):
|
||||
super(VarLine, cls).validate(varlines)
|
||||
|
||||
|
||||
class Perf(ModelSQL,ModelView):
|
||||
"Perf"
|
||||
__name__ = 'risk.perf'
|
||||
line = fields.Many2One('risk.var.line',"Lines")
|
||||
price_index = fields.Many2One('price.price',"Curve")
|
||||
date = fields.Date("Date")
|
||||
d_perf = fields.Numeric("Perf")
|
||||
d_up = fields.Numeric("VaR")
|
||||
d_down = fields.Numeric("Result")
|
||||
d_es = fields.Numeric("ES")
|
||||
d_drop = fields.Numeric("CDaR")
|
||||
d_mean = fields.Numeric("Mean")
|
||||
d_std = fields.Numeric("Std")
|
||||
|
||||
class VarValue(ModelSQL, ModelView):
|
||||
"VaR value"
|
||||
__name__ = 'risk.var.values'
|
||||
|
||||
var = fields.Many2One('risk.var',"Var")
|
||||
product = fields.Many2One('product.product',"Product")
|
||||
party = fields.Many2One('party.party',"Trader")
|
||||
r_var = fields.Numeric("VaR")
|
||||
r_es = fields.Numeric("ES")
|
||||
r_cdar = fields.Numeric("CDaR")
|
||||
|
||||
class VarExecute(Wizard):
|
||||
"Execute VaR"
|
||||
__name__ = "risk.var.execute"
|
||||
|
||||
start = StateTransition()
|
||||
|
||||
def get_var(self,type,var):
|
||||
if type == 'historical':
|
||||
return var.historic().iloc[0][0],var.historic().iloc[0][1],var.historic().iloc[0][2]
|
||||
elif type == 'parametric':
|
||||
return var.parametric().iloc[0][0],var.parametric().iloc[0][1],var.parametric().iloc[0][2]
|
||||
elif type == 'montecarlo':
|
||||
return var.monte_carlo().iloc[0][0],var.monte_carlo().iloc[0][1],var.monte_carlo().iloc[0][2]
|
||||
else:
|
||||
return var.garch().iloc[0][0],var.garch().iloc[0][1],var.garch().iloc[0][2]
|
||||
|
||||
def transition_start(self):
|
||||
rv = self.records[0]
|
||||
last_var = None
|
||||
for l in rv.lines:
|
||||
Perf = Pool().get('risk.perf')
|
||||
perf = Perf.search([('line','=',l.id),('date','<=',l.simul_date)],order=[('date','ASC')])
|
||||
if perf:
|
||||
data = [(e.date, float(e.d_perf)) for e in perf]
|
||||
logger.info("DATA:%s",data)
|
||||
df = pd.DataFrame(data, columns=['Date', 'd_perf'])
|
||||
df.set_index('Date', inplace=True)
|
||||
weights = np.array([1.0])
|
||||
cumul = 0
|
||||
if len(df) > (l.nb_days + l.nb_days_past):
|
||||
start = len(df) - (l.nb_days + l.nb_days_past)
|
||||
else:
|
||||
start = 0
|
||||
i = start + l.nb_days_past
|
||||
for index, p in enumerate(perf):
|
||||
if index < start:
|
||||
continue
|
||||
if index < start + l.nb_days_past:
|
||||
sub_df = df.iloc[start:(start+index)]
|
||||
sub_df['d_perf'] = sub_df['d_perf'].astype(float)
|
||||
logger.info("PPPPP:%s",p)
|
||||
p.d_mean = np.mean(sub_df)
|
||||
p.d_std = np.std(sub_df,ddof=1).loc['d_perf']
|
||||
if math.isnan(p.d_mean):
|
||||
p.d_mean = None
|
||||
if math.isnan(p.d_std):
|
||||
p.d_std = None
|
||||
Perf.save([p])
|
||||
continue
|
||||
subset_df = df.iloc[start:i-1]
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter('ignore', UserWarning)
|
||||
var = VaR(subset_df, weights, alpha=float(1-rv.confidence), distribution=l.model)
|
||||
|
||||
#fit distribution if option ticked
|
||||
if index == start + l.nb_days_past and l.fit:
|
||||
output = io.StringIO()
|
||||
with contextlib.redirect_stdout(output):
|
||||
var.fit_distributions(include_other=True, verbose=True)
|
||||
l.fit_text = output.getvalue()
|
||||
Line = Pool().get('risk.var.line')
|
||||
Line.save([l])
|
||||
|
||||
p.d_up,p.d_es,p.d_drop = self.get_var(rv.type, var)
|
||||
if math.isnan(p.d_up):
|
||||
p.d_up = None
|
||||
if math.isnan(p.d_es):
|
||||
p.d_es = None
|
||||
if math.isnan(p.d_drop):
|
||||
p.d_drop = None
|
||||
if p.d_up:
|
||||
if p.d_perf < 0 and p.d_up > p.d_perf:
|
||||
cumul += abs(p.d_perf) - abs(p.d_up)
|
||||
logger.info("PPPPP2:%s",p)
|
||||
p.d_down = cumul
|
||||
Perf.save([p])
|
||||
i+=1
|
||||
#update position if execute
|
||||
if index == len(perf)-1:
|
||||
last_var = var
|
||||
if self.records[0].execute:
|
||||
Pos = Pool().get('risk.position')
|
||||
pos = Pos.search([('future_market','=',l.future_market),('quantity','>',0),('month','=',l.month),('pricing_type','!=','Priced'),('ct_type','!=','Inventory')])
|
||||
if pos:
|
||||
PosVar = Pool().get('risk.position.var')
|
||||
posvar = PosVar.search([('position','in',[e.id for e in pos])])
|
||||
PosVar.delete(posvar)
|
||||
for po in pos:
|
||||
pv = PosVar()
|
||||
pv.var = self.records[0].id
|
||||
pv.position = po.id
|
||||
pv.d_var = p.d_up
|
||||
pv.d_cvar = p.d_es
|
||||
pv.d_cdar = p.d_drop
|
||||
pv.whatif = (1 + l.whatif/100) if l.whatif else 1
|
||||
PosVar.save([pv])
|
||||
Var = Pool().get('risk.var')
|
||||
v = Var(rv)
|
||||
d_up,d_es,d_drop = self.get_var('historical', last_var)
|
||||
v.var_his = d_up
|
||||
v.cvar_his = d_es
|
||||
v.cdar_his = d_drop
|
||||
d_up,d_es,d_drop = self.get_var('parametric', last_var)
|
||||
v.var_par = d_up
|
||||
v.cvar_par = d_es
|
||||
v.cdar_par = d_drop
|
||||
d_up,d_es,d_drop = self.get_var('montecarlo', last_var)
|
||||
v.var_mc = d_up
|
||||
v.cvar_mc = d_es
|
||||
v.cdar_mc = d_drop
|
||||
d_up,d_es,d_drop = self.get_var('garch', last_var)
|
||||
v.var_gar = d_up
|
||||
v.cvar_gar = d_es
|
||||
v.cdar_gar = d_drop
|
||||
Var.save([v])
|
||||
return 'end'
|
||||
|
||||
class VarGetPos(Wizard):
|
||||
"Get position"
|
||||
__name__ = "risk.var.get"
|
||||
|
||||
start = StateTransition()
|
||||
|
||||
def get_curve(self,month,market):
|
||||
Price = Pool().get('price.price')
|
||||
PM = Pool().get('product.month')
|
||||
pm = PM.search([('month_name','=',month)])
|
||||
if pm:
|
||||
price = Price.search([('price_desc','=',market),('price_period','=',pm[0])])
|
||||
if price:
|
||||
return price[0].id
|
||||
|
||||
def transition_start(self):
|
||||
rv = self.records[0]
|
||||
Pos = Pool().get('risk.position.process')
|
||||
if rv.market:
|
||||
pos = Pos.search([('future_market','=',rv.market)])
|
||||
else:
|
||||
pos = Pos.search([])
|
||||
if pos:
|
||||
VarLine = Pool().get('risk.var.line')
|
||||
tot_quantity = sum([e.quantity for e in pos])
|
||||
logger.info("TOTQAUNTITY:%s",tot_quantity)
|
||||
for p in pos:
|
||||
vl = VarLine()
|
||||
vl.var = rv.id
|
||||
vl.month = p.month
|
||||
vl.future_market = p.future_market
|
||||
if rv.model:
|
||||
vl.model = rv.model
|
||||
else:
|
||||
vl.model = 'norm'
|
||||
vl.curve = self.get_curve(p.month,p.future_market)
|
||||
# if tot_quantity > 0:
|
||||
# vl.weight = p.quantity / tot_quantity
|
||||
VarLine.save([vl])
|
||||
|
||||
return 'end'
|
||||
Reference in New Issue
Block a user