strategy.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. package sentio
  2. import (
  3. "errors"
  4. "git.beejay.kim/Gshopper/sentio/indicator"
  5. "math"
  6. "time"
  7. )
  8. type Strategy interface {
  9. Name() string
  10. Model() string
  11. MarketId() string
  12. PositionSymbols() map[Side]string
  13. PositionProbabilities() map[Side]float64
  14. Interval() uint8
  15. Cooldown(periods uint8) time.Duration
  16. Handle(market Market, probability Probability) error
  17. }
  18. const (
  19. ATR_MULTIPLIER = 1.5
  20. ATR_PERIOD = 5
  21. ATR_HHV = 4
  22. ATR_STOPLOSS_THRESHOLD = 1
  23. EXTRA_POSITION_THRESHOLD = 1.002
  24. )
  25. type BaseStrategy struct {
  26. Strategy
  27. }
  28. func (strategy BaseStrategy) Symbols() []string {
  29. var symbols []string
  30. for side, s := range strategy.PositionSymbols() {
  31. if BASE == side {
  32. continue
  33. }
  34. symbols = append(symbols, s)
  35. }
  36. return symbols
  37. }
  38. func (strategy BaseStrategy) CloseAllOrders(market Market) error {
  39. var (
  40. symbols = strategy.Symbols()
  41. orders []Order
  42. err error
  43. )
  44. if orders, err = market.Orders(OrderListCriteria{
  45. Status: "open",
  46. Symbols: symbols,
  47. Nested: true,
  48. }); err != nil {
  49. return err
  50. }
  51. for i := range orders {
  52. if _, err = market.CloseOrder(orders[i].GetId()); err != nil {
  53. return err
  54. }
  55. }
  56. return nil
  57. }
  58. func (strategy BaseStrategy) AtrStopLoss(market Market, symbols ...string) (map[string]float64, error) {
  59. var (
  60. stoplosses map[string]float64
  61. bars map[string][]Bar
  62. err error
  63. )
  64. if bars, err = market.HistoricalBars(symbols, time.Minute, nil); err != nil {
  65. return nil, err
  66. }
  67. if bars == nil || len(bars) < ATR_PERIOD {
  68. return nil, errors.New("AtrStopLoss: could not calculate stoploss for too short timeseries")
  69. }
  70. stoplosses = make(map[string]float64)
  71. for s := range bars {
  72. h := make([]float64, len(bars[s]))
  73. l := make([]float64, len(bars[s]))
  74. c := make([]float64, len(bars[s]))
  75. for i := range bars[s] {
  76. h[i] = bars[s][i].High
  77. l[i] = bars[s][i].Low
  78. c[i] = bars[s][i].Close
  79. }
  80. trailing := indicator.AtrTrailingStopLoss(h, l, c, ATR_PERIOD, ATR_MULTIPLIER, ATR_HHV)
  81. stoplosses[s] = trailing[len(trailing)-1]
  82. }
  83. return stoplosses, nil
  84. }
  85. func (strategy BaseStrategy) CreateOrder(m Market, t Side, proba Probability, p Portfolio, q map[string]Quote, sl map[string]float64) error {
  86. var (
  87. symbol = strategy.PositionSymbols()[t]
  88. position Position
  89. account MarketAccount
  90. has = false
  91. threshold float64
  92. size uint
  93. ok bool
  94. err error
  95. )
  96. // ensure portfolio
  97. position, has = p.Get(symbol)
  98. // define threshold
  99. if threshold, ok = strategy.PositionProbabilities()[t]; !ok {
  100. threshold = -1
  101. }
  102. if threshold == -1 || symbol == "" {
  103. return nil
  104. }
  105. // Prevent Market.CreateOrder when BidPrice less than ATR_STOPLOSS_THRESHOLD
  106. if q[symbol].BidPrice/sl[symbol] < ATR_STOPLOSS_THRESHOLD {
  107. return nil
  108. }
  109. if account, err = m.Account(); err != nil {
  110. return err
  111. }
  112. if account.GetCash() < q[symbol].BidPrice {
  113. return ErrTooSmallOrder
  114. }
  115. if !has && proba.Value > threshold {
  116. // create a new order
  117. if size = uint(math.Floor(account.GetCash() * .7 / q[symbol].BidPrice)); size < 1 {
  118. return ErrTooSmallOrder
  119. }
  120. _, err = m.CreateOrder(symbol, size, sl[symbol])
  121. return err
  122. } else if has && position.GetAvgPrice()/position.GetCurrentPrice() > EXTRA_POSITION_THRESHOLD {
  123. // create an extra position
  124. if size = uint(math.Floor(account.GetCash() * .5 / q[symbol].BidPrice)); size < 1 {
  125. return ErrTooSmallOrder
  126. }
  127. _, err = m.CreateOrder(symbol, size, sl[symbol])
  128. return err
  129. }
  130. return nil
  131. }