Code Monkey home page Code Monkey logo

karlwancl / trady Goto Github PK

View Code? Open in Web Editor NEW
544.0 85.0 184.0 2.29 MB

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.

Home Page: https://lppkarl.github.io/Trady

License: Apache License 2.0

C# 98.35% HTML 1.65%
strategy technical analysis stock indicator quant netstandard ohlc

trady's Introduction

Trady

build NuGet Pre Release NuGet license

Trady is a handy library for computing technical indicators, and 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.

Read Before You Use

This library is a hobby project, and would probably making breaking changes, use with care when in production.

Currently Available Features

  • Stock data feeding (including csv, yahoo finance, quandl, alphavantage, stooq)
  • Indicator computing (including SMA, EMA, RSI, MACD, BB, etc.)
  • Signal capturing by rules
  • Strategy backtesting by buy/sell rule

v3.2.8 update

  • Fixes IsBull, IsBear recognition, added IsDoji extension by @ridicoulous
  • Added vwap indicator by @codebeaulieu

v3.2.7 update

  • Added candles transformation by @ridicoulous

v3.2.2 update

  • Fixes CommodityChannelIndex

v3.2 update

  • Added WeightedMovingAverage, HullMovingAverage & KeltnerChannels
  • Added AlphaVantage importer (thanks to @irperez)
  • Reactivate support for QuandlImporter
  • Boost performance on date time transformation (thanks to @pavlexander)
  • Boost performance for various indicators (HistoricalHighest/HistoricalLowest/EmaOsc/Macd/ADLine/Obv/ParabolicSar, etc.)
  • Update dependencies for importers
  • Remove redundant sdCount parameter for Sd operation
  • EffeciencyRatio & Kama now accepts nullable decimal as input
  • Allow use of simple operation for ParabolicSar
  • Renamed simple operation "PcDiff" to "RDiff"

For older version history, please refer to another markdown document here

Updates from 2.0.x to 3.0.0

Please refer to another markdown document here

Supported Platforms

  • .NET Core 2.0 or above
  • .NET Framework 4.6.1 or above
  • Mono 5.4 or above
  • Xamarin.iOS 10.4 or above
  • Xamarin.Android 7.5 or above
  • Xamarin.Mac 3.8 or above

Currently Supported Importers

  • Csv file
  • Yahoo Finance
  • Quandl
  • Stooq
  • AlphaVantage

Currently Supported Indicators

Please refer to another markdown document here

How To Install

Nuget package is available in modules, please install the package according to the needs

// For importing
PM > Install-Package Trady.Importer

// For computing & backtesting
PM > Install-Package Trady.Analysis

How To Use

Tldr

var importer = new YahooFinanceImporter();
var candles = await importer.ImportAsync("FB");
var last = candles.Sma(30).Last();
Console.WriteLine($"{last.DateTime}, {last.Tick}");

Back to content

Import stock data

// From Quandl wiki database
var importer = new QuandlWikiImporter(apiKey);

// From Yahoo Finance
var importer = new YahooFinanceImporter();

// From Stooq
var importer = new StooqImporter();

// From AlphaVantage
var importer = new AlphaVantageImporter(apiKey);

// From Google Finance (Temporarily not available now)
// var importer = new GoogleFinanceImporter();

// Or from dedicated csv file
var importer = new CsvImporter("FB.csv");

// Get stock data from the above importer
var candles = await importer.ImportAsync("FB");

Back to content

Transform stock data to specified period before computation

// Transform the series for computation, downcast is forbidden
// Supported period: PerSecond, PerMinute, Per15Minutes, Per30Minutes, Hourly, BiHourly, Daily, Weekly, Monthly

var transformedCandles = candles.Transform<Daily, Weekly>();

Back to content

Compute indicator

// This library supports computing from tuples or candles, extensions are recommended for computing
var closes = new List<decimal>{ ... };
var smaTs = closes.Sma(30);
var sma = closes.Sma(30)[index];

// or, traditional call
var sma = new SimpleMovingAverageByTuple(closes, 30)[index];

// the corresponding version of candle
var sma = new SimpleMovingAverage(candles, 30)[index];

Back to content

Compute simple operation on an indicator

// Simple operation on indicator is supported, now supports only diff, rDiff, sma, sd
var closes = new List<decimal>{ ... };
var smaDiff = closes.Sma(30).Diff(index);   // i-th term - (i-1)-th term
var smaSma = closes.Sma(30).Sma(10, index); // average(n items)
var smaRDiff = closes.Sma(30).RDiff(index); // (i-th term - (i-1)-th term) / (i-1)-th term * 100
var smaSd = closes.Sma(30).Sd(10, 2, index);

// This also applies to candles

Back to content

Convert function to indicator

// Sometimes, we want to utilize the Analyzable infra for some simple indicators but doesn't want to implement a new class, we are adding AsAnalyzable for conversion from Func

// Before conversion, on the very top of your file, you should add
using AFunc = System.Func<System.Collections.Generic.IReadOnlyList<Trady.Core.Candle>, int, System.Collections.Generic.IReadOnlyList<decimal>, Trady.Core.Infrastructure.IAnalyzeContext<Trady.Core.Candle>, decimal?>;

// And use it in your code
AFunc aFunc = (c, i, p, ctx) => c[i].High - c[i].Low;   // The four parameters: candles, index, parameters, context
var aFuncInstance = aFunc.AsAnalyzable(candles);
var a = aFuncInstance[index];

// You may also combine with simple operation computation
var aSma = aFuncInstance.Sma(30, index);

Back to content

Register function for global use

// To use your func globally for analysis, you can register you func by using FuncRegistry.Register method
// The four parameters is the same as the above section, namely: candles, index, parameters, context
FuncRegistry.Register("modified_sma", (c, i, p, ctx) => ctx.Get<SimpleMovingAverage>(p[0])[i].Tick);

// You can use your func globally using the extension
var lastModifiedSmaValue = candles.Func("modified_sma", 10)[candles.Count() - 1];

// The library also support register by plain text expression, you can dynamically create an analyzable as follows:
// Please notice that you must follow the naming convention: c, i, p, ctx when using this approach
FuncRegistry.Register("dy_sma", "var sma = ctx.Get<SimpleMovingAverage>(10); return sma[i].Tick;");

// And the use case is similar:
var lastModifiedSmaValue = candles.Func("modified_sma")[candles.Count() - 1];

Capture signals by rules

// The following shows the number of candles that fulfill both the IsAboveSma(30) & IsAboveSma(10) rule
var rule = Rule.Create(c => c.IsAboveSma(30))
    .And(c => c.IsAboveSma(10));

// Use context here for caching indicator results
using (var ctx = new AnalyzeContext(candles))
{
    var validObjects = new SimpleRuleExecutor(ctx, rule).Execute();
    Console.WriteLine(validObjects.Count());
}

Strategy building & backtesting

// Import your candles
var importer = new YahooFinanceImporter();
var fb = await importer.ImportAsync("FB");
var aapl = await importer.ImportAsync("AAPL");

// Build buy rule & sell rule based on various patterns
var buyRule = Rule.Create(c => c.IsFullStoBullishCross(14, 3, 3))
    .And(c => c.IsMacdOscBullish(12, 26, 9))
    .And(c => c.IsSmaOscBullish(10, 30))
    .And(c => c.IsAccumDistBullish());

var sellRule = Rule.Create(c => c.IsFullStoBearishCross(14, 3, 3))
    .Or(c => c.IsMacdBearishCross(12, 24, 9))
    .Or(c => c.IsSmaBearishCross(10, 30));

