order.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. package util
  2. import (
  3. "errors"
  4. "git.beejay.kim/Gshopper/sentio"
  5. "math"
  6. )
  7. const TradingRange = .02
  8. func ToPtr[T any](x T) *T {
  9. return &x
  10. }
  11. func NewOrder(
  12. m sentio.Market,
  13. s sentio.Strategy,
  14. action sentio.Action,
  15. quotes map[string]sentio.Quote,
  16. rm sentio.RiskManager,
  17. ) (opts sentio.OrderCreateOptions, err error) {
  18. var (
  19. side sentio.Side
  20. base string
  21. bid float64
  22. ok bool
  23. )
  24. if side = sentio.ActionToSide(action); sentio.BASE == side {
  25. err = errors.New("`CreateOrder`: do nothing while `sentio.BASE`")
  26. return
  27. }
  28. if base, ok = s.PositionSymbols()[sentio.BASE]; !ok {
  29. err = errors.New("`CreateOrder`: unknown `sentio.BASE`")
  30. return
  31. }
  32. if opts.Symbol, ok = s.PositionSymbols()[side]; !ok {
  33. err = errors.New("`CreateOrder`: unknown Side for the current strategy")
  34. return
  35. }
  36. canLeverage := (side == sentio.LONG && m.EnableLongLeverage()) || (side == sentio.SHORT && m.EnableShortLeverage())
  37. if !canLeverage {
  38. opts.Symbol = base
  39. }
  40. bid = sentio.ToFixed(quotes[opts.Symbol].BidPrice, 2)
  41. opts.Action = sentio.Action_BUY
  42. opts.TakeProfit = ToPtr(sentio.ToFixed(rm.TakeProfit(opts.Symbol, bid), 2))
  43. opts.StopLoss = ToPtr(sentio.ToFixed(rm.StopLoss(opts.Symbol, bid), 2))
  44. // Prevent orders those have too small expected profit
  45. if *opts.TakeProfit < bid+TradingRange {
  46. err = sentio.ErrLowTakeProfit
  47. return
  48. }
  49. // Prevent cases when order will be closed ASAP they were opened.
  50. // Also, Alpaca requires at least 0.01 gap against base_price
  51. if *opts.StopLoss > bid-TradingRange {
  52. err = sentio.ErrHighStoploss
  53. return
  54. }
  55. // Invert OrderOptions if we will do SHORT for BASE symbol
  56. if sentio.SHORT == side && !canLeverage {
  57. tmp := opts.StopLoss
  58. opts.StopLoss = opts.TakeProfit
  59. opts.TakeProfit = tmp
  60. opts.Action = sentio.Action_SELL
  61. }
  62. var (
  63. account sentio.MarketAccount
  64. cash float64
  65. ratio float64
  66. )
  67. if account, err = m.Account(); err != nil {
  68. return
  69. }
  70. cash = account.GetCash(false)
  71. if budget := m.MaxBudget(); budget > 0 && cash > budget {
  72. cash = budget
  73. }
  74. if cash <= bid {
  75. err = sentio.ErrTooSmallOrder
  76. return
  77. }
  78. if ratio = rm.GetOrderSize(opts.Symbol, bid); ratio <= 0 {
  79. err = sentio.ErrRiskManagementPrevent
  80. return
  81. }
  82. if opts.Size = uint(math.Floor(cash * ratio / bid)); opts.Size < 1 {
  83. err = sentio.ErrTooSmallOrder
  84. }
  85. return opts, nil
  86. }
  87. func CloseAllOrders(m sentio.Market, s sentio.Strategy) error {
  88. var (
  89. symbols = Symbols(s)
  90. orders []sentio.Order
  91. err error
  92. )
  93. if orders, err = m.Orders(sentio.OrderListCriteria{
  94. Status: ToPtr("open"),
  95. Symbols: symbols,
  96. }); err != nil {
  97. return err
  98. }
  99. for i := range orders {
  100. if _, err = m.CloseOrder(orders[i], nil); err != nil {
  101. return err
  102. }
  103. }
  104. return nil
  105. }