C#: decrementing a clock using modulus math - c#

Trying to emulate the rollover of a 24 hour clock by hand (with math vs. using the timespan classes). The incrementing part was easy to figure out how to roll over from 23:00 to 0:00 and from, but getting it to go the other way is turning out to be really confusing. Here's what I have so far:
static void IncrementMinute(int min, int incr)
{
int newMin = min + incr,
hourIncrement = newMin / 60;
//increment or decrement the hour
if((double)newMin % 60 < 0 && (double)newMin % 60 > -1)
hourIncrement = -1;
Console.WriteLine("Hour increment is {0}: ", hourIncrement);
}
The problem that im finding is when going backwards, if the the modulus of is between numbers, it will not decrement correctly. Example: it is 12:00 and you subtract 61 minutes, we know the time would be 10:59 as the hour should roll back 1 hour for going from 12:00 to 11:59, then back again for going from 11:00 to 10:59. Unfortunately the way im calculating it: newMin % 60 in this case, only grabs the first hour rollback, but since the second rollback is technically -1.0166 as a remainder, and since mod only returns a whole number, its rounding off. Im sure im missing some basic math here, but could someone help me out?
EDIT: I've written this a number of ways long and short. Some are closer than others, but I know this is simpler than it seems. I know this one seems kinda "wtf was he doing", but you should be able to see basically what Im trying to do. Incrementing a clock and having it rollover from 23:59 to 0:00 is easy. Going backwards has proven to be not so easy.
OK, here's the incrementMinute with the rollover. Simple. But try to go backwards. Doesn't work.
static void IncrementMinute(int min, int incr)
{
int newMin = min + incr,
hourIncrement = newMin / 60;
min = newMin % 60;
Console.WriteLine("The new minute is {0} and the hour has incremented by {1}", min, hourIncrement);
}

I'd go for something a bit simpler
public class Clock
{
public const int HourPerDay = 24;
public const int MinutesPerHour = 60;
public const int MinutesPerDay = MinutesPerHour * HourPerDay;
private int totalMinutes;
public int Minute
{
get { return this.totalMinutes % MinutesPerHour; }
}
public int Hour
{
get { return this.totalMinutes / MinutesPerHour; }
}
public void AddMinutes(int minutes)
{
this.totalMinutes += minutes;
this.totalMinutes %= MinutesPerDay;
if (this.totalMinutes < 0)
this.totalMinutes += MinutesPerDay;
}
public void AddHours(int hours)
{
this.AddMinutes(hours * MinutesPerHour);
}
public override string ToString()
{
return string.Format("{0:00}:{1:00}", this.Hour, this.Minute);
}
}
Sample usage :
new Clock().AddMinutes(-1); // 23:59
new Clock().AddMinutes(-61); // 22:59
new Clock().AddMinutes(-1441); // 23:59
new Clock().AddMinutes(1); // 00:01
new Clock().AddMinutes(61); // 01:01
new Clock().AddMinutes(1441); // 00:01

You might try calculating both minute and hour increments first, then handling cases where the new minutes crosses an hour boundary, something like this:
int hourIncrement = incr / 60;
int minIncrement = incr % 60;
int newMin = min + minIncrement;
if (newMin < 0)
{
newMin += 60;
hourIncrement--;
}
else if (newMin > 60)
{
newMin -= 60;
hourIncrement++;
}
Edit
I like #Ben Voigts answer, but was wondering if there would be any difference in performance. I ran the console application below to time them both, and was a little surprised by the results.
40 ms for the code above
2876 ms for Ben's answer
This was done in a release build. Can anyone else run this and confirm? Am I making any mistakes in the way I time them?
using System;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
int max = 100000000;
sw.Start();
for (int i = 0; i < max; i++)
IncrementMinute1(0, -61);
sw.Stop();
Console.WriteLine("IncrementMinute1: {0} ms", sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for (int i = 0; i < max; i++)
IncrementMinute2(0, -61);
sw.Stop();
Console.WriteLine("IncrementMinute2: {0} ms", sw.ElapsedMilliseconds);
Console.ReadLine();
}
static void IncrementMinute1(int min, int incr)
{
int hourIncrement = incr / 60;
int minIncrement = incr % 60;
int newMin = min + minIncrement;
if (newMin < 0)
{
newMin += 60;
hourIncrement--;
}
else if (newMin > 60)
{
newMin -= 60;
hourIncrement++;
}
}
static void IncrementMinute2(int min, int incr)
{
min += incr;
int hourIncrement = (int)Math.Floor(min / 60.0);
min -= hourIncrement * 60;
}
}
}