// Create portfolio instance by using PortfolioBuilder
var runner = new Builder()
    .Add(fb, 10)
    .Add(aapl, 30)
    .Buy(buyRule)
    .Sell(sellRule)
    .BuyWithAllAvailableCash()
    .FlatExchangeFeeRate(0.001m)
    .Premium(1)
    .Build();

// Start backtesting with the portfolio
var result = await runner.RunAsync(10000);

// Get backtest result for the portfolio
Console.WriteLine(string.Format("Transaction count: {0:#.##}, P/L ratio: {1:0.##}%, Principal: {2:#}, Total: {3:#}",
    resultresult.Transactions.Count(),
    result.CorrectedProfitLoss * 100,
    result.Principal,
    result.CorrectedBalance));

Back to content

Implement your own pattern through Extension

// Implement your pattern by creating a static class for extending IndexedCandle class
public static class IndexedCandleExtension
{
    public static bool IsEma10Rising(this IndexedCandle ic)
        => ic.Get<ExponentialMovingAverage>(10).Diff(ic.Index).Tick.IsPositive();

    public static bool IsEma10Dropping(this IndexedCandle ic)
        => ic.Get<ExponentialMovingAverage>(10).Diff(ic.Index).Tick.IsNegative();

    public static bool IsEma10BullishCrossEma30(this IndexedCandle ic)
        => ic.Get<ExponentialMovingAverageOscillator>(10, 30).ComputeNeighbour(ic.Index).IsTrue(
            (prev, current, next) => prev.Tick.IsNegative() && current.Tick.IsPositive());

    public static bool IsEma10BearishCrossEma30(this IndexedCandle ic)
        => ic.Get<ExponentialMovingAverageOscillator>(10, 30).ComputeNeighbour(ic.Index).IsTrue(
            (prev, current, next) => prev.Tick.IsPositive() && current.Tick.IsNegative());
}

// Use case
var buyRule = Rule.Create(c => c.IsEma10BullishCrossEma30()).And(c => c.IsEma10Rising());
var sellRule = Rule.Create(c => c.IsEma10BearishCrossEma30()).Or(c => c.IsEma10Dropping());
var runner = new Builder().Add(candles, 10).Buy(buyRule).Sell(sellRule).Build();
var result = await runner.RunAsync(10000, 1);

Back to content

Register rule for global use

// To use your rule in global, you may register it by using RuleRegistry.Register method
RuleRegistry.Register("IsBelowSmaX", (ic, p) => ic.Get<SimpleMovingAverage>(p[0])[ic.Index].Tick.IsTrue(t => t > ic.Close));

// Or, using plain text
RuleRegistry.Register("IsBelowSma30", "ic.Get<SimpleMovingAverage>(30)[ic.Index].Tick.IsTrue(t => t > ic.Close)");

// Call it using GetRule method from AnalyzeContext
using (var ctx = new AnalyzeContext(candles))
{
    var ruleX = ctx.GetRule("IsBelowSmaX", 30); // Substitute parameter to the rule
    var rule30 = ctx.GetRule("IsBelowSma30");

    var ruleXByRuleCreateEval = Rule.Create(ic => ic.Eval("IsBelowSmaX", 30));  // Create rule with indexedCandle Eval

    var isAboveSma30Candles = new SimpleRuleExecutor(ctx, ruleX).Execute();
}

Implement your own importer

// You can also implement your own importer by implementing the IImporter interface
public class MyImporter : IImporter
{
    public async Task<IReadOnlyList<IOhlcv>> ImportAsync(string symbol, DateTime? startTime = null, DateTime? endTime = null, PeriodOption period = PeriodOption.Daily, CancellationToken token = default(CancellationToken))
    {
        // Your implementation to return a list of candles
    }
}

// Use case
var importer = new MyImporter();
var candles = await importer.ImportAsync("FB");

Back to content

Implement your own indicator - Simple Type

// You can also implement your own indicator by extending the AnalyzableBase<TInput, TOutput> class
public class MyIndicator : AnalyzableBase<IOhlcv, AnalyzableTick<decimal?>>
{
    // Backing indicator for the indicator
    SimpleMovingAverageByTuple _sma;

    public MyIndicator(IEnumerable<IOhlcv> inputs, int periodCount) : base(inputs)
    {
        // Initialize reference indicators, or other required stuff here
        _sma = new SimpleMovingAverageByTuple(inputs.Select(i => i.Close).ToList(), periodCount);
    }

    // Implement the compute algorithm, uses mappedInputs, index & backing indicators for computation here
    protected override AnalyzableTick<decimal?> ComputeByIndexImpl(IReadOnlyList<IOhlcv> mappedInputs, int index)
    {
		return new AnalyzableTick<decimal?>(mappedInputs[index].DateTime, _sma[index]);
	}
}

// Use case
var results = new MyIndicator(candles, 30).Compute();
foreach (var r in results)
{
    Console.WriteLine($"{r.DateTime}, {r.Tick.Value}");
}

Back to content

Implement your own indicator - Cummulative Type

// You can implement your own indicator by extending the CummulativeAnalyzableBase<TInput, TOutput> class
public class MyCumulativeIndicator : CumulativeAnalyzableBase<IOhlcv, AnalyzableTick<decimal?>>
{
    // Backing indicator for the indicator
    readonly SimpleMovingAverageByTuple _sma;

    public MyCumulativeIndicator(IEnumerable<IOhlcv> inputs, int periodCount) : base(inputs)
    {
        // Initialize reference indicators, or other required stuff here
        _sma = new SimpleMovingAverageByTuple(inputs.Select(i => i.Close).ToList(), periodCount);
    }

    // Implement the compute algorithm for index > InitialValueIndex, uses mappedInputs, index, prev analyzable tick & backing indicators for computation here
    protected override AnalyzableTick<decimal?> ComputeCumulativeValue(IReadOnlyList<IOhlcv> mappedInputs, int index, AnalyzableTick<decimal?> prevOutputToMap)
    {
        return new AnalyzableTick<decimal?>(mappedInputs[index].DateTime, _sma[index] + prevOutputToMap.Tick);
    }

    // Same for the above but for index = InitialValueIndex
    protected override AnalyzableTick<decimal?> ComputeInitialValue(IReadOnlyList<IOhlcv> mappedInputs, int index)
    {
        return new AnalyzableTick<decimal?>(mappedInputs[index].DateTime, _sma[index]);
    }

    // You can also override the InitialValueIndex property & ComputeNullValue method if needed
}

// Use case
var results = new MyCumulativeIndicator(candles).Compute();
foreach (var r in results)
{
    Console.WriteLine($"{r.DateTime}, {r.Tick.Value}");
}   

Back to content

Implement your own indicator - Moving-Average Type

// You can make use of the GenericMovingAverage class to get rid of implementing MA-related indicator on your own
 public class MyGmaIndicator : AnalyzableBase<IOhlcv, AnalyzableTick<decimal?>>
{
    GenericMovingAverage _gma;

    public MyGmaIndicator(IEnumerable<IOhlcv> inputs, int periodCount) : base(inputs)
    {
        // parameters: initialValueIndex, initialValueFunction, indexValueFunction, smoothingFactorFunction
		_gma = new GenericMovingAverage(
			i => inputs.Select(ip => ip.Close).ElementAt(i),
			2.0m / (periodCount + 1),
			inputs.Count());
    }

    protected override AnalyzableTick<decimal?> ComputeByIndexImpl(IReadOnlyList<IOhlcv> mappedInputs, int index)
    {
        return new AnalyzableTick<decimal?>(mappedInputs[index].DateTime, _gma[index]);
    }
}

Back to content

