-
-
Notifications
You must be signed in to change notification settings - Fork 150
Add trend indicator DPO #281
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+403
−0
Merged
Changes from 2 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
e60ea3a
implemented SkipLast function and Detrended Price Oscillator (DPO) wi…
SagarKansara98 d41e070
Merge branch 'master' into feature/dpo
SagarKansara98 6067733
fix: handle invalid period values in DPO indicator initialization
SagarKansara98 bff12e0
Merge branch 'master' into feature/dpo
SagarKansara98 e7d342e
fix: minor formatting and code style issues
SagarKansara98 a12fbe0
fix: prevent invalid period values by making field unexported
SagarKansara98 516ff35
fix: nity comment
SagarKansara98 f90f81f
Add DPO to documentation.
cinar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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,32 @@ | ||
| package helper | ||
|
|
||
| // SkipLast skips the specified number of elements | ||
| // from the end of the given channel. | ||
| // | ||
| // Example: | ||
| // | ||
| // c := helper.SliceToChan([]int{2, 4, 6, 8}) | ||
| // actual := helper.SkipLast(c, 2) | ||
| // fmt.Println(helper.ChanToSlice(actual)) // [2, 4] | ||
| func SkipLast[T any](c <-chan T, count int) <-chan T { | ||
| result := make(chan T, cap(c)) | ||
|
|
||
| go func() { | ||
| defer close(result) | ||
|
|
||
| // Buffer to hold the last "count" elements | ||
| buf := make([]T, 0, count) | ||
|
|
||
| for v := range c { | ||
| buf = append(buf, v) | ||
| if len(buf) > count { | ||
| // send the oldest value | ||
| result <- buf[0] | ||
| buf = buf[1:] | ||
| } | ||
| } | ||
| // drop the last `count` elements automatically | ||
| }() | ||
|
|
||
| return result | ||
| } |
This file contains hidden or 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,23 @@ | ||
| // Copyright (c) 2021-2024 Onur Cinar. | ||
| // The source code is provided under GNU AGPLv3 License. | ||
| // https://github.com/cinar/indicator | ||
|
|
||
| package helper_test | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/cinar/indicator/v2/helper" | ||
| ) | ||
|
|
||
| func TestSkipLast(t *testing.T) { | ||
| input := helper.SliceToChan([]int{2, 4, 6, 8}) | ||
| expected := helper.SliceToChan([]int{2, 4}) | ||
|
|
||
| actual := helper.SkipLast(input, 2) | ||
|
|
||
| err := helper.CheckEquals(actual, expected) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| } |
This file contains hidden or 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,75 @@ | ||
| // Copyright (c) 2021-2024 Onur Cinar. | ||
| // The source code is provided under GNU AGPLv3 License. | ||
| // https://github.com/cinar/indicator | ||
|
|
||
| package trend | ||
|
|
||
| import ( | ||
| "fmt" | ||
|
|
||
| "github.com/cinar/indicator/v2/helper" | ||
| ) | ||
|
|
||
| // DefaultDpoPeriod is the default period for DPO calculation. | ||
| const DefaultDpoPeriod = 20 | ||
|
|
||
| // Dpo computes the Detrended Price Oscillator. | ||
| // | ||
| // Formula (common approximation): | ||
| // | ||
| // k = period/2 + 1 | ||
| // DPO = Price - SMA(Price shifted by k) | ||
| // | ||
| // Example: | ||
| // | ||
| // dpo := trend.NewDpo[float64]() | ||
| // dpo.Period = 20 | ||
| // out := dpo.Compute(c) | ||
| type Dpo[T helper.Number] struct { | ||
| Period int | ||
| } | ||
|
|
||
| // NewDpo creates a new DPO instance with default parameters. | ||
| func NewDpo[T helper.Number]() *Dpo[T] { | ||
| return &Dpo[T]{ | ||
| Period: DefaultDpoPeriod, | ||
| } | ||
| } | ||
|
|
||
| // NewDpoWithPeriod function initializes a new DPO instance with the given period. | ||
| func NewDpoWithPeriod[T helper.Number](period int) *Dpo[T] { | ||
| dpo := NewDpo[T]() | ||
| dpo.Period = period | ||
|
|
||
| return dpo | ||
|
|
||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // Compute calculates the DPO indicator over the input price channel. | ||
| func (d *Dpo[T]) Compute(closing <-chan T) <-chan T { | ||
| closingSplice := helper.Duplicate(closing, 2) | ||
|
|
||
| // compute SMA on the first duplicated stream | ||
| sma := NewSma[T]() | ||
| sma.Period = d.Period | ||
| smaOut := sma.Compute(closingSplice[0]) | ||
|
|
||
| // align the original price stream and the SMA stream according to DPO formula | ||
| skippedClosing := helper.Skip(closingSplice[1], d.IdlePeriod()) | ||
| smaDelayed := helper.SkipLast(smaOut, d.Period/2+1) | ||
|
|
||
| // DPO = Price - shifted SMA | ||
| return helper.Operate(skippedClosing, smaDelayed, func(price, shiftedSma T) T { | ||
| return price - shiftedSma | ||
| }) | ||
| } | ||
|
|
||
| // IdlePeriod is the initial period that DPO yield any results. | ||
| func (d *Dpo[T]) IdlePeriod() int { | ||
| return (d.Period - 1) + (d.Period/2 + 1) | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // String is the string representation of the DPO. | ||
| func (d *Dpo[T]) String() string { | ||
| return fmt.Sprintf("DPO(%d)", d.Period) | ||
| } | ||
This file contains hidden or 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,42 @@ | ||
| package trend_test | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/cinar/indicator/v2/helper" | ||
| "github.com/cinar/indicator/v2/trend" | ||
| ) | ||
|
|
||
| func TestDpo(t *testing.T) { | ||
| type DpoData struct { | ||
| Close float64 | ||
| Dpo float64 | ||
| } | ||
|
|
||
| input, err := helper.ReadFromCsvFile[DpoData]("testdata/dpo.csv") | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
|
|
||
| inputs := helper.Duplicate(input, 2) | ||
| closing := helper.Map(inputs[0], func(d *DpoData) float64 { return d.Close }) | ||
| expected := helper.Map(inputs[1], func(d *DpoData) float64 { return d.Dpo }) | ||
|
|
||
| dpo := trend.NewDpo[float64]() | ||
| actual := helper.RoundDigits(dpo.Compute(closing), 2) | ||
| expected = helper.Skip(expected, dpo.IdlePeriod()) | ||
|
|
||
| err = helper.CheckEquals(actual, expected) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| } | ||
|
|
||
| func TestDpoString(t *testing.T) { | ||
| expected := "DPO(10)" | ||
| actual := trend.NewDpoWithPeriod[float64](10).String() | ||
|
|
||
| if actual != expected { | ||
| t.Fatalf("actual %v expected %v", actual, expected) | ||
| } | ||
| } |
This file contains hidden or 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,128 @@ | ||
| Close,Dpo | ||
| 1111.55,00.0 | ||
| 1062.70,00.0 | ||
| 1082.90,00.0 | ||
| 1085.15,00.0 | ||
| 1082.85,00.0 | ||
| 1111.70,00.0 | ||
| 1083.25,00.0 | ||
| 1070.40,00.0 | ||
| 1080.35,00.0 | ||
| 1078.95,00.0 | ||
| 1069.40,00.0 | ||
| 1053.05,00.0 | ||
| 1057.95,00.0 | ||
| 1112.70,00.0 | ||
| 1136.20,00.0 | ||
| 1144.50,00.0 | ||
| 1142.90,00.0 | ||
| 1135.00,00.0 | ||
| 1128.30,00.0 | ||
| 1119.20,00.0 | ||
| 1137.15,00.0 | ||
| 1137.15,00.0 | ||
| 1173.50,00.0 | ||
| 1177.45,00.0 | ||
| 1188.80,00.0 | ||
| 1200.80,00.0 | ||
| 1182.45,00.0 | ||
| 1181.60,00.0 | ||
| 1196.75,00.0 | ||
| 1182.95,00.0 | ||
| 1174.75,77.30 | ||
| 1195.45,96.72 | ||
| 1199.90,97.45 | ||
| 1148.35,41.37 | ||
| 1110.65,-0.95 | ||
| 1131.35,14.45 | ||
| 1131.80,10.45 | ||
| 1164.55,38.24 | ||
| 1210.80,78.93 | ||
| 1232.90,95.21 | ||
| 1259.40,116.51 | ||
| 1243.10,94.94 | ||
| 1233.20,77.92 | ||
| 1239.10,76.72 | ||
| 1236.40,72.24 | ||
| 1193.00,30.12 | ||
| 1209.90,47.68 | ||
| 1216.40,54.73 | ||
| 1216.50,53.36 | ||
| 1263.10,95.83 | ||
| 1347.10,174.15 | ||
| 1320.80,141.73 | ||
| 1338.20,153.84 | ||
| 1333.10,145.75 | ||
| 1306.30,115.87 | ||
| 1362.10,169.29 | ||
| 1369.20,176.78 | ||
| 1371.80,178.01 | ||
| 1406.20,210.66 | ||
| 1407.40,210.88 | ||
| 1398.30,197.77 | ||
| 1384.60,175.45 | ||
| 1384.60,169.19 | ||
| 1371.40,149.07 | ||
| 1395.40,163.83 | ||
| 1400.20,158.85 | ||
| 1404.20,151.31 | ||
| 1411.00,146.24 | ||
| 1438.60,163.48 | ||
| 1432.80,147.91 | ||
| 1468.00,174.39 | ||
| 1432.30,131.74 | ||
| 1436.20,128.57 | ||
| 1456.70,141.50 | ||
| 1471.70,149.88 | ||
| 1467.10,137.33 | ||
| 1473.80,133.67 | ||
| 1456.40,106.56 | ||
| 1445.80,86.23 | ||
| 1405.00,34.32 | ||
| 1400.60,21.44 | ||
| 1392.30,7.09 | ||
| 1372.60,-18.18 | ||
| 1338.00,-57.68 | ||
| 1349.30,-52.56 | ||
| 1354.80,-55.33 | ||
| 1389.50,-25.88 | ||
| 1393.10,-27.51 | ||
| 1429.30,4.46 | ||
| 1440.20,13.38 | ||
| 1450.20,23.50 | ||
| 1447.00,20.18 | ||
| 1443.90,16.70 | ||
| 1431.10,4.50 | ||
| 1424.40,-0.53 | ||
| 1435.60,12.97 | ||
| 1448.50,28.14 | ||
| 1443.00,23.38 | ||
| 1443.10,24.37 | ||
| 1431.70,13.44 | ||
| 1433.00,14.37 | ||
| 1445.10,27.36 | ||
| 1456.70,38.22 | ||
| 1452.50,33.64 | ||
| 1443.10,25.52 | ||
| 1450.50,35.28 | ||
| 1420.70,7.06 | ||
| 1420.80,8.42 | ||
| 1411.60,-0.11 | ||
| 1396.30,-15.27 | ||
| 1376.00,-36.91 | ||
| 1395.90,-18.63 | ||
| 1394.00,-23.17 | ||
| 1373.10,-48.27 | ||
| 1347.10,-80.00 | ||
| 1388.90,-42.89 | ||
| 1358.10,-78.47 | ||
| 1367.10,-71.03 | ||
| 1345.40,-94.12 | ||
| 1325.00,-113.63 | ||
| 1339.40,-97.04 | ||
| 1330.50,-102.23 | ||
| 1319.60,-110.57 | ||
| 1300.30,-127.38 | ||
| 1327.20,-97.58 | ||
| 1369.40,-51.51 | ||
| 1370.60,-47.98 |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.