mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-05 15:10:59 +00:00
depth: Add methods to derive liquidity allowances on orderbooks by volume and slippage (#962)
* depth: methods to derive liquidity impact details * depth: fix comments on link list methods * depth: fix whoopsie * Update exchanges/orderbook/linked_list_test.go Co-authored-by: Scott <gloriousCode@users.noreply.github.com> * glorious: nits * orderbook: standardise methods to GCT math package * averagePrice: implementation (WIP) * ll: hmmmmmm * continued * orderbook: reworked functions * WIP * orderbook: add tests link up with RPC * orderbook: refined calculations, add tests (WIP) * orderbook: add tests finalise/verify remove state until next PR if needed * rpcserver/orderbook: remove redundant type and change wording * linter: fix * Update exchanges/orderbook/linked_list.go Co-authored-by: Scott <gloriousCode@users.noreply.github.com> * gctcli: noobed it up * orderbook: work work work (yesterday) * depth: WIP and testing * orderbook: improve calculations for bids traversal * orderbook: adjust tests * orderbook: linters/nits * orderbook: fix error returns and add asset to whalebomb * orderbook: drop error when full book is potentially consumed * Update cmd/gctcli/orderbook.go Co-authored-by: Scott <gloriousCode@users.noreply.github.com> * orderbook: Add tests and nits * glorious: nits * backtester: handle new errors * grpc: linter * orderbook: remove pesky t.Log()s * glorious: nits (yesterday) * depth/gctcli: Add in purchase requirements into orderbook movement, will also standardize in next commit after tests are fixed. * orderbook: standardize and overhaul * orderbook: update comments, update tests * rpcserver: add average ordercost and amounts * depth: add spread and imbalance methods * linter: fix * glorious: nits * orderbook: don't purge price, rn test. * glorious: codes nits * linter: fix * Update exchanges/orderbook/linked_list.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * Update exchanges/orderbook/linked_list.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * Update exchanges/orderbook/linked_list.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * Update exchanges/orderbook/linked_list.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * Update exchanges/orderbook/linked_list.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * thrasher: nits Co-authored-by: Scott <gloriousCode@users.noreply.github.com> Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io> Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
This commit is contained in:
@@ -585,118 +585,6 @@ func getTickers(c *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var getOrderbookCommand = &cli.Command{
|
||||
Name: "getorderbook",
|
||||
Usage: "gets the orderbook for a specific currency pair and exchange",
|
||||
ArgsUsage: "<exchange> <pair> <asset>",
|
||||
Action: getOrderbook,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Usage: "the exchange to get the orderbook for",
|
||||
},
|
||||
&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",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func getOrderbook(c *cli.Context) error {
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, "getorderbook")
|
||||
}
|
||||
|
||||
var exchangeName string
|
||||
var currencyPair string
|
||||
var assetType string
|
||||
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
if c.IsSet("pair") {
|
||||
currencyPair = c.String("pair")
|
||||
} else {
|
||||
currencyPair = c.Args().Get(1)
|
||||
}
|
||||
|
||||
if !validPair(currencyPair) {
|
||||
return errInvalidPair
|
||||
}
|
||||
|
||||
if c.IsSet("asset") {
|
||||
assetType = c.String("asset")
|
||||
} else {
|
||||
assetType = c.Args().Get(2)
|
||||
}
|
||||
|
||||
assetType = strings.ToLower(assetType)
|
||||
if !validAsset(assetType) {
|
||||
return errInvalidAsset
|
||||
}
|
||||
|
||||
p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.GetOrderbook(c.Context,
|
||||
&gctrpc.GetOrderbookRequest{
|
||||
Exchange: exchangeName,
|
||||
Pair: &gctrpc.CurrencyPair{
|
||||
Delimiter: p.Delimiter,
|
||||
Base: p.Base.String(),
|
||||
Quote: p.Quote.String(),
|
||||
},
|
||||
AssetType: assetType,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
var getOrderbooksCommand = &cli.Command{
|
||||
Name: "getorderbooks",
|
||||
Usage: "gets all orderbooks for all enabled exchanges and currency pairs",
|
||||
Action: getOrderbooks,
|
||||
}
|
||||
|
||||
func getOrderbooks(c *cli.Context) error {
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.GetOrderbooks(c.Context, &gctrpc.GetOrderbooksRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
var getAccountInfoCommand = &cli.Command{
|
||||
Name: "getaccountinfo",
|
||||
Usage: "gets the exchange account balance info",
|
||||
@@ -1766,107 +1654,6 @@ func simulateOrder(c *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var whaleBombCommand = &cli.Command{
|
||||
Name: "whalebomb",
|
||||
Usage: "whale bomb finds the amount required to reach a price target",
|
||||
ArgsUsage: "<exchange> <pair> <side> <price>",
|
||||
Action: whaleBomb,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Usage: "the exchange to whale bomb",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "pair",
|
||||
Usage: "the currency pair",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "side",
|
||||
Usage: "the order side to use (BUY OR SELL)",
|
||||
},
|
||||
&cli.Float64Flag{
|
||||
Name: "price",
|
||||
Usage: "the price target",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func whaleBomb(c *cli.Context) error {
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, "whalebomb")
|
||||
}
|
||||
|
||||
var exchangeName string
|
||||
var currencyPair string
|
||||
var orderSide string
|
||||
var price float64
|
||||
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
if c.IsSet("pair") {
|
||||
currencyPair = c.String("pair")
|
||||
} else {
|
||||
currencyPair = c.Args().Get(1)
|
||||
}
|
||||
|
||||
if !validPair(currencyPair) {
|
||||
return errInvalidPair
|
||||
}
|
||||
|
||||
if c.IsSet("side") {
|
||||
orderSide = c.String("side")
|
||||
} else {
|
||||
orderSide = c.Args().Get(2)
|
||||
}
|
||||
|
||||
if orderSide == "" {
|
||||
return errors.New("order side must be set")
|
||||
}
|
||||
|
||||
if c.IsSet("price") {
|
||||
price = c.Float64("price")
|
||||
} else if c.Args().Get(3) != "" {
|
||||
var err error
|
||||
price, err = strconv.ParseFloat(c.Args().Get(3), 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.WhaleBomb(c.Context, &gctrpc.WhaleBombRequest{
|
||||
Exchange: exchangeName,
|
||||
Pair: &gctrpc.CurrencyPair{
|
||||
Delimiter: p.Delimiter,
|
||||
Base: p.Base.String(),
|
||||
Quote: p.Quote.String(),
|
||||
},
|
||||
Side: orderSide,
|
||||
PriceTarget: price,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
var cancelOrderCommand = &cli.Command{
|
||||
Name: "cancelorder",
|
||||
Usage: "cancel order cancels an exchange order",
|
||||
@@ -3354,212 +3141,6 @@ func setLoggerDetails(c *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var getOrderbookStreamCommand = &cli.Command{
|
||||
Name: "getorderbookstream",
|
||||
Usage: "gets the orderbook stream for a specific currency pair and exchange",
|
||||
ArgsUsage: "<exchange> <pair> <asset>",
|
||||
Action: getOrderbookStream,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Usage: "the exchange to get the orderbook from",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "pair",
|
||||
Usage: "currency pair",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "asset",
|
||||
Usage: "the asset type of the currency pair",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func getOrderbookStream(c *cli.Context) error {
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, "getorderbookstream")
|
||||
}
|
||||
|
||||
var exchangeName string
|
||||
var pair string
|
||||
var assetType string
|
||||
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
if c.IsSet("pair") {
|
||||
pair = c.String("pair")
|
||||
} else {
|
||||
pair = c.Args().Get(1)
|
||||
}
|
||||
|
||||
if !validPair(pair) {
|
||||
return errInvalidPair
|
||||
}
|
||||
|
||||
if c.IsSet("asset") {
|
||||
assetType = c.String("asset")
|
||||
} else {
|
||||
assetType = c.Args().Get(2)
|
||||
}
|
||||
|
||||
assetType = strings.ToLower(assetType)
|
||||
|
||||
if !validAsset(assetType) {
|
||||
return errInvalidAsset
|
||||
}
|
||||
|
||||
p, err := currency.NewPairDelimiter(pair, pairDelimiter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.GetOrderbookStream(c.Context,
|
||||
&gctrpc.GetOrderbookStreamRequest{
|
||||
Exchange: exchangeName,
|
||||
Pair: &gctrpc.CurrencyPair{
|
||||
Base: p.Base.String(),
|
||||
Quote: p.Quote.String(),
|
||||
Delimiter: p.Delimiter,
|
||||
},
|
||||
AssetType: assetType,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
resp, err := result.Recv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = clearScreen()
|
||||
if err != nil {
|
||||
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 := len(resp.Bids) - 1
|
||||
askLen := len(resp.Asks) - 1
|
||||
|
||||
var maxLen int
|
||||
if bidLen >= askLen {
|
||||
maxLen = bidLen
|
||||
} else {
|
||||
maxLen = askLen
|
||||
}
|
||||
|
||||
for i := 0; i < maxLen; i++ {
|
||||
var bidAmount, bidPrice float64
|
||||
if i <= bidLen {
|
||||
bidAmount = resp.Bids[i].Amount
|
||||
bidPrice = resp.Bids[i].Price
|
||||
}
|
||||
|
||||
var askAmount, askPrice float64
|
||||
if i <= askLen {
|
||||
askAmount = resp.Asks[i].Amount
|
||||
askPrice = resp.Asks[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)
|
||||
|
||||
if i >= 49 {
|
||||
// limits orderbook display output
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var getExchangeOrderbookStreamCommand = &cli.Command{
|
||||
Name: "getexchangeorderbookstream",
|
||||
Usage: "gets a stream for all orderbooks associated with an exchange",
|
||||
ArgsUsage: "<exchange>",
|
||||
Action: getExchangeOrderbookStream,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Usage: "the exchange to get the orderbook from",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func getExchangeOrderbookStream(c *cli.Context) error {
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, "getexchangeorderbookstream")
|
||||
}
|
||||
|
||||
var exchangeName string
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.GetExchangeOrderbookStream(c.Context,
|
||||
&gctrpc.GetExchangeOrderbookStreamRequest{
|
||||
Exchange: exchangeName,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
resp, err := result.Recv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = clearScreen()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Orderbook streamed for %s %s", exchangeName, resp.Pair)
|
||||
if resp.Error != "" {
|
||||
fmt.Printf("%s\n", resp.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var getTickerStreamCommand = &cli.Command{
|
||||
Name: "gettickerstream",
|
||||
Usage: "gets the ticker stream for a specific currency pair and exchange",
|
||||
|
||||
@@ -162,8 +162,6 @@ func main() {
|
||||
getExchangeInfoCommand,
|
||||
getTickerCommand,
|
||||
getTickersCommand,
|
||||
getOrderbookCommand,
|
||||
getOrderbooksCommand,
|
||||
getAccountInfoCommand,
|
||||
getAccountInfoStreamCommand,
|
||||
updateAccountInfoCommand,
|
||||
@@ -179,7 +177,6 @@ func main() {
|
||||
getOrderCommand,
|
||||
submitOrderCommand,
|
||||
simulateOrderCommand,
|
||||
whaleBombCommand,
|
||||
cancelOrderCommand,
|
||||
cancelBatchOrdersCommand,
|
||||
cancelAllOrdersCommand,
|
||||
@@ -196,8 +193,6 @@ func main() {
|
||||
getLoggerDetailsCommand,
|
||||
setLoggerDetailsCommand,
|
||||
exchangePairManagerCommand,
|
||||
getOrderbookStreamCommand,
|
||||
getExchangeOrderbookStreamCommand,
|
||||
getTickerStreamCommand,
|
||||
getExchangeTickerStreamCommand,
|
||||
getAuditEventCommand,
|
||||
@@ -213,6 +208,7 @@ func main() {
|
||||
shutdownCommand,
|
||||
technicalAnalysisCommand,
|
||||
getMarginRatesHistoryCommand,
|
||||
orderbookCommand,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
768
cmd/gctcli/orderbook.go
Normal file
768
cmd/gctcli/orderbook.go
Normal file
@@ -0,0 +1,768 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/gctrpc"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var orderbookCommonFlags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Usage: "the exchange to get the orderbook for",
|
||||
},
|
||||
&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",
|
||||
},
|
||||
}
|
||||
|
||||
var orderbookCommand = &cli.Command{
|
||||
Name: "orderbook",
|
||||
Usage: "orderbook system simulations and analytics command",
|
||||
ArgsUsage: "<command> <args>",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "sell",
|
||||
Usage: "simulates sell to derive orderbook liquidity impact information",
|
||||
ArgsUsage: "<command> <args>",
|
||||
Subcommands: []*cli.Command{nominal, impact, base, quoteRequired},
|
||||
Flags: []cli.Flag{&cli.BoolFlag{Name: "sell", Hidden: true, Value: true}},
|
||||
},
|
||||
{
|
||||
Name: "buy",
|
||||
Usage: "simulates buy to derive orderbook liquidity impact information",
|
||||
ArgsUsage: "<command> <args>",
|
||||
Subcommands: []*cli.Command{nominal, impact, quote, baseRequired},
|
||||
},
|
||||
getOrderbookCommand,
|
||||
getOrderbooksCommand,
|
||||
getOrderbookStreamCommand,
|
||||
getExchangeOrderbookStreamCommand,
|
||||
whaleBombCommand,
|
||||
},
|
||||
}
|
||||
|
||||
var nominal = &cli.Command{
|
||||
Name: "nominal",
|
||||
Usage: "simulates a buy or sell based off the percentage between the reference price and the average order cost",
|
||||
ArgsUsage: "<exchange> <pair> <asset> <percent>",
|
||||
Action: getNominal,
|
||||
Flags: append(orderbookCommonFlags, &cli.Float64Flag{
|
||||
Name: "percent",
|
||||
Usage: "the max percentage slip you wish to occur e.g. 1 = 1% and 100 = 100%. Note: If selling base/hitting the bids you can only have a max value of 100%",
|
||||
}),
|
||||
}
|
||||
|
||||
func getNominal(c *cli.Context) error {
|
||||
isSelling := c.Bool("sell")
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, "nominal")
|
||||
}
|
||||
|
||||
var exchangeName string
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
var currencyPair string
|
||||
if c.IsSet("pair") {
|
||||
currencyPair = c.String("pair")
|
||||
} else {
|
||||
currencyPair = c.Args().Get(1)
|
||||
}
|
||||
|
||||
if !validPair(currencyPair) {
|
||||
return errInvalidPair
|
||||
}
|
||||
|
||||
var assetType string
|
||||
if c.IsSet("asset") {
|
||||
assetType = c.String("asset")
|
||||
} else {
|
||||
assetType = c.Args().Get(2)
|
||||
}
|
||||
|
||||
assetType = strings.ToLower(assetType)
|
||||
if !validAsset(assetType) {
|
||||
return errInvalidAsset
|
||||
}
|
||||
|
||||
p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var percentage float64
|
||||
if c.IsSet("asset") {
|
||||
percentage = c.Float64("percent")
|
||||
} else {
|
||||
percentage, _ = strconv.ParseFloat(c.Args().Get(3), 64)
|
||||
}
|
||||
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.GetOrderbookAmountByNominal(c.Context,
|
||||
&gctrpc.GetOrderbookAmountByNominalRequest{
|
||||
Exchange: exchangeName,
|
||||
Pair: &gctrpc.CurrencyPair{
|
||||
Base: p.Base.String(),
|
||||
Quote: p.Quote.String(),
|
||||
},
|
||||
Asset: assetType,
|
||||
Sell: isSelling,
|
||||
NominalPercentage: percentage,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
var impact = &cli.Command{
|
||||
Name: "impact",
|
||||
Usage: "simulates a buy or sell based off the reference price and the orderbook impact slippage",
|
||||
ArgsUsage: "<exchange> <pair> <asset> <percent>",
|
||||
Action: getImpact,
|
||||
Flags: append(orderbookCommonFlags, &cli.Float64Flag{
|
||||
Name: "percent",
|
||||
Usage: "the max percentage slip you wish to occur e.g. 1 = 1% and 100 = 100%. Note: If selling base/hitting the bids you can only have a max value of 100%",
|
||||
}),
|
||||
}
|
||||
|
||||
func getImpact(c *cli.Context) error {
|
||||
isSelling := c.Bool("sell")
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, "impact")
|
||||
}
|
||||
|
||||
var exchangeName string
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
var currencyPair string
|
||||
if c.IsSet("pair") {
|
||||
currencyPair = c.String("pair")
|
||||
} else {
|
||||
currencyPair = c.Args().Get(1)
|
||||
}
|
||||
|
||||
if !validPair(currencyPair) {
|
||||
return errInvalidPair
|
||||
}
|
||||
|
||||
var assetType string
|
||||
if c.IsSet("asset") {
|
||||
assetType = c.String("asset")
|
||||
} else {
|
||||
assetType = c.Args().Get(2)
|
||||
}
|
||||
|
||||
assetType = strings.ToLower(assetType)
|
||||
if !validAsset(assetType) {
|
||||
return errInvalidAsset
|
||||
}
|
||||
|
||||
p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var percentage float64
|
||||
if c.IsSet("asset") {
|
||||
percentage = c.Float64("percent")
|
||||
} else {
|
||||
percentage, _ = strconv.ParseFloat(c.Args().Get(3), 64)
|
||||
}
|
||||
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.GetOrderbookAmountByImpact(c.Context,
|
||||
&gctrpc.GetOrderbookAmountByImpactRequest{
|
||||
Exchange: exchangeName,
|
||||
Pair: &gctrpc.CurrencyPair{
|
||||
Base: p.Base.String(),
|
||||
Quote: p.Quote.String(),
|
||||
},
|
||||
Asset: assetType,
|
||||
Sell: isSelling,
|
||||
ImpactPercentage: percentage,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
var purchase = &cli.BoolFlag{
|
||||
Name: "purchase",
|
||||
Hidden: true,
|
||||
Value: true,
|
||||
}
|
||||
|
||||
var quote = &cli.Command{
|
||||
Name: "quote",
|
||||
Usage: "simulates a buy using quotation amount",
|
||||
ArgsUsage: "<exchange> <pair> <asset> <amount>",
|
||||
Action: getMovement,
|
||||
Flags: append(orderbookCommonFlags, &cli.Float64Flag{
|
||||
Name: "amount",
|
||||
Usage: "the amount of quotation currency lifting the asks",
|
||||
}),
|
||||
}
|
||||
|
||||
var baseRequired = &cli.Command{
|
||||
Name: "baserequired",
|
||||
Usage: "simulates a buy with a required base amount to be purchased",
|
||||
ArgsUsage: "<exchange> <pair> <asset> <amount>",
|
||||
Action: getMovement,
|
||||
Flags: append(orderbookCommonFlags, &cli.Float64Flag{
|
||||
Name: "amount",
|
||||
Usage: "the amount of base currency required to be purchased when lifting the asks",
|
||||
}, purchase),
|
||||
}
|
||||
|
||||
var base = &cli.Command{
|
||||
Name: "base",
|
||||
Usage: "simulates a sell using base amount",
|
||||
ArgsUsage: "<exchange> <pair> <asset> <amount>",
|
||||
Action: getMovement,
|
||||
Flags: append(orderbookCommonFlags, &cli.Float64Flag{
|
||||
Name: "amount",
|
||||
Usage: "the amount of base currency hitting the bids",
|
||||
}),
|
||||
}
|
||||
|
||||
var quoteRequired = &cli.Command{
|
||||
Name: "quoterequired",
|
||||
Usage: "simulates a sell with a required quote amount to be purchased",
|
||||
ArgsUsage: "<exchange> <pair> <asset> <amount>",
|
||||
Action: getMovement,
|
||||
Flags: append(orderbookCommonFlags, &cli.Float64Flag{
|
||||
Name: "amount",
|
||||
Usage: "the amount of quotation currency required to be purchased when hitting the bids",
|
||||
}, purchase),
|
||||
}
|
||||
|
||||
func getMovement(c *cli.Context) error {
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, c.Command.Name)
|
||||
}
|
||||
|
||||
var exchangeName string
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
var currencyPair string
|
||||
if c.IsSet("pair") {
|
||||
currencyPair = c.String("pair")
|
||||
} else {
|
||||
currencyPair = c.Args().Get(1)
|
||||
}
|
||||
|
||||
if !validPair(currencyPair) {
|
||||
return errInvalidPair
|
||||
}
|
||||
|
||||
var assetType string
|
||||
if c.IsSet("asset") {
|
||||
assetType = c.String("asset")
|
||||
} else {
|
||||
assetType = c.Args().Get(2)
|
||||
}
|
||||
|
||||
assetType = strings.ToLower(assetType)
|
||||
if !validAsset(assetType) {
|
||||
return errInvalidAsset
|
||||
}
|
||||
|
||||
p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var amount float64
|
||||
if c.IsSet("amount") {
|
||||
amount = c.Float64("amount")
|
||||
} else {
|
||||
amount, _ = strconv.ParseFloat(c.Args().Get(3), 64)
|
||||
}
|
||||
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.GetOrderbookMovement(c.Context, &gctrpc.GetOrderbookMovementRequest{
|
||||
Exchange: exchangeName,
|
||||
Pair: &gctrpc.CurrencyPair{
|
||||
Base: p.Base.String(),
|
||||
Quote: p.Quote.String(),
|
||||
},
|
||||
Asset: assetType,
|
||||
Sell: c.Bool("sell"),
|
||||
Amount: amount,
|
||||
Purchase: c.Bool("purchase"),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var getOrderbookCommand = &cli.Command{
|
||||
Name: "getorderbook",
|
||||
Usage: "gets the orderbook for a specific currency pair and exchange",
|
||||
ArgsUsage: "<exchange> <pair> <asset>",
|
||||
Action: getOrderbook,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Usage: "the exchange to get the orderbook for",
|
||||
},
|
||||
&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",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func getOrderbook(c *cli.Context) error {
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, "getorderbook")
|
||||
}
|
||||
|
||||
var exchangeName string
|
||||
var currencyPair string
|
||||
var assetType string
|
||||
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
if c.IsSet("pair") {
|
||||
currencyPair = c.String("pair")
|
||||
} else {
|
||||
currencyPair = c.Args().Get(1)
|
||||
}
|
||||
|
||||
if !validPair(currencyPair) {
|
||||
return errInvalidPair
|
||||
}
|
||||
|
||||
if c.IsSet("asset") {
|
||||
assetType = c.String("asset")
|
||||
} else {
|
||||
assetType = c.Args().Get(2)
|
||||
}
|
||||
|
||||
assetType = strings.ToLower(assetType)
|
||||
if !validAsset(assetType) {
|
||||
return errInvalidAsset
|
||||
}
|
||||
|
||||
p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.GetOrderbook(c.Context,
|
||||
&gctrpc.GetOrderbookRequest{
|
||||
Exchange: exchangeName,
|
||||
Pair: &gctrpc.CurrencyPair{
|
||||
Delimiter: p.Delimiter,
|
||||
Base: p.Base.String(),
|
||||
Quote: p.Quote.String(),
|
||||
},
|
||||
AssetType: assetType,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
var getOrderbooksCommand = &cli.Command{
|
||||
Name: "getorderbooks",
|
||||
Usage: "gets all orderbooks for all enabled exchanges and currency pairs",
|
||||
Action: getOrderbooks,
|
||||
}
|
||||
|
||||
func getOrderbooks(c *cli.Context) error {
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.GetOrderbooks(c.Context, &gctrpc.GetOrderbooksRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
var getOrderbookStreamCommand = &cli.Command{
|
||||
Name: "getorderbookstream",
|
||||
Usage: "gets the orderbook stream for a specific currency pair and exchange",
|
||||
ArgsUsage: "<exchange> <pair> <asset>",
|
||||
Action: getOrderbookStream,
|
||||
Flags: orderbookCommonFlags,
|
||||
}
|
||||
|
||||
func getOrderbookStream(c *cli.Context) error {
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, "getorderbookstream")
|
||||
}
|
||||
|
||||
var exchangeName string
|
||||
var pair string
|
||||
var assetType string
|
||||
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
if c.IsSet("pair") {
|
||||
pair = c.String("pair")
|
||||
} else {
|
||||
pair = c.Args().Get(1)
|
||||
}
|
||||
|
||||
if !validPair(pair) {
|
||||
return errInvalidPair
|
||||
}
|
||||
|
||||
if c.IsSet("asset") {
|
||||
assetType = c.String("asset")
|
||||
} else {
|
||||
assetType = c.Args().Get(2)
|
||||
}
|
||||
|
||||
assetType = strings.ToLower(assetType)
|
||||
|
||||
if !validAsset(assetType) {
|
||||
return errInvalidAsset
|
||||
}
|
||||
|
||||
p, err := currency.NewPairDelimiter(pair, pairDelimiter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.GetOrderbookStream(c.Context,
|
||||
&gctrpc.GetOrderbookStreamRequest{
|
||||
Exchange: exchangeName,
|
||||
Pair: &gctrpc.CurrencyPair{
|
||||
Base: p.Base.String(),
|
||||
Quote: p.Quote.String(),
|
||||
Delimiter: p.Delimiter,
|
||||
},
|
||||
AssetType: assetType,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
resp, err := result.Recv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = clearScreen()
|
||||
if err != nil {
|
||||
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 := len(resp.Bids) - 1
|
||||
askLen := len(resp.Asks) - 1
|
||||
|
||||
var maxLen int
|
||||
if bidLen >= askLen {
|
||||
maxLen = bidLen
|
||||
} else {
|
||||
maxLen = askLen
|
||||
}
|
||||
|
||||
for i := 0; i < maxLen; i++ {
|
||||
var bidAmount, bidPrice float64
|
||||
if i <= bidLen {
|
||||
bidAmount = resp.Bids[i].Amount
|
||||
bidPrice = resp.Bids[i].Price
|
||||
}
|
||||
|
||||
var askAmount, askPrice float64
|
||||
if i <= askLen {
|
||||
askAmount = resp.Asks[i].Amount
|
||||
askPrice = resp.Asks[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)
|
||||
|
||||
if i >= 49 {
|
||||
// limits orderbook display output
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var getExchangeOrderbookStreamCommand = &cli.Command{
|
||||
Name: "getexchangeorderbookstream",
|
||||
Usage: "gets a stream for all orderbooks associated with an exchange",
|
||||
ArgsUsage: "<exchange>",
|
||||
Action: getExchangeOrderbookStream,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Usage: "the exchange to get the orderbook from",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func getExchangeOrderbookStream(c *cli.Context) error {
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, "getexchangeorderbookstream")
|
||||
}
|
||||
|
||||
var exchangeName string
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.GetExchangeOrderbookStream(c.Context,
|
||||
&gctrpc.GetExchangeOrderbookStreamRequest{
|
||||
Exchange: exchangeName,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
resp, err := result.Recv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = clearScreen()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Orderbook streamed for %s %s", exchangeName, resp.Pair)
|
||||
if resp.Error != "" {
|
||||
fmt.Printf("%s\n", resp.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var whaleBombCommand = &cli.Command{
|
||||
Name: "whalebomb",
|
||||
Usage: "whale bomb finds the amount required to reach a price target",
|
||||
ArgsUsage: "<exchange> <pair> <side> <asset> <price>",
|
||||
Action: whaleBomb,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Usage: "the exchange to whale bomb",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "pair",
|
||||
Usage: "the currency pair",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "side",
|
||||
Usage: "the order side to use (BUY OR SELL)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "asset",
|
||||
Usage: "the asset type of the currency pair to get the orderbook for",
|
||||
},
|
||||
&cli.Float64Flag{
|
||||
Name: "price",
|
||||
Usage: "the price target",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func whaleBomb(c *cli.Context) error {
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, "whalebomb")
|
||||
}
|
||||
|
||||
var exchangeName string
|
||||
var currencyPair string
|
||||
var orderSide string
|
||||
var price float64
|
||||
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
if c.IsSet("pair") {
|
||||
currencyPair = c.String("pair")
|
||||
} else {
|
||||
currencyPair = c.Args().Get(1)
|
||||
}
|
||||
|
||||
if !validPair(currencyPair) {
|
||||
return errInvalidPair
|
||||
}
|
||||
|
||||
if c.IsSet("side") {
|
||||
orderSide = c.String("side")
|
||||
} else {
|
||||
orderSide = c.Args().Get(2)
|
||||
}
|
||||
|
||||
if orderSide == "" {
|
||||
return errors.New("order side must be set")
|
||||
}
|
||||
|
||||
var assetType string
|
||||
if c.IsSet("asset") {
|
||||
assetType = c.String("asset")
|
||||
} else {
|
||||
assetType = c.Args().Get(3)
|
||||
}
|
||||
|
||||
if c.IsSet("price") {
|
||||
price = c.Float64("price")
|
||||
} else if c.Args().Get(4) != "" {
|
||||
var err error
|
||||
price, err = strconv.ParseFloat(c.Args().Get(4), 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.WhaleBomb(c.Context, &gctrpc.WhaleBombRequest{
|
||||
Exchange: exchangeName,
|
||||
Pair: &gctrpc.CurrencyPair{
|
||||
Delimiter: p.Delimiter,
|
||||
Base: p.Base.String(),
|
||||
Quote: p.Quote.String(),
|
||||
},
|
||||
Side: orderSide,
|
||||
PriceTarget: price,
|
||||
AssetType: assetType,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user