Backlog

  • (✔) Dynamically create indicator & rule patterns from text, allows internal call to dynamic generated stuffs
  • () Complete other indicators (e.g. Keltner Channels, MA Envelopes, etc.)
  • () Complete candlestick patterns
  • (-) Add more rule patterns
  • () Data-feeding: Add broker adaptor for real-time trade (e.g. interactive broker, etc.)
  • () Graphing: Generate stock chart by indicator values
  • () Pipelining: Provide simple programming interface for pipelining the real-time process, i.e. Data-feeding -> decision making by auto rule evaluation -> call to broker for trade -> auto adjust trading strategy based on prev decisions made -> (loop)
  • () REPL for dynamic indicator creation, rule creation, strategy making, backtesting, etc.
    • State saver & loader
  • MORE, MORE AND MORE!!!!

Powered by

trady's People

Contributors

blaise-braye avatar enricodetoma avatar fernaramburu avatar hansmbakker avatar irperez avatar mike-e-angelo avatar pahlot avatar richardsjoberg avatar ridicoulous avatar sarisultan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

trady's Issues

PeriodOption of Per5Minute and Per10Minute

Thanks for the great stuff.

Today I faced this problem, I built a custom importer for an exchange that supports 5-min and 10-min candles, but I didn't find those in the passed PeriodOption enum. I think those are important to be added as I see lots of exchanges out there supporting those periods, and those periods are very important for day traders.

Streamable Candles

I've made a change to AnalyzableBase to allow for Candles to be streamed, rather than having to pass in the entire collection every time. if you can kindly create a branch where I can push these changes for you to review. I have a fork where progress can be reviewed. :) ObservableAnalyzableBase
thanks,
Mina

3.1.1 Beta nuget release?

Is it possible to publish a beta release or alpha of the 3.1 branch to nuget? The latest changes as of 12/13/17?

Improve for CSV importer

Hi, @lppkarl

I have a suggestion (actually if I contribute, are you open to review pull requests :) ?)
Csc import now is

 return new Candle(
                csv.GetField<DateTime>(0),
                csv.GetField<Decimal>(1),
                csv.GetField<Decimal>(2),
                csv.GetField<Decimal>(3),
                csv.GetField<Decimal>(4),
                csv.GetField<Decimal>(5)
            );

But CsvHelper supports auto parsing by field names. I suggest doing that
var myDatas = csvReader.GetRecords().ToList();

For example, i spent half an hour before look at code to find out that header is ignored

Test

Hammer and inverted hammer is not working.when we test with the hammer and inverted hammer candlestick than it is not working.

CLR Exception - Stochastics.Fast

Hi, I'm running into issues computing the stachastics Fast indicator.

The code that I used:
var stoch3 = new Stochastics.Fast(candles, 3, 3).Compute();

The error that I get:
Managed Debugging Assistant 'FatalExecutionEngineError'
Message=Managed Debugging Assistant 'FatalExecutionEngineError' : 'The runtime has encountered a fatal error. The address of the error was at 0x2dfa7486, on thread 0xc710. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.'

Am I using an incorrect method of computing the indicator here or is there an actual issue?

Could not figure out how to use candlesticks with backtest

Can you provide a sample for that?

For ex; suppose I have a set of candles and I want to determine whether it's a bullish or bearish engulfing pattern using backtest.

I couldn't find any extension that will help me using in buy/sell rules.

Client application for visualize candles, rules, transactions etc

Hello, thanks again for your awesome library!

What do you use for visualizing the charts/backtesting? I wrote an "proof of concept" wpf application using https://github.com/beto-rodriguez/Live-Charts that I use for testing.

If you want I can refactor it to a class lib with a user control that takes candles, rules, transactions etc as proeperies and make a pull request.

financial chart_v4

I also looked at http://techanjs.org/ which seems to be nice and a bit more performant, an alternative could be to write a web application using techan.js. Maybe it would be more nice with a web app?

Performance discussion

Hi, Karl @lppkarl
After first research of library i started some more serious development :)
And first and the biggest problem that I faced now is performance.
i just run some code in release configuration

             var buyRule = Rule.Create(c => c.IsEmaBullishCross(3, 7));
            var sellRule = Rule.Create(c => c.IsEmaBearishCross(3, 7));

            var importer = new CsvImporter(setting.CsvPath);
            IReadOnlyList<IOhlcv> candles = await importer.ImportAsync(setting.Symbol);
            _logger.LogInformation($"Readed {candles.Count} of record, start backtest: ", setting);

            var runner = new Builder()
                .Add(candles)
                .Buy(buyRule )
                .Sell(sellRule )
                .Build();

            Result result = await runner.RunAsync(setting.Amount);

Where i have just 1 month of candles for each minute, it is 40 320 candles.
And it is executed 15 minutes. Looks like all time was consumed by evaluating rules.
Is that something that you expect? Did you plan to use the library for those volumes of data?

I also playing with https://enigmampc.github.io/catalyst/index.html
but i don't like Python :) Will comment if i will test the same calculation with Catalyst.

Do you have any thoughts or plans about speedup?

Problem with TransformingCandles, InvalidTimeframeException

Today faced problem with converting candles from lower interval(i.e. 15min) to higher interval(i.e. 30m or 1h)

Application perfectly works on my home pc, but refuses to work on another pc.
I am in russia, moscow (+3 UTC), he is in somewhere in canada(-5 UTC).

So i looked into Transform extension function and sibling methods and heres what i found:

Trady/CandleExtension.cs
In method IsTimeframesValid
this method works with 2 candles as i understood and for me

var candleEndTime = periodInstance.NextTimestamp(candles.ElementAt(i).DateTime);

produces right time and in next if condition it doesnt fall through and doesnt produce false result.

however periodInstance.NextTimestamp from his pc changes last numbers in timestamps
As i understood in just rounds timestamp

on my pc:
23:29:59 00:00 -> 23:30:00 00:00
and then compared with
23:44:59 00:00

on his pc
23:29:59 00:00 -> 23:30:00 -04:00
and then compared with
23:44:59 00:00

This somehow produces error, i dont know why, but for now i just made my own transform methods where i doesnt use periodInstance.NextTimestamp and just do straight compare

MovingAverageConvergenceDivergence and .Macd both unusable

I am attempting to utilize the MACD indicators in this library and keep having exceptions thrown whenever the indicator is instantiated or computed. I tried debugging in several different manners to no avail. If necesarry I can post the larger code context but I don't believe that it is the issue since I can create and execute RSI, EMA, and MA indicators with no issue using the same parameters

  • When utilizing Trady in the traditional manner, the following occurs:
    var tCandles = ToTradyCandles(allCandlesForCoin); var macd = new MovingAverageConvergenceDivergence(tCandles, MA_period_fast, MA_period_slow, signal_period).Compute();

Throws the exception:

Managed Debugging Assistant 'FatalExecutionEngineError'
The runtime has encountered a fatal error. The address of the error was at 0x74841f8d, on thread 0x4338. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.

  • When utilizing Trady in the extension manner, the following occurs:
    var currentValues = tCandles.Macd(MA_period_fast, MA_period_slow, signal_period)[tCandles.Count - 1].Tick;

Throws the exception:

