karlwancl / Trady

Trady is a handy library for computing technical indicators, and it targets to be an automated trading system that provides stock data feeding, indicator computing, strategy building and automatic trading. It is built based on .NET Standard 2.0.
https://lppkarl.github.io/Trady
Apache License 2.0
545 stars 185 forks source link

Ichimoku Cloud improvements #92

Open pavlexander opened 5 years ago

pavlexander commented 5 years ago

Description

I would like to report some issues with Ichimoku Cloud

Issue 1: No Displacement parameter

If you look at some implementations (https://www.tradingview.com/chart/) of Ichimoku cloud - it is NOT always calculating leading and lagging lines using the medium period length. In most of the cases a new parameter is introduced, which is called Displacement . It defines - by which period should the leading and the lagging span be shifted.

I would appreciate if you added a new optional parameter, like this:

public IchimokuCloud(IEnumerable<TInput> inputs, Func<TInput, (decimal High, decimal Low, decimal Close)> inputMapper, int shortPeriodCount, int middlePeriodCount, int longPeriodCount, int displacement = null) : base(inputs, inputMapper)
{
    displacement = displacement ?? middlePeriodCount;
    // ..
}

This makes sense in fickle markets, such as crypto markets.

Issue 2/feature!: not clear when the Ichimoku cloud parameters are synchronized with candlesticks.

Ichimoku is using a concept of lagging and leading lines.

Therefore, we can not take the fist DateTimeOffset of Candle 1 and expect to get the Ichimoku values on the same index.

I don't know how this issue could be solved using existing framework which you are maintaining, but it would be nice if, for example, after computation, we would get an output parameter that would say that "Ichimoku values starting at Index 55 correspond to candles from index 0".

Issue 3: DateTime values are wrong for leading and lagging lines.

After you compute the indicator - in a list of values you will get following results:

For first X results and for last Y results:

Trady.Core.Infrastructure.ITick.DateTime: {01.01.0001 00:00:00 +00:00}

This issue can actually be solved by finding the first result with present DateTime and then setting -1 period for each next result entity. For last entities - it is almost the same. But instead of subtracting 1 period - we add it.

This is not really a big issue at the moment. I can always fix it with something like this (not tested):

    public static class IchimokuCloudExtensions
    {
        public static IReadOnlyList<AnalyzableTick<(decimal? ConversionLine, decimal? BaseLine, decimal? LeadingSpanA, decimal? LeadingSpanB, decimal? LaggingSpan)>> FixResults(this IReadOnlyList<AnalyzableTick<(decimal? ConversionLine, decimal? BaseLine, decimal? LeadingSpanA, decimal? LeadingSpanB, decimal? LaggingSpan)>> input)
        {
            var result = new List<AnalyzableTick<(decimal? ConversionLine, decimal? BaseLine, decimal? LeadingSpanA, decimal? LeadingSpanB, decimal? LaggingSpan)>>();

            DateTimeOffset firstDT = (DateTimeOffset)input
                .Where(w => w.DateTime != default(DateTimeOffset))
                .First()
                .DateTime;

            DateTimeOffset lastDT = (DateTimeOffset)input
                .Where(w => w.DateTime != default(DateTimeOffset))
                .Last()
                .DateTime;

            int dataCount = input.Count;

            int firstIndexWithValue = 0;
            for (int i = 0; i < dataCount; i++)
            {
                if (input[i].DateTime != default(DateTimeOffset))
                {
                    firstIndexWithValue = i;
                    break;
                }
            }

            int lastIndexWithValue = 0;
            for (int i = dataCount - 1; i >= 0; i--)
            {
                if (input[i].DateTime != default(DateTimeOffset))
                {
                    lastIndexWithValue = i;
                    break;
                }
            }

            for (int i = 0; i < dataCount; i++)
            {
                var offset = input[i].DateTime;
                var tick = input[i].Tick;

                if (i < firstIndexWithValue)
                {
                    int diff = -(firstIndexWithValue - i);
                    offset = firstDT.AddHours(diff);
                }
                else if (i > lastIndexWithValue)
                {
                    int diff = i - lastIndexWithValue;
                    offset = lastDT.AddHours(diff);
                }

                var entity = new AnalyzableTick<(decimal? ConversionLine, decimal? BaseLine, decimal? LeadingSpanA, decimal? LeadingSpanB, decimal? LaggingSpan)>(offset, tick);
                result.Add(entity);
            }

            return result;
        }
    }

But it would be nice if the issue was fixed on a lower level during data generation.