Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

exchanges/order: Added TimeInForce type and values #1382

Open
wants to merge 50 commits into
base: master
Choose a base branch
from

Conversation

samuael
Copy link
Collaborator

@samuael samuael commented Oct 29, 2023

ሰላም፣ ወንድሞች

PR Description

This PR adds a type TimeInForce(TIF) to represent common Time In Force values. It affects some exchanges and packages that were using the ImmediateOrCancel and FillOrKill values. Units that were affected by the change are tested. Additionally, new unit tests are added for newly added functions.

Exchanges that are directly affected by the change are Binance, Bitrex, BTCMarket, CoinbasePro, Huobi, Kraken, Poloniex

Packages affected: Orders, Engine, cmd/exchange_wrapper_standards

Fixes # (issue)

Type of change

Please delete options that are not relevant and add an x in [] as the item is complete.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How has this been tested

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration and
also consider improving test coverage whilst working on a certain feature or package.

  • go test ./... -race
  • golangci-lint run
  • Test X

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented on my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation and regenerated documentation via the documentation tool
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally and on Github Actions/AppVeyor with my changes
  • Any dependent changes have been merged and published in downstream modules

@gloriousCode gloriousCode requested a review from a team October 29, 2023 21:13
@gloriousCode gloriousCode added the review me This pull request is ready for review label Oct 29, 2023
Copy link
Collaborator

@shazbert shazbert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really cool update. Thanks big boss.

Copy link
Collaborator

@shazbert shazbert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes. 🦸

@gloriousCode gloriousCode added reconstructing Based on PR feedback, this is currently being reworked and is not to be merged and removed review me This pull request is ready for review labels Nov 13, 2023
@samuael
Copy link
Collaborator Author

samuael commented Nov 15, 2023

Sorry for the delayed update on this PR.
I think I have added all the required updates.

Copy link
Collaborator

@shazbert shazbert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update, not much left over.

@shazbert shazbert added review me This pull request is ready for review and removed reconstructing Based on PR feedback, this is currently being reworked and is not to be merged labels Dec 5, 2023
Copy link
Collaborator

@shazbert shazbert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes. Things are looking good. Just need to test all endpoints associated with these changes.

@samuael
Copy link
Collaborator Author

samuael commented Dec 6, 2023

The endpoints I can run are only those that are public. The change I made, TimeInForce is mostly used by the authenticated, order-related endpoints.

I am open to hear any other alternative of running the authenticated tests you may suggest

@github-actions github-actions bot removed the stale label Jan 16, 2025
Copy link
Collaborator

@gloriousCode gloriousCode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating things!

Comment on lines 114 to 115
ImmediateOrCancel bool
FillOrKill bool
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to keep fields ImmediateOrCancel and FillOrKill with your new TimeInForce?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and post only.

@@ -3437,7 +3437,7 @@ func TestGetTimeInForce(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "gtc", ret)

ret, err = getTimeInForce(&order.Submit{Type: order.Market, FillOrKill: true})
ret, err = getTimeInForce(&order.Submit{Type: order.Market, TimeInForce: order.FOK})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

@shazbert shazbert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice direction and clean. Just some nits and suggestions. Thanks heaps!

ImmediateOrCancel bool
FillOrKill bool
// TimeInForce holds time in force values
TimeInForce TimeInForce

PostOnly bool
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Post only can be merged with TimeInForce across all order types as it conflicts with FOK, IOC and it implies limit order type

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Post-only can be a value for either the Order Type or the Time In Force type. Some exchanges have a dedicated PostOnly field, and in such cases, we need to check whether either the Time In Force or the Order Type is set to Post-only. I left this as it is to avoid making that decision.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Post-only can be used with GTT, GTC, or GTD, and having it as a parameter here is restrictive. Some exchanges have TimeInForce and PostOnly as separate parameters.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: You can either flag TimeInForce values order.GTT | order.POSTONLY or you can merge the entire TimeInForce values and flag everything together into order.Type - order.Limit | order.GTT | order.PostOnly and with both options update to parser functions to allow for delimited string values and methods for extraction. I will have a play around today and see if there are any caveats.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merging them altogether into order type is a bit of a mission. And can be circled around in a future update. In your other comment:

FOK and IOC has an order type replica.. So, I am not renaming these values.
However, I have renamed the rest

suggestion: Seems like a time in force policy as well as PostOnly and that should be removed from order types and moved to TIF. Could have a method Is(order.FillOrKill) bool and Is(order.InstanceOrCancelled) bool Just so its an easy catch all expression and self obvious.

There are a bunch of trigger conditionals in order type StopLimit which could be improved as it should just be Stop | Limit as well as SpecialMode/exchange specific e.g. TWAP or MarketMakerProtection (which has post only variant which can be removed and handled) but that is out of scope.

