I have a simple working annuity loan calculator written in python, which gives correct results when compared to calculators online. That is, the monthly amount (what part is interest, what is the downpayment amount etc) and the effective interest rate (EIR). It uses two numpy-functions, ppmt and ipmt
loanAmount = 100000
monthlyIntRate = 2.5 / 12
effectiveIntRate = 100 * ((1 + monthlyIntRate/100.)**12 - 1)
However, when I add a monthly fee to the payments, my EIR changes, but does no longer equal the answers given by online loan calculators.
monthlyFee = -5
monthlyIntToBePaid = np.ipmt(rate, per, nPer, loanAmount)
monthDownPay = np.ppmt(rate, per, nPer, loanAmount)
amountDue = monthlyInt + monthDownPay + monthlyFee
Everything else, is still in perfect agreement. I think my formula is a somewhat ok approximation, but I would like to know a better way to do this!
effectiveIntRate = 100 * ((1+ monthlyIntRate/100.)**12 - 1)
effectiveIntRate += 100 * monthlyFee*12*2./loanAmount # <-- this line!
Try this (uses IRR to find the rate after fee):
nPer=12
rate=monthlyIntRate/100.
Monthpay=np.pmt(rate, nPer, loanAmount, fv=0)
amountDue = Monthpay + monthlyFee
effectiveIntRate = 100 * ((1+ monthlyIntRate/100.)**12 - 1)
#effectiveIntRate += 100 * monthlyFee*12*2./loanAmount # <-- this line!
monthpays = [-amountDue] * nPer
monthpaysf=[-loanAmount] + monthpays
efratem=np.irr(monthpaysf)
effectiveIntRateF = 100 * ((1 + efratem)**12 - 1)
print(efratem*100,effectiveIntRateF)
(0.21749271256861213, 2.6413600327578557)
Related
I am trying to modify the function below so that it gives the expected output below. For the first calculation it go like 100+ 100*-87/100 = 13 with the equation NonC_Amount + Int_amount * np.cumprod(PnL / 100). Since -87 is the first element in the PnL, for the second calculation it will go as 13 + 100*-4/100 = 9. the NonC_Amounts value is updated.
PnL = np.array([-87., -4., -34.1, 8.5])
Int_amount = 100
NonC_Amount = 100
PnL.prod(initial=Int_amount)
NonCompounding =NonC_Amount + Int_amount * np.cumprod(PnL / 100)
Current Output:
[ 13, 103.48 , 98.81332, 99.8991322]
Expected Output:
[ 13, 9, -25.1, -16.6]
You do the wrong calculations. From your description it seems you want to do
NonC_Amount = 100
NonCompounding = np.zeros_like(PnL)
for i in range(PnL.shape[0]):
NonCompounding[i] = NonC_Amount + Int_amount * PnL[i] / 100
NonC_Amount = NonCompounding[i]
Edit: If you want this operation vectorised, you can do
NonCompounding = NonC_Amount + np.cumsum(Int_amount * PnL / 100)
np.cumprod() gives you the cumulative product up to the ith element. For np.cumprod(PnL) that'd be
[-87, -87*(-4), -87*(-4)*(-34.1), etc...]
and it's only by pure chance it gives you the correct result for the first element.
I have the following optimization problem:
Assign a set of Users to a set of Shifts that minimizes labor cost. Each user has his own hourly wage but the caveat is that any hour worked above a certain overtime threshold needs to be counted with a wage multiplier. E.g. if the threshold if 5 hours and the shift is 8 hours then 5 hours would be paid with regular user wage and the remaining 3 with the wage multiplied by a predefined factor. And it carries over to shifts being worked later in the same date range (e.g. a week) so if a Monday shift may cause a shift on Friday to be counted for overtime.
Example:
threshold: 5
multiplier: 2
wage: 10,
shift duration: 8
cost = 5 * 10 + (8 - 5) * 10 * 2 = 110
I'm modelling my problem with pyomo library for python and I ran into an issue with
Evaluating Pyomo variables in a Boolean context, e.g.
Here is the complete example code that I'm trying to run:
import numpy
from datetime import datetime
from pyomo.environ import *
from pyomo.opt import SolverFactory
users = ['U1', 'U2', 'U3']
shifts = ['S1', 'S2'] # shifts in a chronological order
user_data = {
'U1': dict(wage=10),
'U2': dict(wage=20),
'U3': dict(wage=30),
}
shifts_data = {
'S1': dict(dtstart=datetime(2020, 2, 15, 9, 0), dtend=datetime(2020, 2, 15, 18, 0)),
'S2': dict(dtstart=datetime(2020, 2, 15, 19, 0), dtend=datetime(2020, 2, 15, 23, 0))
}
OVERTIME_THRESHOLD = 5 # hours
OVERTIME_MULTIPLIER = 2
model = ConcreteModel()
# (user, shift) binary pairs. If 1 then "user" works the given "shift"
model.assignments = Var(((user, shift) for user in users for shift in shifts), within=Binary, initialize=0)
def get_shift_hours(shift):
return (shifts_data[shift]['dtend'] - shifts_data[shift]['dtstart']).total_seconds() / 3600
def get_shift_cost(m, shift, shift_index, user):
shift_hours = get_shift_hours(shift)
all_hours_including_shift = sum(get_shift_hours(s) * m.assignments[user, s] for i, s in enumerate(shifts) if i <= shift_index)
# overtime hours are any hours above the OVERTIME_THRESHOLD threshold
ot_hours_including_shift = max(0, all_hours_including_shift - OVERTIME_THRESHOLD)
all_hours_excluding_shift = sum(get_shift_hours(s) * m.assignments[user, s] for i, s in enumerate(shifts) if i < shift_index)
ot_hours_excluding_shift = max(0, all_hours_excluding_shift - OVERTIME_THRESHOLD)
shift_ot_hours = ot_hours_including_shift - ot_hours_excluding_shift
shift_reg_hous = shift_hours - shift_ot_hours
return user_data[user]['wage'] * (shift_reg_hous + OVERTIME_MULTIPLIER * shift_ot_hours)
def obj_rule(m):
s = 0
# if a shift gets scheduled it has a negative impace on the objective function so it maximizes the number of scheduled shifts
s = s - sum(m.assignments[user, shift] * 1000000 for user in users for shift in shifts)
for user in users:
for shift_index, shift in enumerate(shifts):
# add the cost of a shift if the "user" was assigned to it (via the binary decision variable)
s = s + m.assignments[user, shift] * get_shift_cost(m, shift, shift_index, user)
return s
model.constraints = ConstraintList()
"""
Constraints that ensure the same user is not scheduled for overlapping shifts
"""
def shifts_overlap(shift_1, shift_2):
s1 = shifts_data[shift_1]
s2 = shifts_data[shift_2]
return s2['dtstart'] < s1['dtend'] and s2['dtend'] > s1['dtstart']
for shift_1 in shifts:
for shift_2 in shifts:
if shift_1 == shift_2 or not shifts_overlap(shift_1, shift_2):
continue
for user in users:
model.constraints.add(
1 >= model.assignments[user, shift_1] + model.assignments[user, shift_2]
)
"""
Constraints that a shift has only 1 assignee
"""
for shift in shifts:
model.constraints.add(
1 >= sum(model.assignments[user, shift] for user in users)
)
"""
End constraints
"""
model.obj = Objective(rule=obj_rule, sense=minimize)
opt = SolverFactory('cbc') # choose a solver
results = opt.solve(model) # solve the model with the selected solver
model.pprint()
I've been reading about disjunctions and piecewise constraints but I cannot find a way to apply these concepts to my problem. Any help would be much appreciated, thanks!
This can be modeled linearly and without binary variables.
Use something like:
totalHours = normalHours + overtimeHours
normalHours <= 40
cost = normalWage*normalHours + overtimePay*overtimeHours
We are lucky: overtime is more expensive so we will automatically first exhaust the normal hours.
I have set a small script that describes a diet optimization solution in pulp. The particular integers are not really relevant, they are just macros from foods. The strange thing is that when one of protein_ratio, carb_ratio or fat_ratio is 0.1, then the problem becomes infeasible. For other combinations of these factors (which always should add up to 1) the problem has a solution. Is there any way to sort of relax the objective function so that the solution might have a small error margin? For example instead of giving you the grams that will lead to a 800 calorie meal, it would give you the grams that lead to a 810 calorie meal. This would still be acceptable. Here is the script:
from pulp import *
target_calories = 1500
protein_ratio = 0.4 #play around with this - 0.1 breaks it
carb_ratio = 0.4 #play around with this - 0.1 breaks it
fat_ratio = 0.2 #play around with this - 0.1 breaks it
problem = LpProblem("diet", sense = LpMinimize)
gramsOfMeat = LpVariable("gramsOfMeat", lowBound = 1)
gramsOfPasta = LpVariable("gramsOfPasta", lowBound = 1 )
gramsOfOil = LpVariable("gramsOfOil", lowBound = 1)
problem += gramsOfMeat*1.29 + gramsOfPasta*3.655 + gramsOfOil*9 - target_calories
totalprotein = gramsOfMeat*0.21 + gramsOfPasta*0.13 + gramsOfOil*0
totalcarb = gramsOfMeat*0 + gramsOfPasta*0.75 + gramsOfOil*0
totalfat = gramsOfMeat*0.05 + gramsOfPasta*0.015 + gramsOfOil*1
totalmacros = totalprotein + totalcarb + totalfat
problem += totalfat== fat_ratio*totalmacros
problem += totalcarb == carb_ratio*totalmacros
problem += totalprotein == protein_ratio*totalmacros
problem += gramsOfMeat*1.29 + gramsOfPasta*3.655 + gramsOfOil*9 - target_calories == 0
status = problem.solve()
print(status)
#assert status == pulp.LpStatusOptimal
#print(totalmacros)
print("Grams of meat: {}, grams of pasta: {}, grams of oil: {}, error: {}".format(value(gramsOfMeat), value(gramsOfPasta), value(gramsOfOil), value(problem.objective)))
You can add a penalty for violating the target. The idea would be to introduce two new decision variables, say under and over, and add constraints that say
problem += gramsOfMeat*1.29 + gramsOfPasta*3.655 + gramsOfOil*9 - target_calories <= under
problem += target_calories - (gramsOfMeat*1.29 + gramsOfPasta*3.655 + gramsOfOil*9) <= over
Then change your objective function to something like
problem += c_under * under + c_over * over
where c_under is the penalty per unit for being under the target and c_over is the penalty for being over. (These are parameters.) If you want to impose a hard bound on the over/under, you can add new constraints:
problem += under <= max_under
problem += over <= max_over
where max_under and max_over are the maximum allowable deviations (again, parameters).
One note: Your model is a little weird because it doesn't really have an objective function. Normally in the diet problem you want to minimize cost or maximize calories or something like that, and in general in linear programming you want to minimize or maximize something. In your model, you only have constraints. True, there is something that looks like an objective function --
problem += gramsOfMeat*1.29 + gramsOfPasta*3.655 + gramsOfOil*9 - target_calories
-- but since you have constrained this to equal 0, it doesn't really have any effect. There's certainly nothing incorrect about not having an objective function, but it's unusual, and I wanted to mention it in case this is not what you intended.
I understand there is a erf (Wikipedia) function in python. But in this assignment we are specifically asked to write the error function as if it was not already implemented in python while using a while loop.
erf (x) is simplified already as : (2/ (sqrt(pi)) (x - x^3/3 + x^5/10 - x^7/42...)
Terms in the series must be added until the absolute total is less than 10^-20.
First of all - SO is not the place when people code for you, here people help you to solve particular problem not the whole task
Any way:
It's not too hard to implement wikipedia algorithm:
import math
def erf(x):
n = 1
res = 0
res1 = (2 / math.sqrt(math.pi)) * x
diff = 1
s = x
while diff > math.pow(10, -20):
dividend = math.pow((-1), n) * math.pow(x, 2 * n + 1)
divider = math.factorial(n) * (2 * n + 1)
s += dividend / divider
res = ((2 / math.sqrt(math.pi)) * s)
diff = abs(res1 - res)
res1 = res
n += 1
return res
print(erf(1))
Please read the source code carefully and post all questions that you don't understand.
Also you may check python sources and see how erf is implemented
For my school project I was trying to compute the value of using different methods. One of the formula I found was the Machin Formula that can be calculated using the Taylor expansion of arctan(x).
I wrote the following code in python:
import decimal
count = pi = a = b = c = d = val1 = val2 = decimal.Decimal(0) #Initializing the variables
decimal.getcontext().prec = 25 #Setting percision
while (decimal.Decimal(count) <= decimal.Decimal(100)):
a = pow(decimal.Decimal(-1), decimal.Decimal(count))
b = ((decimal.Decimal(2) * decimal.Decimal(count)) + decimal.Decimal(1))
c = pow(decimal.Decimal(1/5), decimal.Decimal(b))
d = (decimal.Decimal(a) / decimal.Decimal(b)) * decimal.Decimal(c)
val1 = decimal.Decimal(val1) + decimal.Decimal(d)
count = decimal.Decimal(count) + decimal.Decimal(1)
#The series has been divided into multiple small parts to reduce confusion
count = a = b = c = d = decimal.Decimal(0) #Resetting the variables
while (decimal.Decimal(count) <= decimal.Decimal(10)):
a = pow(decimal.Decimal(-1), decimal.Decimal(count))
b = ((decimal.Decimal(2) * decimal.Decimal(count)) + decimal.Decimal(1))
c = pow(decimal.Decimal(1/239), decimal.Decimal(b))
d = (decimal.Decimal(a) / decimal.Decimal(b)) * decimal.Decimal(c)
val2 = decimal.Decimal(val2) + decimal.Decimal(d)
count = decimal.Decimal(count) + decimal.Decimal(1)
#The series has been divided into multiple small parts to reduce confusion
pi = (decimal.Decimal(16) * decimal.Decimal(val1)) - (decimal.Decimal(4) * decimal.Decimal(val2))
print(pi)
The problem is that I am getting the right value of pi only till 15 decimal places, no matter the number of times the loop repeats itself.
For example:
at 11 repetitions of the first loop
pi = 3.141592653589793408632493
at 100 repetitions of the first loop
pi = 3.141592653589793410703296
I am not increasing the repetitions of the second loop as arctan(1/239) is very small and reaches an extremely small value with a few repetitions and therefore should not affect the value of pi at only 15 decimal places.
EXTRA INFORMATION:
The Machin Formula states that:
π = (16 * Summation of (((-1)^n) / 2n+1) * ((1/5)^(2n+1))) - (4 * Summation of (((-1)^n) / 2n+1) * ((1/239)^(2n+1)))
That many terms is enough to get you over 50 decimal places. The problem is that you are mixing Python floats with Decimals, so your calculations are polluted with the errors in those floats, which are only precise to 53 bits (around 15 decimal digits).
You can fix that by changing
c = pow(decimal.Decimal(1/5), decimal.Decimal(b))
to
c = pow(1 / decimal.Decimal(5), decimal.Decimal(b))
or
c = pow(decimal.Decimal(5), decimal.Decimal(-b))
Obviously, a similar change needs to be made to
c = pow(decimal.Decimal(1/239), decimal.Decimal(b))
You could make your code a lot more readable. For starters, you should put the stuff that calculates the arctan series into a function, rather than duplicating it for arctan(1/5) and arctan(1/239).
Also, you don't need to use Decimal for everything. You can just use simple Python integers for things like count and a. Eg, your calculation for a can be written as
a = (-1) ** count
or you could just set a to 1 outside the loop and negate it each time through the loop.
Here's a more compact version of your code.
import decimal
decimal.getcontext().prec = 60 #Setting precision
def arccot(n, terms):
base = 1 / decimal.Decimal(n)
result = 0
sign = 1
for b in range(1, 2*terms, 2):
result += sign * (base ** b) / b
sign = -sign
return result
pi = 16 * arccot(5, 50) - 4 * arccot(239, 11)
print(pi)
output
3.14159265358979323846264338327950288419716939937510582094048
The last 4 digits are rubbish, but the rest are fine.