Accessing strategy data by an indicator - how?

Questions about MultiCharts .NET and user contributed studies.
User avatar
JoshM
Posts: 2075
Joined: 20 May 2011
Location: The Netherlands
Has thanked: 1523 times
Been thanked: 1480 times
Contact:

Accessing strategy data by an indicator - how?

Postby JoshM » 11 Aug 2012

Who knows how I can access strategy data (such as the TotalTrades property) in an indicator?

This is my code so far (which doesn't work):

Code: Select all

using System;
using System.Drawing;
using System.Linq;
using PowerLanguage.Function;

namespace PowerLanguage.Indicator
{
    [SameAsSymbol(false)]   
    public class MyTestIndicator : IndicatorObject
    {
        public MyTestIndicator(object _ctx) : base(_ctx) { }

        private Strategy.MACrossOver_Test myTest;
       
        private VariableSeries<Int32> numTrades;
        private IPlotObject Plot1;

        protected override void Create()
        {
            myTest    = new Strategy.MACrossOver_Test(this);
            numTrades = new VariableSeries<Int32>(this);

            Plot1 = AddPlot(new PlotAttributes("NumTrades", 0, Color.Green, Color.Empty, 1, (int)EPlotStyles.Solid, true));
        }

        protected override void StartCalc() { }
       
        protected override void CalcBar()
        {
            numTrades.Value = myTest.TotalTrades;

            Plot1.Set(0, numTrades.Value);
        }       
    }
}


As soon as I apply this indicator to the start, I get an error message about an InvalidCastException, which is of course expected (in hindsight :) ), since I pass an IndicatorObject in the Create() method to the 'myTest' StrategyObject.

But despite me understanding the error, I haven't succeeded in solving it. Does someone has an idea?

Dru
Posts: 107
Joined: 28 Aug 2007
Has thanked: 4 times
Been thanked: 170 times

Re: Accessing strategy data by an indicator - how?

Postby Dru » 14 Aug 2012

JoshM wrote:Who knows how I can access strategy data (such as the TotalTrades property) in an indicator?

All available strategy data for indicators is accessible through StrategyInfo property.

User avatar
JoshM
Posts: 2075
Joined: 20 May 2011
Location: The Netherlands
Has thanked: 1523 times
Been thanked: 1480 times
Contact:

Re: Accessing strategy data by an indicator - how?

Postby JoshM » 14 Aug 2012

Dru wrote:
JoshM wrote:Who knows how I can access strategy data (such as the TotalTrades property) in an indicator?

All available strategy data for indicators is accessible through StrategyInfo property.

Thanks Dru. For the MarketPosition, AvgEntryPrice and Equity that works, but the StrategyInfo property doesn't contain TotalTrades. According to the help, that is in the IPortfolioPerformance interface.

Does someone has an example of how to implement the IPortfolioPerformance interface? I understand that the implementer of the interface requires to implement the properties and methods of the interface. But how should one perform the actual implementation?

From what I understand of it, it's up to the designer of the implementing class to decide about how the implementation is performed. Does that mean that the programmer has to manually program how to calculate the IPortfolioPerformance properties?

In regular MultiCharts, these values are already tracked by the program itself, but in MC .NET we have to "reinvent the wheel"?

Code: Select all

using System;
using System.Drawing;
using System.Linq;
using PowerLanguage.Function;

namespace PowerLanguage.Indicator
{
    [SameAsSymbol(false)]   
    public class MyTestIndicator : IndicatorObject, IPortfolioPerformance
    {
        public MyTestIndicator(object _ctx) : base(_ctx) { }
               
        private VariableSeries<Int32> numTrades;
        private IPlotObject Plot1;

        // Properties required by IPortfolioPerformance
        public int CurrentEntries { get; set; }
        public double GrossLoss { get; set;  }
        public double GrossProfit { get; set;  }
        public double InvestedCapital { get; set; }
        public int LossTradesNumber { get; set; }
        public double MarginPerContract { get; set; }
        public double MaxIDDrawdown { get; set; }
        public double MaxOpenPositionPotentialLoss { get; set; }
        public double MaxPotentialLossPerContract { get; set; }
        public double MaxRiskEquityPerPosPercent { get; set; }
        public double NetProfit { get; set; }
        public double OpenPositionProfit { get; set; }
        public double PercentProfit { get; set; }
        public Lambda<double> PortfolioEntriesPriority { get; set; }
        public int ProfitTradesNumber { get; set;}
        public double StrategyDrawdown { get; set; }
        public double TotalMaxRiskEquityPercent { get; set; }
        public int TotalTrades { get; set; }

