How to calculate RSI in SQL Server? - sql

Can somebody give me an idea how to calculate the RSI (Relative Strength Index)?
The formula for RSI:
100 - 100 / (1 + RS)
RS (Relative Strength) = AverageGain / AverageLoss
RSI calculation is based on 14 periods. Losses are expressed as positive values, not negative values.
The very first calculations for average gain and average loss are simple 14-period averages:
First Average Gain = Sum of Gains over the past 14 periods / 14
First Average Loss = Sum of Losses over the past 14 periods / 14
The second, and subsequent, calculations are based on the prior averages and the current gain loss:
Average Gain = [(previous Average Gain) x 13 + current Gain] / 14.
Average Loss = [(previous Average Loss) x 13 + current Loss] / 14.
Below is my query which is not working because something in my recursion part is wrong. The way I calculate ROW_NUMBER: my first row is relative to the most recent trading day. I also make calculations over a 250 periods because of additional explanations, not relative to the calculation:
WITH bas AS
(
SELECT *
FROM
(SELECT
p.CurrentTicker, p.ClosePrice, p.PriceDate, p.ClosePriceChange,
(CASE WHEN p.ClosePriceChange > 0 THEN p.ClosePriceChange ELSE 0 END) Gain,
(CASE WHEN p.ClosePriceChange < 0 THEN ABS(p.ClosePriceChange) ELSE 0 END) Loss,
rownum
FROM
(SELECT
*,
ROW_NUMBER() OVER (PARTITION BY CurrentTicker ORDER BY PriceDate DESC) rownum
FROM
PriceHistory
WHERE
PriceDate >= DATEADD(day, -400, GETDATE())) p
INNER JOIN
Ticker t ON p.CurrentTicker = t.CurrentTicker
WHERE
rownum <= 250) a
),
rec AS
(
SELECT
CurrentTicker, PriceDate, ClosePriceChange, rownum,
AvgGain = SUM(Gain) OVER (PARTITION BY CurrentTicker ORDER BY PriceDate ROWS BETWEEN 13 PRECEDING and CURRENT ROW) / 14,
AvgLoss = SUM(Loss) OVER (PARTITION BY CurrentTicker ORDER BY PriceDate ROWS BETWEEN 13 PRECEDING and CURRENT ROW) / 14
FROM
bas
WHERE
rownum = (SELECT MAX(rownum) - 14 FROM bas)
UNION ALL
SELECT
bas.CurrentTicker, bas.PriceDate, bas.ClosePriceChange, bas.rownum,
a.AvgGain, a.AvgLoss
FROM
rec
INNER JOIN
bas ON rec.rownum - 1 = bas.rownum
AND rec.CurrentTicker = bas.CurrentTicker
CROSS APPLY
(SELECT
(rec.AvgGain * 13 + bas.Gain) / 14 AS AvgGain,
(rec.AvgLoss * 13 + bas.Loss)/14 as AvgLoss) a
)
SELECT *
FROM rec
WHERE CurrentTicker = 'AAPL'
ORDER BY PriceDate DESC
OPTION (MAXRECURSION 0)
Here is AAPL prices table:
PriceDate
ClosePrice
2021-02-23
125.86
2021-02-22
126
2021-02-19
129.87
2021-02-17
130.84
2021-02-16
133.19
2021-02-12
135.37
2021-02-11
135.13
2021-02-10
135.39
2021-02-09
136.01
2021-02-08
136.91
2021-02-05
136.76
2021-02-04
137.39
2021-02-03
133.94
2021-02-02
134.99
2021-02-01
134.14
2021-01-29
131.96
2021-01-28
137.09
2021-01-27
142.06
2021-01-26
143.16
2021-01-25
142.92
2021-01-22
139.07
2021-01-21
136.87
2021-01-20
132.03
2021-01-19
127.83
2021-01-15
127.14
2021-01-14
128.91
2021-01-13
130.89
2021-01-12
128.8
2021-01-11
128.98
2021-01-08
132.05
2021-01-07
130.92
2021-01-06
126.6
2021-01-05
131.01
2021-01-04
129.41
2020-12-31
132.69
2020-12-30
133.72
2020-12-29
134.87
2020-12-28
136.69
2020-12-24
131.97
2020-12-23
130.96
2020-12-22
131.88
2020-12-21
128.23
2020-12-18
126.655
2020-12-17
128.7
2020-12-16
127.81
2020-12-15
127.88
2020-12-14
121.78
2020-12-11
122.41
2020-12-10
123.24
2020-12-09
121.78
Now I hope someone will be able to help me. Thanks in advance.

Related

Why does my cumulative column not work as expected?

