mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 14:55:16 +00:00
feature: post orders for ewo
This commit is contained in:
parent
42a3737f2e
commit
0fe14c5fe5
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/bbgo"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
@ -19,11 +20,12 @@ func init() {
|
|||
}
|
||||
|
||||
type Strategy struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Interval types.Interval `json:"interval"`
|
||||
Threshold float64 `json:"threshold"` // strength threshold
|
||||
UseEma bool `json:"useEma"` // use exponential ma or simple ma
|
||||
SignalWindow int `json:"sigWin"` // signal window
|
||||
Symbol string `json:"symbol"`
|
||||
Interval types.Interval `json:"interval"`
|
||||
Threshold float64 `json:"threshold"` // strength threshold
|
||||
UseEma bool `json:"useEma"` // use exponential ma or simple ma
|
||||
SignalWindow int `json:"sigWin"` // signal window
|
||||
StopLoss fixedpoint.Value `json:"stoploss"` // stop price = latest price * (1 - stoploss)
|
||||
}
|
||||
|
||||
func (s *Strategy) ID() string {
|
||||
|
@ -41,21 +43,27 @@ type EwoSignal interface {
|
|||
}
|
||||
|
||||
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
|
||||
log.Infof("stoploss: %v", s.StopLoss)
|
||||
indicatorSet, ok := session.StandardIndicatorSet(s.Symbol)
|
||||
if !ok {
|
||||
log.Errorf("cannot get indicatorSet of %s", s.Symbol)
|
||||
return nil
|
||||
}
|
||||
store, ok := session.MarketDataStore(s.Symbol)
|
||||
//store, ok := session.MarketDataStore(s.Symbol)
|
||||
//if !ok {
|
||||
// log.Errorf("cannot get marketdatastore of %s", s.Symbol)
|
||||
// return nil
|
||||
//}
|
||||
market, ok := session.Market(s.Symbol)
|
||||
if !ok {
|
||||
log.Errorf("cannot get marketdatastore of %s", s.Symbol)
|
||||
log.Errorf("fetch market fail %s", s.Symbol)
|
||||
return nil
|
||||
}
|
||||
window, ok := store.KLinesOfInterval(s.Interval)
|
||||
if !ok {
|
||||
log.Errorf("cannot get klinewindow of %s", s.Interval)
|
||||
}
|
||||
mid := types.Div(types.Add(window.Open(), window.Close()), 2)
|
||||
//window, ok := store.KLinesOfInterval(s.Interval)
|
||||
//if !ok {
|
||||
// log.Errorf("cannot get klinewindow of %s", s.Interval)
|
||||
//}
|
||||
//mid := types.Div(types.Add(window.Open(), window.Close()), 2)
|
||||
var ma5, ma34, ewo types.Series
|
||||
if s.UseEma {
|
||||
ma5 = indicatorSet.EWMA(types.IntervalWindow{s.Interval, 5})
|
||||
|
@ -71,18 +79,25 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
} else {
|
||||
ewoSignal = &indicator.SMA{IntervalWindow: types.IntervalWindow{s.Interval, s.SignalWindow}}
|
||||
}
|
||||
entryPrice := fixedpoint.Zero
|
||||
stopPrice := fixedpoint.Zero
|
||||
inTrade := false
|
||||
tradeDirectionLong := true
|
||||
session.MarketDataStream.OnKLineClosed(func(kline types.KLine) {
|
||||
if kline.Symbol != s.Symbol || kline.Interval != s.Interval {
|
||||
if kline.Symbol != s.Symbol {
|
||||
return
|
||||
}
|
||||
if ewoSignal.Length() == 0 {
|
||||
// lazy init
|
||||
ewoVals := types.ToReverseArray(ewo)
|
||||
for _, ewoValue := range ewoVals {
|
||||
ewoSignal.Update(ewoValue)
|
||||
|
||||
if kline.Interval == s.Interval {
|
||||
if ewoSignal.Length() == 0 {
|
||||
// lazy init
|
||||
ewoVals := types.ToReverseArray(ewo)
|
||||
for _, ewoValue := range ewoVals {
|
||||
ewoSignal.Update(ewoValue)
|
||||
}
|
||||
} else {
|
||||
ewoSignal.Update(ewo.Last())
|
||||
}
|
||||
} else {
|
||||
ewoSignal.Update(ewo.Last())
|
||||
}
|
||||
|
||||
lastPrice, ok := session.LastPrice(s.Symbol)
|
||||
|
@ -90,29 +105,179 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
return
|
||||
}
|
||||
|
||||
// stoploss
|
||||
if inTrade {
|
||||
if tradeDirectionLong && kline.Low.Compare(stopPrice) <= 0 {
|
||||
balances := session.Account.Balances()
|
||||
baseBalance := balances[market.BaseCurrency].Available
|
||||
baseAmount := baseBalance.Mul(lastPrice)
|
||||
if baseBalance.Sign() <= 0 ||
|
||||
baseBalance.Compare(market.MinQuantity) < 0 ||
|
||||
baseAmount.Compare(market.MinNotional) < 0 {
|
||||
//log.Infof("base balance %v is not enough. stop generating sell orders", baseBalance)
|
||||
return
|
||||
}
|
||||
_, err := orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
||||
Symbol: kline.Symbol,
|
||||
Side: types.SideTypeSell,
|
||||
Type: types.OrderTypeMarket,
|
||||
Quantity: baseBalance,
|
||||
Market: market,
|
||||
TimeInForce: types.TimeInForceGTC,
|
||||
})
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("cannot place order for stoploss")
|
||||
return
|
||||
}
|
||||
log.Warnf("StopLoss Long at %v", lastPrice)
|
||||
inTrade = false
|
||||
entryPrice = fixedpoint.Zero
|
||||
stopPrice = fixedpoint.Zero
|
||||
return
|
||||
} else if !tradeDirectionLong && kline.High.Compare(stopPrice) >= 0 {
|
||||
quoteBalance, ok := session.Account.Balance(market.QuoteCurrency)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
quantityAmount := quoteBalance.Available
|
||||
totalQuantity := quantityAmount.Div(lastPrice)
|
||||
if quantityAmount.Sign() <= 0 ||
|
||||
quantityAmount.Compare(market.MinNotional) < 0 ||
|
||||
totalQuantity.Compare(market.MinQuantity) < 0 {
|
||||
//log.Infof("quote balance %v is not enough. stop generating buy orders", quoteBalance)
|
||||
return
|
||||
}
|
||||
_, err := orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
||||
Symbol: kline.Symbol,
|
||||
Side: types.SideTypeBuy,
|
||||
Type: types.OrderTypeMarket,
|
||||
Quantity: totalQuantity,
|
||||
Market: market,
|
||||
TimeInForce: types.TimeInForceGTC,
|
||||
})
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("cannot place order for stoploss")
|
||||
return
|
||||
}
|
||||
log.Warnf("StopLoss Short at %v", lastPrice)
|
||||
inTrade = false
|
||||
entryPrice = fixedpoint.Zero
|
||||
stopPrice = fixedpoint.Zero
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if kline.Interval != s.Interval {
|
||||
return
|
||||
}
|
||||
|
||||
longSignal := types.CrossOver(ewo, ewoSignal)
|
||||
shortSignal := types.CrossUnder(ewo, ewoSignal)
|
||||
nextVal := types.Predict(mid, 5, 1)
|
||||
IsBull := mid.Last() < nextVal
|
||||
log.Warnf("mid %v, last %v, next %v", kline.Mid(), mid.Last(), nextVal)
|
||||
IsBull := kline.Close.Compare(kline.Open) > 0
|
||||
//nextVal := types.Predict(mid, 3, 1)
|
||||
//IsBull := lastPrice.Float64() < nextVal
|
||||
//log.Warnf("mid %v, last %v, next %v", kline.Mid(), mid.Last(), nextVal)
|
||||
//log.Warnf("long %v %v, short %v %v", longSignal.Index(1), longSignal.Last(), shortSignal.Index(1), shortSignal.Last())
|
||||
//log.Infof("(%f, %f, %f) (%f, %f, %f)", ewo.Last(), ewo.Index(1), ewo.Index(2), ewoSignal.Last(), ewoSignal.Index(1), ewoSignal.Index(2))
|
||||
//log.Infof("long %v %v %v", longSignal.Last(), longSignal.Index(1), ewo.Last() >= ewoSignal.Last() && ewoSignal.Index(1) >= ewo.Index(1))
|
||||
//log.Infof("short %v %v", shortSignal.Last(), shortSignal.Index(1))
|
||||
|
||||
if longSignal.Last() && IsBull {
|
||||
var orders []types.SubmitOrder
|
||||
if longSignal.Index(1) && !shortSignal.Last() && IsBull {
|
||||
quoteBalance, ok := session.Account.Balance(market.QuoteCurrency)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
quantityAmount := quoteBalance.Available
|
||||
totalQuantity := quantityAmount.Div(lastPrice)
|
||||
if quantityAmount.Sign() <= 0 ||
|
||||
quantityAmount.Compare(market.MinNotional) < 0 ||
|
||||
totalQuantity.Compare(market.MinQuantity) < 0 {
|
||||
log.Infof("quote balance %v is not enough. stop generating buy orders", quoteBalance)
|
||||
return
|
||||
}
|
||||
if ewo.Last() < -s.Threshold {
|
||||
// strong long
|
||||
log.Infof("strong long at %v, timestamp: %s", lastPrice, kline.StartTime)
|
||||
} else {
|
||||
log.Infof("long at %v, timestamp: %s", lastPrice, kline.StartTime)
|
||||
|
||||
orders = append(orders, types.SubmitOrder{
|
||||
Symbol: kline.Symbol,
|
||||
Side: types.SideTypeBuy,
|
||||
Type: types.OrderTypeLimit,
|
||||
Price: lastPrice,
|
||||
Quantity: totalQuantity,
|
||||
Market: market,
|
||||
TimeInForce: types.TimeInForceGTC,
|
||||
})
|
||||
} else if ewo.Last() < 0 {
|
||||
log.Infof("long at %v, amount: %v, timestamp: %s", lastPrice, lastPrice.Mul(totalQuantity), kline.StartTime)
|
||||
// Long
|
||||
|
||||
// TODO: smaller quantity?
|
||||
|
||||
orders = append(orders, types.SubmitOrder{
|
||||
Symbol: s.Symbol,
|
||||
Side: types.SideTypeBuy,
|
||||
Type: types.OrderTypeLimit,
|
||||
Price: lastPrice,
|
||||
Quantity: totalQuantity,
|
||||
Market: market,
|
||||
TimeInForce: types.TimeInForceGTC,
|
||||
})
|
||||
}
|
||||
} else if shortSignal.Index(1) && !longSignal.Last() && !IsBull {
|
||||
balances := session.Account.Balances()
|
||||
baseBalance := balances[market.BaseCurrency].Available
|
||||
baseAmount := baseBalance.Mul(lastPrice)
|
||||
if baseBalance.Sign() <= 0 ||
|
||||
baseBalance.Compare(market.MinQuantity) < 0 ||
|
||||
baseAmount.Compare(market.MinNotional) < 0 {
|
||||
log.Infof("base balance %v is not enough. stop generating sell orders", baseBalance)
|
||||
return
|
||||
}
|
||||
} else if shortSignal.Last() && !IsBull {
|
||||
if ewo.Last() > s.Threshold {
|
||||
// Strong short
|
||||
log.Infof("strong short at %v, timestamp: %s", lastPrice, kline.StartTime)
|
||||
} else {
|
||||
orders = append(orders, types.SubmitOrder{
|
||||
Symbol: s.Symbol,
|
||||
Side: types.SideTypeSell,
|
||||
Type: types.OrderTypeLimit,
|
||||
Market: market,
|
||||
Quantity: baseBalance,
|
||||
Price: lastPrice,
|
||||
TimeInForce: types.TimeInForceGTC,
|
||||
})
|
||||
} else if ewo.Last() > 0 {
|
||||
// short
|
||||
log.Infof("short at %v, timestamp: %s", lastPrice, kline.StartTime)
|
||||
// TODO: smaller quantity?
|
||||
orders = append(orders, types.SubmitOrder{
|
||||
Symbol: s.Symbol,
|
||||
Side: types.SideTypeSell,
|
||||
Type: types.OrderTypeLimit,
|
||||
Market: market,
|
||||
Quantity: baseBalance,
|
||||
Price: lastPrice,
|
||||
TimeInForce: types.TimeInForceGTC,
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(orders) > 0 {
|
||||
createdOrders, err := orderExecutor.SubmitOrders(ctx, orders...)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("cannot place order")
|
||||
return
|
||||
}
|
||||
entryPrice = lastPrice
|
||||
inTrade = true
|
||||
tradeDirectionLong = IsBull
|
||||
if tradeDirectionLong {
|
||||
stopPrice = entryPrice.Mul(fixedpoint.One.Sub(s.StopLoss))
|
||||
} else {
|
||||
stopPrice = entryPrice.Mul(fixedpoint.One.Add(s.StopLoss))
|
||||
}
|
||||
log.Infof("Place orders %v", createdOrders)
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user