Considerations For Compiled Assemblies
<< Click to Display Table of Contents >> Considerations For Compiled Assemblies |
Compiled assemblies (DLL's) allow you to bundle your scripts into a format that hides your proprietary code along with any supporting resources. Compiled assemblies provide distinct benefits, especially for commercially distributed code, but there are a few considerations to keep in mind. Typecasting and building resource files (sounds, images, etc.) into your assemblies must be approached differently to ensure cleanly packaged, error-free DLL's.
When creating custom enum properties, it is advised to create the enum outside of your NinjaScript class, and designating it in its own fully qualified namespace. For an example, please see here. When using the enum in code, please use the fully qualified namespace as opposed to using a using directive to shorthand the expression.
Sometimes, you may need to cast your objects to NinjaScript types, such as when iterating through the DrawObjects collection to obtain a reference to a particular Drawing Object on a chart. When running C# code which has not been compiled into an assembly, typecasting can be done normally, as in the example below:
Typecasting in code outside of a compiled assembly |
---|
protected override void OnBarUpdate() |
An obstacle arises with traditional typecasting in a compiled assembly, since the NinjaScript type you attempt to cast will be present in both your DLL and NinjaTrader's Custom.dll assembly. If you plan to compile your code into a DLL, you will need to use the dynamic type to avoid this conflict by dynamically assigning the type at runtime, using the guidelines below:
1.Loop through your collection using the interface type
2.Use ToString() to check the fully qualified namespace of the object in the loop
3.Cast the object to dynamic, and reference properties of that object assuming it is the expected type
Dynamic variables as an alternative to typecasting inside of a compiled assembly |
---|
foreach (IDrawingTool line in DrawObjects.ToList()) |
The above dynamic approach will work for primitive types. For instantiating more complex types / classes though, such as adding a new PriceLevel programmatically to an existing drawing tool, Reflection would need to used.
Instantiating more complex types such as the PriceLevels class inside of a compiled assembly |
---|
foreach (dynamic dt in DrawObjects.ToList()) |
Using dynamic variables in the technique above requires careful attention to accessing members appropriately, and thus should be avoided if you do not intend to use or distribute compiled assemblies.
•No Intelliprompt: Since the compiler cannot know which type you assume a dynamic variable to be, no Intelliprompt will be displayed to help search through type members. The same applies to Visual Studio's Intellisense or similar utilities.
•No Compile Errors: For the same reason, the compiler cannot know if you are using the variable in a way not supported by its expected type, trying to access members not present in that type, or other related errors. Thus, any such errors which would be caught by the compiler when typecasting will be missed, and will result in runtime errors instead. If a runtime error were to be triggered, the error may be more difficult to interpret.
oExample: If you tried to access "line.tag" (improper capitalization) in the examples above, you would receive the following errors:
▪Typecasting / Compile Error: "'NinjaTrader.NinjaScript.DrawingTools.HorizontalLine' does not contain a definition for 'tag' and no extension method accepting a first argument of type 'NinjaTrader.NinjaScript.DrawingTools.HorizontalLine' can be found (are you missing a using directive or an assembly reference?)"
▪dynamic / Runtime Error: "Error on calling 'OnBarUpdate' method on bar 0: 'NinjaTrader.NinjaScript.DrawingTools.DrawingTool.tag' is inaccessible due to its protection level"
When exporting a compiled assembly through NinjaTrader, no additional resource files can be added. There are two ways around this. The first is to export the DLL from NinjaTrader, then open the exported .zip file, add any additional files, and re-zip the archive, but this will result in your resource files being fully accessible to end users. The second and recommended approach is to use a fully featured IDE such as Visual Studio to build your DLL's.
For more information on how to accomplish this with Visual Studio, see the "AddOn Development Environment" section of the AddOn Development Overview page. Although the page focuses on AddOn development, the sample project it provides can be used to develop other NinjaScript types, as well.
When planning to distribute your custom drawing tools via assemblies, please understand it's paramount that you implement your own Draw. method to allow the drawing tool getting called programmatically by other NinjaScript objects.
The NinjaTrader default drawing tools would implement this via a partial class, for example you would see -
Default NinjaTrader drawing tool Draw. method handling |
---|
public static partial class Draw |
However since partial classes could not span across two assemblies, therefore a custom non partial Draw. method for your NinjaScript drawing tool would be needed.
Custom drawing tool Draw. method handling |
---|
public static class MyDrawCustom |
NinjaScript exports might not be backwards compatible with previous versions of NinjaTrader.
This is known to happen every time a new type (e.g. Enum) was introduced, since the newly introduced types are not known to prior releases of NinjaTrader
Typically an error message like the following would be seen:
"Error on calling 'SetState' method: Could not load type 'NinjaTrader.NinjaScript.Indicators.CumulativeDeltaType' from assembly 'NinjaTrader.Vendor, Version=8.0.12.0, Culture=neutral, PublicKeyToken=null'."