Here is my query , I have a column called cum_balance which is supposed to calculate the cumulative balance but after row number 10 there is an anamoly and it doesn't work as expected , all I notice is that from row number 10 onwards the hour column has same value. What's the right syntax for this?
[select
hour,
symbol,
amount_usd,
category,
sum(amount_usd) over (
order by
hour asc RANGE BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW
) as cum_balance
from
combined_transfers_usd_netflow
order by
hour][1]
I have tried removing RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW , adding a partition by hour and group by hour. None of them gave the expected result or errors
Row Number
Hour
SYMBOL
AMOUNT_USD
CATEGORY
CUM_BALANCE
1
2021-12-02 23:00:00
WETH
227.2795
in
227.2795
2
2021-12-03 00:00:00
WETH
-226.4801153
out
0.7993847087
3
2022-01-05 21:00:00
WETH
5123.716203
in
5124.515587
4
2022-01-18 14:00:00
WETH
-4466.2366
out
658.2789873
5
2022-01-19 00:00:00
WETH
2442.618599
in
3100.897586
6
2022-01-21 14:00:00
USDC
99928.68644
in
103029.584
7
2022-03-01 16:00:00
UNI
8545.36098
in
111574.945
8
2022-03-04 22:00:00
USDC
-2999.343
out
108575.602
9
2022-03-09 22:00:00
USDC
-5042.947675
out
103532.6543
10
2022-03-16 21:00:00
USDC
-4110.6579
out
98594.35101
11
2022-03-16 21:00:00
UNI
-3.209306045
out
98594.35101
12
2022-03-16 21:00:00
UNI
-16.04653022
out
98594.35101
13
2022-03-16 21:00:00
UNI
-16.04653022
out
98594.35101
14
2022-03-16 21:00:00
UNI
-16.04653022
out
98594.35101
15
2022-03-16 21:00:00
UNI
-6.418612089
out
98594.35101
The "problem" with your data in all the ORDER BY values after row 10 are the same.
So if we shrink the data down a little, and use for groups to repeat the experiment:
with data(grp, date, val) as (
select * from values
(1,'2021-01-01'::date, 10),
(1,'2021-01-02'::date, 11),
(1,'2021-01-03'::date, 12),
(2,'2021-01-01'::date, 20),
(2,'2021-01-02'::date, 21),
(2,'2021-01-02'::date, 22),
(2,'2021-01-04'::date, 23)
)
select d.*
,sum(val) over ( partition by grp order by date RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) as cum_val_1
,sum(val) over ( partition by grp order by date ) as cum_val_2
from data as d
order by 1,2;
we get:
GRP
DATE
VAL
CUM_VAL_1
CUM_VAL_2
1
2021-01-01
10
10
10
1
2021-01-02
11
21
21
1
2021-01-03
12
33
33
2
2021-01-01
20
20
20
2
2021-01-02
21
63
63
2
2021-01-02
22
63
63
2
2021-01-04
23
86
86
we see with group 1 that values accumulate as we expect. So for group 2 we put duplicate values as see those rows get the same value, but rows after "work as expected again".
This tells us how this function work across unstable data (values that sort the same) is that they are all stepped in one leap.
Thus if you want each row to be different they will need better ORDER distinctness. This could be forced by add random values of literal random nature, or feeling non random ROW_NUMBER, but really they would be random, albeit not explicit, AND if you use random, you might get duplicates, thus really should use ROW_NUMBER or SEQx to have unique values.
Also the second formula shows they are equal, and it's the ORDER BY problem not the framing of "which rows" are used.
with data(grp, date, val) as (
select * from values
(1,'2021-01-01'::date, 10),
(1,'2021-01-02'::date, 11),
(1,'2021-01-03'::date, 12),
(2,'2021-01-01'::date, 20),
(2,'2021-01-02'::date, 21),
(2,'2021-01-02'::date, 22),
(2,'2021-01-04'::date, 23)
)
select d.*
,seq8() as s
,sum(val) over ( partition by grp order by date ) as cum_val_1
,sum(val) over ( partition by grp order by date, s ) as cum_val_2
,sum(val) over ( partition by grp order by date, seq8() ) as cum_val_3
from data as d
order by 1,2;
gives:
GRP
DATE
VAL S
CUM_VAL_1
CUM_VAL_2
CUM_VAL_2_2
1
2021-01-01
10
0
10
10
1
2021-01-02
11
1
21
21
1
2021-01-03
12
2
33
33
2
2021-01-01
20
3
20
20
2
2021-01-02
21
4
63
41
2
2021-01-02
22
5
63
63
2
2021-01-04
23
6
86
86

Calculate growth percentage using sql on multiple stocks

