Review RSI function - Python - python

does this RSI code looks correct for you?
RSI I'm getting with this code are very often touching oscillator peaks (0 or 100) when comparing to RSIs on different market data apps (TradingView etc.) they hardly ever do that.
Does this formula looks correct? I found it in quite old book Technical Traders Guide to Computer Analysis of the Futures Market. Also it returns almost identical results as RSI=100-(100-RS) where RS=AVG GAIN/AVG LOSS. But still I'm a bit mad that results are different comparing to RSIs available on the web...
#
# Formula used:
#
# RSI(n) = 100 * (avg_up(n) / (avg_up(n)+avg_down(n)))
#
# Where:
#
# avg_up = average percentage gain from n-periods
# avg_down = average percentage loss from n-periods
# n = number of periods to calculate averages
#
def calculate_percentage_gains_and_losses(prices):
percentage = {"gains":[0.0],
"losses":[0.0]}
for i in range(len(prices)-1):
diff=((float(prices[i + 1]) / float(prices[i]))*100)-100
if diff>=0:
percentage["gains"].append(diff)
percentage["losses"].append(0.0)
else:
percentage["losses"].append(abs(diff))
percentage["gains"].append(0.0)
return percentage
def calculate_avg_percentage_gains_and_losses(prices, gains, losses, periods):
avg_percentage = {"gains":[ 0.0 for x in range(periods - 1)],
"losses":[ 0.0 for x in range(periods - 1)]}
for i in range(periods,len(prices)+1):
avg_percentage["gains"].append(sum(gains[i - periods:i]) / periods)
avg_percentage["losses"].append(sum(losses[i - periods:i]) / periods)
return avg_percentage
def calculate_relative_strength_index(prices, periods):
percentage = calculate_percentage_gains_and_losses(prices)
avg_percentage = calculate_avg_percentage_gains_and_losses(prices, percentage["gains"], percentage["losses"], periods)
rsi_list=[0.0 for x in range(periods - 1)]
for i in range(periods - 1, len(prices)):
rsi = 100 * round((avg_percentage["gains"][i] / (avg_percentage["gains"][i] + avg_percentage["losses"][i])), 2)
rsi_list.append(rsi)
return rsi_list
EDIT
Here is the code after adjustment
def calculate_percentage_gains_and_losses(prices):
percentage = {"gains":[0.0],
"losses":[0.0]}
for i in range(len(prices)-1):
diff=((float(prices[i + 1]) / float(prices[i]))*100)-100
if diff>=0:
percentage["gains"].append(diff)
percentage["losses"].append(0.0)
else:
percentage["losses"].append(abs(diff))
percentage["gains"].append(0.0)
return percentage
def calculate_smoothed_avg_percentage_gains_and_losses(prices, gains, losses, periods):
avg_percentage = {"gains":[ 0.0 if i<(periods-1) else sum(gains[:periods]) / periods for i in range(periods)],
"losses":[ 0.0 if i<(periods-1) else sum(losses[:periods]) / periods for i in range(periods)]}
for i in range(periods, len(prices)):
avg_percentage["gains"].append((gains[i] + (avg_percentage["gains"][i-1]* (periods-1))) / periods)
avg_percentage["losses"].append((losses[i] + (avg_percentage["losses"][i-1]* (periods-1))) / periods)
return avg_percentage
def calculate_relative_strength_index(prices, periods):
percentage = calculate_percentage_gains_and_losses(prices)
avg_percentage = calculate_smoothed_avg_percentage_gains_and_losses(prices, percentage["gains"], percentage["losses"], periods)
rsi=[ 0.0 if i < (periods-1) else round((100 * (avg_percentage["gains"][i] / (avg_percentage["gains"][i] + avg_percentage["losses"][i]))),2) for i in range(len(prices))]
return rsi