        public double CalcMaxPotentialLossForEntry(int side)
        {
            // ?
            return 10.0;
        }

        public double CalcMaxPotentialLossForEntry(int side, int contracts)
        {
            // ?
            return 10.0;
        }

        public double CalcMaxPotentialLossForEntry(int side, int contracts, double price)
        {
            // ?
            return 10.0;
        }

        public bool SetMaxPotentialLossPerContract(double newValue)
        {
            // ?
            return false;
        }

        protected override void Create()
        {
            numTrades = new VariableSeries<Int32>(this);

            Plot1 = AddPlot(new PlotAttributes("NumTrades", 0, Color.Green, Color.Empty, 1, (int)EPlotStyles.Solid, true));
        }

        protected override void StartCalc() { }
       
        protected override void CalcBar()
        {
            numTrades.Value = TotalTrades;
           
            Plot1.Set(0, numTrades.Value);
        }       
    }
}

MidKnight
Posts: 343
Joined: 12 Aug 2012
Has thanked: 123 times
Been thanked: 56 times

Re: Accessing strategy data by an indicator - how?

Postby MidKnight » 14 Aug 2012

Hi JoshM,

I'm a total newb with this MC.net thing (actually with multicharts in general) so I apologize in advance for any extra noise this may create.

In looking at the help docs, it looks like you should be able to get the IPortfolioPerformance handle through the SignalObject public property Portfolio.

Hopefully this helps a bit,
MK

User avatar
JoshM
Posts: 2075
Joined: 20 May 2011
Location: The Netherlands
Has thanked: 1523 times
Been thanked: 1480 times
Contact:

Re: Accessing strategy data by an indicator - how?

Postby JoshM » 14 Aug 2012

MidKnight wrote:In looking at the help docs, it looks like you should be able to get the IPortfolioPerformance handle through the SignalObject public property Portfolio.

Thanks MidKnight, no extra noise but a valuable idea.

However, I haven't succeeded in creating a SignalObject instance to call the Portfolio property on, since I can't create an instance of an abstract class. The indicator to plot the strategy information already uses the IndicatorObject base class.

Code: Select all

using System;
using System.Drawing;
using System.Linq;
using PowerLanguage.Function;

namespace PowerLanguage.Indicator
{
    [SameAsSymbol(false)]   
    public class MyTestIndicator : IndicatorObject
    {
        public MyTestIndicator(object _ctx) : base(_ctx) { }
               
        private VariableSeries<Int32> numTrades;
        private IPlotObject Plot1;
        private Strategy.SignalObject sigObj;

        protected override void Create()
        {
            numTrades = new VariableSeries<Int32>(this);

            //sigObj = ...?

            Plot1 = AddPlot(new PlotAttributes("NumTrades", 0, Color.Green, Color.Empty, 1, (int)EPlotStyles.Solid, true));
        }

        protected override void StartCalc() { }
       
        protected override void CalcBar()
        {
            numTrades.Value = sigObj.Portfolio.TotalTrades;
           
            Plot1.Set(0, numTrades.Value);
        }       
    }
}

MidKnight
Posts: 343
Joined: 12 Aug 2012
Has thanked: 123 times
Been thanked: 56 times

Re: Accessing strategy data by an indicator - how?

Postby MidKnight » 14 Aug 2012

But isn't your strategy a subclass of SignalObject? I must be missing stuff that you are trying to do.... I would imagine that you make a strategy and then, from that a strategy instance you access the portfolio property?

I do not know if you can instantiate a strategy within an indicator though, but what you are trying to do is graph some strategy stats. Maybe they only way to do it would be to extract the strategy logic into its own class so you can hook the logic into either an indicator object or a signal object.