I would like to calculate the growth for one ticker, while I'm querying multiple tickers. The code I'm trying only works when I run it on a single ticker.
Source Table:
line
ticker
calendardate
revenueusd
growth
1
INTC
2021-09-30
19192
2
AMD
2021-09-30
4313
3
AMD
2021-12-31
4826
?
4
INTC
2021-12-31
20528
?
5
INTC
2022-03-31
18353
?
6
AMD
2022-03-31
5887
?
7
INTC
2022-06-30
15321
?
8
AMD
2022-06-30
6550
?
SELECT
ticker,
calendardate,
(revenueusd - LAG (revenueusd) OVER (ORDER BY calendardate ASC)) / LAG (revenueusd) OVER (ORDER BY calendardate ASC) AS growth
FROM
sf1
WHERE
ticker IN ('AMD', 'INTC')
ORDER BY calendardate ASC
Is there anything that I can add to make LAG get the corresponding ticker not just the one "above"? Currently this code at line 4 (INTC) would use the revenue for line 3 (AMD). Insted I would need line 1 (INTC) data.
The data comes from: Nasdaq, Core US Fundamentals Data
you need to PARTITION BY and what you also need to avoid integer division is to9 convert the data into decimal or float
SELECT
ticker,
calendardate,
(revenueusd - LAG (revenueusd) OVER (PARTITION BY ticker ORDER BY calendardate ASC)::DECIMAL(7,2)
/ LAG (revenueusd) OVER (PARTITION BY ticker ORDER BY calendardate ASC))::DECIMAL (7,2) AS growth
FROM
sf1
WHERE
ticker IN ('AMD', 'INTC')
ORDER BY calendardate ASC
ticker
calendardate
growth
AMD
2021-09-30
null
INTC
2021-09-30
null
INTC
2021-12-31
20527.00
AMD
2021-12-31
4825.00
AMD
2022-03-31
5886.00
INTC
2022-03-31
18352.00
AMD
2022-06-30
6549.00
INTC
2022-06-30
15320.00
SELECT 8
fiddle

How to subtract value with next row value

I want to calculation subtraction field energy with next value energy I just try with my query but I think result is still wrong
For my code
SELECT
datapm.id,
datapm.tgl,
CONVERT ( CHAR ( 5 ), datapm.stamp , 108 ) stamp,
datapm.pmid,
datapm.vavg,
datapm.pf,
( CAST (datapm.energy AS FLOAT) - (select top 1 CAST (energy AS FLOAT) from datapm as dt2 where dt2.id > datapm.id and dt2.tgl=datapm.tgl)) as energy
FROM
datapm
GROUP BY
datapm.id,
datapm.tgl,
datapm.stamp,
datapm.pmid,
datapm.vavg,
datapm.pf,
datapm.energy
ORDER BY tgl desc
My sample
id pmdi tgl stamp vavg pf energy
787 SDPEXT_2 2021-09-06 06:00:00.0000000 407.82 0.98 1408014.25
788 SDPEXT_2 2021-09-06 07:00:00.0000000 403.31 0.85 1408041.00
789 SDPEXT_2 2021-09-06 08:00:00.0000000 408.82 0.87 1408081.75
Result I want
id pmdi tgl stamp vavg pf energy
787 SDPEXT_2 2021-09-06 06:00:00.0000000 407.82 0.98 -2.675
788 SDPEXT_2 2021-09-06 07:00:00.0000000 403.31 0.85 -4.075
789 SDPEXT_2 2021-09-06 08:00:00.0000000 408.82 0.87 -11.012
remove the GROUP BY in your query, you are not using any aggregate function
If energy is already in numeric data type, don't convert to float.
use LEAD() to get the next row value
SELECT . . .
(d.energy - LEAD (d.energy) OVER (PARTITION BY d.tgl
ORDER BY d.id)) / 10
FROM datapm d
not sure what is the actual formula, but looking at the result, you need to divide by 10 to obtain it

How to calculate the median by fixing some variables?

I have a data set that I already aggregated. This basically shows the median prices for each cat, root_cat, and cluster on daily basis.
date cluster root_cat cat median_price
2020-12-07 A X 1 20
2020-12-07 A X 2 15
2020-12-07 A X 2 30
2020-12-08 B Y 3 24
Here is the query that I wrote for calculating the median price.
SELECT date,
page_impressions_cluster,
root_cat,
cat,
MAX(CASE
WHEN tile2 = 1 THEN
min_price/100 END) AS median
FROM
(SELECT pl.*,
NTILE(2)
OVER (PARTITION BY product_id
ORDER BY min_price) AS tile2
FROM pl
WHERE cluster is NOT null
AND (date_parse(date, '%Y-%m-%d') >= current_date - interval '15' day) ) d
GROUP BY 1, 2, 3, 4
Now, I would like to have one more column that shows the median price for each cat and root_cat last 14 days except the latest day. How can I do this?
Here is the desired output:
date cluster root_cat cat median_price median_price_root median_price_cat
2020-12-07 A X 1 20 20 20
2020-12-07 A X 2 15 20 22,5
2020-12-07 A X 2 30 20 22,5
2020-12-08 B Y 3 24 24 24
If an approximation of the median is good enough, then you can use
SELECT date,
page_impressions_cluster,
root_cat,
cat,
MAX(CASE
WHEN tile2 = 1 THEN
min_price/100 END) AS median,
approx_percentile(price, 0.5) -- <<== the 0.5 percentile is the median
FROM ...
See the doc for the approc_percentile function here.

