mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
feature: SLTP from bookticker. fix: bookTicker typename, depth buffer error message
This commit is contained in:
parent
668328dd16
commit
71fe6c2d26
|
@ -582,7 +582,7 @@ func (session *ExchangeSession) MarketDataStore(symbol string) (s *MarketDataSto
|
|||
return s, ok
|
||||
}
|
||||
|
||||
// MarketDataStore returns the market data store of a symbol
|
||||
// OrderBook returns the personal orderbook of a symbol
|
||||
func (session *ExchangeSession) OrderBook(symbol string) (s *types.StreamOrderBook, ok bool) {
|
||||
s, ok = session.orderBooks[symbol]
|
||||
return s, ok
|
||||
|
@ -935,9 +935,9 @@ func (session *ExchangeSession) SlackAttachment() slack.Attachment {
|
|||
return slack.Attachment{
|
||||
// Pretext: "",
|
||||
// Text: text,
|
||||
Title: session.Name,
|
||||
Fields: fields,
|
||||
Title: session.Name,
|
||||
Fields: fields,
|
||||
FooterIcon: footerIcon,
|
||||
Footer: util.Render("update time {{ . }}", time.Now().Format(time.RFC822)),
|
||||
Footer: util.Render("update time {{ . }}", time.Now().Format(time.RFC822)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,13 +117,14 @@ func (b *Buffer) AddUpdate(o types.SliceOrderBook, firstUpdateID int64, finalArg
|
|||
if u.FirstUpdateID > b.finalUpdateID+1 {
|
||||
// emitReset will reset the once outside the mutex lock section
|
||||
b.buffer = []Update{u}
|
||||
finalUpdateID = b.finalUpdateID
|
||||
b.resetSnapshot()
|
||||
b.emitReset()
|
||||
b.mu.Unlock()
|
||||
return fmt.Errorf("found missing update between finalUpdateID %d and firstUpdateID %d, diff: %d",
|
||||
b.finalUpdateID+1,
|
||||
finalUpdateID+1,
|
||||
u.FirstUpdateID,
|
||||
u.FirstUpdateID-b.finalUpdateID)
|
||||
u.FirstUpdateID-finalUpdateID)
|
||||
}
|
||||
|
||||
log.Debugf("depth update id %d -> %d", b.finalUpdateID, u.FinalUpdateID)
|
||||
|
@ -142,6 +143,7 @@ func (b *Buffer) fetchAndPush() error {
|
|||
log.Debugf("fetched depth snapshot, final update id %d", finalUpdateID)
|
||||
|
||||
b.mu.Lock()
|
||||
|
||||
if len(b.buffer) > 0 {
|
||||
// the snapshot is too early
|
||||
if finalUpdateID < b.buffer[0].FirstUpdateID {
|
||||
|
|
|
@ -1428,9 +1428,22 @@ func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *type
|
|||
|
||||
// QueryDepth query the order book depth of a symbol
|
||||
func (e *Exchange) QueryDepth(ctx context.Context, symbol string) (snapshot types.SliceOrderBook, finalUpdateID int64, err error) {
|
||||
response, err := e.client.NewDepthService().Symbol(symbol).Do(ctx)
|
||||
if err != nil {
|
||||
return snapshot, finalUpdateID, err
|
||||
var response *binance.DepthResponse
|
||||
if e.IsFutures {
|
||||
res, err := e.futuresClient.NewDepthService().Symbol(symbol).Do(ctx)
|
||||
if err != nil {
|
||||
return snapshot, finalUpdateID, err
|
||||
}
|
||||
response = &binance.DepthResponse{
|
||||
LastUpdateID: res.LastUpdateID,
|
||||
Bids: res.Bids,
|
||||
Asks: res.Asks,
|
||||
}
|
||||
} else {
|
||||
response, err = e.client.NewDepthService().Symbol(symbol).Do(ctx)
|
||||
if err != nil {
|
||||
return snapshot, finalUpdateID, err
|
||||
}
|
||||
}
|
||||
|
||||
snapshot.Symbol = symbol
|
||||
|
|
|
@ -113,17 +113,17 @@ func (e *ExecutionReportEvent) Order() (*types.Order, error) {
|
|||
orderCreationTime := time.Unix(0, e.OrderCreationTime*int64(time.Millisecond))
|
||||
return &types.Order{
|
||||
SubmitOrder: types.SubmitOrder{
|
||||
ClientOrderID: e.ClientOrderID,
|
||||
Symbol: e.Symbol,
|
||||
Side: toGlobalSideType(binance.SideType(e.Side)),
|
||||
Type: toGlobalOrderType(binance.OrderType(e.OrderType)),
|
||||
Quantity: e.OrderQuantity,
|
||||
Price: e.OrderPrice,
|
||||
StopPrice: e.StopPrice,
|
||||
TimeInForce: types.TimeInForce(e.TimeInForce),
|
||||
IsFutures: false,
|
||||
ReduceOnly: false,
|
||||
ClosePosition: false,
|
||||
ClientOrderID: e.ClientOrderID,
|
||||
Symbol: e.Symbol,
|
||||
Side: toGlobalSideType(binance.SideType(e.Side)),
|
||||
Type: toGlobalOrderType(binance.OrderType(e.OrderType)),
|
||||
Quantity: e.OrderQuantity,
|
||||
Price: e.OrderPrice,
|
||||
StopPrice: e.StopPrice,
|
||||
TimeInForce: types.TimeInForce(e.TimeInForce),
|
||||
IsFutures: false,
|
||||
ReduceOnly: false,
|
||||
ClosePosition: false,
|
||||
},
|
||||
Exchange: types.ExchangeBinance,
|
||||
IsWorking: e.IsOnBook,
|
||||
|
@ -276,7 +276,7 @@ func parseWebSocketEvent(message []byte) (interface{}, error) {
|
|||
// fmt.Println(str)
|
||||
eventType := string(val.GetStringBytes("e"))
|
||||
if eventType == "" && IsBookTicker(val) {
|
||||
eventType = "bookticker"
|
||||
eventType = "bookTicker"
|
||||
}
|
||||
|
||||
switch eventType {
|
||||
|
@ -284,7 +284,7 @@ func parseWebSocketEvent(message []byte) (interface{}, error) {
|
|||
var event KLineEvent
|
||||
err := json.Unmarshal([]byte(message), &event)
|
||||
return &event, err
|
||||
case "bookticker":
|
||||
case "bookTicker":
|
||||
var event BookTickerEvent
|
||||
err := json.Unmarshal([]byte(message), &event)
|
||||
event.Event = eventType
|
||||
|
|
|
@ -23,6 +23,7 @@ func init() {
|
|||
}
|
||||
|
||||
type Strategy struct {
|
||||
*bbgo.Environment
|
||||
Market types.Market
|
||||
Session *bbgo.ExchangeSession
|
||||
UseHeikinAshi bool `json:"useHeikinAshi"` // use heikinashi kline
|
||||
|
@ -45,6 +46,8 @@ type Strategy struct {
|
|||
heikinAshi *HeikinAshi
|
||||
peakPrice fixedpoint.Value
|
||||
bottomPrice fixedpoint.Value
|
||||
midPrice fixedpoint.Value
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *Strategy) ID() string {
|
||||
|
@ -59,6 +62,10 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
|
|||
log.Infof("subscribe %s", s.Symbol)
|
||||
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: types.Interval1m.String()})
|
||||
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.Interval.String()})
|
||||
|
||||
if s.Environment != nil && s.Environment.IsBackTesting() {
|
||||
session.Subscribe(types.BookTickerChannel, s.Symbol, types.SubscribeOptions{})
|
||||
}
|
||||
s.SmartStops.Subscribe(session)
|
||||
}
|
||||
|
||||
|
@ -531,15 +538,176 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
|
||||
s.SetupIndicators()
|
||||
|
||||
sellOrderTPSL := func(price fixedpoint.Value) {
|
||||
balances := session.GetAccount().Balances()
|
||||
quoteBalance := balances[s.Market.QuoteCurrency].Available
|
||||
atrx2 := fixedpoint.NewFromFloat(s.atr.Last() * 2)
|
||||
lastPrice := price
|
||||
var ok bool
|
||||
if s.Environment.IsBackTesting() {
|
||||
lastPrice, ok = session.LastPrice(s.Symbol)
|
||||
if !ok {
|
||||
log.Errorf("cannot get last price")
|
||||
return
|
||||
}
|
||||
}
|
||||
buyall := false
|
||||
if !sellPrice.IsZero() {
|
||||
if s.bottomPrice.IsZero() || s.bottomPrice.Compare(price) > 0 {
|
||||
s.bottomPrice = price
|
||||
}
|
||||
}
|
||||
takeProfit := false
|
||||
bottomBack := s.bottomPrice
|
||||
if !quoteBalance.IsZero() && !sellPrice.IsZero() && !s.DisableShortStop {
|
||||
// TP
|
||||
if !atrx2.IsZero() && s.bottomPrice.Add(atrx2).Compare(lastPrice) >= 0 &&
|
||||
lastPrice.Compare(sellPrice) < 0 {
|
||||
buyall = true
|
||||
s.bottomPrice = fixedpoint.Zero
|
||||
takeProfit = true
|
||||
}
|
||||
|
||||
// SL
|
||||
if (!atrx2.IsZero() && sellPrice.Add(atrx2).Compare(lastPrice) <= 0) ||
|
||||
lastPrice.Sub(sellPrice).Div(sellPrice).Compare(s.Stoploss) > 0 {
|
||||
buyall = true
|
||||
s.bottomPrice = fixedpoint.Zero
|
||||
}
|
||||
}
|
||||
if buyall {
|
||||
totalQuantity := quoteBalance.Div(lastPrice)
|
||||
order := types.SubmitOrder{
|
||||
Symbol: s.Symbol,
|
||||
Side: types.SideTypeBuy,
|
||||
Type: types.OrderTypeMarket,
|
||||
Quantity: totalQuantity,
|
||||
Market: s.Market,
|
||||
}
|
||||
if s.validateOrder(&order) {
|
||||
if takeProfit {
|
||||
log.Errorf("takeprofit buy at %v, avg %v, l: %v, atrx2: %v", lastPrice, sellPrice, bottomBack, atrx2)
|
||||
} else {
|
||||
log.Errorf("stoploss buy at %v, avg %v, l: %v, atrx2: %v", lastPrice, sellPrice, bottomBack, atrx2)
|
||||
}
|
||||
|
||||
createdOrders, err := orderExecutor.SubmitOrders(ctx, order)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("cannot place order")
|
||||
return
|
||||
}
|
||||
log.Infof("stoploss bought order %v", createdOrders)
|
||||
s.tradeCollector.Process()
|
||||
}
|
||||
}
|
||||
}
|
||||
buyOrderTPSL := func(price fixedpoint.Value) {
|
||||
balances := session.GetAccount().Balances()
|
||||
baseBalance := balances[s.Market.BaseCurrency].Available
|
||||
atrx2 := fixedpoint.NewFromFloat(s.atr.Last() * 2)
|
||||
lastPrice := price
|
||||
var ok bool
|
||||
if s.Environment.IsBackTesting() {
|
||||
lastPrice, ok = session.LastPrice(s.Symbol)
|
||||
if !ok {
|
||||
log.Errorf("cannot get last price")
|
||||
return
|
||||
}
|
||||
}
|
||||
sellall := false
|
||||
if !buyPrice.IsZero() {
|
||||
if s.peakPrice.IsZero() || s.peakPrice.Compare(price) < 0 {
|
||||
s.peakPrice = price
|
||||
}
|
||||
}
|
||||
takeProfit := false
|
||||
peakBack := s.peakPrice
|
||||
if !baseBalance.IsZero() && !buyPrice.IsZero() {
|
||||
|
||||
// TP
|
||||
if !atrx2.IsZero() && s.peakPrice.Sub(atrx2).Compare(lastPrice) >= 0 &&
|
||||
lastPrice.Compare(buyPrice) > 0 {
|
||||
sellall = true
|
||||
s.peakPrice = fixedpoint.Zero
|
||||
takeProfit = true
|
||||
}
|
||||
|
||||
// SL
|
||||
if buyPrice.Sub(lastPrice).Div(buyPrice).Compare(s.Stoploss) > 0 ||
|
||||
(!atrx2.IsZero() && buyPrice.Sub(atrx2).Compare(lastPrice) >= 0) {
|
||||
sellall = true
|
||||
s.peakPrice = fixedpoint.Zero
|
||||
}
|
||||
}
|
||||
|
||||
if sellall {
|
||||
order := types.SubmitOrder{
|
||||
Symbol: s.Symbol,
|
||||
Side: types.SideTypeSell,
|
||||
Type: types.OrderTypeMarket,
|
||||
Market: s.Market,
|
||||
Quantity: baseBalance,
|
||||
}
|
||||
if s.validateOrder(&order) {
|
||||
if takeProfit {
|
||||
log.Errorf("takeprofit sell at %v, avg %v, h: %v, atrx2: %v", lastPrice, buyPrice, peakBack, atrx2)
|
||||
} else {
|
||||
log.Errorf("stoploss sell at %v, avg %v, h: %v, atrx2: %v", lastPrice, buyPrice, peakBack, atrx2)
|
||||
}
|
||||
createdOrders, err := orderExecutor.SubmitOrders(ctx, order)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("cannot place order")
|
||||
return
|
||||
}
|
||||
log.Infof("stoploss sold order %v", createdOrders)
|
||||
s.tradeCollector.Process()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set last price by realtime book ticker update
|
||||
// to trigger TP/SL
|
||||
session.MarketDataStream.OnBookTickerUpdate(func(ticker types.BookTicker) {
|
||||
if s.Environment.IsBackTesting() {
|
||||
return
|
||||
}
|
||||
bestBid := ticker.Buy
|
||||
bestAsk := ticker.Sell
|
||||
var midPrice fixedpoint.Value
|
||||
if s.lock.TryLock() {
|
||||
if !bestAsk.IsZero() && !bestBid.IsZero() {
|
||||
s.midPrice = bestAsk.Add(bestBid).Div(types.Two)
|
||||
} else if !bestAsk.IsZero() {
|
||||
s.midPrice = bestAsk
|
||||
} else {
|
||||
s.midPrice = bestBid
|
||||
}
|
||||
midPrice = s.midPrice
|
||||
s.lock.Unlock()
|
||||
}
|
||||
if !midPrice.IsZero() {
|
||||
buyOrderTPSL(midPrice)
|
||||
sellOrderTPSL(midPrice)
|
||||
}
|
||||
//log.Infof("best bid %v, best ask %v, mid %v", bestBid, bestAsk, midPrice)
|
||||
})
|
||||
|
||||
session.MarketDataStream.OnKLineClosed(func(kline types.KLine) {
|
||||
if kline.Symbol != s.Symbol {
|
||||
return
|
||||
}
|
||||
|
||||
lastPrice, ok := session.LastPrice(s.Symbol)
|
||||
if !ok {
|
||||
log.Errorf("cannot get last price")
|
||||
return
|
||||
var lastPrice fixedpoint.Value
|
||||
var ok bool
|
||||
if s.Environment.IsBackTesting() {
|
||||
lastPrice, ok = session.LastPrice(s.Symbol)
|
||||
if !ok {
|
||||
log.Errorf("cannot get last price")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
s.lock.RLock()
|
||||
lastPrice = s.midPrice
|
||||
s.lock.RUnlock()
|
||||
}
|
||||
|
||||
// cancel non-traded orders
|
||||
|
@ -558,12 +726,10 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
s.tradeCollector.Process()
|
||||
}
|
||||
|
||||
balances := session.GetAccount().Balances()
|
||||
baseBalance := balances[s.Market.BaseCurrency].Available
|
||||
quoteBalance := balances[s.Market.QuoteCurrency].Available
|
||||
atrx2 := fixedpoint.NewFromFloat(s.atr.Last() * 2)
|
||||
log.Infof("Get last price: %v, kline: %v, balance[base]: %v balance[quote]: %v, atrx2: %v",
|
||||
lastPrice, kline, baseBalance, quoteBalance, atrx2)
|
||||
if !s.Environment.IsBackTesting() {
|
||||
log.Infof("Get last price: %v, kline: %v, atrx2: %v", lastPrice, kline, atrx2)
|
||||
}
|
||||
|
||||
// well, only track prices on 1m
|
||||
if kline.Interval == types.Interval1m {
|
||||
|
@ -590,105 +756,11 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
log.Infof("repost order %v", createdOrders)
|
||||
s.tradeCollector.Process()
|
||||
}
|
||||
sellall := false
|
||||
buyall := false
|
||||
if !buyPrice.IsZero() {
|
||||
if s.peakPrice.IsZero() || s.peakPrice.Compare(kline.High) < 0 {
|
||||
s.peakPrice = kline.High
|
||||
}
|
||||
}
|
||||
|
||||
if !sellPrice.IsZero() {
|
||||
if s.bottomPrice.IsZero() || s.bottomPrice.Compare(kline.Low) > 0 {
|
||||
s.bottomPrice = kline.Low
|
||||
}
|
||||
}
|
||||
if s.Environment.IsBackTesting() {
|
||||
buyOrderTPSL(kline.High)
|
||||
sellOrderTPSL(kline.Low)
|
||||
|
||||
takeProfit := false
|
||||
peakBack := s.peakPrice
|
||||
bottomBack := s.bottomPrice
|
||||
if !baseBalance.IsZero() && !buyPrice.IsZero() {
|
||||
|
||||
// TP
|
||||
if !atrx2.IsZero() && s.peakPrice.Sub(atrx2).Compare(lastPrice) >= 0 &&
|
||||
lastPrice.Compare(buyPrice) > 0 {
|
||||
sellall = true
|
||||
s.peakPrice = fixedpoint.Zero
|
||||
takeProfit = true
|
||||
}
|
||||
|
||||
// SL
|
||||
if buyPrice.Sub(lastPrice).Div(buyPrice).Compare(s.Stoploss) > 0 ||
|
||||
(!atrx2.IsZero() && buyPrice.Sub(atrx2).Compare(lastPrice) >= 0) {
|
||||
sellall = true
|
||||
s.peakPrice = fixedpoint.Zero
|
||||
}
|
||||
}
|
||||
|
||||
if !quoteBalance.IsZero() && !sellPrice.IsZero() && !s.DisableShortStop {
|
||||
// TP
|
||||
if !atrx2.IsZero() && s.bottomPrice.Add(atrx2).Compare(lastPrice) >= 0 &&
|
||||
lastPrice.Compare(sellPrice) < 0 {
|
||||
buyall = true
|
||||
s.bottomPrice = fixedpoint.Zero
|
||||
takeProfit = true
|
||||
}
|
||||
|
||||
// SL
|
||||
if (!atrx2.IsZero() && sellPrice.Add(atrx2).Compare(lastPrice) <= 0) ||
|
||||
lastPrice.Sub(sellPrice).Div(sellPrice).Compare(s.Stoploss) > 0 {
|
||||
buyall = true
|
||||
s.bottomPrice = fixedpoint.Zero
|
||||
}
|
||||
}
|
||||
if sellall {
|
||||
order := types.SubmitOrder{
|
||||
Symbol: s.Symbol,
|
||||
Side: types.SideTypeSell,
|
||||
Type: types.OrderTypeMarket,
|
||||
Market: s.Market,
|
||||
Quantity: baseBalance,
|
||||
}
|
||||
if s.validateOrder(&order) {
|
||||
if takeProfit {
|
||||
log.Errorf("takeprofit sell at %v, avg %v, h: %v, atrx2: %v, timestamp: %s", lastPrice, buyPrice, peakBack, atrx2, kline.StartTime)
|
||||
} else {
|
||||
log.Errorf("stoploss sell at %v, avg %v, h: %v, atrx2: %v, timestamp %s", lastPrice, buyPrice, peakBack, atrx2, kline.StartTime)
|
||||
}
|
||||
createdOrders, err := orderExecutor.SubmitOrders(ctx, order)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("cannot place order")
|
||||
return
|
||||
}
|
||||
log.Infof("stoploss sold order %v", createdOrders)
|
||||
s.tradeCollector.Process()
|
||||
}
|
||||
}
|
||||
|
||||
if buyall {
|
||||
totalQuantity := quoteBalance.Div(lastPrice)
|
||||
order := types.SubmitOrder{
|
||||
Symbol: kline.Symbol,
|
||||
Side: types.SideTypeBuy,
|
||||
Type: types.OrderTypeMarket,
|
||||
Quantity: totalQuantity,
|
||||
Market: s.Market,
|
||||
}
|
||||
if s.validateOrder(&order) {
|
||||
if takeProfit {
|
||||
log.Errorf("takeprofit buy at %v, avg %v, l: %v, atrx2: %v, timestamp: %s", lastPrice, sellPrice, bottomBack, atrx2, kline.StartTime)
|
||||
} else {
|
||||
log.Errorf("stoploss buy at %v, avg %v, l: %v, atrx2: %v, timestamp: %s", lastPrice, sellPrice, bottomBack, atrx2, kline.StartTime)
|
||||
}
|
||||
|
||||
createdOrders, err := orderExecutor.SubmitOrders(ctx, order)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("cannot place order")
|
||||
return
|
||||
}
|
||||
log.Infof("stoploss bought order %v", createdOrders)
|
||||
s.tradeCollector.Process()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -718,10 +790,12 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
// kline downthrough ma5, ma50 trend down, and ewo < threshold
|
||||
IsBear := !bull && breakDown && s.ewo.Last() <= mean-2*std
|
||||
|
||||
log.Infof("IsBull: %v, bull: %v, longSignal[1]: %v, shortSignal: %v",
|
||||
IsBull, bull, longSignal.Index(1), shortSignal.Last())
|
||||
log.Infof("IsBear: %v, bear: %v, shortSignal[1]: %v, longSignal: %v",
|
||||
IsBear, !bull, shortSignal.Index(1), longSignal.Last())
|
||||
if !s.Environment.IsBackTesting() {
|
||||
log.Infof("IsBull: %v, bull: %v, longSignal[1]: %v, shortSignal: %v",
|
||||
IsBull, bull, longSignal.Index(1), shortSignal.Last())
|
||||
log.Infof("IsBear: %v, bear: %v, shortSignal[1]: %v, longSignal: %v",
|
||||
IsBear, !bull, shortSignal.Index(1), longSignal.Last())
|
||||
}
|
||||
|
||||
var orders []types.SubmitOrder
|
||||
var price fixedpoint.Value
|
||||
|
|
Loading…
Reference in New Issue
Block a user