Skip to content

Commit

Permalink
redesign Marubozu (#683)
Browse files Browse the repository at this point in the history
+semver: minor
  • Loading branch information
DaveSkender authored Jan 23, 2022
1 parent 3127eaa commit 8a3a623
Show file tree
Hide file tree
Showing 14 changed files with 203 additions and 103 deletions.
8 changes: 8 additions & 0 deletions docs/_includes/candle-result.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
### CandleResult

| name | type | notes
| -- |-- |--
| `Date` | DateTime | Date
| `Price` | decimal | Indicates the candle `Close` price when a signal is present
| `Signal` | [Signal]({{site.baseurl}}/guide/#signal) | Generated signal type
| `Candle` | [Candle]({{site.baseurl}}/guide/#candle) | Candle properties
20 changes: 8 additions & 12 deletions docs/_indicators/Marubozu.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type: candlestick-pattern

```csharp
// usage
IEnumerable<MarubozuResult> results =
IEnumerable<CandleResult> results =
quotes.GetMarubozu(minBodyPercent);
```

Expand All @@ -26,31 +26,27 @@ IEnumerable<MarubozuResult> results =

### Historical quotes requirements

You must have at least one historical quote.
You must have at least one historical quote; however, more is typically provided since this is a chartable candlestick pattern.

`quotes` is an `IEnumerable<TQuote>` collection of historical price quotes. It should have a consistent frequency (day, hour, minute, etc). See [the Guide]({{site.baseurl}}/guide/#historical-quotes) for more information.

## Response

```csharp
IEnumerable<MarubozuResult>
IEnumerable<CandleResult>
```

- This method returns a time series of all available indicator values for the `quotes` provided.
- It always returns the same number of elements as there are in the historical quotes.
- It does not return a single incremental indicator value.
- The candlestick pattern is indicated on dates when `Marubozu` has a non-null value.
- The candlestick pattern is indicated on dates where `Signal` is `Signal.BullSignal` or `Signal.BearSignal`.
- There is no intrinsic basis or confirmation signal information provided for this pattern.

### MarubozuResult

| name | type | notes
| -- |-- |--
| `Date` | DateTime | Date
| `Marubozu` | decimal | Indicates a Marubozu candle `Close` price; otherwise `null`
| `IsBullish` | bool | Direction of the candle
{% include candle-result.md %}

### Utilities

- [.Condense()]({{site.baseurl}}/utilities#condense)
- [.Find(lookupDate)]({{site.baseurl}}/utilities#find-indicator-result-by-date)
- [.RemoveWarmupPeriods(qty)]({{site.baseurl}}/utilities#remove-warmup-periods)

Expand All @@ -63,5 +59,5 @@ See [Utilities and Helpers]({{site.baseurl}}/utilities#utilities-for-indicator-r
IEnumerable<Quote> quotes = GetHistoryFromFeed("SPY");

// calculate
IEnumerable<MarubozuResult> results = quotes.GetMarubozu();
IEnumerable<CandleResult> results = quotes.GetMarubozu();
```
43 changes: 43 additions & 0 deletions docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ redirect_from:
- [Using custom quote classes](#using-custom-quote-classes)
- [Using derived results classes](#using-derived-results-classes)
- [Generating indicator of indicators](#generating-indicator-of-indicators)
- [Candlestick patterns](#candlestick-patterns)
- [Utilities and Helper functions]({{site.baseurl}}/utilities/#content)
- [Contributing guidelines]({{site.github.repository_url}}/blob/main/docs/contributing.md#readme)

Expand Down Expand Up @@ -280,6 +281,48 @@ List<Quote> obvQuotes = obvResults
IEnumerable<RsiResult> results = obvQuotes.GetRsi(14);
```

## Candlestick patterns

[Candlestick Patterns]({{site.baseurl}}/indicators/#candlestick-pattern) are a unique form of indicator and have a common output model.

{% include candle-result.md %}

### Signal

When a candlestick pattern is recognized, it produces a signal. In some cases, an intrinsic confirmation is also available. In cases where previous bars were used to identify a pattern, they are indicates as the basis for the signal. This `enum` can also be referenced as the shown `int` value. [Documentation for each candlestick pattern]({{site.baseurl}}/indicators/#candlestick-pattern) will indicate whether confirmation and/or basis information is produced.

| type | int | description
|-- |--: |--
| `Signal.BullConfirmed` | 200 | Confirmation of a prior bull signal
| `Signal.BullSignal` | 100 | Matching bullish pattern
| `Signal.BullBasis` | 10 | Bars supporting a bullish signal
| `Signal.None` | 0 | No match
| `Signal.BearBasis` | -10 | Bars supporting a bearish signal
| `Signal.BearSignal` | -100 | Matching bearish pattern
| `Signal.BearConfirmed` | -200 | Confirmation of a prior bear signal

### Candle

The `Candle` class is an extended version of `Quote`, and contains additional properties.

| name | type | notes
| -- |-- |--
| `Date` | DateTime | Date
| `Open` | decimal | Open price
| `High` | decimal | High price
| `Low` | decimal | Low price
| `Close` | decimal | Close price
| `Volume` | decimal | Volume
| `Size` | decimal | `High-Low`
| `Body` | decimal | `|Open-Close|` ($)
| `UpperWick` | decimal | Upper wick size ($)
| `LowerWick` | decimal | Lower wick size ($)
| `BodyPct` | double | `Body/Size`
| `UpperWickPct` | double | `UpperWick/Size`
| `LowerWickPct` | double | `Lowerwick/Size`
| `IsBullish` | bool | `Close>Open` direction
| `IsBearish` | bool | `Close<Open` direction

## Utilities

See [Utilities and Helper functions]({{site.baseurl}}/utilities/#content) for additional tools.
27 changes: 21 additions & 6 deletions docs/utilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ IEnumerable<Quote> validatedQuotes = quotes.Validate();
IEnumerable<TQuote> minuteBarQuotes = GetHistoryFromFeed("MSFT");

// aggregate into larger bars
IEnumerable<Quote> dayBarQuotes =
IEnumerable<Quote> dayBarQuotes =
minuteBarQuotes.Aggregate(PeriodSize.Day);
```

An alternate version of this utility is provided where you can use any native `TimeSpan` value that is greater than `TimeSpan.Zero`.

```csharp
// alternate usage
IEnumerable<Quote> dayBarQuotes =
IEnumerable<Quote> dayBarQuotes =
minuteBarQuotes.Aggregate(TimeSpan timeSpan);
```

Expand All @@ -64,21 +64,36 @@ IEnumerable<Quote> dayBarQuotes =

## Utilities for indicator results

### Condense

`results.Condense()` will remove non-essential results so it only returns meaningful data records. For example, when used on [Candlestick Patterns]({{site.baseurl}}/indicators/#candlestick-pattern), it will only return records where a signal is generated.

```csharp
// example: only show Marubozu signals
IEnumerable<CandleResult> results
= quotes.GetMarubozu(..)
.Condense();
```

Currently, `.Condense()` is only available on a select few indicators. If you find an indicator that is a good candidate for this utility, please [submit an Issue]({{site.github.repository_url}}/issues).

:warning: WARNING! In all cases, `.Condense()` will remove non-essential results and will produce fewer records than are in `quotes`.

### Convert to quotes

`results.ConvertToQuotes()` will transform indicator results back into an `IEnumerable<Quote>` so it can be re-used to generate an [indicator of indicators]({{site.baseurl}}/guide/#generating-indicator-of-indicators).

```csharp
// example: an RSI of Renko bricks
IEnumerable<RsiResult> results
IEnumerable<RsiResult> results
= quotes.GetRenko(..)
.ConvertToQuotes()
.GetRsi(14);
```

Currently, `.ConvertToQuotes` is only available on a select few indicators. If you find an indicator that is a good candidate for this utility, please [submit an Issue]({{site.github.repository_url}}/issues).

:warning: WARNING! In many cases, `.ConvertToQuotes` will remove any `null` results -- this will produce fewer historical `quotes` than were originally provided.
:warning: WARNING! In many cases, `.ConvertToQuotes` will remove any `null` results -- this will produce fewer records than are in `quotes`.

### Find indicator result by date

Expand All @@ -103,12 +118,12 @@ An alternative `.RemoveWarmupPeriods(removePeriods)` is also provided if you wan

```csharp
// auto remove recommended warmup periods
IEnumerable<AdxResult> results =
IEnumerable<AdxResult> results =
quotes.GetAdx(14)
.RemoveWarmupPeriods();

// remove user-specific quantity of periods
IEnumerable<AdxResult> results =
IEnumerable<AdxResult> results =
quotes.GetAdx(14)
.RemoveWarmupPeriods(50);
```
Expand Down
28 changes: 21 additions & 7 deletions src/_common/Candles/Candles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,33 @@ namespace Skender.Stock.Indicators;

public static class Candlesticks
{
public static IEnumerable<CandleResult> Condense(
this IEnumerable<CandleResult> candleResults)
{
return candleResults
.Where(candle => candle.Signal != Signal.None)
.ToList();
}

// convert/sort quotes into candles
internal static List<Candle> ConvertToCandles<TQuote>(
internal static List<CandleResult> ConvertToCandleResults<TQuote>(
this IEnumerable<TQuote> quotes)
where TQuote : IQuote
{
List<Candle> candlesList = quotes
.Select(x => new Candle
List<CandleResult> candlesList = quotes
.Select(x => new CandleResult
{
Date = x.Date,
Open = x.Open,
High = x.High,
Low = x.Low,
Close = x.Close
Signal = Signal.None,
Candle = new Candle
{
Date = x.Date,
Open = x.Open,
High = x.High,
Low = x.Low,
Close = x.Close,
Volume = x.Volume
}
})
.OrderBy(x => x.Date)
.ToList();
Expand Down
16 changes: 12 additions & 4 deletions src/_common/Candles/Models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ namespace Skender.Stock.Indicators;
// CANDLESTICK MODELS

[Serializable]
internal class Candle : Quote
public class Candle : Quote
{
// raw sizes
internal decimal Size => High - Low;
Expand All @@ -12,11 +12,19 @@ internal class Candle : Quote
internal decimal LowerWick => (Open > Close ? Close : Open) - Low;

// percent sizes
internal decimal BodyPct => (Size != 0) ? Body / Size : 1m;
internal decimal UpperWickPct => (Size != 0) ? UpperWick / Size : 1m;
internal decimal LowerWickPct => (Size != 0) ? LowerWick / Size : 1m;
internal double BodyPct => (Size != 0) ? (double)(Body / Size) : 1;
internal double UpperWickPct => (Size != 0) ? (double)(UpperWick / Size) : 1;
internal double LowerWickPct => (Size != 0) ? (double)(LowerWick / Size) : 1;

// directional info
internal bool IsBullish => Close > Open;
internal bool IsBearish => Close < Open;
}

[Serializable]
public class CandleResult : ResultBase
{
public decimal? Price { get; set; }
public Signal Signal { get; set; }
public Candle Candle { get; set; }
}
11 changes: 11 additions & 0 deletions src/_common/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,14 @@ public enum PeriodSize
TwoMinutes,
OneMinute
}

public enum Signal
{
BullConfirmed = 200,
BullSignal = 100,
BullBasis = 10,
None = 0,
BearBasis = -10,
BearSignal = -100,
BearConfirmed = -200
}
2 changes: 1 addition & 1 deletion src/_common/Results/Results.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public interface IResult
}

[Serializable]
public class ResultBase : IResult
public abstract class ResultBase : IResult
{
public DateTime Date { get; set; }
}
Expand Down
8 changes: 0 additions & 8 deletions src/m-r/Marubozu/Marubozu.Models.cs

This file was deleted.

26 changes: 9 additions & 17 deletions src/m-r/Marubozu/Marubozu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,28 @@ public static partial class Indicator
// MARUBOZU
/// <include file='./info.xml' path='indicator/*' />
///
public static IEnumerable<MarubozuResult> GetMarubozu<TQuote>(
public static IEnumerable<CandleResult> GetMarubozu<TQuote>(
this IEnumerable<TQuote> quotes,
double minBodyPercent = 0.95)
where TQuote : IQuote
{
// check parameter arguments
ValidateMarubozu(minBodyPercent);

// convert quotes
List<Candle> candles = quotes.ConvertToCandles();

// initialize
int size = candles.Count;
List<MarubozuResult> results = new(size);
List<CandleResult> results = quotes.ConvertToCandleResults();
int length = results.Count;

// roll through candles
for (int i = 0; i < size; i++)
for (int i = 0; i < length; i++)
{
Candle c = candles[i];

MarubozuResult result = new()
{
Date = c.Date,
IsBullish = c.IsBullish
};
results.Add(result);
CandleResult r = results[i];

if (c.BodyPct >= (decimal)minBodyPercent)
// check for current signal
if (r.Candle.BodyPct >= minBodyPercent)
{
result.Marubozu = c.Close;
r.Price = r.Candle.Close;
r.Signal = r.Candle.IsBullish ? Signal.BullSignal : Signal.BearSignal;
}
}

Expand Down
Loading

0 comments on commit 8a3a623

Please sign in to comment.