129 lines
3.1 KiB
Go
129 lines
3.1 KiB
Go
|
package ccinr
|
|||
|
|
|||
|
import (
|
|||
|
"context"
|
|||
|
"fmt"
|
|||
|
"git.qtrade.icu/lychiyu/qbtrade/pkg/exchange/binance"
|
|||
|
"git.qtrade.icu/lychiyu/qbtrade/pkg/qbtrade"
|
|||
|
"git.qtrade.icu/lychiyu/qbtrade/pkg/strategy/common"
|
|||
|
"git.qtrade.icu/lychiyu/qbtrade/pkg/types"
|
|||
|
log "github.com/sirupsen/logrus"
|
|||
|
"sync"
|
|||
|
)
|
|||
|
|
|||
|
const ID = "ccinr"
|
|||
|
|
|||
|
func init() {
|
|||
|
qbtrade.RegisterStrategy(ID, &Strategy{})
|
|||
|
}
|
|||
|
|
|||
|
type Strategy struct {
|
|||
|
*common.Strategy
|
|||
|
|
|||
|
Symbol string `json:"symbol"`
|
|||
|
Interval types.Interval `json:"interval"`
|
|||
|
|
|||
|
ExchangeSession *qbtrade.ExchangeSession
|
|||
|
}
|
|||
|
|
|||
|
func (s *Strategy) ID() string {
|
|||
|
return ID
|
|||
|
}
|
|||
|
|
|||
|
func (s *Strategy) Subscribe(session *qbtrade.ExchangeSession) {
|
|||
|
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.Interval})
|
|||
|
if !qbtrade.IsBackTesting {
|
|||
|
session.Subscribe(types.MarketTradeChannel, s.Symbol, types.SubscribeOptions{})
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (s *Strategy) Initialize() error {
|
|||
|
if s.Strategy == nil {
|
|||
|
s.Strategy = &common.Strategy{}
|
|||
|
}
|
|||
|
|
|||
|
return nil
|
|||
|
}
|
|||
|
|
|||
|
func (s *Strategy) Run(ctx context.Context, orderExecutor qbtrade.OrderExecutor, session *qbtrade.ExchangeSession) error {
|
|||
|
s.ExchangeSession = session
|
|||
|
|
|||
|
nr := session.Indicators(s.Symbol).NR(s.Interval, 4, true)
|
|||
|
nr.OnUpdate(func(v float64) {
|
|||
|
msg := fmt.Sprintf("交易信号:时间: %s, 最高价:%f,最低价:%f", nr.NrKLine.GetStartTime(), nr.NrKLine.High.Float64(), nr.NrKLine.Low.Float64())
|
|||
|
qbtrade.Notify(msg)
|
|||
|
fmt.Println(v)
|
|||
|
})
|
|||
|
|
|||
|
//session.MarketDataStream.OnKLineClosed(func(k types.KLine) {
|
|||
|
// if k.Symbol != s.Symbol || k.Interval != s.Interval {
|
|||
|
// return
|
|||
|
// }
|
|||
|
// fmt.Println(k)
|
|||
|
//})
|
|||
|
//
|
|||
|
//session.MarketDataStream.OnMarketTrade(func(trade types.Trade) {
|
|||
|
// // handle market trade event here
|
|||
|
// fmt.Println(trade)
|
|||
|
//})
|
|||
|
|
|||
|
b, ok := s.getBalance(ctx)
|
|||
|
fmt.Println(b, ok)
|
|||
|
session.UserDataStream.OnOrderUpdate(func(order types.Order) {
|
|||
|
if order.Status == types.OrderStatusFilled {
|
|||
|
log.Infof("your order is filled: %+v", order)
|
|||
|
}
|
|||
|
})
|
|||
|
|
|||
|
session.UserDataStream.OnTradeUpdate(func(trade types.Trade) {
|
|||
|
log.Infof("trade price %f, fee %f %s", trade.Price.Float64(), trade.Fee.Float64(), trade.FeeCurrency)
|
|||
|
})
|
|||
|
|
|||
|
qbtrade.OnShutdown(ctx, func(ctx context.Context, wg *sync.WaitGroup) {
|
|||
|
defer wg.Done()
|
|||
|
|
|||
|
if err := s.Strategy.OrderExecutor.GracefulCancel(ctx); err != nil {
|
|||
|
log.WithError(err).Error("unable to cancel open orders...")
|
|||
|
}
|
|||
|
|
|||
|
qbtrade.Sync(ctx, s)
|
|||
|
})
|
|||
|
|
|||
|
return nil
|
|||
|
}
|
|||
|
|
|||
|
func (s *Strategy) handleBalanceUpdate(balances types.BalanceMap) {
|
|||
|
for _, b := range balances {
|
|||
|
if b.Available.IsZero() && b.Borrowed.IsZero() {
|
|||
|
continue
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (s *Strategy) handleBinanceBalanceUpdateEvent(event *binance.BalanceUpdateEvent) {
|
|||
|
qbtrade.Notify(event)
|
|||
|
|
|||
|
account := s.ExchangeSession.GetAccount()
|
|||
|
|
|||
|
fmt.Println(account)
|
|||
|
delta := event.Delta
|
|||
|
|
|||
|
// ignore outflow
|
|||
|
if delta.Sign() < 0 {
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// getBalance 获取账户余额
|
|||
|
func (s *Strategy) getBalance(ctx context.Context) (balance types.Balance, ok bool) {
|
|||
|
// 更新并获取account信息
|
|||
|
account, err := s.ExchangeSession.UpdateAccount(ctx)
|
|||
|
if err != nil {
|
|||
|
log.WithError(err).Error("unable to update account")
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// 获取balance信息
|
|||
|
return account.Balance("USDT")
|
|||
|
}
|