I'm sorry mate. I'm too new to MC.net to say the "MC way" - I can only suggest the general programming approach to take :(
These users thanked the author MidKnight for the post:
JoshM

User avatar
JoshM
Posts: 2075
Joined: 20 May 2011
Location: The Netherlands
Has thanked: 1523 times
Been thanked: 1480 times
Contact:

Re: Accessing strategy data by an indicator - how?

Postby JoshM » 14 Aug 2012

MidKnight wrote:I do not know if you can instantiate a strategy within an indicator though, but what you are trying to do is graph some strategy stats. Maybe they only way to do it would be to extract the strategy logic into its own class so you can hook the logic into either an indicator object or a signal object.

I'm sorry mate. I'm too new to MC.net to say the "MC way" - I can only suggest the general programming approach to take :(

That gave me a good idea MidKnight, thanks! I've now created a class derived from SignalObject that creates a SignalObject and passes this along to the class derived from IndicatorObject. In theory, the indicator class will then have access to the strategy's Portfolio properties by calling on the passed SignalObject.

However, can someone tell me with what I should replace the 'this' keyword in the code below (line 13)? I tried passing in a 'object' but that didn't work. I know that 'this' refers to the current instance of the object (StrategyObject), but I can't pass StrategyObject in that statement.

I tried making a private method, so that would instantiate the SignalObject, but the static GetObject() method still needs to create an instance of the current class to call other non-static methods. If I set the GetObject() method to non-static, so that it can use 'this', it can't be called to return the SignalObject, because the first call to it will result in a null reference exception.

Code: Select all

public class StrategyObject : SignalObject
{
   private static StrategyObject stratObj;

   public StrategyObject(object _ctx):base(_ctx) { }

   protected override void CalcBar() { }
   
   public static StrategyObject GetObject()
   {
      if (stratObj == null)
      {
         stratObj = new StrategyObject(this);
      }
      return stratObj;
   }
}

User avatar
JoshM
Posts: 2075
Joined: 20 May 2011
Location: The Netherlands
Has thanked: 1523 times
Been thanked: 1480 times
Contact:

Re: Accessing strategy data by an indicator - how?

Postby JoshM » 16 Aug 2012

A little update.

I tried incorporating an extension method (thanks Dru for the code and RiverTrader for the explanation of it), but that didn't work due to a null reference because the 'test' object isn't instantiated before its called:

Code: Select all

using System;
using System.Drawing;
using System.Linq;
using PowerLanguage.Function;
using PowerLanguage.Strategy;

namespace PowerLanguage
{
    public static class Extensions
    {
        public static int NumOfTrades(this IPortfolioPerformance _this)
        {
            return _this.TotalTrades;
        }
    }
}

namespace PowerLanguage.Indicator
{
    [SameAsSymbol(false)]
    public class MyTestIndicator : IndicatorObject
    {
        public MyTestIndicator(object _ctx) : base(_ctx) { }

        private VariableSeries<Int32> numTrades;
        private IPlotObject Plot1;

        private IPortfolioPerformance test;

        protected override void Create()
        {
            numTrades = new VariableSeries<Int32>(this);
            Plot1 = AddPlot(new PlotAttributes("NumTrades", 0, Color.Green, Color.Empty, 1, (int)EPlotStyles.Solid, true));
        }

        protected override void StartCalc() { }

        protected override void CalcBar()
        {
            numTrades.Value = test.NumOfTrades();

            Plot1.Set(0, numTrades.Value);
        }
    }
}


I've also tried using properties, so not passing a StrategyObject to an IndicatorObject but letting the StrategyObject encapsulate the TotalTrades property, but this gave the same null reference problem. This because of the NumberOfTrades property being called on a StrategyObject that hasn't been initialized yet:

Code: Select all

using System;
using System.Drawing;
using System.Linq;
using PowerLanguage.Function;
using PowerLanguage.Strategy;

namespace PowerLanguage.Indicator
{
    [SameAsSymbol(false)]
    public class MyTestIndicator : IndicatorObject
    {
        public MyTestIndicator(object _ctx) : base(_ctx) { }

        private VariableSeries<Int32> numTrades;
        private IPlotObject Plot1;

        private StrategyObject stratObj;

        protected override void Create()
        {
            numTrades = new VariableSeries<Int32>(this);
            Plot1 = AddPlot(new PlotAttributes("NumTrades", 0, Color.Green, Color.Empty, 1, (int)EPlotStyles.Solid, true));
        }

        protected override void StartCalc() { }

        protected override void CalcBar()
        {
            numTrades.Value = stratObj.NumberOfTrades;

            Plot1.Set(0, numTrades.Value);
        }
    }

    // StrategyObject class
    public class StrategyObject : SignalObject
    {
        private StrategyObject stratObj;

        public StrategyObject(object _ctx) : base(_ctx) { }

        public int NumberOfTrades
        {
            get
            {
                if (stratObj == null)
                    MakeObject();

                return stratObj.Portfolio.TotalTrades;
            }
        }
       
        protected override void CalcBar() { }

        private StrategyObject MakeObject()
        {
            stratObj = new StrategyObject(this);
         
         return stratObj;
        }
    }
}


The obvious solution would be: instantiate the StrategyObject before calling it. So far I haven't found a way to do that, since both objects (MyTestIndicator and StrategyObject) are derived from different base classes and the 'this' keyword will then return an incompatible instance to it.

I've also started a thread on a general C# forum on this problem, but so far no solution has come out of it.

Anyway, I know it's possible since MC Support said so, now I just have to figure out how. Anyone has an idea I could try? Even a speculation would be great. :)

Dru
Posts: 107
Joined: 28 Aug 2007
Has thanked: 4 times
Been thanked: 170 times

Re: Accessing strategy data by an indicator - how?

Postby Dru » 16 Aug 2012

JoshM wrote:Anyway, I know it's possible since MC Support said so, now I just have to figure out how. Anyone has an idea I could try? Even a speculation would be great. :)

JoshM, you are on a wrong way.
The instance of the strategy object already exists and is applied on the chart. You just need get access to it.
Look on attach.
1) Add the '_MyStrategy_' to your signals on the chart.
2) Apply on that chart the indicator '_MyStrategyIndicator_'.
Attachments
StrategyAccess.pln
(2.5 KiB) Downloaded 258 times
Last edited by Dru on 16 Aug 2012, edited 1 time in total.
These users thanked the author Dru for the post:
JoshM