Exception thrown: 'System.MissingMethodException' in Tex.exe
Exception thrown: 'System.AggregateException' in mscorlib.dll
Exception thrown: 'System.AggregateException' in mscorlib.dll
System.Transactions Critical: 0 : <TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Critical"><TraceIdentifier>http://msdn.microsoft.com/TraceCodes/System/ActivityTracing/2004/07/Reliability/Exception/Unhandled</TraceIdentifier><Description>Unhandled exception</Description><AppDomain>Tex.exe</AppDomain><Exception><ExceptionType>System.AggregateException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>One or more errors occurred.</Message><StackTrace>   at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout)
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks)
   at Tex.Driver.StartDriver() in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 187
   at Tex.Program.Main(String[] args) in C:\Users\Administrator\source\repos\Tex\Tex\Program.cs:line 16</StackTrace><ExceptionString>System.AggregateException: One or more errors occurred. ---&amp;gt; System.AggregateException: One or more errors occurred. ---&amp;gt; System.MissingMethodException: Method not found: 'System.Collections.Generic.IReadOnlyList`1&amp;lt;Trady.Analysis.AnalyzableTick`1&amp;lt;System.ValueTuple`3&amp;lt;System.Nullable`1&amp;lt;System.Decimal&amp;gt;,System.Nullable`1&amp;lt;System.Decimal&amp;gt;,System.Nullable`1&amp;lt;System.Decimal&amp;gt;&amp;gt;&amp;gt;&amp;gt; Trady.Analysis.CandlesExtension.Macd(System.Collections.Generic.IEnumerable`1&amp;lt;Trady.Core.Candle&amp;gt;, Int32, Int32, Int32, System.Nullable`1&amp;lt;Int32&amp;gt;, System.Nullable`1&amp;lt;Int32&amp;gt;)'.
   at Tex.Commands.MACDCommand.Execute(List`1 allCandlesForCoin)
   at Tex.Driver.&amp;lt;&amp;gt;c__DisplayClass2_1.&amp;lt;Execute&amp;gt;b__2() in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 245
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout)
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks)
   at Tex.Driver.Execute(List`1 allCandles, Int64 newestTimestamp) in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 254
   at Tex.Driver.&amp;lt;&amp;gt;c__DisplayClass1_2.&amp;lt;StartDriver&amp;gt;b__1() in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 180
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout)
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks)
   at Tex.Driver.StartDriver() in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 187
   at Tex.Program.Main(String[] args) in C:\Users\Administrator\source\repos\Tex\Tex\Program.cs:line 16