So just keeping it time in force is a nice reviewable action but I am open to other perspectives and direction. If we proceed to merge them altogether we add in edge cases where they cannot be combined but if we refactor to keep them separate like

type Order struct {
    Type        OrderType
    Condition   ConditionType
    TimeInForce TimeInForce
    SpecialMode SpecialMode
}

might be impractical.

Copy link
Collaborator Author

@samuael samuael Feb 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for delayed reply to this comments & Pardon me if I don't fully understand what you are saying here.

I have a point here: Three exchanges Okx, CoinbasePro, Deribit, and Huobi use PostOnly as an independent Order Type too. How do we handle these?

As it has to be directly mapped in the wrapper file between order.Type and String representation.

The same applies to FillOrKill and ImmediateOrCancel

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Boss 👑. You would apply the same logic their end but from a submission perspective from the IBotInterface/wrapper funcs, an order would apply order.Limit as type and for the TimeInForce value as Post Only or FillOrKIll or Immediate Or Cancel we can extract the string that we need from that. That way we standardise the layer on our side and we segregate primary areas of concern. I will have a play today and see if that can be done while you are offline so I can give a concrete example.

Comment on lines 420 to 421
POC // POC represents PendingOrCancel
PostOnlyGTC // PostOnlyGCT represents PostOnlyGoodTilCancelled
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

POC and PostOnlyGTC can merged to PostOnly and all Abbreviations can be changed from GTC -> GoodTillCancel and comments removed etc.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FOK and IOC has an order type replica.. So, I am not renaming these values.
However, I have renamed the rest

@@ -1234,6 +1254,37 @@ func StringToOrderStatus(status string) (Status, error) {
}
}

// StringToTimeInForce converts time in force string value to TimeInForce instance.
func StringToTimeInForce(timeInForce string) (TimeInForce, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: This is a good easy catch all but it adds bloat and reduced performance there should be an umarshal json method or function to a type exchange side. So suggestion would be to remove this completely. This is just a suggestion doesn't need to be done.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep it here and remove it for when we need the UnmarshalJSON method rather than this one

@shazbert
Copy link
Collaborator

Please review and merge/cherry-pick changes from this commit @samuael boss this is regarding our conversation here

@samuael
Copy link
Collaborator Author

samuael commented Feb 28, 2025

Please review and merge/cherry-pick changes from this commit @samuael boss this is regarding our conversation here

I have done so and applied the changes. I will try to do further updates based on your recent approach.
Thank you @shazbert boss!

Copy link
Collaborator

@shazbert shazbert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes, Heres some more nits for current state.

var requestParamsOrderType RequestParamsOrderType
switch s.Type {
case order.Market:
timeInForce = ""
requestParamsOrderType = BinanceRequestParamsOrderMarket
case order.Limit:
if s.ImmediateOrCancel {
timeInForce = BinanceRequestParamsTimeIOC
if s.TimeInForce == order.ImmediateOrCancel {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can use IS method

@@ -1420,7 +1420,7 @@ func TestNewOrderTest(t *testing.T) {
TradeType: BinanceRequestParamsOrderLimit,
Price: 0.0025,
Quantity: 100000,
TimeInForce: BinanceRequestParamsTimeGTC,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Binance request params can be removed?

}
if s.FillOrKill {
return fillOrKill
switch s.TimeInForce {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can use method Is

if s.ImmediateOrCancel {
timeInForce = CoinbaseRequestParamsTimeIOC
timeInForce := order.GoodTillCancel.String()
if s.TimeInForce == order.ImmediateOrCancel {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use method Is

timeInForce := ""
if s.ImmediateOrCancel {
var timeInForce string
switch s.TimeInForce {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use method Is as we are flagging and can be order.GoodTillDay | order.PostOnly for example.

type TimeInForce uint16

// Is checks to see if the enum contains the flag
func (t TimeInForce) Is(in TimeInForce) bool {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PLease have a test for this and remove IsIOC method as this is more generic for that application.

Comment on lines 420 to 422
FillOrKill // FOK represents FillOrKill
ImmediateOrCancel // IOC represents ImmediateOrCancel
PostOnly // PostOnlyGCT represents PostOnlyGoodTilCancelled
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove commentary

}

// IsIOC determines whether the TimeInForce value is set to IOC (Immediate or Cancel).
func (t TimeInForce) IsIOC() bool {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RM

@@ -740,6 +718,33 @@ func (t Type) String() string {
}
}

// String implements the stringer interface.
func (t TimeInForce) String() string {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this can be flagged with Post only and GTT, GTD, GTC, it needs to be able to be a delimitated string and umarshal multiple options. Please add a test.

@@ -549,7 +549,7 @@ func (p *Poloniex) ModifyOrder(ctx context.Context, action *order.Modify) (*orde
action.Price,
action.Amount,
action.PostOnly,
action.ImmediateOrCancel)
action.TimeInForce.IsIOC())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Is method

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
review me This pull request is ready for review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants