order.go 2.4 KB

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