add pause trade
This commit is contained in:
parent
d973d95121
commit
a07d255d77
|
@ -7,13 +7,13 @@
|
||||||
# db: 0
|
# db: 0
|
||||||
|
|
||||||
sessions:
|
sessions:
|
||||||
binance_futures:
|
binance:
|
||||||
exchange: binance
|
exchange: binance
|
||||||
envVarPrefix: BINANCE
|
envVarPrefix: BINANCE
|
||||||
futures: true
|
futures: true
|
||||||
|
|
||||||
exchangeStrategies:
|
exchangeStrategies:
|
||||||
- on: binance_futures
|
- on: binance
|
||||||
ccinr:
|
ccinr:
|
||||||
symbols:
|
symbols:
|
||||||
- ARUSDT
|
- ARUSDT
|
||||||
|
@ -55,9 +55,33 @@ exchangeStrategies:
|
||||||
placePriceType: 2
|
placePriceType: 2
|
||||||
lossType: 1
|
lossType: 1
|
||||||
profitOrderType: 0
|
profitOrderType: 0
|
||||||
atrProfitRange: 0.8
|
atrProfitRange: 1.0
|
||||||
atrLossRange: 1.0
|
atrLossRange: 2.0
|
||||||
|
tradeStartHour: 0
|
||||||
|
tradeEndHour: 8
|
||||||
|
pauseTradeLoss: -10.0
|
||||||
# recalculate: false
|
# recalculate: false
|
||||||
# dry_run: false
|
# dry_run: false
|
||||||
# # quantity: 3
|
# # quantity: 3
|
||||||
# strict_mode: true
|
# strict_mode: true
|
||||||
|
|
||||||
|
backtest:
|
||||||
|
startTime: "2023-10-15"
|
||||||
|
endTime: "2024-08-02"
|
||||||
|
symbols:
|
||||||
|
- ARUSDT
|
||||||
|
- ORDIUSDT
|
||||||
|
- OPUSDT
|
||||||
|
- OMUSDT
|
||||||
|
- WIFUSDT
|
||||||
|
- DYDXUSDT
|
||||||
|
- XRPUSDT
|
||||||
|
- PEOPLEUSDT
|
||||||
|
sessions: [ binance ]
|
||||||
|
syncSecKLines: true
|
||||||
|
accounts:
|
||||||
|
binance:
|
||||||
|
takerFeeRate: 0.0002
|
||||||
|
makerFeeRate: 0.0005
|
||||||
|
balances:
|
||||||
|
USDT: 200
|
BIN
otp.png
BIN
otp.png
Binary file not shown.
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 2.9 KiB |
|
@ -174,7 +174,8 @@ func (n *Notifier) NotifyTo(channel string, obj interface{}, args ...interface{}
|
||||||
message = fmt.Sprintf(a, args...)
|
message = fmt.Sprintf(a, args...)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.Errorf("unsupported notification format: %T %+v", a, a)
|
//log.Errorf("unsupported notification format: %T %+v", a, a)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ID = "ccinr"
|
const ID = "ccinr"
|
||||||
|
@ -49,6 +50,9 @@ type Strategy struct {
|
||||||
LossRange fixedpoint.Value `json:"lossRange"`
|
LossRange fixedpoint.Value `json:"lossRange"`
|
||||||
AtrProfitRange float64 `json:"atrProfitRange"`
|
AtrProfitRange float64 `json:"atrProfitRange"`
|
||||||
AtrLossRange float64 `json:"atrLossRange"`
|
AtrLossRange float64 `json:"atrLossRange"`
|
||||||
|
TradeStartHour int `json:"tradeStartHour"`
|
||||||
|
TradeEndHour int `json:"tradeEndHour"`
|
||||||
|
PauseTradeLoss fixedpoint.Value `json:"pauseTradeLoss"`
|
||||||
|
|
||||||
qbtrade.QuantityOrAmount
|
qbtrade.QuantityOrAmount
|
||||||
|
|
||||||
|
@ -89,6 +93,10 @@ type Strategy struct {
|
||||||
TotalOrderCount int
|
TotalOrderCount int
|
||||||
TotalProfitCount int
|
TotalProfitCount int
|
||||||
TotalLossCount int
|
TotalLossCount int
|
||||||
|
// 累计暂停交易的次数
|
||||||
|
PauseTradeCount fixedpoint.Value
|
||||||
|
// 最近一次暂停交易的时间
|
||||||
|
PauseTradeTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Strategy) ID() string {
|
func (s *Strategy) ID() string {
|
||||||
|
@ -101,9 +109,12 @@ func (s *Strategy) Subscribe(session *qbtrade.ExchangeSession) {
|
||||||
session.Subscribe(types.KLineChannel, symbol, types.SubscribeOptions{Interval: s.CCIInterval})
|
session.Subscribe(types.KLineChannel, symbol, types.SubscribeOptions{Interval: s.CCIInterval})
|
||||||
session.Subscribe(types.KLineChannel, symbol, types.SubscribeOptions{Interval: s.ATRInterval})
|
session.Subscribe(types.KLineChannel, symbol, types.SubscribeOptions{Interval: s.ATRInterval})
|
||||||
session.Subscribe(types.KLineChannel, symbol, types.SubscribeOptions{Interval: s.NRInterval})
|
session.Subscribe(types.KLineChannel, symbol, types.SubscribeOptions{Interval: s.NRInterval})
|
||||||
|
|
||||||
|
if !qbtrade.IsBackTesting {
|
||||||
session.Subscribe(types.MarketTradeChannel, symbol, types.SubscribeOptions{})
|
session.Subscribe(types.MarketTradeChannel, symbol, types.SubscribeOptions{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Strategy) Initialize() error {
|
func (s *Strategy) Initialize() error {
|
||||||
if s.Strategy == nil {
|
if s.Strategy == nil {
|
||||||
|
@ -114,7 +125,7 @@ func (s *Strategy) Initialize() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Strategy) InstanceID() string {
|
func (s *Strategy) InstanceID() string {
|
||||||
return fmt.Sprintf("%s:%s:%s", ID, strings.Join(s.Symbols, "-"), s.Interval)
|
return fmt.Sprintf("%s:%s", ID, s.Interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Strategy) ClosePosition(ctx context.Context, percentage fixedpoint.Value) error {
|
func (s *Strategy) ClosePosition(ctx context.Context, percentage fixedpoint.Value) error {
|
||||||
|
@ -206,10 +217,17 @@ func (s *Strategy) generateOrders(ctx context.Context, kline types.KLine) ([]typ
|
||||||
|
|
||||||
// 止盈订单类型
|
// 止盈订单类型
|
||||||
profitOrderType := types.OrderTypeTakeProfitMarket
|
profitOrderType := types.OrderTypeTakeProfitMarket
|
||||||
|
// 止损订单类型
|
||||||
|
lossOrderType := types.OrderTypeStopMarket
|
||||||
if s.ProfitOrderType == 1 {
|
if s.ProfitOrderType == 1 {
|
||||||
profitOrderType = types.OrderTypeStopMarket
|
profitOrderType = types.OrderTypeStopMarket
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if qbtrade.IsBackTesting {
|
||||||
|
profitOrderType = types.OrderTypeStopLimit
|
||||||
|
lossOrderType = types.OrderTypeStopLimit
|
||||||
|
}
|
||||||
|
|
||||||
// 计算止损止盈价格,以ATR为基准或者固定百分比
|
// 计算止损止盈价格,以ATR为基准或者固定百分比
|
||||||
lossPrice := fixedpoint.Zero
|
lossPrice := fixedpoint.Zero
|
||||||
profitPrice := fixedpoint.Zero
|
profitPrice := fixedpoint.Zero
|
||||||
|
@ -267,7 +285,7 @@ func (s *Strategy) generateOrders(ctx context.Context, kline types.KLine) ([]typ
|
||||||
s.ShortLossOrder[symbol] = types.SubmitOrder{
|
s.ShortLossOrder[symbol] = types.SubmitOrder{
|
||||||
Symbol: symbol,
|
Symbol: symbol,
|
||||||
Side: types.SideTypeBuy,
|
Side: types.SideTypeBuy,
|
||||||
Type: types.OrderTypeStopMarket,
|
Type: lossOrderType,
|
||||||
PositionSide: types.PositionSideTypeShort,
|
PositionSide: types.PositionSideTypeShort,
|
||||||
StopPrice: lossPrice,
|
StopPrice: lossPrice,
|
||||||
TimeInForce: types.TimeInForceGTC,
|
TimeInForce: types.TimeInForceGTC,
|
||||||
|
@ -300,7 +318,7 @@ func (s *Strategy) generateOrders(ctx context.Context, kline types.KLine) ([]typ
|
||||||
s.LongLossOrder[symbol] = types.SubmitOrder{
|
s.LongLossOrder[symbol] = types.SubmitOrder{
|
||||||
Symbol: symbol,
|
Symbol: symbol,
|
||||||
Side: types.SideTypeSell,
|
Side: types.SideTypeSell,
|
||||||
Type: types.OrderTypeStopMarket,
|
Type: lossOrderType,
|
||||||
PositionSide: types.PositionSideTypeLong,
|
PositionSide: types.PositionSideTypeLong,
|
||||||
StopPrice: lossPrice,
|
StopPrice: lossPrice,
|
||||||
TimeInForce: types.TimeInForceGTC,
|
TimeInForce: types.TimeInForceGTC,
|
||||||
|
@ -394,6 +412,38 @@ func (s *Strategy) notifyProfit(ctx context.Context, symbol string) {
|
||||||
qbtrade.Notify(fmt.Sprintf("总交易次数:%v, 总收益:%v, 总手续费:%v, 盈利次数:%v, 亏损次数:%v",
|
qbtrade.Notify(fmt.Sprintf("总交易次数:%v, 总收益:%v, 总手续费:%v, 盈利次数:%v, 亏损次数:%v",
|
||||||
s.TotalOrderCount, s.TotalProfit.Float64(), s.TotalFree.Float64(), s.TotalProfitCount, s.TotalLossCount))
|
s.TotalOrderCount, s.TotalProfit.Float64(), s.TotalFree.Float64(), s.TotalProfitCount, s.TotalLossCount))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isTradeTime 是否交易时间
|
||||||
|
func (s *Strategy) isTradeTime(ctx context.Context) bool {
|
||||||
|
// 如果时间一致则表示不限制交易时间
|
||||||
|
if s.TradeEndHour == s.TradeStartHour {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
location, err := time.LoadLocation("Asia/Shanghai")
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
now := time.Now().In(location)
|
||||||
|
|
||||||
|
hour := now.Hour()
|
||||||
|
return hour >= s.TradeStartHour && hour < s.TradeEndHour
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Strategy) isPauseTrade(ctx context.Context) bool {
|
||||||
|
// 被暂停次数不为0,且最近一次的暂停时间和今天一致,则表示暂停
|
||||||
|
if s.PauseTradeCount != fixedpoint.Zero && s.PauseTradeTime.Day() == time.Now().Day() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// 总收益大于(暂停次数+1)*暂停亏损,则表示暂停
|
||||||
|
if s.TotalProfit < s.PauseTradeLoss.Mul(s.PauseTradeCount.Add(fixedpoint.One)) {
|
||||||
|
s.PauseTradeCount.Add(fixedpoint.One)
|
||||||
|
s.PauseTradeTime = time.Now()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Strategy) Run(ctx context.Context, orderExecutor qbtrade.OrderExecutor, session *qbtrade.ExchangeSession) error {
|
func (s *Strategy) Run(ctx context.Context, orderExecutor qbtrade.OrderExecutor, session *qbtrade.ExchangeSession) error {
|
||||||
|
|
||||||
s.ExchangeSession = session
|
s.ExchangeSession = session
|
||||||
|
@ -427,6 +477,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor qbtrade.OrderExecutor,
|
||||||
s.TotalOrderCount = 0
|
s.TotalOrderCount = 0
|
||||||
s.TotalProfitCount = 0
|
s.TotalProfitCount = 0
|
||||||
s.TotalLossCount = 0
|
s.TotalLossCount = 0
|
||||||
|
s.PauseTradeCount = fixedpoint.Zero
|
||||||
|
s.PauseTradeTime = time.Now().Add(-24 * time.Hour)
|
||||||
|
|
||||||
for _, symbol := range s.Symbols {
|
for _, symbol := range s.Symbols {
|
||||||
s.Positions[symbol] = types.NewPositionFromMarket(s.markets[symbol])
|
s.Positions[symbol] = types.NewPositionFromMarket(s.markets[symbol])
|
||||||
|
@ -481,6 +533,15 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor qbtrade.OrderExecutor,
|
||||||
if s.Traded[sym] {
|
if s.Traded[sym] {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !s.isTradeTime(ctx) || s.isPauseTrade(ctx) {
|
||||||
|
pauseMsg := fmt.Sprintf("暂停交易:总收益:%v, 暂停次数:%v, 暂停时间:%v; 暂停时间段:[%v, %v)",
|
||||||
|
s.TotalProfit.Float64(), s.PauseTradeCount.Float64(), s.PauseTradeTime, s.TradeStartHour,
|
||||||
|
s.TradeEndHour)
|
||||||
|
qbtrade.Notify(pauseMsg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
cciV := s.cci[sym].Last(0)
|
cciV := s.cci[sym].Last(0)
|
||||||
if cciV <= s.LongCCI.Float64() {
|
if cciV <= s.LongCCI.Float64() {
|
||||||
s.TradeType[sym] = "long"
|
s.TradeType[sym] = "long"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user