Firstly, issues with formula:
your RSI formula is calculated differently to the usual. As you acknowledge, RS is normally calculated as (Avg gains)/(Avg losses) yet you use (Avg gains)/((Avg gains) + (Avg losses)). While there may be strong correlation between the two, they are two different formulas and will therefore give you different answers. Furthermore you use percentage gains/losses when most charting platforms use raw gains/losses. And finally, you use a simple average of the gains/losses (known as Cutler's RSI). The much more widely used Wells Wilder RSI uses a smoothed average given by:
Avg gain = (gain + (prev gain) * (period -1))/period
The upshot is you will get very different numbers.
Secondly, your code:
While I have not scrutinized your code with a fine tooth comb, there are several issues rendering the code, at best, inefficient. If you intend on continuing to code in Python you MUST learn AND understand list comprehension and how to use it. It is what makes Python so useful and powerful. You have used it merely to initialize some lists. There are many websites and books that cover list comprehension quite extensively, just google it.
Additionally, if you intend to use python to calculate series information on chronologically ordered data, I strongly recommend you import the pandas module and use DataFrames rather than lists. DataFrames are like database records that keeps all data neatly in the row they belong two and are easily manipulated. List will get messy as you try to line up the data.
I will not provide code at this point because I think you already have plenty to think a bout and work on but will happily help once you have understood the above.

Related

Convert tradingview's pine script to python in VScode

I want to convert the following Pine script to python to calculate the value for vwap1 variable without plotting the results, I just want to calculate the value for vwap1:
...
wapScore(pds) =>
mean = sum(volume*close,pds)/sum(volume,pds)
vwapsd = sqrt(sma(pow(close-mean, 2), pds) )
(close-mean)/vwapsd
vwap1 = input(48)
plot(vwapScore(vwap1),title="ZVWAP2-48",color=#35e8ff, linewidth=2,transp=0.75)
...
I have tried the following:
...
def calculate_SMA(ser, days):
sma = ser.rolling(window=days).mean()
return sma
def calculate_Zscore(pds, volume, close):
mean = sum(volume * close, pds) / sum(volume, pds)
vwapsd = np.sqrt(calculate_SMA(pow(close - mean, 2), pds))
return (close - mean) / vwapsd
...
And I am using calculate_Zscore function to calculate the value and add it to pandas dataframe but it gives me different values rather than the values on trading view
I just wanted to comment ...but my reputation cannot allow me :)
Surely the sum of pine script (tradingview) has a different type signature than the python sum.
In particular the second parameter has a totally different meaning.
In pine script sum(x,y) tells you the sliding sum of last y values of x (sum of x for y bars back).
In python sum(x,y) sum the iterable x and if y, the second parameter has passed (optional), this value is added to the sum of items of the iterable.So if your sum(x) == 4.5 then sum(x,10) == 14.5
So your code surely need to be changed at least on the use of this method
Hoping to be helpful

How to find discount rate value if total present value and future time series of cash flows is known in Python

While going through an article, I encountered a situation where I encountered below polynomial equation.
For reference, below is the equation.
15446 = 537.06/(1+r) + 612.25/(1+r)**2 + 697.86/(1+r)**3 + 795.67/(1+r)**4 + 907.07/(1+r)**5
This is discount cash flow time series values which we use in finance to get the idea of present value of future cash flows after applying the appropriate discount rate.
So from above equation, I need to calculate the variable r in python programming environment?. I do hope that there must be some library which can be used to solve such equations?.
I solve this, I thought to use the numpy.npv API.
import numpy as np
presentValue = 15446
futureValueList = [537.06, 612.25, 697.86,795.67, 907.07]
// I know it is not possible to get r from below. Just put
// it like this to describe my intention.
presentValue = np.npv(r, futureValueList)
print(r)
You can multiply your NPV formula with the highest power or (1+r) and then find the roots of the polynomial with polyroots (just take the only real root and disregard the complex ones):
import numpy as np
presentValue = 15446
futureValueList = [537.06, 612.25, 697.86,795.67, 907.07]
roots = np.polynomial.polynomial.polyroots(futureValueList[::-1]+[-presentValue])
r = roots[np.argwhere(roots.imag==0)].real[0,0] - 1
print(r)
#-0.3332398877886278
As it turns out the formula given is incomplete, see p. 14 of the linked article. The correct equation can be solved with standard optimization procedures, e.g. optimize.root providing a sensible initial guess:
from scipy import optimize
def fun(r):
r1 = 1 + r
return 537.06/r1 + 612.25/r1**2 + 697.86/r1**3 + 795.67/r1**4 + 907.07/r1**5 * (1 + 1.0676/(r-.0676)) - 15446
roots = optimize.root(fun, [.1])
print(roots.x if roots.success else roots.message)
#[0.11177762]

Finding the global minimum of a noisy function via simulated annealing in python

I'm trying to find the global minimum of the function from the hundred digit hundred dollars challenge, question #4 as an exercise for simulated annealing.
As the basis of my understanding and approach to writing the code, I refer to the global optimization algorithms version 3 book which is found for free online.
Consequently, I've initially come up with the following code:
The noisy func:
def noisy_func(x, y):
return (math.exp(math.sin(50*x)) +
math.sin(60*math.exp(y)) +
math.sin(70*math.sin(x)) +
math.sin(math.sin(80*y)) -
math.sin(10*(x + y)) +
0.25*(math.pow(x, 2) +
math.pow(y, 2)))
The function used to mutate the values:
def mutate(X_Value, Y_Value):
mutationResult_X = X_Value + randomNumForInput()
mutationResult_Y = Y_Value + randomNumForInput()
while mutationResult_X > 4 or mutationResult_X < -4:
mutationResult_X = X_Value + randomNumForInput()
while mutationResult_Y > 4 or mutationResult_Y < -4:
mutationResult_Y = Y_Value + randomNumForInput()
mutationResults = [mutationResult_X, mutationResult_Y]
return mutationResults
randomNumForInput simply returns a random number between 4 and -4. (Interval Limits for the search.) Hence it is equivalent to random.uniform(-4, 4).
This is the central function of the program.
def simulated_annealing(f):
"""Peforms simulated annealing to find a solution"""
#Start by initializing the current state with the initial state
#acquired by a random generation of a number and then using it
#in the noisy func, also set solution(best_state) as current_state
#for a start
pCurSelect = [randomNumForInput(),randomNumForInput()]
current_state = f(pCurSelect[0],pCurSelect[1])
best_state = current_state
#Begin time monitoring, this will represent the
#Number of steps over time
TimeStamp = 1
#Init current temp via the func, using such values as to get the initial temp
initial_temp = 100
final_temp = .1
alpha = 0.001
num_of_steps = 1000000
#calculates by how much the temperature should be tweaked
#each iteration
#suppose the number of steps is linear, we'll send in 100
temp_Delta = calcTempDelta(initial_temp, final_temp, num_of_steps)
#set current_temp via initial temp
current_temp = getTemperature(initial_temp, temp_Delta)
#max_iterations = 100
#initial_temp = get_Temperature_Poly(TimeStamp)
#current_temp > final_temp
while current_temp > final_temp:
#get a mutated value from the current value
#hence being a 'neighbour' value
#with it, acquire the neighbouring state
#to the current state
neighbour_values = mutate(pCurSelect[0], pCurSelect[1])
neighbour_state = f(neighbour_values[0], neighbour_values[1])
#calculate the difference between the newly mutated
#neighbour state and the current state
delta_E_Of_States = neighbour_state - current_state
# Check if neighbor_state is the best state so far
# if the new solution is better (lower), accept it
if delta_E_Of_States <= 0:
pCurSelect = neighbour_values
current_state = neighbour_state
if current_state < best_state:
best_state = current_state
# if the new solution is not better, accept it with a probability of e^(-cost/temp)
else:
if random.uniform(0, 1) < math.exp(-(delta_E_Of_States) / current_temp):
pCurSelect = neighbour_values
current_state = neighbour_state
# Here, we'd decrement the temperature or increase the timestamp, normally
"""current_temp -= alpha"""
#print("Run number: " + str(TimeStamp) + " current_state = " + str(current_state) )
#increment TimeStamp
TimeStamp = TimeStamp + 1
# calc temp for next iteration
current_temp = getTemperature(current_temp, temp_Delta)
#print("Iteration Count: " + str(TimeStamp))
return best_state
alpha is not used for this implementation, however temperature is moderated linearly using the following funcs:
def calcTempDelta(T_Initial, T_Final, N):
return((T_Initial-T_Final)/N)
def getTemperature(T_old, T_new):
return (T_old - T_new)
This is how I implemented the solution described in page 245 of the book. However, this implementation does not return to me the global minimum of the noisy function, but rather, one of its near-by local minimum.
The reasons I implemented the solution in this way is two fold:
It has been provided to me as a working example of a linear temperature moderation, and thus a working template.
Although I have tried to understand the other forms of temperature moderation laid out in the book in pages 248-249, it is not entirely clear to me how the variable "Ts" is calculated, and even after trying to look through some of the cited sources the book references, it remains esoteric for me still. Thus I figured, I'd rather try to make this "simple" solution work correctly first, before proceeding to attempt other approaches of temperature quenching (logarithmic, exponential, etc).
Since then I have tried in numerous ways to acquire the global minimum of the noisy func through various different iterations of the code, which would be too much to post here all at once. I've tried different rewrites of this code:
Decrease the randomly rolled number over each iteration as in order to search within a smaller scope every time, this has resulted in more consistent but still incorrect results.
Mutate by different increments, so lets say, between -1 and 1, etc. Same effect.
Rewrite mutate as in order to examine the neighbouring points to the current point via some step size, and examine neighboring points by adding/reducing said step size from the current point's x/y values, checking the differences between the newly generated point and the current point (the delta of E's, basically), and return the appropriate values with whichever one produced the lowest distance to the current function, thus being its closest proximity neighbour.
Reduce the intervals limits over which the search occurs.
It is in these, the solutions involving step-size/reducing limits/checking neighbours by quadrants that I have used movements comprised of some constant alpha times the time_stamp.
These and other solutions which I've attempted have not worked, either producing even less accurate results (albeit in some cases more consistent results) or in one case, not working at all.
Therefore I must be missing something, whether its to do with the temperature moderation, or the precise way (formula) by which I'm supposed to make the next step (mutate) in the algorithm.
I know its a lot to take in and look at, but I'd appreciate any constructive criticism/help/advice you can provide me.
If it will be of any help to showcase code bits of the other solution attempts, I'll post them if asked.
It is important that you keep track of what you are doing.
I have put a few important tips on frigidum
The alpha cooling generally works well, it makes sure you don't speed through the interesting sweet-spot, where about 0.1 of the proposals are accepted.
Make sure your proposals are not too coarse, I have put a example where I only change x or y, but never both. The idea is that annealing will take whats best, or take a tour, and let the scheme decide.
I use the package frigidum for the algo, but its pretty much the same are your code. Also notice I have 2 proposals, a large change and a small change, combinations usually work well.
Finally, I noticed its hopping a lot. A small variation would be to pick the best-so-far before you go in the last 5% of your cooling.
I use/install frigidum
!pip install frigidum
And made a small change to make use of numpy arrays;
import math
def noisy_func(X):
x, y = X
return (math.exp(math.sin(50*x)) +
math.sin(60*math.exp(y)) +
math.sin(70*math.sin(x)) +
math.sin(math.sin(80*y)) -
math.sin(10*(x + y)) +
0.25*(math.pow(x, 2) +
math.pow(y, 2)))
import frigidum
import numpy as np
import random
def random_start():
return np.random.random( 2 ) * 4
def random_small_step(x):
if np.random.random() < .5:
return np.clip( x + np.array( [0, 0.02 * (random.random() - .5)] ), -4,4)
else:
return np.clip( x + np.array( [0.02 * (random.random() - .5), 0] ), -4,4)
def random_big_step(x):
if np.random.random() < .5:
return np.clip( x + np.array( [0, 0.5 * (random.random() - .5)] ), -4,4)
else:
return np.clip( x + np.array( [0.5 * (random.random() - .5), 0] ), -4,4)
local_opt = frigidum.sa(random_start=random_start,
neighbours=[random_small_step, random_big_step],
objective_function=noisy_func,
T_start=10**2,
T_stop=0.00001,
repeats=10**4,
copy_state=frigidum.annealing.copy)
The output of the above was
---
Neighbour Statistics:
(proportion of proposals which got accepted *and* changed the objective function)
random_small_step : 0.451045
random_big_step : 0.268002
---
(Local) Minimum Objective Value Found:
-3.30669277
With the above code sometimes I get below -3, but I also noticed sometimes it has found something around -2, than it is stuck in the last phase.
So a small tweak would be to re-anneal the last phase of the annealing, with the best-found-so-far.
Hope that helps, let me know if any questions.

