What I do
I am making graph of fictitious stock options.
The price is updated each second, with this function
function stockVariation($price,$max_up,$max_down)
{
// Price > 1
if($price > 1)
{
// Calculate
$ratio=(mt_rand(0,$max_up/2)-mt_rand(0,$max_down/2))/1000;
$price+=$ratio;
}
// Price <=1 (we don't want 0 or negative price...)
else
$price+=mt_rand(1,$max_up)/1000;
return round($price,3);
}
I use a max_up and max_down values (from 10 to 100) to make the price change progressively and simulate some volatility.
For example, with max_up : 40 and max_down : 45, the price will progressively go down.
My question
But the problem, is that prices generated are too much volatile, even if max_up = max_down.
The result is "non-natural". (for example +10 points in one day for a base price of 15,000).
Result of price evolution per hour in 24 hour
Perhaps making round($price,4) and divisions by 10 000 instead of 1 000, will be better ?
If anyone have an idea or an advice to generate "natural" prices evolution, thanks in advance.
There are 86400 seconds in a day, so you'll need to divide by a much larger number. And rather than adding and subtracting, you may want to multiply the current price by a factor that's slightly larger or smaller than 1. That would simulate a percentage increase or decrease, rather than an absolute gain or loss.
function stockVariation($price, $max_up, $max_down)
{
// Convert up/down to fractions of the current price.
// These will be very small positive numbers.
$random_up = mt_rand(0, $max_up) / $price;
$random_down = mt_rand(0, $max_down) / $price;
// Increase the price based on $max_up and decrease based on $max_down.
// This calculates the daily change that would result, which is slightly
// larger or smaller than 1.
$daily_change = (1 + $random_up) / (1 + $random_down);
// Since we're calling this function every second, we need to convert
// from change-per-day to change-per-second. This will make only a
// tiny change to $price.
$price = $price * $daily_change / 86400;
return round($price, 3);
}
Building upon the idea, you could use an actual volatility number. If you want e.g. a volatility of 35%/year, you can find the volatility per second. In pseudocode:
vol_yr = 0.35
vol_month = vol_yr * sqrt(1.0/12)
vol_second = vol_yr * sqrt(1.0/(252 * 86400)) # or 365 * 86400
Then, every second, you "flip a coin" and either multiply or divide current stock price by (1 + vol_second). This is the principle of how binomial trees are created to evaluate exotic stock options.
Related
I am trying to figure out if there is a way to find the percentage between 2 numbers.
It's a progress / ranking system.
I want to find the percentage the $current_exp is between the $current_min and the $current_max values, is there a way to achieve this in PHP? So far I've got to this, but it doesn't work as you progress in ranks, it doesn't treat the $current_min as 0 so when you rank up, it says you are like 75% into your next rank progression when you're in fact 0. Does this make sense?
$currentProg = ($current_exp * 100) / $current_max;
Say the current minimum is 18750 and the current maximum is 25100, the current exp is 22000... What percentage from the min to the max is the current exp? This will change each rank as the $current_min and $current_max variables get set depending on the exp of the user.
The next rank is Current min is 25100 Current max is 34230
Currently, when you are at 26000 exp, the output is saying 75.956763073327% which is not correct, it should be like 1 or 2%?
Thanks in advance 🙏
Not a good mathematician, but it looks like it should be:
(Difference of rank - minimum) / (Difference of maximum - minimum) * 100
<?php
$x = 25100;
$z = 34230;
$y = 26000;
echo ($y - $x + 1) / ($z - $x + 1) * 100; // outputs 9.8674843938232 %
Online Demo
Note: + 1 is added to both numerator and denominator to avoid divide by zero errors.
I would like to calculate the spending factor in a rule where we spend Nth time the previous payment done
Here is an example of spending.
firstPaymentAmount=10
SpendingFactor=5
PaymentCount=4
payment1: 10
payment2: 50 (= 10 x 5)
payment3: 250 (= 50 x 5)
payment4: 1250 (= 250 x 5)
At the end we get the sum of all payment made and we have :
10 + 50 + 250 + 1250 = 1560
I would like to know the formula that let me retrieve the spending factor (=5 here) by only knowing these parameters:
paymentCount = 4
initalPayment = 10
totalPaid = 1560
By knowing the formula to calculate the spendingFactor, I will then be able to know the amount and detail of each payment.
I've came with a solution. It gets an approximation of the spending factor because there is no direct formula.
Getting the exact spending factor is too CPU intensive. So, I'm calculating a near approximation first. This value will always be above the solution. I then decrease that number until I'm getting below the total paid amount.
Here is a PHP sample of what I've done.
$paymentCount = 4;
$initialPayment = 10;
$totalPaid = 1560;
//----- precalculate the factor based on total payment for faster computation
//----- this predefined factor will always be above our final factor
$estimatedSpendingFactor = exp(log($totalPaid) / $paymentCount);
//----- find the estimated spending factor
do
{
$estimatedSpendingFactor -= 0.0001;
$y = $initialPayment * (pow($estimatedSpendingFactor, $paymentCount) - 1)
/ ($estimatedSpendingFactor-1);
}
while ($y > $totalPaid);
//-----
printf("The spending factor is %f\n", $estimatedSpendingFactor);
the output will be :
The spending factor is : 5.000000
I've got a significant problem in working out the percentage tax applied to a product, due to the rounding.
For example:
If you have a product which is £1.00 including 20% tax the break down would be:
0.83 ex tax
0.17 tax
1.00 total
However, if you work out the percentage increase:
round( (( ( 1 - 0.83 ) / 0.83 ) * 100), 2);
The answer is 20.48, because the actual price ex VAT is 0.8333333333
Therefore if you calculate:
round( (( ( 1 - 0.8333333333 ) /0.8333333333 ) * 100), 2);
You get the correct answer of 20.
In this case it would obviously work to round the 20.48 down to 20, but thats not a solution because some tax rates are to 2 decimal places so the assumption can't be made that you can just round the tax rate.
Am I missing something or is this impossible without knowing the original tax percentage?
0.17 is not 20% of 0.83, so your basic assumption is inaccurate( is rounded :P ).
Don't round money, calculate it without all that rounding and display rounded if need be. That avoids having to loose the precision in calculations.
A simple test will demonstrate
$price=0.8333333333;
$taxRate=21.25;
$tax=$price*$taxRate/100;
$total=$price+$tax;
$calculatedTaxRate=(($total-$price)/$price)*100; // 21.25
Since we didn't round anywhere, we can reverse engineer the tax rate always down to the dot.
Try with 20%
$price=0.8333333333;
$taxRate=20;
$tax=$price*$taxRate/100;
$total=$price+$tax;
$calculatedTaxRate=(($total-$price)/$price)*100; // 20
Wouldn't it something like:
17% VAT
85.47 taxless
85.47x0.17 = 14.53
Total: 100
So 100/1.17 = 85.47
My little-and-dirty version:
$taxrate=22; // 22%
$price=100;
$priceMinusTax=($price/(($taxrate/100)+1));
As long as you are dealing with low precision numbers, you will get a low precision answer. If you must show low precision numbers, you can round them when you show them to the user in the view, but save them as high precision numbers in the database.
Assuming your total price is £1 and tax rate is 20%:
$vatDecimal = (float) 1 + (20 / 100);
$priceExclVAT = round((1.00 / $vatDecimal), 2);
$priceDisplay = number_format($priceExclVAT, 2, ',', '.');
echo $priceDisplay;
The user enters the total amount and tax. Sometime its come form database, we can also use this code.
$actualPrice = "";
$total = 1000;//User Entry Total Amount
$gst = 18;//User Entry GST Tax %
$calculateTax = 100+$gst;
$calculateAmount = $total*100;
$actualPrice = $calculateAmount/$calculateTax;
echo $actualPrice = round($actualPrice,2);
echo $gstAmount = round($total-$actualPrice,2);
I have a project that stores money as bigint column in a database (storing in cents). I'm planning to rewrite this thing to use BCMATH instead. I don't mind integers, but they give me some terrible rounding errors event stored in cents and I suspect I might have the same rounding errors in BCMATH. The problem arises in situations, like in this pseudocode:
$price = $some_price_in_cents * $store_price_increase; // second value is a float for final price calculation, so the result might have fractions of a cent
$price_total = $price * $qty;
$discount = // some discount in cents (might have fractions of a cent)
$discount *= $qty;
$discounted_price = $price_total - $discount;
When inserting into a database, I do round() on all values in cents. And now I have a record which says:
total price = 12134
discount = 460
discounted price = 11675
Now if I do 12134 - 460 ... I obviously get 11674 and not 11675.
I also suspect that if I changed the way things are calculated (eg. multiply everything by the QTY at the end), I'd get even different results.
Would I get this kind of behaviour using BCMATH? Would the result depend on the order of math operations? How would I properly calculate the above using BCMATH and store it in DB (assuming 2 decimal places are required)?
I believe this is what you need. Note that bcmath requires strings. The number 2 is to specify how many decimals you need.
$price = bcmul($some_price_in_cents, $store_price_increase, 2);
$price_total = bcmul($price, $qty, 2);
$discount = bcmul($qty, "discount amount", 2);
$discounted_price = bcsub($price_total, $discount, 2);
I'm working on a shipping module for wine, and was wondering if anyone could give me a hand - basically:
The wine can be shipped in cases of 8, 12 or 15 bottles, each with its own price. The module needs to take the total number of bottles in the order, and work out which combination of cases gives the lowest price. Eg in an order of 31 bottles, the lowest price works out to 1 case of 15 and two cases of 8, (rather than 2 cases of 15 and 1 of 8, or 2 of 12 and one of 8). Currently, I have the following, which almost works, but misses a few possible combinations
foreach ($rates as $case_size => $case_price)
{
$price = floor($total_bottles / $case_size) * $case_price;
$rem = $total_bottles % $case_size;
if($rem > 12)
{
//needs to use another case of 15
$price = $price + $rates[15];
}
elseif($rem > 8)
{
//needs an extra case of 12
$price = $price + $rates[12];
}
elseif($rem > 0)
{
//needs an extra case of 8
$price = $price + $rates[8];
}
$quotes[] = $price;
}
return min($quotes);
From your post your saying that the most price-effective system wouldn't just the one that has uses the lowest cost per bottle of the container, but also needs to be the most efficient at filling the containers. However your algorithm is only looking at would use the fewest large boxes possible. You need an algorithm that will completely fill each case possible.
I would do something like this: Use a recursive program to find the combination that would most completely fill each case.
function fit_case($number, $case_size) {
$rem = $number % $case_size;
$next_size=magic_voodo0();
if($rem==0) { //if perfectly fills it you're done
return ($number/$case_size)*$rates[$case_size];
} else if(($rem % $next_size)/$next_size>.5) {
//if over 50% fills the next case add the next smaller case
return floor($number/$case_size)*$rates[$case_size]+fit_case($rem, $next_size);
} else { //otherwise back off 1 of the biggest cases, and fill the rest
return (floor($number/$case_size)-1)*$rates[$case_size]+fit_case($rem, $next_size);
Hope this helps.
Different approach. Use a lookup table that has all combinations of boxes for a specific number of bottles.
1 bottle - 8
...
31 bottle - 15-8-8,15-15-8,8-8-8-8, and so on
and so on
Use another lookup table for the different rates per box per country
In your function
get table row for country prices
get the different combinations for the number of bottles
do a foreach loop on combinations
save the price and combination of first loop to variables
compare the price of the next loop with the saved value
if it is lower, save price and combination/if not, continue
loop through all combinations
return lowest price/box combination