-
Notifications
You must be signed in to change notification settings - Fork 250
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add PivotPoints indicators +semver: minor
- Loading branch information
1 parent
96ef389
commit 9c96bea
Showing
12 changed files
with
2,440 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
using System; | ||
|
||
namespace Skender.Stock.Indicators | ||
{ | ||
[Serializable] | ||
public class PivotPointsResult : ResultBase | ||
{ | ||
public decimal? R4 { get; set; } | ||
public decimal? R3 { get; set; } | ||
public decimal? R2 { get; set; } | ||
public decimal? R1 { get; set; } | ||
public decimal? PP { get; set; } | ||
public decimal? S1 { get; set; } | ||
public decimal? S2 { get; set; } | ||
public decimal? S3 { get; set; } | ||
public decimal? S4 { get; set; } | ||
} | ||
|
||
public enum PivotPointType | ||
{ | ||
// do not modify numbers, | ||
// just add new random numbers if extending | ||
|
||
Standard = 0, | ||
Camarilla = 1, | ||
Demark = 2, | ||
Fibonacci = 3, | ||
Woodie = 4 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,271 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
namespace Skender.Stock.Indicators | ||
{ | ||
public static partial class Indicator | ||
{ | ||
// PIVOT POINTS | ||
public static IEnumerable<PivotPointsResult> GetPivotPoints<TQuote>( | ||
IEnumerable<TQuote> history, | ||
PeriodSize windowSize, | ||
PivotPointType pointType = PivotPointType.Standard) | ||
where TQuote : IQuote | ||
{ | ||
|
||
// clean quotes | ||
List<TQuote> historyList = history.Sort(); | ||
|
||
// check parameters | ||
ValidatePivotPoints(history, windowSize); | ||
|
||
// initialize | ||
List<PivotPointsResult> results = new List<PivotPointsResult>(historyList.Count); | ||
PivotPointsResult windowPoint = new PivotPointsResult(); | ||
|
||
TQuote h0 = historyList[0]; | ||
int windowId = GetWindowNumber(h0.Date, windowSize); | ||
int windowEval; | ||
bool firstWindow = true; | ||
|
||
decimal windowHigh = h0.High; | ||
decimal windowLow = h0.Low; | ||
decimal windowOpen = h0.Open; | ||
decimal windowClose = h0.Close; | ||
|
||
// roll through history | ||
for (int i = 0; i < historyList.Count; i++) | ||
{ | ||
TQuote h = historyList[i]; | ||
|
||
PivotPointsResult r = new PivotPointsResult | ||
{ | ||
Date = h.Date | ||
}; | ||
|
||
// new window evaluation | ||
windowEval = GetWindowNumber(h.Date, windowSize); | ||
|
||
if (windowEval != windowId) | ||
{ | ||
windowId = windowEval; | ||
firstWindow = false; | ||
|
||
// set new levels | ||
if (pointType == PivotPointType.Woodie) | ||
{ | ||
windowOpen = h.Open; | ||
} | ||
|
||
windowPoint = GetPivotPoint(pointType, windowOpen, windowHigh, windowLow, windowClose); | ||
|
||
// reset window min/max thresholds | ||
windowOpen = h.Open; | ||
windowHigh = h.High; | ||
windowLow = h.Low; | ||
} | ||
|
||
// add levels | ||
if (!firstWindow) | ||
{ | ||
// pivot point | ||
r.PP = windowPoint.PP; | ||
|
||
// support | ||
r.S1 = windowPoint.S1; | ||
r.S2 = windowPoint.S2; | ||
r.S3 = windowPoint.S3; | ||
r.S4 = windowPoint.S4; | ||
|
||
// resistance | ||
r.R1 = windowPoint.R1; | ||
r.R2 = windowPoint.R2; | ||
r.R3 = windowPoint.R3; | ||
r.R4 = windowPoint.R4; | ||
} | ||
|
||
results.Add(r); | ||
|
||
// capture window threholds (for next iteration) | ||
windowHigh = (h.High > windowHigh) ? h.High : windowHigh; | ||
windowLow = (h.Low < windowLow) ? h.Low : windowLow; | ||
windowClose = h.Close; | ||
} | ||
|
||
return results; | ||
} | ||
|
||
|
||
internal static PivotPointsResult GetPivotPoint( | ||
PivotPointType type, decimal open, decimal high, decimal low, decimal close) | ||
{ | ||
return type switch | ||
{ | ||
PivotPointType.Standard => GetPivotPointStandard(high, low, close), | ||
PivotPointType.Camarilla => GetPivotPointCamarilla(high, low, close), | ||
PivotPointType.Demark => GetPivotPointDemark(open, high, low, close), | ||
PivotPointType.Fibonacci => GetPivotPointFibonacci(high, low, close), | ||
PivotPointType.Woodie => GetPivotPointWoodie(open, high, low), | ||
_ => null | ||
}; | ||
} | ||
|
||
public static PivotPointsResult GetPivotPointStandard( | ||
decimal high, decimal low, decimal close) | ||
{ | ||
decimal pp = (high + low + close) / 3; | ||
|
||
return new PivotPointsResult | ||
{ | ||
PP = pp, | ||
S1 = pp * 2 - high, | ||
S2 = pp - (high - low), | ||
R1 = pp * 2 - low, | ||
R2 = pp + (high - low) | ||
}; | ||
} | ||
|
||
public static PivotPointsResult GetPivotPointCamarilla( | ||
decimal high, decimal low, decimal close) | ||
{ | ||
return new PivotPointsResult | ||
{ | ||
PP = (high + low + close) / 3, | ||
S1 = close - (1m + 1m / 12) * (high - low), | ||
S2 = close - (1m + 1m / 6) * (high - low), | ||
S3 = close - (1m + 1m / 4) * (high - low), | ||
S4 = close - (1m + 1m / 2) * (high - low), | ||
R1 = close + (1m + 1m / 12) * (high - low), | ||
R2 = close + (1m + 1m / 6) * (high - low), | ||
R3 = close + (1m + 1m / 4) * (high - low), | ||
R4 = close + (1m + 1m / 2) * (high - low) | ||
}; | ||
} | ||
|
||
public static PivotPointsResult GetPivotPointDemark( | ||
decimal open, decimal high, decimal low, decimal close) | ||
{ | ||
decimal? x; | ||
|
||
if (close < open) | ||
{ | ||
x = high + 2 * low + close; | ||
} | ||
else if (close > open) | ||
{ | ||
x = 2 * high + low + close; | ||
} | ||
else if (close == open) | ||
{ | ||
x = high + low + 2 * close; | ||
} | ||
else | ||
{ | ||
x = null; | ||
} | ||
|
||
return new PivotPointsResult | ||
{ | ||
PP = x / 4, | ||
S1 = x / 2 - high, | ||
R1 = x / 2 - low | ||
}; | ||
} | ||
|
||
public static PivotPointsResult GetPivotPointFibonacci( | ||
decimal high, decimal low, decimal close) | ||
{ | ||
decimal pp = (high + low + close) / 3; | ||
|
||
return new PivotPointsResult | ||
{ | ||
PP = pp, | ||
S1 = pp - 0.382m * (high - low), | ||
S2 = pp - 0.618m * (high - low), | ||
S3 = pp - 1.000m * (high - low), | ||
R1 = pp + 0.382m * (high - low), | ||
R2 = pp + 0.618m * (high - low), | ||
R3 = pp + 1.000m * (high - low) | ||
}; | ||
} | ||
|
||
public static PivotPointsResult GetPivotPointWoodie( | ||
decimal currentOpen, decimal high, decimal low) | ||
{ | ||
decimal pp = (high + low + 2 * currentOpen) / 4; | ||
|
||
return new PivotPointsResult | ||
{ | ||
PP = pp, | ||
S1 = pp * 2 - high, | ||
S2 = pp - high + low, | ||
S3 = low - 2 * (high - pp), | ||
R1 = pp * 2 - low, | ||
R2 = pp + high - low, | ||
R3 = high + 2 * (pp - low), | ||
}; | ||
} | ||
|
||
|
||
private static int GetWindowNumber(DateTime d, PeriodSize windowSize) | ||
{ | ||
return windowSize switch | ||
{ | ||
PeriodSize.Month => d.Month, | ||
PeriodSize.Week => englishCalendar.GetWeekOfYear(d, englishCalendarWeekRule, englishFirstDayOfWeek), | ||
PeriodSize.Day => d.Day, | ||
PeriodSize.Hour => d.Hour, | ||
_ => 0 | ||
}; | ||
} | ||
|
||
|
||
private static void ValidatePivotPoints<TQuote>( | ||
IEnumerable<TQuote> history, PeriodSize windowSize) | ||
where TQuote : IQuote | ||
{ | ||
|
||
// count periods based on periodSize | ||
int qtyWindows = 0; | ||
|
||
switch (windowSize) | ||
{ | ||
case PeriodSize.Month: | ||
qtyWindows = history.Select(x => x.Date.Month).Distinct().Count(); | ||
break; | ||
|
||
case PeriodSize.Week: | ||
qtyWindows = history.Select(x => englishCalendar | ||
.GetWeekOfYear(x.Date, englishCalendarWeekRule, englishFirstDayOfWeek)) | ||
.Distinct().Count(); | ||
break; | ||
|
||
case PeriodSize.Day: | ||
qtyWindows = history.Select(x => x.Date.Day).Distinct().Count(); | ||
break; | ||
|
||
case PeriodSize.Hour: | ||
qtyWindows = history.Select(x => x.Date.Hour).Distinct().Count(); | ||
break; | ||
|
||
default: | ||
break; | ||
}; | ||
|
||
// check history to ensure 2+ periods are present | ||
if (qtyWindows < 2) | ||
{ | ||
string message = "Insufficient history provided for Pivot Points. " + | ||
string.Format(englishCulture, | ||
"You provided {0} {1} windows of history when at least 2 are required. " | ||
+ "This can be from either not enough history or insufficiently detailed Date values.", | ||
qtyWindows, Enum.GetName(typeof(PeriodSize), windowSize)); | ||
|
||
throw new BadHistoryException(nameof(history), message); | ||
} | ||
|
||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# PivotPoints | ||
|
||
[Pivot Points](https://en.wikipedia.org/wiki/Pivot_point_(technical_analysis)) depict support and resistance levels, based on the prior lookback window. You can specify window size (e.g. month, week, day, etc). | ||
[[Discuss] :speech_balloon:](https://github.com/DaveSkender/Stock.Indicators/discussions/274 "Community discussion about this indicator") | ||
|
||
![image](chart.png) | ||
|
||
```csharp | ||
// usage | ||
IEnumerable<PivotPointResult> results = Indicator.GetPivotPoints(history, windowSize, pointType); | ||
``` | ||
|
||
## Parameters | ||
|
||
| name | type | notes | ||
| -- |-- |-- | ||
| `history` | IEnumerable\<[TQuote](../../docs/GUIDE.md#quote)\> | Historical price quotes should have a consistent frequency (day, hour, minute, etc) | ||
| `windowSize` | PeriodSize | Size of the lookback window | ||
| `pointType` | PivotPointType | Type of Pivot Point. Default is `PivotPointType.Standard` | ||
|
||
### Minimum history requirements | ||
|
||
You must supply at least `2` windows of `history`. For example, if you specify a `Week` window size, you need at least 14 days of `history`. | ||
|
||
### PeriodSize options (for windowSize) | ||
|
||
| type | description | ||
|-- |-- | ||
| `PeriodSize.Month` | Use the prior month's data to calculate current month's Pivot Points | ||
| `PeriodSize.Week` | [..] weekly | ||
| `PeriodSize.Day` | [..] daily. Commonly used for intraday data. | ||
| `PeriodSize.Hour` | [..] hourly | ||
|
||
### PivotPointType options | ||
|
||
| type | description | ||
|-- |-- | ||
| `PivotPointType.Standard` | Standard "floor trading" Pivot Points | ||
| `PivotPointType.Camarilla` | Camarilla | ||
| `PivotPointType.Demark` | Demark | ||
| `PivotPointType.Fibonacci` | Fibonacci | ||
| `PivotPointType.Woodie` | Woodie | ||
|
||
## Response | ||
|
||
```csharp | ||
IEnumerable<PivotPointsResult> | ||
``` | ||
|
||
The first window will have `null` values since there's not enough data to calculate. We always return the same number of elements as there are in the historical quotes. | ||
|
||
:warning: **Warning**: The second window may be innaccurate if the first window contains incomplete data. For example, this can occur if you specify a `Month` window size and only provide 45 days (1.5 months) of `history`. | ||
|
||
### PivotPointResult | ||
|
||
| name | type | notes | ||
| -- |-- |-- | ||
| `Date` | DateTime | Date | ||
| `R3` | decimal | Resistance level 3 | ||
| `R2` | decimal | Resistance level 2 | ||
| `R1` | decimal | Resistance level 1 | ||
| `PP` | decimal | Pivot Point | ||
| `S1` | decimal | Support level 1 | ||
| `S2` | decimal | Support level 2 | ||
| `S3` | decimal | Support level 3 | ||
|
||
## Example | ||
|
||
```csharp | ||
// fetch historical quotes from your favorite feed, in Quote format | ||
IEnumerable<Quote> history = GetHistoryFromFeed("SPY"); | ||
|
||
// calculate Woodie-style month-based Pivot Points | ||
IEnumerable<PivotPointResult> results = Indicator.GetPivotPoints(history,PeriodSize.Month,PivotPointType.Woodie); | ||
|
||
// use results as needed | ||
PivotPointsResult result = results.LastOrDefault(); | ||
Console.WriteLine("PP on {0} was ${1}", result.Date, result.PP); | ||
``` | ||
|
||
```bash | ||
PP on 12/31/2018 was $251.86 | ||
``` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.