script: implementation of error insertion on return (#986)

* exchanges/account: shift credentials to account package and segregate funds to keys

* merge: fixes

* linter: fix

* Update exchanges/account/account.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits + protection for string panic

* glorious_suggestion: add method for matching keys

* linter: fix tests

* account: add protected method for credentials minimizing access, display full account details to rpc.

* linter: spelling kweeeeeeen

* accounts/portfolio: clean/check portfolio code and quickly check balances from change. Add protected method for future matching.

* accounts: theres no point in pointerising everything

* linter: ok pointerise this then...

* exchanges: fix regression add in little notes.

* glorious: nits

* Update exchanges/account/credentials.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update exchanges/account/credentials_test.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update exchanges/account/credentials_test.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits

* gloriously: fix glorious glorious test gloriously

* script: initial implementation of error insertion on return

* script: make script context aware(ish) and update error handle in examples

* script: add tests

* script: add syntax highlighting to readme

* Update gctscript/vm/vm.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update gctscript/vm/vm.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update gctscript/examples/exchange/account_info.gct

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update gctscript/examples/exchange/cancel_order.gct

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update gctscript/examples/verbose.gct

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update gctscript/modules/gct/gct_test.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits

* rm: bros

* scripts: handle errors in examples when they are going to use the data after fetching

* linter: fix rides again

* SCOTT_SPELL_CHECK_LINTER: fix

* gctscript: fix tests

* glorious: niiiiiiiiiiiiits

* scriptmodules/gct: standardize runtime errors

* Update gctscript/modules/gct/exchange.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update gctscript/modules/gct/exchange.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update gctscript/modules/gct/exchange.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update gctscript/modules/gct/exchange.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update gctscript/modules/gct/gct.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update gctscript/modules/gct/gct.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update gctscript/modules/gct/gct.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update gctscript/modules/gct/gct.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update gctscript/modules/gct/gct.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update gctscript/modules/gct/gct.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update gctscript/modules/gct/gct.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits/reverts

* go mod: tidy

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
Co-authored-by: Scott <gloriousCode@users.noreply.github.com>
This commit is contained in:
Ryan O'Hara-Reid
2022-08-17 14:18:53 +10:00
committed by GitHub
parent 68588560e3
commit e93ee83563
42 changed files with 1110 additions and 372 deletions

View File

@@ -59,6 +59,26 @@ func WriteAsCSV(args ...objects.Object) (objects.Object, error) {
case indicators.OHLCV:
temp, err = convertOHLCV(args[i])
front = true
case "scriptContext":
if target != "" {
return nil, fmt.Errorf("filename already set, extra string %v cannot be processed", args[i])
}
scriptCtx, ok := objects.ToInterface(args[i]).(*Context)
if !ok {
return nil, common.GetAssertError("*gct.Context", args[i])
}
scriptDetails, ok := scriptCtx.Value["script"]
if !ok {
return nil, errors.New("no script details")
}
target, ok = objects.ToString(scriptDetails)
if !ok {
return nil, errors.New("failed to convert incoming output to string")
}
target = processTarget(target)
case "string":
if target != "" {
return nil, fmt.Errorf("filename already set, extra string %v cannot be processed", args[i])
@@ -73,26 +93,7 @@ func WriteAsCSV(args ...objects.Object) (objects.Object, error) {
return nil, errors.New("script context details not specified")
}
// Removes file transversal
target = filepath.Base(target)
// checks to see if file is context defined, if not it will allow
// a client defined filename and append a date, forces the use of
// .csv file extension
switch {
case filepath.Ext(target) != ".csv" && strings.Contains(target, common.GctExt):
target += ".csv"
case filepath.Ext(target) == ".csv":
s := strings.Split(target, ".")
if len(s) == 2 {
target = s[0] + "-" + strconv.FormatInt(time.Now().UnixNano(), 10) + ".csv"
}
default:
target += "-" + strconv.FormatInt(time.Now().UnixNano(), 10) + ".csv"
}
target = filepath.Join(OutputDir, target)
target = processTarget(target)
default:
err = fmt.Errorf("%s type is not handled", args[i].TypeName())
}
@@ -130,7 +131,7 @@ func WriteAsCSV(args ...objects.Object) (objects.Object, error) {
return nil, err
}
log.Infof(log.GCTScriptMgr,
log.Debugf(log.GCTScriptMgr,
"CSV file successfully saved to: %s",
target)
return nil, nil
@@ -484,3 +485,24 @@ func convertOHLCV(a objects.Object) ([][]string, error) {
}
return bucket, nil
}
func processTarget(target string) string {
// Removes file transversal
target = filepath.Base(target)
// checks to see if file is context defined, if not it will allow
// a client defined filename and append a date, forces the use of
// .csv file extension
switch {
case filepath.Ext(target) != ".csv" && strings.Contains(target, common.GctExt):
target += ".csv"
case filepath.Ext(target) == ".csv":
s := strings.Split(target, ".")
if len(s) == 2 {
target = s[0] + "-" + strconv.FormatInt(time.Now().UnixNano(), 10) + ".csv"
}
default:
target += "-" + strconv.FormatInt(time.Now().UnixNano(), 10) + ".csv"
}
return filepath.Join(OutputDir, target)
}

View File

@@ -0,0 +1,39 @@
package gct
import (
"errors"
"fmt"
objects "github.com/d5/tengo/v2"
"github.com/thrasher-corp/gocryptotrader/common"
)
const standardFormatting = "%s"
var (
errFormatStringIsEmpty = errors.New("format string is empty")
errNoArguments = errors.New("no arguments for error response")
)
// errorResponsef is a helper function to apply error details to a return object
// for better script side error handling
func errorResponsef(format string, a ...interface{}) (objects.Object, error) {
if format == "" {
return nil, fmt.Errorf("cannot generate tengo error object %w", errFormatStringIsEmpty)
}
if len(a) == 0 {
return nil, fmt.Errorf("cannot generate tengo error object %w", errNoArguments)
}
return &objects.Error{
Value: &objects.String{Value: fmt.Sprintf(format, a...)},
}, nil
}
func constructRuntimeError(argPosition int, funcName, expectedType string, unexpectedData interface{}) error {
return fmt.Errorf("function [%s] argument position [%d] - %w",
funcName,
argPosition,
common.GetAssertError(expectedType, unexpectedData))
}

View File

@@ -0,0 +1,38 @@
package gct
import (
"errors"
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
)
func TestErrorResponse(t *testing.T) {
t.Parallel()
_, err := errorResponsef("")
if !errors.Is(err, errFormatStringIsEmpty) {
t.Fatalf("received: '%v' but expected: '%v'", err, errFormatStringIsEmpty)
}
_, err = errorResponsef("--")
if !errors.Is(err, errNoArguments) {
t.Fatalf("received: '%v' but expected: '%v'", err, errNoArguments)
}
errResp, err := errorResponsef("error %s", "hello")
if err != nil {
t.Fatal(err)
}
if errResp.String() != `error: "error hello"` {
t.Fatalf("received: %v but expected: %v", errResp.String(), `error: "error hello"`)
}
}
func TestConstructRuntimeError(t *testing.T) {
t.Parallel()
err := constructRuntimeError(0, "", "", nil)
if !errors.Is(err, common.ErrTypeAssertFailure) {
t.Fatalf("receieved: '%v' but expected: '%v'", err, common.ErrTypeAssertFailure)
}
}

View File

@@ -1,7 +1,6 @@
package gct
import (
"context"
"fmt"
"time"
@@ -16,74 +15,93 @@ import (
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
)
const (
orderbookFunc = "orderbook"
tickerFunc = "ticker"
exchangesFunc = "exchanges"
pairsFunc = "pairs"
accountInfoFunc = "accountinfo"
depositAddressFunc = "depositaddress"
orderQueryFunc = "orderquery"
orderCancelFunc = "ordercancel"
orderSubmitFunc = "ordersubmit"
withdrawCryptoFunc = "withdrawcrypto"
withdrawFiatFunc = "withdrawfiat"
ohlcvFunc = "ohlcv"
)
var exchangeModule = map[string]objects.Object{
"orderbook": &objects.UserFunction{Name: "orderbook", Value: ExchangeOrderbook},
"ticker": &objects.UserFunction{Name: "ticker", Value: ExchangeTicker},
"exchanges": &objects.UserFunction{Name: "exchanges", Value: ExchangeExchanges},
"pairs": &objects.UserFunction{Name: "pairs", Value: ExchangePairs},
"accountinfo": &objects.UserFunction{Name: "accountinfo", Value: ExchangeAccountInfo},
"depositaddress": &objects.UserFunction{Name: "depositaddress", Value: ExchangeDepositAddress},
"orderquery": &objects.UserFunction{Name: "orderquery", Value: ExchangeOrderQuery},
"ordercancel": &objects.UserFunction{Name: "ordercancel", Value: ExchangeOrderCancel},
"ordersubmit": &objects.UserFunction{Name: "ordersubmit", Value: ExchangeOrderSubmit},
"withdrawcrypto": &objects.UserFunction{Name: "withdrawcrypto", Value: ExchangeWithdrawCrypto},
"withdrawfiat": &objects.UserFunction{Name: "withdrawfiat", Value: ExchangeWithdrawFiat},
"ohlcv": &objects.UserFunction{Name: "ohlcv", Value: exchangeOHLCV},
orderbookFunc: &objects.UserFunction{Name: orderbookFunc, Value: ExchangeOrderbook},
tickerFunc: &objects.UserFunction{Name: tickerFunc, Value: ExchangeTicker},
exchangesFunc: &objects.UserFunction{Name: exchangesFunc, Value: ExchangeExchanges},
pairsFunc: &objects.UserFunction{Name: pairsFunc, Value: ExchangePairs},
accountInfoFunc: &objects.UserFunction{Name: accountInfoFunc, Value: ExchangeAccountInfo},
depositAddressFunc: &objects.UserFunction{Name: depositAddressFunc, Value: ExchangeDepositAddress},
orderQueryFunc: &objects.UserFunction{Name: orderQueryFunc, Value: ExchangeOrderQuery},
orderCancelFunc: &objects.UserFunction{Name: orderCancelFunc, Value: ExchangeOrderCancel},
orderSubmitFunc: &objects.UserFunction{Name: orderSubmitFunc, Value: ExchangeOrderSubmit},
withdrawCryptoFunc: &objects.UserFunction{Name: withdrawCryptoFunc, Value: ExchangeWithdrawCrypto},
withdrawFiatFunc: &objects.UserFunction{Name: withdrawFiatFunc, Value: ExchangeWithdrawFiat},
ohlcvFunc: &objects.UserFunction{Name: ohlcvFunc, Value: exchangeOHLCV},
}
// ExchangeOrderbook returns orderbook for requested exchange & currencypair
func ExchangeOrderbook(args ...objects.Object) (objects.Object, error) {
if len(args) != 4 {
if len(args) != 5 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
scriptCtx, ok := objects.ToInterface(args[0]).(*Context)
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
return nil, constructRuntimeError(1, orderbookFunc, "*gct.Context", args[0])
}
currencyPair, ok := objects.ToString(args[1])
exchangeName, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, currencyPair)
return nil, constructRuntimeError(2, orderbookFunc, "string", args[1])
}
delimiter, ok := objects.ToString(args[2])
currencyPair, ok := objects.ToString(args[2])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, delimiter)
return nil, constructRuntimeError(3, orderbookFunc, "string", args[2])
}
assetTypeParam, ok := objects.ToString(args[3])
delimiter, ok := objects.ToString(args[3])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, assetTypeParam)
return nil, constructRuntimeError(4, orderbookFunc, "string", args[3])
}
assetTypeParam, ok := objects.ToString(args[4])
if !ok {
return nil, constructRuntimeError(5, orderbookFunc, "string", args[4])
}
pair, err := currency.NewPairDelimiter(currencyPair, delimiter)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
assetType, err := asset.New(assetTypeParam)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
ob, err := wrappers.GetWrapper().
Orderbook(context.TODO(), exchangeName, pair, assetType)
ctx := processScriptContext(scriptCtx)
ob, err := wrappers.GetWrapper().Orderbook(ctx, exchangeName, pair, assetType)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
var asks, bids objects.Array
asks := objects.Array{Value: make([]objects.Object, len(ob.Asks))}
for x := range ob.Asks {
temp := make(map[string]objects.Object, 2)
temp["amount"] = &objects.Float{Value: ob.Asks[x].Amount}
temp["price"] = &objects.Float{Value: ob.Asks[x].Price}
asks.Value = append(asks.Value, &objects.Map{Value: temp})
asks.Value[x] = &objects.Map{Value: temp}
}
bids := objects.Array{Value: make([]objects.Object, len(ob.Bids))}
for x := range ob.Bids {
temp := make(map[string]objects.Object, 2)
temp["amount"] = &objects.Float{Value: ob.Bids[x].Amount}
temp["price"] = &objects.Float{Value: ob.Bids[x].Price}
bids.Value = append(bids.Value, &objects.Map{Value: temp})
bids.Value[x] = &objects.Map{Value: temp}
}
data := make(map[string]objects.Object, 5)
@@ -93,32 +111,35 @@ func ExchangeOrderbook(args ...objects.Object) (objects.Object, error) {
data["bids"] = &bids
data["asset"] = &objects.String{Value: ob.Asset.String()}
return &objects.Map{
Value: data,
}, nil
return &objects.Map{Value: data}, nil
}
// ExchangeTicker returns ticker data for requested exchange and currency pair
func ExchangeTicker(args ...objects.Object) (objects.Object, error) {
if len(args) != 4 {
if len(args) != 5 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
scriptCtx, ok := objects.ToInterface(args[0]).(*Context)
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
return nil, constructRuntimeError(1, tickerFunc, "*gct.Context", args[0])
}
currencyPair, ok := objects.ToString(args[1])
exchangeName, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, currencyPair)
return nil, constructRuntimeError(2, tickerFunc, "string", args[1])
}
delimiter, ok := objects.ToString(args[2])
currencyPair, ok := objects.ToString(args[2])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, delimiter)
return nil, constructRuntimeError(3, tickerFunc, "string", args[2])
}
assetTypeParam, ok := objects.ToString(args[3])
delimiter, ok := objects.ToString(args[3])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, assetTypeParam)
return nil, constructRuntimeError(4, tickerFunc, "string", args[3])
}
assetTypeParam, ok := objects.ToString(args[4])
if !ok {
return nil, constructRuntimeError(5, tickerFunc, "string", args[4])
}
pair, err := currency.NewPairDelimiter(currencyPair, delimiter)
@@ -128,13 +149,13 @@ func ExchangeTicker(args ...objects.Object) (objects.Object, error) {
assetType, err := asset.New(assetTypeParam)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
tx, err := wrappers.GetWrapper().
Ticker(context.TODO(), exchangeName, pair, assetType)
ctx := processScriptContext(scriptCtx)
tx, err := wrappers.GetWrapper().Ticker(ctx, exchangeName, pair, assetType)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
data := make(map[string]objects.Object, 14)
@@ -166,13 +187,15 @@ func ExchangeExchanges(args ...objects.Object) (objects.Object, error) {
enabledOnly, ok := objects.ToBool(args[0])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, enabledOnly)
return nil, constructRuntimeError(1, exchangesFunc, "bool", args[0])
}
rtnValue := wrappers.GetWrapper().Exchanges(enabledOnly)
r := objects.Array{}
r := objects.Array{
Value: make([]objects.Object, len(rtnValue)),
}
for x := range rtnValue {
r.Value = append(r.Value, &objects.String{Value: rtnValue[x]})
r.Value[x] = &objects.String{Value: rtnValue[x]}
}
return &r, nil
@@ -186,56 +209,64 @@ func ExchangePairs(args ...objects.Object) (objects.Object, error) {
exchangeName, ok := objects.ToString(args[0])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
return nil, constructRuntimeError(1, pairsFunc, "string", args[0])
}
enabledOnly, ok := objects.ToBool(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, enabledOnly)
return nil, constructRuntimeError(2, pairsFunc, "bool", args[1])
}
assetTypeParam, ok := objects.ToString(args[2])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, assetTypeParam)
return nil, constructRuntimeError(3, pairsFunc, "string", args[2])
}
assetType, err := asset.New(assetTypeParam)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
rtnValue, err := wrappers.GetWrapper().Pairs(exchangeName, enabledOnly, assetType)
pairs, err := wrappers.GetWrapper().Pairs(exchangeName, enabledOnly, assetType)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
r := objects.Array{}
pairs := *(*[]currency.Pair)(rtnValue)
for x := range pairs {
r.Value = append(r.Value, &objects.String{Value: pairs[x].String()})
r := objects.Array{
Value: make([]objects.Object, len(*pairs)),
}
for x := range *pairs {
r.Value[x] = &objects.String{Value: (*pairs)[x].String()}
}
return &r, nil
}
// ExchangeAccountInfo returns account information for requested exchange
func ExchangeAccountInfo(args ...objects.Object) (objects.Object, error) {
if len(args) != 2 {
if len(args) != 3 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
scriptCtx, ok := objects.ToInterface(args[0]).(*Context)
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
return nil, constructRuntimeError(1, accountInfoFunc, "*gct.Context", args[0])
}
assetString, ok := objects.ToString(args[1])
exchangeName, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, assetString)
return nil, constructRuntimeError(2, accountInfoFunc, "string", args[1])
}
assetString, ok := objects.ToString(args[2])
if !ok {
return nil, constructRuntimeError(3, accountInfoFunc, "string", args[2])
}
assetType, err := asset.New(assetString)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
ctx := processScriptContext(scriptCtx)
rtnValue, err := wrappers.GetWrapper().
AccountInformation(context.TODO(), exchangeName, assetType)
AccountInformation(ctx, exchangeName, assetType)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
var funds objects.Array
@@ -252,62 +283,65 @@ func ExchangeAccountInfo(args ...objects.Object) (objects.Object, error) {
data := make(map[string]objects.Object, 2)
data["exchange"] = &objects.String{Value: rtnValue.Exchange}
data["currencies"] = &funds
return &objects.Map{
Value: data,
}, nil
return &objects.Map{Value: data}, nil
}
// ExchangeOrderQuery query order on exchange
func ExchangeOrderQuery(args ...objects.Object) (objects.Object, error) {
if len(args) < 2 {
if len(args) < 3 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
scriptCtx, ok := objects.ToInterface(args[0]).(*Context)
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
return nil, constructRuntimeError(1, orderQueryFunc, "*gct.Context", args[0])
}
orderID, ok := objects.ToString(args[1])
exchangeName, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, orderID)
return nil, constructRuntimeError(2, orderQueryFunc, "string", args[1])
}
orderID, ok := objects.ToString(args[2])
if !ok {
return nil, constructRuntimeError(3, orderQueryFunc, "string", args[2])
}
var pair currency.Pair
assetTypeString := asset.Spot.String()
switch len(args) {
case 4:
assetTypeString, ok = objects.ToString(args[3])
case 5:
assetTypeString, ok = objects.ToString(args[4])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, assetTypeString)
return nil, constructRuntimeError(5, orderQueryFunc, "string", args[4])
}
fallthrough
case 3:
currencyPairString, isOk := objects.ToString(args[2])
case 4:
currencyPairString, isOk := objects.ToString(args[3])
if !isOk {
return nil, fmt.Errorf(ErrParameterConvertFailed, currencyPairString)
return nil, constructRuntimeError(4, orderQueryFunc, "string", args[3])
}
var err error
pair, err = currency.NewPairFromString(currencyPairString)
if err != nil {
return nil, fmt.Errorf(ErrParameterConvertFailed, currencyPairString)
return errorResponsef(standardFormatting, err)
}
}
assetType, err := asset.New(assetTypeString)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
ctx := processScriptContext(scriptCtx)
orderDetails, err := wrappers.GetWrapper().
QueryOrder(context.TODO(), exchangeName, orderID, pair, assetType)
QueryOrder(ctx, exchangeName, orderID, pair, assetType)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
var tradeHistory objects.Array
tradeHistory.Value = make([]objects.Object, len(orderDetails.Trades))
for x := range orderDetails.Trades {
temp := make(map[string]objects.Object, 7)
temp["timestamp"] = &objects.Time{Value: orderDetails.Trades[x].Timestamp}
@@ -317,7 +351,7 @@ func ExchangeOrderQuery(args ...objects.Object) (objects.Object, error) {
temp["type"] = &objects.String{Value: orderDetails.Trades[x].Type.String()}
temp["side"] = &objects.String{Value: orderDetails.Trades[x].Side.String()}
temp["description"] = &objects.String{Value: orderDetails.Trades[x].Description}
tradeHistory.Value = append(tradeHistory.Value, &objects.Map{Value: temp})
tradeHistory.Value[x] = &objects.Map{Value: temp}
}
data := make(map[string]objects.Object, 14)
@@ -336,63 +370,65 @@ func ExchangeOrderQuery(args ...objects.Object) (objects.Object, error) {
data["status"] = &objects.String{Value: orderDetails.Status.String()}
data["trades"] = &tradeHistory
return &objects.Map{
Value: data,
}, nil
return &objects.Map{Value: data}, nil
}
// ExchangeOrderCancel cancels order on requested exchange
func ExchangeOrderCancel(args ...objects.Object) (objects.Object, error) {
if len(args) < 2 || len(args) > 4 {
if len(args) < 3 || len(args) > 5 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
scriptCtx, ok := objects.ToInterface(args[0]).(*Context)
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
return nil, constructRuntimeError(1, orderCancelFunc, "*gct.Context", args[0])
}
exchangeName, ok := objects.ToString(args[1])
if !ok {
return nil, constructRuntimeError(2, orderCancelFunc, "string", args[1])
}
if exchangeName == "" {
return nil, fmt.Errorf(ErrEmptyParameter, "exchange name")
}
var orderID string
orderID, ok = objects.ToString(args[1])
orderID, ok = objects.ToString(args[2])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, orderID)
return nil, constructRuntimeError(3, orderCancelFunc, "string", args[2])
}
if orderID == "" {
return nil, fmt.Errorf(ErrEmptyParameter, "orderID")
}
var err error
var cp currency.Pair
if len(args) > 2 {
if len(args) > 3 {
var currencyPair string
currencyPair, ok = objects.ToString(args[2])
currencyPair, ok = objects.ToString(args[3])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, currencyPair)
return nil, constructRuntimeError(4, orderCancelFunc, "string", args[3])
}
cp, err = currency.NewPairFromString(currencyPair)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
}
var a asset.Item
if len(args) > 3 {
if len(args) > 4 {
var assetType string
assetType, ok = objects.ToString(args[3])
assetType, ok = objects.ToString(args[4])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, assetType)
return nil, constructRuntimeError(5, orderCancelFunc, "string", args[4])
}
a, err = asset.New(assetType)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
}
var isCancelled bool
isCancelled, err = wrappers.GetWrapper().
CancelOrder(context.TODO(), exchangeName, orderID, cp, a)
ctx := processScriptContext(scriptCtx)
isCancelled, err := wrappers.GetWrapper().
CancelOrder(ctx, exchangeName, orderID, cp, a)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
if isCancelled {
@@ -403,63 +439,69 @@ func ExchangeOrderCancel(args ...objects.Object) (objects.Object, error) {
// ExchangeOrderSubmit submit order on exchange
func ExchangeOrderSubmit(args ...objects.Object) (objects.Object, error) {
if len(args) != 9 {
if len(args) != 10 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
scriptCtx, ok := objects.ToInterface(args[0]).(*Context)
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
return nil, constructRuntimeError(1, orderSubmitFunc, "*gct.Context", args[0])
}
currencyPair, ok := objects.ToString(args[1])
exchangeName, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, currencyPair)
return nil, constructRuntimeError(2, orderSubmitFunc, "string", args[1])
}
delimiter, ok := objects.ToString(args[2])
currencyPair, ok := objects.ToString(args[2])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, delimiter)
return nil, constructRuntimeError(3, orderSubmitFunc, "string", args[2])
}
delimiter, ok := objects.ToString(args[3])
if !ok {
return nil, constructRuntimeError(4, orderSubmitFunc, "string", args[3])
}
orderType, ok := objects.ToString(args[4])
if !ok {
return nil, constructRuntimeError(5, orderSubmitFunc, "string", args[4])
}
orderSide, ok := objects.ToString(args[5])
if !ok {
return nil, constructRuntimeError(6, orderSubmitFunc, "string", args[5])
}
orderPrice, ok := objects.ToFloat64(args[6])
if !ok {
return nil, constructRuntimeError(7, orderSubmitFunc, "float64", args[6])
}
orderAmount, ok := objects.ToFloat64(args[7])
if !ok {
return nil, constructRuntimeError(8, orderSubmitFunc, "float64", args[7])
}
orderClientID, ok := objects.ToString(args[8])
if !ok {
return nil, constructRuntimeError(9, orderSubmitFunc, "string", args[8])
}
assetType, ok := objects.ToString(args[9])
if !ok {
return nil, constructRuntimeError(10, orderSubmitFunc, "string", args[9])
}
pair, err := currency.NewPairDelimiter(currencyPair, delimiter)
if err != nil {
return nil, err
}
orderType, ok := objects.ToString(args[3])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, orderType)
}
orderSide, ok := objects.ToString(args[4])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, orderSide)
}
orderPrice, ok := objects.ToFloat64(args[5])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, orderPrice)
}
orderAmount, ok := objects.ToFloat64(args[6])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, orderAmount)
}
orderClientID, ok := objects.ToString(args[7])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, orderClientID)
}
assetType, ok := objects.ToString(args[8])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, orderClientID)
return errorResponsef(standardFormatting, err)
}
a, err := asset.New(assetType)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
side, err := order.StringToOrderSide(orderSide)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
oType, err := order.StringToOrderType(orderType)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
tempSubmit := &order.Submit{
@@ -473,18 +515,17 @@ func ExchangeOrderSubmit(args ...objects.Object) (objects.Object, error) {
Exchange: exchangeName,
}
rtn, err := wrappers.GetWrapper().SubmitOrder(context.TODO(), tempSubmit)
ctx := processScriptContext(scriptCtx)
rtn, err := wrappers.GetWrapper().SubmitOrder(ctx, tempSubmit)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
data := make(map[string]objects.Object, 2)
data["orderid"] = &objects.String{Value: rtn.OrderID}
data["isorderplaced"] = objects.TrueValue
return &objects.Map{
Value: data,
}, nil
return &objects.Map{Value: data}, nil
}
// ExchangeDepositAddress returns deposit address (if supported by exchange)
@@ -495,65 +536,67 @@ func ExchangeDepositAddress(args ...objects.Object) (objects.Object, error) {
exchangeName, ok := objects.ToString(args[0])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
return nil, constructRuntimeError(1, depositAddressFunc, "string", args[0])
}
currencyCode, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, currencyCode)
return nil, constructRuntimeError(2, depositAddressFunc, "string", args[1])
}
chain, ok := objects.ToString(args[2])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, chain)
return nil, constructRuntimeError(3, depositAddressFunc, "string", args[2])
}
currCode := currency.NewCode(currencyCode)
rtn, err := wrappers.GetWrapper().DepositAddress(exchangeName, chain, currCode)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
data := make(map[string]objects.Object, 2)
data["address"] = &objects.String{Value: rtn.Address}
data["tag"] = &objects.String{Value: rtn.Tag}
return &objects.Map{
Value: data,
}, nil
return &objects.Map{Value: data}, nil
}
// ExchangeWithdrawCrypto submit request to withdraw crypto assets
func ExchangeWithdrawCrypto(args ...objects.Object) (objects.Object, error) {
if len(args) != 7 {
if len(args) != 8 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
scriptCtx, ok := objects.ToInterface(args[0]).(*Context)
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
return nil, constructRuntimeError(1, withdrawCryptoFunc, "*gct.Context", args[0])
}
cur, ok := objects.ToString(args[1])
exchangeName, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, cur)
return nil, constructRuntimeError(2, withdrawCryptoFunc, "string", args[1])
}
address, ok := objects.ToString(args[2])
cur, ok := objects.ToString(args[2])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, address)
return nil, constructRuntimeError(3, withdrawCryptoFunc, "string", args[2])
}
addressTag, ok := objects.ToString(args[3])
address, ok := objects.ToString(args[3])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, addressTag)
return nil, constructRuntimeError(4, withdrawCryptoFunc, "string", args[3])
}
amount, ok := objects.ToFloat64(args[4])
addressTag, ok := objects.ToString(args[4])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, amount)
return nil, constructRuntimeError(5, withdrawCryptoFunc, "string", args[4])
}
feeAmount, ok := objects.ToFloat64(args[5])
amount, ok := objects.ToFloat64(args[5])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, feeAmount)
return nil, constructRuntimeError(6, withdrawCryptoFunc, "float64", args[5])
}
description, ok := objects.ToString(args[6])
feeAmount, ok := objects.ToFloat64(args[6])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, description)
return nil, constructRuntimeError(7, withdrawCryptoFunc, "float64", args[6])
}
description, ok := objects.ToString(args[7])
if !ok {
return nil, constructRuntimeError(8, withdrawCryptoFunc, "string", args[7])
}
withdrawRequest := &withdraw.Request{
@@ -568,10 +611,10 @@ func ExchangeWithdrawCrypto(args ...objects.Object) (objects.Object, error) {
Amount: amount,
}
rtn, err := wrappers.GetWrapper().
WithdrawalCryptoFunds(context.TODO(), withdrawRequest)
ctx := processScriptContext(scriptCtx)
rtn, err := wrappers.GetWrapper().WithdrawalCryptoFunds(ctx, withdrawRequest)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
return &objects.String{Value: rtn}, nil
@@ -579,29 +622,33 @@ func ExchangeWithdrawCrypto(args ...objects.Object) (objects.Object, error) {
// ExchangeWithdrawFiat submit request to withdraw fiat assets
func ExchangeWithdrawFiat(args ...objects.Object) (objects.Object, error) {
if len(args) != 5 {
if len(args) != 6 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
scriptCtx, ok := objects.ToInterface(args[0]).(*Context)
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
return nil, constructRuntimeError(1, withdrawFiatFunc, "*gct.Context", args[0])
}
cur, ok := objects.ToString(args[1])
exchangeName, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, cur)
return nil, constructRuntimeError(2, withdrawFiatFunc, "string", args[1])
}
description, ok := objects.ToString(args[2])
cur, ok := objects.ToString(args[2])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, description)
return nil, constructRuntimeError(3, withdrawFiatFunc, "string", args[2])
}
amount, ok := objects.ToFloat64(args[3])
description, ok := objects.ToString(args[3])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, amount)
return nil, constructRuntimeError(4, withdrawFiatFunc, "string", args[3])
}
bankAccountID, ok := objects.ToString(args[4])
amount, ok := objects.ToFloat64(args[4])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, bankAccountID)
return nil, constructRuntimeError(5, withdrawFiatFunc, "float64", args[4])
}
bankAccountID, ok := objects.ToString(args[5])
if !ok {
return nil, constructRuntimeError(6, withdrawFiatFunc, "string", args[5])
}
withdrawRequest := &withdraw.Request{
@@ -611,10 +658,11 @@ func ExchangeWithdrawFiat(args ...objects.Object) (objects.Object, error) {
Amount: amount,
}
ctx := processScriptContext(scriptCtx)
rtn, err := wrappers.GetWrapper().
WithdrawalFiatFunds(context.TODO(), bankAccountID, withdrawRequest)
WithdrawalFiatFunds(ctx, bankAccountID, withdrawRequest)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
return &objects.String{Value: rtn}, nil
@@ -631,56 +679,61 @@ func (o *OHLCV) TypeName() string {
}
func exchangeOHLCV(args ...objects.Object) (objects.Object, error) {
if len(args) != 7 {
if len(args) != 8 {
return nil, objects.ErrWrongNumArguments
}
exchangeName, ok := objects.ToString(args[0])
scriptCtx, ok := objects.ToInterface(args[0]).(*Context)
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, exchangeName)
return nil, constructRuntimeError(1, ohlcvFunc, "*gct.Context", args[0])
}
currencyPair, ok := objects.ToString(args[1])
exchangeName, ok := objects.ToString(args[1])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, currencyPair)
return nil, constructRuntimeError(2, ohlcvFunc, "string", args[1])
}
delimiter, ok := objects.ToString(args[2])
currencyPair, ok := objects.ToString(args[2])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, delimiter)
return nil, constructRuntimeError(3, ohlcvFunc, "string", args[2])
}
assetTypeParam, ok := objects.ToString(args[3])
delimiter, ok := objects.ToString(args[3])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, assetTypeParam)
return nil, constructRuntimeError(4, ohlcvFunc, "string", args[3])
}
assetTypeParam, ok := objects.ToString(args[4])
if !ok {
return nil, constructRuntimeError(5, ohlcvFunc, "string", args[4])
}
startTime, ok := objects.ToTime(args[4])
startTime, ok := objects.ToTime(args[5])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, startTime)
return nil, constructRuntimeError(6, ohlcvFunc, "string", args[5])
}
endTime, ok := objects.ToTime(args[5])
endTime, ok := objects.ToTime(args[6])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, endTime)
return nil, constructRuntimeError(7, ohlcvFunc, "time.Time", args[6])
}
intervalStr, ok := objects.ToString(args[6])
intervalStr, ok := objects.ToString(args[7])
if !ok {
return nil, fmt.Errorf(ErrParameterConvertFailed, endTime)
return nil, constructRuntimeError(8, ohlcvFunc, "string", args[7])
}
interval, err := parseInterval(intervalStr)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
pair, err := currency.NewPairDelimiter(currencyPair, delimiter)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
assetType, err := asset.New(assetTypeParam)
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
ctx := processScriptContext(scriptCtx)
ret, err := wrappers.GetWrapper().
OHLCV(context.TODO(),
OHLCV(ctx,
exchangeName,
pair,
assetType,
@@ -688,21 +741,21 @@ func exchangeOHLCV(args ...objects.Object) (objects.Object, error) {
endTime,
kline.Interval(interval))
if err != nil {
return nil, err
return errorResponsef(standardFormatting, err)
}
var candles objects.Array
candles := objects.Array{Value: make([]objects.Object, len(ret.Candles))}
for x := range ret.Candles {
candle := &objects.Array{}
candle.Value = append(candle.Value, &objects.Int{Value: ret.Candles[x].Time.Unix()},
&objects.Float{Value: ret.Candles[x].Open},
&objects.Float{Value: ret.Candles[x].High},
&objects.Float{Value: ret.Candles[x].Low},
&objects.Float{Value: ret.Candles[x].Close},
&objects.Float{Value: ret.Candles[x].Volume},
)
candles.Value = append(candles.Value, candle)
candles.Value[x] = &objects.Array{
Value: []objects.Object{
&objects.Int{Value: ret.Candles[x].Time.Unix()},
&objects.Float{Value: ret.Candles[x].Open},
&objects.Float{Value: ret.Candles[x].High},
&objects.Float{Value: ret.Candles[x].Low},
&objects.Float{Value: ret.Candles[x].Close},
&objects.Float{Value: ret.Candles[x].Volume},
},
}
}
retValue := make(map[string]objects.Object, 5)

View File

@@ -1,5 +1,25 @@
package gct
import (
"context"
objects "github.com/d5/tengo/v2"
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
)
const (
setVerboseFunc = "set_verbose"
setAccountFunc = "set_account"
setSubAccountFunc = "set_sub_account"
)
var globalModules = map[string]objects.Object{
setVerboseFunc: &objects.UserFunction{Name: setVerboseFunc, Value: setVerbose},
setAccountFunc: &objects.UserFunction{Name: setAccountFunc, Value: setAccount},
setSubAccountFunc: &objects.UserFunction{Name: setSubAccountFunc, Value: setSubAccount},
}
// AllModuleNames returns a list of all default module names.
func AllModuleNames() []string {
names := make([]string, 0, len(Modules))
@@ -8,3 +28,183 @@ func AllModuleNames() []string {
}
return names
}
// setVerbose specifically sets verbosity for http rest requests for this script
// Params: scriptCTX
func setVerbose(args ...objects.Object) (objects.Object, error) {
if len(args) != 1 {
return nil, objects.ErrWrongNumArguments
}
ctx, ok := objects.ToInterface(args[0]).(*Context)
if !ok {
return nil, constructRuntimeError(1, setVerboseFunc, "*gct.Context", args[0])
}
if ctx.Value == nil {
ctx.Value = make(map[string]objects.Object)
}
ctx.Value["verbose"] = objects.TrueValue
return ctx, nil
}
// setAccount sets account details which overrides default credentials for
// script account management, api key and secret are required.
// Params: scriptCTX, apiKey, apiSecret, subAccount, clientID, PEMKey, OneTimePassword string
func setAccount(args ...objects.Object) (objects.Object, error) {
if len(args) < 3 || len(args) > 7 {
return nil, objects.ErrWrongNumArguments
}
ctx, ok := objects.ToInterface(args[0]).(*Context)
if !ok {
return nil, constructRuntimeError(1, setAccountFunc, "*gct.Context", args[0])
}
apikey, ok := objects.ToInterface(args[1]).(string)
if !ok {
return nil, constructRuntimeError(2, setAccountFunc, "string", args[1])
}
if ctx.Value == nil {
ctx.Value = make(map[string]objects.Object)
}
ctx.Value["apikey"] = &objects.String{Value: apikey}
apisecret, ok := objects.ToInterface(args[2]).(string)
if !ok {
return nil, constructRuntimeError(3, setAccountFunc, "string", args[2])
}
ctx.Value["apisecret"] = &objects.String{Value: apisecret}
if len(args) > 3 {
var subaccount string
subaccount, ok = objects.ToInterface(args[3]).(string)
if !ok {
return nil, constructRuntimeError(4, setAccountFunc, "string", args[3])
}
if subaccount != "" {
ctx.Value["subaccount"] = &objects.String{Value: subaccount}
}
}
if len(args) > 4 {
var clientID string
clientID, ok = objects.ToInterface(args[4]).(string)
if !ok {
return nil, constructRuntimeError(5, setAccountFunc, "string", args[4])
}
if clientID != "" {
ctx.Value["clientid"] = &objects.String{Value: clientID}
}
}
if len(args) > 5 {
var pemKey string
pemKey, ok = objects.ToInterface(args[5]).(string)
if !ok {
return nil, constructRuntimeError(6, setAccountFunc, "string", args[5])
}
if pemKey != "" {
ctx.Value["pemkey"] = &objects.String{Value: pemKey}
}
}
if len(args) > 5 {
var oneTimePassword string
oneTimePassword, ok = objects.ToInterface(args[6]).(string)
if !ok {
return nil, constructRuntimeError(7, setAccountFunc, "string", args[6])
}
if oneTimePassword != "" {
ctx.Value["otp"] = &objects.String{Value: oneTimePassword}
}
}
return ctx, nil
}
// setSubAccount sets sub account details which overrides default credential
// sub account but uses the same configured api keys.
// Params: scriptCTX, subAccount string
func setSubAccount(args ...objects.Object) (objects.Object, error) {
if len(args) != 2 {
return nil, objects.ErrWrongNumArguments
}
ctx, ok := objects.ToInterface(args[0]).(*Context)
if !ok {
return nil, constructRuntimeError(1, setSubAccountFunc, "*gct.Context", args[0])
}
sub, ok := objects.ToInterface(args[1]).(string)
if !ok {
return nil, constructRuntimeError(2, setSubAccountFunc, "string", args[1])
}
if ctx.Value == nil {
ctx.Value = make(map[string]objects.Object)
}
ctx.Value["subaccount"] = &objects.String{Value: sub}
return ctx, nil
}
func processScriptContext(scriptCtx *Context) context.Context {
ctx := context.Background()
if scriptCtx == nil || scriptCtx.Value == nil {
return ctx
}
var object objects.Object
if object = scriptCtx.Value["verbose"]; object != nil {
ctx = request.WithVerbose(ctx)
}
if object = scriptCtx.Value["apikey"]; object != nil {
key, _ := objects.ToString(object)
var secret string
if object = scriptCtx.Value["apisecret"]; object != nil {
secret, _ = objects.ToString(object)
}
var subAccount string
if object = scriptCtx.Value["subaccount"]; object != nil {
subAccount, _ = objects.ToString(object)
}
var clientID string
if object = scriptCtx.Value["clientid"]; object != nil {
clientID, _ = objects.ToString(object)
}
var pemKey string
if object = scriptCtx.Value["pemkey"]; object != nil {
pemKey, _ = objects.ToString(object)
}
var otp string
if object = scriptCtx.Value["otp"]; object != nil {
otp, _ = objects.ToString(object)
}
ctx = account.DeployCredentialsToContext(ctx, &account.Credentials{
Key: key,
Secret: secret,
SubAccount: subAccount,
ClientID: clientID,
PEMKey: pemKey,
OneTimePassword: otp,
})
} else if object = scriptCtx.Value["subaccount"]; object != nil {
subAccount, _ := objects.ToString(object)
ctx = account.DeploySubAccountOverrideToContext(ctx, subAccount)
}
return ctx
}
// TypeName returns the name of the custom type.
func (c *Context) TypeName() string {
return "scriptContext"
}

View File

@@ -8,12 +8,15 @@ import (
"time"
objects "github.com/d5/tengo/v2"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/gctscript/modules"
"github.com/thrasher-corp/gocryptotrader/gctscript/wrappers/validator"
)
var (
ctx = &Context{}
exch = &objects.String{
Value: "BTC Markets",
}
@@ -48,7 +51,7 @@ func TestMain(m *testing.M) {
func TestExchangeOrderbook(t *testing.T) {
t.Parallel()
_, err := ExchangeOrderbook(exch, currencyPair, delimiter, assetType)
_, err := ExchangeOrderbook(ctx, exch, currencyPair, delimiter, assetType)
if err != nil {
t.Error(err)
}
@@ -66,7 +69,7 @@ func TestExchangeOrderbook(t *testing.T) {
func TestExchangeTicker(t *testing.T) {
t.Parallel()
_, err := ExchangeTicker(exch, currencyPair, delimiter, assetType)
_, err := ExchangeTicker(ctx, exch, currencyPair, delimiter, assetType)
if err != nil {
t.Error(err)
}
@@ -133,12 +136,12 @@ func TestAccountInfo(t *testing.T) {
t.Error(err)
}
_, err = ExchangeAccountInfo(exch, assetType)
_, err = ExchangeAccountInfo(ctx, exch, assetType)
if err != nil {
t.Error(err)
}
_, err = ExchangeAccountInfo(exchError, assetType)
_, err = ExchangeAccountInfo(ctx, exchError, assetType)
if err != nil && !errors.Is(err, errTestFailed) {
t.Error(err)
}
@@ -152,12 +155,12 @@ func TestExchangeOrderQuery(t *testing.T) {
t.Error(err)
}
_, err = ExchangeOrderQuery(exch, orderID)
_, err = ExchangeOrderQuery(ctx, exch, orderID)
if err != nil {
t.Error(err)
}
_, err = ExchangeOrderQuery(exchError, orderID)
_, err = ExchangeOrderQuery(ctx, exchError, orderID)
if err != nil && !errors.Is(err, errTestFailed) {
t.Error(err)
}
@@ -180,17 +183,17 @@ func TestExchangeOrderCancel(t *testing.T) {
t.Error("expecting error")
}
_, err = ExchangeOrderCancel(exch, orderID)
_, err = ExchangeOrderCancel(ctx, exch, orderID)
if err != nil {
t.Error(err)
}
_, err = ExchangeOrderCancel(exch, orderID, currencyPair)
_, err = ExchangeOrderCancel(ctx, exch, orderID, currencyPair)
if err != nil {
t.Error(err)
}
_, err = ExchangeOrderCancel(exch, orderID, currencyPair, assetType)
_, err = ExchangeOrderCancel(ctx, exch, orderID, currencyPair, assetType)
if err != nil {
t.Error(err)
}
@@ -209,19 +212,19 @@ func TestExchangeOrderSubmit(t *testing.T) {
orderAmount := &objects.Float{Value: 1}
orderAsset := &objects.String{Value: asset.Spot.String()}
_, err = ExchangeOrderSubmit(exch, currencyPair, delimiter,
_, err = ExchangeOrderSubmit(ctx, exch, currencyPair, delimiter,
orderType, orderSide, orderPrice, orderAmount, orderID, orderAsset)
if err != nil && !errors.Is(err, errTestFailed) {
t.Error(err)
}
_, err = ExchangeOrderSubmit(exch, currencyPair, delimiter,
_, err = ExchangeOrderSubmit(ctx, exch, currencyPair, delimiter,
orderType, orderSide, orderPrice, orderAmount, orderID, orderAsset)
if err != nil {
t.Error(err)
}
_, err = ExchangeOrderSubmit(objects.TrueValue, currencyPair, delimiter,
_, err = ExchangeOrderSubmit(ctx, objects.TrueValue, currencyPair, delimiter,
orderType, orderSide, orderPrice, orderAmount, orderID, orderAsset)
if err != nil {
t.Error(err)
@@ -269,7 +272,7 @@ func TestExchangeWithdrawCrypto(t *testing.T) {
address := &objects.String{Value: "0xTHISISALEGITBTCADDRESSS"}
amount := &objects.Float{Value: 1.0}
_, err = ExchangeWithdrawCrypto(exch, currCode, address, address, amount, amount, desc)
_, err = ExchangeWithdrawCrypto(ctx, exch, currCode, address, address, amount, amount, desc)
if err != nil {
t.Error(err)
}
@@ -286,7 +289,7 @@ func TestExchangeWithdrawFiat(t *testing.T) {
desc := &objects.String{Value: "Hello"}
amount := &objects.Float{Value: 1.0}
bankID := &objects.String{Value: "test-bank-01"}
_, err = ExchangeWithdrawFiat(exch, currCode, desc, amount, bankID)
_, err = ExchangeWithdrawFiat(ctx, exch, currCode, desc, amount, bankID)
if err != nil {
t.Error(err)
}
@@ -333,3 +336,193 @@ func TestParseInterval(t *testing.T) {
}
}
}
func TestSetVerbose(t *testing.T) {
t.Parallel()
_, err := setVerbose()
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Fatalf("received: '%v' but expected: '%v'", err, objects.ErrWrongNumArguments)
}
_, err = setVerbose(objects.TrueValue)
if !errors.Is(err, common.ErrTypeAssertFailure) {
t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrTypeAssertFailure)
}
resp, err := setVerbose(&Context{})
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
ctx, ok := objects.ToInterface(resp).(*Context)
if !ok {
t.Fatal("should be of type *Context")
}
val := ctx.Value["verbose"]
if val.String() != objects.TrueValue.String() {
t.Fatal("should contain verbose string in map")
}
}
var dummyStr = &objects.String{Value: "xxxx"}
func TestSetAccount(t *testing.T) {
t.Parallel()
_, err := setAccount()
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Fatalf("received: '%v' but expected: '%v'", err, objects.ErrWrongNumArguments)
}
_, err = setAccount(objects.TrueValue, objects.TrueValue, objects.TrueValue)
if !errors.Is(err, common.ErrTypeAssertFailure) {
t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrTypeAssertFailure)
}
_, err = setAccount(&Context{}, objects.TrueValue, objects.TrueValue)
if !errors.Is(err, common.ErrTypeAssertFailure) {
t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrTypeAssertFailure)
}
_, err = setAccount(&Context{}, dummyStr, objects.TrueValue)
if !errors.Is(err, common.ErrTypeAssertFailure) {
t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrTypeAssertFailure)
}
_, err = setAccount(&Context{}, dummyStr, dummyStr, objects.TrueValue)
if !errors.Is(err, common.ErrTypeAssertFailure) {
t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrTypeAssertFailure)
}
_, err = setAccount(&Context{}, dummyStr, dummyStr, dummyStr, objects.TrueValue)
if !errors.Is(err, common.ErrTypeAssertFailure) {
t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrTypeAssertFailure)
}
_, err = setAccount(&Context{}, dummyStr, dummyStr, dummyStr, dummyStr, objects.TrueValue)
if !errors.Is(err, common.ErrTypeAssertFailure) {
t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrTypeAssertFailure)
}
_, err = setAccount(&Context{}, dummyStr, dummyStr, dummyStr, dummyStr, dummyStr, objects.TrueValue)
if !errors.Is(err, common.ErrTypeAssertFailure) {
t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrTypeAssertFailure)
}
resp, err := setAccount(&Context{}, dummyStr, dummyStr, dummyStr, dummyStr, dummyStr, dummyStr)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
ctx, ok := objects.ToInterface(resp).(*Context)
if !ok {
t.Fatal("should be of type *Context")
}
val := ctx.Value["apikey"]
if val.String() != dummyStr.String() {
t.Fatal("should contain apikey string in map")
}
val = ctx.Value["apisecret"]
if val.String() != dummyStr.String() {
t.Fatal("should contain apisecret string in map")
}
val = ctx.Value["subaccount"]
if val.String() != dummyStr.String() {
t.Fatal("should contain subaccount string in map")
}
val = ctx.Value["clientid"]
if val.String() != dummyStr.String() {
t.Fatal("should contain clientid string in map")
}
val = ctx.Value["pemkey"]
if val.String() != dummyStr.String() {
t.Fatal("should contain pemkey string in map")
}
val = ctx.Value["otp"]
if val.String() != dummyStr.String() {
t.Fatal("should contain otp string in map")
}
}
func TestSetSubAccount(t *testing.T) {
t.Parallel()
_, err := setSubAccount()
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Fatalf("received: '%v' but expected: '%v'", err, objects.ErrWrongNumArguments)
}
_, err = setSubAccount(objects.TrueValue, objects.TrueValue)
if !errors.Is(err, common.ErrTypeAssertFailure) {
t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrTypeAssertFailure)
}
_, err = setSubAccount(&Context{}, objects.TrueValue)
if !errors.Is(err, common.ErrTypeAssertFailure) {
t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrTypeAssertFailure)
}
subby, err := setSubAccount(&Context{}, dummyStr)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
ctxWSubAcc, ok := subby.(*Context)
if !ok {
t.Fatal("unexpected type returned")
}
if ctxWSubAcc.Value["subaccount"].String() != dummyStr.String() {
t.Fatalf("received: '%v' but expected: '%v'", ctxWSubAcc.Value["subaccount"].String(), dummyStr.String())
}
// Deploy override to actual context.Context type
ctx := processScriptContext(ctxWSubAcc)
if ctx == nil {
t.Fatal("should not be nil")
}
subaccount, ok := ctx.Value(account.ContextSubAccountFlag).(string)
if !ok {
t.Fatal("wrong type")
}
if subaccount != dummyStr.String()[1:5] {
t.Fatalf("received: '%v' but expected: '%v'", subaccount, dummyStr.String()[1:5])
}
}
func TestProcessScriptContext(t *testing.T) {
t.Parallel()
ctx := processScriptContext(nil)
if ctx == nil {
t.Fatal("should not be nil")
}
fromScript, err := setAccount(&Context{}, dummyStr, dummyStr, dummyStr, dummyStr, dummyStr, dummyStr)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
fromScript, err = setVerbose(fromScript)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
scriptCTX, ok := objects.ToInterface(fromScript).(*Context)
if !ok {
t.Fatal("should assert correctly")
}
ctx = processScriptContext(scriptCTX)
if ctx == nil {
t.Fatal("should not be nil")
}
}
func TestScriptCredentialTypeName(t *testing.T) {
t.Parallel()
if name := (&Context{}).TypeName(); name != "scriptContext" {
t.Fatal("unexpected value")
}
}

View File

@@ -3,7 +3,7 @@ package gct
import (
"errors"
"github.com/d5/tengo/v2"
objects "github.com/d5/tengo/v2"
)
const (
@@ -17,7 +17,13 @@ var errInvalidInterval = errors.New("invalid interval")
var supportedDurations = []string{"1m", "3m", "5m", "15m", "30m", "1h", "2h", "4h", "6h", "12h", "24h", "1d", "3d", "1w"}
// Modules map of all loadable modules
var Modules = map[string]map[string]tengo.Object{
var Modules = map[string]map[string]objects.Object{
"exchange": exchangeModule,
"common": commonModule,
"global": globalModules,
}
// Context defines a juncture for script context to go context awareness
type Context struct {
objects.Map
}