User avatar
JoshM
Posts: 2075
Joined: 20 May 2011
Location: The Netherlands
Has thanked: 1523 times
Been thanked: 1480 times
Contact:

Re: Accessing strategy data by an indicator - how?

Postby JoshM » 16 Aug 2012

Dru wrote:The instance of the strategy object already exists and is applied on the chart. You just need get access to it.
Look on attach.
1) Add the '_MyStrategy_' to your signals on the chart.
2) Apply on that chart the indicator '_MyStrategyIndicator_'.

Thanks Dru. You mean the ObjectContext.Attach method from the Entity framework? Just checking if I undertood you correctly, since all tutorials I've found so far on this subject deal with connections to and manipulations in databases.

Dru
Posts: 107
Joined: 28 Aug 2007
Has thanked: 4 times
Been thanked: 170 times

Re: Accessing strategy data by an indicator - how?

Postby Dru » 16 Aug 2012

Sorry, Josh
Forgot attach pnl :)
Updated
These users thanked the author Dru for the post:
JoshM

User avatar
JoshM
Posts: 2075
Joined: 20 May 2011
Location: The Netherlands
Has thanked: 1523 times
Been thanked: 1480 times
Contact:

Re: Accessing strategy data by an indicator - how?

Postby JoshM » 16 Aug 2012

Wow this is incredible and something I'd never thought of. Thanks so much Dru. :)

I don't want to overstretch your generosity, but if I may ask, is it possible to have the return value (like TotalTrades) update? Now it’s a horizontal line from the first bar to the last, which is certainly better than no value at all, but it would be even greater if the TotalTrades updates along with the strategy’s executed trades.

