public class TrendIndicatorBase : Indicator { private string _test = "test"; [NinjaScriptProperty] [Display(Name = "Test", Description = "Testing.", GroupName = "Parameters", Order = 0)] public string Test { get { return _id; } set { _test = value; } } public void override OnBarUpdate() { } } public class OptimisticTrend : TrendIndicatorBase { } public class AggressiveTrend : TrendIndicatorBase { }
Announcement
Collapse
No announcement yet.
Partner 728x90
Collapse
NinjaTrader
Base class properties not included in indicators factory methods
Collapse
X
-
Base class properties not included in indicators factory methods
The properties for base classes are not included in the generated factory methods for indicators. Example:
Code: -
I wanted to follow up on this item.
It looks like this is not currently matching NT7 so I have submitted this item to development for further review.
Thank you for providing the sample on this, that is very helpful.
I look forward to being of further assistance.JesseNinjaTrader Customer Service
Comment
-
-
Hello,
It was requested from development to get a greater sample that demonstrates a usage of the classes you have defined.
I created a sample where I had Instantiated a class from an indicator similar to yours but that was an assumption.
Can you instead provide an example that demonstrates exactly how these classes would be instantiated, used or called and specifically what property you are trying to inherit and use?
I look forward to being of further assistance.JesseNinjaTrader Customer Service
Comment
-
Sure Jesse. No problem.
Here's one scenario:
I want to use an indicator in a strategy and also want to show the indicator on two charts (one with the strategy and one without). The strategy needs to change the parameters of the indicator but it should not affect the instances that are shown on the charts.
So, if I have SimpleStrategy and SimpleEntryIndicator defined as follows:
Code:public class SimpleEntryIndicator : Indicator { [NinjaScriptProperty] [Display(Name = "Strong Signals Only")] public bool StrongSignalsOnly { get; set; } } public class SimpleStrategy : Strategy { private SimpleEntryIndicator indicator; protected override void OnStateChanged() { if (State == State.Configure) indicator = SimpleEntryIndicator(true); } protected override void OnBarUpdate() { if (someCondition) indicator.StrongSignalsOnly = true; else indicator.StrongSignalsOnly = false; ... } }
If I didn't make the property a NinjaScriptProperty (which would be the correct way for this scenario), it gets even worse because now the indicator is cached only by Input so everything will always share the same instance.
One way to fix this is the following:
Code:public partial class Indicator { private string _id = "global"; [NinjaScriptProperty] [Display(Name = "Indicator ID")] public bool Id { get { return _id; } set { _id = value; } } } public class SimpleEntryIndicator : Indicator { [NinjaScriptProperty] [Display(Name = "Strong Signals Only")] public bool StrongSignalsOnly { get; set; } } public class SimpleStrategy : Strategy { private SimpleEntryIndicator indicator; protected override void OnStateChanged() { if (State == State.Configure) indicator = SimpleEntryIndicator("simplestrategy", true); } protected override void OnBarUpdate() { if (someCondition) indicator.StrongSignalsOnly = true; else indicator.StrongSignalsOnly = false; ... } }
There still remains a problem with this though. If the feature is implemented as is, I would be forced to provide the Id as a parameter to the factory methods each time. Ideally, what I would want is:
Code:public partial class Indicator { private string _id = "global"; [NinjaScriptProperty(Default = "global")] [Display(Name = "Indicator ID")] public bool Id { get { return _id; } set { _id = value; } } } public class SimpleEntryIndicator : Indicator { [NinjaScriptProperty(Default = false)] [Display(Name = "Strong Signals Only")] public bool StrongSignalsOnly { get; set; } #region NinjaTrader generated public SimpleEntryIndicator SimpleEntryIndicator(string id = "global", bool strongSignalsOnly = false) { return SimpleEntryIndicator(Input, id, strongSignalsOnly) } public SimpleEntryIndicator SimpleEntryIndicator(ISeries<double> input, string id = "global", bool strongSignalsOnly = false) { // Caching logic with indicator cached by Input, Id, StrongSignalOnly ... return cachedVersion ?? AddToCache(SimpleEntryIndicator(Input, id, strongSignalsOnly)); } #endregion } public class SimpleStrategy : Strategy { private SimpleEntryIndicator indicator; protected override void OnStateChanged() { if (State == State.Configure) indicator = SimpleEntryIndicator("simplestrategy", true); } protected override void OnBarUpdate() { if (someCondition) indicator.StrongSignalsOnly = true; else indicator.StrongSignalsOnly = false; ... } }
Personally, I can understand adding and augmenting features but I don't think removing features from version to version is such a good idea.
Comment
-
Hello,
Thank you for the detailed reply, unfortunately I am unsure on what the end goal is based on what is provided. Could you provide more details on what the goal is regarding what you are doing in this example?
I would need a more simplified clarification for development as I myself do not really understand what you are trying to accomplish and need further information to relay to development.
Also if you could provide a working script from NT7 that can compile and that shows what you are trying to accomplish in a simplified form that may be helpful.
I look forward to being of further assistance.JesseNinjaTrader Customer Service
Comment
-
Hi Jesse,
I'm not sure how to help you - I'm really confused. I started with a simple example which you verified works in NT7 but not NT8. That is the simple working script that demonstrates the issue. You asked for a more detailed explanation, which I provided. Now you are asking for a simple example!
FWIW, I'll summarize what I've already posted.
The end goal:
If you use NT8 to compile the script from the original post, you will notice that the generated factory methods in the OptimisticTrend and AggressiveTrend indicators do not include the Test property from their base class, TrendIndicatorBase. If you do the same in NT7, they are. That is the issue.
Additionally, it would be good to have default/optional parameters as was the case with NT7 (at least the optional ones were). This is illustrated in the last code snippet with the Indicator and SimpleEntryIndicator indicator classes. Again, compile the two indicators from the script in NT8 (Minus the example generated code I provided. You'll need to add an OnBarUpdate override for it to compile) and look at the generated factory methods in SimpleEntryIndicator. The point is they should look more like the example generated code - with default parameter values.
I hope this clarifies. I don't know how else to say it otherwise.
Comment
-
Thanks for the clarification. We were on the wrong track since we were working on the actual properties be missing from the derived type.
This is about support for indicators which are derived from a class which is derived from Indicator. To access an indicator from another indicators,strategy, Market Analyzer column NinjaTrader generated code at the bottom of an indicator allows this to happen. Its generated each time you compile and is what enabled that functionality.
With NinjaTrader 7 we did this based on the folder your .CS files were located. Based on the expanded NS support we are having in NT8 we decided to move to a regex based approach where we scan to see if a class is derived from Indicator and if so generated wrapper code at the bottom of the file.
This is an expected difference between NT7 and NT8 which means a wrapper wont be generated for custom IndicatorBase classes. I will add to the list the demand to go back to a folder like approach like NT7 to support this use case.
Thanks for taking the time to work through that with us.
Comment
-
Thanks for the response Brett!
To be clear, wrapper code is getting generated for each class in the hierarchy (except any placed in the AddOns folder). It's just the leaf classes don't include the properties (in the generated code) of any classes above them (not even properties in Indicator). I believe this is what you are saying - just wanted to make sure we're on the same page.
Also, the properties and other regular properties (that don't have the [NinjaScriptProperty] attribute) are getting picked up correctly and shown in the properties grid - so this is just a code generation issue. Again, I think your information about using regex for the generation explains the reason for this.
Comment
-
No problem and thanks for writing back to clarify. We are almost on the same page but don't think we are there yet. In my test I have no wrapper code generated as I expect for derived types from a derived indicator. Are you using beta 5?
Example:
Test A: http://screencast.com/t/pynwnQprTNvN
Has the collapsed "NinjaScript generated code. Neither change nor remove. at the bottom and is derived from Indicators. Works as expected.
Test B: http://screencast.com/t/wzDezUsNxVx
Does not have the generated code at the bottom and is derived from "TestA" indicator class which is expected.
Comment
-
Yes, I am using Beta 5.
It seems the issue is more insidious than first glance. I created two indicators like you did in the screenshots and got the same results you got. This is REALLY weird because I've been doing this without issue since I started using the beta. I have only one indicator that derives directly from Indicator. All other indicators are derived from a child class of Indicator and I've had no problems with it generating code.
I've been trying to figure out what the difference is but am having no luck so far. If I change the base class of TestB to my base class (that is derived from Indicator), the code IS generated. If I change it back to deriving from TestA, the code is NOT generated!
I develop in Visual Studio and when I make a change to TestA and save, NT compiles (chime is heard and the code updated in the NT editor). When I make a change to TestB (when it derives from TestA) from Visual Studio and save, NT does not compile and the code is not updated in the NT editor!
This is all very weird. I'm still trying to find out what conditions will cause it to generate the code for grandchildren.
Comment
-
Aha! I figured it out! Your developers have made a big boo boo!
After re-reading your post about how the inheritance is determined, I spotted a clue (regex) and came up with a theory that proved to be true. Apply any of the following changes to have the code generated:
Code:/* I can't believe public class TestB is not butter! Broken code: indicator of anything? */ public class TestB : TestA // Indicator ...
Code:public class TestB : TestA // Indicator ...
Code:public class TestAIndicator : Indicator ... public class TestB : TestAIndicator ...
Code:/* This used to be "public class TestC : MyIndicator" but there were issues. */ public class TestB : TestAIndicator ... or /* Changed from another public class in version 1: TestD. An indicator of differences in version 2. */ public class TestB : TestAIndicator ... or [Notes("There is another public class for this: it's TestZIndicator.")] public class TestB : TestAIndicator ...
Code:Regex.IsMatch(code, @"^\s*(?!//).*public\s+class\s+(?<name>\S+).*Indicator", RegexOptions.IgnoreCase);
This is a terrible use of regular expressions. The resulting code will be very brittle. A much better approach would be to compile the code and use reflection to determine hierarchy, discover properties, etc... I think this and some of the other design choices are really going to come back to bite when it's time to develop NinjaTrader IX (I suspect they are probably causing a lot of bug reports in the betas and probably caused a lot of issues during development as well). The two main offenders are introducing static classes and going from interfaces to concrete classes. This is DEFINITELY the wrong direction, IMHO. As per the SOLID principles of software development, one should code against abstractions not "concretions". These will certainly bite down the road.
</rant>
<ShamelessPitch>
This particular scenario would be, IMHO, best solved by upgrading to .Net 4.6 with C# 6.0. This is one of the prime use cases for the Roslyn API.
</ShamelessPitch>
In case my shameless pitch isn't enough, could you pretty please upgrade to .Net 4.6? I'll throw in as many pretty pleases as you guys want
P.S.
If you're hiring developers, I'm currently available. I primarily do C# and WPF but have also done C++, Ruby, Delphi and others.
Comment
-
Thanks for isolating that out, would make sense. I will forward that to the developer.
As to the <Rant> reflection would not fly or would be to unwieldy during the coding process for clients. Since we ultimately needed to add code to the file since the purpose of the code at the bottom of the file is to allow other users to use c# programming and reference your indicator during their development of their own indicators/strategy. Reflection only works in run-time and not prior to compile time where a user will be programming. Plus a user will want to enjoy all the benefits of intellisense etc. Point is reflection is for runtime use (Which we do in fact utilize reflection heavily for the exact purposes you point out) and the code at the bottom of the file is for a compile time use.
As to the <ShamelessPitch> you are correct the roslyn compiler could be used for that purpose and something we could look into. However its something that implementing the Roslyn compiler could be tricky, its much more complex of a task then what we are doing now that currently works for the majority of our user base. We did look into .net 4.6 however we will not be upgrading in the short term since most PC's don't have .net 4.6 installed yet and its just too early to adopt it (Last month there was even critical .net 4.6 bugs that would cause random application crashed being hotfixed). Its just not ready yet.
Finally we have two open developer positions, please see them here:
Software Developer - NinjaTrader Entrepreneurial, growing and accomplished firm with excellent reputation is looking to expand its staff. We excel in providing advanced technical solutions and world class customer service and support to our clients. Offices located in Denver, Colorado, and Chicago, Il
Jr. Software Developer - NinjaTraderEntrepreneurial, growing and accomplished firm with excellent reputation is looking to expand its staff. We excel in providing advanced technical solutions and world class customer service and support to our clients. Offices located in Denver, Colorado, and Chicago, Il
Comment
-
Thanks Brett!
All questions have been answered so I think this thread is complete. Without the intention of stretching it out, I just want to reply to your comment about reflection.
Using reflection would not affect the user's ability to have intellisense or any other feature. The only thing it would add is an extra compile cycle. The current process is that the user makes a code change, compiles the code and then the code generation happens (note: intellisense would not pick up any factory method signature changes since generation does not happen until the code is compiled). That would not change. The difference is you would compile the code, use reflection to do the code generation and then compile again. That's what Roslyn would do as well - you have to compile to get semantic analysis. From the user's perspective, nothing changed. The benefit of Roslyn is you could do the generation without waiting for the user to do an explicit compile! That *would* enhance intellisense as the generation could happen in real time as the user is typing!
Thanks a lot for your help! I really appreciate your time.
Comment
Latest Posts
Collapse
Topics | Statistics | Last Post | ||
---|---|---|---|---|
Started by funk10101, Today, 12:02 AM
|
0 responses
3 views
0 likes
|
Last Post
by funk10101
Today, 12:02 AM
|
||
Started by gravdigaz6, Yesterday, 11:40 PM
|
1 response
7 views
0 likes
|
Last Post Yesterday, 11:49 PM | ||
Started by MarianApalaghiei, Yesterday, 10:49 PM
|
3 responses
10 views
0 likes
|
Last Post Yesterday, 11:33 PM | ||
Started by XXtrader, Yesterday, 11:30 PM
|
0 responses
4 views
0 likes
|
Last Post
by XXtrader
Yesterday, 11:30 PM
|
||
Started by love2code2trade, 04-17-2024, 01:45 PM
|
4 responses
28 views
0 likes
|
Last Post Yesterday, 10:31 PM |
Comment