I need to be able to determine whether a particular "trade" (indicated by "signal") resulted in a profit or loss by indicating a win or loss for each.
I need Python to check the next location ( the signal or entry point or date + 1 ) in the High and Low lists ( the lists: close, highs, and lows will have the same number of values ) for an increase in value equal to or greater than 2.5% at some point beyond the entry signal.
However, I also want Python to determine if the value drops 3% or more prior to appreciating 2.5% or more.
This must occur for each entry in signal.
In essence, I need a limit to sell at 102.5% and a stop at 97%.
Unfortunately, the code I developed so far doesn't seem to be working.
What am I missing?
signals = [1,5,7]
close = [5,10,10,10.5,11,12,11.9,14,14,15,16]
highs = [7,10.2,10.1,11,12,12.1,12.2,14.5,18,19,20]
lows = [4,9.9,9.8,10,10,11.8,11.8,12,13.8,13.85,14]
for i in signals:
entry = close[i]
print i
for high in highs[i+1:]:
profit = ( ( high - entry ) / entry ) * 100
for low in lows[i+1:]:
loss = ( ( low - entry ) / entry ) * 100
if abs( loss ) < 3:
if profit >= 2.5:
print 'Win'
else:
print 'Loss'
Your profit is only calculated for highs[-1] while loss is only calculated for lows[-1]. Everything else is discarded, as you replace profit and loss in each loop.
You want to find the set of values where your condition is true. Use zip to put lows and highs together:
for i in signals:
entry = float(close[i])
for high, low in zip(high[i + 1:], low[i + 1:]):
profit = ((high - entry) / entry) * 100
loss = ((low - entry) / entry) * 100
if loss > -3:
if profit >= 2.5:
print "Win"
else:
print "Loss"
Did you already check python-libraries for backtesting? In fact I use other libraries, but there are some very popular python-based solutions such as "pybacktest", "PyAlgoTrade", or "UltraFinance". Maybe integrating such a library could be advantageous for your use case...
Related
in Chapter 5 of AI Crash Course, the author writes
nSelected = nPosReward + nNegReward
for i in range(d):
print('Machine number ' + str(i + 1) + ' was selected ' + str(nSelected[i]) + ' times')
print('Conclusion: Best machine is machine number ' + str(np.argmax(nSelected) + 1))
Why are the number of negative rewards added to the number of positive rewards? To find the best machine shouldn't we only be concerned about the machine with the most positive rewards? I'm confused as to why we need to add the negative with the positive rewards. Also I understand that this is a simulation where you randomly assign successes and and you pre assign success rates. However in a real life situation, how do you know the success rates of each slot machine ahead of time? And how do you know which machines should be assigned a "1" ? Thank you so much! Here is the full code:
# Importing the libraries
import numpy as np
# Setting conversion rates and the number of samples
conversionRates = [0.15, 0.04, 0.13, 0.11, 0.05]
N = 10000
d = len(conversionRates)
# Creating the dataset
X = np.zeros((N, d))
for i in range(N):
for j in range(d):
if np.random.rand() < conversionRates[j]:
X[i][j] = 1
# Making arrays to count our losses and wins
nPosReward = np.zeros(d)
nNegReward = np.zeros(d)
# Taking our best slot machine through beta distribution and updating its losses and wins
for i in range(N):
selected = 0
maxRandom = 0
for j in range(d):
randomBeta = np.random.beta(nPosReward[j] + 1, nNegReward[j] + 1)
if randomBeta > maxRandom:
maxRandom = randomBeta
selected = j
if X[i][selected] == 1:
nPosReward[selected] += 1
else:
nNegReward[selected] += 1
# Showing which slot machine is considered the best
nSelected = nPosReward + nNegReward
for i in range(d):
print('Machine number ' + str(i + 1) + ' was selected ' + str(nSelected[i]) + ' times')
print('Conclusion: Best machine is machine number ' + str(np.argmax(nSelected) + 1))
With more and more feedback, Thompson Sampling shifts its focus more and more from exploration to exploitation. That is, with large nSelected values across the board (due to the large N), all Beta distributions will be quite concentrated around their mean (nPosReward[i]/nSelected[i]) and for larger iterations, with increasing probability, Thompson Sampling will pick the machine that it thinks is the most rewarding. By looking at a long enough horizon, you are pushing the probability of seeing the best considered machine also being the most often picked machine close to 1.
To sum up, your intuition is correct. The machine that is the most rewarding in expectation (given the observed feedback so far) is the one that has the highest empirical mean. Due to the probabilistic phenomena I just described, if you run the algorithm for long enough, the most frequently picked and the machine with highest expected reward will coincide with probability approaching 1.
About the second part of your question, we don't know the success rates. If we knew them, the optimal algorithm would simply pick the ones with the highest success rate at all times. What we do have in real life is observing outputs from these random processes. For example, when you show an online advertisement, you don't know the probability of them clicking. However, with a gross simplification assuming everybody behaves the same way, by showing it to people and observing whether they click it or not, we learn the success rate on the fly.
Steven,
(I'm writing this almost 7 months after you posted. Hopefully you will reply and share some of your own insights, and the code that you used to gain the insight. I started the AI Crash Course in the end of November 2020, and similarly I was curious about the Thompson sampling in chapter 5. In my case I was interested mostly in the cases when Thompson Sampling doesn't select the best machine. I was curious how often the 'worst machine' was selected. So over the past six weeks I've likely tried over a thousand different variations of code to gain some insight. I've likely made over a thousand coding mistakes and hundred different rabbit holes, in an attempt to "catch" when Thompson doesn't work. As well as understand how the betaRandom function and adding the posRewards and NegRewards works. Likely there are mistakes in the following code, and the overall approach to better insight could be made more graphical, so please be kind.:-)
curlycharcoal provides some insight in his thoughtful answers. Even Hadelin in the same chapter offers the reader a lot of insight. What follows is an attempt at an "iterative", "trap the errors" approach that helped me gain some insight. We can try something like the code below and "compare" the result of posReward+negReward vs posRewardOnly.
Consider the following: First, insert a few lines of code that: accumulates posRewardsOnly. Also, insert some additional parameters to the concluding print statement so you can see the results of both. Also insert the true values of the conversion rates (i.e. the mean of the X values) so you can show the actual conversion rates used. Remove the multiple print statements about the selections for the other machines, just to clean up the output.
Second, create a large loop over most of Hadelin's original code. Then iterate over that loop. Since we inserted the posRewardOnly result into the concluding print you can compare the results of when you add in the negative rewards, vs selecting the best machine with positive rewards only. (You can think of this outer loop as a crude "AI" test environment, wherein you gain insight into which approach is a better performer. )
We even insert an array at each iteration, for which machine is selected correctly that compares the with negativeRewards vs posRewardsOnly and graph that at the end. ( I haven't done this but would be fun to see )
We can also insert an array to keep track of the original betaRandom selections on the inner loop compared to the actual best machine, and see how that selection does a drunkards walk over each time step, and eventually sobers up and selects the best machine if N is large enough usually say a few thousand time steps N>5000.
Also, we can compare if there are times where the best machine isn't selected (this will provide some insight into the error rates, of the Thompson Sampling overall), with five machines, and N=600 it's interesting to see sometimes as much as25% the best machine is not selected, and sometimes, even the worst machine is selected (although rarely).
Also, as curlycharcoal noted, that negative rewards aren't always assigned through every N, for every machine, they're only assigned when the result of the betarandom function returns a maxValue, then that machine is selected to provide "a sample". That said, if you play with the code below you might find out that your posOnlyReward idea may perform better, and converge faster than the Pos+Neg reward... or does it? ;-)
######################################################################
# Try and catch when Thompson fails:
# Also, compare performance of selecting
# based on negRewards+posRewards vs just posRewards
# 03 Jan 2021 JFB
#
#######################################################################
import numpy as np
np.set_printoptions(precision=2)
# Note the following are the target conversion rates.
# Further down pls remember to compare actual rates against selected machine.
# Also, in later versions reorder the rates from low to hi and visa-versa
# to determine if there is some "greedy Thompson" bias
# based on order of best rates.
conversionRates = [0.15, 0.04, 0.13, 0.11, 0.05]# hadelins AI Crash Course
N = 200
# Increasing N improves the result, Hadelin explains this in same chapter
# I've found that 10,000 results in about 1% error
# 2000 in about 20% error give or take when using
# Hadelin's original conversion rates above.
# 100 results results in about 48% error,
# and posRewards + negRewards disagree with posRewardOnly varying percent,
# my initial sampling of this indicates will be tricky to determine which
# performs better over a variety of situations. But Hadelin provides code
# to create "tests" with various input states and policies.
catchLimit = 100
d = len(conversionRates)
wrong = 0.0
pcntWrong = 0.0
selectedWrong = 0.0
posOnlyWrong = 0.0
pcntPosOnlyWrong = 0.0
posOnlyVsActual = 0.0
pcntPosOnlyVsActual = 0.0
nSelectedArgMax = -1
NSelectedArgMaxPosOnly = -1
for ii in range( 1, catchLimit):
################---- Original X generator----##########################
#creating the set of the bandit payouts at each time t.
# Five columns, many rows.
# a 1 value means the the slot machine
# paid out if you selected that machine at this point in time.
# this can be improved upon so we can order
# the best to worst, and visa vs.
#
X = np.zeros((N, d))
for i in range(N):
for j in range(d):
if np.random.rand() < conversionRates[j]:
X[i][j] = 1
Xmean = X.mean(axis=0)
##############-- end of the Original X generator----###################
#make arrays to count rewards from the table of losses and wins.
nPosReward = np.zeros(d)
nNegReward = np.zeros(d)
#Taking our best slot machine through beta distribution
# and updating its losses and wins.
# Taking some of the slot machines through the beta distribution,
# with the goal of
# determining which slot machine is the best.
# because sometimes the best slot machine isn't found.
for i in range(N):
selected = 0
maxRandom = 0
for j in range(d):
randomBeta = np.random.beta(nPosReward[j] + 1,
nNegReward[j] + 1)
if randomBeta > maxRandom:
maxRandom = randomBeta
selected = j
if X[i][selected] == 1:
nPosReward[selected] +=1
else:
nNegReward[selected] +=1
nSelected = nPosReward + nNegReward
nSelectedPosOnly = nPosReward
nSelectedArgMax = np.argmax(nSelected) + 1
nSelectedArgMaxPosOnly = np.argmax(nSelectedPosOnly) + 1
XMeanArgMax = np.argmax(Xmean) + 1 # find the actual true best slot machine
if ( nSelectedArgMax != XMeanArgMax and
XMeanArgMax != nSelectedArgMaxPosOnly):
#for i in range(d):
#print('Machine number ' + str(i+1) + ' was selected ' + str(nSelected[i]) + ' times')
print('Fail: Pos&Neg predct slot ' + str(nSelectedArgMax),
'posOnly predct ' + str(nSelectedArgMaxPosOnly),
'But Xconv rates', Xmean,'actual best=',XMeanArgMax,'<>' )
wrong +=1
elif ( nSelectedArgMax != XMeanArgMax and
XMeanArgMax == nSelectedArgMaxPosOnly):
print('PosOnly==Actual pos&neg ' + str(nSelectedArgMax),
'posOnly predct ' + str(nSelectedArgMaxPosOnly),
'But Xconv rates', Xmean,'actual best=',XMeanArgMax,'*' )
selectedWrong +=1
elif ( nSelectedArgMax == XMeanArgMax and
XMeanArgMax != nSelectedArgMaxPosOnly):
print('PosNeg==Actual predcts ' + str(nSelectedArgMax),
'posOnly predct ' + str(nSelectedArgMaxPosOnly),
'But Xconv rates', Xmean,'actual best=',XMeanArgMax,'***' )
posOnlyWrong +=1
elif ( nSelectedArgMax == nSelectedArgMaxPosOnly and
XMeanArgMax != nSelectedArgMax):
print('PosNeg == PosOnly but != actual ' + str(nSelectedArgMax),
'posOnly predct ' + str(nSelectedArgMaxPosOnly),
'But Xconv rates', Xmean,'actual best=',XMeanArgMax,'<>' )
wrong +=1
pcntWrong = wrong / catchLimit * 100
pcntSelectedWrong = selectedWrong / catchLimit * 100
pcntPosOnlyVsActual = posOnlyWrong / catchLimit * 100
print('Catch Limit =', catchLimit, 'N=', N)
print('<>wrong: pos+neg != Actual, and PosOnly != Actual Failure Rate= %.1f' %pcntWrong, '%')
print('* PosOnly == Actual but Actual != pos+neg Failure rate = %.1f' %pcntSelectedWrong,'%')
print('** pos+Neg == Actual but Actual != PosOnly Failure rate = %.1f' %pcntPosOnlyVsActual, '%')
############# END #################
I am a first time user of PuLP and I the last time I did linear programming, Python did not exist.
I can solve this problem with LibreOffice's Solve extension (which does LP)
But I need to do it in code.
I want to optimise a stock picking problem.
We need to pick a certain quantity of screws, say 98.
Screws are packed in packs of 25 and 100. I name those pack sizes '25' and '100'.
The cost of the pick needs to be minimised.
There is a cost to pick each pack, and there is a cost to the excess quantity picked.
The constraint is that the quantity picked >= target_qty
For example, if the cost to each unit of excess was 0.1 and the cost to pick the '25' pack was 1 and the cost to pack the '100' pack is 1.1., the cost of picking is 1 x 100 pack is
(100 - 98) *.1 + 0*1 + 1*1.1
This is cheaper than picking 4*'25' pack.
Assuming that there are dicts pack_cost{} and pack_capacity{} which both have the key pack_name,
e.g. pack_cost = {'25':1,'100':1.1} and therefore list_of_pack_names = ['25','100']
I try this:
lp_prob = pulp.LpProblem('PackSizes', pulp.LpMinimize)
packs_used = pulp.LpVariable.dicts("Packs",list_of_pack_names,lowBound=0,cat="Integer")
pack_cost = [pack_costs[pack_name]*packs_used[pack_name] for pack_name in list_of_pack_names]
excess_cost = cost_per_unit * ( sum([pack_sizes[pack_name]*packs_used[pack_name] for pack_name in list_of_pack_names])- original_qty)
lp_prob += pulp.lpSum(pack_cost) + pulp.lpSum(excess_cost) #objective function
# and constraint: total picked >= needed
lp_prob += pulp.lpSum(sum([pack_sizes[pack_name]*packs_used[pack_name] for pack_name in list_of_pack_names]) >= target_qty)
Results:
print("Status:",pulp.LpStatus[lp_prob.status])
shows Optimal
lp_prob.objective is 10*Packs_10 + 15*Packs_15 + 30*Packs_30 - 16.5
but the solution is 0 of each pack size
You may check your problem with
print(lp_prob)
You do not add any essential constraint that prevents all vars from becoming zero.
Probably, you misprinted in the constraint statement. This constraint makes the problem not trivial (check brackets):
lp_prob += pulp.lpSum(sum([pack_sizes[pack_name]*packs_used[pack_name] for pack_name in list_of_pack_names])) >= target_qty
Following is the problem set from MIT opencourseware
Part C: Finding the right amount to save away
Your semiannual raise is .07 (7%)
Your investments have an annual return of 0.04 (4%)
The down payment is 0.25 (25%) of the cost of the house
The cost of the house that you are saving for is $1M.
I am now going to try to find the best rate of savings to achieve a down payment on a $1M house in 36 months. And I want your savings to be within $100 of the required down payment.I am stuck with the bisection search and 'It is not possible to pay the down payment in three years.' this output. I am new to programmers and English.Any help is appreciated.
And here is my code:
starting_salary = float(input("Enter your starting salary: "))
months_salary = starting_salary/12
total_cost = 1000000.0
semi_annual_rate = .07
investment_return = 0.04
down_payment = total_cost * 0.25
r = 0.04
current_savings = 0.0
#months = 36
tolerance = 100
steps = 0
high = 1.0
low = 0.0
guess = (high+low)/2.0
total_salaries = 0.0
def calSavings(current_savings,months_salary,guess,month):
for months in range(0,37):
if months%6==1 and months >1:
months_salary=months_salary*(1+semi_annual_rate)
current_savings = current_savings + months_salary * guess
current_savings = current_savings * (1+0.04/12)
return(current_savings)
current_savings = calSavings(current_savings,months_salary,guess,1)
while abs(current_savings-down_payment)>=100:
if current_savings < down_payment:
low = guess
elif current_savings > down_payment:
high = guess
else:
print("It is not possible to pay the down payment in three years.")
break
guess = (low+high)/2
steps = steps +1
print("Best saving rate: ", guess)
When I run this code, it will be stuck. I don't know why.
And I don't know how to determine which is the condition of output this "It is not possible to pay the down payment in three years."
I have seen Similar questions on stackoverflow like this and this but I am not quite followed.
# user input
annual_salary = float(input('Enter your annual salary: '))
semi_annual_raise = 0.07
r = 0.04
portion_down_payment = 0.25
total_cost = 1000000
steps = 0
current_savings = 0
low = 0
high = 10000
guess_rate = (high + low)//2
# Use a while loop since we check UNTIL something happens.
while abs(current_savings - total_cost*portion_down_payment) >= 100:
# Reset current_savings at the beginning of the loop
current_savings = 0
# Create a new variable for use within the for loop.
for_annual_salary = annual_salary
# convert guess_rate into a float
rate = guess_rate/10000
# Since we have a finite number of months, use a for loop to calculate
# amount saved in that time.`enter code here`
for month in range(36):
# With indexing starting a zero, we need to calculate at the beginning
# of the loop.
if month % 6 == 0 and month > 0:
for_annual_salary += for_annual_salary*semi_annual_raise
# Set monthly_salary inside loop where annual_salary is modified
monthly_salary = for_annual_salary/12
# Calculate current savings
current_savings += monthly_salary*rate+current_savings*r/12
# The statement that makes this a bisection search
if current_savings < total_cost*portion_down_payment:
low = guess_rate
else:
high = guess_rate
guess_rate = (high + low)//2
steps += 1
# The max amount of guesses needed is log base 2 of 10000 which is slightly
# above 13. Once it gets to the 14th guess it breaks out of the while loop.
if steps > 13:
break
# output
if steps > 13:
print('It is not possible to pay the down payment in three years.')
else:
print('Best savings rate:', rate)
print('Steps in bisection search:', steps)
I've already addressed most of my points in the comments, but I will recap:
You are trying to solve a problem using a method called recursive solving, in this case using the bisection method.
The steps are as follows:
start with an initial guess you chose 0.5
Perform your calculations in a loop and iterate the initial guess, in this case you must account for the following:
Maximum number of steps before failure, remember we can always add 2 values and divide by 2, your result will tend to 0.999999.. otherwise
A certain tolerance, if your step size is not small enough 25% of 1 M is 250 000 and you might never hit that number exactly, that' why you make a tolerance interval, for example: anything between 250 000 and 251 000 --> break loop, show result.
Your if statements for changing low and high to adjust guess are correct, but you forget to re-initialize savings to 0 which means savings was going to infinity.
Now that's all said, here's a working version of your code:
starting_salary = 100000 # Assuming Annual Salary of 100k
months_salary = starting_salary/12
total_cost = 1000000.0
semi_annual_rate = .07
investment_return = 0.04
down_payment = total_cost * 0.25
print("down payment: ", down_payment)
r = 0.04
current_savings = 0.0
#months = 36
tolerance = 100
steps = 0
high = 1.0
low = 0.0
guess = (high+low)/2.0
total_salaries = 0.0
tolerance = down_payment/100 # I chose this tolerance to say if my savings are between [down_payment - (downpayment + down_payment/100)] result is admissible. (this is quite a high tolerance but you can change at leisure)
def calSavings(current_savings,months_salary,guess,month):
for months in range(0,37):
if months%6==1 and months >1:
months_salary=months_salary*(1+semi_annual_rate)
current_savings = current_savings + months_salary * guess
current_savings = current_savings * (1+0.04)
return(current_savings)
while abs(current_savings-down_payment)>=100:
current_savings = calSavings(current_savings,months_salary,guess,1)
if current_savings < down_payment:
low = guess
current_savings = 0.
elif current_savings > down_payment + tolerance:
high = guess
current_savings = 0.
if (steps > 100): # I chose a maximum step number of 100 because my tolerance is high
print("It is not possible to pay the down payment in three years.")
break
guess = (low+high)/2
steps = steps +1
print("Best saving rate: ", guess)
print("With current savings: ", current_savings)
Output:
down payment: 250000.0
Best saving rate: 0.656982421875
With current savings: 250072.3339667072
[Finished in 0.08s]
For answer visit the GitHub link - MIT has added Problem set solutions
Part C: Finding the right amount to save away
In Part B, you had a chance to explore how both the percentage of your salary that you save each month and your annual raise affect how long it takes you to save for a down payment. This is nice, but suppose you want to set a particular goal, e.g. to be able to afford the down payment in three years. How much should you save each month to achieve this? In this problem, you are going to write a program to answer that question. To simplify things, assume:
Your semiannual raise is .07 (7%)
Your investments have an annual return of 0.04 (4%)
The down payment is 0.25 (25%) of the cost of the house
The cost of the house that you are saving for is $1M.
You are now going to try to find the best rate of savings to achieve a down payment on a $1M house in 36 months. Since hitting this exactly is a challenge, we simply want your savings to be within $100 of the required down payment.
In ps1c.py, write a program to calculate the best savings rate, as a function of your starting salary.
You should use bisection search to help you do this efficiently. You should keep track of the number of steps it takes your bisections search to finish. You should be able to reuse some of the code you wrote for part B in this problem.
Because we are searching for a value that is in principle a float, we are going to limit ourselves to two decimals of accuracy (i.e., we may want to save at 7.04% or 0.0704 in decimal – but we are not going to worry about the difference between 7.041% and 7.039%). This means we can search for an integer between 0 and 10000 (using integer division), and then convert it to a decimal percentage (using float division) to use when we are calculating the current_savings after 36 months. By using this range, there are only a finite number of numbers that we are searching over, as opposed to the infinite number of decimals between 0 and 1. This range will help prevent infinite loops. The reason we use 0 to 10000 is to account for two additional decimal places in the range 0% to 100%. Your code should print out a decimal (e.g. 0.0704 for 7.04%).
Try different inputs for your starting salary, and see how the percentage you need to save changes to reach your desired down payment. Also keep in mind it may not be possible for to save a down payment in a year and a half for some salaries. In this case your function should notify the user that it is not possible to save for the down payment in 36 months with a print statement. Please make your program print results in the format shown in the test cases below.
Note: There are multiple right ways to implement bisection search/number of steps so your results may not perfectly match those of the test case.
Hints:
● There may be multiple savings rates that yield a savings amount that is within $100 of the required down payment on a $1M house. In this case, you can just return any of the possible values.
● Depending on your stopping condition and how you compute a trial value for bisection search, your number of steps may vary slightly from the example test cases.
● Watch out for integer division when calculating if a percentage saved is appropriate and when calculating final decimal percentage savings rate.
● Remember to reset the appropriate variable(s) to their initial values for each iteration of bisection search.
def profit():
price = input('Enter a price: ')
demand = 650 - (10 * price)
cost_per_item = 35
firm_profit = (price - cost_per_item) * demand
return firm_profit
How can one search through a range of prices to find the price that provides the highest profit based on this relationship between demand and the product?
It is really a mathematical problem. Your formula for profit is:
profit = (p - c) * d = (p - c) * (650 - 10 * p)
where I abbreviated p for price, c for cost_per_item, d for demand.
To maximize profit all that you need to do is to find the value of p for which derivative of profit with regard to p is zero:
d(profit) / d(p) = 650 + 10*c - 20*p = 0 =>
pmax = (650 + 10*c) / 20
If you need to pick a price from a list of possible values, pick the one closest to pmax (since profit is a parabola with regard to p and so it is symmetric around the vertical line passing through pmax).
Therefore, if you do have a list of prices (I suppose this is what you mean by "range of values") contained in a list prices, then:
best_price = min(abs(x - pmax) for x in prices)
where pmax was computed earlier.
Another possibility: first define a demand function, next a profit function and eventually use the max builtin applied to a generator expression that produces couples of values (profit, price) that max, by default, compares taking into account the first value in the couple.
>>> def demand_01(price):
... return 650-10*price
>>> def profit(price, cost, demand):
... return (price-cost)*demand(price)
>>> print(max((profit(price, 35, demand_01), price) for price in (39.95, 69.90, 99.00))
(1239.9750000000008, 39.95)
The advantage of definining separately a demand and a profit function and using max lies in
the possibility of using whatever mathematical definition (even piecewise), w/o involving the differentiability of the profitfunction and
the possibility of definining different demand functions to explore the sensitivity of the results on different assumptions.
Addendum
To have the best price and the associated maximum profit you can unpack the result returned by the max builtin:
max_profit, best_price = max( (profit(price, 35, demand_01), price)
for price in (...))
If this problem uses discreet units (for example, if you must use integers) then you just use a loop to try every possibility from a price of 0 to a price which would produce a zero demand (65 in this case, because 650 - 10*65 = 0).
I would start by moving the price from an input to a parameter of the function, so:
def profit(price):
demand = 650 - (10 * price)
cost_per_item = 35
firm_profit = (price - cost_per_item) * demand
return firm_profit
Then we define a variable to pass through the function and increment it to try every possibility:
price = 0
best_price = 0
increment = 1
while price<65:
if profit(price)>profit(best_price):
best_price = price
price += increment
If you end up needing to use a decimal increment, you might want to use the decimal module to avoid some strange, floating point behavior.
I'm learning the Python 2.5.4 programming language using the MIT OCW Scholar course 6.00.
I have written a program, but with the same input, my program's output differs from the output that the MIT people have shown the program to produce.
I wish to write a Python 2.5.4 program that will get current outstanding balance and annual interest rate as input from the user, and then uses the bisection search method to find the minimum fixed minimum monthly payment that would cause the balance to fall below zero within 12 months. Then the program displays the right monthly amount, the number of months needed to claer the outstanding amount, and the final balance.
Here's my code:
# Start of code
balance = float(raw_input('Enter the outstanding balance on your credit card: '))
interestRate = float(raw_input('Enter the annual credit card interest rate as a decimal: '))
lower = balance / 12.0
upper = ( balance * ( 1 + interestRate / 12.0 ) ** 12.0 ) / 12.0
payment = ( lower + upper ) / 2.0
currentBalance = balance
while currentBalance > 0:
for months in range(1, 13):
currentBalance = currentBalance * ( 1 + interestRate / 12.0 ) - payment
if currentBalance <= 0:
break
if currentBalance <= 0:
break
else:
lower = payment
payment = ( lower + upper ) / 2.0
currentBalance = balance
print 'RESULT'
print 'Monthly payment to pay off debt in 1 year:', '$' + str(round(payment, 2))
print 'Number of months needed:', months
print 'Balance:', '$' + str(round(currentBalance, 2))
#End of code
Does my code do the desired job? If not, where lies the problem? And, how to fix it? Even if the program is right, in what ways can it be improved?
Please do bear in mind that I'm a only a beginner.
Regards.
You have a couple of typos (which may have been introduced when copying into your posting):
you split balance on your credit / card: across a line-ending, which will give a SyntaxError
currentBalance = currentBalance * ( 1 + interestRate / 12.0 ) payment is missing a - operator before payment
The first logical error is
if currentBalance <= 0:
break
... you stop as soon as you find a higher than needed payment; you only ever increase possible payment values (lower = payment), never decrease it (you should have a clause leading to upper = payment, otherwise your bisection search will be lopsided).
Once you do that, you will have to add a new test to know when to stop looping. What is your target accuracy - payment to the nearest cent? How will you know when you have found it?
My only other suggestion would be to improve your code organization by defining some operations as functions - get_float(prompt) and final_balance(current_balance, interest_rate, num_payments) are obvious candidates.