Perhaps using np.random.normal would give better results. You could scale down the distribution to the 0.005-0.045 range using the proportion of 80% that is variable (above 0.005). Because normal distributions can still have outliers, it will be necessary to "retry" the calculation if the values go out of bounds (but that shouldn't happen too frequently unless you give a large standard deviation):
import numpy as np
def randStock(count=40,minR=0.005,maxR=0.045,sd=3):
iterations = 0
while True:
iterations += 1
r = np.random.normal(1,sd,count) #normal distribution
r -= min(r) # offset to zero
r /= max(r) # scale to 0..1
r = minR + r/sum(r)*(maxR-minR)/(maxR+minR) # scale to range
if min(r)>=minR and max(r)<=maxR: return r, iterations
Output:
for _ in range(10):
s,i = randStock()
print(*map("{:6.4f}".format,(sum(s),min(s),max(s))),i,"iterations")
[sum] [min] [max] [mean]
1.0000 0.0050 0.0404 0.0250 1 iterations
1.0000 0.0050 0.0409 0.0250 2 iterations
1.0000 0.0050 0.0395 0.0250 1 iterations
1.0000 0.0050 0.0411 0.0250 4 iterations
1.0000 0.0050 0.0410 0.0250 2 iterations
1.0000 0.0050 0.0428 0.0250 1 iterations
1.0000 0.0050 0.0433 0.0250 1 iterations
1.0000 0.0050 0.0424 0.0250 1 iterations
1.0000 0.0050 0.0371 0.0250 1 iterations
1.0000 0.0050 0.0446 0.0250 1 iterations
Note that this could be improved to randomize the lower bound a bit more and you can chose a different standard deviations