-
Notifications
You must be signed in to change notification settings - Fork 0
Feat: Implement CheckPatternBasic Algorithm #218
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,81 @@ | ||||||
package checkpatternbasic | ||||||
|
||||||
import ( | ||||||
"testing" | ||||||
|
||||||
"github.com/stretchr/testify/assert" | ||||||
) | ||||||
|
||||||
func TestXxx(t *testing.T) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 테스트 코드 이름에 테스트 대상이 명확히 드러나도록 수정해주세요
Suggested change
|
||||||
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) | ||||||
} | ||||||
} | ||||||
|
||||||
} |
This file was deleted.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Job 구현이 깔끔하고 좋네요. 앞으로 새로운 job을 추가하실 때도 이처럼 하시면 됩니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이미 알고리즘을 테스트 하셨지만, job이 데이터를 잘 처리하는지 보기 위해 간단한 테스트만 추가해주세요. 여기 테스트는 알고리즘 테스트케이스만큼 많을 필요는 없습니다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
파일명을 example이 아니라 내용물이 잘 드러나도록 수정해주시기 바랍니다.
ex)model