mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
Merge pull request #1688 from c9s/c9s/xdepthmaker/separate-hedge-symbol
FEATURE: [xdepthmaker] separate hedge symbol
This commit is contained in:
commit
eb6e5cda39
|
@ -12,6 +12,10 @@ import (
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type TradeConverter interface {
|
||||||
|
Convert(trade types.Trade) (types.Trade, error)
|
||||||
|
}
|
||||||
|
|
||||||
//go:generate callbackgen -type TradeCollector
|
//go:generate callbackgen -type TradeCollector
|
||||||
type TradeCollector struct {
|
type TradeCollector struct {
|
||||||
Symbol string
|
Symbol string
|
||||||
|
@ -25,6 +29,8 @@ type TradeCollector struct {
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
|
||||||
|
tradeConverters []TradeConverter
|
||||||
|
|
||||||
recoverCallbacks []func(trade types.Trade)
|
recoverCallbacks []func(trade types.Trade)
|
||||||
|
|
||||||
tradeCallbacks []func(trade types.Trade, profit, netProfit fixedpoint.Value)
|
tradeCallbacks []func(trade types.Trade, profit, netProfit fixedpoint.Value)
|
||||||
|
@ -49,6 +55,28 @@ func NewTradeCollector(symbol string, position *types.Position, orderStore *Orde
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *TradeCollector) AddTradeConverter(converter TradeConverter) {
|
||||||
|
c.tradeConverters = append(c.tradeConverters, converter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TradeCollector) convertTrade(trade types.Trade) types.Trade {
|
||||||
|
if len(c.tradeConverters) == 0 {
|
||||||
|
return trade
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, converter := range c.tradeConverters {
|
||||||
|
convTrade, err := converter.Convert(trade)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Errorf("trade %+v converter error, trade: %s", converter, trade.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
trade = convTrade
|
||||||
|
}
|
||||||
|
|
||||||
|
return trade
|
||||||
|
}
|
||||||
|
|
||||||
// OrderStore returns the order store used by the trade collector
|
// OrderStore returns the order store used by the trade collector
|
||||||
func (c *TradeCollector) OrderStore() *OrderStore {
|
func (c *TradeCollector) OrderStore() *OrderStore {
|
||||||
return c.orderStore
|
return c.orderStore
|
||||||
|
@ -116,6 +144,8 @@ func (c *TradeCollector) Recover(
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TradeCollector) RecoverTrade(td types.Trade) bool {
|
func (c *TradeCollector) RecoverTrade(td types.Trade) bool {
|
||||||
|
td = c.convertTrade(td)
|
||||||
|
|
||||||
logrus.Debugf("checking trade: %s", td.String())
|
logrus.Debugf("checking trade: %s", td.String())
|
||||||
if c.processTrade(td) {
|
if c.processTrade(td) {
|
||||||
logrus.Infof("recovered trade: %s", td.String())
|
logrus.Infof("recovered trade: %s", td.String())
|
||||||
|
@ -230,7 +260,7 @@ func (c *TradeCollector) processTrade(trade types.Trade) bool {
|
||||||
// return true when the given trade is added
|
// return true when the given trade is added
|
||||||
// return false when the given trade is not added
|
// return false when the given trade is not added
|
||||||
func (c *TradeCollector) ProcessTrade(trade types.Trade) bool {
|
func (c *TradeCollector) ProcessTrade(trade types.Trade) bool {
|
||||||
return c.processTrade(trade)
|
return c.processTrade(c.convertTrade(trade))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run is a goroutine executed in the background
|
// Run is a goroutine executed in the background
|
||||||
|
@ -249,7 +279,8 @@ func (c *TradeCollector) Run(ctx context.Context) {
|
||||||
c.Process()
|
c.Process()
|
||||||
|
|
||||||
case trade := <-c.tradeC:
|
case trade := <-c.tradeC:
|
||||||
c.processTrade(trade)
|
c.processTrade(c.convertTrade(trade))
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -361,7 +361,7 @@ func convertWithdrawStatusV3(status max.WithdrawStatus) types.WithdrawStatus {
|
||||||
return types.WithdrawStatus(status)
|
return types.WithdrawStatus(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertWithdrawStatus(state max.WithdrawState) types.WithdrawStatus {
|
func convertWithdrawStatusV2(state max.WithdrawState) types.WithdrawStatus {
|
||||||
switch state {
|
switch state {
|
||||||
|
|
||||||
case max.WithdrawStateSent, max.WithdrawStateSubmitting, max.WithdrawStatePending, "accepted", "approved":
|
case max.WithdrawStateSent, max.WithdrawStateSubmitting, max.WithdrawStatePending, "accepted", "approved":
|
||||||
|
|
|
@ -865,8 +865,7 @@ func (e *Exchange) QueryWithdrawHistory(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// we can convert this later
|
status := convertWithdrawStatusV2(d.State)
|
||||||
status := convertWithdrawStatusV3(d.Status)
|
|
||||||
|
|
||||||
txIDs[d.TxID] = struct{}{}
|
txIDs[d.TxID] = struct{}{}
|
||||||
withdraw := types.Withdraw{
|
withdraw := types.Withdraw{
|
||||||
|
|
|
@ -194,7 +194,7 @@ type Withdraw struct {
|
||||||
// "sygna_verifying"
|
// "sygna_verifying"
|
||||||
State WithdrawState `json:"state"`
|
State WithdrawState `json:"state"`
|
||||||
|
|
||||||
Status WithdrawStatus `json:"status,omitempty"`
|
// Status WithdrawStatus `json:"status,omitempty"`
|
||||||
|
|
||||||
CreatedAt types.MillisecondTimestamp `json:"created_at"`
|
CreatedAt types.MillisecondTimestamp `json:"created_at"`
|
||||||
UpdatedAt types.MillisecondTimestamp `json:"updated_at"`
|
UpdatedAt types.MillisecondTimestamp `json:"updated_at"`
|
||||||
|
|
|
@ -56,7 +56,8 @@ type CrossExchangeMarketMakingStrategy struct {
|
||||||
func (s *CrossExchangeMarketMakingStrategy) Initialize(
|
func (s *CrossExchangeMarketMakingStrategy) Initialize(
|
||||||
ctx context.Context, environ *bbgo.Environment,
|
ctx context.Context, environ *bbgo.Environment,
|
||||||
makerSession, hedgeSession *bbgo.ExchangeSession,
|
makerSession, hedgeSession *bbgo.ExchangeSession,
|
||||||
symbol, strategyID, instanceID string,
|
symbol, hedgeSymbol,
|
||||||
|
strategyID, instanceID string,
|
||||||
) error {
|
) error {
|
||||||
s.parent = ctx
|
s.parent = ctx
|
||||||
s.ctx, s.cancel = context.WithCancel(ctx)
|
s.ctx, s.cancel = context.WithCancel(ctx)
|
||||||
|
@ -67,9 +68,9 @@ func (s *CrossExchangeMarketMakingStrategy) Initialize(
|
||||||
s.hedgeSession = hedgeSession
|
s.hedgeSession = hedgeSession
|
||||||
|
|
||||||
var ok bool
|
var ok bool
|
||||||
s.hedgeMarket, ok = s.hedgeSession.Market(symbol)
|
s.hedgeMarket, ok = s.hedgeSession.Market(hedgeSymbol)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("source session market %s is not defined", symbol)
|
return fmt.Errorf("hedge session market %s is not defined", hedgeSymbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.makerMarket, ok = s.makerSession.Market(symbol)
|
s.makerMarket, ok = s.makerSession.Market(symbol)
|
||||||
|
@ -150,13 +151,18 @@ type Strategy struct {
|
||||||
|
|
||||||
Symbol string `json:"symbol"`
|
Symbol string `json:"symbol"`
|
||||||
|
|
||||||
// HedgeExchange session name
|
// HedgeSymbol is the symbol for the hedge exchange
|
||||||
HedgeExchange string `json:"hedgeExchange"`
|
// symbol could be different from the maker exchange
|
||||||
|
HedgeSymbol string `json:"hedgeSymbol"`
|
||||||
|
|
||||||
// MakerExchange session name
|
// MakerExchange session name
|
||||||
MakerExchange string `json:"makerExchange"`
|
MakerExchange string `json:"makerExchange"`
|
||||||
|
|
||||||
|
// HedgeExchange session name
|
||||||
|
HedgeExchange string `json:"hedgeExchange"`
|
||||||
|
|
||||||
UpdateInterval types.Duration `json:"updateInterval"`
|
UpdateInterval types.Duration `json:"updateInterval"`
|
||||||
|
|
||||||
HedgeInterval types.Duration `json:"hedgeInterval"`
|
HedgeInterval types.Duration `json:"hedgeInterval"`
|
||||||
|
|
||||||
FullReplenishInterval types.Duration `json:"fullReplenishInterval"`
|
FullReplenishInterval types.Duration `json:"fullReplenishInterval"`
|
||||||
|
@ -239,12 +245,12 @@ func (s *Strategy) CrossSubscribe(sessions map[string]*bbgo.ExchangeSession) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hedgeSession.Subscribe(types.BookChannel, s.Symbol, types.SubscribeOptions{
|
hedgeSession.Subscribe(types.BookChannel, s.HedgeSymbol, types.SubscribeOptions{
|
||||||
Depth: types.DepthLevelMedium,
|
Depth: types.DepthLevelMedium,
|
||||||
Speed: types.SpeedLow,
|
Speed: types.SpeedLow,
|
||||||
})
|
})
|
||||||
|
|
||||||
hedgeSession.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: "1m"})
|
hedgeSession.Subscribe(types.KLineChannel, s.HedgeSymbol, types.SubscribeOptions{Interval: "1m"})
|
||||||
makerSession.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: "1m"})
|
makerSession.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: "1m"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,6 +287,10 @@ func (s *Strategy) Defaults() error {
|
||||||
s.HedgeInterval = types.Duration(3 * time.Second)
|
s.HedgeInterval = types.Duration(3 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.HedgeSymbol == "" {
|
||||||
|
s.HedgeSymbol = s.Symbol
|
||||||
|
}
|
||||||
|
|
||||||
if s.NumLayers == 0 {
|
if s.NumLayers == 0 {
|
||||||
s.NumLayers = 1
|
s.NumLayers = 1
|
||||||
}
|
}
|
||||||
|
@ -358,13 +368,13 @@ func (s *Strategy) CrossRun(
|
||||||
|
|
||||||
if err := s.CrossExchangeMarketMakingStrategy.Initialize(ctx,
|
if err := s.CrossExchangeMarketMakingStrategy.Initialize(ctx,
|
||||||
s.Environment,
|
s.Environment,
|
||||||
makerSession,
|
makerSession, hedgeSession,
|
||||||
hedgeSession,
|
s.Symbol, s.HedgeSymbol,
|
||||||
s.Symbol, ID, s.InstanceID()); err != nil {
|
ID, s.InstanceID()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.pricingBook = types.NewStreamBook(s.Symbol)
|
s.pricingBook = types.NewStreamBook(s.HedgeSymbol)
|
||||||
s.pricingBook.BindStream(s.hedgeSession.MarketDataStream)
|
s.pricingBook.BindStream(s.hedgeSession.MarketDataStream)
|
||||||
|
|
||||||
s.stopC = make(chan struct{})
|
s.stopC = make(chan struct{})
|
||||||
|
@ -488,7 +498,7 @@ func (s *Strategy) CrossRun(
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.HedgeOrderExecutor.GracefulCancel(ctx); err != nil {
|
if err := s.HedgeOrderExecutor.GracefulCancel(ctx); err != nil {
|
||||||
log.WithError(err).Errorf("graceful cancel %s order error", s.Symbol)
|
log.WithError(err).Errorf("graceful cancel %s order error", s.HedgeSymbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
bbgo.Sync(ctx, s)
|
bbgo.Sync(ctx, s)
|
||||||
|
@ -576,12 +586,12 @@ func (s *Strategy) Hedge(ctx context.Context, pos fixedpoint.Value) {
|
||||||
s.hedgeErrorRateReservation = nil
|
s.hedgeErrorRateReservation = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("submitting %s hedge order %s %v", s.Symbol, side.String(), quantity)
|
log.Infof("submitting %s hedge order %s %v", s.HedgeSymbol, side.String(), quantity)
|
||||||
bbgo.Notify("Submitting %s hedge order %s %v", s.Symbol, side.String(), quantity)
|
bbgo.Notify("Submitting %s hedge order %s %v", s.HedgeSymbol, side.String(), quantity)
|
||||||
|
|
||||||
_, err := s.HedgeOrderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
_, err := s.HedgeOrderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
||||||
Market: s.hedgeMarket,
|
Market: s.hedgeMarket,
|
||||||
Symbol: s.Symbol,
|
Symbol: s.hedgeMarket.Symbol,
|
||||||
Type: types.OrderTypeMarket,
|
Type: types.OrderTypeMarket,
|
||||||
Side: side,
|
Side: side,
|
||||||
Quantity: quantity,
|
Quantity: quantity,
|
||||||
|
@ -627,7 +637,7 @@ func (s *Strategy) runTradeRecover(ctx context.Context) {
|
||||||
|
|
||||||
startTime := time.Now().Add(-tradeScanInterval).Add(-tradeScanOverlapBufferPeriod)
|
startTime := time.Now().Add(-tradeScanInterval).Add(-tradeScanOverlapBufferPeriod)
|
||||||
|
|
||||||
if err := s.HedgeOrderExecutor.TradeCollector().Recover(ctx, s.hedgeSession.Exchange.(types.ExchangeTradeHistoryService), s.Symbol, startTime); err != nil {
|
if err := s.HedgeOrderExecutor.TradeCollector().Recover(ctx, s.hedgeSession.Exchange.(types.ExchangeTradeHistoryService), s.HedgeSymbol, startTime); err != nil {
|
||||||
log.WithError(err).Errorf("query trades error")
|
log.WithError(err).Errorf("query trades error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,7 +649,9 @@ func (s *Strategy) runTradeRecover(ctx context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Strategy) generateMakerOrders(
|
func (s *Strategy) generateMakerOrders(
|
||||||
pricingBook *types.StreamOrderBook, maxLayer int, availableBase fixedpoint.Value, availableQuote fixedpoint.Value,
|
pricingBook *types.StreamOrderBook,
|
||||||
|
maxLayer int,
|
||||||
|
availableBase, availableQuote fixedpoint.Value,
|
||||||
) ([]types.SubmitOrder, error) {
|
) ([]types.SubmitOrder, error) {
|
||||||
_, _, hasPrice := pricingBook.BestBidAndAsk()
|
_, _, hasPrice := pricingBook.BestBidAndAsk()
|
||||||
if !hasPrice {
|
if !hasPrice {
|
||||||
|
@ -776,7 +788,7 @@ func (s *Strategy) generateMakerOrders(
|
||||||
}
|
}
|
||||||
|
|
||||||
submitOrders = append(submitOrders, types.SubmitOrder{
|
submitOrders = append(submitOrders, types.SubmitOrder{
|
||||||
Symbol: s.Symbol,
|
Symbol: s.makerMarket.Symbol,
|
||||||
Type: types.OrderTypeLimitMaker,
|
Type: types.OrderTypeLimitMaker,
|
||||||
Market: s.makerMarket,
|
Market: s.makerMarket,
|
||||||
Side: side,
|
Side: side,
|
||||||
|
@ -829,7 +841,7 @@ func (s *Strategy) updateQuote(ctx context.Context, maxLayer int) {
|
||||||
|
|
||||||
bestBidPrice := bestBid.Price
|
bestBidPrice := bestBid.Price
|
||||||
bestAskPrice := bestAsk.Price
|
bestAskPrice := bestAsk.Price
|
||||||
log.Infof("%s book ticker: best ask / best bid = %v / %v", s.Symbol, bestAskPrice, bestBidPrice)
|
log.Infof("%s book ticker: best ask / best bid = %v / %v", s.HedgeSymbol, bestAskPrice, bestBidPrice)
|
||||||
|
|
||||||
s.lastPrice = bestBidPrice.Add(bestAskPrice).Div(Two)
|
s.lastPrice = bestBidPrice.Add(bestAskPrice).Div(Two)
|
||||||
|
|
||||||
|
@ -898,11 +910,7 @@ func (s *Strategy) cleanUpOpenOrders(ctx context.Context, session *bbgo.Exchange
|
||||||
log.Infof("found existing open orders:")
|
log.Infof("found existing open orders:")
|
||||||
types.OrderSlice(openOrders).Print()
|
types.OrderSlice(openOrders).Print()
|
||||||
|
|
||||||
if err := session.Exchange.CancelOrders(ctx, openOrders...); err != nil {
|
return session.Exchange.CancelOrders(ctx, openOrders...)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectSessions2(
|
func selectSessions2(
|
||||||
|
|
Loading…
Reference in New Issue
Block a user