---&amp;gt; (Inner Exception #0) System.AggregateException: One or more errors occurred. ---&amp;gt; System.MissingMethodException: Method not found: 'System.Collections.Generic.IReadOnlyList`1&amp;lt;Trady.Analysis.AnalyzableTick`1&amp;lt;System.ValueTuple`3&amp;lt;System.Nullable`1&amp;lt;System.Decimal&amp;gt;,System.Nullable`1&amp;lt;System.Decimal&amp;gt;,System.Nullable`1&amp;lt;System.Decimal&amp;gt;&amp;gt;&amp;gt;&amp;gt; Trady.Analysis.CandlesExtension.Macd(System.Collections.Generic.IEnumerable`1&amp;lt;Trady.Core.Candle&amp;gt;, Int32, Int32, Int32, System.Nullable`1&amp;lt;Int32&amp;gt;, System.Nullable`1&amp;lt;Int32&amp;gt;)'.
   at Tex.Commands.MACDCommand.Execute(List`1 allCandlesForCoin)
   at Tex.Driver.&amp;lt;&amp;gt;c__DisplayClass2_1.&amp;lt;Execute&amp;gt;b__2() in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 245
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout)
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks)
   at Tex.Driver.Execute(List`1 allCandles, Int64 newestTimestamp) in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 254
   at Tex.Driver.&amp;lt;&amp;gt;c__DisplayClass1_2.&amp;lt;StartDriver&amp;gt;b__1() in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 180
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
---&amp;gt; (Inner Exception #0) System.MissingMethodException: Method not found: 'System.Collections.Generic.IReadOnlyList`1&amp;lt;Trady.Analysis.AnalyzableTick`1&amp;lt;System.ValueTuple`3&amp;lt;System.Nullable`1&amp;lt;System.Decimal&amp;gt;,System.Nullable`1&amp;lt;System.Decimal&amp;gt;,System.Nullable`1&amp;lt;System.Decimal&amp;gt;&amp;gt;&amp;gt;&amp;gt; Trady.Analysis.CandlesExtension.Macd(System.Collections.Generic.IEnumerable`1&amp;lt;Trady.Core.Candle&amp;gt;, Int32, Int32, Int32, System.Nullable`1&amp;lt;Int32&amp;gt;, System.Nullable`1&amp;lt;Int32&amp;gt;)'.
   at Tex.Commands.MACDCommand.Execute(List`1 allCandlesForCoin)
   at Tex.Driver.&amp;lt;&amp;gt;c__DisplayClass2_1.&amp;lt;Execute&amp;gt;b__2() in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 245
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()&amp;lt;---
&amp;lt;---
</ExceptionString><InnerException><ExceptionType>System.AggregateException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>One or more errors occurred.</Message><StackTrace>   at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout)
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks)
   at Tex.Driver.Execute(List`1 allCandles, Int64 newestTimestamp) in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 254
   at Tex.Driver.&amp;lt;&amp;gt;c__DisplayClass1_2.&amp;lt;StartDriver&amp;gt;b__1() in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 180
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()</StackTrace><ExceptionString>System.AggregateException: One or more errors occurred. ---&amp;gt; System.MissingMethodException: Method not found: 'System.Collections.Generic.IReadOnlyList`1&amp;lt;Trady.Analysis.AnalyzableTick`1&amp;lt;System.ValueTuple`3&amp;lt;System.Nullable`1&amp;lt;System.Decimal&amp;gt;,System.Nullable`1&amp;lt;System.Decimal&amp;gt;,System.Nullable`1&amp;lt;System.Decimal&amp;gt;&amp;gt;&amp;gt;&amp;gt; Trady.Analysis.CandlesExtension.Macd(System.Collections.Generic.IEnumerable`1&amp;lt;Trady.Core.Candle&amp;gt;, Int32, Int32, Int32, System.Nullable`1&amp;lt;Int32&amp;gt;, System.Nullable`1&amp;lt;Int32&amp;gt;)'.
   at Tex.Commands.MACDCommand.Execute(List`1 allCandlesForCoin)
   at Tex.Driver.&amp;lt;&amp;gt;c__DisplayClass2_1.&amp;lt;Execute&amp;gt;b__2() in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 245
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout)
   at System.Threading.Tasks.Task.WaitAll(Task[] tasks)
   at Tex.Driver.Execute(List`1 allCandles, Int64 newestTimestamp) in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 254
   at Tex.Driver.&amp;lt;&amp;gt;c__DisplayClass1_2.&amp;lt;StartDriver&amp;gt;b__1() in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 180
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
---&amp;gt; (Inner Exception #0) System.MissingMethodException: Method not found: 'System.Collections.Generic.IReadOnlyList`1&amp;lt;Trady.Analysis.AnalyzableTick`1&amp;lt;System.ValueTuple`3&amp;lt;System.Nullable`1&amp;lt;System.Decimal&amp;gt;,System.Nullable`1&amp;lt;System.Decimal&amp;gt;,System.Nullable`1&amp;lt;System.Decimal&amp;gt;&amp;gt;&amp;gt;&amp;gt; Trady.Analysis.CandlesExtension.Macd(System.Collections.Generic.IEnumerable`1&amp;lt;Trady.Core.Candle&amp;gt;, Int32, Int32, Int32, System.Nullable`1&amp;lt;Int32&amp;gt;, System.Nullable`1&amp;lt;Int32&amp;gt;)'.
   at Tex.Commands.MACDCommand.Execute(List`1 allCandlesForCoin)
   at Tex.Driver.&amp;lt;&amp;gt;c__DisplayClass2_1.&amp;lt;Execute&amp;gt;b__2() in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 245
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()&amp;lt;---
</ExceptionString><InnerException><ExceptionType>System.MissingMethodException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>Method not found: 'System.Collections.Generic.IReadOnlyList`1&amp;lt;Trady.Analysis.AnalyzableTick`1&amp;lt;System.ValueTuple`3&amp;lt;System.Nullable`1&amp;lt;System.Decimal&amp;gt;,System.Nullable`1&amp;lt;System.Decimal&amp;gt;,System.Nullable`1&amp;lt;System.Decimal&amp;gt;&amp;gt;&amp;gt;&amp;gt; Trady.Analysis.CandlesExtension.Macd(System.Collections.Generic.IEnumerable`1&amp;lt;Trady.Core.Candle&amp;gt;, Int32, Int32, Int32, System.Nullable`1&amp;lt;Int32&amp;gt;, System.Nullable`1&amp;lt;Int32&amp;gt;)'.</Message><StackTrace>   at Tex.Commands.MACDCommand.Execute(List`1 allCandlesForCoin)
   at Tex.Driver.&amp;lt;&amp;gt;c__DisplayClass2_1.&amp;lt;Execute&amp;gt;b__2() in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 245
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()</StackTrace><ExceptionString>System.MissingMethodException: Method not found: 'System.Collections.Generic.IReadOnlyList`1&amp;lt;Trady.Analysis.AnalyzableTick`1&amp;lt;System.ValueTuple`3&amp;lt;System.Nullable`1&amp;lt;System.Decimal&amp;gt;,System.Nullable`1&amp;lt;System.Decimal&amp;gt;,System.Nullable`1&amp;lt;System.Decimal&amp;gt;&amp;gt;&amp;gt;&amp;gt; Trady.Analysis.CandlesExtension.Macd(System.Collections.Generic.IEnumerable`1&amp;lt;Trady.Core.Candle&amp;gt;, Int32, Int32, Int32, System.Nullable`1&amp;lt;Int32&amp;gt;, System.Nullable`1&amp;lt;Int32&amp;gt;)'.
   at Tex.Commands.MACDCommand.Execute(List`1 allCandlesForCoin)
   at Tex.Driver.&amp;lt;&amp;gt;c__DisplayClass2_1.&amp;lt;Execute&amp;gt;b__2() in C:\Users\Administrator\source\repos\Tex\Tex\Driver.cs:line 245
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()</ExceptionString></InnerException></InnerException></Exception></TraceRecord>
An unhandled exception of type 'System.AggregateException' occurred in mscorlib.dll
One or more errors occurred.

Need more docs.

I found this issue, it help a lot
#26 Fully understand the Strategy building & backtesting sample
but still have question.
Maybe experienced trader will understand, but i don't understand a lot of in code.

var result = await runner.RunAsync(10000, 1);

What is principal here? It like the initial amount of money?

btw readme looked outdated

Console.WriteLine(string.Format("Transaction count: {0:#.##}, P/L ratio: {1:0.##}%, Principal: {2:#}, Total: {3:#}",
    result.TransactionCount,
    result.ProfitLossRatio * 100,
    result.Principal,
    result.Total));

not compiled
at least it should be

            Console.WriteLine(
                $"Transaction count: {result.Transactions.Count():#.##}, " +
                $"Buy: {result.TotalBuyCount}, Sell: {result.TotalSellCount} " +
                $"P/L ratio: {result.TotalCorrectedProfitLossRatio * 100:0.##}%, " +
                $" Principal: {result.TotalPrincipal:#}, Total corrected balance: {result.TotalCorrectedBalance:#}");

Is there a test project where the examples are working?

Hi I am very intersted in experimenting with this project, but I have not had any luck with the sample snippets on https://github.com/lppkarl/Trady

I imported using Nuget and made a basic test harness:

Dim closes = New List(Of Candle)
Dim closes2 = New List(Of Candle)

        Using Context As New mainEntities
            Dim Results = (From HR In Context.HistoricalRecords
                           Where HR.instrument_id = 247
                           Order By HR.time Ascending).ToList

            Dim ResultCount As Integer = 0
            For Each HR In Results
                ResultCount += 1
                Dim NewCandle As Candle = New Candle(TimeFunctions.ConvertUTCToDate(HR.time), HR.open, HR.high, HR.low, HR.close, HR.volume_to)
                If ResultCount < 150 Then
                    closes.Add(NewCandle)
                Else
                    closes2.Add(NewCandle)
                End If
            Next
        End Using

Then I tried the sample:

Dim smaTs = closes.Sma(30)
Dim sma = closes.Sma(30)(40)

        Dim buyRule = Rule.Create(Function(c) c.IsFullStoBullishCross(14, 3, 3)).[And](Function(c) c.IsMacdOscBullish(12, 26, 9)).[And](Function(c) c.IsSmaOscBullish(10, 30)).[And](Function(c) c.IsAccumDistBullish())

        Dim sellRule = Rule.Create(Function(c) c.IsFullStoBearishCross(14, 3, 3)).[Or](Function(c) c.IsMacdBearishCross(12, 24, 9)).[Or](Function(c) c.IsSmaBearishCross(10, 30))
        Dim runner = New Backtest.Builder().Add(closes, 10).Add(closes2, 30).Buy(buyRule).Sell(sellRule).Build()
        Dim result = runner.Run(200, 1)

        Console.WriteLine(String.Format("Transaction count: {0:#.##}, P/L ratio: {1:0.##}%, Principal: {2:#}, Total: {3:#}",
                                        result.TotalCorrectedTransactionCount,
                                        result.TotalCorrectedProfitLossRatio * 100,
                                        result.TotalPrincipal,
                                        result.TotalCorrectedBalance))


On the line:
Dim result = runner.Run(200, 1)
I get

Object reference not set to an instance of an object.

This happens for both Run and RunAsync.

So instead I tried:

     Dim buyRule = Rule.Create(Function(c) c.IsEma10BullishCrossEma30()).[And](Function(c) c.IsEma10Rising())

        Dim sellRule = Rule.Create(Function(c) c.IsEma10BearishCrossEma30()).[Or](Function(c) c.IsEma10Dropping())

        Dim runner = New Backtest.Builder().Add(closes, 10).Buy(buyRule).Sell(sellRule).Build()

        Dim result = Await runner.RunAsync(10000, 1)

Now on the line:
Dim result = Await runner.RunAsync(10000, 1)
I get

Method not found: 'System.ValueTuple3<!3,!3,!3> Trady.Analysis.Infrastructure.AnalyzableBase4.ComputeNeighbour(Int32)'.


I appreciate the effort you have put into this but I am having no luck with the samples indicated. If there was a simple project where calls like this are succeeding, that might help us work out our problems.

There also seems to be a shortage of comments in the code or other documentation explaining how to use it successfully, so I am not clear where to go from here.

Thanks for any help.

Add support for crytocurrencies (Bittrex)

First off... wow. :) Thank you for putting this really awesome library together... and in .NET Standard 2.0, even. I was having to muck around with TA-Lib, which is something straight out of 1985 haha.

I am getting familiar with your importers, but it appears that none of them have access to cryptocurrencies, which is what I am interested in. Are there plans to make an importer that has access any crypto listings? If not, I might be interested in helping out here. FWIW, I use TradingView which has access to everything. I am not sure where they get their data, though. And from surfing their API it doesn't look like they provide the data, just the charts. I could be wrong, however.

Anyways, wanted to get some thoughts around this and see if I am missing something obvious here. Thanks in advance for any information/assistance!

Upgrade compiler to 7.3

The latest compiler version 7.3 allows us to take advantage of some good performance features that were not available.

In particular the use of ref, in, out keywords in ways not possible before.

This may be useful especially when dealing with a large amount of candles. Using references instead of objects as it may boost performance.

So for example, the original did without the "in" keyword:

public class ExponentialMovingAverage : ExponentialMovingAverage<IOhlcv, AnalyzableTick<decimal?>>
    {
        public ExponentialMovingAverage(in IEnumerable<IOhlcv> inputs, int periodCount)
            : base(inputs, i => i.Close, periodCount)
        {
        }
    }

The "in" keyword ensures that the function uses a reference to the actual IEnumerable object, but cannot modify the reference. The original code actually makes a copy of that IEnumerable.

https://dotnetcoretutorials.com/2018/01/08/new-keyword-c-7-2/

Consider Using DateTimeOffset Instead of DateTime

I've been going over my code and have been moving over my "ticks" or "candles" to Trady.Core.Candle. However, I just noticed that the "tick" is a DateTime rather than a DateTimeOffset. From my understanding, DateTimeOffsets are preferred as they contain timezones and save a lot of additional work (in theory?).

Anyways, I was wondering what the feeling is on updating the ITick.DateTime field to be a DateTimeOffset instead.

How to use candlesticks with backtest?

There is already an issue #38 which is closed and probably no one would see it anymore.
That's why I'm opening a new one hoping some one knows how it is done.

I have a set of candles and I want to determine whether it's a bullish/bearish engulfing pattern or BullishAbandonedBaby using backtest.
I couldn't find any extension that will help me using in buy/sell rules.

Thanks in advance.

RSI calculation is giving "Divide 0" exception

var rsi = candles.Rsi(9)[candles.Count() - 1];

Exception -> Attempted to divide by zero
StackTrace ->

at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
at System.Decimal.op_Division(Decimal d1, Decimal d2)
at Trady.Analysis.Indicator.RelativeStrength2.ComputeByIndexImpl(IReadOnlyList1 mappedInputs, Int32 index)
at Trady.Analysis.Infrastructure.AnalyzableBase4.<ComputeByIndex>b__25_0(Int32 i) at System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory) at Trady.Analysis.Infrastructure.AnalyzableBase4.ComputeByIndex(Int32 index)
at Trady.Analysis.Infrastructure.AnalyzableBase4.Map(Func2 otmFunc, Int32 index)
at Trady.Analysis.Infrastructure.AnalyzableBase4.get_Item(Int32 index) at Trady.Analysis.Indicator.RelativeStrengthIndex2.ComputeByIndexImpl(IReadOnlyList1 mappedInputs, Int32 index) at Trady.Analysis.Infrastructure.AnalyzableBase4.b__25_0(Int32 i)
at System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory)
at Trady.Analysis.Infrastructure.AnalyzableBase4.ComputeByIndex(Int32 index) at Trady.Analysis.Infrastructure.AnalyzableBase4.Map(Func2 otmFunc, Int32 index) at Trady.Analysis.Infrastructure.AnalyzableBase4.get_Item(Int32 index)
at Trady.Analysis.Infrastructure.AnalyzableBase4.<Compute>b__7_0(Int32 i) at System.Linq.Enumerable.WhereSelectEnumerableIterator2.MoveNext()
at System.Collections.Generic.List1..ctor(IEnumerable1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source) at Trady.Analysis.Infrastructure.AnalyzableBase4.Compute(Func2 outputFunc, Nullable1 startIndex, Nullable1 endIndex) at Trady.Analysis.Infrastructure.AnalyzableBase4.Compute(Nullable1 startIndex, Nullable1 endIndex)
at Trady.Analysis.CandlesExtension.Rsi(IEnumerable1 candles, Int32 periodCount, Nullable1 startIndex, Nullable1 endIndex) at Kite.AutoTrading.StrategyManager.Strategy.MAStrategy.Scan(Symbol symbol, IEnumerable1 candles, Boolean isPlaceOrder) in C:\Users\sagar.raut\source\repos\Kite.AutoTrading\Kite.AutoTrading.StrategyManager\Strategy\MAStrategy.cs:line 130
at Kite.Autotrading.Test.MAStrategyTest.GetProfitOfDay(Symbol symbol, IEnumerable1 candles) in C:\Users\sagar.raut\source\repos\Kite.AutoTrading\Kite.Autotrading.Test\Tests\MAStrategyTest.cs:line 154 at Kite.Autotrading.Test.MAStrategyTest.<>c__DisplayClass2_3.<WeeklyWatchlistIsProfitableOrNot>b__0(Symbol symbol) in C:\Users\sagar.raut\source\repos\Kite.AutoTrading\Kite.Autotrading.Test\Tests\MAStrategyTest.cs:line 124 at System.Threading.Tasks.Parallel.<>c__DisplayClass42_02.b__1()

I do not know how to get sma

I do not know how to get sma, this is my code, please help me!

var sma = candles.Sma(6).Last();
Console.WriteLine(sma);
Print out >> Trady.Analysis.AnalyzableTick1[System.Nullable1[System.Decimal]]

Unknown backtesting parameters

Hello, this is a great library, I've just started to look into the backtesting classes and have some questions regarding some parameters which I'm not sure what their purpose is so any help here would be great.

The 2 parameters I'm having difficulty with understanding are "weighting" and "premium". What is their purpose, I've changed the value of 'weighting' but it has no impact of profit value.

var imp = new Importer();
var candles = imp.Import("EURUSD", startDate, endDate, PeriodOption.Per15Minute);

// Build buy rule & sell rule based on various patterns
var buyRule = Trady.Analysis.Rule.Create(c => c.IsRsiOversold(14));
var sellRule = Trady.Analysis.Rule.Create(c => c.IsRsiOverbought(14));

// Create portfolio instance by using PortfolioBuilder
//'weighting' value here is 10
Trady.Analysis.Backtest.Runner runner = new Trady.Analysis.Backtest.Builder()
    .Add(candles, 10)
    .Buy(buyRule)
    .Sell(sellRule)
    .Build();

// Start backtesting with the portfolio
//'premium' value here is 1
Trady.Analysis.Backtest.Result result = await runner.RunAsync(10000, 1);

is it possible to process indicator using live data

library looks great
but i have one question
lets say i am subscribed to some feed and every second i am getting new Candle is it possible to calculate indicator value on that candle. instead of reprocessing all candles again?

Add support for crytocurrencies

Find today this huge project and i would like to contribute and adopt it.

I'm building a trading engine, it is a C# UWP app running on Raspberry PI, so far it retrieves candles from the exchange every 4 hours and stores in your OneDrive folder in order to build a big prices database available for backtesting, the exchange allows you to retrieve only the last 12 hours for the minute period.

I can implement the retriever from the Kraken Exchange (well known for low commissions) if you like and you give me permissions.

Cheers!

Bug: Calculation of ADX, +DI and -DI are wrong

Hi,

I think there is something wrong with calculating ADX, + DI and -DI. The values that I receive are very different from what I see on TradingView or other platforms.
The difference gets worse when I have a Period other than default 14. Then, for example, I get 30 and TradingView shows 20 for that same Period.

I do not believe that the TradingView or other websites like Binance calculate it incorrectly.

It is easy to test, choose a cryptocurrency and let the engine calculate these values. Compare the result with the TradingView or other platform.

Missing reference to PortfolioBuilder() ?

Thank you for this project !!!

// Create portfolio instance by using PortfolioBuilder
var portfolio = new PortfolioBuilder()
.Add(equity, 10)
.Add(equity2, 30)
.Buy(buyRule)
.Sell(sellRule)
.Build();

I can't find any reference to PortfolioBuilder() in the project, is it missing ?

Add GitHub Pages for Better Documentation

I feel the one area that is lacking is really good mature documentation. If the documentation can be better explained with even more examples, I believe the use of this library will explode. Google will also put this library higher in its rankings.

My first recommendation is to setup GitHub Pages and migrate all the documentation there. Break down the documentation into sections that drill down into details.

Secondly, I think it would be a good exercise to add a lot more

documentation within the library to increase the quality of the intellisense. Right now if I want to add an indicator or study what other indicators are doing, I find myself in base classes that I'm not sure what their purpose is.

If we want more contributors, we need to increase the quality of documentation.

How to get Buy/Sell Signal for last candle?

Hello, First of all thanks for the solution. I am trying to develop bot on this. The problem is, I cannot get the signal for last (recent) candle. I tried the following code;

IndexedCandle indexedCandle = new IndexedCandle(candles, 1);
if (indexedCandle.IsMacdBullishCross(x, y, z) == true)
{
  Buy....
}

Doing something wrong?

IsEmaBullishCross Or IsEmaBearishCross Always false

Hello I am trying to figure out how to know if the Ema is Bullish or Bearish.

I did a backtesting using IsEmaBullishCross and IsEmaBearishCross and they are working fine

` var buyRule = Rule.Create(ic => ic.IsEmaBullishCross(21, 55));
var sellRule = Rule.Create(ic => ic.IsEmaBearishCross(21, 55));

        var runner = new Trady.Analysis.Backtest.Builder()
            .Add(candles)
            .Buy(buyRule)
            .Sell(sellRule)`

The problem is when I am trying to get the buy signal and sell signal from the exchange always is False.

I am using the IndexedCandle like this:

`
var ic = new IndexedCandle(candles, candles.Count - 1);
var buyRule =(ic => ic.IsEmaBullishCross(21, 55));
var sellRule = (ic => ic.IsEmaBearishCross(21, 55));

`

Maybe I am doing something wrong.

I would appreciate some help.

I did my own testing and I found something very interesting
I implemented my own indicator called TemaIndicator
I followed the examples and I did thisd

//public static bool IsTemaBullishCross(this IIndexedOhlcv ic, int periodCount1, int periodCount2)
// => ic.Get(periodCount1, periodCount2).ComputeNeighbour(ic.Index)
// .IsTrue((prev, current, _) => prev.Tick.IsNegative() && current.Tick.IsPositive());

    //public static bool IsTemaBearishCross(this IIndexedOhlcv ic, int periodCount1, int periodCount2)
    //    => ic.Get<TemaIndicatorOcillator>(periodCount1, periodCount2).ComputeNeighbour(ic.Index)
    //         .IsTrue((prev, current, _) => prev.Tick.IsPositive() && current.Tick.IsNegative());

Same as many extensions when I use them with back testing is working fine but with real data never always they return false like macd crossing, emabearishcross, emabullishcross etc..

I modified this extension like this:

public static bool IsTemaBullishCross(this IIndexedOhlcv ic, int periodCount1, int periodCount2)
    {

// original code
var test = ic.Get(periodCount1, periodCount2).ComputeNeighbour(ic.Index)
.IsTrue((prev, current, _) => prev.Tick.IsNegative() && current.Tick.IsPositive());

// traditional way
var tema1 = ic.Get(periodCount1).Compute()[ic.Index];
var tema2 = ic.Get(periodCount2).Compute()[ic.Index];
if (tema1.Tick.Value > tema2.Tick.Value)
{
return true;
}
else
{
return false;
}

    }

and finally the cross was fired ....

there is a bug with the extensions.

I would like to help to fix the problem but for now is the only way I found to fix them.

Thank you!

Update CsvHelper

Used in project CsvHelper 2.16.3 and latest 6.1.1 not compatible.
If you use custom Importer with CsvHelper 6 app is break, because CsvHelper has breaked changes.

Could not load type 'CsvHelper.Configuration.CsvConfiguration' from assembly 'CsvHelper, Version=6.0.0.0, Culture=neutral, PublicKeyToken=8c4959082be5c823'.

And CsvHelper.Configuration.CsvConfiguration really not exists in 6

Null Reference Exception

Hello, does anyone know why the IsMacdOscBullish rule would throw an exception in this ruleset?

var buyRule = Rule.Create(c => c.IsMacdOscBullish(12, 26, 9))
.And(c => c.IsAboveEma(12))
.And(c => c.IsAboveEma(26));

However, if I add in a IsFullStoBullishCross condition IsMacdOscBullish does not throw an exception.

var buyRule = Rule.Create(c => c.IsFullStoBullishCross(14, 3, 3))
.And(c => c.IsMacdOscBullish(12, 26, 9))
.And(c => c.IsAboveEma(12))
.And(c => c.IsAboveEma(26));

The exception is being thrown on this line. Diff returns NULL.

public static bool IsMacdOscBullish(this IIndexedOhlcv ic, int emaPeriodCount1, int emaPeriodCount2, int demPeriodCount)
=> ic.Get(emaPeriodCount1, emaPeriodCount2, demPeriodCount).Diff(ic.Index).Tick.IsPositive();

  Name Value Type
$exception {System.NullReferenceException: Object reference not set to an instance of an object. at Trady.Analysis.Extension.IndexedIOhlcvDataExtension.IsMacdOscBullish(IIndexedOhlcv ic, Int32 emaPeriodCount1, Int32 emaPeriodCount2, Int32 demPeriodCount) in C:\Users\Brian\Documents\TradingApps\Trady-master\Trady.Analysis\Extension\IndexedCandleExtension.cs:line 111 at Trady.Test.BacktestTestMarket.<>c.b__10_0(IIndexedOhlcv c) in C:\Users\Brian\Documents\TradingApps\Trady-master\Trady.Test\BacktestTestMarket.cs:line 108 at Trady.Analysis.Rule.<>c__DisplayClass2_0.b__0(IIndexedOhlcv ic) in C:\Users\Brian\Documents\TradingApps\Trady-master\Trady.Analysis\Rule.cs:line 15 at Trady.Analysis.Rule.<>c__DisplayClass2_0.b__0(IIndexedOhlcv ic) in C:\Users\Brian\Documents\TradingApps\Trady-master\Trady.Analysis\Rule.cs:line 15 at Trady.Analysis.Backtest.Builder.<>c__DisplayClass6_0.b__0(IIndexedOhlcv ic) in C:\Users\Brian\Documents\TradingApps\Trady-master\Trady.Analysis\Backtest\Builder.cs:line 34 at Trady.Analysis.Backtest.Runner.<>c__DisplayClass14_0.b__1(IIndexedOhlcv ic) in C:\Users\Brian\Documents\TradingApps\Trady-master\Trady.Analysis\Backtest\Runner.cs:line 73 at Trady.Analysis.Infrastructure.RuleExecutorBase3.Execute(Nullable1 startIndex, Nullable1 endIndex) in C:\Users\Brian\Documents\TradingApps\Trady-master\Trady.Analysis\Infrastructure\RuleExecutorBase.cs:line 37 at Trady.Analysis.Backtest.Runner.Run(Decimal principal, Decimal premium, Nullable1 startTime, Nullable1 endTime) in C:\Users\Brian\Documents\TradingApps\Trady-master\Trady.Analysis\Backtest\Runner.cs:line 60 at Trady.Analysis.Backtest.Runner.<>c__DisplayClass12_0.<RunAsync>b__0() in C:\Users\Brian\Documents\TradingApps\Trady-master\Trady.Analysis\Backtest\Runner.cs:line 36 at System.Threading.Tasks.Task1.InnerInvoke() at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)} System.NullReferenceException

Additional indicators

I don't know what kind of indicators are still on the to do list, but I'd love to see the following:

  • Parabolic SAR / SAR
  • Momentum Index
  • CCI

Great library!

Demo of integrating to SciChart

Hi Karl,

I'm interested in making a demo of integrating trading to SciChart - High Performance WPF Charts. If I do it, would you put a link in your readme.md?

SciChart is commercial software, not free, but it is very good and allows financial charting in realtime. Also if we see improvements to Trady we're willing to help contribute!

Best regards,
Andrew

Pattern matching

Hi @lppkarl ! Thanks for the wonderful library!

I would like to learn, is patern matching possible via Trady?
I mean, I want to record/snapshot existing patern, and if same patern will find, it'll fire an event. :)

Patern mean: shoulder-head-shoulder etc. But custom.

I hope it's clear :)
Regards!

Fully understand the Strategy building & backtesting sample

Hi
I'm investigating and testing possible tools to help me build a simple analyse tool to be user with Bittrex.
I found You Trady and You implementation of BackTest, Builder and rules.

In the Strategy building & backtesting sample You mention

// Create portfolio instance by using PortfolioBuilder
var runner = new Builder()
.Add(equity, 10)
.Add(equity2, 30)
.Buy(buyRule)
.Sell(sellRule)
.Build();

For me to fully understand this I would kindly ask if You could include some more code that show how the equity and equity2 is created.

I'm new into Technical indicators, trading and cryptcoins and is trying to learn more about these subjects.

I managed to add a working Bittrex import, so I can start testing some of me ideas

Best regards
Klaus E. Frederiksen
Denmark

Release 3.2 NuGet package?

With all of the recent changes (new indicators, performance improvements, bug fixes), shall we release 3.2 nuget package?

candle transformation is very slow

I have encountered a situation with a big set of data, that transform function is very slow and needs improvements.

  • start set of data: 385,577
  • start interval: 1 Minute
  • end set of data: 3,243 (without fix: 3,221)
  • end interval: 2 Hours

Default time taken to execute: 150,000 ms
Time taken to execute after optimizations: 29,000 ms

Code:

IEnumerable<IOhlcv> ienumCandles = tradyCandles.AsEnumerable();
var transformedTradyCandles = ienumCandles.Transform<PerMinute, BiHourly>();

Before:

private static IOhlcv ComputeIOhlcvData(IEnumerable<IOhlcv> candles, DateTimeOffset startTime, DateTimeOffset endTime)
{
	var candle = candles.Where(c => c.DateTime >= startTime && c.DateTime < endTime);
	if (candle.Any())
	{
		var dateTime = candle.First().DateTime;
		var open = candle.First().Open;
		var high = candle.Max(stick => stick.High);
		var low = candle.Min(stick => stick.Low);
		var close = candle.Last().Close;
		var volume = candle.Sum(stick => stick.Volume);
		return new Candle(dateTime, open, high, low, close, volume);
	}
	return null;
}

After:

private static IOhlcv ComputeIOhlcvData(IEnumerable<IOhlcv> candles, DateTimeOffset startTime, DateTimeOffset endTime)
{
	List<IOhlcv> inputCandles = candles.ToList();
	List<IOhlcv> candle = new List<IOhlcv>();
	bool started = false;
	for (int i = 0; i < candles.Count(); i++)
	{
		if (!started && inputCandles[i].DateTime >= startTime)
		{
			started = true;
			candle.Add(inputCandles[i]);
		}
		else if (started)
		{
			if (inputCandles[i].DateTime >= endTime)
			{
				break;
			}

			candle.Add(inputCandles[i]);
		}
	}

	if (candle.Any())
	{
		var dateTime = candle.First().DateTime;
		var open = candle.First().Open;
		var high = candle.Max(stick => stick.High);
		var low = candle.Min(stick => stick.Low);
		var close = candle.Last().Close;
		var volume = candle.Sum(stick => stick.Volume);
		return new Candle(dateTime, open, high, low, close, volume);
	}
	return null;
}

While I do see, that there is some difference in output result (I don't have time now, to study what exactly is wrong) - the improvement is seen with a naked eye.

The only requirement is that data needs to be ordered. It can be done as the first step using LINQ. In my set of data all candles were in correct order so I skipped that part.

Additionally, I think transformation can be performed in parallel for different date-time periods.

I would appreciate if you investigated this issue more deeply and came up with some code optimizations.

P.S. Yes, according to profiler ComputeIOhlcvData takes the most time to execute. I think that the reason for that is that LINQ is iterating over all records over and over again. In the best case scenario (if data is ordered) - we need to do it only once!!! (in default implementation the code does it at least 3,000 times) In my new implementation, code also does it 3,000 time, but, it quits iterating over candles as soon as first out-of range candle is encountered. It gave 5x performance boost. I imagine that completely-rewritten transformation will get the job done in at most 1/5 time than my result..

Several Divide by zero exceptions

Hi,

Working against 3.1;

after much digging I have found that if a price hasn't moved within a given period of time the CCI will cause a DivideByZero exception @ line 29 because the mean deviation is 0.

To reproduce:
`[TestMethod]
public void Expect_CCI_Exception()
{
var candles = new List();
for(int i = 0; i <= 100; i++)
{
candles.Add(new Candle()
{
Open = 0.03m,
Close = 0.03m
});
}
Assert.ThrowsException(() => candles.Cci(20));

    }`

Ask or Bid

I couldn't seem to find anywhere in the source code that mentions Ask or Bid. Are they supported currently?

Add setters to IOhlcv

While the current design makes sense to some degree, the fact that there are no setters outside of the constructor limits the ability to serialize/de-serialize instances that inherit from IOhlcv, this especially an issue when passing instances that inherit from IOhlcv via a message bus.

Nonlinear time to compute sma

I'm using this code to test the performance of Sma. I'm wondering why I don't get a linear time increase when I raise the number of items to calculate. Will this be the same with all indicators? Is it possible to mitigate this in some way?

        DateTime then = DateTime.Now;
        var t = candles.Sma(30, 0, 100);
        System.Diagnostics.Debug.WriteLine(DateTime.Now.Subtract(then).TotalMilliseconds);

        then = DateTime.Now;
        t = candles.Sma(30, 0, 1000);
        System.Diagnostics.Debug.WriteLine(DateTime.Now.Subtract(then).TotalMilliseconds);

        then = DateTime.Now;
        t = candles.Sma(30, 0, 10000);
        System.Diagnostics.Debug.WriteLine(DateTime.Now.Subtract(then).TotalMilliseconds);

        then = DateTime.Now;
        t = candles.Sma(30, 0, 100000);
        System.Diagnostics.Debug.WriteLine(DateTime.Now.Subtract(then).TotalMilliseconds);

The output is:
80,0151
38,0028
576,0439
52473,7246

I'm using version 3.0.0

Possible abstraction for class Candle

Hello and thanks for this awesome library!

I might have a suggestion that could help me better integrate this library into my application. It seems that most if not all indicators depend somehow on class Candle or delegate mapper to select proper values.

Hower when dealing with large collections I have to convert my data objects to Candles which creates a lot of unnecessary allocations. It would be better if I could loosely depend on this libary by using some interface like this:

public interface IOhlcData
{
    DateTime DateTime { get; }

    decimal Open { get; }

    decimal High { get; }

    decimal Low { get; }

    decimal Close { get; }
}

I would then just implement it and use my own business objects without much or any modifications.
Thanks for considering and if you find it suitable I think I can create a pull request with such modification.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.