Uppercase TimeInForce (align with ccxt)

This commit is contained in:
Matthias 2022-08-27 10:24:56 +02:00
parent 6686489c06
commit 104a73025d
18 changed files with 49 additions and 51 deletions

View File

@ -64,8 +64,8 @@
"stoploss_on_exchange_limit_ratio": 0.99 "stoploss_on_exchange_limit_ratio": 0.99
}, },
"order_time_in_force": { "order_time_in_force": {
"entry": "gtc", "entry": "GTC",
"exit": "gtc" "exit": "GTC"
}, },
"pairlists": [ "pairlists": [
{"method": "StaticPairList"}, {"method": "StaticPairList"},

View File

@ -525,16 +525,16 @@ It means if the order is not executed immediately AND fully then it is cancelled
It is the same as FOK (above) except it can be partially fulfilled. The remaining part It is the same as FOK (above) except it can be partially fulfilled. The remaining part
is automatically cancelled by the exchange. is automatically cancelled by the exchange.
The `order_time_in_force` parameter contains a dict with buy and sell time in force policy values. The `order_time_in_force` parameter contains a dict with entry and exit time in force policy values.
This can be set in the configuration file or in the strategy. This can be set in the configuration file or in the strategy.
Values set in the configuration file overwrites values set in the strategy. Values set in the configuration file overwrites values set in the strategy.
The possible values are: `gtc` (default), `fok` or `ioc`. The possible values are: `GTC` (default), `FOK` or `IOC`.
``` python ``` python
"order_time_in_force": { "order_time_in_force": {
"entry": "gtc", "entry": "GTC",
"exit": "gtc" "exit": "GTC"
}, },
``` ```

View File

@ -278,7 +278,7 @@ For example, to test the order type `FOK` with Kraken, and modify candle limit t
"exchange": { "exchange": {
"name": "kraken", "name": "kraken",
"_ft_has_params": { "_ft_has_params": {
"order_time_in_force": ["gtc", "fok"], "order_time_in_force": ["GTC", "FOK"],
"ohlcv_candle_limit": 200 "ohlcv_candle_limit": 200
} }
//... //...

View File

@ -332,8 +332,8 @@ After:
``` python hl_lines="2 3" ``` python hl_lines="2 3"
order_time_in_force: Dict = { order_time_in_force: Dict = {
"entry": "gtc", "entry": "GTC",
"exit": "gtc", "exit": "GTC",
} }
``` ```

View File

@ -23,7 +23,8 @@ REQUIRED_ORDERTIF = ['entry', 'exit']
REQUIRED_ORDERTYPES = ['entry', 'exit', 'stoploss', 'stoploss_on_exchange'] REQUIRED_ORDERTYPES = ['entry', 'exit', 'stoploss', 'stoploss_on_exchange']
PRICING_SIDES = ['ask', 'bid', 'same', 'other'] PRICING_SIDES = ['ask', 'bid', 'same', 'other']
ORDERTYPE_POSSIBILITIES = ['limit', 'market'] ORDERTYPE_POSSIBILITIES = ['limit', 'market']
ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc'] _ORDERTIF_POSSIBILITIES = ['GTC', 'FOK', 'IOC', 'PO']
ORDERTIF_POSSIBILITIES = _ORDERTIF_POSSIBILITIES + [t.lower() for t in _ORDERTIF_POSSIBILITIES]
HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss', HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss',
'SharpeHyperOptLoss', 'SharpeHyperOptLossDaily', 'SharpeHyperOptLoss', 'SharpeHyperOptLossDaily',
'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily', 'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily',

View File

@ -23,8 +23,7 @@ class Binance(Exchange):
_ft_has: Dict = { _ft_has: Dict = {
"stoploss_on_exchange": True, "stoploss_on_exchange": True,
"stoploss_order_types": {"limit": "stop_loss_limit"}, "stoploss_order_types": {"limit": "stop_loss_limit"},
"order_time_in_force": ['gtc', 'fok', 'ioc'], "order_time_in_force": ['GTC', 'FOK', 'IOC'],
"time_in_force_parameter": "timeInForce",
"ohlcv_candle_limit": 1000, "ohlcv_candle_limit": 1000,
"trades_pagination": "id", "trades_pagination": "id",
"trades_pagination_arg": "fromId", "trades_pagination_arg": "fromId",

View File

@ -62,7 +62,7 @@ class Exchange:
# or by specifying them in the configuration. # or by specifying them in the configuration.
_ft_has_default: Dict = { _ft_has_default: Dict = {
"stoploss_on_exchange": False, "stoploss_on_exchange": False,
"order_time_in_force": ["gtc"], "order_time_in_force": ["GTC"],
"time_in_force_parameter": "timeInForce", "time_in_force_parameter": "timeInForce",
"ohlcv_params": {}, "ohlcv_params": {},
"ohlcv_candle_limit": 500, "ohlcv_candle_limit": 500,
@ -611,7 +611,7 @@ class Exchange:
""" """
Checks if order time in force configured in strategy/config are supported Checks if order time in force configured in strategy/config are supported
""" """
if any(v not in self._ft_has["order_time_in_force"] if any(v.upper() not in self._ft_has["order_time_in_force"]
for k, v in order_time_in_force.items()): for k, v in order_time_in_force.items()):
raise OperationalException( raise OperationalException(
f'Time in force policies are not supported for {self.name} yet.') f'Time in force policies are not supported for {self.name} yet.')
@ -989,12 +989,12 @@ class Exchange:
ordertype: str, ordertype: str,
leverage: float, leverage: float,
reduceOnly: bool, reduceOnly: bool,
time_in_force: str = 'gtc', time_in_force: str = 'GTC',
) -> Dict: ) -> Dict:
params = self._params.copy() params = self._params.copy()
if time_in_force != 'gtc' and ordertype != 'market': if time_in_force != 'GTC' and ordertype != 'market':
param = self._ft_has.get('time_in_force_parameter', '') param = self._ft_has.get('time_in_force_parameter', '')
params.update({param: time_in_force}) params.update({param: time_in_force.upper()})
if reduceOnly: if reduceOnly:
params.update({'reduceOnly': True}) params.update({'reduceOnly': True})
return params return params
@ -1009,7 +1009,7 @@ class Exchange:
rate: float, rate: float,
leverage: float, leverage: float,
reduceOnly: bool = False, reduceOnly: bool = False,
time_in_force: str = 'gtc', time_in_force: str = 'GTC',
) -> Dict: ) -> Dict:
if self._config['dry_run']: if self._config['dry_run']:
dry_order = self.create_dry_run_order( dry_order = self.create_dry_run_order(

View File

@ -25,8 +25,7 @@ class Gateio(Exchange):
_ft_has: Dict = { _ft_has: Dict = {
"ohlcv_candle_limit": 1000, "ohlcv_candle_limit": 1000,
"time_in_force_parameter": "timeInForce", "order_time_in_force": ['GTC', 'IOC'],
"order_time_in_force": ['gtc', 'ioc'],
"stoploss_order_types": {"limit": "limit"}, "stoploss_order_types": {"limit": "limit"},
"stoploss_on_exchange": True, "stoploss_on_exchange": True,
} }
@ -57,7 +56,7 @@ class Gateio(Exchange):
ordertype: str, ordertype: str,
leverage: float, leverage: float,
reduceOnly: bool, reduceOnly: bool,
time_in_force: str = 'gtc', time_in_force: str = 'GTC',
) -> Dict: ) -> Dict:
params = super()._get_params( params = super()._get_params(
side=side, side=side,
@ -69,7 +68,7 @@ class Gateio(Exchange):
if ordertype == 'market' and self.trading_mode == TradingMode.FUTURES: if ordertype == 'market' and self.trading_mode == TradingMode.FUTURES:
params['type'] = 'market' params['type'] = 'market'
param = self._ft_has.get('time_in_force_parameter', '') param = self._ft_has.get('time_in_force_parameter', '')
params.update({param: 'ioc'}) params.update({param: 'IOC'})
return params return params
def get_trades_for_order(self, order_id: str, pair: str, since: datetime, def get_trades_for_order(self, order_id: str, pair: str, since: datetime,

View File

@ -171,7 +171,7 @@ class Kraken(Exchange):
ordertype: str, ordertype: str,
leverage: float, leverage: float,
reduceOnly: bool, reduceOnly: bool,
time_in_force: str = 'gtc' time_in_force: str = 'GTC'
) -> Dict: ) -> Dict:
params = super()._get_params( params = super()._get_params(
side=side, side=side,

View File

@ -23,8 +23,7 @@ class Kucoin(Exchange):
"stoploss_order_types": {"limit": "limit", "market": "market"}, "stoploss_order_types": {"limit": "limit", "market": "market"},
"l2_limit_range": [20, 100], "l2_limit_range": [20, 100],
"l2_limit_range_required": False, "l2_limit_range_required": False,
"order_time_in_force": ['gtc', 'fok', 'ioc'], "order_time_in_force": ['GTC', 'FOK', 'IOC'],
"time_in_force_parameter": "timeInForce",
"ohlcv_candle_limit": 1500, "ohlcv_candle_limit": 1500,
} }

View File

@ -98,7 +98,7 @@ class Okx(Exchange):
ordertype: str, ordertype: str,
leverage: float, leverage: float,
reduceOnly: bool, reduceOnly: bool,
time_in_force: str = 'gtc', time_in_force: str = 'GTC',
) -> Dict: ) -> Dict:
params = super()._get_params( params = super()._get_params(
side=side, side=side,

View File

@ -78,8 +78,8 @@ class IStrategy(ABC, HyperStrategyMixin):
# Optional time in force # Optional time in force
order_time_in_force: Dict = { order_time_in_force: Dict = {
'entry': 'gtc', 'entry': 'GTC',
'exit': 'gtc', 'exit': 'GTC',
} }
# run "populate_indicators" only for new candle # run "populate_indicators" only for new candle

View File

@ -88,8 +88,8 @@ class {{ strategy }}(IStrategy):
# Optional order time in force. # Optional order time in force.
order_time_in_force = { order_time_in_force = {
'entry': 'gtc', 'entry': 'GTC',
'exit': 'gtc' 'exit': 'GTC'
} }
{{ plot_config | indent(4) }} {{ plot_config | indent(4) }}

View File

@ -88,8 +88,8 @@ class SampleStrategy(IStrategy):
# Optional order time in force. # Optional order time in force.
order_time_in_force = { order_time_in_force = {
'entry': 'gtc', 'entry': 'GTC',
'exit': 'gtc' 'exit': 'GTC'
} }
plot_config = { plot_config = {

View File

@ -275,7 +275,7 @@ def test_validate_order_time_in_force(default_conf, mocker, caplog):
ex.validate_order_time_in_force(tif2) ex.validate_order_time_in_force(tif2)
# Patch to see if this will pass if the values are in the ft dict # Patch to see if this will pass if the values are in the ft dict
ex._ft_has.update({"order_time_in_force": ["gtc", "fok", "ioc"]}) ex._ft_has.update({"order_time_in_force": ["GTC", "FOK", "IOC"]})
ex.validate_order_time_in_force(tif2) ex.validate_order_time_in_force(tif2)
@ -1503,7 +1503,7 @@ def test_buy_considers_time_in_force(default_conf, mocker, exchange_name):
assert api_mock.create_order.call_args[0][3] == 1 assert api_mock.create_order.call_args[0][3] == 1
assert api_mock.create_order.call_args[0][4] == 200 assert api_mock.create_order.call_args[0][4] == 200
assert "timeInForce" in api_mock.create_order.call_args[0][5] assert "timeInForce" in api_mock.create_order.call_args[0][5]
assert api_mock.create_order.call_args[0][5]["timeInForce"] == time_in_force assert api_mock.create_order.call_args[0][5]["timeInForce"] == time_in_force.upper()
order_type = 'market' order_type = 'market'
time_in_force = 'ioc' time_in_force = 'ioc'
@ -1642,10 +1642,10 @@ def test_sell_considers_time_in_force(default_conf, mocker, exchange_name):
assert api_mock.create_order.call_args[0][3] == 1 assert api_mock.create_order.call_args[0][3] == 1
assert api_mock.create_order.call_args[0][4] == 200 assert api_mock.create_order.call_args[0][4] == 200
assert "timeInForce" in api_mock.create_order.call_args[0][5] assert "timeInForce" in api_mock.create_order.call_args[0][5]
assert api_mock.create_order.call_args[0][5]["timeInForce"] == time_in_force assert api_mock.create_order.call_args[0][5]["timeInForce"] == time_in_force.upper()
order_type = 'market' order_type = 'market'
time_in_force = 'ioc' time_in_force = 'IOC'
order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell",
amount=1, rate=200, leverage=1.0, amount=1, rate=200, leverage=1.0,
time_in_force=time_in_force) time_in_force=time_in_force)
@ -3319,7 +3319,7 @@ def test_merge_ft_has_dict(default_conf, mocker):
ex = Binance(default_conf) ex = Binance(default_conf)
assert ex._ft_has != Exchange._ft_has_default assert ex._ft_has != Exchange._ft_has_default
assert ex.get_option('stoploss_on_exchange') assert ex.get_option('stoploss_on_exchange')
assert ex.get_option('order_time_in_force') == ['gtc', 'fok', 'ioc'] assert ex.get_option('order_time_in_force') == ['GTC', 'FOK', 'IOC']
assert ex.get_option('trades_pagination') == 'id' assert ex.get_option('trades_pagination') == 'id'
assert ex.get_option('trades_pagination_arg') == 'fromId' assert ex.get_option('trades_pagination_arg') == 'fromId'
@ -4954,7 +4954,7 @@ def test__get_params(mocker, default_conf, exchange_name):
params1 = {'test': True} params1 = {'test': True}
params2 = { params2 = {
'test': True, 'test': True,
'timeInForce': 'ioc', 'timeInForce': 'IOC',
'reduceOnly': True, 'reduceOnly': True,
} }
@ -4969,7 +4969,7 @@ def test__get_params(mocker, default_conf, exchange_name):
side="buy", side="buy",
ordertype='market', ordertype='market',
reduceOnly=False, reduceOnly=False,
time_in_force='gtc', time_in_force='GTC',
leverage=1.0, leverage=1.0,
) == params1 ) == params1
@ -4977,7 +4977,7 @@ def test__get_params(mocker, default_conf, exchange_name):
side="buy", side="buy",
ordertype='market', ordertype='market',
reduceOnly=False, reduceOnly=False,
time_in_force='ioc', time_in_force='IOC',
leverage=1.0, leverage=1.0,
) == params1 ) == params1
@ -4985,7 +4985,7 @@ def test__get_params(mocker, default_conf, exchange_name):
side="buy", side="buy",
ordertype='limit', ordertype='limit',
reduceOnly=False, reduceOnly=False,
time_in_force='gtc', time_in_force='GTC',
leverage=1.0, leverage=1.0,
) == params1 ) == params1
@ -4998,7 +4998,7 @@ def test__get_params(mocker, default_conf, exchange_name):
side="buy", side="buy",
ordertype='limit', ordertype='limit',
reduceOnly=True, reduceOnly=True,
time_in_force='ioc', time_in_force='IOC',
leverage=3.0, leverage=3.0,
) == params2 ) == params2