Modular mathematics is only defined for the integers. If you are attempting to mix modular arithmetic with real numbers you will not succeed. You need to figure out a different mathematical approach.

Try
int newMin = min + incr,
hourIncrement = (int)Math.Floor(newMin / 60.0);
min -= hourIncrement * 60;
The essential problem was that you want hourIncrement to round down, but integer division rounds toward zero. They're the same with positive numbers, but not for negative...
EDIT (getting rid of useless extra variable):
min += incr;
int hourIncrement = (int)Math.Floor(min / 60.0);
min -= hourIncrement * 60;
EDIT2 (avoid floating-point arithmetic):
min += incr;
int hourIncrement = min / 60;
min -= hourIncrement * 60;
if (min < 0) { min += 60; --hourIncrement; }

Why to complicate things
public System.Timers.Timer timer = new System.Timers.Timer(1000);
public DateTime d;
public void init()
{
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
d = new DateTime(2011, 11, 11, 23, 59, 50);
d=d.AddHours(1);
Console.Writeline(d);
d=d.AddHours(-2);
Console.Writeline(d);
timer.Enabled = true;
}
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
{
MoveClockHands();
d=d.AddSeconds(1);
Console.WriteLine(d);
}));
}
void MoveClockHands() //12 hours clock
(
s=d.Second * 6;
m=d.Minute * 6;
h=0.5 * ((d.Hour % 12) * 60 + d.Minute)
}

Related

How to make 5 minute countdown using TimeOfDay

