mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-06 15:10:59 +00:00
FTX: Funding rates, payments & stats + order manager tracking (#976)
* Adds basic PoC for calculating/retrieving position data * A very unfortunate day of miscalculations * Adds position summary and funding rate details to RPC * Offline funding rate calculations * More helpers, more stats, refining data, automated retrieval * Adds new rpc server commands and attempts some organisation * lower string, lower stress * Adds ordermanager config. Fleshes outcli. Tracks positions automatically * Adds new separation for funding payments/rates * Combines funding rates and payments * Fun test coverage * ALL THE TESTS... I hope * Fixes * polishes ftx tests. improves perp check. Loops rates * Final touches before nit attax * buff 💪 * Stops NotYetImplemented spam with one simple trick! * Some lovely little niteroos * linteroo * Clarifies a couple of errors to help narrow likely end user problems * Fixes asset type bug, fixes closed position order return, fixes unset status bug * Fixes order manager handling when no rates are available yet * Continues on no funding rates instead. Removes err * Don't show predicted rate if the time is zero * Addresses scenario with no funding rate payments * Bug fixes and commentary before updating maps to use *currency.Item * Adds a pair key type * Polishes pKey, fixes map order bug * key is not a property in the event someone changes the base/quote * Adds improvements to order processing...Breaks it all * Shakes up the design of things by removing a function * Fixes issues with order manager positions. Limits update range * Fixes build issues. Identification of bad tests. * Merges and fixes features from master and this branch * buff linter 💪 * re-gen * proto regen * Addresses some nits. But not all of them. * Fixes issue where funding rates weren't returned 🎉 * completes transition futures tracking to map[*currency.Item]map[*currency.Item] * who did that? not me * removes redundant check on account of being redundant and unnecessary * so buf * addresses nits: duplications, startTime, loops, go tidy, typos * fixes minor mistakes * fixes 🍣 🐻 changes to int64
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/file"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
@@ -508,6 +509,41 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config)
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{""}),
|
||||
})
|
||||
|
||||
fundingRateRequest := &order.FundingRatesRequest{
|
||||
Asset: assetTypes[i],
|
||||
Pairs: currency.Pairs{p},
|
||||
StartDate: time.Now().Add(-time.Hour),
|
||||
EndDate: time.Now(),
|
||||
}
|
||||
var fundingRateResponse []order.FundingRates
|
||||
fundingRateResponse, err = e.GetFundingRates(context.TODO(), fundingRateRequest)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{fundingRateRequest}),
|
||||
Function: "GetFundingRates",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{fundingRateResponse}),
|
||||
})
|
||||
|
||||
var isPerpetualFutures bool
|
||||
isPerpetualFutures, err = e.IsPerpetualFutureCurrency(assetTypes[i], p)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{assetTypes[i], p}),
|
||||
Function: "IsPerpetualFutureCurrency",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{isPerpetualFutures}),
|
||||
})
|
||||
}
|
||||
|
||||
var fetchAccountInfoResponse account.Holdings
|
||||
@@ -665,7 +701,7 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config)
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{config.OrderSubmission.OrderID}),
|
||||
SentParams: jsonifyInterface([]interface{}{config.OrderSubmission.OrderID, p, assetTypes[i]}),
|
||||
Function: "GetOrderInfo",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r15}),
|
||||
@@ -676,6 +712,8 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config)
|
||||
Side: testOrderSide,
|
||||
Pairs: []currency.Pair{p},
|
||||
AssetType: assetTypes[i],
|
||||
StartTime: time.Now().Add(-time.Hour),
|
||||
EndTime: time.Now(),
|
||||
}
|
||||
var getOrderHistoryResponse []order.Detail
|
||||
getOrderHistoryResponse, err = e.GetOrderHistory(context.TODO(), &historyRequest)
|
||||
@@ -696,6 +734,8 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config)
|
||||
Side: testOrderSide,
|
||||
Pairs: []currency.Pair{p},
|
||||
AssetType: assetTypes[i],
|
||||
StartTime: time.Now().Add(-time.Hour),
|
||||
EndTime: time.Now(),
|
||||
}
|
||||
var getActiveOrdersResponse []order.Detail
|
||||
getActiveOrdersResponse, err = e.GetActiveOrders(context.TODO(), &orderRequest)
|
||||
@@ -872,6 +912,109 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config)
|
||||
Response: marginRateHistoryResponse,
|
||||
})
|
||||
|
||||
positionSummaryRequest := &order.PositionSummaryRequest{
|
||||
Asset: assetTypes[i],
|
||||
Pair: p,
|
||||
}
|
||||
var positionSummaryResponse *order.PositionSummary
|
||||
positionSummaryResponse, err = e.GetPositionSummary(context.TODO(), positionSummaryRequest)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{positionSummaryRequest}),
|
||||
Function: "GetPositionSummary",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{positionSummaryResponse}),
|
||||
})
|
||||
|
||||
calculatePNLRequest := &order.PNLCalculatorRequest{
|
||||
Pair: p,
|
||||
Underlying: p.Base,
|
||||
Asset: assetTypes[i],
|
||||
EntryPrice: decimal.NewFromInt(1337),
|
||||
OpeningDirection: testOrderSide,
|
||||
OrderDirection: testOrderSide,
|
||||
Time: time.Now(),
|
||||
Exposure: decimal.NewFromInt(1337),
|
||||
EntryAmount: decimal.NewFromInt(1337),
|
||||
PreviousPrice: decimal.NewFromInt(1337),
|
||||
}
|
||||
var calculatePNLResponse *order.PNLResult
|
||||
calculatePNLResponse, err = e.CalculatePNL(context.TODO(), calculatePNLRequest)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{calculatePNLRequest}),
|
||||
Function: "CalculatePNL",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{calculatePNLResponse}),
|
||||
})
|
||||
|
||||
collateralCalculator := &order.CollateralCalculator{
|
||||
CollateralCurrency: p.Quote,
|
||||
Asset: assetTypes[i],
|
||||
Side: testOrderSide,
|
||||
USDPrice: decimal.NewFromInt(1337),
|
||||
FreeCollateral: decimal.NewFromInt(1337),
|
||||
LockedCollateral: decimal.NewFromInt(1337),
|
||||
UnrealisedPNL: decimal.NewFromInt(1337),
|
||||
}
|
||||
var scaleCollateralResponse *order.CollateralByCurrency
|
||||
scaleCollateralResponse, err = e.ScaleCollateral(context.TODO(), collateralCalculator)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{collateralCalculator}),
|
||||
Function: "ScaleCollateral",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{scaleCollateralResponse}),
|
||||
})
|
||||
|
||||
totalCollateralCalculator := &order.TotalCollateralCalculator{
|
||||
CollateralAssets: []order.CollateralCalculator{*collateralCalculator},
|
||||
}
|
||||
var calculateTotalCollateralResponse *order.TotalCollateralResponse
|
||||
calculateTotalCollateralResponse, err = e.CalculateTotalCollateral(context.TODO(), totalCollateralCalculator)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{totalCollateralCalculator}),
|
||||
Function: "CalculateTotalCollateral",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{calculateTotalCollateralResponse}),
|
||||
})
|
||||
|
||||
var futuresPositionsResponse []order.PositionDetails
|
||||
futuresPositionsRequest := &order.PositionsRequest{
|
||||
Asset: assetTypes[i],
|
||||
Pairs: currency.Pairs{p},
|
||||
StartDate: time.Now().Add(-time.Hour),
|
||||
}
|
||||
futuresPositionsResponse, err = e.GetFuturesPositions(context.TODO(), futuresPositionsRequest)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{futuresPositionsRequest}),
|
||||
Function: "GetFuturesPositions",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{futuresPositionsResponse}),
|
||||
})
|
||||
|
||||
response = append(response, responseContainer)
|
||||
}
|
||||
return response
|
||||
|
||||
@@ -4742,316 +4742,6 @@ func findMissingSavedCandleIntervals(c *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var getFuturesPositionsCommand = &cli.Command{
|
||||
Name: "getfuturesposition",
|
||||
Usage: "will retrieve all futures positions in a timeframe, then calculate PNL based on that. Note, the dates have an impact on PNL calculations, ensure your start date is not after a new position is opened",
|
||||
ArgsUsage: "<exchange> <pair> <asset> <start> <end> <limit> <status> <verbose> <overwrite>",
|
||||
Action: getFuturesPositions,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Aliases: []string{"e"},
|
||||
Usage: "the exchange to retrieve futures positions from",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "asset",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "the asset type of the currency pair, must be a futures type",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "pair",
|
||||
Aliases: []string{"p"},
|
||||
Usage: "the currency pair",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "start",
|
||||
Aliases: []string{"sd"},
|
||||
Usage: "<start> rounded down to the nearest hour, ensure your starting position is within this window for accurate calculations",
|
||||
Value: time.Now().AddDate(-1, 0, 0).Truncate(time.Hour).Format(common.SimpleTimeFormat),
|
||||
Destination: &startTime,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "end",
|
||||
Aliases: []string{"ed"},
|
||||
Usage: "<end> rounded down to the nearest hour, ensure your last position is within this window for accurate calculations",
|
||||
Value: time.Now().Format(common.SimpleTimeFormat),
|
||||
Destination: &endTime,
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "limit",
|
||||
Aliases: []string{"l"},
|
||||
Usage: "the number of positions (not orders) to return",
|
||||
Value: 86400,
|
||||
Destination: &limit,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "status",
|
||||
Aliases: []string{"s"},
|
||||
Usage: "limit return to position statuses - open, closed, any",
|
||||
Value: "ANY",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "includes all orders that make up a position in the response",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "overwrite",
|
||||
Aliases: []string{"o"},
|
||||
Usage: "if true, will overwrite futures results for the provided exchange, asset, pair",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func getFuturesPositions(c *cli.Context) error {
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, "getfuturesposition")
|
||||
}
|
||||
|
||||
var exchangeName string
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
var assetType string
|
||||
if c.IsSet("asset") {
|
||||
assetType = c.String("asset")
|
||||
} else {
|
||||
assetType = c.Args().Get(1)
|
||||
}
|
||||
|
||||
if !validAsset(assetType) {
|
||||
return errInvalidAsset
|
||||
}
|
||||
|
||||
var currencyPair string
|
||||
if c.IsSet("pair") {
|
||||
currencyPair = c.String("pair")
|
||||
} else {
|
||||
currencyPair = c.Args().Get(2)
|
||||
}
|
||||
if !validPair(currencyPair) {
|
||||
return errInvalidPair
|
||||
}
|
||||
|
||||
p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !c.IsSet("start") {
|
||||
if c.Args().Get(3) != "" {
|
||||
startTime = c.Args().Get(3)
|
||||
}
|
||||
}
|
||||
|
||||
if !c.IsSet("end") {
|
||||
if c.Args().Get(4) != "" {
|
||||
endTime = c.Args().Get(4)
|
||||
}
|
||||
}
|
||||
if c.IsSet("limit") {
|
||||
limit = c.Int("limit")
|
||||
} else if c.Args().Get(5) != "" {
|
||||
var limit64 int64
|
||||
limit64, err = strconv.ParseInt(c.Args().Get(5), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
limit = int(limit64)
|
||||
}
|
||||
|
||||
var status string
|
||||
if c.IsSet("status") {
|
||||
status = c.String("status")
|
||||
} else if c.Args().Get(6) != "" {
|
||||
status = c.Args().Get(6)
|
||||
}
|
||||
if !strings.EqualFold(status, "any") &&
|
||||
!strings.EqualFold(status, "open") &&
|
||||
!strings.EqualFold(status, "closed") &&
|
||||
status != "" {
|
||||
return errors.New("unrecognised status")
|
||||
}
|
||||
|
||||
var verbose bool
|
||||
if c.IsSet("verbose") {
|
||||
verbose = c.Bool("verbose")
|
||||
} else if c.Args().Get(7) != "" {
|
||||
verbose, err = strconv.ParseBool(c.Args().Get(7))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var overwrite bool
|
||||
if c.IsSet("overwrite") {
|
||||
overwrite = c.Bool("overwrite")
|
||||
} else if c.Args().Get(8) != "" {
|
||||
overwrite, err = strconv.ParseBool(c.Args().Get(8))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var s, e time.Time
|
||||
s, err = time.Parse(common.SimpleTimeFormat, startTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid time format for start: %v", err)
|
||||
}
|
||||
e, err = time.Parse(common.SimpleTimeFormat, endTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid time format for end: %v", err)
|
||||
}
|
||||
|
||||
if e.Before(s) {
|
||||
return errors.New("start cannot be after end")
|
||||
}
|
||||
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.GetFuturesPositions(c.Context,
|
||||
&gctrpc.GetFuturesPositionsRequest{
|
||||
Exchange: exchangeName,
|
||||
Asset: assetType,
|
||||
Pair: &gctrpc.CurrencyPair{
|
||||
Delimiter: p.Delimiter,
|
||||
Base: p.Base.String(),
|
||||
Quote: p.Quote.String(),
|
||||
},
|
||||
StartDate: negateLocalOffset(s),
|
||||
EndDate: negateLocalOffset(e),
|
||||
Status: status,
|
||||
PositionLimit: int64(limit),
|
||||
Verbose: verbose,
|
||||
Overwrite: overwrite,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
var getCollateralCommand = &cli.Command{
|
||||
Name: "getcollateral",
|
||||
Usage: "returns total collateral for an exchange asset, with optional per currency breakdown",
|
||||
ArgsUsage: "<exchange> <asset> <calculateoffline> <includebreakdown> <includezerovalues>",
|
||||
Action: getCollateral,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Aliases: []string{"e"},
|
||||
Usage: "the exchange to retrieve futures positions from",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "asset",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "the asset type of the currency pair, must be a futures type",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "calculateoffline",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "use local scaling calculations instead of requesting the collateral values directly, depending on individual exchange support",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "includebreakdown",
|
||||
Aliases: []string{"i"},
|
||||
Usage: "include a list of each held currency and its contribution to the overall collateral value",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "includezerovalues",
|
||||
Aliases: []string{"z"},
|
||||
Usage: "include collateral values that are zero",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func getCollateral(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 assetType string
|
||||
if c.IsSet("asset") {
|
||||
assetType = c.String("asset")
|
||||
} else {
|
||||
assetType = c.Args().Get(1)
|
||||
}
|
||||
|
||||
if !validAsset(assetType) {
|
||||
return errInvalidAsset
|
||||
}
|
||||
|
||||
var err error
|
||||
var calculateOffline bool
|
||||
if c.IsSet("calculateoffline") {
|
||||
calculateOffline = c.Bool("calculateoffline")
|
||||
} else if c.Args().Get(2) != "" {
|
||||
calculateOffline, err = strconv.ParseBool(c.Args().Get(2))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var includeBreakdown bool
|
||||
if c.IsSet("includebreakdown") {
|
||||
includeBreakdown = c.Bool("includebreakdown")
|
||||
} else if c.Args().Get(3) != "" {
|
||||
includeBreakdown, err = strconv.ParseBool(c.Args().Get(3))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var includeZeroValues bool
|
||||
if c.IsSet("includezerovalues") {
|
||||
includeZeroValues = c.Bool("includezerovalues")
|
||||
} else if c.Args().Get(4) != "" {
|
||||
includeZeroValues, err = strconv.ParseBool(c.Args().Get(4))
|
||||
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.GetCollateral(c.Context,
|
||||
&gctrpc.GetCollateralRequest{
|
||||
Exchange: exchangeName,
|
||||
Asset: assetType,
|
||||
IncludeBreakdown: includeBreakdown,
|
||||
CalculateOffline: calculateOffline,
|
||||
IncludeZeroValues: includeZeroValues,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
var shutdownCommand = &cli.Command{
|
||||
Name: "shutdown",
|
||||
Usage: "shuts down bot instance",
|
||||
|
||||
813
cmd/gctcli/futures.go
Normal file
813
cmd/gctcli/futures.go
Normal file
@@ -0,0 +1,813 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/gctrpc"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// futuresCommands contains all commands related to futures
|
||||
// position data, funding rates, collateral, pnl etc
|
||||
var futuresCommands = &cli.Command{
|
||||
Name: "futures",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "contains all futures based rpc commands",
|
||||
ArgsUsage: "<command> <args>",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "getmanagedposition",
|
||||
Aliases: []string{"managedposition", "mp"},
|
||||
Usage: "retrieves an open position monitored by the order manager",
|
||||
ArgsUsage: "<exchange> <asset> <pair> <includeorderdetails> <getfundingdata> <includefundingentries> <includepredictedrate>",
|
||||
Action: getManagedPosition,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Aliases: []string{"e"},
|
||||
Usage: "the exchange to retrieve futures positions from",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "asset",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "the asset type of the currency pair, must be a futures type",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "pair",
|
||||
Aliases: []string{"p"},
|
||||
Usage: "the currency pair of the position",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "includeorderdetails",
|
||||
Aliases: []string{"orders"},
|
||||
Usage: "includes all orders that make up a position in the response",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "getfundingdata",
|
||||
Aliases: []string{"funding", "fd"},
|
||||
Usage: "if true, will return funding rate summary",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "includefundingentries",
|
||||
Aliases: []string{"allfunding", "af"},
|
||||
Usage: "if true, will return all funding rate entries - requires --getfundingdata",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "includepredictedrate",
|
||||
Aliases: []string{"predicted", "pr"},
|
||||
Usage: "if true, will return the predicted funding rate - requires --getfundingdata",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "getallmanagedpositions",
|
||||
Aliases: []string{"managedpositions", "mps"},
|
||||
Usage: "retrieves all open positions monitored by the order manager",
|
||||
ArgsUsage: "<includeorderdetails> <getfundingdata> <includefundingentries> <includepredictedrate>",
|
||||
Action: getAllManagedPositions,
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "includeorderdetails",
|
||||
Aliases: []string{"orders"},
|
||||
Usage: "includes all orders that make up a position in the response",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "getfundingdata",
|
||||
Aliases: []string{"funding", "fd"},
|
||||
Usage: "if true, will return funding rate summary",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "includefundingentries",
|
||||
Aliases: []string{"allfunding", "af"},
|
||||
Usage: "if true, will return all funding rate entries - requires --getfundingdata",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "includepredictedrate",
|
||||
Aliases: []string{"predicted", "pr"},
|
||||
Usage: "if true, will return the predicted funding rate - requires --getfundingdata",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "getfuturespositions",
|
||||
Aliases: []string{"positions", "p"},
|
||||
Usage: "will retrieve all futures positions in a timeframe, then calculate PNL based on that. Note, the dates have an impact on PNL calculations, ensure your start date is not after a new position is opened",
|
||||
ArgsUsage: "<exchange> <asset> <pair> <start> <end> <limit> <status> <overwrite> <includeorderdetails> <getpositionstats> <getfundingdata> <includefundingentries> <includepredictedrate>",
|
||||
Action: getFuturesPositions,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Aliases: []string{"e"},
|
||||
Usage: "the exchange to retrieve futures positions from",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "asset",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "the asset type of the currency pair, must be a futures type",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "pair",
|
||||
Aliases: []string{"p"},
|
||||
Usage: "the currency pair",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "start",
|
||||
Aliases: []string{"sd"},
|
||||
Usage: "<start> rounded down to the nearest hour, ensure your starting position is within this window for accurate calculations",
|
||||
Value: time.Now().AddDate(-1, 0, 0).Truncate(time.Hour).Format(common.SimpleTimeFormat),
|
||||
Destination: &startTime,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "end",
|
||||
Aliases: []string{"ed"},
|
||||
Usage: "<end> rounded down to the nearest hour, ensure your last position is within this window for accurate calculations",
|
||||
Value: time.Now().Format(common.SimpleTimeFormat),
|
||||
Destination: &endTime,
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "limit",
|
||||
Aliases: []string{"l"},
|
||||
Usage: "the number of positions (not orders) to return",
|
||||
Value: 86400,
|
||||
Destination: &limit,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "status",
|
||||
Aliases: []string{"s"},
|
||||
Usage: "limit return to position statuses - open, closed, any",
|
||||
Value: "ANY",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "overwrite",
|
||||
Aliases: []string{"o"},
|
||||
Usage: "if true, will overwrite futures results for the provided exchange, asset, pair",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "includeorderdetails",
|
||||
Aliases: []string{"orders"},
|
||||
Usage: "includes all orders that make up a position in the response",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "getpositionstats",
|
||||
Aliases: []string{"stats"},
|
||||
Usage: "if true, will return extra stats on the position from the exchange",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "getfundingdata",
|
||||
Aliases: []string{"funding", "fd"},
|
||||
Usage: "if true, will return funding rate summary",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "includefundingentries",
|
||||
Aliases: []string{"allfunding", "af"},
|
||||
Usage: "if true, will return all funding rate entries - requires --getfundingdata",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "includepredictedrate",
|
||||
Aliases: []string{"predicted", "pr"},
|
||||
Usage: "if true, will return the predicted funding rate - requires --getfundingdata",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "getcollateral",
|
||||
Aliases: []string{"collateral", "c"},
|
||||
Usage: "returns total collateral for an exchange asset, with optional per currency breakdown",
|
||||
ArgsUsage: "<exchange> <asset> <calculateoffline> <includebreakdown> <includezerovalues>",
|
||||
Action: getCollateral,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Aliases: []string{"e"},
|
||||
Usage: "the exchange to retrieve futures positions from",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "asset",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "the asset type of the currency pair, must be a futures type",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "calculateoffline",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "use local scaling calculations instead of requesting the collateral values directly, depending on individual exchange support",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "includebreakdown",
|
||||
Aliases: []string{"i"},
|
||||
Usage: "include a list of each held currency and its contribution to the overall collateral value",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "includezerovalues",
|
||||
Aliases: []string{"z"},
|
||||
Usage: "include collateral values that are zero",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "getfundingrates",
|
||||
Aliases: []string{"funding", "f"},
|
||||
Usage: "returns funding rate data between two dates",
|
||||
ArgsUsage: "<exchange> <asset> <pairs> <start> <end> <includepredicted> <includepayments>",
|
||||
Action: getFundingRates,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Aliases: []string{"e"},
|
||||
Usage: "the exchange to retrieve futures positions from",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "asset",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "the asset type of the currency pair, must be a futures type",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "pairs",
|
||||
Aliases: []string{"p"},
|
||||
Usage: "comma delimited list of pairs you wish to get funding rate data for",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "start",
|
||||
Aliases: []string{"sd"},
|
||||
Usage: "<start> rounded down to the nearest hour, ensure your starting position is within this window for accurate calculations",
|
||||
Value: time.Now().AddDate(-1, 0, 0).Truncate(time.Hour).Format(common.SimpleTimeFormat),
|
||||
Destination: &startTime,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "end",
|
||||
Aliases: []string{"ed"},
|
||||
Usage: "<end> rounded down to the nearest hour, ensure your last position is within this window for accurate calculations",
|
||||
Value: time.Now().Format(common.SimpleTimeFormat),
|
||||
Destination: &endTime,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "includepredicted",
|
||||
Aliases: []string{"ip", "predicted"},
|
||||
Usage: "include the predicted next funding rate",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "includepayments",
|
||||
Aliases: []string{"pay"},
|
||||
Usage: "include funding rate payments",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func getManagedPosition(c *cli.Context) error {
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, "getmanagedposition")
|
||||
}
|
||||
|
||||
var exchangeName string
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
var assetType string
|
||||
if c.IsSet("asset") {
|
||||
assetType = c.String("asset")
|
||||
} else {
|
||||
assetType = c.Args().Get(1)
|
||||
}
|
||||
err := isFuturesAsset(assetType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var currencyPair string
|
||||
if c.IsSet("pair") {
|
||||
currencyPair = c.String("pair")
|
||||
} else {
|
||||
currencyPair = c.Args().Get(2)
|
||||
}
|
||||
if !validPair(currencyPair) {
|
||||
return errInvalidPair
|
||||
}
|
||||
|
||||
p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var includeOrderDetails bool
|
||||
if c.IsSet("includeorderdetails") {
|
||||
includeOrderDetails = c.Bool("includeorderdetails")
|
||||
} else if c.Args().Get(3) != "" {
|
||||
includeOrderDetails, err = strconv.ParseBool(c.Args().Get(3))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var getFundingData bool
|
||||
if c.IsSet("getfundingdata") {
|
||||
getFundingData = c.Bool("getfundingdata")
|
||||
} else if c.Args().Get(4) != "" {
|
||||
getFundingData, err = strconv.ParseBool(c.Args().Get(4))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var includeFundingEntries bool
|
||||
if c.IsSet("includefundingentries") {
|
||||
includeFundingEntries = c.Bool("includefundingentries")
|
||||
} else if c.Args().Get(5) != "" {
|
||||
includeFundingEntries, err = strconv.ParseBool(c.Args().Get(5))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var includePredictedRate bool
|
||||
if c.IsSet("includepredictedrate") {
|
||||
includePredictedRate = c.Bool("includepredictedrate")
|
||||
} else if c.Args().Get(6) != "" {
|
||||
includePredictedRate, err = strconv.ParseBool(c.Args().Get(6))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = order.CheckFundingRatePrerequisites(getFundingData, includePredictedRate, includeFundingEntries)
|
||||
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.GetManagedPosition(c.Context,
|
||||
&gctrpc.GetManagedPositionRequest{
|
||||
Exchange: exchangeName,
|
||||
Asset: assetType,
|
||||
Pair: &gctrpc.CurrencyPair{
|
||||
Delimiter: p.Delimiter,
|
||||
Base: p.Base.String(),
|
||||
Quote: p.Quote.String(),
|
||||
},
|
||||
IncludeFullOrderData: includeOrderDetails,
|
||||
GetFundingPayments: getFundingData,
|
||||
IncludeFullFundingRates: includeFundingEntries,
|
||||
IncludePredictedRate: includePredictedRate,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAllManagedPositions(c *cli.Context) error {
|
||||
var (
|
||||
err error
|
||||
includeOrderDetails bool
|
||||
getFundingData bool
|
||||
includeFundingEntries bool
|
||||
includePredictedRate bool
|
||||
)
|
||||
if c.IsSet("includeorderdetails") {
|
||||
includeOrderDetails = c.Bool("includeorderdetails")
|
||||
} else if c.Args().Get(0) != "" {
|
||||
includeOrderDetails, err = strconv.ParseBool(c.Args().Get(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.IsSet("getfundingdata") {
|
||||
getFundingData = c.Bool("getfundingdata")
|
||||
} else if c.Args().Get(1) != "" {
|
||||
getFundingData, err = strconv.ParseBool(c.Args().Get(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.IsSet("includefundingentries") {
|
||||
includeFundingEntries = c.Bool("includefundingentries")
|
||||
} else if c.Args().Get(2) != "" {
|
||||
includeFundingEntries, err = strconv.ParseBool(c.Args().Get(2))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.IsSet("includepredictedrate") {
|
||||
includePredictedRate = c.Bool("includepredictedrate")
|
||||
} else if c.Args().Get(2) != "" {
|
||||
includePredictedRate, err = strconv.ParseBool(c.Args().Get(3))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = order.CheckFundingRatePrerequisites(getFundingData, includePredictedRate, includeFundingEntries)
|
||||
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.GetAllManagedPositions(c.Context,
|
||||
&gctrpc.GetAllManagedPositionsRequest{
|
||||
IncludeFullOrderData: includeOrderDetails,
|
||||
GetFundingPayments: getFundingData,
|
||||
IncludeFullFundingRates: includeFundingEntries,
|
||||
IncludePredictedRate: includePredictedRate,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFuturesPositions(c *cli.Context) error {
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, "getfuturespositions")
|
||||
}
|
||||
var (
|
||||
exchangeName string
|
||||
assetType string
|
||||
currencyPair string
|
||||
err error
|
||||
includeOrderDetails bool
|
||||
status string
|
||||
overwrite bool
|
||||
getFundingData bool
|
||||
includeFundingEntries bool
|
||||
getPositionsStats bool
|
||||
includePredicted bool
|
||||
s, e time.Time
|
||||
)
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
if c.IsSet("asset") {
|
||||
assetType = c.String("asset")
|
||||
} else {
|
||||
assetType = c.Args().Get(1)
|
||||
}
|
||||
|
||||
err = isFuturesAsset(assetType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.IsSet("pair") {
|
||||
currencyPair = c.String("pair")
|
||||
} else {
|
||||
currencyPair = c.Args().Get(2)
|
||||
}
|
||||
if !validPair(currencyPair) {
|
||||
return errInvalidPair
|
||||
}
|
||||
|
||||
p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !c.IsSet("start") {
|
||||
if c.Args().Get(3) != "" {
|
||||
startTime = c.Args().Get(3)
|
||||
}
|
||||
}
|
||||
|
||||
if !c.IsSet("end") {
|
||||
if c.Args().Get(4) != "" {
|
||||
endTime = c.Args().Get(4)
|
||||
}
|
||||
}
|
||||
if c.IsSet("limit") {
|
||||
limit = c.Int("limit")
|
||||
} else if c.Args().Get(5) != "" {
|
||||
var limit64 int64
|
||||
limit64, err = strconv.ParseInt(c.Args().Get(5), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
limit = int(limit64)
|
||||
}
|
||||
if limit <= 0 {
|
||||
return errors.New("limit must be greater than 0")
|
||||
}
|
||||
|
||||
if c.IsSet("status") {
|
||||
status = c.String("status")
|
||||
} else if c.Args().Get(6) != "" {
|
||||
status = c.Args().Get(6)
|
||||
}
|
||||
if !strings.EqualFold(status, "any") &&
|
||||
!strings.EqualFold(status, "open") &&
|
||||
!strings.EqualFold(status, "closed") &&
|
||||
status != "" {
|
||||
return errors.New("unrecognised status")
|
||||
}
|
||||
|
||||
if c.IsSet("overwrite") {
|
||||
overwrite = c.Bool("overwrite")
|
||||
} else if c.Args().Get(7) != "" {
|
||||
overwrite, err = strconv.ParseBool(c.Args().Get(7))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.IsSet("includeorderdetails") {
|
||||
includeOrderDetails = c.Bool("includeorderdetails")
|
||||
} else if c.Args().Get(8) != "" {
|
||||
includeOrderDetails, err = strconv.ParseBool(c.Args().Get(8))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.IsSet("getpositionstats") {
|
||||
getPositionsStats = c.Bool("getpositionstats")
|
||||
} else if c.Args().Get(9) != "" {
|
||||
getPositionsStats, err = strconv.ParseBool(c.Args().Get(9))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.IsSet("getfundingdata") {
|
||||
getFundingData = c.Bool("getfundingdata")
|
||||
} else if c.Args().Get(10) != "" {
|
||||
getFundingData, err = strconv.ParseBool(c.Args().Get(10))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.IsSet("includefundingentries") {
|
||||
includeFundingEntries = c.Bool("includefundingentries")
|
||||
} else if c.Args().Get(11) != "" {
|
||||
includeFundingEntries, err = strconv.ParseBool(c.Args().Get(11))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.IsSet("includepredictedrate") {
|
||||
includePredicted = c.Bool("includepredictedrate")
|
||||
} else if c.Args().Get(12) != "" {
|
||||
includePredicted, err = strconv.ParseBool(c.Args().Get(12))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = order.CheckFundingRatePrerequisites(getFundingData, includePredicted, includeFundingEntries)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s, err = time.Parse(common.SimpleTimeFormat, startTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid time format for start: %v", err)
|
||||
}
|
||||
e, err = time.Parse(common.SimpleTimeFormat, endTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid time format for end: %v", err)
|
||||
}
|
||||
|
||||
if e.Before(s) {
|
||||
return errors.New("start cannot be after end")
|
||||
}
|
||||
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.GetFuturesPositions(c.Context,
|
||||
&gctrpc.GetFuturesPositionsRequest{
|
||||
Exchange: exchangeName,
|
||||
Asset: assetType,
|
||||
Pair: &gctrpc.CurrencyPair{
|
||||
Delimiter: p.Delimiter,
|
||||
Base: p.Base.String(),
|
||||
Quote: p.Quote.String(),
|
||||
},
|
||||
StartDate: negateLocalOffset(s),
|
||||
EndDate: negateLocalOffset(e),
|
||||
Status: status,
|
||||
PositionLimit: int64(limit),
|
||||
Overwrite: overwrite,
|
||||
GetPositionStats: getPositionsStats,
|
||||
IncludeFullOrderData: includeOrderDetails,
|
||||
GetFundingPayments: getFundingData,
|
||||
IncludeFullFundingRates: includeFundingEntries,
|
||||
IncludePredictedRate: includePredicted,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCollateral(c *cli.Context) error {
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, c.Command.Name)
|
||||
}
|
||||
var (
|
||||
exchangeName, assetType string
|
||||
calculateOffline, includeBreakdown, includeZeroValues bool
|
||||
err error
|
||||
)
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
if c.IsSet("asset") {
|
||||
assetType = c.String("asset")
|
||||
} else {
|
||||
assetType = c.Args().Get(1)
|
||||
}
|
||||
|
||||
err = isFuturesAsset(assetType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.IsSet("calculateoffline") {
|
||||
calculateOffline = c.Bool("calculateoffline")
|
||||
} else if c.Args().Get(2) != "" {
|
||||
calculateOffline, err = strconv.ParseBool(c.Args().Get(2))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.IsSet("includebreakdown") {
|
||||
includeBreakdown = c.Bool("includebreakdown")
|
||||
} else if c.Args().Get(3) != "" {
|
||||
includeBreakdown, err = strconv.ParseBool(c.Args().Get(3))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.IsSet("includezerovalues") {
|
||||
includeZeroValues = c.Bool("includezerovalues")
|
||||
} else if c.Args().Get(4) != "" {
|
||||
includeZeroValues, err = strconv.ParseBool(c.Args().Get(4))
|
||||
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.GetCollateral(c.Context,
|
||||
&gctrpc.GetCollateralRequest{
|
||||
Exchange: exchangeName,
|
||||
Asset: assetType,
|
||||
IncludeBreakdown: includeBreakdown,
|
||||
CalculateOffline: calculateOffline,
|
||||
IncludeZeroValues: includeZeroValues,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFundingRates(c *cli.Context) error {
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
return cli.ShowCommandHelp(c, "getfundingrates")
|
||||
}
|
||||
var (
|
||||
exchangeName, assetType string
|
||||
currencyPairs []string
|
||||
includePredicted, includePayments bool
|
||||
p currency.Pair
|
||||
s, e time.Time
|
||||
err error
|
||||
)
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
if c.IsSet("asset") {
|
||||
assetType = c.String("asset")
|
||||
} else {
|
||||
assetType = c.Args().Get(1)
|
||||
}
|
||||
|
||||
err = isFuturesAsset(assetType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.IsSet("pairs") {
|
||||
currencyPairs = c.StringSlice("pairs")
|
||||
} else {
|
||||
currencyPairs = strings.Split(c.Args().Get(2), ",")
|
||||
}
|
||||
for i := range currencyPairs {
|
||||
if !validPair(currencyPairs[i]) {
|
||||
return errInvalidPair
|
||||
}
|
||||
p, err = currency.NewPairDelimiter(currencyPairs[i], pairDelimiter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currencyPairs[i] = p.String()
|
||||
}
|
||||
if !c.IsSet("start") {
|
||||
if c.Args().Get(3) != "" {
|
||||
startTime = c.Args().Get(3)
|
||||
}
|
||||
}
|
||||
if !c.IsSet("end") {
|
||||
if c.Args().Get(4) != "" {
|
||||
endTime = c.Args().Get(4)
|
||||
}
|
||||
}
|
||||
if c.IsSet("includepredicted") {
|
||||
includePredicted = c.Bool("includepredicted")
|
||||
} else if c.Args().Get(5) != "" {
|
||||
includePredicted, err = strconv.ParseBool(c.Args().Get(5))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.IsSet("includepayments") {
|
||||
includePayments = c.Bool("includepayments")
|
||||
} else if c.Args().Get(6) != "" {
|
||||
includePayments, err = strconv.ParseBool(c.Args().Get(6))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s, err = time.Parse(common.SimpleTimeFormat, startTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid time format for start: %v", err)
|
||||
}
|
||||
e, err = time.Parse(common.SimpleTimeFormat, endTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid time format for end: %v", err)
|
||||
}
|
||||
|
||||
if e.Before(s) {
|
||||
return errors.New("start cannot be after end")
|
||||
}
|
||||
|
||||
conn, cancel, err := setupClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeConn(conn, cancel)
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderServiceClient(conn)
|
||||
result, err := client.GetFundingRates(c.Context,
|
||||
&gctrpc.GetFundingRatesRequest{
|
||||
Exchange: exchangeName,
|
||||
Asset: assetType,
|
||||
Pairs: currencyPairs,
|
||||
StartDate: negateLocalOffset(s),
|
||||
EndDate: negateLocalOffset(e),
|
||||
IncludePredicted: includePredicted,
|
||||
IncludePayments: includePayments,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
@@ -209,8 +209,7 @@ func main() {
|
||||
tradeCommand,
|
||||
dataHistoryCommands,
|
||||
currencyStateManagementCommand,
|
||||
getFuturesPositionsCommand,
|
||||
getCollateralCommand,
|
||||
futuresCommands,
|
||||
shutdownCommand,
|
||||
technicalAnalysisCommand,
|
||||
getMarginRatesHistoryCommand,
|
||||
|
||||
@@ -2,9 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -20,3 +22,14 @@ func validAsset(i string) bool {
|
||||
_, err := asset.New(i)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func isFuturesAsset(a string) error {
|
||||
i, err := asset.New(a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !i.IsFutures() {
|
||||
return fmt.Errorf("%w '%s'", order.ErrNotFuturesAsset, a)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user