View File

@ -50,7 +50,7 @@ def test_buy_kraken_trading_agreement(default_conf, mocker):
assert api_mock.create_order.call_args[0][2] == 'buy' assert api_mock.create_order.call_args[0][2] == 'buy'
assert api_mock.create_order.call_args[0][3] == 1 assert api_mock.create_order.call_args[0][3] == 1
assert api_mock.create_order.call_args[0][4] == 200 assert api_mock.create_order.call_args[0][4] == 200
assert api_mock.create_order.call_args[0][5] == {'timeInForce': 'ioc', assert api_mock.create_order.call_args[0][5] == {'timeInForce': 'IOC',
'trading_agreement': 'agree'} 'trading_agreement': 'agree'}

View File

@ -275,8 +275,8 @@ def test_strategy_override_order_tif(caplog, default_conf):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
order_time_in_force = { order_time_in_force = {
'entry': 'fok', 'entry': 'FOK',
'exit': 'gtc', 'exit': 'GTC',
} }
default_conf.update({ default_conf.update({
@ -290,11 +290,11 @@ def test_strategy_override_order_tif(caplog, default_conf):
assert strategy.order_time_in_force[method] == order_time_in_force[method] assert strategy.order_time_in_force[method] == order_time_in_force[method]
assert log_has("Override strategy 'order_time_in_force' with value in config file:" assert log_has("Override strategy 'order_time_in_force' with value in config file:"
" {'entry': 'fok', 'exit': 'gtc'}.", caplog) " {'entry': 'FOK', 'exit': 'GTC'}.", caplog)
default_conf.update({ default_conf.update({
'strategy': CURRENT_TEST_STRATEGY, 'strategy': CURRENT_TEST_STRATEGY,
'order_time_in_force': {'entry': 'fok'} 'order_time_in_force': {'entry': 'FOK'}
}) })
# Raise error for invalid configuration # Raise error for invalid configuration
with pytest.raises(ImportError, with pytest.raises(ImportError,

View File

@ -973,17 +973,17 @@ def test_validate_time_in_force(default_conf, caplog) -> None:
conf = deepcopy(default_conf) conf = deepcopy(default_conf)
conf['order_time_in_force'] = { conf['order_time_in_force'] = {
'buy': 'gtc', 'buy': 'gtc',
'sell': 'gtc', 'sell': 'GTC',
} }
validate_config_consistency(conf) validate_config_consistency(conf)
assert log_has_re(r"DEPRECATED: Using 'buy' and 'sell' for time_in_force is.*", caplog) assert log_has_re(r"DEPRECATED: Using 'buy' and 'sell' for time_in_force is.*", caplog)
assert conf['order_time_in_force']['entry'] == 'gtc' assert conf['order_time_in_force']['entry'] == 'gtc'
assert conf['order_time_in_force']['exit'] == 'gtc' assert conf['order_time_in_force']['exit'] == 'GTC'
conf = deepcopy(default_conf) conf = deepcopy(default_conf)
conf['order_time_in_force'] = { conf['order_time_in_force'] = {
'buy': 'gtc', 'buy': 'GTC',
'sell': 'gtc', 'sell': 'GTC',
} }
conf['trading_mode'] = 'futures' conf['trading_mode'] = 'futures'
with pytest.raises(OperationalException, with pytest.raises(OperationalException,