Perhaps something like an Update() method in the StrategiesPool class? (Doesn't work, but thinking along :) ).

Code: Select all

public static void Update(T _str)
{
    lock (typeof(StrategiesPool<T>))
    {
        s_strategies_pool.Add(_str.Environment.ChartWindowHWND, T);
    }
}


Code: Select all

namespace PowerLanguage.Indicator
{
    [SameAsSymbol(false)]
    public class MyTestIndicator : IndicatorObject
    {
        public MyTestIndicator(object _ctx) : base(_ctx) { }
       
        private VariableSeries<Int32> numTrades;
        private IPlotObject Plot1;
       
        protected override void Create()
        {
            numTrades = new VariableSeries<Int32>(this);
            Plot1 = AddPlot(new PlotAttributes("NumTrades", 0, Color.Green, Color.Empty, 1, (int)EPlotStyles.Solid, true));
        }

        protected override void StartCalc() { }

        protected override void CalcBar()
        {
            var _strat = StrategiesPool<MACrossOver_Test>.GetInstanceForChart(Environment);
            if (null != _strat)
                numTrades.Value = _strat.TotalTrades;

            Plot1.Set(numTrades.Value);
        }
    }
}

Dru
Posts: 107
Joined: 28 Aug 2007
Has thanked: 4 times
Been thanked: 170 times

Re: Accessing strategy data by an indicator - how?

Postby Dru » 21 Aug 2012

JoshM wrote:but it would be even greater if the TotalTrades updates along with the strategy’s executed trades.

It's simple.
You need to add field in the strategy class:

Code: Select all

List<int> m_total_trades_per_bar;

And add TotalTrades value for each bar from the strategy to this List.
From Indicator you just get access to this field.
These users thanked the author Dru for the post:
JoshM

User avatar
JoshM
Posts: 2075
Joined: 20 May 2011
Location: The Netherlands
Has thanked: 1523 times
Been thanked: 1480 times
Contact:

Re: Accessing strategy data by an indicator - how?

Postby JoshM » 24 Aug 2012

Thanks Dru! :)

It pretty much works:

scr.24-08-2012 17.39.24.png
scr.24-08-2012 17.39.24.png (23.53 KiB) Viewed 757 times


Two things are still disadvantageous about this solution, for which I hope there is a better way:

- Is there a robust way of passing the data quickly between the indicator and strategy? The current implementation is using CurrentBar, which is a disadvantage if the strategy get stopped out in a bar - the indicator only knows that such a thing happened at the end of that bar.

- Perhaps this is MC .NET related, but if I change one line in the source code of the indicator (even if it's a enter to make some whitespace) and save the indicator, the indicator line returns back to a horizontal zero line.

To then get the correct indicator line, I need to remove the indicator and strategy, then re-apply the strategy and change it settings to my preference, than re-apply the indicator and change it settings, and then the indicator plots the correct values. It seems to me there are easier ways to get RSI :), so I'm hoping I'm supposed to call a Dispose() or anything on a certain object to prevent these steps.

----
Code of indicator:

Code: Select all

using System;
using System.Drawing;
using System.Linq;
using PowerLanguage.Function;
using PowerLanguage.Strategy;

namespace PowerLanguage.Indicator
{
    [SameAsSymbol(false)]
    public class MyTestIndicator : IndicatorObject
    {
        public MyTestIndicator(object _ctx) : base(_ctx) { }
       
        private VariableSeries<Int32> numTrades;
        private IPlotObject Plot1;
       
        protected override void Create()
        {
            numTrades = new VariableSeries<Int32>(this);
            Plot1 = AddPlot(new PlotAttributes("NumTrades", 0, Color.Green, Color.Empty, 1, (int)EPlotStyles.Solid, true));
        }

        protected override void StartCalc() { }

        protected override void CalcBar()
        {
            var _strat = StrategiesPool<TestStrategy2>.GetInstanceForChart(Environment);
            if (null != _strat)
                numTrades.Value = _strat.m_total_trades_per_bar[Bars.CurrentBar - 1];// _strat.m_total_trades_per_bar.Count - 1];

            Plot1.Set(numTrades.Value);
        }
    }
}


Code of strategy:

Code: Select all

using System;
using System.Drawing;
using System.Linq;
using PowerLanguage.Function;
using ATCenterProxy.interop;
using System.Collections.Generic;

namespace PowerLanguage.Strategy
{
    static class StrategiesPool<T> where T : SignalObject
    {
        private static readonly Dictionary<IntPtr, T> s_strategies_pool = new Dictionary<IntPtr, T>();

        public static T GetInstanceForChart(IApplicationInfo _info)
        {
            var _chart_id = _info.ChartWindowHWND;
            lock (typeof(StrategiesPool<T>))
            {
                if (s_strategies_pool.ContainsKey(_chart_id))
                    return s_strategies_pool[_chart_id];
            }
            return null;
        }

