strategy.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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. }
  27. func (strategy BaseStrategy) Name() string {
  28. panic("implement me")
  29. }
  30. func (strategy BaseStrategy) Model() string {
  31. panic("implement me")
  32. }
  33. func (strategy BaseStrategy) MarketId() string {
  34. return "alpaca"
  35. }
  36. func (strategy BaseStrategy) PositionSymbols() map[Side]string {
  37. return nil
  38. }
  39. func (strategy BaseStrategy) PositionProbabilities() map[Side]float64 {
  40. return nil
  41. }
  42. func (strategy BaseStrategy) Interval() uint8 {
  43. return 5
  44. }
  45. func (strategy BaseStrategy) Cooldown(periods uint8) time.Duration {
  46. return time.Minute * time.Duration(strategy.Interval()*periods)
  47. }
  48. func (strategy BaseStrategy) Handle(market Market, probability Probability) error {
  49. return nil
  50. }
  51. func (strategy BaseStrategy) Symbols() []string {
  52. var symbols []string
  53. for side, s := range strategy.PositionSymbols() {
  54. if BASE == side {
  55. continue
  56. }
  57. symbols = append(symbols, s)
  58. }
  59. return symbols
  60. }
  61. func (strategy BaseStrategy) CloseAllOrders(market Market) error {
  62. var (
  63. symbols = strategy.Symbols()
  64. orders []Order
  65. err error
  66. )
  67. if orders, err = market.Orders(OrderListCriteria{
  68. Status: "open",
  69. Symbols: symbols,
  70. Nested: true,
  71. }); err != nil {
  72. return err
  73. }
  74. for i := range orders {
  75. if _, err = market.CloseOrder(orders[i].GetId()); err != nil {
  76. return err
  77. }
  78. }
  79. return nil
  80. }
  81. func (strategy BaseStrategy) AtrStopLoss(market Market, symbols ...string) (map[string]float64, error) {
  82. var (
  83. stoplosses map[string]float64
  84. bars map[string][]Bar
  85. err error
  86. )
  87. if bars, err = market.HistoricalBars(symbols, time.Minute, nil); err != nil {
  88. return nil, err
  89. }
  90. if bars == nil || len(bars) < ATR_PERIOD {
  91. return nil, errors.New("AtrStopLoss: could not calculate stoploss for too short timeseries")
  92. }
  93. stoplosses = make(map[string]float64)
  94. for s := range bars {
  95. h := make([]float64, len(bars[s]))
  96. l := make([]float64, len(bars[s]))
  97. c := make([]float64, len(bars[s]))
  98. for i := range bars[s] {
  99. h[i] = bars[s][i].High
  100. l[i] = bars[s][i].Low
  101. c[i] = bars[s][i].Close
  102. }
  103. trailing := indicator.AtrTrailingStopLoss(h, l, c, ATR_PERIOD, ATR_MULTIPLIER, ATR_HHV)
  104. stoplosses[s] = trailing[len(trailing)-1]
  105. }
  106. return stoplosses, nil
  107. }
  108. func (strategy BaseStrategy) CreateOrder(m Market, t Side, proba Probability, p Portfolio, q map[string]Quote, sl map[string]float64) error {
  109. var (
  110. symbol = strategy.PositionSymbols()[t]
  111. position Position
  112. account MarketAccount
  113. has = false
  114. threshold float64
  115. size uint
  116. ok bool
  117. err error
  118. )
  119. // ensure portfolio
  120. position, has = p.Get(symbol)
  121. // define threshold
  122. if threshold, ok = strategy.PositionProbabilities()[t]; !ok {
  123. threshold = -1
  124. }
  125. if threshold == -1 || symbol == "" {
  126. return nil
  127. }
  128. // Prevent Market.CreateOrder when BidPrice less than ATR_STOPLOSS_THRESHOLD
  129. if q[symbol].BidPrice/sl[symbol] < ATR_STOPLOSS_THRESHOLD {
  130. return nil
  131. }
  132. if account, err = m.Account(); err != nil {
  133. return err
  134. }
  135. if account.GetCash() < q[symbol].BidPrice {
  136. return ErrTooSmallOrder
  137. }
  138. if !has && proba.Value > threshold {
  139. // create a new order
  140. if size = uint(math.Floor(account.GetCash() * .7 / q[symbol].BidPrice)); size < 1 {
  141. return ErrTooSmallOrder
  142. }
  143. _, err = m.CreateOrder(symbol, size, sl[symbol])
  144. return err
  145. } else if has && position.GetAvgPrice()/position.GetCurrentPrice() > EXTRA_POSITION_THRESHOLD {
  146. // create an extra position
  147. if size = uint(math.Floor(account.GetCash() * .5 / q[symbol].BidPrice)); size < 1 {
  148. return ErrTooSmallOrder
  149. }
  150. _, err = m.CreateOrder(symbol, size, sl[symbol])
  151. return err
  152. }
  153. return nil
  154. }