diff --git a/internal/algorithm/checkpatternbasic/example.go b/internal/algorithm/checkpatternbasic/example.go new file mode 100644 index 0000000..7eb2560 --- /dev/null +++ b/internal/algorithm/checkpatternbasic/example.go @@ -0,0 +1,111 @@ +package checkpatternbasic + +import "math" + +type Status int + +const ( + StatusUndefined Status = iota + StatusDecreasing + StatusIncreasing +) + +type ChangeStatus int + +const ( + ChangeStatusUnchanged ChangeStatus = iota + ChangeStatusDecreasing + ChangeStatusIncreasing +) + +type TradeEvent int + +const ( + TradeEventHold TradeEvent = iota + TradeEventBuy + TradeEventSell +) + +func (m *Model) calculateChangeStatus(value float64) ChangeStatus { + if m.prvValue < value && (value-m.currMin) >= m.threshold { + return ChangeStatusIncreasing + } + + if m.prvValue > value && (m.currMax-value) >= m.threshold { + return ChangeStatusDecreasing + } + + return ChangeStatusUnchanged +} + +type Model struct { + Status Status + currMin float64 + currMax float64 + prvValue float64 + threshold float64 +} + +func NewModel(threshold float64) *Model { + return &Model{ + Status: StatusUndefined, + currMin: math.MaxFloat64, + currMax: -math.MaxFloat64, + prvValue: 0.0, + threshold: threshold, + } +} + +func (m *Model) OnEvent(value float64) (float64, TradeEvent) { + m.currMax = max(m.currMax, value) + m.currMin = min(m.currMin, value) + + var event TradeEvent = TradeEventHold + + switch m.Status { + + case StatusIncreasing: + switch m.calculateChangeStatus(value) { + case ChangeStatusDecreasing: + m.Status = StatusDecreasing + event = TradeEventSell + m.currMin = value + } + + case StatusDecreasing: + switch m.calculateChangeStatus(value) { + case ChangeStatusIncreasing: + m.Status = StatusIncreasing + event = TradeEventBuy + m.currMax = value + } + + case StatusUndefined: + switch m.calculateChangeStatus(value) { + case ChangeStatusIncreasing: + m.Status = StatusIncreasing + event = TradeEventBuy + case ChangeStatusDecreasing: + m.Status = StatusDecreasing + event = TradeEventSell + } + } + + m.prvValue = value + + var power float64 + if m.currMax == m.currMin { + power = 0 + return power, event + } + + switch m.Status { + case StatusIncreasing: + power = value - m.currMax + case StatusDecreasing: + power = value - m.currMin + case StatusUndefined: + power = 0 + } + return power, event +} diff --git a/internal/algorithm/checkpatternbasic/example_test.go b/internal/algorithm/checkpatternbasic/example_test.go new file mode 100644 index 0000000..1877e0a --- /dev/null +++ b/internal/algorithm/checkpatternbasic/example_test.go @@ -0,0 +1,81 @@ +package checkpatternbasic + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestXxx(t *testing.T) { + type outputFormat struct { + rate float64 + event TradeEvent + } + + // Arrange + var tests = []struct { + model *Model + input []float64 + output []outputFormat + }{ + { + NewModel(3), + []float64{0, 1, 2, 4, 5}, + []outputFormat{ + {0, TradeEventHold}, + {0, TradeEventHold}, + {0, TradeEventHold}, + {0, TradeEventBuy}, + {0, TradeEventHold}, + }, + }, + { + NewModel(2.5), + []float64{5, 4, 3, 2, 1}, + []outputFormat{ + {0, TradeEventHold}, + {0, TradeEventHold}, + {0, TradeEventHold}, + {0, TradeEventSell}, + {0, TradeEventHold}, + }, + }, + { + NewModel(3), + []float64{1, 3, 5, 4, 2}, + []outputFormat{ + {0, TradeEventHold}, + {0, TradeEventHold}, + {0, TradeEventBuy}, + {-1, TradeEventHold}, + {0, TradeEventSell}, + }, + }, + { + NewModel(5), + []float64{1, 3, 7, 5, 2, 3, 4, 1}, + []outputFormat{ + {0, TradeEventHold}, + {0, TradeEventHold}, + {0, TradeEventBuy}, + {-2, TradeEventHold}, + {0, TradeEventSell}, + {1, TradeEventHold}, + {2, TradeEventHold}, + {0, TradeEventHold}, + }, + }, + } + + for i, v := range tests { + for j, value := range v.input { + // Act + rate, event := v.model.OnEvent(value) + + // Assert + assert.Equal(t, v.output[j].rate, rate, "Test case %d, input %d", i, j) + assert.Equal(t, v.output[j].event, event, "Test case %d, input %d", i, j) + } + } + +} diff --git a/internal/algorithm/example.go b/internal/algorithm/example.go deleted file mode 100644 index e25d47b..0000000 --- a/internal/algorithm/example.go +++ /dev/null @@ -1,3 +0,0 @@ -package algorithm - -//이 패키지에 알고리즘 관련 공통모듈 구현 diff --git a/internal/job/analyzer/checkpatternbasic.go b/internal/job/analyzer/checkpatternbasic.go new file mode 100644 index 0000000..09e16e1 --- /dev/null +++ b/internal/job/analyzer/checkpatternbasic.go @@ -0,0 +1,70 @@ +package analyzer + +import ( + "github.com/Goboolean/core-system.worker/internal/algorithm/checkpatternbasic" + "github.com/Goboolean/core-system.worker/internal/job" + "github.com/Goboolean/core-system.worker/internal/model" + "github.com/Goboolean/core-system.worker/internal/util/chanutil" +) + +type CheckPatternBasic struct { + in job.DataChan + out job.DataChan + + model checkpatternbasic.Model +} + +func NewCheckPatternBasic(parmas *job.UserParams) (*CheckPatternBasic, error) { + instance := &CheckPatternBasic{ + out: make(job.DataChan), + } + + return instance, nil +} + +func (s *CheckPatternBasic) Execute() error { + defer close(s.out) + defer func() { + go chanutil.DummyChannelConsumer(s.in) + }() + + for v := range s.in { + t := v.Time + stock := v.Data.(*model.StockAggregate) + + indicator, action := s.model.OnEvent(float64(stock.Close)) + + switch action { + case checkpatternbasic.TradeEventBuy: + s.out <- model.Packet{ + Time: t, + Data: &model.TradeCommand{ + Action: model.Buy, + ProportionPercent: 0, + }, + } + case checkpatternbasic.TradeEventSell: + s.out <- model.Packet{ + Time: t, + Data: &model.TradeCommand{ + Action: model.Sell, + ProportionPercent: 0, + }, + } + } + + s.out <- model.Packet{ + Time: t, + Data: indicator, + } + } + return nil +} + +func (s *CheckPatternBasic) SetInput(in job.DataChan) { + s.in = in +} + +func (s *CheckPatternBasic) Output() job.DataChan { + return s.out +} diff --git a/internal/job/analyzer/example.go b/internal/job/analyzer/example.go deleted file mode 100644 index 4ef62c9..0000000 --- a/internal/job/analyzer/example.go +++ /dev/null @@ -1,51 +0,0 @@ -package analyzer - -import ( - "github.com/Goboolean/core-system.worker/internal/job" - "github.com/Goboolean/core-system.worker/internal/model" - "github.com/Goboolean/core-system.worker/internal/util/chanutil" -) - -type Example struct { - in job.DataChan - out job.DataChan -} - -func NewExample(parmas *job.UserParams) (*Example, error) { - instance := &Example{ - out: make(job.DataChan), - } - - return instance, nil -} - -func (s *Example) Execute() error { - - defer close(s.out) - defer func() { - go chanutil.DummyChannelConsumer(s.in) - }() - - for v := range s.in { - t := v.Time - //stock := v.Data.(*model.StockAggregate) - //여기에 연산 로직 구현 - - s.out <- model.Packet{ - Time: t, - Data: &model.TradeCommand{ - Action: model.Sell, - ProportionPercent: 0, - }, - } - } - return nil -} - -func (s *Example) SetInput(in job.DataChan) { - s.in = in -} - -func (s *Example) Output() job.DataChan { - return s.out -}