|
| 1 | +// Copyright (c) 2021-2026 Onur Cinar. |
| 2 | +// The source code is provided under GNU AGPLv3 License. |
| 3 | +// https://github.com/cinar/indicator |
| 4 | + |
| 5 | +package momentum |
| 6 | + |
| 7 | +import ( |
| 8 | + "context" |
| 9 | + |
| 10 | + "github.com/cinar/indicator/v2/asset" |
| 11 | + "github.com/cinar/indicator/v2/helper" |
| 12 | + "github.com/cinar/indicator/v2/momentum" |
| 13 | + "github.com/cinar/indicator/v2/strategy" |
| 14 | +) |
| 15 | + |
| 16 | +// CoppockCurveStrategy represents the configuration parameters for calculating the Coppock Curve strategy. |
| 17 | +// A positive Coppock Curve value suggests a Buy signal, while a negative value suggests a Sell signal. |
| 18 | +type CoppockCurveStrategy struct { |
| 19 | + // CoppockCurve represents the configuration parameters for calculating the Coppock Curve. |
| 20 | + CoppockCurve *momentum.CoppockCurve[float64] |
| 21 | +} |
| 22 | + |
| 23 | +// NewCoppockCurveStrategy function initializes a new Coppock Curve strategy instance with the default parameters. |
| 24 | +func NewCoppockCurveStrategy() *CoppockCurveStrategy { |
| 25 | + return &CoppockCurveStrategy{ |
| 26 | + CoppockCurve: momentum.NewCoppockCurve[float64](), |
| 27 | + } |
| 28 | +} |
| 29 | + |
| 30 | +// Name returns the name of the strategy. |
| 31 | +func (*CoppockCurveStrategy) Name() string { |
| 32 | + return "Coppock Curve Strategy" |
| 33 | +} |
| 34 | + |
| 35 | +// ComputeWithContext processes the provided asset snapshots and generates a stream of actionable recommendations. |
| 36 | +func (c *CoppockCurveStrategy) ComputeWithContext(ctx context.Context, snapshots <-chan *asset.Snapshot) <-chan strategy.Action { |
| 37 | + closings := asset.SnapshotsAsClosingsWithContext(ctx, snapshots) |
| 38 | + |
| 39 | + coppock := c.CoppockCurve.ComputeWithContext(ctx, closings) |
| 40 | + |
| 41 | + actions := helper.MapWithContext(ctx, coppock, func(value float64) strategy.Action { |
| 42 | + if value > 0 { |
| 43 | + return strategy.Buy |
| 44 | + } |
| 45 | + |
| 46 | + if value < 0 { |
| 47 | + return strategy.Sell |
| 48 | + } |
| 49 | + |
| 50 | + return strategy.Hold |
| 51 | + }) |
| 52 | + |
| 53 | + actions = helper.ShiftWithContext(ctx, actions, c.CoppockCurve.IdlePeriod(), strategy.Hold) |
| 54 | + |
| 55 | + return actions |
| 56 | +} |
| 57 | + |
| 58 | +// Report processes the provided asset snapshots and generates a report annotated with the recommended actions. |
| 59 | +func (c *CoppockCurveStrategy) Report(cr <-chan *asset.Snapshot) *helper.Report { |
| 60 | + // |
| 61 | + // snapshots[0] -> dates |
| 62 | + // snapshots[1] -> Compute -> actions -> annotations |
| 63 | + // snapshots[2] -> closings[0] -> close |
| 64 | + // closings[1] -> CoppockCurve.Compute -> coppock |
| 65 | + // |
| 66 | + snapshots := helper.Duplicate(cr, 3) |
| 67 | + |
| 68 | + dates := asset.SnapshotsAsDates(snapshots[0]) |
| 69 | + |
| 70 | + closings := helper.Duplicate(asset.SnapshotsAsClosings(snapshots[2]), 2) |
| 71 | + |
| 72 | + coppock := helper.Shift(c.CoppockCurve.Compute(closings[0]), c.CoppockCurve.IdlePeriod(), 0) |
| 73 | + |
| 74 | + actions, outcomes := strategy.ComputeWithOutcome(c, snapshots[1]) |
| 75 | + annotations := strategy.ActionsToAnnotations(actions) |
| 76 | + outcomes = helper.MultiplyBy(outcomes, 100) |
| 77 | + |
| 78 | + report := helper.NewReport(c.Name(), dates) |
| 79 | + report.AddChart() |
| 80 | + report.AddChart() |
| 81 | + |
| 82 | + report.AddColumn(helper.NewNumericReportColumn("Close", closings[1])) |
| 83 | + report.AddColumn(helper.NewNumericReportColumn("Coppock Curve", coppock), 1) |
| 84 | + report.AddColumn(helper.NewAnnotationReportColumn(annotations), 0, 1) |
| 85 | + |
| 86 | + report.AddColumn(helper.NewNumericReportColumn("Outcome", outcomes), 2) |
| 87 | + |
| 88 | + return report |
| 89 | +} |
| 90 | + |
| 91 | +// Compute wraps ComputeWithContext for backwards compatibility. |
| 92 | +// |
| 93 | +// Deprecated: Use ComputeWithContext instead. |
| 94 | +func (c *CoppockCurveStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan strategy.Action { |
| 95 | + return c.ComputeWithContext(context.Background(), snapshots) |
| 96 | +} |
0 commit comments