Files
gocryptotrader/exchanges/order/timeinforce.go
Samuael A. 640960aec1 exchanges/order: Add TimeInForce type (#1382)
* Added TimeInForce type and updated related files

* Linter issue fix and minor coinbasepro type update

* Bitrex consts update

* added unit test and minor changes in bittrex

* Unit tests update

* Fix minor linter issues

* Update TestStringToTimeInForce unit test

* fix conflict with gateio timeInForce

* Update order tests

* Complete updating the order unit tests

* update kucoin and deribit wrapper to match the time in force change

* fix time-in-force related test errors

* linter issue fix

* time in force constants, functions and unit tests update

* shift tif policies to TimeInForce

* Update time-in-force, related functions, and unit tests

* fix linter issue and time-in-force processing

* added a good till crossing tif value

* order type fix and fix related tim-in-force entries

* update time-in-force unmarshaling and unit test

* fix time-in-force error in gateio

* linter issue fix

* update based on review comments

* add unit test and fix missing issues

* minor fix and added benchmark unit test

* change GTT to GTC for limit

* fix linter issue

* added time-in-force value to place order param

* fix minor issues based on review comment and move tif code to separate files

* update on exchanges linked to time-in-force

* resolve missing review comments

* minor linter issues fix

* added time-in-force handler and update timeInForce parametered endpoint

* minor fixes based on review

* nits fix

* update based on review

* linter fix

* rm getTimeInForce func and minor change to time-in-force

* minor change

* update based on review comments

* wrappers and time-in-force calling approach

* minor change

* update gateio string to timeInForce conversion and unit test

* updated order test unit tes functions

* minor fixes on unit tests

* nits fix based on feedback

* update TestDeriveCancel unit test assert messages

* update TestDeriveCancel unit test assert messages

* update timeInForceFromString method to return formatted error and update functions using it

* restructure and fix minor exchanges time-in-force handling issues

* replaced unused getTypeFromTimeInForce with inline switch-based order type check

* separated the repeated timeInForce conversion code to  a function

* update exchanges time-in-force handling based on review comments

* limter fix

* edded comment to validTimesInForce var

* added comment to gateio's timeInForceString func

* added goodTillCancel switch case to gateio timeInForceString func
2025-05-23 09:07:09 +10:00

141 lines
3.8 KiB
Go

package order
import (
"errors"
"fmt"
"strings"
)
// var error definitions
var (
ErrInvalidTimeInForce = errors.New("invalid time in force value provided")
ErrUnsupportedTimeInForce = errors.New("unsupported time in force value")
)
// TimeInForce enforces a standard for time-in-force values across the code base.
type TimeInForce uint8
// TimeInForce types
const (
UnknownTIF TimeInForce = 0
GoodTillCancel TimeInForce = 1 << iota
GoodTillDay
GoodTillTime
GoodTillCrossing
FillOrKill
ImmediateOrCancel
PostOnly
supportedTimeInForceFlag = GoodTillCancel | GoodTillDay | GoodTillTime | GoodTillCrossing | FillOrKill | ImmediateOrCancel | PostOnly
)
// time-in-force string representations
const (
gtcStr = "GTC"
gtdStr = "GTD"
gttStr = "GTT"
gtxStr = "GTX"
fokStr = "FOK"
iocStr = "IOC"
postonlyStr = "POSTONLY"
)
// Is checks to see if the enum contains the flag
func (t TimeInForce) Is(in TimeInForce) bool {
return in != 0 && t&in == in
}
// StringToTimeInForce converts time in force string value to TimeInForce instance.
func StringToTimeInForce(timeInForce string) (TimeInForce, error) {
var result TimeInForce
timeInForce = strings.ToUpper(timeInForce)
switch timeInForce {
case "IMMEDIATEORCANCEL", "IMMEDIATE_OR_CANCEL", iocStr:
result = ImmediateOrCancel
case "GOODTILLCANCEL", "GOODTILCANCEL", "GOOD_TIL_CANCELLED", "GOOD_TILL_CANCELLED", "GOOD_TILL_CANCELED", gtcStr:
result = GoodTillCancel
case "GOODTILLDAY", "GOOD_TIL_DAY", "GOOD_TILL_DAY", gtdStr:
result = GoodTillDay
case "GOODTILLTIME", "GOOD_TIL_TIME", gttStr:
result = GoodTillTime
case "GOODTILLCROSSING", "GOOD_TIL_CROSSING", "GOOD TIL CROSSING", "GOOD_TILL_CROSSING", gtxStr:
result = GoodTillCrossing
case "FILLORKILL", "FILL_OR_KILL", fokStr:
result = FillOrKill
case "POC", "POST_ONLY", "PENDINGORCANCEL", postonlyStr:
result = PostOnly
}
if result == UnknownTIF && timeInForce != "" {
return UnknownTIF, fmt.Errorf("%w: tif=%s", ErrInvalidTimeInForce, timeInForce)
}
return result, nil
}
// IsValid returns whether or not the supplied time in force value is valid or
// not
func (t TimeInForce) IsValid() bool {
// Neither ImmediateOrCancel nor FillOrKill can coexist with anything else
// If either bit is set then it must be the only bit set
isIOCorFOK := t&(ImmediateOrCancel|FillOrKill) != 0
hasTwoBitsSet := t&(t-1) != 0
if isIOCorFOK && hasTwoBitsSet {
return false
}
return t == UnknownTIF || supportedTimeInForceFlag&t == t
}
// String implements the stringer interface.
func (t TimeInForce) String() string {
if t == UnknownTIF {
return ""
}
var tifStrings []string
if t.Is(ImmediateOrCancel) {
tifStrings = append(tifStrings, iocStr)
}
if t.Is(GoodTillCancel) {
tifStrings = append(tifStrings, gtcStr)
}
if t.Is(GoodTillDay) {
tifStrings = append(tifStrings, gtdStr)
}
if t.Is(GoodTillTime) {
tifStrings = append(tifStrings, gttStr)
}
if t.Is(GoodTillCrossing) {
tifStrings = append(tifStrings, gtxStr)
}
if t.Is(FillOrKill) {
tifStrings = append(tifStrings, fokStr)
}
if t.Is(PostOnly) {
tifStrings = append(tifStrings, postonlyStr)
}
if len(tifStrings) == 0 {
return "UNKNOWN"
}
return strings.Join(tifStrings, ",")
}
// Lower returns a lower case string representation of time-in-force
func (t TimeInForce) Lower() string {
return strings.ToLower(t.String())
}
// UnmarshalJSON deserializes a string data into TimeInForce instance.
func (t *TimeInForce) UnmarshalJSON(data []byte) error {
for val := range strings.SplitSeq(strings.Trim(string(data), `"`), ",") {
tif, err := StringToTimeInForce(val)
if err != nil {
return err
}
*t |= tif
}
return nil
}
// MarshalJSON returns the JSON-encoded order time-in-force value
func (t TimeInForce) MarshalJSON() ([]byte, error) {
return []byte(`"` + t.String() + `"`), nil
}