Finding minimum value of a function wit 11,390,625 variable combinations

I am working on a code to solve for the optimum combination of diameter size of number of pipelines. The objective function is to find the least sum of pressure drops in six pipelines.
As I have 15 choices of discrete diameter sizes which are [2,4,6,8,12,16,20,24,30,36,40,42,50,60,80] that can be used for any of the six pipelines that I have in the system, the list of possible solutions becomes 15^6 which is equal to 11,390,625
To solve the problem, I am using Mixed-Integer Linear Programming using Pulp package. I am able to find the solution for the combination of same diameters (e.g. [2,2,2,2,2,2] or [4,4,4,4,4,4]) but what I need is to go through all combinations (e.g. [2,4,2,2,4,2] or [4,2,4,2,4,2] to find the minimum. I attempted to do this but the process is taking a very long time to go through all combinations. Is there a faster way to do this ?
Note that I cannot calculate the pressure drop for each pipeline as the choice of diameter will affect the total pressure drop in the system. Therefore, at anytime, I need to calculate the pressure drop of each combination in the system.
I also need to constraint the problem such that the rate/cross section of pipeline area > 2.
Your help is much appreciated.
The first attempt for my code is the following:
from pulp import *
import random
import itertools
import numpy
rate = 5000
numberOfPipelines = 15
def pressure(diameter):
diameterList = numpy.tile(diameter,numberOfPipelines)
pressure = 0.0
for pipeline in range(numberOfPipelines):
pressure += rate/diameterList[pipeline]
return pressure
diameterList = [2,4,6,8,12,16,20,24,30,36,40,42,50,60,80]
pipelineIds = range(0,numberOfPipelines)
pipelinePressures = {}
for diameter in diameterList:
pressures = []
for pipeline in range(numberOfPipelines):
pressures.append(pressure(diameter))
pressureList = dict(zip(pipelineIds,pressures))
pipelinePressures[diameter] = pressureList
print 'pipepressure', pipelinePressures
prob = LpProblem("Warehouse Allocation",LpMinimize)
use_diameter = LpVariable.dicts("UseDiameter", diameterList, cat=LpBinary)
use_pipeline = LpVariable.dicts("UsePipeline", [(i,j) for i in pipelineIds for j in diameterList], cat = LpBinary)
## Objective Function:
prob += lpSum(pipelinePressures[j][i] * use_pipeline[(i,j)] for i in pipelineIds for j in diameterList)
## At least each pipeline must be connected to a diameter:
for i in pipelineIds:
prob += lpSum(use_pipeline[(i,j)] for j in diameterList) ==1
## The diameter is activiated if at least one pipelines is assigned to it:
for j in diameterList:
for i in pipelineIds:
prob += use_diameter[j] >= lpSum(use_pipeline[(i,j)])
## run the solution
prob.solve()
print("Status:", LpStatus[prob.status])
for i in diameterList:
if use_diameter[i].varValue> pressureTest:
print("Diameter Size",i)
for v in prob.variables():
print(v.name,"=",v.varValue)
This what I did for the combination part which took really long time.
xList = np.array(list(itertools.product(diameterList,repeat = numberOfPipelines)))
print len(xList)
for combination in xList:
pressures = []
for pipeline in range(numberOfPipelines):
pressures.append(pressure(combination))
pressureList = dict(zip(pipelineIds,pressures))
pipelinePressures[combination] = pressureList
print 'pipelinePressures',pipelinePressures
I would iterate through all combinations, I think you would run into memory problems otherwise trying to model ALL combinations in a MIP.
If you iterate through the problems perhaps using the multiprocessing library to use all cores, it shouldn't take long just remember only to hold information on the best combination so far, and not to try and generate all combinations at once and then evaluate them.
If the problem gets bigger you should consider Dynamic Programming Algorithms or use pulp with column generation.

Implied volatility calculation in Python

With the comments from the answer, I rewrote the code below (math.1p(x)->math.log(x)), which now should work and give a good approximation of the volatility.
I am trying to create a short code to calculate the implied volatility of a European Call option. I wrote the code below:
from scipy.stats import norm
import math
norm.cdf(1.96)
#c_p - Call(+1) or Put(-1) option
#P - Price of option
#S - Strike price
#E - Exercise price
#T - Time to expiration
#r - Risk-free rate
#C = SN(d_1) - Ee^{-rT}N(D_2)
def implied_volatility(Price,Stock,Exercise,Time,Rf):
P = float(Price)
S = float(Stock)
E = float(Exercise)
T = float(Time)
r = float(Rf)
sigma = 0.01
print (P, S, E, T, r)
while sigma < 1:
d_1 = float(float((math.log(S/E)+(r+(sigma**2)/2)*T))/float((sigma*(math.sqrt(T)))))
d_2 = float(float((math.log(S/E)+(r-(sigma**2)/2)*T))/float((sigma*(math.sqrt(T)))))
P_implied = float(S*norm.cdf(d_1) - E*math.exp(-r*T)*norm.cdf(d_2))
if P-(P_implied) < 0.001:
return sigma
sigma +=0.001
return "could not find the right volatility"
print implied_volatility(15,100,100,1,0.05)
This yields: 0.595 volatility which should be somewhere 0.3203. That is a huge difference...
I know this is not a fast method by any means, I just want to demonstrate how the principle works, but I am not able to calculate a good approximation.
For some reason when I call the function it gives me really bad approximation of the actual implied volatility which I calculated using a Matlab Program and the following webpage: Implied Volatility. Could anyone please help me to figure out where I made the mistake?
There are two problems I see, none of which are directly python related:
You are using log1p(x), which is the natural logarithm of 1+x, while you actually want log(x), which is the natural logarithm of x (cf. Wikipedia).
An option price of 100 is way to high considering the other parameters. Try to calculate the implied volatility for a price of 10 - which should be about 0.18 both by your program and the calculator you linked.
In Python2, the result of 5 / 2 is 2. It uses floor division. To fix that, make every number a float. In your implied_volatility function, change P = Price to P = float(Price), S = Stock to S = float(Stock), etc.

Categories

Resources