        public static void Register(T _str)
        {
            lock (typeof(StrategiesPool<T>))
                s_strategies_pool[_str.Environment.ChartWindowHWND] = _str;
        }

        public static void UnRegister(T _str)
        {
            lock (typeof(StrategiesPool<T>))
                s_strategies_pool.Remove(_str.Environment.ChartWindowHWND);
        }
    }


   public class TestStrategy2 : SignalObject
    {
      public TestStrategy2(object _ctx):base(_ctx){}

        // Strategy variables
        private TimeSpan exitTime  = new TimeSpan(22, 00, 00);
        private TimeSpan beginTime = new TimeSpan(6, 0, 0);

        // Strategy objects
        private XAverage quickMAfunction;
        private XAverage slowMAFunction;
        private VariableSeries<Double> m_fastEMA;
        private VariableSeries<Double> m_slowEMA;

        public List<Int32> m_total_trades_per_bar = new List<Int32>();

        // Orders
        private IOrderMarket buy_order, sell_order, short_order, cover_order;

        // create variable objects, function objects, order objects etc.
        protected override void Create()
        {
            buy_order   = OrderCreator.MarketNextBar(new SOrderParameters(Contracts.UserSpecified, "MACrossLE", EOrderAction.Buy));
            sell_order  = OrderCreator.MarketNextBar(new SOrderParameters(Contracts.UserSpecified, "MACrossXL", EOrderAction.Sell));
            short_order = OrderCreator.MarketNextBar(new SOrderParameters(Contracts.UserSpecified, "MACrossSE", EOrderAction.SellShort));
            cover_order = OrderCreator.MarketNextBar(new SOrderParameters(Contracts.UserSpecified, "MACrossXS", EOrderAction.BuyToCover));

            quickMAfunction   = new XAverage(this);
            slowMAFunction    = new XAverage(this);
            m_fastEMA         = new VariableSeries<Double>(this);
            m_slowEMA         = new VariableSeries<Double>(this);
        }

        // Assign inputs
        protected override void StartCalc()
        {
            //Output.Clear();

            quickMAfunction.Length = 10;
            slowMAFunction.Length  = 25;

            quickMAfunction.Price  = Bars.Close;
            slowMAFunction.Price   = Bars.Close;
            m_fastEMA.DefaultValue = 0;
            m_slowEMA.DefaultValue = 0;

            StrategiesPool<TestStrategy2>.Register(this);
        }

        // Strategy logic
        protected override void CalcBar()
        {
            if (Bars.Status == EBarState.Close)
            {
                m_total_trades_per_bar.Add(TotalTrades);
            }

            m_fastEMA.Value = quickMAfunction[0];
            m_slowEMA.Value = slowMAFunction[0];

            if (Bars.TimeValue.TimeOfDay < beginTime)               // Return if time is before begin time
                return;

            // Enter long
            if (PublicFunctions.DoubleGreater(m_fastEMA[0], m_slowEMA[0]) &&
                PublicFunctions.DoubleLess(m_fastEMA[1], m_slowEMA[1]))
            {
                buy_order.Send(1);
            }

            // Enter short
            if (PublicFunctions.DoubleLess(m_fastEMA[0], m_slowEMA[0]) &&
                PublicFunctions.DoubleGreater(m_fastEMA[1], m_slowEMA[1]))
            {
                short_order.Send(1);
            }

            // Manage open positions
            if (StrategyInfo.MarketPosition != 0)
            {
                // Exit long
                if (StrategyInfo.MarketPosition > 0 && (PublicFunctions.DoubleLess(m_fastEMA[0], m_slowEMA[0]) || Bars.TimeValue.TimeOfDay >= exitTime))
                {
                    sell_order.Send(1);
                }

                // Exit short
                if (StrategyInfo.MarketPosition < 0 && (PublicFunctions.DoubleGreater(m_fastEMA[0], m_slowEMA[0]) || Bars.TimeValue.TimeOfDay >= exitTime))
                {
                    cover_order.Send(1);
                }
            }

        }

        protected override void Destroy()
        {
            StrategiesPool<TestStrategy2>.UnRegister(this);
            base.Destroy();
        }

    }
}


Return to “MultiCharts .NET”