-
-
Notifications
You must be signed in to change notification settings - Fork 304
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement the basic flow of interact
- Loading branch information
Showing
6 changed files
with
418 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
"strconv" | ||
"strings" | ||
"syscall" | ||
"time" | ||
|
||
log "github.com/sirupsen/logrus" | ||
tb "gopkg.in/tucnak/telebot.v2" | ||
|
||
"github.com/c9s/bbgo/pkg/cmd/cmdutil" | ||
"github.com/c9s/bbgo/pkg/interact" | ||
) | ||
|
||
func parseFloatPercent(s string, bitSize int) (f float64, err error) { | ||
i := strings.Index(s, "%") | ||
if i < 0 { | ||
return strconv.ParseFloat(s, bitSize) | ||
} | ||
|
||
f, err = strconv.ParseFloat(s[:i], bitSize) | ||
if err != nil { | ||
return 0, err | ||
} | ||
return f / 100.0, nil | ||
} | ||
|
||
type closePositionTask struct { | ||
symbol string | ||
percentage float64 | ||
confirmed bool | ||
} | ||
|
||
type PositionInteraction struct { | ||
closePositionTask closePositionTask | ||
} | ||
|
||
func (m *PositionInteraction) Commands(i *interact.Interact) { | ||
i.Command("/closePosition", func(reply interact.Reply) error { | ||
// send symbol options | ||
reply.Message("Choose your position") | ||
for _, symbol := range []string{"BTCUSDT", "ETHUSDT"} { | ||
reply.AddButton(symbol) | ||
} | ||
|
||
return nil | ||
}).Next(func(reply interact.Reply, symbol string) error { | ||
// get symbol from user | ||
if len(symbol) == 0 { | ||
reply.Message("Please enter a symbol") | ||
return fmt.Errorf("empty symbol") | ||
} | ||
switch symbol { | ||
case "BTCUSDT", "ETHUSDT": | ||
|
||
default: | ||
reply.Message("Invalid symbol") | ||
return fmt.Errorf("invalid symbol") | ||
|
||
} | ||
|
||
m.closePositionTask.symbol = symbol | ||
|
||
reply.Message("Choose or enter the percentage to close") | ||
for _, symbol := range []string{"25%", "50%", "100%"} { | ||
reply.AddButton(symbol) | ||
} | ||
|
||
// send percentage options | ||
return nil | ||
}).Next(func(reply interact.Reply, percentageStr string) error { | ||
p, err := parseFloatPercent(percentageStr, 64) | ||
if err != nil { | ||
reply.Message("Not a valid percentage string") | ||
return err | ||
} | ||
|
||
// get percentage from user | ||
m.closePositionTask.percentage = p | ||
|
||
// send confirmation | ||
reply.Message("Are you sure to close the position?") | ||
reply.AddButton("Yes") | ||
return nil | ||
}).Next(func(reply interact.Reply, confirm string) error { | ||
switch strings.ToLower(confirm) { | ||
case "yes": | ||
m.closePositionTask.confirmed = true | ||
reply.Message(fmt.Sprintf("Your %s position is closed", m.closePositionTask.symbol)) | ||
reply.RemoveKeyboard() | ||
|
||
default: | ||
|
||
} | ||
// call position close | ||
|
||
// reply result | ||
return nil | ||
}) | ||
} | ||
|
||
func main() { | ||
b, err := tb.NewBot(tb.Settings{ | ||
// You can also set custom API URL. | ||
// If field is empty it equals to "https://api.telegram.org". | ||
// URL: "http://195.129.111.17:8012", | ||
|
||
Token: os.Getenv("TELEGRAM_BOT_TOKEN"), | ||
Poller: &tb.LongPoller{Timeout: 10 * time.Second}, | ||
// Synchronous: false, | ||
// Verbose: true, | ||
// ParseMode: "", | ||
// Reporter: nil, | ||
// Client: nil, | ||
// Offline: false, | ||
}) | ||
|
||
if err != nil { | ||
log.Fatal(err) | ||
return | ||
} | ||
|
||
ctx := context.Background() | ||
|
||
globalInteraction := interact.New() | ||
globalInteraction.SetMessenger(&interact.Telegram{ | ||
Interact: globalInteraction, | ||
Bot: b, | ||
}) | ||
|
||
globalInteraction.AddCustomInteraction(&PositionInteraction{}) | ||
if err := globalInteraction.Start(ctx); err != nil { | ||
log.Fatal(err) | ||
} | ||
cmdutil.WaitForSignal(ctx, syscall.SIGINT, syscall.SIGTERM) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package interact | ||
|
||
import "strconv" | ||
|
||
// Command is a domain specific language syntax helper | ||
// It's used for helping developer define the state and transition function | ||
type Command struct { | ||
// Name is the command name | ||
Name string | ||
|
||
// StateF is the command handler function | ||
F interface{} | ||
|
||
stateID int | ||
states map[State]State | ||
statesFunc map[State]interface{} | ||
initState, lastState State | ||
} | ||
|
||
func NewCommand(name string, f interface{}) *Command { | ||
c := &Command{ | ||
Name: name, | ||
F: f, | ||
states: make(map[State]State), | ||
statesFunc: make(map[State]interface{}), | ||
initState: State(name + "_" + strconv.Itoa(0)), | ||
} | ||
return c.Next(f) | ||
} | ||
|
||
// Transit defines the state transition that is not related to the last defined state. | ||
func (c *Command) Transit(state1, state2 State, f interface{}) *Command { | ||
c.states[state1] = state2 | ||
c.statesFunc[state1] = f | ||
return c | ||
} | ||
|
||
func (c *Command) NamedNext(n string, f interface{}) *Command { | ||
var curState State | ||
if c.lastState == "" { | ||
curState = State(c.Name + "_" + strconv.Itoa(c.stateID)) | ||
} else { | ||
curState = c.lastState | ||
} | ||
|
||
nextState := State(n) | ||
c.states[curState] = nextState | ||
c.statesFunc[curState] = f | ||
c.lastState = nextState | ||
return c | ||
} | ||
|
||
// Next defines the next state with the transition function from the last defined state. | ||
func (c *Command) Next(f interface{}) *Command { | ||
var curState State | ||
if c.lastState == "" { | ||
curState = State(c.Name + "_" + strconv.Itoa(c.stateID)) | ||
} else { | ||
curState = c.lastState | ||
} | ||
|
||
// generate the next state by the stateID | ||
c.stateID++ | ||
nextState := State(c.Name + "_" + strconv.Itoa(c.stateID)) | ||
|
||
c.states[curState] = nextState | ||
c.statesFunc[curState] = f | ||
c.lastState = nextState | ||
return c | ||
} |
Oops, something went wrong.