WITH latest_capital AS (
SELECT
a.SECU_ID,
a.TRD_DT,
c.LIST_NEGO_SHR_TTL as FLOAT_SHR,
c.TTL_SHR,
c.END_DT,
ROW_NUMBER() OVER (PARTITION BY a.SECU_ID, a.TRD_DT ORDER BY c.END_DT DESC) AS rn
FROM STK_HK_EXCH_QUOT a
JOIN BASE_SECURITY_INFO b ON a.SECU_ID = b.SECURITY_ID
JOIN COO_SHR_STRU c ON b.COMPANY_ID = c.CORP_ID
WHERE c.END_DT <= a.TRD_DT
),
annual_profit AS (
SELECT
b.COMPANY_ID,
a.TRD_DT,
d.NET_PROF,
d.OP_TINC,
d.END_DT,
ROW_NUMBER() OVER (PARTITION BY b.COMPANY_ID, a.TRD_DT ORDER BY d.END_DT DESC) AS rn
FROM STK_HK_EXCH_QUOT a
JOIN BASE_SECURITY_INFO b ON a.SECU_ID = b.SECURITY_ID
JOIN COO_HK_INC_STMT d ON b.COMPANY_ID = d.CORP_ID
WHERE d.END_DT <= a.TRD_DT
AND EXTRACT(MONTH FROM d.END_DT) = 12
AND EXTRACT(DAY FROM d.END_DT) = 31
AND d.DATA_FLAG_CD = 102
AND d.NET_PROF IS NOT NULL AND d.OP_TINC IS NOT NULL
AND d.END_DT <= ADD_MONTHS(TRUNC(a.TRD_DT, 'YEAR') - 1, 0)
),
ttm_net_profit AS (
SELECT
b.COMPANY_ID,
a.TRD_DT,
SUM(d.NET_PROF) AS NET_PROFIT_TTM,
SUM(d.OP_TINC) AS OP_TINC_TTM,
COUNT(DISTINCT d.END_DT) as period_count
FROM STK_HK_EXCH_QUOT a
JOIN BASE_SECURITY_INFO b ON a.SECU_ID = b.SECURITY_ID
JOIN COO_HK_INC_STMT d ON b.COMPANY_ID = d.CORP_ID
WHERE d.END_DT >= ADD_MONTHS(a.TRD_DT, -12)
AND d.END_DT < a.TRD_DT
AND d.DATA_FLAG_CD = 102
AND d.NET_PROF IS NOT NULL AND d.OP_TINC IS NOT NULL
GROUP BY b.COMPANY_ID, a.TRD_DT
HAVING COUNT(DISTINCT d.END_DT) >= 4
),
ttm_OP_CF_NET_AMT (
SELECT
b.COMPANY_ID,
a.TRD_DT,
SUM(d.OP_CF_NET_AMT) AS OP_CF_NET_AMT_TTM,
COUNT(DISTINCT d.END_DT) as period_count
FROM STK_HK_EXCH_QUOT a
JOIN BASE_SECURITY_INFO b ON a.SECU_ID = b.SECURITY_ID
JOIN COO_HK_FIN_DATA d ON b.COMPANY_ID = d.CORP_ID
WHERE d.END_DT >= ADD_MONTHS(a.TRD_DT, -12)
AND d.END_DT < a.TRD_DT
AND d.DATA_FLAG_CD = 102
AND d.OP_CF_NET_AMT IS NOT NULL
GROUP BY b.COMPANY_ID, a.TRD_DT
HAVING COUNT(DISTINCT d.END_DT) >= 4
),
latest_profit AS (
SELECT
b.COMPANY_ID,
a.TRD_DT,
d.NET_PROF,
d.OP_TINC,
d.END_DT,
ROW_NUMBER() OVER (PARTITION BY b.COMPANY_ID, a.TRD_DT ORDER BY d.END_DT DESC) AS rn
FROM STK_HK_EXCH_QUOT a
JOIN BASE_SECURITY_INFO b ON a.SECU_ID = b.SECURITY_ID
JOIN COO_HK_INC_STMT d ON b.COMPANY_ID = d.CORP_ID
WHERE d.END_DT <= a.TRD_DT
AND d.DATA_FLAG_CD = 102
AND d.NET_PROF IS NOT NULL AND d.OP_TINC IS NOT NULL
),
financial_ratios AS (
SELECT
COMPANY_ID,
TRD_DT,
LATEST_PB_END_DT,
PB,
PB_NEW,
LATEST_OP_CF_NET_AMT
FROM (
SELECT
b.COMPANY_ID,
a.TRD_DT,
c.END_DT AS LATEST_PB_END_DT,
CASE WHEN c.BVPS IS NULL OR c.BVPS = 0 THEN 0
ELSE ROUND(a.CPRC/c.BVPS,4)
END AS PB,
CASE WHEN c.BVPS IS NULL OR c.BVPS = 0 THEN 0
ELSE ROUND(a.CPRC/c.BVPS,4)
END AS PB_NEW,
d.OP_CF_NET_AMT AS LATEST_OP_CF_NET_AMT,
ROW_NUMBER() OVER (PARTITION BY c.CORP_ID, a.TRD_DT ORDER BY c.END_DT DESC) AS rn
FROM STK_HK_EXCH_QUOT a
JOIN BASE_SECURITY_INFO b ON a.SECU_ID = b.SECURITY_ID
JOIN COO_FIN_DATA_NAS c ON c.CORP_ID = b.COMPANY_ID
JOIN COO_HK_FIN_DATA d ON c.CORP_ID = d.CORP_ID AND c.END_DT = d.END_DT
AND c.RPT_TYPE_CD = d.RPT_TYPE_CD AND c.DATA_FLAG_CD = d.DATA_FLAG_CD
WHERE c.END_DT <= a.TRD_DT
AND c.DATA_FLAG_CD = 102
AND c.RPT_TYPE_CD IN (1,6,9,12)
AND d.OP_CF_NET_AMT IS NOT NULL
)
WHERE rn = 1
),
period_aggregates AS (
SELECT
a.SECU_ID,
b.COMPANY_ID,
a.TRD_DT,
a.TRD_STS,
a.CPRC,
a.LST_CPRC,
a.HPRC,
a.LPRC,
a.OPRC,
a.TRD_AMT,
a.TRD_VOL,
a.DAY_TUROV_RATE,
TRUNC(a.TRD_DT, 'IW') AS WEEK_START,
TRUNC(a.TRD_DT, 'MM') AS MONTH_START,
TRUNC(a.TRD_DT, 'Q') AS QUARTER_START,
TRUNC(a.TRD_DT, 'YEAR') AS YEAR_START,
CASE
WHEN EXTRACT(MONTH FROM a.TRD_DT) BETWEEN 1 AND 6
THEN TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-01-01', 'YYYY-MM-DD')
ELSE TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-07-01', 'YYYY-MM-DD')
END AS HALF_YEAR_START,
SUM(a.TRD_AMT) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW')) / 10000 AS WEEK_TRD_AMT,
SUM(a.TRD_VOL) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW')) / 10000 AS WEEK_TRD_VOL,
MAX(a.HPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW')) AS WEEK_HIGH,
MIN(a.LPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW')) AS WEEK_LOW,
FIRST_VALUE(a.OPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW') ORDER BY a.TRD_DT) AS WEEK_FIRST_OPEN,
FIRST_VALUE(a.CPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW') ORDER BY a.TRD_DT) AS WEEK_FIRST_CLOSE,
LAST_VALUE(a.CPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW')
ORDER BY a.TRD_DT ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS WEEK_LAST_CLOSE,
COUNT(DISTINCT a.TRD_DT) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW')) AS WEEK_TRADING_DAYS,
SUM(a.TRD_AMT) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'MM')) / 10000 AS MONTH_TRD_AMT,
SUM(a.TRD_VOL) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'MM')) / 10000 AS MONTH_TRD_VOL,
MAX(a.HPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'MM')) AS MONTH_HIGH,
MIN(a.LPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'MM')) AS MONTH_LOW,
FIRST_VALUE(a.OPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'MM') ORDER BY a.TRD_DT) AS MONTH_FIRST_OPEN,
FIRST_VALUE(a.CPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'MM') ORDER BY a.TRD_DT) AS MONTH_FIRST_CLOSE,
LAST_VALUE(a.CPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'MM')
ORDER BY a.TRD_DT ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS MONTH_LAST_CLOSE,
COUNT(DISTINCT a.TRD_DT) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'MM')) AS MONTH_TRADING_DAYS,
SUM(a.TRD_AMT) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'Q')) / 10000 AS QUARTER_TRD_AMT,
SUM(a.TRD_VOL) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'Q')) / 10000 AS QUARTER_TRD_VOL,
MAX(a.HPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'Q')) AS QUARTER_HIGH,
MIN(a.LPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'Q')) AS QUARTER_LOW,
FIRST_VALUE(a.OPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'Q') ORDER BY a.TRD_DT) AS QUARTER_FIRST_OPEN,
FIRST_VALUE(a.CPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'Q') ORDER BY a.TRD_DT) AS QUARTER_FIRST_CLOSE,
LAST_VALUE(a.CPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'Q')
ORDER BY a.TRD_DT ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS QUARTER_LAST_CLOSE,
COUNT(DISTINCT a.TRD_DT) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'Q')) AS QUARTER_TRADING_DAYS,
SUM(a.TRD_AMT) OVER (PARTITION BY a.SECU_ID,
CASE
WHEN EXTRACT(MONTH FROM a.TRD_DT) BETWEEN 1 AND 6
THEN TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-01-01', 'YYYY-MM-DD')
ELSE TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-07-01', 'YYYY-MM-DD')
END) / 10000 AS HALF_YEAR_TRD_AMT,
SUM(a.TRD_VOL) OVER (PARTITION BY a.SECU_ID,
CASE
WHEN EXTRACT(MONTH FROM a.TRD_DT) BETWEEN 1 AND 6
THEN TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-01-01', 'YYYY-MM-DD')
ELSE TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-07-01', 'YYYY-MM-DD')
END) / 10000 AS HALF_YEAR_TRD_VOL,
MAX(a.HPRC) OVER (PARTITION BY a.SECU_ID,
CASE
WHEN EXTRACT(MONTH FROM a.TRD_DT) BETWEEN 1 AND 6
THEN TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-01-01', 'YYYY-MM-DD')
ELSE TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-07-01', 'YYYY-MM-DD')
END) AS HALF_YEAR_HIGH,
MIN(a.LPRC) OVER (PARTITION BY a.SECU_ID,
CASE
WHEN EXTRACT(MONTH FROM a.TRD_DT) BETWEEN 1 AND 6
THEN TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-01-01', 'YYYY-MM-DD')
ELSE TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-07-01', 'YYYY-MM-DD')
END) AS HALF_YEAR_LOW,
FIRST_VALUE(a.OPRC) OVER (PARTITION BY a.SECU_ID,
CASE
WHEN EXTRACT(MONTH FROM a.TRD_DT) BETWEEN 1 AND 6
THEN TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-01-01', 'YYYY-MM-DD')
ELSE TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-07-01', 'YYYY-MM-DD')
END ORDER BY a.TRD_DT) AS HALF_YEAR_FIRST_OPEN,
FIRST_VALUE(a.CPRC) OVER (PARTITION BY a.SECU_ID,
CASE
WHEN EXTRACT(MONTH FROM a.TRD_DT) BETWEEN 1 AND 6
THEN TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-01-01', 'YYYY-MM-DD')
ELSE TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-07-01', 'YYYY-MM-DD')
END ORDER BY a.TRD_DT) AS HALF_YEAR_FIRST_CLOSE,
LAST_VALUE(a.CPRC) OVER (PARTITION BY a.SECU_ID,
CASE
WHEN EXTRACT(MONTH FROM a.TRD_DT) BETWEEN 1 AND 6
THEN TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-01-01', 'YYYY-MM-DD')
ELSE TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-07-01', 'YYYY-MM-DD')
END ORDER BY a.TRD_DT ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS HALF_YEAR_LAST_CLOSE,
COUNT(DISTINCT a.TRD_DT) OVER (PARTITION BY a.SECU_ID,
CASE
WHEN EXTRACT(MONTH FROM a.TRD_DT) BETWEEN 1 AND 6
THEN TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-01-01', 'YYYY-MM-DD')
ELSE TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-07-01', 'YYYY-MM-DD')
END) AS HALF_YEAR_TRADING_DAYS,
SUM(a.TRD_AMT) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'YEAR')) / 10000 AS YEAR_TRD_AMT,
SUM(a.TRD_VOL) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'YEAR')) / 10000 AS YEAR_TRD_VOL,
MAX(a.HPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'YEAR')) AS YEAR_HIGH,
MIN(a.LPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'YEAR')) AS YEAR_LOW,
FIRST_VALUE(a.OPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'YEAR') ORDER BY a.TRD_DT) AS YEAR_FIRST_OPEN,
FIRST_VALUE(a.CPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'YEAR') ORDER BY a.TRD_DT) AS YEAR_FIRST_CLOSE,
LAST_VALUE(a.CPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'YEAR')
ORDER BY a.TRD_DT ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS YEAR_LAST_CLOSE,
COUNT(DISTINCT a.TRD_DT) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'YEAR')) AS YEAR_TRADING_DAYS
FROM STK_HK_EXCH_QUOT a
JOIN BASE_SECURITY_INFO b ON a.SECU_ID = b.SECURITY_ID
WHERE a.TRD_DT > v_min_trd_dt
),
period_calculations AS (
SELECT
pa.*,
lc.TTL_SHR,
(pa.CPRC - pa.LST_CPRC) AS DAY_CHG,
CASE WHEN pa.LST_CPRC = 0 THEN 0
ELSE (pa.CPRC - pa.LST_CPRC) * 100.0 / pa.LST_CPRC
END AS DAY_CHG_RAT,
pa.CPRC * lc.TTL_SHR AS TMKT_VAL,
CASE WHEN pa.DAY_TUROV_RATE = 0 THEN 0
ELSE pa.CPRC * (pa.TRD_VOL * 100.0 / pa.DAY_TUROV_RATE)
END AS FMKT_VAL,
CASE WHEN pa.DAY_TUROV_RATE = 0 THEN 0
ELSE pa.TRD_AMT * 100.0 / (pa.CPRC * (pa.TRD_VOL * 100.0 / pa.DAY_TUROV_RATE))
END AS TUROV_RATE,
CASE WHEN pa.LST_CPRC = 0 THEN 0
ELSE (pa.HPRC - pa.LPRC) * 100.0 / pa.LST_CPRC
END AS DAY_AMP,
ROUND(pa.WEEK_TRD_VOL * 10000 / NULLIF(lc.FLOAT_SHR, 0) * 100, 4) AS WEEK_TURNOVER_RATE,
ROUND((pa.WEEK_LAST_CLOSE - LAG(pa.WEEK_LAST_CLOSE) OVER (
PARTITION BY pa.SECU_ID ORDER BY pa.WEEK_START)) * 100.0 /
NULLIF(LAG(pa.WEEK_LAST_CLOSE) OVER (
PARTITION BY pa.SECU_ID ORDER BY pa.WEEK_START), 0), 4) AS WEEK_CHG_RAT,
CASE WHEN pa.WEEK_FIRST_OPEN IS NULL OR pa.WEEK_FIRST_OPEN = 0 THEN NULL
ELSE ROUND((pa.WEEK_HIGH - pa.WEEK_LOW) * 100.0 / pa.WEEK_FIRST_OPEN, 4)
END AS WEEK_AMPLITUDE,
(pa.WEEK_LAST_CLOSE - LAG(pa.WEEK_LAST_CLOSE) OVER (
PARTITION BY pa.SECU_ID ORDER BY pa.WEEK_START)) AS WEEK_CHG,
CASE WHEN pa.WEEK_TRADING_DAYS = 0 THEN NULL
ELSE ROUND(pa.WEEK_TRD_AMT / pa.WEEK_TRADING_DAYS, 4)
END AS WEEK_AVG_TRD_AMT,
CASE WHEN pa.WEEK_TRADING_DAYS = 0 THEN NULL
ELSE ROUND(pa.WEEK_TRD_VOL * 10000 / NULLIF(lc.FLOAT_SHR, 0) * 100 / pa.WEEK_TRADING_DAYS, 4)
END AS W_AVG_TURNOVER_RATE,
ROUND(pa.MONTH_TRD_VOL * 10000 / NULLIF(lc.FLOAT_SHR, 0) * 100, 4) AS MONTH_TURNOVER_RATE,
ROUND((pa.MONTH_LAST_CLOSE - LAG(pa.MONTH_LAST_CLOSE) OVER (
PARTITION BY pa.SECU_ID ORDER BY pa.MONTH_START)) * 100.0 /
NULLIF(LAG(pa.MONTH_LAST_CLOSE) OVER (
PARTITION BY pa.SECU_ID ORDER BY pa.MONTH_START), 0), 4) AS MONTH_CHG_RAT,
CASE WHEN pa.MONTH_FIRST_OPEN IS NULL OR pa.MONTH_FIRST_OPEN = 0 THEN NULL
ELSE ROUND((pa.MONTH_HIGH - pa.MONTH_LOW) * 100.0 / pa.MONTH_FIRST_OPEN, 4)
END AS MONTH_AMPLITUDE,
(pa.MONTH_LAST_CLOSE - LAG(pa.MONTH_LAST_CLOSE) OVER (
PARTITION BY pa.SECU_ID ORDER BY pa.MONTH_START)) AS MONTH_CHG,
CASE WHEN pa.MONTH_TRADING_DAYS = 0 THEN NULL
ELSE ROUND(pa.MONTH_TRD_AMT / pa.MONTH_TRADING_DAYS, 4)
END AS MONTH_AVG_TRD_AMT,
CASE WHEN pa.MONTH_TRADING_DAYS = 0 THEN NULL
ELSE ROUND(pa.MONTH_TRD_VOL * 10000 / NULLIF(lc.FLOAT_SHR, 0) * 100 / pa.MONTH_TRADING_DAYS, 4)
END AS M_AVG_TURNOVER_RATE,
ROUND(pa.QUARTER_TRD_VOL * 10000 / NULLIF(lc.FLOAT_SHR, 0) * 100, 4) AS QUARTER_TURNOVER_RATE,
ROUND((pa.QUARTER_LAST_CLOSE - LAG(pa.QUARTER_LAST_CLOSE) OVER (
PARTITION BY pa.SECU_ID ORDER BY pa.QUARTER_START)) * 100.0 /
NULLIF(LAG(pa.QUARTER_LAST_CLOSE) OVER (
PARTITION BY pa.SECU_ID ORDER BY pa.QUARTER_START), 0), 4) AS QUARTER_CHG_RAT,
CASE WHEN pa.QUARTER_FIRST_OPEN IS NULL OR pa.QUARTER_FIRST_OPEN = 0 THEN NULL
ELSE ROUND((pa.QUARTER_HIGH - pa.QUARTER_LOW) * 100.0 / pa.QUARTER_FIRST_OPEN, 4)
END AS QUARTER_AMPLITUDE,
(pa.QUARTER_LAST_CLOSE - LAG(pa.QUARTER_LAST_CLOSE) OVER (
PARTITION BY pa.SECU_ID ORDER BY pa.QUARTER_START)) AS QUARTER_CHG,
CASE WHEN pa.QUARTER_TRADING_DAYS = 0 THEN NULL
ELSE ROUND(pa.QUARTER_TRD_AMT / pa.QUARTER_TRADING_DAYS, 4)
END AS QUARTER_AVG_TRD_AMT,
CASE WHEN pa.QUARTER_TRADING_DAYS = 0 THEN NULL
ELSE ROUND(pa.QUARTER_TRD_VOL * 10000 / NULLIF(lc.FLOAT_SHR, 0) * 100 / pa.QUARTER_TRADING_DAYS, 4)
END AS Q_AVG_TURNOVER_RATE,
ROUND(pa.HALF_YEAR_TRD_VOL * 10000 / NULLIF(lc.FLOAT_SHR, 0) * 100, 4) AS H_Y_TURNOVER_RATE,
ROUND((pa.HALF_YEAR_LAST_CLOSE - LAG(pa.HALF_YEAR_LAST_CLOSE) OVER (
PARTITION BY pa.SECU_ID ORDER BY pa.HALF_YEAR_START)) * 100.0 /
NULLIF(LAG(pa.HALF_YEAR_LAST_CLOSE) OVER (
PARTITION BY pa.SECU_ID ORDER BY pa.HALF_YEAR_START), 0), 4) AS HALF_YEAR_CHG_RAT,
CASE WHEN pa.HALF_YEAR_FIRST_OPEN IS NULL OR pa.HALF_YEAR_FIRST_OPEN = 0 THEN NULL
ELSE ROUND((pa.HALF_YEAR_HIGH - pa.HALF_YEAR_LOW) * 100.0 / pa.HALF_YEAR_FIRST_OPEN, 4)
END AS HALF_YEAR_AMPLITUDE,
(pa.HALF_YEAR_LAST_CLOSE - LAG(pa.HALF_YEAR_LAST_CLOSE) OVER (
PARTITION BY pa.SECU_ID ORDER BY pa.HALF_YEAR_START)) AS HALF_YEAR_CHG,
CASE WHEN pa.HALF_YEAR_TRADING_DAYS = 0 THEN NULL
ELSE ROUND(pa.HALF_YEAR_TRD_AMT / pa.HALF_YEAR_TRADING_DAYS, 4)
END AS H_Y_AVG_TRD_AMT,
CASE WHEN pa.HALF_YEAR_TRADING_DAYS = 0 THEN NULL
ELSE ROUND(pa.HALF_YEAR_TRD_VOL * 10000 / NULLIF(lc.FLOAT_SHR, 0) * 100 / pa.HALF_YEAR_TRADING_DAYS, 4)
END AS H_Y_AVG_TURNOVER_RATE,
ROUND(pa.YEAR_TRD_VOL * 10000 / NULLIF(lc.FLOAT_SHR, 0) * 100, 4) AS YEAR_TURNOVER_RATE,
ROUND((pa.YEAR_LAST_CLOSE - LAG(pa.YEAR_LAST_CLOSE) OVER (
PARTITION BY pa.SECU_ID ORDER BY pa.YEAR_START)) * 100.0 /
NULLIF(LAG(pa.YEAR_LAST_CLOSE) OVER (
PARTITION BY pa.SECU_ID ORDER BY pa.YEAR_START), 0), 4) AS YEAR_CHG_RAT,
CASE WHEN pa.YEAR_FIRST_OPEN IS NULL OR pa.YEAR_FIRST_OPEN = 0 THEN NULL
ELSE ROUND((pa.YEAR_HIGH - pa.YEAR_LOW) * 100.0 / pa.YEAR_FIRST_OPEN, 4)
END AS YEAR_AMPLITUDE,
(pa.YEAR_LAST_CLOSE - LAG(pa.YEAR_LAST_CLOSE) OVER (
PARTITION BY pa.SECU_ID ORDER BY pa.YEAR_START)) AS YEAR_CHG,
CASE WHEN pa.YEAR_TRADING_DAYS = 0 THEN NULL
ELSE ROUND(pa.YEAR_TRD_AMT / pa.YEAR_TRADING_DAYS, 4)
END AS YEAR_AVG_TRD_AMT,
CASE WHEN pa.YEAR_TRADING_DAYS = 0 THEN NULL
ELSE ROUND(pa.YEAR_TRD_VOL * 10000 / NULLIF(lc.FLOAT_SHR, 0) * 100 / pa.YEAR_TRADING_DAYS, 4)
END AS Y_AVG_TURNOVER_RATE
FROM period_aggregates pa
LEFT JOIN latest_capital lc ON pa.SECU_ID = lc.SECU_ID
AND pa.TRD_DT = lc.TRD_DT AND lc.rn = 1
),
cumulative_data AS (
SELECT
a.SECU_ID,
b.COMPANY_ID,
a.TRD_DT,
TRUNC(a.TRD_DT, 'IW') AS WEEK_START,
SUM(a.TRD_AMT) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW') ORDER BY a.TRD_DT) / 10000 AS CUM_TRD_AMT,
SUM(a.TRD_VOL) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW') ORDER BY a.TRD_DT) / 10000 AS CUM_TRD_VOL,
MAX(a.HPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW')) AS CUM_HIGH,
MIN(a.LPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW')) AS CUM_LOW,
FIRST_VALUE(a.OPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW') ORDER BY a.TRD_DT) AS WEEK_FIRST_OPEN,
FIRST_VALUE(a.CPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW') ORDER BY a.TRD_DT) AS WEEK_FIRST_CLOSE,
a.CPRC AS CURRENT_CLOSE,
ROW_NUMBER() OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW') ORDER BY a.TRD_DT) AS DAYS_IN_WEEK,
TRUNC(a.TRD_DT, 'MM') AS MONTH_START,
SUM(a.TRD_AMT) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'MM') ORDER BY a.TRD_DT) / 10000 AS CUM_MONTH_TRD_AMT,
SUM(a.TRD_VOL) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'MM') ORDER BY a.TRD_DT) / 10000 AS CUM_MONTH_TRD_VOL,
MAX(a.HPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'MM')) AS CUM_MONTH_HIGH,
MIN(a.LPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'MM')) AS CUM_MONTH_LOW,
FIRST_VALUE(a.OPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'MM') ORDER BY a.TRD_DT) AS MONTH_FIRST_OPEN,
FIRST_VALUE(a.CPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'MM') ORDER BY a.TRD_DT) AS MONTH_FIRST_CLOSE,
a.CPRC AS CURRENT_MONTH_CLOSE,
ROW_NUMBER() OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'MM') ORDER BY a.TRD_DT) AS DAYS_IN_MONTH,
TRUNC(a.TRD_DT, 'YEAR') AS YEAR_START,
SUM(a.TRD_AMT) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'YEAR') ORDER BY a.TRD_DT) / 10000 AS CUM_YEAR_TRD_AMT,
SUM(a.TRD_VOL) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'YEAR') ORDER BY a.TRD_DT) / 10000 AS CUM_YEAR_TRD_VOL,
MAX(a.HPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'YEAR')) AS CUM_YEAR_HIGH,
MIN(a.LPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'YEAR')) AS CUM_YEAR_LOW,
FIRST_VALUE(a.OPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'YEAR') ORDER BY a.TRD_DT) AS YEAR_FIRST_OPEN,
FIRST_VALUE(a.CPRC) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'YEAR') ORDER BY a.TRD_DT) AS YEAR_FIRST_CLOSE,
a.CPRC AS CURRENT_YEAR_CLOSE,
ROW_NUMBER() OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'YEAR') ORDER BY a.TRD_DT) AS DAYS_IN_YEAR
FROM STK_HK_EXCH_QUOT a
JOIN BASE_SECURITY_INFO b ON a.SECU_ID = b.SECURITY_ID
WHERE a.TRD_DT > v_min_trd_dt
),
base_data AS (
SELECT
pc.SECU_ID,
pc.COMPANY_ID,
pc.TRD_DT,
pc.TRD_STS,
pc.CPRC,
pc.DAY_CHG,
pc.DAY_CHG_RAT,
pc.TMKT_VAL,
pc.FMKT_VAL,
pc.TUROV_RATE,
pc.DAY_AMP,
ap.NET_PROF AS LAST_ANNUAL_PROFIT,
ap.OP_TINC AS LAST_ANNUAL_INC,
ap.END_DT AS ANNUAL_PROFIT_END_DT,
lp.NET_PROF AS LATEST_NET_PROFIT,
lp.OP_TINC AS LATEST_OP_TINC,
lp.END_DT AS LATEST_PROFIT_END_DT,
tnp.NET_PROFIT_TTM,
tnp.OP_TINC_TTM,
toc.OP_CF_NET_AMT_TTM,
fr.PB,
fr.PB_NEW,
CASE WHEN fr.LATEST_OP_CF_NET_AMT IS NOT NULL AND fr.LATEST_OP_CF_NET_AMT > 0
THEN pc.CPRC * pc.TTL_SHR / fr.LATEST_OP_CF_NET_AMT
ELSE NULL
END AS PC,
CASE WHEN fr.LATEST_OP_CF_NET_AMT IS NULL OR fr.LATEST_OP_CF_NET_AMT <= 0 THEN NULL
WHEN EXTRACT(MONTH FROM fr.LATEST_PB_END_DT) = 12
AND EXTRACT(DAY FROM fr.LATEST_PB_END_DT) = 31 THEN pc.CPRC * pc.TTL_SHR / fr.LATEST_OP_CF_NET_AMT
WHEN EXTRACT(MONTH FROM fr.LATEST_PB_END_DT) = 9
AND EXTRACT(DAY FROM fr.LATEST_PB_END_DT) = 30 THEN pc.CPRC * pc.TTL_SHR / (fr.LATEST_OP_CF_NET_AMT / 3.0 * 4.0)
WHEN EXTRACT(MONTH FROM fr.LATEST_PB_END_DT) = 6
AND EXTRACT(DAY FROM fr.LATEST_PB_END_DT) = 30 THEN pc.CPRC * pc.TTL_SHR / (fr.LATEST_OP_CF_NET_AMT * 2.0)
WHEN EXTRACT(MONTH FROM fr.LATEST_PB_END_DT) = 3
AND EXTRACT(DAY FROM fr.LATEST_PB_END_DT) = 31 THEN pc.CPRC * pc.TTL_SHR / (fr.LATEST_OP_CF_NET_AMT * 4.0)
ELSE NULL
END AS PC_DYM,
pc.MONTH_TRD_AMT,
pc.MONTH_TRD_VOL,
pc.MONTH_HIGH,
pc.MONTH_LOW,
pc.MONTH_CHG_RAT,
pc.MONTH_TURNOVER_RATE,
pc.MONTH_AMPLITUDE,
pc.MONTH_CHG,
pc.MONTH_AVG_TRD_AMT,
pc.M_AVG_TURNOVER_RATE,
pc.QUARTER_TRD_AMT,
pc.QUARTER_TRD_VOL,
pc.QUARTER_HIGH,
pc.QUARTER_LOW,
pc.QUARTER_CHG_RAT,
pc.QUARTER_TURNOVER_RATE,
pc.QUARTER_AMPLITUDE,
pc.QUARTER_CHG,
pc.QUARTER_AVG_TRD_AMT,
pc.Q_AVG_TURNOVER_RATE,
pc.HALF_YEAR_TRD_AMT,
pc.HALF_YEAR_TRD_VOL,
pc.HALF_YEAR_HIGH,
pc.HALF_YEAR_LOW,
pc.HALF_YEAR_CHG_RAT,
pc.H_Y_TURNOVER_RATE,
pc.HALF_YEAR_AMPLITUDE,
pc.HALF_YEAR_CHG,
pc.H_Y_AVG_TRD_AMT,
pc.H_Y_AVG_TURNOVER_RATE,
pc.YEAR_TRD_AMT,
pc.YEAR_TRD_VOL,
pc.YEAR_HIGH,
pc.YEAR_LOW,
pc.YEAR_CHG_RAT,
pc.YEAR_TURNOVER_RATE,
pc.YEAR_AMPLITUDE,
pc.YEAR_CHG,
pc.YEAR_AVG_TRD_AMT,
pc.Y_AVG_TURNOVER_RATE,
pc.WEEK_TRD_AMT,
pc.WEEK_TRD_VOL,
pc.WEEK_HIGH,
pc.WEEK_LOW,
pc.WEEK_CHG_RAT,
pc.WEEK_TURNOVER_RATE,
pc.WEEK_AMPLITUDE,
pc.WEEK_AVG_TRD_AMT,
pc.W_AVG_TURNOVER_RATE,
pc.WEEK_CHG,
cd.CUM_TRD_AMT AS WEEK_TO_DATE_TRD_AMT,
cd.CUM_TRD_VOL AS WEEK_TO_DATE_TRD_VOL,
cd.CUM_HIGH AS WEEK_TO_DATE_HIGH,
cd.CUM_LOW AS WEEK_TO_DATE_LOW,
cd.WEEK_FIRST_OPEN,
cd.WEEK_FIRST_CLOSE,
cd.CURRENT_CLOSE,
cd.DAYS_IN_WEEK,
cd.CUM_MONTH_TRD_AMT,
cd.CUM_MONTH_TRD_VOL,
cd.CUM_MONTH_HIGH,
cd.CUM_MONTH_LOW,
cd.MONTH_FIRST_OPEN,
cd.MONTH_FIRST_CLOSE,
cd.CURRENT_MONTH_CLOSE,
cd.DAYS_IN_MONTH,
cd.CUM_YEAR_TRD_AMT,
cd.CUM_YEAR_TRD_VOL,
cd.CUM_YEAR_HIGH,
cd.CUM_YEAR_LOW,
cd.YEAR_FIRST_OPEN,
cd.YEAR_FIRST_CLOSE,
cd.CURRENT_YEAR_CLOSE,
cd.DAYS_IN_YEAR
FROM period_calculations pc
LEFT JOIN annual_profit ap ON pc.COMPANY_ID = ap.COMPANY_ID AND pc.TRD_DT = ap.TRD_DT AND ap.rn = 1
LEFT JOIN latest_profit lp ON pc.COMPANY_ID = lp.COMPANY_ID AND pc.TRD_DT = lp.TRD_DT AND lp.rn = 1
LEFT JOIN ttm_net_profit tnp ON pc.COMPANY_ID = tnp.COMPANY_ID AND pc.TRD_DT = tnp.TRD_DT
LEFT JOIN ttm_OP_CF_NET_AMT toc on pc.COMPANY_ID = toc.COMPANY_ID AND pc.TRD_DT = toc.TRD_DT
LEFT JOIN financial_ratios fr ON pc.COMPANY_ID = fr.COMPANY_ID AND pc.TRD_DT = fr.TRD_DT
LEFT JOIN cumulative_data cd ON pc.SECU_ID = cd.SECU_ID AND pc.TRD_DT = cd.TRD_DT
)
SELECT
SECU_ID,
COMPANY_ID,
TRD_DT,
TRD_STS,
CPRC,
DAY_CHG,
DAY_CHG_RAT,
TMKT_VAL,
FMKT_VAL,
TUROV_RATE,
DAY_AMP,
LAST_ANNUAL_PROFIT,
LAST_ANNUAL_INC,
ANNUAL_PROFIT_END_DT,
LATEST_NET_PROFIT,
LATEST_OP_TINC,
LATEST_PROFIT_END_DT,
NET_PROFIT_TTM,
OP_TINC_TTM,
OP_CF_NET_AMT_TTM,
PB,
PB_NEW,
PC,
PC_DYM,
CASE WHEN LAST_ANNUAL_PROFIT > 0
THEN TMKT_VAL / LAST_ANNUAL_PROFIT
ELSE NULL
END AS PE,
CASE WHEN LAST_ANNUAL_INC > 0
THEN TMKT_VAL / LAST_ANNUAL_INC
ELSE NULL
END AS PS,
CASE WHEN LATEST_NET_PROFIT IS NULL OR LATEST_NET_PROFIT <= 0 THEN NULL
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 12
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 31 THEN TMKT_VAL / LATEST_NET_PROFIT
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 9
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 30 THEN TMKT_VAL / (LATEST_NET_PROFIT / 3.0 * 4.0)
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 6
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 30 THEN TMKT_VAL / (LATEST_NET_PROFIT * 2.0)
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 3
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 31 THEN TMKT_VAL / (LATEST_NET_PROFIT * 4.0)
ELSE NULL
END AS PE_DYM,
CASE WHEN LATEST_OP_TINC IS NULL OR LATEST_OP_TINC <= 0 THEN NULL
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 12
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 31 THEN TMKT_VAL / LATEST_OP_TINC
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 9
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 30 THEN TMKT_VAL / (LATEST_OP_TINC / 3.0 * 4.0)
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 6
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 30 THEN TMKT_VAL / (LATEST_OP_TINC * 2.0)
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 3
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 31 THEN TMKT_VAL / (LATEST_OP_TINC * 4.0)
ELSE NULL
END AS PS_DYM,
CASE WHEN OP_CF_NET_AMT_TTM > 0
THEN TMKT_VAL / OP_CF_NET_AMT_TTM
ELSE NULL
END AS PCTTM,
CASE WHEN NET_PROFIT_TTM > 0
THEN TMKT_VAL / NET_PROFIT_TTM
ELSE NULL
END AS PETTM,
CASE WHEN OP_TINC_TTM > 0
THEN TMKT_VAL / OP_TINC_TTM
ELSE NULL
END AS PSTTM,
MONTH_TRD_AMT AS TRD_AMT_RM,
MONTH_TRD_VOL AS TRD_VOL_RM,
MONTH_HIGH AS HPRC_RM,
MONTH_LOW AS LPRC_RM,
MONTH_CHG_RAT AS CHG_RAT_RM,
MONTH_TURNOVER_RATE AS TUROV_RATE_RM,
MONTH_AMPLITUDE AS AMP_RAT_RM,
MONTH_CHG AS CHG_RM,
MONTH_AVG_TRD_AMT AS TRD_AMT_AVG_RM,
M_AVG_TURNOVER_RATE AS TUROV_RATE_AVG_RM,
QUARTER_TRD_AMT AS TRD_AMT_R3M,
QUARTER_TRD_VOL AS TRD_VOL_R3M,
QUARTER_HIGH AS HPRC_R3M,
QUARTER_LOW AS LPRC_R3M,
QUARTER_CHG_RAT AS CHG_RAT_R3M,
QUARTER_TURNOVER_RATE AS TUROV_RATE_R3M,
QUARTER_AMPLITUDE AS AMP_RAT_R3M,
QUARTER_CHG AS CHG_R3M,
QUARTER_AVG_TRD_AMT AS TRD_AMT_AVG_R3M,
Q_AVG_TURNOVER_RATE AS TUROV_RATE_AVG_R3M,
HALF_YEAR_TRD_AMT AS TRD_AMT_R6M,
HALF_YEAR_TRD_VOL AS TRD_VOL_R6M,
HALF_YEAR_HIGH AS HPRC_R6M,
HALF_YEAR_LOW AS LPRC_R6M,
HALF_YEAR_CHG_RAT AS CHG_RAT_R6M,
H_Y_TURNOVER_RATE AS TUROV_RATE_R6M,
HALF_YEAR_AMPLITUDE AS AMP_RAT_R6M,
HALF_YEAR_CHG AS CHG_R6M,
H_Y_AVG_TRD_AMT AS TRD_AMT_AVG_R6M,
H_Y_AVG_TURNOVER_RATE AS TUROV_RATE_AVG_R6M,
YEAR_TRD_AMT AS TRD_AMT_RY,
YEAR_TRD_VOL AS TRD_VOL_RY,
YEAR_HIGH AS HPRC_RY,
YEAR_LOW AS LPRC_RY,
YEAR_CHG_RAT AS CHG_RAT_RY,
YEAR_TURNOVER_RATE AS TUROV_RATE_RY,
YEAR_AMPLITUDE AS AMP_RAT_RY,
YEAR_CHG AS CHG_RY,
YEAR_AVG_TRD_AMT AS TRD_AMT_AVG_RY,
Y_AVG_TURNOVER_RATE AS TUROV_RATE_AVG_RY,
WEEK_TRD_AMT AS TRD_AMT_RW,
WEEK_TRD_VOL AS TRD_VOL_RW,
WEEK_HIGH AS HPRC_RW,
WEEK_LOW AS LPRC_RW,
WEEK_CHG_RAT AS CHG_RAT_RW,
WEEK_TURNOVER_RATE AS TUROV_RATE_RW,
WEEK_AMPLITUDE AS AMP_RAT_RW,
WEEK_AVG_TRD_AMT AS TRD_AMT_AVG_RW,
W_AVG_TURNOVER_RATE AS TUROV_RATE_AVG_RW,
WEEK_CHG AS CHG_RW,
WEEK_TO_DATE_TRD_AMT AS TRD_AMT_TW,
WEEK_TO_DATE_TRD_VOL AS TRD_VOL_TW,
WEEK_TO_DATE_HIGH AS HPRC_TW,
WEEK_TO_DATE_LOW AS LPRC_TW,
CASE WHEN WEEK_FIRST_CLOSE IS NULL OR WEEK_FIRST_CLOSE = 0 THEN NULL
ELSE ROUND((CURRENT_CLOSE - WEEK_FIRST_CLOSE) * 100.0 / WEEK_FIRST_CLOSE, 4)
END AS CHG_RAT_TW,
ROUND(WEEK_TO_DATE_TRD_VOL * 10000 / NULLIF((SELECT lc2.FLOAT_SHR FROM latest_capital lc2
WHERE lc2.SECU_ID = base_data.SECU_ID AND lc2.TRD_DT = base_data.TRD_DT AND lc2.rn = 1), 0) * 100, 4) AS TUROV_RATE_TW,
CASE WHEN WEEK_FIRST_OPEN IS NULL OR WEEK_FIRST_OPEN = 0 THEN NULL
ELSE ROUND((WEEK_TO_DATE_HIGH - WEEK_TO_DATE_LOW) * 100.0 / WEEK_FIRST_OPEN, 4)
END AS AMP_RAT_TW,
(CURRENT_CLOSE - WEEK_FIRST_CLOSE) AS CHG_TW,
CASE WHEN DAYS_IN_WEEK = 0 THEN NULL
ELSE ROUND(WEEK_TO_DATE_TRD_AMT / DAYS_IN_WEEK, 4)
END AS TRD_AMT_AVG_TW,
CASE WHEN DAYS_IN_WEEK = 0 THEN NULL
ELSE ROUND(WEEK_TO_DATE_TRD_VOL * 10000 / NULLIF((SELECT lc2.FLOAT_SHR FROM latest_capital lc2
WHERE lc2.SECU_ID = base_data.SECU_ID AND lc2.TRD_DT = base_data.TRD_DT AND lc2.rn = 1), 0) * 100 / DAYS_IN_WEEK, 4)
END AS TUROV_RATE_AVG_TW,
CUM_MONTH_TRD_AMT AS TRD_AMT_TM,
CUM_MONTH_TRD_VOL AS TRD_VOL_TM,
CUM_MONTH_HIGH AS HPRC_TM,
CUM_MONTH_LOW AS LPRC_TM,
CASE WHEN MONTH_FIRST_CLOSE IS NULL OR MONTH_FIRST_CLOSE = 0 THEN NULL
ELSE ROUND((CURRENT_MONTH_CLOSE - MONTH_FIRST_CLOSE) * 100.0 / MONTH_FIRST_CLOSE, 4)
END AS CHG_RAT_TM,
ROUND(CUM_MONTH_TRD_VOL * 10000 / NULLIF((SELECT lc2.FLOAT_SHR FROM latest_capital lc2
WHERE lc2.SECU_ID = base_data.SECU_ID AND lc2.TRD_DT = base_data.TRD_DT AND lc2.rn = 1), 0) * 100, 4) AS TUROV_RATE_TM,
CASE WHEN MONTH_FIRST_OPEN IS NULL OR MONTH_FIRST_OPEN = 0 THEN NULL
ELSE ROUND((CUM_MONTH_HIGH - CUM_MONTH_LOW) * 100.0 / MONTH_FIRST_OPEN, 4)
END AS AMP_RAT_TM,
(CURRENT_MONTH_CLOSE - MONTH_FIRST_CLOSE) AS CHG_TM,
CASE WHEN DAYS_IN_MONTH = 0 THEN NULL
ELSE ROUND(CUM_MONTH_TRD_AMT / DAYS_IN_MONTH, 4)
END AS TRD_AMT_AVG_TM,
CASE WHEN DAYS_IN_MONTH = 0 THEN NULL
ELSE ROUND(CUM_MONTH_TRD_VOL * 10000 / NULLIF((SELECT lc2.FLOAT_SHR FROM latest_capital lc2
WHERE lc2.SECU_ID = base_data.SECU_ID AND lc2.TRD_DT = base_data.TRD_DT AND lc2.rn = 1), 0) * 100 / DAYS_IN_MONTH, 4)
END AS TUROV_RATE_AVG_TM,
CUM_YEAR_TRD_AMT AS TRD_AMT_TY,
CUM_YEAR_TRD_VOL AS TRD_VOL_TY,
CUM_YEAR_HIGH AS HPRC_TY,
CUM_YEAR_LOW AS LPRC_TY,
CASE WHEN YEAR_FIRST_CLOSE IS NULL OR YEAR_FIRST_CLOSE = 0 THEN NULL
ELSE ROUND((CURRENT_YEAR_CLOSE - YEAR_FIRST_CLOSE) * 100.0 / YEAR_FIRST_CLOSE, 4)
END AS CHG_RAT_TY,
ROUND(CUM_YEAR_TRD_VOL * 10000 / NULLIF((SELECT lc2.FLOAT_SHR FROM latest_capital lc2
WHERE lc2.SECU_ID = base_data.SECU_ID AND lc2.TRD_DT = base_data.TRD_DT AND lc2.rn = 1), 0) * 100, 4) AS TUROV_RATE_TY,
CASE WHEN YEAR_FIRST_OPEN IS NULL OR YEAR_FIRST_OPEN = 0 THEN NULL
ELSE ROUND((CUM_YEAR_HIGH - CUM_YEAR_LOW) * 100.0 / YEAR_FIRST_OPEN, 4)
END AS AMP_RAT_TY,
(CURRENT_YEAR_CLOSE - YEAR_FIRST_CLOSE) AS CHG_TY,
CASE WHEN DAYS_IN_YEAR = 0 THEN NULL
ELSE ROUND(CUM_YEAR_TRD_AMT / DAYS_IN_YEAR, 4)
END AS TRD_AMT_AVG_TY,
CASE WHEN DAYS_IN_YEAR = 0 THEN NULL
ELSE ROUND(CUM_YEAR_TRD_VOL * 10000 / NULLIF((SELECT lc2.FLOAT_SHR FROM latest_capital lc2
WHERE lc2.SECU_ID = base_data.SECU_ID AND lc2.TRD_DT = base_data.TRD_DT AND lc2.rn = 1), 0) * 100 / DAYS_IN_YEAR, 4)
END AS TUROV_RATE_AVG_TY
FROM base_data 这段SQL帮我加一下hint优化
根据您提供的SQL代码,这是一个包含多个CTE(Common Table Expression)的复杂查询。针对这类复杂查询的Oracle SQL性能优化,我为您添加了一些关键的HINT,以改善执行计划,减少TEMP表空间的使用。
优化后的SQL(添加HINT)
SELECT /*+
-- 主查询HINT
MATERIALIZE
NO_MERGE
LEADING(pa)
USE_HASH(lc)
FULL(pa)
INDEX(lc)
PARALLEL(4)
*/
SECU_ID,
COMPANY_ID,
TRD_DT,
TRD_STS,
CPRC,
DAY_CHG,
DAY_CHG_RAT,
TMKT_VAL,
FMKT_VAL,
TUROV_RATE,
DAY_AMP,
LAST_ANNUAL_PROFIT,
LAST_ANNUAL_INC,
ANNUAL_PROFIT_END_DT,
LATEST_NET_PROFIT,
LATEST_OP_TINC,
LATEST_PROFIT_END_DT,
NET_PROFIT_TTM,
OP_TINC_TTM,
OP_CF_NET_AMT_TTM,
PB,
PB_NEW,
PC,
PC_DYM,
CASE WHEN LAST_ANNUAL_PROFIT > 0
THEN TMKT_VAL / LAST_ANNUAL_PROFIT
ELSE NULL
END AS PE,
CASE WHEN LAST_ANNUAL_INC > 0
THEN TMKT_VAL / LAST_ANNUAL_INC
ELSE NULL
END AS PS,
CASE WHEN LATEST_NET_PROFIT IS NULL OR LATEST_NET_PROFIT <= 0 THEN NULL
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 12
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 31 THEN TMKT_VAL / LATEST_NET_PROFIT
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 9
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 30 THEN TMKT_VAL / (LATEST_NET_PROFIT / 3.0 * 4.0)
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 6
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 30 THEN TMKT_VAL / (LATEST_NET_PROFIT * 2.0)
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 3
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 31 THEN TMKT_VAL / (LATEST_NET_PROFIT * 4.0)
ELSE NULL
END AS PE_DYM,
CASE WHEN LATEST_OP_TINC IS NULL OR LATEST_OP_TINC <= 0 THEN NULL
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 12
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 31 THEN TMKT_VAL / LATEST_OP_TINC
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 9
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 30 THEN TMKT_VAL / (LATEST_OP_TINC / 3.0 * 4.0)
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 6
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 30 THEN TMKT_VAL / (LATEST_OP_TINC * 2.0)
WHEN EXTRACT(MONTH FROM LATEST_PROFIT_END_DT) = 3
AND EXTRACT(DAY FROM LATEST_PROFIT_END_DT) = 31 THEN TMKT_VAL / (LATEST_OP_TINC * 4.0)
ELSE NULL
END AS PS_DYM,
CASE WHEN OP_CF_NET_AMT_TTM > 0
THEN TMKT_VAL / OP_CF_NET_AMT_TTM
ELSE NULL
END AS PCTTM,
CASE WHEN NET_PROFIT_TTM > 0
THEN TMKT_VAL / NET_PROFIT_TTM
ELSE NULL
END AS PETTM,
CASE WHEN OP_TINC_TTM > 0
THEN TMKT_VAL / OP_TINC_TTM
ELSE NULL
END AS PSTTM,
MONTH_TRD_AMT AS TRD_AMT_RM,
MONTH_TRD_VOL AS TRD_VOL_RM,
MONTH_HIGH AS HPRC_RM,
MONTH_LOW AS LPRC_RM,
MONTH_CHG_RAT AS CHG_RAT_RM,
MONTH_TURNOVER_RATE AS TUROV_RATE_RM,
MONTH_AMPLITUDE AS AMP_RAT_RM,
MONTH_CHG AS CHG_RM,
MONTH_AVG_TRD_AMT AS TRD_AMT_AVG_RM,
M_AVG_TURNOVER_RATE AS TUROV_RATE_AVG_RM,
QUARTER_TRD_AMT AS TRD_AMT_R3M,
QUARTER_TRD_VOL AS TRD_VOL_R3M,
QUARTER_HIGH AS HPRC_R3M,
QUARTER_LOW AS LPRC_R3M,
QUARTER_CHG_RAT AS CHG_RAT_R3M,
QUARTER_TURNOVER_RATE AS TUROV_RATE_R3M,
QUARTER_AMPLITUDE AS AMP_RAT_R3M,
QUARTER_CHG AS CHG_R3M,
QUARTER_AVG_TRD_AMT AS TRD_AMT_AVG_R3M,
Q_AVG_TURNOVER_RATE AS TUROV_RATE_AVG_R3M,
HALF_YEAR_TRD_AMT AS TRD_AMT_R6M,
HALF_YEAR_TRD_VOL AS TRD_VOL_R6M,
HALF_YEAR_HIGH AS HPRC_R6M,
HALF_YEAR_LOW AS LPRC_R6M,
HALF_YEAR_CHG_RAT AS CHG_RAT_R6M,
H_Y_TURNOVER_RATE AS TUROV_RATE_R6M,
HALF_YEAR_AMPLITUDE AS AMP_RAT_R6M,
HALF_YEAR_CHG AS CHG_R6M,
H_Y_AVG_TRD_AMT AS TRD_AMT_AVG_R6M,
H_Y_AVG_TURNOVER_RATE AS TUROV_RATE_AVG_R6M,
YEAR_TRD_AMT AS TRD_AMT_RY,
YEAR_TRD_VOL AS TRD_VOL_RY,
YEAR_HIGH AS HPRC_RY,
YEAR_LOW AS LPRC_RY,
YEAR_CHG_RAT AS CHG_RAT_RY,
YEAR_TURNOVER_RATE AS TUROV_RATE_RY,
YEAR_AMPLITUDE AS AMP_RAT_RY,
YEAR_CHG AS CHG_RY,
YEAR_AVG_TRD_AMT AS TRD_AMT_AVG_RY,
Y_AVG_TURNOVER_RATE AS TUROV_RATE_AVG_RY,
WEEK_TRD_AMT AS TRD_AMT_RW,
WEEK_TRD_VOL AS TRD_VOL_RW,
WEEK_HIGH AS HPRC_RW,
WEEK_LOW AS LPRC_RW,
WEEK_CHG_RAT AS CHG_RAT_RW,
WEEK_TURNOVER_RATE AS TUROV_RATE_RW,
WEEK_AMPLITUDE AS AMP_RAT_RW,
WEEK_AVG_TRD_AMT AS TRD_AMT_AVG_RW,
W_AVG_TURNOVER_RATE AS TUROV_RATE_AVG_RW,
WEEK_CHG AS CHG_RW,
WEEK_TO_DATE_TRD_AMT AS TRD_AMT_TW,
WEEK_TO_DATE_TRD_VOL AS TRD_VOL_TW,
WEEK_TO_DATE_HIGH AS HPRC_TW,
WEEK_TO_DATE_LOW AS LPRC_TW,
CASE WHEN WEEK_FIRST_CLOSE IS NULL OR WEEK_FIRST_CLOSE = 0 THEN NULL
ELSE ROUND((CURRENT_CLOSE - WEEK_FIRST_CLOSE) * 100.0 / WEEK_FIRST_CLOSE, 4)
END AS CHG_RAT_TW,
ROUND(WEEK_TO_DATE_TRD_VOL * 10000 / NULLIF((SELECT /*+ NO_UNNEST */ lc2.FLOAT_SHR FROM latest_capital lc2
WHERE lc2.SECU_ID = base_data.SECU_ID AND lc2.TRD_DT = base_data.TRD_DT AND lc2.rn = 1), 0) * 100, 4) AS TUROV_RATE_TW,
CASE WHEN WEEK_FIRST_OPEN IS NULL OR WEEK_FIRST_OPEN = 0 THEN NULL
ELSE ROUND((WEEK_TO_DATE_HIGH - WEEK_TO_DATE_LOW) * 100.0 / WEEK_FIRST_OPEN, 4)
END AS AMP_RAT_TW,
(CURRENT_CLOSE - WEEK_FIRST_CLOSE) AS CHG_TW,
CASE WHEN DAYS_IN_WEEK = 0 THEN NULL
ELSE ROUND(WEEK_TO_DATE_TRD_AMT / DAYS_IN_WEEK, 4)
END AS TRD_AMT_AVG_TW,
CASE WHEN DAYS_IN_WEEK = 0 THEN NULL
ELSE ROUND(WEEK_TO_DATE_TRD_VOL * 10000 / NULLIF((SELECT /*+ NO_UNNEST */ lc2.FLOAT_SHR FROM latest_capital lc2
WHERE lc2.SECU_ID = base_data.SECU_ID AND lc2.TRD_DT = base_data.TRD_DT AND lc2.rn = 1), 0) * 100 / DAYS_IN_WEEK, 4)
END AS TUROV_RATE_AVG_TW,
CUM_MONTH_TRD_AMT AS TRD_AMT_TM,
CUM_MONTH_TRD_VOL AS TRD_VOL_TM,
CUM_MONTH_HIGH AS HPRC_TM,
CUM_MONTH_LOW AS LPRC_TM,
CASE WHEN MONTH_FIRST_CLOSE IS NULL OR MONTH_FIRST_CLOSE = 0 THEN NULL
ELSE ROUND((CURRENT_MONTH_CLOSE - MONTH_FIRST_CLOSE) * 100.0 / MONTH_FIRST_CLOSE, 4)
END AS CHG_RAT_TM,
ROUND(CUM_MONTH_TRD_VOL * 10000 / NULLIF((SELECT /*+ NO_UNNEST */ lc2.FLOAT_SHR FROM latest_capital lc2
WHERE lc2.SECU_ID = base_data.SECU_ID AND lc2.TRD_DT = base_data.TRD_DT AND lc2.rn = 1), 0) * 100, 4) AS TUROV_RATE_TM,
CASE WHEN MONTH_FIRST_OPEN IS NULL OR MONTH_FIRST_OPEN = 0 THEN NULL
ELSE ROUND((CUM_MONTH_HIGH - CUM_MONTH_LOW) * 100.0 / MONTH_FIRST_OPEN, 4)
END AS AMP_RAT_TM,
(CURRENT_MONTH_CLOSE - MONTH_FIRST_CLOSE) AS CHG_TM,
CASE WHEN DAYS_IN_MONTH = 0 THEN NULL
ELSE ROUND(CUM_MONTH_TRD_AMT / DAYS_IN_MONTH, 4)
END AS TRD_AMT_AVG_TM,
CASE WHEN DAYS_IN_MONTH = 0 THEN NULL
ELSE ROUND(CUM_MONTH_TRD_VOL * 10000 / NULLIF((SELECT /*+ NO_UNNEST */ lc2.FLOAT_SHR FROM latest_capital lc2
WHERE lc2.SECU_ID = base_data.SECU_ID AND lc2.TRD_DT = base_data.TRD_DT AND lc2.rn = 1), 0) * 100 / DAYS_IN_MONTH, 4)
END AS TUROV_RATE_AVG_TM,
CUM_YEAR_TRD_AMT AS TRD_AMT_TY,
CUM_YEAR_TRD_VOL AS TRD_VOL_TY,
CUM_YEAR_HIGH AS HPRC_TY,
CUM_YEAR_LOW AS LPRC_TY,
CASE WHEN YEAR_FIRST_CLOSE IS NULL OR YEAR_FIRST_CLOSE = 0 THEN NULL
ELSE ROUND((CURRENT_YEAR_CLOSE - YEAR_FIRST_CLOSE) * 100.0 / YEAR_FIRST_CLOSE, 4)
END AS CHG_RAT_TY,
ROUND(CUM_YEAR_TRD_VOL * 10000 / NULLIF((SELECT /*+ NO_UNNEST */ lc2.FLOAT_SHR FROM latest_capital lc2
WHERE lc2.SECU_ID = base_data.SECU_ID AND lc2.TRD_DT = base_data.TRD_DT AND lc2.rn = 1), 0) * 100, 4) AS TUROV_RATE_TY,
CASE WHEN YEAR_FIRST_OPEN IS NULL OR YEAR_FIRST_OPEN = 0 THEN NULL
ELSE ROUND((CUM_YEAR_HIGH - CUM_YEAR_LOW) * 100.0 / YEAR_FIRST_OPEN, 4)
END AS AMP_RAT_TY,
(CURRENT_YEAR_CLOSE - YEAR_FIRST_CLOSE) AS CHG_TY,
CASE WHEN DAYS_IN_YEAR = 0 THEN NULL
ELSE ROUND(CUM_YEAR_TRD_AMT / DAYS_IN_YEAR, 4)
END AS TRD_AMT_AVG_TY,
CASE WHEN DAYS_IN_YEAR = 0 THEN NULL
ELSE ROUND(CUM_YEAR_TRD_VOL * 10000 / NULLIF((SELECT /*+ NO_UNNEST */ lc2.FLOAT_SHR FROM latest_capital lc2
WHERE lc2.SECU_ID = base_data.SECU_ID AND lc2.TRD_DT = base_data.TRD_DT AND lc2.rn = 1), 0) * 100 / DAYS_IN_YEAR, 4)
END AS TUROV_RATE_AVG_TY
FROM base_data
针对各个CTE的HINT优化建议
由于您的查询包含多个CTE,我建议在关键CTE的SELECT语句前也添加HINT:
1. 优化 latest_capitalCTE
latest_capital AS (
SELECT /*+ MATERIALIZE INDEX(a IDX_STK_HK_EXCH_QUOT_DT) USE_HASH(a b) USE_HASH(b c) */
a.SECU_ID,
a.TRD_DT,
c.LIST_NEGO_SHR_TTL as FLOAT_SHR,
c.TTL_SHR,
c.END_DT,
ROW_NUMBER() OVER (PARTITION BY a.SECU_ID, a.TRD_DT ORDER BY c.END_DT DESC) AS rn
FROM STK_HK_EXCH_QUOT a
JOIN BASE_SECURITY_INFO b ON a.SECU_ID = b.SECURITY_ID
JOIN COO_SHR_STRU c ON b.COMPANY_ID = c.CORP_ID
WHERE c.END_DT <= a.TRD_DT
)
2. 优化 period_aggregatesCTE
这个CTE包含了大量的窗口函数,是最消耗资源的:
period_aggregates AS (
SELECT /*+
MATERIALIZE
NO_MERGE
PARALLEL(a 4)
INDEX(a IDX_STK_HK_EXCH_QUOT_DT)
USE_HASH(a b)
*/
a.SECU_ID,
b.COMPANY_ID,
a.TRD_DT,
a.TRD_STS,
a.CPRC,
a.LST_CPRC,
a.HPRC,
a.LPRC,
a.OPRC,
a.TRD_AMT,
a.TRD_VOL,
a.DAY_TUROV_RATE,
TRUNC(a.TRD_DT, 'IW') AS WEEK_START,
TRUNC(a.TRD_DT, 'MM') AS MONTH_START,
TRUNC(a.TRD_DT, 'Q') AS QUARTER_START,
TRUNC(a.TRD_DT, 'YEAR') AS YEAR_START,
CASE
WHEN EXTRACT(MONTH FROM a.TRD_DT) BETWEEN 1 AND 6
THEN TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-01-01', 'YYYY-MM-DD')
ELSE TO_DATE(EXTRACT(YEAR FROM a.TRD_DT) || '-07-01', 'YYYY-MM-DD')
END AS HALF_YEAR_START,
-- 窗口函数部分保持不变
SUM(a.TRD_AMT) OVER (PARTITION BY a.SECU_ID, TRUNC(a.TRD_DT, 'IW')) / 10000 AS WEEK_TRD_AMT,
-- ... 其他窗口函数
FROM STK_HK_EXCH_QUOT a
JOIN BASE_SECURITY_INFO b ON a.SECU_ID = b.SECURITY_ID
WHERE a.TRD_DT > v_min_trd_dt
)
3. 优化 period_calculationsCTE
period_calculations AS (
SELECT /*+
NO_MERGE
LEADING(pa)
USE_HASH(pa lc)
SWAP_JOIN_INPUTS(lc)
*/
pa.*,
lc.TTL_SHR,
(pa.CPRC - pa.LST_CPRC) AS DAY_CHG,
-- ... 其他计算字段
FROM period_aggregates pa
LEFT JOIN latest_capital lc ON pa.SECU_ID = lc.SECU_ID
AND pa.TRD_DT = lc.TRD_DT AND lc.rn = 1
)
4. 优化 base_dataCTE
base_data AS (
SELECT /*+
NO_MERGE
LEADING(pc)
USE_HASH(pc ap)
USE_HASH(pc lp)
USE_HASH(pc tnp)
USE_HASH(pc toc)
USE_HASH(pc fr)
USE_HASH(pc cd)
NO_EXPAND
*/
pc.SECU_ID,
pc.COMPANY_ID,
pc.TRD_DT,
-- ... 字段列表
FROM period_calculations pc
LEFT JOIN annual_profit ap ON pc.COMPANY_ID = ap.COMPANY_ID AND pc.TRD_DT = ap.TRD_DT AND ap.rn = 1
LEFT JOIN latest_profit lp ON pc.COMPANY_ID = lp.COMPANY_ID AND pc.TRD_DT = lp.TRD_DT AND lp.rn = 1
LEFT JOIN ttm_net_profit tnp ON pc.COMPANY_ID = tnp.COMPANY_ID AND pc.TRD_DT = tnp.TRD_DT
LEFT JOIN ttm_OP_CF_NET_AMT toc on pc.COMPANY_ID = toc.COMPANY_ID AND pc.TRD_DT = toc.TRD_DT
LEFT JOIN financial_ratios fr ON pc.COMPANY_ID = fr.COMPANY_ID AND pc.TRD_DT = fr.TRD_DT
LEFT JOIN cumulative_data cd ON pc.SECU_ID = cd.SECU_ID AND pc.TRD_DT = cd.TRD_DT
)
关键HINT说明
MATERIALIZE:强制Oracle物化CTE结果,避免重复计算
NO_MERGE:阻止查询转换器合并视图/子查询
LEADING(table):指定驱动表
USE_HASH(table1 table2):强制使用哈希连接
PARALLEL(n):启用并行查询(n为并行度)
NO_UNNEST:阻止子查询解嵌套
INDEX(table index_name):强制使用特定索引
FULL(table):强制全表扫描
SWAP_JOIN_INPUTS(table):交换哈希连接的输入
其他优化建议
创建必要的索引:
-- 基础索引
CREATE INDEX IDX_STK_HK_EXCH_QUOT_DT ON STK_HK_EXCH_QUOT(TRD_DT);
CREATE INDEX IDX_STK_HK_EXCH_QUOT_SEC_DT ON STK_HK_EXCH_QUOT(SECU_ID, TRD_DT);
CREATE INDEX IDX_COO_SHR_STRU_CORP_DT ON COO_SHR_STRU(CORP_ID, END_DT);
CREATE INDEX IDX_COO_HK_INC_STMT_CORP_DT ON COO_HK_INC_STMT(CORP_ID, END_DT);
CREATE INDEX IDX_BASE_SECURITY_INFO_SEC ON BASE_SECURITY_INFO(SECURITY_ID);
分批处理:将增量处理改为每次处理5-7天的数据,而不是一次性处理所有数据
调整数据库参数(需要DBA权限):
-- 增加PGA和临时表空间
ALTER SYSTEM SET pga_aggregate_target=8G SCOPE=BOTH;
ALTER SYSTEM SET workarea_size_policy=AUTO SCOPE=BOTH;
请注意,HINT的效果取决于实际的数据分布和表结构。建议在生产环境应用前,先在测试环境中验证执行计划和性能。