Skip to content

Commit

Permalink
Added a "results" indexer to all of the indicators
Browse files Browse the repository at this point in the history
  • Loading branch information
squideyes committed Jun 3, 2023
1 parent 13ace04 commit d342d8f
Show file tree
Hide file tree
Showing 34 changed files with 311 additions and 250 deletions.
45 changes: 31 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,47 @@ To be clear, there are a good number of C#-based technical indicator libraries (
To take a rather simple example, an EmaIndicator might be used as follows:

```csharp
// OpenOn,Open,High,Low,Close data in CSV multi-line format
const string CSV = "...";

var indicator = new EmaIndicator(
period: 21, priceToUse: PriceToUse.Close);

var buffer = new SlidingBuffer<DataPoint>(
size: 100, reversed: true);

// Add the result to a chart, perform a calculation, etc.
static void PrintResult(BasicResult result) =>
Debug.WriteLine($"{result.CloseOn} ={result.Value}");

// CloseOn,Open,High,Low,Close data in CSV multi-line format
const string CSV = """
05/15/2023 14:36:00,12872.75,12873.75,12866,12870.75
05/15/2023 14:37:00,12871.25,12871.5,12864.75,12869.5
05/15/2023 14:38:00,12869.25,12871.75,12867.5,12871.25
05/15/2023 14:39:00,12871,12873.75,12869.5,12872.75
05/15/2023 14:40:00,12872.75,12873.75,12867.75,12870.5
""";

// Most of the indicators return BasicResult, but a few return
// custom results (BollingerIndicator, MacddIndicator, etc.)
var indicator = new EmaIndicator(period: 3,
priceToUse: PriceToUse.Close, maxResults: 10000);

// Parse the data then
foreach (var fields in new CsvEnumerator(CSV.ToStream(), 5))
{
var candle = new TestCandle()
{
OpenOn = DateTime.Parse(fields[0]),
CloseOn = DateTime.Parse(fields[0]),
Open = float.Parse(fields[1]),
High = float.Parse(fields[2]),
Low = float.Parse(fields[3]),
Close = float.Parse(fields[4])
};

buffer.Add(indicator.AddAndCalc(candle));
// Adds the current result to the indicator's results
// collection (a reversed SlidingBuffer) then returns
// that result directly
_ = indicator.AddAndCalc(candle);

if (!indicator.IsPrimed)
continue;

if (buffer.IsPrimed)
AddToChart(buffer[0].OpenOn, buffer[0].Value);
// A result may also be obtained via a [0..maxResults - 1]
// index on the indicator, with zero being the most recent
PrintResult(indicator[2]);
}
```

