mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
gctcli: Add colourful exchange-style rendering to orderbook fetching commands (optional) (#1348)
* fancybook * fix bug * oopsie-doodle * now I remember why we don't use required
This commit is contained in:
@@ -10,6 +10,15 @@ import (
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
// use these to change text colours in CMD output
|
||||
redText = "\033[38;5;203m"
|
||||
greenText = "\033[38;5;157m"
|
||||
whiteText = "\033[38;5;255m"
|
||||
grayText = "\033[38;5;243m"
|
||||
defaultText = "\u001b[0m"
|
||||
)
|
||||
|
||||
func clearScreen() error {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
|
||||
@@ -5,7 +5,9 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/gctrpc"
|
||||
"github.com/urfave/cli/v2"
|
||||
@@ -352,22 +354,17 @@ func getMovement(c *cli.Context) error {
|
||||
var getOrderbookCommand = &cli.Command{
|
||||
Name: "getorderbook",
|
||||
Usage: "gets the orderbook for a specific currency pair and exchange",
|
||||
ArgsUsage: "<exchange> <pair> <asset>",
|
||||
ArgsUsage: "<exchange> <pair> <asset> <exchangestyle> <depthlimit>",
|
||||
Action: getOrderbook,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Usage: "the exchange to get the orderbook for",
|
||||
Flags: append(orderbookCommonFlags,
|
||||
&cli.BoolFlag{
|
||||
Name: "exchangestyle",
|
||||
Usage: "optional - renders the books like on an exchange website",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "pair",
|
||||
Usage: "the currency pair to get the orderbook for",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "asset",
|
||||
Usage: "the asset type of the currency pair to get the orderbook for",
|
||||
},
|
||||
},
|
||||
&cli.Int64Flag{
|
||||
Name: "depthlimit",
|
||||
Usage: "optional - limit how deep the book rendering is, max 100 - only works if exchangestyle is true",
|
||||
}),
|
||||
}
|
||||
|
||||
func getOrderbook(c *cli.Context) error {
|
||||
@@ -375,9 +372,12 @@ func getOrderbook(c *cli.Context) error {
|
||||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
var exchangeName string
|
||||
var currencyPair string
|
||||
var assetType string
|
||||
var (
|
||||
exchangeName, pair, assetType string
|
||||
depthLimit int64
|
||||
exchangeStyle bool
|
||||
err error
|
||||
)
|
||||
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
@@ -386,12 +386,12 @@ func getOrderbook(c *cli.Context) error {
|
||||
}
|
||||
|
||||
if c.IsSet("pair") {
|
||||
currencyPair = c.String("pair")
|
||||
pair = c.String("pair")
|
||||
} else {
|
||||
currencyPair = c.Args().Get(1)
|
||||
pair = c.Args().Get(1)
|
||||
}
|
||||
|
||||
if !validPair(currencyPair) {
|
||||
if !validPair(pair) {
|
||||
return errInvalidPair
|
||||
}
|
||||
|
||||
@@ -401,12 +401,30 @@ func getOrderbook(c *cli.Context) error {
|
||||
assetType = c.Args().Get(2)
|
||||
}
|
||||
|
||||
if c.IsSet("exchangestyle") {
|
||||
exchangeStyle = c.Bool("exchangestyle")
|
||||
} else if c.Args().Get(3) != "" {
|
||||
exchangeStyle, err = strconv.ParseBool(c.Args().Get(3))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.IsSet("depthlimit") {
|
||||
depthLimit = c.Int64("depthlimit")
|
||||
} else if c.Args().Get(4) != "" {
|
||||
depthLimit, err = strconv.ParseInt(c.Args().Get(4), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
assetType = strings.ToLower(assetType)
|
||||
if !validAsset(assetType) {
|
||||
return errInvalidAsset
|
||||
}
|
||||
|
||||
p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter)
|
||||
p, err := currency.NewPairDelimiter(pair, pairDelimiter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -434,7 +452,25 @@ func getOrderbook(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
if exchangeStyle {
|
||||
var maxLen, bidLen, askLen int64
|
||||
bidLen = int64(len(result.Bids) - 1)
|
||||
askLen = int64(len(result.Asks) - 1)
|
||||
if bidLen >= askLen {
|
||||
maxLen = bidLen
|
||||
} else {
|
||||
maxLen = askLen
|
||||
}
|
||||
if depthLimit > 0 && depthLimit < maxLen {
|
||||
maxLen = depthLimit
|
||||
}
|
||||
if maxLen > 100 {
|
||||
maxLen = 100
|
||||
}
|
||||
renderOrderbookExchangeStyle(result, exchangeName, assetType, maxLen, askLen, bidLen)
|
||||
} else {
|
||||
jsonOutput(result)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -464,9 +500,17 @@ func getOrderbooks(c *cli.Context) error {
|
||||
var getOrderbookStreamCommand = &cli.Command{
|
||||
Name: "getorderbookstream",
|
||||
Usage: "gets the orderbook stream for a specific currency pair and exchange",
|
||||
ArgsUsage: "<exchange> <pair> <asset>",
|
||||
ArgsUsage: "<exchange> <pair> <asset> <exchangestyle> <depthlimit>",
|
||||
Action: getOrderbookStream,
|
||||
Flags: orderbookCommonFlags,
|
||||
Flags: append(orderbookCommonFlags,
|
||||
&cli.BoolFlag{
|
||||
Name: "exchangestyle",
|
||||
Usage: "optional - renders the books like on an exchange website",
|
||||
},
|
||||
&cli.Int64Flag{
|
||||
Name: "depthlimit",
|
||||
Usage: "optional - limit how deep the book rendering is, max 50",
|
||||
}),
|
||||
}
|
||||
|
||||
func getOrderbookStream(c *cli.Context) error {
|
||||
@@ -474,9 +518,12 @@ func getOrderbookStream(c *cli.Context) error {
|
||||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
var exchangeName string
|
||||
var pair string
|
||||
var assetType string
|
||||
var (
|
||||
exchangeName, pair, assetType string
|
||||
depthLimit int64
|
||||
exchangeStyle bool
|
||||
err error
|
||||
)
|
||||
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
@@ -500,6 +547,24 @@ func getOrderbookStream(c *cli.Context) error {
|
||||
assetType = c.Args().Get(2)
|
||||
}
|
||||
|
||||
if c.IsSet("exchangestyle") {
|
||||
exchangeStyle = c.Bool("exchangestyle")
|
||||
} else if c.Args().Get(3) != "" {
|
||||
exchangeStyle, err = strconv.ParseBool(c.Args().Get(3))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.IsSet("depthlimit") {
|
||||
depthLimit = c.Int64("depthlimit")
|
||||
} else if c.Args().Get(4) != "" {
|
||||
depthLimit, err = strconv.ParseInt(c.Args().Get(4), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
assetType = strings.ToLower(assetType)
|
||||
|
||||
if !validAsset(assetType) {
|
||||
@@ -545,56 +610,91 @@ func getOrderbookStream(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Orderbook stream for %s %s:\n\n", exchangeName, resp.Pair)
|
||||
if resp.Error != "" {
|
||||
fmt.Printf("%s\n", resp.Error)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Println("\t\tBids\t\t\t\tAsks")
|
||||
fmt.Println()
|
||||
bidLen := int64(len(resp.Bids) - 1)
|
||||
askLen := int64(len(resp.Asks) - 1)
|
||||
|
||||
bidLen := len(resp.Bids) - 1
|
||||
askLen := len(resp.Asks) - 1
|
||||
|
||||
var maxLen int
|
||||
var maxLen int64
|
||||
if bidLen >= askLen {
|
||||
maxLen = bidLen
|
||||
} else {
|
||||
maxLen = askLen
|
||||
}
|
||||
if depthLimit > 0 && depthLimit < maxLen {
|
||||
maxLen = depthLimit
|
||||
}
|
||||
if maxLen > 50 {
|
||||
maxLen = 50
|
||||
}
|
||||
|
||||
for i := 0; i < maxLen; i++ {
|
||||
var bidAmount, bidPrice float64
|
||||
if i <= bidLen {
|
||||
bidAmount = resp.Bids[i].Amount
|
||||
bidPrice = resp.Bids[i].Price
|
||||
}
|
||||
if exchangeStyle {
|
||||
renderOrderbookExchangeStyle(resp, exchangeName, assetType, maxLen, askLen, bidLen)
|
||||
} else {
|
||||
fmt.Printf("Orderbook stream for %s %s:\n\n", exchangeName, resp.Pair)
|
||||
fmt.Println("\t\tBids\t\t\t\tAsks")
|
||||
fmt.Println()
|
||||
|
||||
var askAmount, askPrice float64
|
||||
if i <= askLen {
|
||||
askAmount = resp.Asks[i].Amount
|
||||
askPrice = resp.Asks[i].Price
|
||||
}
|
||||
for i := int64(0); i < maxLen; i++ {
|
||||
var bidAmount, bidPrice float64
|
||||
if i <= bidLen {
|
||||
bidAmount = resp.Bids[i].Amount
|
||||
bidPrice = resp.Bids[i].Price
|
||||
}
|
||||
|
||||
fmt.Printf("%.8f %s @ %.8f %s\t\t%.8f %s @ %.8f %s\n",
|
||||
bidAmount,
|
||||
resp.Pair.Base,
|
||||
bidPrice,
|
||||
resp.Pair.Quote,
|
||||
askAmount,
|
||||
resp.Pair.Base,
|
||||
askPrice,
|
||||
resp.Pair.Quote)
|
||||
var askAmount, askPrice float64
|
||||
if i <= askLen {
|
||||
askAmount = resp.Asks[i].Amount
|
||||
askPrice = resp.Asks[i].Price
|
||||
}
|
||||
|
||||
if i >= 49 {
|
||||
// limits orderbook display output
|
||||
break
|
||||
fmt.Printf("%.8f %s @ %.8f %s\t\t%.8f %s @ %.8f %s\n",
|
||||
bidAmount,
|
||||
resp.Pair.Base,
|
||||
bidPrice,
|
||||
resp.Pair.Quote,
|
||||
askAmount,
|
||||
resp.Pair.Base,
|
||||
askPrice,
|
||||
resp.Pair.Quote)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func renderOrderbookExchangeStyle(resp *gctrpc.OrderbookResponse, exchangeName, assetType string, maxLen, askLen, bidLen int64) {
|
||||
maxLen-- // ensure we get the 0 index at the correct max length
|
||||
upperBase := strings.ToUpper(resp.Pair.Base)
|
||||
upperQuote := strings.ToUpper(resp.Pair.Quote)
|
||||
printFmt := "%s%.8f\t\t%.8f\n"
|
||||
fmt.Printf("%sOrderbook stream for %v %v %v - Last updated %v\n",
|
||||
whiteText, strings.ToUpper(exchangeName), assetType, upperBase+"-"+upperQuote, time.UnixMicro(resp.LastUpdated).Format(common.SimpleTimeFormatWithTimezone))
|
||||
|
||||
fmt.Printf("%sPrice(%v)\t\tAmount(%s)\n",
|
||||
grayText, upperQuote, upperBase)
|
||||
for i := maxLen; i >= 0; i-- {
|
||||
var askAmount, askPrice float64
|
||||
if i <= askLen {
|
||||
askAmount = resp.Asks[i].Amount
|
||||
askPrice = resp.Asks[i].Price
|
||||
}
|
||||
fmt.Printf(printFmt, redText, askPrice, askAmount)
|
||||
}
|
||||
fmt.Println()
|
||||
for i := int64(0); i <= maxLen; i++ {
|
||||
var bidAmount, bidPrice float64
|
||||
if i <= bidLen {
|
||||
bidAmount = resp.Bids[i].Amount
|
||||
bidPrice = resp.Bids[i].Price
|
||||
}
|
||||
fmt.Printf(printFmt, greenText, bidPrice, bidAmount)
|
||||
}
|
||||
fmt.Println(defaultText)
|
||||
}
|
||||
|
||||
var getExchangeOrderbookStreamCommand = &cli.Command{
|
||||
Name: "getexchangeorderbookstream",
|
||||
Usage: "gets a stream for all orderbooks associated with an exchange",
|
||||
@@ -647,7 +747,7 @@ func getExchangeOrderbookStream(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Orderbook streamed for %s %s", exchangeName, resp.Pair)
|
||||
fmt.Printf("Orderbook streamed for %s %s at %s", exchangeName, resp.Pair, time.UnixMicro(resp.LastUpdated).Format(common.SimpleTimeFormatWithTimezone))
|
||||
if resp.Error != "" {
|
||||
fmt.Printf("%s\n", resp.Error)
|
||||
}
|
||||
|
||||
@@ -2141,8 +2141,9 @@ func (s *RPCServer) GetOrderbookStream(r *gctrpc.GetOrderbookStreamRequest, stre
|
||||
base, err := depth.Retrieve()
|
||||
if err != nil {
|
||||
resp.Error = err.Error()
|
||||
resp.LastUpdated = time.Now().Unix()
|
||||
resp.LastUpdated = time.Now().UnixMicro()
|
||||
} else {
|
||||
resp.LastUpdated = base.LastUpdated.UnixMicro()
|
||||
resp.Bids = make([]*gctrpc.OrderbookItem, len(base.Bids))
|
||||
for i := range base.Bids {
|
||||
resp.Bids[i] = &gctrpc.OrderbookItem{
|
||||
@@ -2204,8 +2205,9 @@ func (s *RPCServer) GetExchangeOrderbookStream(r *gctrpc.GetExchangeOrderbookStr
|
||||
ob, err := d.Retrieve()
|
||||
if err != nil {
|
||||
resp.Error = err.Error()
|
||||
resp.LastUpdated = time.Now().Unix()
|
||||
resp.LastUpdated = time.Now().UnixMicro()
|
||||
} else {
|
||||
resp.LastUpdated = ob.LastUpdated.UnixMicro()
|
||||
resp.Pair = &gctrpc.CurrencyPair{
|
||||
Base: ob.Pair.Base.String(),
|
||||
Quote: ob.Pair.Quote.String(),
|
||||
|
||||
Reference in New Issue
Block a user