|
@@ -84,10 +84,9 @@ def cal_basic_performance(entity_info, ret, trailing_month) {
|
|
|
|
|
|
// accumulate 版的 skewness, kurtosis, var, cvar 似乎都不对劲,只好找个笨办法来实现
|
|
|
if(trailing_month == 'incep') {
|
|
|
+
|
|
|
// 需要至少6个数才计算标准差、峰度、偏度
|
|
|
- t0 = SELECT price_date.max() AS price_date,
|
|
|
- prod(1+ret)-1 AS trailing_ret,
|
|
|
- prod(1+ret)-1 AS trailing_ret_a,
|
|
|
+ t0 = SELECT price_date.max() AS price_date, nav, ret,
|
|
|
iif(count(entity_id) > 5, std(ret), null) AS std_dev,
|
|
|
iif(count(entity_id) > 5, skew(ret, false), null) AS skewness,
|
|
|
iif(count(entity_id) > 5, kurtosis(ret, false), null)-3 AS kurtosis,
|
|
@@ -99,10 +98,12 @@ def cal_basic_performance(entity_info, ret, trailing_month) {
|
|
|
ORDER BY end_date;
|
|
|
|
|
|
// 年化收益(给后面计算Calmar用)
|
|
|
+ t0.addColumn(['trailing_ret', 'trailing_ret_a'], [DOUBLE, DOUBLE]);
|
|
|
+ // MySQL 有bug导致首月ret_1m为空,所以用 prod(1+ret)-1算的有时不对
|
|
|
UPDATE t0
|
|
|
- SET trailing_ret_a = (1+trailing_ret).pow(12\(t0.end_date - ei.inception_date.month()))-1
|
|
|
- FROM ej(t0, entity_info ei, 'entity_id')
|
|
|
- WHERE t0.end_date > ei.inception_date.month() + 12;
|
|
|
+ SET trailing_ret = nav\ini_value - 1,
|
|
|
+ trailing_ret_a = iif(t0.end_date - ei.inception_date.month() > 12, (nav\ini_value).pow(12\(t0.end_date - ei.inception_date.month())) - 1, nav\ini_value - 1)
|
|
|
+ FROM ej(t0, entity_info ei, 'entity_id');
|
|
|
|
|
|
// 不会用上面的办法算最大回撤, VaR, CVaR
|
|
|
t_var = SELECT entity_id, end_date, ret,
|
|
@@ -122,7 +123,7 @@ def cal_basic_performance(entity_info, ret, trailing_month) {
|
|
|
|
|
|
} else if(trailing_month == 'ytd') {
|
|
|
|
|
|
- t1 = SELECT entity_id, end_date, price_date.cummax() AS price_date,
|
|
|
+ t1 = SELECT entity_id, end_date, price_date.cummax() AS price_date, nav, ret,
|
|
|
cumprod(1+ret)-1 AS trailing_ret,
|
|
|
cumprod(1+ret)-1 AS trailing_ret_a, // no need annulization for ytd
|
|
|
iif(cumcount(entity_id) > 5, cumstd(ret), null) AS std_dev,
|
|
@@ -136,12 +137,12 @@ def cal_basic_performance(entity_info, ret, trailing_month) {
|
|
|
|
|
|
// trailing x month
|
|
|
} else {
|
|
|
-
|
|
|
- win = trailing_month$INT;
|
|
|
-
|
|
|
- t1 = SELECT entity_id, end_date, price_date.mmax(win) AS price_date,
|
|
|
+ // 先转成STRING,避免单字符被认为是CHAR而导致转整型出错的结果
|
|
|
+ win = trailing_month$STRING$INT;
|
|
|
+
|
|
|
+ t1 = SELECT entity_id, end_date, price_date.mmax(win) AS price_date, nav, ret,
|
|
|
mprod(1+ret, win)-1 AS trailing_ret,
|
|
|
- iif(trailing_month > 12,
|
|
|
+ iif(win > 12,
|
|
|
mprod(1+ret, win).pow(12\win)-1,
|
|
|
mprod(1+ret, win)-1) AS trailing_ret_a,
|
|
|
mstd(ret, win) AS std_dev,
|
|
@@ -197,7 +198,7 @@ def cal_LPM(ret, risk_free, trailing_month) {
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- win = trailing_month$INT;
|
|
|
+ win = trailing_month$STRING$INT;
|
|
|
lpm = SELECT t.entity_id, t.end_date,
|
|
|
(msum (iif(rfr.ret > t.ret, rfr.ret - t.ret, 0), win) \ mcount(end_date, win)).pow(1\1) AS lpm1,
|
|
|
(msum2(iif(rfr.ret > t.ret, rfr.ret - t.ret, 0), win) \ mcount(end_date, win)).pow(1\2) AS lpm2,
|
|
@@ -253,7 +254,7 @@ def cal_omega_sortino_kappa(ret, risk_free, trailing_month) {
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- win = trailing_month$INT;
|
|
|
+ win = trailing_month$STRING$INT;
|
|
|
|
|
|
tb = SELECT t.entity_id, t.end_date,
|
|
|
l.lpm2 AS ds_dev,
|
|
@@ -325,7 +326,7 @@ def cal_benchmark_tracking(ret, benchmarks, bmk_ret, trailing_month) {
|
|
|
ORDER BY entity_id, end_date, benchmark_id;
|
|
|
} else {
|
|
|
|
|
|
- win = trailing_month$INT;
|
|
|
+ win = trailing_month$STRING$INT;
|
|
|
|
|
|
t0 = SELECT t.entity_id, t.end_date, t.price_date,
|
|
|
t.ret, bmk.ret AS ret_bmk,
|
|
@@ -393,7 +394,7 @@ def cal_alpha_beta(ret, benchmarks, bmk_ret, risk_free, trailing_month) {
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- win = trailing_month$INT;
|
|
|
+ win = trailing_month$STRING$INT;
|
|
|
|
|
|
beta = SELECT entity_id, end_date, benchmark_id,
|
|
|
iif(mcount(end_date, win) > 5, ret.mbeta(ret_bmk, win), null) AS beta
|
|
@@ -458,7 +459,7 @@ def cal_capture_ratio(ret, benchmarks, bmk_ret, trailing_month) {
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- win = trailing_month$INT;
|
|
|
+ win = trailing_month$STRING$INT;
|
|
|
|
|
|
t1 = SELECT t.entity_id, t.end_date,
|
|
|
(1 + iif(bmk.ret >= 0, t.ret, 0)).mprod(win) AS upside_ret,
|
|
@@ -513,7 +514,7 @@ def cal_sharpe(ret, std_dev, risk_free, trailing_month) {
|
|
|
CONTEXT BY t.entity_id, t.end_date.year();
|
|
|
} else {
|
|
|
|
|
|
- win = trailing_month$INT;
|
|
|
+ win = trailing_month$STRING$INT;
|
|
|
|
|
|
sharpe = SELECT t.entity_id, t.end_date, (t.ret - rfr.ret).mavg(win) / std.std_dev AS sharpe
|
|
|
FROM ret t
|
|
@@ -564,7 +565,7 @@ def cal_treynor(ret, risk_free, beta, trailing_month) {
|
|
|
CONTEXT BY t.entity_id, beta.benchmark_id, t.end_date.year();
|
|
|
} else {
|
|
|
|
|
|
- win = trailing_month$INT;
|
|
|
+ win = trailing_month$STRING$INT;
|
|
|
|
|
|
t = SELECT *, mcount(entity_id, win) AS cnt
|
|
|
FROM ret t
|
|
@@ -611,7 +612,7 @@ def cal_jensen(ret, bmk_ret, risk_free, beta, trailing_month) {
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- win = trailing_month$INT;
|
|
|
+ win = trailing_month$STRING$INT;
|
|
|
|
|
|
jensen = SELECT t.entity_id, t.end_date, t.ret.mavg(win) - rfr.ret.mavg(win) - beta.beta * (bmk.ret.mavg(win) - rfr.ret.mavg(win)) AS jensen, beta.benchmark_id
|
|
|
FROM ret t
|
|
@@ -657,7 +658,7 @@ def cal_m2(ret, benchmarks, bmk_ret, risk_free, trailing_month) {
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- win = trailing_month$INT;
|
|
|
+ win = trailing_month$STRING$INT;
|
|
|
|
|
|
m2 = SELECT t.entity_id, t.end_date,
|
|
|
iif(t.entity_id.mcount(win) > 5, (t.ret - rfr.ret).mavg(win) / t.ret.mstd(win) * bmk.ret.mstd(win) + rfr.ret.mavg(win), null) AS m2, bm.benchmark_id
|
|
@@ -686,7 +687,7 @@ def cal_m2(ret, benchmarks, bmk_ret, risk_free, trailing_month) {
|
|
|
*/
|
|
|
def cal_ms_return(ret, risk_free, trailing_month) {
|
|
|
|
|
|
- win = trailing_month$INT;
|
|
|
+ win = trailing_month$STRING$INT;
|
|
|
|
|
|
r = SELECT t.entity_id, t.end_date,
|
|
|
((1 + t.ret)\(1 + rfr.ret)).mprod(win).pow(12\(t.end_date.mmax(win) - t.end_date.mmin(win)))-1 AS ms_ret_a,
|
|
@@ -736,7 +737,7 @@ def get_effective_benchmarks(benchmarks, end_day, trailing_month, isEffectiveOnl
|
|
|
HAVING bmk.end_date.cumcount() >= t.cnt * min_pct;
|
|
|
} else {
|
|
|
|
|
|
- win = trailing_month$INT;
|
|
|
+ win = trailing_month$STRING$INT;
|
|
|
|
|
|
t = SELECT entity_id, end_date, end_date.mcount(win) AS cnt FROM t_dates CONTEXT BY entity_id;
|
|
|
|
|
@@ -900,7 +901,7 @@ def cal_indicators(entity_info, benchmarks, end_day, tb_ret, benchmark_ret, risk
|
|
|
|
|
|
|
|
|
/*
|
|
|
- * Calculate trailing 6m, ytd, 1y, 2y, 3y, 4y, 5y, 10y and since inception datapoints
|
|
|
+ * Calculate trailing 3m, 6m, ytd, 1y, 2y, 3y, 4y, 5y, 10y and since inception datapoints
|
|
|
*
|
|
|
* @param: func <FUNCTION>: the calculation function
|
|
|
* @param: entity_info <TABLE>: basic information of entity, NEED COLUMNS entity_id, inception_date
|
|
@@ -909,14 +910,14 @@ def cal_indicators(entity_info, benchmarks, end_day, tb_ret, benchmark_ret, risk
|
|
|
* @param: ret <TABLE>: 收益表,NEED COLUMNS entity_id, price_dat, end_date, nav
|
|
|
* @param bmk_ret <TABLE>: historical benchmark return table, NEED COLUMNS fund_id, end_date, ret
|
|
|
* @param risk_free <TABLE>: historical risk free rate table, NEED COLUMNS fund_id, end_date, ret
|
|
|
- * @param periods <BOOL VECTOR>: 是否计算的区间向量,分别对应 incep, ytd, 6m, 1y, 2y, 3y, 4y, 5y, 10y
|
|
|
*
|
|
|
*
|
|
|
*/
|
|
|
-def cal_trailing(func, entity_info, benchmarks, end_day, tb_ret, bmk_ret, risk_free_rate) {
|
|
|
+def cal_trailing(func, entity_info, benchmarks, end_day, tb_ret, bmk_ret, risk_free_rate ) {
|
|
|
|
|
|
r_incep = null;
|
|
|
r_ytd = null;
|
|
|
+ r_3m = null;
|
|
|
r_6m = null;
|
|
|
r_1y = null;
|
|
|
r_2y = null;
|
|
@@ -931,6 +932,9 @@ def cal_trailing(func, entity_info, benchmarks, end_day, tb_ret, bmk_ret, risk_f
|
|
|
// ytd
|
|
|
r_ytd = func(entity_info, benchmarks, end_day, tb_ret, bmk_ret, risk_free_rate, 'ytd');
|
|
|
|
|
|
+ // 3m 只需要支持收益计算
|
|
|
+ r_3m = cal_basic_performance(entity_info, tb_ret, '3');
|
|
|
+
|
|
|
// 6m
|
|
|
r_6m = func(entity_info, benchmarks, end_day, tb_ret, bmk_ret, risk_free_rate, '6');
|
|
|
|
|
@@ -952,12 +956,12 @@ def cal_trailing(func, entity_info, benchmarks, end_day, tb_ret, bmk_ret, risk_f
|
|
|
// 10y
|
|
|
r_10y = func(entity_info, benchmarks, end_day, tb_ret, bmk_ret, risk_free_rate, '120');
|
|
|
|
|
|
- return r_incep, r_ytd, r_6m, r_1y, r_2y, r_3y, r_4y, r_5y, r_10y;
|
|
|
+ return r_incep, r_ytd, r_3m, r_6m, r_1y, r_2y, r_3y, r_4y, r_5y, r_10y;
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
- * Calculate trailing ytd, 6m, 1y, 2y, 3y, 4y, 5y, 10y and since inception standard indicators
|
|
|
+ * Calculate trailing ytd, 3m, 6m, 1y, 2y, 3y, 4y, 5y, 10y and since inception standard indicators
|
|
|
*
|
|
|
* @param: entity_info <TABLE>: basic information of entity, NEED COLUMNS entity_id, inception_date
|
|
|
* @param benchmarks <TABLE>: entity-benchmark mapping table
|
|
@@ -974,7 +978,7 @@ def cal_trailing_indicators(entity_info, benchmarks, end_day, tb_ret, bmk_ret, r
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * Calculate trailing 6m, ytd, 1y, 2y, 3y, 4y, 5y, 10y and since inception bfi indicators
|
|
|
+ * Calculate trailing ytd, 3m, 6m, 1y, 2y, 3y, 4y, 5y, 10y and since inception bfi indicators
|
|
|
*
|
|
|
* @param: entity_info <TABLE>: basic information of entity, NEED COLUMNS entity_id, inception_date
|
|
|
* @param benchmarks <TABLE>: entity-benchmark mapping table
|
|
@@ -983,6 +987,8 @@ def cal_trailing_indicators(entity_info, benchmarks, end_day, tb_ret, bmk_ret, r
|
|
|
* @param bmk_ret <TABLE>: historical benchmark return table, NEED COLUMNS fund_id, end_date, ret
|
|
|
* @param risk_free <TABLE>: historical risk free rate table, NEED COLUMNS fund_id, end_date, ret
|
|
|
*
|
|
|
+ * NOTE: 3m 的所有指标没有意义
|
|
|
+ *
|
|
|
*
|
|
|
*/
|
|
|
def cal_trailing_bfi_indicators(entity_info, benchmarks, end_day, tb_ret, bmk_ret, risk_free_rate) {
|
|
@@ -998,7 +1004,7 @@ def cal_trailing_bfi_indicators(entity_info, benchmarks, end_day, tb_ret, bmk_re
|
|
|
* @param indicator_type <STRING>: PBI, BFI
|
|
|
* @param monthly_returns <TABLE>: NEED COLUMN: entity_id, end_date, price_date, nav, ret
|
|
|
*
|
|
|
- * @return <DICT TABLE>: ['PBI-INCEP', 'PBI-YTD', 'PBI-6M', 'PBI-1Y', 'PBI-2Y', 'PBI-3Y', 'PBI-4Y', 'PBI-5Y', 'PBI-10Y', 'MS-3Y', 'MS-5Y', 'MS-10Y']
|
|
|
+ * @return <DICT TABLE>: ['PBI-INCEP', 'PBI-YTD', 'PBI-3M', 'PBI-6M', 'PBI-1Y', 'PBI-2Y', 'PBI-3Y', 'PBI-4Y', 'PBI-5Y', 'PBI-10Y']
|
|
|
*
|
|
|
*/
|
|
|
def cal_monthly_indicators(entity_type, indicator_type, monthly_returns) {
|
|
@@ -1045,13 +1051,13 @@ def cal_monthly_indicators(entity_type, indicator_type, monthly_returns) {
|
|
|
|
|
|
t0 = cal_trailing_bfi_indicators(entity_info, benchmark, end_day, monthly_returns, bmk_ret, risk_free_rate);
|
|
|
|
|
|
- v_table_name = ['BFI-INCEP', 'BFI-YTD', 'BFI-6M', 'BFI-1Y', 'BFI-2Y', 'BFI-3Y', 'BFI-4Y', 'BFI-5Y', 'BFI-10Y'];
|
|
|
+ v_table_name = ['BFI-INCEP', 'BFI-YTD', 'BFI-3M', 'BFI-6M', 'BFI-1Y', 'BFI-2Y', 'BFI-3Y', 'BFI-4Y', 'BFI-5Y', 'BFI-10Y'];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
t0 = cal_trailing_indicators(entity_info, benchmark, end_day, monthly_returns, bmk_ret, risk_free_rate);
|
|
|
|
|
|
- v_table_name = ['PBI-INCEP', 'PBI-YTD', 'PBI-6M', 'PBI-1Y', 'PBI-2Y', 'PBI-3Y', 'PBI-4Y', 'PBI-5Y', 'PBI-10Y'];
|
|
|
+ v_table_name = ['PBI-INCEP', 'PBI-YTD', 'PBI-3M', 'PBI-6M', 'PBI-1Y', 'PBI-2Y', 'PBI-3Y', 'PBI-4Y', 'PBI-5Y', 'PBI-10Y'];
|
|
|
}
|
|
|
|
|
|
return dict(v_table_name, t0);
|
|
@@ -1060,32 +1066,32 @@ def cal_monthly_indicators(entity_type, indicator_type, monthly_returns) {
|
|
|
/*
|
|
|
* Calculate historcial fund trailing indicators
|
|
|
*
|
|
|
- * @param fund_type <STRING>: MF, HF
|
|
|
+ * @param entity_type <STRING>: MF, HF
|
|
|
* @param fund_ids <STRING>: 逗号和单引号分隔的fund_id
|
|
|
* @param end_day <DATE>: 要计算的日期
|
|
|
* @param isFromNav <BOOL>: 用净值实时计算还是从表中取月收益
|
|
|
* @param isFromSQL <BOOL>: TODO: 从MySQL还是本地DolphinDB取净值/收益数据
|
|
|
*
|
|
|
- * @return <DICT TABLE>: ['PBI-INCEP', 'PBI-YTD', 'PBI-6M', 'PBI-1Y', 'PBI-2Y', 'PBI-3Y', 'PBI-4Y', 'PBI-5Y', 'PBI-10Y', 'MS-3Y', 'MS-5Y', 'MS-10Y']
|
|
|
+ * @return <DICT TABLE>: ['PBI-INCEP', 'PBI-YTD', 'PBI-3M', 'PBI-6M', 'PBI-1Y', 'PBI-2Y', 'PBI-3Y', 'PBI-4Y', 'PBI-5Y', 'PBI-10Y', 'MS-3Y', 'MS-5Y', 'MS-10Y']
|
|
|
*
|
|
|
*
|
|
|
* Example: cal_fund_indicators('HF', "'HF000004KN','HF000103EU','HF00018WXG'", 2024.06.28, true);
|
|
|
*
|
|
|
*/
|
|
|
-def cal_fund_indicators(fund_type, fund_ids, end_day, isFromNav) {
|
|
|
+def cal_fund_indicators(entity_type, fund_ids, end_day, isFromNav) {
|
|
|
|
|
|
very_old_date = 1990.01.01;
|
|
|
|
|
|
if(isFromNav == true) {
|
|
|
|
|
|
// 从净值开始计算收益
|
|
|
- tb_ret = SELECT * FROM cal_fund_monthly_returns(fund_type, fund_ids, true) WHERE price_date <= end_day;
|
|
|
+ tb_ret = SELECT * FROM cal_fund_monthly_returns(entity_type, fund_ids, true) WHERE price_date <= end_day;
|
|
|
tb_ret.rename!(['fund_id', 'cumulative_nav'], ['entity_id', 'nav']);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 从fund_performance表里读月收益
|
|
|
- tb_ret = get_monthly_ret(fund_type, fund_ids, very_old_date, end_day, true);
|
|
|
+ tb_ret = get_monthly_ret(entity_type, fund_ids, very_old_date, end_day, true);
|
|
|
|
|
|
v_end_date = tb_ret.end_date.temporalParse('yyyy-MM');
|
|
|
tb_ret.replaceColumn!('end_date', v_end_date);
|
|
@@ -1095,7 +1101,7 @@ def cal_fund_indicators(fund_type, fund_ids, end_day, isFromNav) {
|
|
|
if(tb_ret.isVoid() || tb_ret.size() == 0) { return null; }
|
|
|
|
|
|
// 标准的指标
|
|
|
- d = cal_monthly_indicators(fund_type, 'PBI', tb_ret);
|
|
|
+ d = cal_monthly_indicators(entity_type, 'PBI', tb_ret);
|
|
|
|
|
|
return d;
|
|
|
|
|
@@ -1104,32 +1110,32 @@ def cal_fund_indicators(fund_type, fund_ids, end_day, isFromNav) {
|
|
|
/*
|
|
|
* Calculate historcial fund trailing BFI indicators
|
|
|
*
|
|
|
- * @param fund_type <STRING>: MF, HF
|
|
|
+ * @param entity_type <STRING>: MF, HF
|
|
|
* @param fund_ids <STRING>: 逗号和单引号分隔的fund_id
|
|
|
* @param end_day <DATE>: 要计算的日期
|
|
|
* @param isFromNav <BOOL>: 用净值实时计算还是从表中取月收益
|
|
|
* @param isFromSQL <BOOL>: TODO: 从MySQL还是本地DolphinDB取净值/收益数据
|
|
|
*
|
|
|
- * @return <DICT TABLE>: ['BFI-INCEP', 'BFI-YTD', 'BFI-6M', 'BFI-1Y', 'BFI-2Y', 'BFI-3Y', 'BFI-4Y', 'BFI-5Y', 'BFI-10Y']
|
|
|
+ * @return <DICT TABLE>: ['BFI-INCEP', 'BFI-YTD', 'BFI-3M', 'BFI-6M', 'BFI-1Y', 'BFI-2Y', 'BFI-3Y', 'BFI-4Y', 'BFI-5Y', 'BFI-10Y']
|
|
|
*
|
|
|
*
|
|
|
* Example: cal_fund_bfi_indicators('MF', "'MF00003PW2', 'MF00003PW1', 'MF00003PXO'", 2024.08.31, true);
|
|
|
*
|
|
|
*/
|
|
|
-def cal_fund_bfi_indicators(fund_type, fund_ids, end_day, isFromNav) {
|
|
|
+def cal_fund_bfi_indicators(entity_type, fund_ids, end_day, isFromNav) {
|
|
|
|
|
|
very_old_date = 1990.01.01;
|
|
|
|
|
|
if(isFromNav == true) {
|
|
|
|
|
|
// 从净值开始计算收益
|
|
|
- tb_ret = SELECT * FROM cal_fund_monthly_returns(fund_type, fund_ids, true) WHERE price_date <= end_day;
|
|
|
+ tb_ret = SELECT * FROM cal_fund_monthly_returns(entity_type, fund_ids, true) WHERE price_date <= end_day;
|
|
|
tb_ret.rename!(['fund_id', 'cumulative_nav'], ['entity_id', 'nav']);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 从fund_performance表里读月收益
|
|
|
- tb_ret = get_monthly_ret(fund_type, fund_ids, very_old_date, end_day, true);
|
|
|
+ tb_ret = get_monthly_ret(entity_type, fund_ids, very_old_date, end_day, true);
|
|
|
tb_ret.rename!(['fund_id'], ['entity_id']);
|
|
|
|
|
|
v_end_date = tb_ret.end_date.temporalParse('yyyy-MM');
|
|
@@ -1140,7 +1146,7 @@ def cal_fund_bfi_indicators(fund_type, fund_ids, end_day, isFromNav) {
|
|
|
if(tb_ret.isVoid() || tb_ret.size() == 0) { return null; }
|
|
|
|
|
|
// BFI指标
|
|
|
- d = cal_monthly_indicators(fund_type, 'BFI', tb_ret);
|
|
|
+ d = cal_monthly_indicators(entity_type, 'BFI', tb_ret);
|
|
|
|
|
|
return d;
|
|
|
}
|