Calculate wrong amount in query

I have a table with some records now want to repeat this table content with some logic. I have two date start date and termination date, means record start from start_date and end on termination date, it will working fine but problem is calculate amount on it,
Logic is amount calculation formula
basesalary / 12 * ( SUTARate / 100 ) * ( x.num+1)
if this amount is less than SUTAMaximumAmount this amount is used, else 0. And one more thing if amount will be remain and year is complete then restart calculation from next year.. x.num is temporary table which hold 90 number from 1 to 90
Table
BaseSalary| S_Date | T_Date | SUTARate| SUTAMaximumAmount |A_S_Percent
48000 | 7-1-2013 | 3-15-2015 | 1.1 | 300 | 5
My result is
DAte amount
2013-07-01 00:00:00.000 44
2013-08-01 00:00:00.000 44
2013-09-01 00:00:00.000 44
2013-10-01 00:00:00.000 44
2013-11-01 00:00:00.000 44
2013-12-01 00:00:00.000 44
2014-01-01 00:00:00.000 36
2014-02-01 00:00:00.000 -8
2014-03-01 00:00:00.000 -52
2014-04-01 00:00:00.000 -96
2014-05-01 00:00:00.000 -140
2014-06-01 00:00:00.000 -184
2014-07-01 00:00:00.000 -228
2014-08-01 00:00:00.000 -272
2014-09-01 00:00:00.000 -316
2014-10-01 00:00:00.000 -360
2014-11-01 00:00:00.000 -404
2014-12-01 00:00:00.000 -448
2015-01-01 00:00:00.000 -492
2015-02-01 00:00:00.000 -536
2015-03-01 00:00:00.000 -580
and I want result like this
Date | Amount
7-1-2013 44
8-1-2013 44
9-1-2013 44
10-1-2013 44
11-1-2013 44
12-1-2013 44
1-1-2014 44
2-1-2014 44
3-1-2014 44
4-1-2014 44
5-1-2014 44
6-1-2014 44
7-1-2014 36
1-1-2015 44
2-1-2015 44
3-1-2015 44
Query
SELECT dateadd(M, (x.num),d.StartDate) AS TheDate,
Round( case when ((convert(float,d.SUTARate)/100* convert(integer,d.BaseSalary) / 12)*(x.num+1)) <=CONVERT(money,d.SUTAMaximumAmount)
then (convert(float,d.SUTARate)/100* convert(integer,d.BaseSalary)* / 12)
else (CONVERT(money,d.SUTAMaximumAmount)-((convert(float,d.SUTARate)/100* (convert(integer,d.BaseSalary) / 12)*x.num)))*Power((1+convert(float,d.AnnualSalaryIncreasePercent)/100),Convert(int,x.num/12)) end, 2) AS Amount,
FROM #Table AS x, myTbl AS d
WHERE (x.num >= 0) AND (x.num <= (DateDiff(M, d.StartDate, d.TerminationDate)) )
temporary table
create TABLE #Table (
num int NOT NULL,
);
;WITH Nbrs ( n ) AS (
SELECT 0 UNION ALL
SELECT 1 + n FROM Nbrs WHERE n < 99 )
INSERT #Table(num)
SELECT n FROM Nbrs
OPTION ( MAXRECURSION 99 )
this table used as x in above query
I created this SQLFiddle.
-- Numbers table is probably a good idea
WITH Nbrs ( num ) AS
(
SELECT 0 UNION ALL
SELECT 1 + num FROM Nbrs WHERE num < 99
)
-- All columns, except for 'num' come from myTbl
SELECT dateadd(M, (num),S_Date) AS TheDate,
Round(
CASE
WHEN (SUTARate / 100) * (BaseSalary / 12) <= SUTAMaximumAmount
THEN (SUTARate / 100) * (BaseSalary / 12)
ELSE 0
END
, 2) As Amount
-- This may be the number you were trying to multiply
,DatePart(Month, dateadd(M, (num),S_Date)) As PotentialMultiiplier
FROM Nbrs AS x, myTbl AS d
WHERE (num >= 0)
AND (num <= (DateDiff(M, S_Date, T_Date)) )
I am not entirely sure what your goal is, but you are probably on the right track with a numbers table. Because the result you are going for does not change much over time (i.e., nearly every month has an amount of $44), it is difficult to determine the correct code for the query. So, I recommend you provide a different set of data for better result-checking.
If you fiddle with the SQL in the provided link, you can re-post with better code, and then we can better solve your issue.