Candlesticks on stock market charts are created every minute. I have created a count down timer to tell me how many seconds are left until next candlestick is to be created.
//logic for 1 min candlestick
const int MINUTE = 60;
int currentSecond = DateTime.UtcNow.Second;
int nextMin = MINUTE - currentSecond;
minuteLabel.Text = nextMin.ToString();
The chart can also display candlesticks every 5 minutes for a different perspective. So in this scenario a candlestick is created every 5 minutes. This is what I'm having trouble with. How do I create a count down timer to show me how much time is left until the next candlestick is to be created? This is what I have so far:
//inefficient logic for 5 min candlestick
int currentMinute = DateTime.UtcNow.Minute;
int nextFiveMin;
if (currentMinute >= 0 && currentMinute < 5) {
nextFiveMin = ((5 * MINUTE) - (currentMinute * MINUTE)) - currentSecond;
}
else if(currentMinute >= 5 && currentMinute < 10) {
nextFiveMin = ((10 * MINUTE) - (currentMinute * MINUTE)) - currentSecond;
}
else if (currentMinute >= 10 && currentMinute < 15) {
nextFiveMin = ((15 * MINUTE) - (currentMinute * MINUTE)) - currentSecond;
}
//etc all the way to currentMinute > 55
TimeSpan t = TimeSpan.FromSeconds(nextFiveMin);
fiverLabel.Text = t.ToString(#"mm\:ss");
Although this code works fine I think that there's probably a much easier way to implement this that I can't think of.
You can just do this:
int currentMinute = DateTime.UtcNow.Minute;
int diffMinutes = (currentMinute/5 +1) * 5;
int nextFiveMin = ((diffMinutes * MINUTE) - (currentMinute * MINUTE)) - currentSecond;
Another approach:
private void timer1_Tick(object sender, EventArgs e)
{
// from the current time, strip the seconds, then add one minute:
DateTime dt = DateTime.Today.Add(new TimeSpan(DateTime.Now.Hour, DateTime.Now.Minute, 0)).AddMinutes(1);
// keep adding minutes until it's a multiple of 5
while (dt.Minute % 5 != 0)
{
dt = dt.AddMinutes(1);
}
// display how much time until the next five minute mark:
TimeSpan t = dt.Subtract(DateTime.Now);
fiverLabel.Text = t.ToString(#"mm\:ss");
}

C# Winforms: Returning values to a button event

I'm completely new to C# programming, and I'm trying to make a custom calculator using Windows Forms.
Three text boxes txtMinSkill, txtMaxSkill, txtCooldown should be keyed values into, and clicking a button buttonCalculate should do some calculations and present the result in a label resultLabel.
I have managed to get everything working down to the skill.Display function, but I have no idea how to display the variables hours, minutes, seconds in the label. When I try to access them from within the button event, I just get a message that it does not exist in the current context. And I can't access the label from the Display method.
Can anyone assist? Thanks!
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void buttonCalculate_Click(object sender, EventArgs e)
{
double minSkill;
double maxSkill;
double coolDown;
minSkill = float.Parse(txtMinSkill.Text) * 10;
maxSkill = float.Parse(txtMaxSkill.Text) * 10;
coolDown = float.Parse(txtCooldown.Text);
SkillGainCalculator skill = new SkillGainCalculator();
skill.IntegerMax(maxSkill);
skill.RemainderMax(maxSkill);
skill.RemainderMin(minSkill);
skill.IntegerMin(minSkill);
skill.Calculate(minSkill,maxSkill);
skill.Display(coolDown);
}
}
class SkillGainCalculator
{
//member variables
private int integerMax;
private int remainderMax;
private int integerMin;
private int remainderMin;
private double counter;
const int constant = 6480;
private int i;
private double totalTime;
private int hours;
private int minutes;
private int seconds;
public double IntegerMax(double intMax)
{
integerMax = (int)((1000 - intMax) / 50);
return integerMax;
}
public int RemainderMax(double intMax)
{
remainderMax = (int)((1000 - intMax) % 50);
return remainderMax;
}
public int RemainderMin(double intMin)
{
remainderMin = (int)((1000 - intMin) % 50);
return remainderMin;
}
public int IntegerMin(double intMin)
{
if (remainderMin == 0)
{
integerMin = (int)((1000 - intMin) / 50) - 1;
return integerMin;
}
else
{
integerMin = (int)((1000 - intMin) / 50);
return integerMin;
}
}
public double Calculate(double intMax, double intMin)
{
for (i = integerMax; i <= integerMin; i++)
{
if (i == integerMax && remainderMax != 0)
{
if (intMax <= 700)
{
counter = counter + constant * Math.Pow(0.8, i) * (50 - remainderMax) / 50;
}
else
{
counter = counter + constant * Math.Pow(0.8, i) * (50 - remainderMax) / 50;
}
}
else if (i == integerMin && remainderMin != 0)
{
if (intMin < 700)
{
counter = counter + constant * Math.Pow(0.8, i) * remainderMin / 50;
}
else
{
counter = counter + constant * Math.Pow(0.8, i) * remainderMin / 50;
}
}
else if (i >= 6)
{
counter = counter + constant * Math.Pow(0.8, i);
}
else
{
counter = counter + constant * Math.Pow(0.8, i);
}
}
return counter;
}
public void Display(double clD)
{
totalTime = counter * clD / 3600;
hours = (int)(counter * clD / 3600);
minutes = (int)((totalTime - hours) * 3600 / 60);
seconds = (int)((totalTime - hours) * 3600 % 60);
}
}
I have no idea, what your code does and as #Steve already said your question misses some key infos. Nevertheless try changing your two methods Display and buttonCalculate_Click like this:
public string Display(double clD)
{
totalTime = counter * clD / 3600;
hours = (int)(counter * clD / 3600);
minutes = (int)((totalTime - hours) * 3600 / 60);
seconds = (int)((totalTime - hours) * 3600 % 60);
return "Hours: " + hours + ", Minutes: " + minutes + ", Seconds: " + seconds;
}
private void buttonCalculate_Click(object sender, EventArgs e)
{
double minSkill;
double maxSkill;
double coolDown;
minSkill = float.Parse(txtMinSkill.Text) * 10;
maxSkill = float.Parse(txtMaxSkill.Text) * 10;
coolDown = float.Parse(txtCooldown.Text);
SkillGainCalculator skill = new SkillGainCalculator();
skill.IntegerMax(maxSkill);
skill.RemainderMax(maxSkill);
skill.RemainderMin(minSkill);
skill.IntegerMin(minSkill);
skill.Calculate(minSkill, maxSkill);
resultLabel.Text = skill.Display(coolDown);
}
The method Display now generates the string you want to display and returns it when called so you can set resultLabel.Text from you calling method:

RSI vs Wilder's RSI Calculation Problems

I am having trouble getting a smoothed RSI. The below picture is from freestockcharts.com. The calculation uses this code.
public static double CalculateRsi(IEnumerable<double> closePrices)
{
var prices = closePrices as double[] ?? closePrices.ToArray();
double sumGain = 0;
double sumLoss = 0;
for (int i = 1; i < prices.Length; i++)
{
var difference = prices[i] - prices[i - 1];
if (difference >= 0)
{
sumGain += difference;
}
else
{
sumLoss -= difference;
}
}
if (sumGain == 0) return 0;
if (Math.Abs(sumLoss) < Tolerance) return 100;
var relativeStrength = sumGain / sumLoss;
return 100.0 - (100.0 / (1 + relativeStrength));
}
https://stackoverflow.com/questions/...th-index-using-some-programming-language-js-c
This seems to be the pure RSI with no smoothing. How does a smoothed RSI get calculated? I have tried changing it to fit the definitions of the these two sites however the output was not correct. It was barely smoothed.
(I don't have enough rep to post links)
tc2000 -> Indicators -> RSI_and_Wilder_s_RSI (Wilder's smoothing = Previous MA value + (1/n periods * (Close - Previous MA)))
priceactionlab -> wilders-cutlers-and-harris-relative-strength-index (RS = EMA(Gain(n), n)/EMA(Loss(n), n))
Can someone actually do the calculation with some sample data?
Wilder's RSI vs RSI
In order to calculate the RSI, you need a period to calculate it with. As noted on Wikipedia, 14 is used quite often.
So the calculation steps would be as follows:
Period 1 - 13, RSI = 0
Period 14:
AverageGain = TotalGain / PeriodCount;
AverageLoss = TotalLoss / PeriodCount;
RS = AverageGain / AverageLoss;
RSI = 100 - 100 / (1 + RS);
Period 15 - to period (N):
if (Period(N)Change > 0
AverageGain(N) = ((AverageGain(N - 1) * (PeriodCount - 1)) + Period(N)Change) / PeriodCount;
else
AverageGain(N) = (AverageGain(N - 1) * (PeriodCount - 1)) / PeriodCount;
if (this.Change < 0)
AverageLoss(N) = ((AverageLoss(N - 1) * (PeriodCount - 1)) + Math.Abs(Period(N)Change)) / PeriodCount;
else
AverageLoss(N) = (AverageLoss(N - 1) * (PeriodCount - 1)) / PeriodCount;
RS = AverageGain / AverageLoss;
RSI = 100 - (100 / (1 + RS));
Thereafter, to smooth the values, you need to apply a moving average of a certain period to your RSI values. To do that, traverse your RSI values from the last index to the first and calculate your average for the current period based on the preceding x smoothing periods.
Once done, just reverse the list of values to get the expected order:
List<double> SmoothedRSI(IEnumerable<double> rsiValues, int smoothingPeriod)
{
if (rsiValues.Count() <= smoothingPeriod)
throw new Exception("Smoothing period too large or too few RSI values passed.");
List<double> results = new List<double>();
List<double> reversedRSIValues = rsiValues.Reverse().ToList();
for (int i = 1; i < reversedRSIValues.Count() - smoothingPeriod - 1; i++)
results.Add(reversedRSIValues.Subset(i, i + smoothingPeriod).Average());
return results.Reverse().ToList();
}
The Subset method is just a simple extension method as follows:
public static List<double> Subset(this List<double> values, int start, int end)
{
List<double> results = new List<double>();
for (int i = start; i <= end; i++)
results.Add(values[i]);
return results;
}
Disclaimer, I did not test the code, but it should give you an idea of how the smoothing is applied.
You can't get accurate values without buffers / global variables to store data.
This is a smoothed indicator, meaning it doesn't only use 14 bars but ALL THE BARS:
Here's a step by step article with working and verified source codes generating exactly the same values if prices and number of available bars are the same, of course (you only need to load the price data from your source):
Tested and verified:
using System;
using System.Data;
using System.Globalization;
namespace YourNameSpace
{
class PriceEngine
{
public static DataTable data;
public static double[] positiveChanges;
public static double[] negativeChanges;
public static double[] averageGain;
public static double[] averageLoss;
public static double[] rsi;
public static double CalculateDifference(double current_price, double previous_price)
{
return current_price - previous_price;
}
public static double CalculatePositiveChange(double difference)
{
return difference > 0 ? difference : 0;
}
public static double CalculateNegativeChange(double difference)
{
return difference < 0 ? difference * -1 : 0;
}
public static void CalculateRSI(int rsi_period, int price_index = 5)
{
for(int i = 0; i < PriceEngine.data.Rows.Count; i++)
{
double current_difference = 0.0;
if (i > 0)
{
double previous_close = Convert.ToDouble(PriceEngine.data.Rows[i-1].Field<string>(price_index));
double current_close = Convert.ToDouble(PriceEngine.data.Rows[i].Field<string>(price_index));
current_difference = CalculateDifference(current_close, previous_close);
}
PriceEngine.positiveChanges[i] = CalculatePositiveChange(current_difference);
PriceEngine.negativeChanges[i] = CalculateNegativeChange(current_difference);
if(i == Math.Max(1,rsi_period))
{
double gain_sum = 0.0;
double loss_sum = 0.0;
for(int x = Math.Max(1,rsi_period); x > 0; x--)
{
gain_sum += PriceEngine.positiveChanges[x];
loss_sum += PriceEngine.negativeChanges[x];
}
PriceEngine.averageGain[i] = gain_sum / Math.Max(1,rsi_period);
PriceEngine.averageLoss[i] = loss_sum / Math.Max(1,rsi_period);
}else if (i > Math.Max(1,rsi_period))
{
PriceEngine.averageGain[i] = ( PriceEngine.averageGain[i-1]*(rsi_period-1) + PriceEngine.positiveChanges[i]) / Math.Max(1, rsi_period);
PriceEngine.averageLoss[i] = ( PriceEngine.averageLoss[i-1]*(rsi_period-1) + PriceEngine.negativeChanges[i]) / Math.Max(1, rsi_period);
PriceEngine.rsi[i] = PriceEngine.averageLoss[i] == 0 ? 100 : PriceEngine.averageGain[i] == 0 ? 0 : Math.Round(100 - (100 / (1 + PriceEngine.averageGain[i] / PriceEngine.averageLoss[i])), 5);
}
}
}
public static void Launch()
{
PriceEngine.data = new DataTable();
//load {date, time, open, high, low, close} values in PriceEngine.data (6th column (index #5) = close price) here
positiveChanges = new double[PriceEngine.data.Rows.Count];
negativeChanges = new double[PriceEngine.data.Rows.Count];
averageGain = new double[PriceEngine.data.Rows.Count];
averageLoss = new double[PriceEngine.data.Rows.Count];
rsi = new double[PriceEngine.data.Rows.Count];
CalculateRSI(14);
}
}
}
For detailed step-by-step instructions, I wrote a lengthy article, you can check it here: https://turmanauli.medium.com/a-step-by-step-guide-for-calculating-reliable-rsi-values-programmatically-a6a604a06b77
P.S. functions only work for simple indicators (Simple Moving Average), even Exponential Moving Average, Average True Range absolutely require global variables to store previous values.

Round any n-digit number to (n-1) zero-digits

Sorry hard to formulate.
I need to round like this:
12 -> 10
152 -> 200
1538 -> 2000
25000 -> 30000
etc.
Twisting my head, but can't see how to make this. Must work for any n number of digits. Anyone got an elegant method for it?
c# or vb.net
How about this:
double num = 152;
int pow = (int)Math.Log10(num);
int factor = (int)Math.Pow(10, pow);
double temp = num / factor;
double result = Math.Round(temp) * factor;
I think you should try with something like this:
public int Round( int number)
{
int power = number.ToString().Length - 1;
int sz = Math.Pow(10, power);
int rounded = (int)Math.Round( number / sz );
return rounded * sz;
}
The idea is to get the size of the nearest 10 power, available by the length of the number expressed as a string. Then divide the number by that power, leaving it like 1,2 and then round it using the Math.Round method and restore the size by remultiplying it to the power.
Much like the previous answer...
I would do it this way:
double d = 25000;
int power = d.ToString().Length - 1;
double multipler = Math.Pow(10,power);
d = Math.Round(d / multipler) * multipler;
Console.WriteLine(d);
One of the way could be
Convert the number to Decimal
Divide it by 10^(n-1) (where n is number of digits)
Now use round function (Decimal.Round)
Multiply again by 10^(n-1)
Divide the number by 10n and round the result, then multiply the result back with 10n;
int MyRound(int num)
{
double divisor = Math.Pow(10, num.ToString().Length - 1);
return (int)(Math.Round(num / divisor, MidpointRounding.AwayFromZero) * divisor);
}
Note that we should use MidpointRounding.AwayFromZero when rounding because of the default banker's rounding.
int MakeOneSigFig(int value)
{
int neg = 1;
if(value <= 10 && value >= -10) { return value; }
if(value == int.MinValue) { value = int.MaxValue; neg = -1; }
if(value < 0) { value = -value; neg = -1; }
int mult = 10; // start at 10 because we've got 'firstDigit = value / 10' below
while(value > 99) { value /= 10; mult *= 10; }
int firstDigit = value / 10;
if(value % 10 >= 5) firstDigit++;
return neg * firstDigit * mult;
}
This is equivalent to MidpointRounding.AwayFromZero. This method doesn't do any double math or string conversions. If you didn't want to loop, you could replace that with the if block below. That would be more efficient, but more code and not quite as easy to read.
if(value < 100) { mult = 10; }
else if(value < 1000) { mult = 100; value /= 10; }
else if(value < 10000) { mult = 1000; value /= 100; }
else if(value < 100000) { mult = 10000; value /= 1000; }
// etc.

What is the most efficient way to truncate a number for a specific accuracy?

What is the most efficient way to truncate a number for a specific accuracy?
In a DateTime, Milliseconds are always comprised between 0 and 999 so you don't have anything to do.
int ms = Convert.ToInt32(
Convert.ToString(DateTime.Now.Millisecond).Substring(0, 3));
or
double Length = Math.Pow(10, (DateTime.Now.Millisecond.ToString().Length - 3));
double Truncate = Math.Truncate((double)DateTime.Now.Millisecond / Length);
EDIT:
After running both the below on the code I will post, the double method works well due to reuse of variables. Over an iteration of 5,000,000 DateTime.Now's (in which many will be skipped by both checks), the SubString() method took 9598ms, and the Double method took 6754ms.
EDIT#2: Edited in * 1000 into tests to make sure the iterations are running.
Code used to test as follows:
Stopwatch stop = new Stopwatch();
stop.Start();
for (int i = 0; i < 5000000; i++)
{
int MSNow = DateTime.Now.Millisecond * 1000;
if (MSNow.ToString().Length > 2)
{
int ms = Convert.ToInt32(
Convert.ToString(MSNow).Substring(0, 3));
}
}
stop.Stop();
Console.WriteLine(stop.ElapsedMilliseconds);
stop = new Stopwatch();
stop.Start();
for (int i = 0; i < 5000000; i++)
{
int MSNow = DateTime.Now.Millisecond * 1000;
int lengthMS = MSNow.ToString().Length;
if (lengthMS > 2)
{
double Length = Math.Pow(10, (lengthMS - 3));
double Truncate = Math.Truncate((double)MSNow / Length);
}
}
stop.Stop();
Console.Write(stop.ElapsedMilliseconds);
Console.ReadKey();
Math.Floor(num * Math.Pow(10, x) + 0.5) / Math.Pow(10, x)
Where x your precision

Categories

Resources