Expand All @@ -64,7 +81,7 @@ Contributions are always welcome (see [CONTRIBUTING.md](https://github.com/squid

**Supper-Duper Extra-Important Caveat**: THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.

More to the point, your use of this code may (literally!) lead to your losing thousands of dollars and more. Be careful, and remember: Caveat Emptor!!
**FINAL WARNING**: The use of **this code may (literally!) lead to your losing thousands of dollars** or more. **Caveat Emptor!!**



File renamed without changes.
2 changes: 1 addition & 1 deletion TechAnalysis/Common/Helpers/Internal/DataExtenders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ priceToUse switch
public static BasicResult ToBasicResult(this ICandle candle, PriceToUse priceToUse) =>
new()
{
OpenOn = candle.OpenOn,
CloseOn = candle.CloseOn,
Value = candle.GetPrice(priceToUse)
};
}
31 changes: 8 additions & 23 deletions TechAnalysis/Common/Helpers/Internal/MiscExtenders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// of the MIT License (https://opensource.org/licenses/MIT)
// ********************************************************

using System.Text;
using System.Runtime.CompilerServices;

namespace SquidEyes.TechAnalysis;

Expand All @@ -14,7 +14,7 @@ internal static class MiscExtenders
public static bool Approximates(this double a, double b) =>
Math.Abs(a - b) < DOUBLE_EPSILON;

public static int ToDecimalDigits(this double value)
public static int ToDigits(this double value)
{
var digits = 0;

Expand All @@ -30,26 +30,11 @@ public static void ForEach<T>(this IEnumerable<T> items, Action<T> action)
action(item);
}

public static Stream ToStream(this string value)
{
var stream = new MemoryStream();

var writer = new StreamWriter(stream, Encoding.UTF8, -1, true);

writer.Write(value);

writer.Flush();

stream.Position = 0;

return stream;
}

public static bool IsEnumValue<T>(this T value) where T : struct, Enum =>
Enum.IsDefined(value);
public static bool IsEnumValue<T>(this T value)
where T : struct, Enum => Enum.IsDefined(value);

public static T Validated<T>(
this T value, string fieldName, Func<T, bool> isValid)
public static T Validated<T>(this T value,
Func<T, bool> isValid, [CallerMemberName] string fieldName = "")
{
if (string.IsNullOrWhiteSpace(fieldName))
throw new ArgumentNullException(nameof(fieldName));
Expand All @@ -60,7 +45,7 @@ public static Stream ToStream(this string value)
throw new ArgumentOutOfRangeException(fieldName);
}

public static bool InRange<T>(
public static bool IsBetween<T>(
this T value, T minValue, T maxValue, bool inclusive = true)
where T : IComparable<T>
{
Expand All @@ -78,5 +63,5 @@ public static Stream ToStream(this string value)
return true;
}

public static R Funcify<T, R>(this T value, Func<T, R> func) => func(value);
public static R Convert<T, R>(this T value, Func<T, R> func) => func(value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class SlidingBuffer<T> : IEnumerable<T>
{
private readonly List<T> buffer = new();

public SlidingBuffer(int size, bool reversed = false)
internal SlidingBuffer(int size, bool reversed = false)
{
if (size < 1)
throw new ArgumentOutOfRangeException(nameof(size));
Expand Down
82 changes: 0 additions & 82 deletions TechAnalysis/Common/Helpers/Public/CsvEnumerator.cs

This file was deleted.

14 changes: 7 additions & 7 deletions TechAnalysis/Common/Helpers/Public/MaFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ namespace SquidEyes.TechAnalysis;
public static class MaFactory
{
public static IBasicIndicator GetMaIndicator(MaKind kind,
int period, PriceToUse priceToUse = PriceToUse.Close)
int period, int maxResults = 10, PriceToUse priceToUse = PriceToUse.Close)
{
return kind switch
{
MaKind.Dema => new DemaIndicator(period, priceToUse),
MaKind.Ema => new EmaIndicator(period, priceToUse),
MaKind.Sma => new SmaIndicator(period, priceToUse),
MaKind.Smma => new SmmaIndicator(period, priceToUse),
MaKind.Tema => new TemaIndicator(period, priceToUse),
MaKind.Wma => new WmaIndicator(period, priceToUse),
MaKind.Dema => new DemaIndicator(period, priceToUse, maxResults),
MaKind.Ema => new EmaIndicator(period, priceToUse, maxResults),
MaKind.Sma => new SmaIndicator(period, priceToUse, maxResults),
MaKind.Smma => new SmmaIndicator(period, priceToUse, maxResults),
MaKind.Tema => new TemaIndicator(period, priceToUse, maxResults),
MaKind.Wma => new WmaIndicator(period, priceToUse, maxResults),
_ => throw new ArgumentOutOfRangeException(nameof(period))
};
}
Expand Down
39 changes: 30 additions & 9 deletions TechAnalysis/Common/Indicator/BasicIndicatorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,42 @@

namespace SquidEyes.TechAnalysis;

public abstract class BasicIndicatorBase
public abstract class BasicIndicatorBase
{
public BasicIndicatorBase(int period, PriceToUse priceToUse, int minPeriod)
{
Period = period.Validated(nameof(period), v => v >= minPeriod);
private readonly SlidingBuffer<BasicResult> results;

PriceToUse = priceToUse.Validated(nameof(priceToUse), v => v.IsEnumValue());
private int getBasicResultCount = 0;
public BasicIndicatorBase(int period, PriceToUse priceToUse, int maxResults)
{
Period = period.Validated(v => v >= 2);

PriceToUse = priceToUse.Validated(v => v.IsEnumValue());

results = new SlidingBuffer<BasicResult>(
maxResults.Validated(v => v >= 2), true);
}

public BasicResult this[int index] => results[index];

public int Count => results.Count;

public bool IsPrimed => getBasicResultCount >= Period;

protected int Period { get; }
protected PriceToUse PriceToUse { get; }

static protected BasicResult GetBasicResult(DateTime openOn, double value) => new()
protected BasicResult GetBasicResult(DateTime openOn, double value)
{
OpenOn = openOn,
Value = value
};
getBasicResultCount++;

var result = new BasicResult()
{
CloseOn = openOn,
Value = value
};

results.Add(result);

return result;
}
}
2 changes: 2 additions & 0 deletions TechAnalysis/Common/Interfaces/IBasicIndicator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ namespace SquidEyes.TechAnalysis;

public interface IBasicIndicator
{
BasicResult this[int index] { get; }

BasicResult AddAndCalc(ICandle candle);
}
2 changes: 1 addition & 1 deletion TechAnalysis/Common/Interfaces/ICandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace SquidEyes.TechAnalysis;

public interface ICandle
{
DateTime OpenOn { get; }
DateTime CloseOn { get; }
float Open { get; }
float High { get; }
float Low { get; }
Expand Down
2 changes: 1 addition & 1 deletion TechAnalysis/Common/Interfaces/IResults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ namespace SquidEyes.TechAnalysis;

public interface IResult
{
public DateTime OpenOn { get; }
public DateTime CloseOn { get; }
}
18 changes: 17 additions & 1 deletion TechAnalysis/Common/Results/BasicResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,28 @@

namespace SquidEyes.TechAnalysis;

public class BasicResult : ResultBase
public class BasicResult : ResultBase, IEquatable<BasicResult>
{
internal BasicResult()
: base(ResultKind.BasicResult)
{
}

public double Value { get; init; }

public bool Equals(BasicResult? other)
{
if (other is null)
return false;

return Value == other.Value
&& Kind == other.Kind
&& CloseOn == other.CloseOn;
}

public override bool Equals(object? other) =>
other is BasicResult && Equals(other as BasicResult);

public override int GetHashCode() =>
HashCode.Combine(Value, Kind, CloseOn);
}
2 changes: 1 addition & 1 deletion TechAnalysis/Common/Results/ChannelResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ public ChannelResult()
public double Lower { get; init; }

public override string ToString() =>
$"{OpenOn:MM/dd/yyyy HH:mm},{Upper},{Middle},{Lower}";
$"{CloseOn:MM/dd/yyyy HH:mm},{Upper},{Middle},{Lower}";
}
2 changes: 1 addition & 1 deletion TechAnalysis/Common/Results/ResultBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ public ResultBase(ResultKind kind)

public ResultKind Kind { get; }

public DateTime OpenOn { get; init; }
public DateTime CloseOn { get; init; }
}

0 comments on commit d342d8f

Please sign in to comment.