Welcome to NexusFi: the best trading community on the planet, with over 150,000 members Sign Up Now for Free
Genuine reviews from real traders, not fake reviews from stealth vendors
Quality education from leading professional traders
We are a friendly, helpful, and positive community
We do not tolerate rude behavior, trolling, or vendors advertising in posts
We are here to help, just let us know what you need
You'll need to register in order to view the content of the threads and start contributing to our community. It's free for basic access, or support us by becoming an Elite Member -- see if you qualify for a discount below.
-- Big Mike, Site Administrator
(If you already have an account, login at the top of the page)
On a similar note, I also needed a way to display time spans in easy to read English, but I was only concerned with Days, Hours, Minutes, and Seconds. Why? In OnTermination() of my strategy, I wanted to print the total runtime since the strategy had started. For example,
I wrote a routine called Duration(), which included several overloads. The main routine just takes 2 DateTime variables,
In certain cases I wanted to disable printing of the seconds (regardless if seconds > 0), thus the middle-man overload which takes a 3rd argument as a boolean. WantSeconds=false will set seconds to zero in the timespan structure, which means it won't be included in the returned string. Normally I call the 2-arg version above, except in those cases where I know I don't want to see Seconds, that's when I call this 3-arg version, setting the 3rd argument to false,
Finally, here is the routine that formats the returned string. Note how if any one of Days, Hours, Minutes or Seconds is zero, that value won't be included. The exception is if all values are zero, then you'd get the default string "0 seconds".
Similar to my Duration() routine, I wrote another routine called BriefDuration(), which uses single character suffixes for the days, hours, minutes, and seconds. See attached screenshot showing an example use case.
Thanks @bltdavid for your contributions to the thread - seeing how many times we all end up reinventing the same stuff makes me wonder what we would all do with the free time on our hands if sharing was just a bit more common
The problem is sharing code at this low, building-block level (aka, small & useful misc utility routines) is not as common as the high level sharing of entire code units (aka, indicators and strategies). My point is: sharing is happening around here, it's just naturally slanted towards sharing fuller units of code in the forms of indicators & strategies, and perhaps a full class library or two.
But I love the world of well thought-out, well-named small routines.
FWIW, I have found great general purpose code snippets on StackOverflow.com.
But finding general purpose NinjaTrader specific routines has been a (rather laborious) continual exercise of being alert to attachments and uploads, then specifically downloading those files and hunting for any useful looking stand-alone routines.
Another problem is: creating these useful well-named utility classes and/or routines (general purpose or otherwise) is a bit of a fine art, usually practiced only by dedicated and skilled programming professionals. I mean, you probably won't find many cool snippets in some quickie indicator or strategy ... mostly because they're not needed but also because not every code author has the skill or insight or desire to build great low-level standalone routines.
[In summary, I heartily agree with you. These new-fangled terms the kids use such as "refactoring" and "agile" are simply thought exercises that typically center on the reusable-ness and readable-ness of well-named and well-designed routines.]
Please allow me to chime in a little bit ... normally when you modify strings so often, they some how affect performance, mostly because there's garbage collection activity involved from the .Net framework.
A good practice, is to use the StringBuilder class that .net provides instead, i.e.:
public static string BriefDuration(TimeSpan ts)
{
StringBuilder result = new StringBuilder();
if (ts.Days > 0)
result.AppendFormat("{0}d", ts.Days);
if (ts.Hours > 0)
result.Append(" ").AppendFormat("{0}h", ts.Hours);
if (ts.Minutes > 0)
result.Append(" ").AppendFormat("{0}m", ts.Minutes);
if (ts.Seconds > 0)
result.Append(" ").AppendFormat("{0}s", ts.Seconds);
return result.ToString().Trim();
}
Not sure if the code above will give the same results as the one you posted, but hope so ...
One of my biggest complaints about NinjaTrader is the fact that some basic NT collections/arrays don't implement IEnumerable interface. This unnecesarilly complicates things - it is simply not making use of the existing .NET features.
I am a great fan of flexibility given to the user by LINQ - for an example of LINQ use for arithmetic operations on arrays see the g3VWStdDev indicator. To make use of LINQ you require to operate on enumerable, so until my requests for future @NinjaTrader releases to make that change materialize, I wrote some extension methods to collections/arrays I use most often. Obviously any conversion will always make some impact on the performance but in most cases for most uses it is still worth the flexibility of the newer .NET features (like LINQ)
Here you have:
- ToDoubleArray extensions to DataSeries with 2 overloads - complete DataSeries and a windowed one
- ToEnumerable extensions to IDataSeries with 2 overloads - complete IDataSeries and a windowed one
public static class DataSeriesExtensions
{
// Example Use: var test = MyPlot.ToDoubleArray();
public static double[] ToDoubleArray(this DataSeries series)
{
if (series == null) return null;
int seriesCount = series.Count;
double[] tempArray = new double[seriesCount];
for (int i = 0; i < seriesCount; i++)
tempArray[i] = series[i];
return tempArray;
}
// Example Use: var test = MyPlot.ToDoubleArray(14);
public static double[] ToDoubleArray(this DataSeries series, int window)
{
if (series == null) return null;
double[] tempArray = new double[window];
for (int i = 0; i < window; i++)
tempArray[i] = series[i];
return tempArray;
}
// Example Use: var test = MyPlot.ToEnumerable<double>();
// The non windowed version doesn't work properly on bar series, eg. Close, Open, etc.)
// As a workaround use: var test = Close.ToEnumerable<double>(CurrentBar);
public static IEnumerable<T> ToEnumerable<T>(this IDataSeries series) //where T: IConvertible
{
if (series == null) return null;
int window = series.Count;
T[] tempArray = new T[window];
for (int i = 0; i < window; i++)
{
tempArray[i] = (T)Convert.ChangeType(series[i], typeof(T));
}
return tempArray;
}
// Example Use: var test = MyPlot.ToEnumerable<double>(14);
public static IEnumerable<T> ToEnumerable<T>(this IDataSeries series, int window) //where T: IConvertible
{
if (series == null) return null;
T[] tempArray = new T[window];
for (int i = 0; i < window; i++)
{
tempArray[i] = (T)Convert.ChangeType(series[i], typeof(T));
}
return tempArray;
}
}
With these extensions getting average of last 10 values of MyPlot dataseries is as simple as:
var myArray = MyPlot.ToDoubleArray(10);
myArray.Average();
There are situations where my calculations/actions in the indicator or strategy depend on the combination of the parameters. My personal preference of dealing with combinations is to utilise bitwise operators. Let me demonstrate an example for enums. The example is quite naive and could be solved with 4 bools - this is just to show how it works so you can use it for more complex problems.
Let's say I want to give the user a flexible option of what detail of the trade to draw on the chart - including all the combination of the details. I will start with providing an enum:
As you can see this provides all the possible combination of objects to be drawn. I provided in the comments the values that will be assigned to individual enum values - you could use any of them if you want eg. decimal or binary but this forces you to be more cautious which values do you assign. Instead in the example for first 4 individual values I used bitwise left shift operator (<<) to provide the right individual values for us. For the combinations bitwise OR (|) to provide the right values.
Now assuming we have eg.
private DrawOrderDetails drawDetails;
and would like to check for the condition we can use bitwise operators like this:
if ((DrawOrderDetails.Entries & drawDetails) == DrawOrderDetails.Entries)
{
//DrawLine for Entry
}
But it's not the most readable code you could find.
Instead we could write an extension method to the enum:
public static class EnumExtensions
{
public static bool Includes(this DrawOrderDetails testValue, DrawOrderDetails testFor)
{
return (testFor & testValue) == testFor;
}
}
Now the test for all the conditions are much easier on the eyes:
if (drawDetails.Includes(DrawOrderDetails.Entries))
{
//DrawLine for Entry
}
if (drawDetails.Includes(DrawOrderDetails.Exits))
{
//DrawLine for Exit
}
if (drawDetails.Includes(DrawOrderDetails.Targets))
{
//DrawLine for Target
}
if (drawDetails.Includes(DrawOrderDetails.Stops))
{
//DrawLine for Stop
}
Now for example the Entry line will be drawn for the enum with that includes Entries (in decimals: 1,3,5,7,9,11,15), the same for other conditions
Now the test for all the conditions are much easier on the eyes:
if (drawDetails.Includes(DrawOrderDetails.Entries))
{
//DrawLine for Entry
}
if (drawDetails.Includes(DrawOrderDetails.Exits))
{
//DrawLine for Exit
}
if (drawDetails.Includes(DrawOrderDetails.Targets))
{
//DrawLine for Target
}
if (drawDetails.Includes(DrawOrderDetails.Stops))
{
//DrawLine for Stop
}
Love it, but I think "IsDefined" or "IsEnabled" is a better name than "Includes" ... reads a bit better.