-
Notifications
You must be signed in to change notification settings - Fork 0
/
strategy.go
138 lines (105 loc) · 4.05 KB
/
strategy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// flashcrash strategy tries to place the orders at 30%~50% of the current price,
// so that you can catch the orders while flashcrash happens
package flashcrash
import (
"context"
"sync"
log "github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/indicator"
"github.com/c9s/bbgo/pkg/types"
)
const ID = "flashcrash"
func init() {
bbgo.RegisterStrategy(ID, &Strategy{})
}
type Strategy struct {
// These fields will be filled from the config file (it translates YAML to JSON)
// Symbol is the symbol of market you want to run this strategy
Symbol string `json:"symbol"`
// Interval is the interval used to trigger order updates
Interval types.Interval `json:"interval"`
// GridNum is the grid number, how many orders you want to places
GridNum int `json:"gridNumber"`
Percentage float64 `json:"percentage"`
// BaseQuantity is the quantity you want to submit for each order.
BaseQuantity float64 `json:"baseQuantity"`
// activeOrders is the locally maintained active order book of the maker orders.
activeOrders *bbgo.LocalActiveOrderBook
// Injection fields start
// --------------------------
// Market stores the configuration of the market, for example, VolumePrecision, PricePrecision, MinLotSize... etc
// This field will be injected automatically since we defined the Symbol field.
types.Market
// StandardIndicatorSet contains the standard indicators of a market (symbol)
// This field will be injected automatically since we defined the Symbol field.
*bbgo.StandardIndicatorSet
// Graceful shutdown function
*bbgo.Graceful
// --------------------------
// ewma is the exponential weighted moving average indicator
ewma *indicator.EWMA
}
func (s *Strategy) ID() string {
return ID
}
func (s *Strategy) updateOrders(orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) {
if err := session.Exchange.CancelOrders(context.Background(), s.activeOrders.Bids.Orders()...); err != nil {
log.WithError(err).Errorf("cancel order error")
}
s.updateBidOrders(orderExecutor, session)
}
func (s *Strategy) updateBidOrders(orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) {
quoteCurrency := s.Market.QuoteCurrency
balances := session.Account.Balances()
balance, ok := balances[quoteCurrency]
if !ok || balance.Available <= 0 {
log.Infof("insufficient balance of %s: %f", quoteCurrency, balance.Available.Float64())
return
}
var startPrice = s.ewma.Last() * s.Percentage
var submitOrders []types.SubmitOrder
for i := 0; i < s.GridNum; i++ {
submitOrders = append(submitOrders, types.SubmitOrder{
Symbol: s.Symbol,
Side: types.SideTypeBuy,
Type: types.OrderTypeLimit,
Market: s.Market,
Quantity: s.BaseQuantity,
Price: startPrice,
TimeInForce: "GTC",
})
startPrice *= s.Percentage
}
orders, err := orderExecutor.SubmitOrders(context.Background(), submitOrders...)
if err != nil {
log.WithError(err).Error("submit bid order error")
return
}
s.activeOrders.Add(orders...)
}
func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: string(s.Interval)})
}
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
// we don't persist orders so that we can not clear the previous orders for now. just need time to support this.
s.activeOrders = bbgo.NewLocalActiveOrderBook()
s.activeOrders.BindStream(session.Stream)
s.Graceful.OnShutdown(func(ctx context.Context, wg *sync.WaitGroup) {
defer wg.Done()
log.Infof("canceling active orders...")
if err := session.Exchange.CancelOrders(ctx, s.activeOrders.Orders()...); err != nil {
log.WithError(err).Errorf("cancel order error")
}
})
s.ewma = s.StandardIndicatorSet.EWMA(types.IntervalWindow{
Interval: s.Interval,
Window: 25,
})
session.Stream.OnKLineClosed(func(kline types.KLine) {
s.updateOrders(orderExecutor, session)
})
// TODO: move this to the stream onConnect handler
s.updateOrders(orderExecutor, session)
return nil
}