Merge pull request #872 from andycheng123/fix/trailing-stop

fix: trailing stop properly works on both long and short positions
This commit is contained in:
Andy Cheng 2022-08-11 17:37:07 +08:00 committed by GitHub
commit 62d450b92d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -76,10 +76,16 @@ func (s *TrailingStop2) getRatio(price fixedpoint.Value, position *types.Positio
switch s.Side { switch s.Side {
case types.SideTypeBuy: case types.SideTypeBuy:
// for short position, it's: // for short position, it's:
// (avg_cost - price) / price // (avg_cost - price) / avg_cost
return position.AverageCost.Sub(price).Div(price), nil return position.AverageCost.Sub(price).Div(position.AverageCost), nil
case types.SideTypeSell: case types.SideTypeSell:
return price.Sub(position.AverageCost).Div(position.AverageCost), nil return price.Sub(position.AverageCost).Div(position.AverageCost), nil
default:
if position.IsLong() {
return price.Sub(position.AverageCost).Div(position.AverageCost), nil
} else if position.IsShort() {
return position.AverageCost.Sub(price).Div(position.AverageCost), nil
}
} }
return fixedpoint.Zero, fmt.Errorf("unexpected side type: %v", s.Side) 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
s.latestHigh = fixedpoint.Min(price, s.latestHigh) s.latestHigh = fixedpoint.Min(price, s.latestHigh)
case types.SideTypeSell: case types.SideTypeSell:
s.latestHigh = fixedpoint.Max(price, s.latestHigh) s.latestHigh = fixedpoint.Max(price, s.latestHigh)
default:
if position.IsLong() {
s.latestHigh = fixedpoint.Max(price, s.latestHigh)
} else if position.IsShort() {
s.latestHigh = fixedpoint.Min(price, s.latestHigh)
}
} }
} }
@ -126,22 +138,31 @@ func (s *TrailingStop2) checkStopPrice(price fixedpoint.Value, position *types.P
switch s.Side { switch s.Side {
case types.SideTypeBuy: case types.SideTypeBuy:
s.latestHigh = fixedpoint.Min(price, s.latestHigh)
change := price.Sub(s.latestHigh).Div(s.latestHigh) change := price.Sub(s.latestHigh).Div(s.latestHigh)
if change.Compare(s.CallbackRate) >= 0 { if change.Compare(s.CallbackRate) >= 0 {
// submit order // submit order
return s.triggerStop(price) return s.triggerStop(price)
} }
case types.SideTypeSell: case types.SideTypeSell:
s.latestHigh = fixedpoint.Max(price, s.latestHigh) change := s.latestHigh.Sub(price).Div(s.latestHigh)
change := s.latestHigh.Sub(price).Div(price)
if change.Compare(s.CallbackRate) >= 0 { if change.Compare(s.CallbackRate) >= 0 {
// submit order // submit order
return s.triggerStop(price) return s.triggerStop(price)
} }
default:
if position.IsLong() {
change := s.latestHigh.Sub(price).Div(s.latestHigh)
if change.Compare(s.CallbackRate) >= 0 {
// submit order
return s.triggerStop(price)
}
} else if position.IsShort() {
change := price.Sub(s.latestHigh).Div(s.latestHigh)
if change.Compare(s.CallbackRate) >= 0 {
// submit order
return s.triggerStop(price)
}
}
} }
return nil return nil