mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 14:55:16 +00:00
Merge pull request #928 from c9s/refactor/iteract-filter
refactor: refactor interact strategy filter functions
This commit is contained in:
commit
42a358fd34
|
@ -77,54 +77,6 @@ func RegisterCommand(command, desc string, f interface{}) *interact.Command {
|
||||||
return it.Cmd
|
return it.Cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStrategySignatures(exchangeStrategies map[string]SingleExchangeStrategy) []string {
|
|
||||||
var strategies []string
|
|
||||||
for signature := range exchangeStrategies {
|
|
||||||
strategies = append(strategies, signature)
|
|
||||||
}
|
|
||||||
|
|
||||||
return strategies
|
|
||||||
}
|
|
||||||
|
|
||||||
func filterStrategyByInterface(checkInterface interface{}, exchangeStrategies map[string]SingleExchangeStrategy) (strategies map[string]SingleExchangeStrategy, found bool) {
|
|
||||||
found = false
|
|
||||||
strategies = make(map[string]SingleExchangeStrategy)
|
|
||||||
rt := reflect.TypeOf(checkInterface).Elem()
|
|
||||||
for signature, strategy := range exchangeStrategies {
|
|
||||||
if ok := reflect.TypeOf(strategy).Implements(rt); ok {
|
|
||||||
strategies[signature] = strategy
|
|
||||||
found = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return strategies, found
|
|
||||||
}
|
|
||||||
|
|
||||||
func filterStrategyByField(fieldName string, fieldType reflect.Type, exchangeStrategies map[string]SingleExchangeStrategy) (strategies map[string]SingleExchangeStrategy, found bool) {
|
|
||||||
found = false
|
|
||||||
strategies = make(map[string]SingleExchangeStrategy)
|
|
||||||
for signature, strategy := range exchangeStrategies {
|
|
||||||
r := reflect.ValueOf(strategy).Elem()
|
|
||||||
f := r.FieldByName(fieldName)
|
|
||||||
if !f.IsZero() && f.Type() == fieldType {
|
|
||||||
strategies[signature] = strategy
|
|
||||||
found = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return strategies, found
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateStrategyButtonsForm(strategies map[string]SingleExchangeStrategy) [][3]string {
|
|
||||||
var buttonsForm [][3]string
|
|
||||||
signatures := getStrategySignatures(strategies)
|
|
||||||
for _, signature := range signatures {
|
|
||||||
buttonsForm = append(buttonsForm, [3]string{signature, "strategy", signature})
|
|
||||||
}
|
|
||||||
|
|
||||||
return buttonsForm
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *CoreInteraction) Commands(i *interact.Interact) {
|
func (it *CoreInteraction) Commands(i *interact.Interact) {
|
||||||
i.PrivateCommand("/sessions", "List Exchange Sessions", func(reply interact.Reply) error {
|
i.PrivateCommand("/sessions", "List Exchange Sessions", func(reply interact.Reply) error {
|
||||||
switch r := reply.(type) {
|
switch r := reply.(type) {
|
||||||
|
@ -172,7 +124,7 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
||||||
i.PrivateCommand("/position", "Show Position", func(reply interact.Reply) error {
|
i.PrivateCommand("/position", "Show Position", func(reply interact.Reply) error {
|
||||||
// it.trader.exchangeStrategies
|
// it.trader.exchangeStrategies
|
||||||
// send symbol options
|
// send symbol options
|
||||||
if strategies, found := filterStrategyByInterface((*PositionReader)(nil), it.exchangeStrategies); found {
|
if strategies, err := filterStrategiesByInterface(it.exchangeStrategies, (*PositionReader)(nil)); err == nil && len(strategies) > 0 {
|
||||||
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
||||||
reply.Message("Please choose one strategy")
|
reply.Message("Please choose one strategy")
|
||||||
} else {
|
} else {
|
||||||
|
@ -211,9 +163,11 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
||||||
})
|
})
|
||||||
|
|
||||||
i.PrivateCommand("/resetposition", "Reset position", func(reply interact.Reply) error {
|
i.PrivateCommand("/resetposition", "Reset position", func(reply interact.Reply) error {
|
||||||
// it.trader.exchangeStrategies
|
strategies, err := filterStrategies(it.exchangeStrategies, func(s SingleExchangeStrategy) bool {
|
||||||
// send symbol options
|
return testInterface(s, (*PositionResetter)(nil)) || hasTypeField(s, &types.Position{})
|
||||||
if strategies, found := filterStrategyByInterface((*PositionResetter)(nil), it.exchangeStrategies); found {
|
})
|
||||||
|
|
||||||
|
if err == nil && len(strategies) > 0 {
|
||||||
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
||||||
reply.Message("Please choose one strategy")
|
reply.Message("Please choose one strategy")
|
||||||
} else {
|
} else {
|
||||||
|
@ -254,7 +208,7 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
||||||
i.PrivateCommand("/closeposition", "Close position", func(reply interact.Reply) error {
|
i.PrivateCommand("/closeposition", "Close position", func(reply interact.Reply) error {
|
||||||
// it.trader.exchangeStrategies
|
// it.trader.exchangeStrategies
|
||||||
// send symbol options
|
// send symbol options
|
||||||
if strategies, found := filterStrategyByInterface((*PositionCloser)(nil), it.exchangeStrategies); found {
|
if strategies, err := filterStrategiesByInterface(it.exchangeStrategies, (*PositionCloser)(nil)); err == nil && len(strategies) > 0 {
|
||||||
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
||||||
reply.Message("Please choose one strategy")
|
reply.Message("Please choose one strategy")
|
||||||
} else {
|
} else {
|
||||||
|
@ -323,7 +277,7 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
||||||
i.PrivateCommand("/status", "Strategy Status", func(reply interact.Reply) error {
|
i.PrivateCommand("/status", "Strategy Status", func(reply interact.Reply) error {
|
||||||
// it.trader.exchangeStrategies
|
// it.trader.exchangeStrategies
|
||||||
// send symbol options
|
// send symbol options
|
||||||
if strategies, found := filterStrategyByInterface((*StrategyStatusReader)(nil), it.exchangeStrategies); found {
|
if strategies, err := filterStrategiesByInterface(it.exchangeStrategies, (*StrategyStatusReader)(nil)); err == nil && len(strategies) > 0 {
|
||||||
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
||||||
reply.Message("Please choose a strategy")
|
reply.Message("Please choose a strategy")
|
||||||
} else {
|
} else {
|
||||||
|
@ -361,7 +315,7 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
||||||
i.PrivateCommand("/suspend", "Suspend Strategy", func(reply interact.Reply) error {
|
i.PrivateCommand("/suspend", "Suspend Strategy", func(reply interact.Reply) error {
|
||||||
// it.trader.exchangeStrategies
|
// it.trader.exchangeStrategies
|
||||||
// send symbol options
|
// send symbol options
|
||||||
if strategies, found := filterStrategyByInterface((*StrategyToggler)(nil), it.exchangeStrategies); found {
|
if strategies, err := filterStrategiesByInterface(it.exchangeStrategies, (*StrategyToggler)(nil)); err == nil && len(strategies) > 0 {
|
||||||
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
||||||
reply.Message("Please choose one strategy")
|
reply.Message("Please choose one strategy")
|
||||||
} else {
|
} else {
|
||||||
|
@ -403,7 +357,7 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
||||||
i.PrivateCommand("/resume", "Resume Strategy", func(reply interact.Reply) error {
|
i.PrivateCommand("/resume", "Resume Strategy", func(reply interact.Reply) error {
|
||||||
// it.trader.exchangeStrategies
|
// it.trader.exchangeStrategies
|
||||||
// send symbol options
|
// send symbol options
|
||||||
if strategies, found := filterStrategyByInterface((*StrategyToggler)(nil), it.exchangeStrategies); found {
|
if strategies, err := filterStrategiesByInterface(it.exchangeStrategies, (*StrategyToggler)(nil)); err == nil && len(strategies) > 0 {
|
||||||
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
||||||
reply.Message("Please choose one strategy")
|
reply.Message("Please choose one strategy")
|
||||||
} else {
|
} else {
|
||||||
|
@ -445,7 +399,7 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
||||||
i.PrivateCommand("/emergencystop", "Emergency Stop", func(reply interact.Reply) error {
|
i.PrivateCommand("/emergencystop", "Emergency Stop", func(reply interact.Reply) error {
|
||||||
// it.trader.exchangeStrategies
|
// it.trader.exchangeStrategies
|
||||||
// send symbol options
|
// send symbol options
|
||||||
if strategies, found := filterStrategyByInterface((*EmergencyStopper)(nil), it.exchangeStrategies); found {
|
if strategies, err := filterStrategiesByInterface(it.exchangeStrategies, (*EmergencyStopper)(nil)); err == nil && len(strategies) > 0 {
|
||||||
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
||||||
reply.Message("Please choose one strategy")
|
reply.Message("Please choose one strategy")
|
||||||
} else {
|
} else {
|
||||||
|
@ -482,7 +436,7 @@ func (it *CoreInteraction) Commands(i *interact.Interact) {
|
||||||
i.PrivateCommand("/modifyposition", "Modify Strategy Position", func(reply interact.Reply) error {
|
i.PrivateCommand("/modifyposition", "Modify Strategy Position", func(reply interact.Reply) error {
|
||||||
// it.trader.exchangeStrategies
|
// it.trader.exchangeStrategies
|
||||||
// send symbol options
|
// send symbol options
|
||||||
if strategies, found := filterStrategyByField("Position", reflect.TypeOf(types.NewPosition("", "", "")), it.exchangeStrategies); found {
|
if strategies, err := filterStrategiesByField(it.exchangeStrategies, "Position", reflect.TypeOf(&types.Position{})); err == nil && len(strategies) > 0 {
|
||||||
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
reply.AddMultipleButtons(generateStrategyButtonsForm(strategies))
|
||||||
reply.Message("Please choose one strategy")
|
reply.Message("Please choose one strategy")
|
||||||
} else {
|
} else {
|
||||||
|
@ -610,3 +564,68 @@ func parseFloatPercent(s string, bitSize int) (f float64, err error) {
|
||||||
}
|
}
|
||||||
return f / 100.0, nil
|
return f / 100.0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getStrategySignatures(exchangeStrategies map[string]SingleExchangeStrategy) []string {
|
||||||
|
var strategies []string
|
||||||
|
for signature := range exchangeStrategies {
|
||||||
|
strategies = append(strategies, signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strategies
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterStrategies filters the exchange strategies by a filter tester function
|
||||||
|
// if filter() returns true, the strategy will be added to the returned map.
|
||||||
|
func filterStrategies(exchangeStrategies map[string]SingleExchangeStrategy, filter func(s SingleExchangeStrategy) bool) (map[string]SingleExchangeStrategy, error) {
|
||||||
|
retStrategies := make(map[string]SingleExchangeStrategy)
|
||||||
|
for signature, strategy := range exchangeStrategies {
|
||||||
|
if ok := filter(strategy); ok {
|
||||||
|
retStrategies[signature] = strategy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retStrategies, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasTypeField(obj interface{}, typ interface{}) bool {
|
||||||
|
targetType := reflect.TypeOf(typ)
|
||||||
|
found := false
|
||||||
|
_ = dynamic.IterateFields(obj, func(ft reflect.StructField, fv reflect.Value) error {
|
||||||
|
if fv.Type() == targetType {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInterface(obj interface{}, checkType interface{}) bool {
|
||||||
|
rt := reflect.TypeOf(checkType).Elem()
|
||||||
|
return reflect.TypeOf(obj).Implements(rt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterStrategiesByInterface(exchangeStrategies map[string]SingleExchangeStrategy, checkInterface interface{}) (map[string]SingleExchangeStrategy, error) {
|
||||||
|
rt := reflect.TypeOf(checkInterface).Elem()
|
||||||
|
return filterStrategies(exchangeStrategies, func(s SingleExchangeStrategy) bool {
|
||||||
|
return reflect.TypeOf(s).Implements(rt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterStrategiesByField(exchangeStrategies map[string]SingleExchangeStrategy, fieldName string, fieldType reflect.Type) (map[string]SingleExchangeStrategy, error) {
|
||||||
|
return filterStrategies(exchangeStrategies, func(s SingleExchangeStrategy) bool {
|
||||||
|
r := reflect.ValueOf(s).Elem()
|
||||||
|
f := r.FieldByName(fieldName)
|
||||||
|
return !f.IsZero() && f.Type() == fieldType
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateStrategyButtonsForm(strategies map[string]SingleExchangeStrategy) [][3]string {
|
||||||
|
var buttonsForm [][3]string
|
||||||
|
signatures := getStrategySignatures(strategies)
|
||||||
|
for _, signature := range signatures {
|
||||||
|
buttonsForm = append(buttonsForm, [3]string{signature, "strategy", signature})
|
||||||
|
}
|
||||||
|
|
||||||
|
return buttonsForm
|
||||||
|
}
|
||||||
|
|
|
@ -6,20 +6,28 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type myStrategy struct {
|
type myStrategy struct {
|
||||||
Symbol string `json:"symbol"`
|
Symbol string `json:"symbol"`
|
||||||
|
Position *types.Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m myStrategy) ID() string {
|
func (m *myStrategy) ID() string {
|
||||||
return "mystrategy"
|
return "mystrategy"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m myStrategy) InstanceID() string {
|
func (m *myStrategy) InstanceID() string {
|
||||||
return fmt.Sprintf("%s:%s", m.ID(), m.Symbol)
|
return fmt.Sprintf("%s:%s", m.ID(), m.Symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *myStrategy) ClosePosition(ctx context.Context, percentage fixedpoint.Value) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *myStrategy) Run(ctx context.Context, orderExecutor OrderExecutor, session *ExchangeSession) error {
|
func (m *myStrategy) Run(ctx context.Context, orderExecutor OrderExecutor, session *ExchangeSession) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -31,3 +39,20 @@ func Test_getStrategySignature(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "mystrategy:BTCUSDT", signature)
|
assert.Equal(t, "mystrategy:BTCUSDT", signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_hasTypeField(t *testing.T) {
|
||||||
|
s := &myStrategy{
|
||||||
|
Symbol: "BTCUSDT",
|
||||||
|
}
|
||||||
|
ok := hasTypeField(s, &types.Position{})
|
||||||
|
assert.True(t, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_testInterface(t *testing.T) {
|
||||||
|
s := &myStrategy{
|
||||||
|
Symbol: "BTCUSDT",
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := testInterface(s, (*PositionCloser)(nil))
|
||||||
|
assert.True(t, ok)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user