strategy.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. package main
  2. import (
  3. "fmt"
  4. "git.beejay.kim/Gshopper/sentio"
  5. "git.beejay.kim/Gshopper/sentio/indicator"
  6. "time"
  7. )
  8. const (
  9. ATR_MULTIPLIER = 1.5
  10. ATR_PERIOD = 5
  11. ATR_HHV = 4
  12. ATR_STOPLOSS_THRESHOLD = 1
  13. EXTRA_POSITION_THRESHOLD = 1.002
  14. )
  15. var Strategy = alpacaQQQ{}
  16. type alpacaQQQ struct{}
  17. func (s alpacaQQQ) Name() string {
  18. return "Alpaca: QQQ [TQQQ : SQQQ]"
  19. }
  20. func (s alpacaQQQ) Model() string {
  21. return "qqq15"
  22. }
  23. func (s alpacaQQQ) MarketId() string {
  24. return "alpaca"
  25. }
  26. func (s alpacaQQQ) PositionSymbols() map[sentio.Side]string {
  27. return map[sentio.Side]string{
  28. sentio.BASE: "QQQ",
  29. sentio.LONG: "TQQQ",
  30. sentio.SHORT: "SQQQ",
  31. }
  32. }
  33. func (s alpacaQQQ) Interval() uint8 {
  34. return 5
  35. }
  36. func (s alpacaQQQ) Cooldown(periods uint8) time.Duration {
  37. return time.Minute * time.Duration(s.Interval()*periods)
  38. }
  39. func (s alpacaQQQ) Handle(market sentio.Market, ts time.Time, proba float64) ([]sentio.StrategyOrder, error) {
  40. var (
  41. portfolio sentio.Portfolio
  42. orders []sentio.StrategyOrder
  43. now = market.Time().Now()
  44. err error
  45. )
  46. // skip too early trades
  47. if now.Hour() == 9 && now.Minute() < 45 {
  48. return orders, nil
  49. }
  50. if portfolio, err = market.Portfolio(); err != nil {
  51. return nil, err
  52. }
  53. for side, symbol := range s.PositionSymbols() {
  54. var (
  55. position sentio.Position
  56. quote *sentio.Quote
  57. bars []sentio.Bar
  58. stoploss float64
  59. uptrending bool
  60. ok bool
  61. )
  62. // no need to trade BASE quote
  63. if sentio.BASE == side {
  64. continue
  65. }
  66. if quote, err = market.Quote(symbol); err != nil {
  67. return nil, err
  68. }
  69. fmt.Printf("Quotes: %s [BID: %f] [ASK: %s]\n", symbol, quote.BidPrice, quote.AskPrice)
  70. if bars, err = market.HistoricalBars(symbol, time.Minute, nil); err == nil && len(bars) > 0 {
  71. h := make([]float64, len(bars))
  72. l := make([]float64, len(bars))
  73. c := make([]float64, len(bars))
  74. for i := range bars {
  75. h[i] = bars[i].High
  76. l[i] = bars[i].Low
  77. c[i] = bars[i].Close
  78. }
  79. trailing := indicator.AtrTrailingStopLoss(h, l, c, ATR_PERIOD, ATR_MULTIPLIER, ATR_HHV)
  80. stoploss = trailing[len(trailing)-1]
  81. uptrending = trailing[len(trailing)-1] > trailing[len(trailing)-2]
  82. fmt.Printf("ATR Trailing StopLoss: [%s: %f; uptrading: %v]\n", symbol, stoploss, uptrending)
  83. }
  84. if portfolio != nil {
  85. position, ok = portfolio.Get(symbol)
  86. ok = ok && position != nil && position.GetSize() != 0
  87. } else {
  88. ok = false
  89. }
  90. // Close positions before market closed
  91. if ok && now.Hour() == 15 && now.Minute() > 55 {
  92. return []sentio.StrategyOrder{{
  93. Symbol: symbol,
  94. Action: sentio.OrderSell,
  95. Ratio: 1,
  96. }}, nil
  97. }
  98. // Close position if BidPrice less than StopLoss
  99. if ok && !uptrending && quote.BidPrice/stoploss < ATR_STOPLOSS_THRESHOLD {
  100. return []sentio.StrategyOrder{{
  101. Symbol: symbol,
  102. Action: sentio.OrderSell,
  103. Ratio: 1,
  104. }}, nil
  105. }
  106. if proba < 0 {
  107. continue
  108. }
  109. // Close LONG position
  110. if ok && sentio.LONG == side && proba < 1.0008 {
  111. return []sentio.StrategyOrder{{
  112. Symbol: symbol,
  113. Action: sentio.OrderSell,
  114. Ratio: 1,
  115. }}, nil
  116. }
  117. // Close SHORT position
  118. if ok && sentio.SHORT == side && proba > .9998 {
  119. return []sentio.StrategyOrder{{
  120. Symbol: symbol,
  121. Action: sentio.OrderSell,
  122. Ratio: 1,
  123. }}, nil
  124. }
  125. // Prevent BUYs for delayed probas
  126. if ts.Add(time.Minute * time.Duration(int64(s.Interval()/2))).Before(now.Round(time.Minute)) {
  127. continue
  128. }
  129. // Prevent BUYs on closing market
  130. if now.Hour() == 15 && now.Minute() > 46 {
  131. continue
  132. }
  133. if quote.BidPrice/stoploss < ATR_STOPLOSS_THRESHOLD {
  134. continue
  135. }
  136. if sentio.LONG == side && proba > 1 {
  137. size := float64(0)
  138. if ok && position.GetAvgPrice()/position.GetCurrentPrice() > EXTRA_POSITION_THRESHOLD {
  139. // extra position with cheaper price
  140. size = .5
  141. } else if !ok && proba > 1.0008 {
  142. // new trade position
  143. size = .7
  144. }
  145. if size > 0 {
  146. orders = append(orders, sentio.StrategyOrder{
  147. Symbol: symbol,
  148. Action: sentio.OrderBuy,
  149. Ratio: size,
  150. })
  151. }
  152. continue
  153. }
  154. if sentio.SHORT == side && proba < 1 {
  155. size := float64(0)
  156. if ok && position.GetAvgPrice()/position.GetCurrentPrice() > EXTRA_POSITION_THRESHOLD {
  157. // extra position with cheaper price
  158. size = .5
  159. } else if !ok && proba < .9990 {
  160. // new trade position
  161. size = .7
  162. }
  163. if size > 0 {
  164. orders = append(orders, sentio.StrategyOrder{
  165. Symbol: symbol,
  166. Action: sentio.OrderBuy,
  167. Ratio: size,
  168. })
  169. }
  170. continue
  171. }
  172. }
  173. return orders, nil
  174. }