currency: Add OrderParameters helpers for currency pair (#1217)

* currency: Add order aspect from pair helper method.

* Update currency/pair_methods.go

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

* Update currency/pair_types.go

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

* glorious: nits

* glorious: nit cont. rm pair field and used IsPopulated method instead of IsEmpty

* thrasher: nits

* ch test name

* glorious: nits

---------

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
2023-06-09 10:07:04 +10:00
committed by GitHub
parent a024ad53ae
commit 9e4ea6c62b
3 changed files with 170 additions and 1 deletions

View File

@@ -2,11 +2,15 @@ package currency
import (
"encoding/json"
"errors"
"fmt"
)
// EMPTYFORMAT defines an empty pair format
var EMPTYFORMAT = PairFormat{}
var errCurrencyNotAssociatedWithPair = errors.New("currency not associated with pair")
// String returns a currency pair string
func (p Pair) String() string {
return p.Base.String() + p.Delimiter + p.Quote.String()
@@ -140,7 +144,82 @@ func (p Pair) Other(c Code) (Code, error) {
return EMPTYCODE, ErrCurrencyCodeEmpty
}
// IsPopulated returns true if the currency pair have both non-empty values for base and quote.
// IsPopulated returns true if the currency pair have both non-empty values for
// base and quote.
func (p Pair) IsPopulated() bool {
return !p.Base.IsEmpty() && !p.Quote.IsEmpty()
}
// MarketSellOrderParameters returns order parameters for when you want to sell
// a currency which purchases another currency. This specifically returns what
// liquidity side you will be affecting, what order side you will be placing and
// what currency you will be purchasing.
func (p Pair) MarketSellOrderParameters(wantingToSell Code) (*OrderParameters, error) {
return p.getOrderParameters(wantingToSell, true, true)
}
// MarketBuyOrderParameters returns order parameters for when you want to sell a
// currency which purchases another currency. This specifically returns what
// liquidity side you will be affecting, what order side you will be placing and
// what currency you will be purchasing.
func (p Pair) MarketBuyOrderParameters(wantingToBuy Code) (*OrderParameters, error) {
return p.getOrderParameters(wantingToBuy, false, true)
}
// LimitSellOrderParameters returns order parameters for when you want to sell a
// currency which purchases another currency. This specifically returns what
// liquidity side you will be affecting, what order side you will be placing and
// what currency you will be purchasing.
func (p Pair) LimitSellOrderParameters(wantingToSell Code) (*OrderParameters, error) {
return p.getOrderParameters(wantingToSell, true, false)
}
// LimitBuyOrderParameters returns order parameters for when you want to
// sell a currency which purchases another currency. This specifically returns
// what liquidity side you will be affecting, what order side you will be
// placing and what currency you will be purchasing.
func (p Pair) LimitBuyOrderParameters(wantingToBuy Code) (*OrderParameters, error) {
return p.getOrderParameters(wantingToBuy, false, false)
}
// getOrderDecisionDetails returns order parameters for the currency pair using
// the provided currency code, whether or not you are selling and whether or not
// you are placing a market order.
func (p Pair) getOrderParameters(c Code, selling, market bool) (*OrderParameters, error) {
if !p.IsPopulated() {
return nil, ErrCurrencyPairEmpty
}
if c.IsEmpty() {
return nil, ErrCurrencyCodeEmpty
}
params := OrderParameters{}
switch {
case p.Base.Equal(c):
if selling {
params.SellingCurrency = p.Base
params.PurchasingCurrency = p.Quote
params.IsBuySide = false
params.IsAskLiquidity = !market
} else {
params.SellingCurrency = p.Quote
params.PurchasingCurrency = p.Base
params.IsBuySide = true
params.IsAskLiquidity = market
}
case p.Quote.Equal(c):
if selling {
params.SellingCurrency = p.Quote
params.PurchasingCurrency = p.Base
params.IsBuySide = true
params.IsAskLiquidity = market
} else {
params.SellingCurrency = p.Base
params.PurchasingCurrency = p.Quote
params.IsBuySide = false
params.IsAskLiquidity = !market
}
default:
return nil, fmt.Errorf("%w %v: %v", errCurrencyNotAssociatedWithPair, c, p)
}
return &params, nil
}

View File

@@ -3,6 +3,7 @@ package currency
import (
"encoding/json"
"errors"
"fmt"
"testing"
)
@@ -961,3 +962,77 @@ func TestIsPopulated(t *testing.T) {
t.Fatal("unexpected value")
}
}
func TestGetOrderParameters(t *testing.T) {
t.Parallel()
p := NewPair(BTC, USDT)
testCases := []struct {
Pair Pair
currency Code
market bool
selling bool
expectedParams *OrderParameters
expectedError error
}{
{expectedError: ErrCurrencyPairEmpty},
{Pair: p, expectedError: ErrCurrencyCodeEmpty},
{Pair: p, currency: XRP, selling: true, market: true, expectedError: errCurrencyNotAssociatedWithPair},
{Pair: p, currency: BTC, selling: true, market: true, expectedParams: &OrderParameters{SellingCurrency: BTC, PurchasingCurrency: USDT, IsBuySide: false, IsAskLiquidity: false}},
{Pair: p, currency: BTC, selling: false, market: true, expectedParams: &OrderParameters{SellingCurrency: USDT, PurchasingCurrency: BTC, IsBuySide: true, IsAskLiquidity: true}},
{Pair: p, currency: BTC, selling: true, market: false, expectedParams: &OrderParameters{SellingCurrency: BTC, PurchasingCurrency: USDT, IsBuySide: false, IsAskLiquidity: true}},
{Pair: p, currency: BTC, selling: false, market: false, expectedParams: &OrderParameters{SellingCurrency: USDT, PurchasingCurrency: BTC, IsBuySide: true, IsAskLiquidity: false}},
{Pair: p, currency: USDT, selling: true, market: true, expectedParams: &OrderParameters{SellingCurrency: USDT, PurchasingCurrency: BTC, IsBuySide: true, IsAskLiquidity: true}},
{Pair: p, currency: USDT, selling: false, market: true, expectedParams: &OrderParameters{SellingCurrency: BTC, PurchasingCurrency: USDT, IsBuySide: false, IsAskLiquidity: false}},
{Pair: p, currency: USDT, selling: true, market: false, expectedParams: &OrderParameters{SellingCurrency: USDT, PurchasingCurrency: BTC, IsBuySide: true, IsAskLiquidity: false}},
{Pair: p, currency: USDT, selling: false, market: false, expectedParams: &OrderParameters{SellingCurrency: BTC, PurchasingCurrency: USDT, IsBuySide: false, IsAskLiquidity: true}},
}
for i, tc := range testCases {
tc := tc
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
t.Parallel()
var resp *OrderParameters
var err error
switch {
case tc.market && tc.selling:
resp, err = tc.Pair.MarketSellOrderParameters(tc.currency)
case tc.market && !tc.selling:
resp, err = tc.Pair.MarketBuyOrderParameters(tc.currency)
case !tc.market && tc.selling:
resp, err = tc.Pair.LimitSellOrderParameters(tc.currency)
case !tc.market && !tc.selling:
resp, err = tc.Pair.LimitBuyOrderParameters(tc.currency)
}
if !errors.Is(err, tc.expectedError) {
t.Fatalf("received %v, expected %v", err, tc.expectedError)
}
if tc.expectedParams == nil {
if resp != nil {
t.Fatalf("received %v, expected nil", resp)
}
return
}
if resp.SellingCurrency != tc.expectedParams.SellingCurrency {
t.Fatalf("SellingCurrency received %v, expected %v", resp.SellingCurrency, tc.expectedParams.SellingCurrency)
}
if resp.PurchasingCurrency != tc.expectedParams.PurchasingCurrency {
t.Fatalf("PurchasingCurrency received %v, expected %v", resp.PurchasingCurrency, tc.expectedParams.PurchasingCurrency)
}
if resp.IsBuySide != tc.expectedParams.IsBuySide {
t.Fatalf("BuySide received %v, expected %v", resp.IsBuySide, tc.expectedParams.IsBuySide)
}
if resp.IsAskLiquidity != tc.expectedParams.IsAskLiquidity {
t.Fatalf("AskLiquidity received %v, expected %v", resp.IsAskLiquidity, tc.expectedParams.IsAskLiquidity)
}
})
}
}

View File

@@ -17,3 +17,18 @@ type PairDifference struct {
Remove Pairs
FormatDifference bool
}
// OrderParameters is used to determine the order side, liquidity side and the
// selling & purchasing currency derived from the currency pair.
type OrderParameters struct {
// SellingCurrency is the currency that will be sold first
SellingCurrency Code
// Purchasing is the currency that will be purchased last
PurchasingCurrency Code
// IsBuySide is the side of the order that will be placed true for buy/long,
// false for sell/short.
IsBuySide bool
// IsAskLiquidity is the side of the orderbook that will be used, false for
// bid liquidity.
IsAskLiquidity bool
}