Skip to content

Commit 62d450b

Browse files
authored
Merge pull request #872 from andycheng123/fix/trailing-stop
fix: trailing stop properly works on both long and short positions
2 parents 88ca021 + 8527d49 commit 62d450b

File tree

1 file changed

+29
-8
lines changed

1 file changed

+29
-8
lines changed

pkg/bbgo/exit_trailing_stop.go

+29-8
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,16 @@ func (s *TrailingStop2) getRatio(price fixedpoint.Value, position *types.Positio
7676
switch s.Side {
7777
case types.SideTypeBuy:
7878
// for short position, it's:
79-
// (avg_cost - price) / price
80-
return position.AverageCost.Sub(price).Div(price), nil
79+
// (avg_cost - price) / avg_cost
80+
return position.AverageCost.Sub(price).Div(position.AverageCost), nil
8181
case types.SideTypeSell:
8282
return price.Sub(position.AverageCost).Div(position.AverageCost), nil
83+
default:
84+
if position.IsLong() {
85+
return price.Sub(position.AverageCost).Div(position.AverageCost), nil
86+
} else if position.IsShort() {
87+
return position.AverageCost.Sub(price).Div(position.AverageCost), nil
88+
}
8389
}
8490

8591
return fixedpoint.Zero, fmt.Errorf("unexpected side type: %v", s.Side)
@@ -117,6 +123,12 @@ func (s *TrailingStop2) checkStopPrice(price fixedpoint.Value, position *types.P
117123
s.latestHigh = fixedpoint.Min(price, s.latestHigh)
118124
case types.SideTypeSell:
119125
s.latestHigh = fixedpoint.Max(price, s.latestHigh)
126+
default:
127+
if position.IsLong() {
128+
s.latestHigh = fixedpoint.Max(price, s.latestHigh)
129+
} else if position.IsShort() {
130+
s.latestHigh = fixedpoint.Min(price, s.latestHigh)
131+
}
120132
}
121133
}
122134

@@ -126,22 +138,31 @@ func (s *TrailingStop2) checkStopPrice(price fixedpoint.Value, position *types.P
126138

127139
switch s.Side {
128140
case types.SideTypeBuy:
129-
s.latestHigh = fixedpoint.Min(price, s.latestHigh)
130-
131141
change := price.Sub(s.latestHigh).Div(s.latestHigh)
132142
if change.Compare(s.CallbackRate) >= 0 {
133143
// submit order
134144
return s.triggerStop(price)
135145
}
136-
137146
case types.SideTypeSell:
138-
s.latestHigh = fixedpoint.Max(price, s.latestHigh)
139-
140-
change := s.latestHigh.Sub(price).Div(price)
147+
change := s.latestHigh.Sub(price).Div(s.latestHigh)
141148
if change.Compare(s.CallbackRate) >= 0 {
142149
// submit order
143150
return s.triggerStop(price)
144151
}
152+
default:
153+
if position.IsLong() {
154+
change := s.latestHigh.Sub(price).Div(s.latestHigh)
155+
if change.Compare(s.CallbackRate) >= 0 {
156+
// submit order
157+
return s.triggerStop(price)
158+
}
159+
} else if position.IsShort() {
160+
change := price.Sub(s.latestHigh).Div(s.latestHigh)
161+
if change.Compare(s.CallbackRate) >= 0 {
162+
// submit order
163+
return s.triggerStop(price)
164+
}
165+
}
145166
}
146167

147168
return nil

0 commit comments

Comments
 (0)