From 16f93bc2b42509e735b3b807ee7d197f219f3233 Mon Sep 17 00:00:00 2001 From: Peter Valachovic Date: Tue, 28 Nov 2023 21:17:16 +0100 Subject: [PATCH 1/7] added support type_of_instrument parameter in open_trade for stc/cfd instruments --- README.md | 16 ++++++++++------ XTBApi/api.py | 6 +++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fe92a07..3e530dc 100644 --- a/README.md +++ b/README.md @@ -54,15 +54,19 @@ def get_expiration_timeStamp(minutes): #specify timestamp for order Some example usage of client.open_trade with/without SL/TP and using volume/dollars ```python # Open trade with SL/TP with volume 1, TP 5%, SL 5%, expiration in 60 minutes -client.open_trade('buy', 'ETHEREUM', volume=1, custom_Message="buy",tp_per = 0.05, sl_per= 0.05,expiration_stamp=get_expiration_timeStamp(60)) +client.open_trade('buy', 'ETHEREUM', volume=1, custom_message="buy",tp_per = 0.05, sl_per= 0.05,expiration_stamp=get_expiration_timeStamp(60)) # Open trade without SL/TP with volume 10 -client.open_trade('buy', 'VWCE.DE', volume=10, custom_Message="buy") +client.open_trade('buy', 'VWCE.DE', volume=10, custom_message="buy") # Open trade without SL/TP with volume 1000 -client.open_trade('buy', 'CARDANO', volume=1000, custom_Message="buy") +client.open_trade('buy', 'CARDANO', volume=1000, custom_message="buy") # Open trade with 'volume=dollars/price' and you specify dollar size of trade, volume is rounded to accomotade 'lotStep' multiply -client.open_trade('buy', 'CARDANO', dollars=1000, custom_Message="buy") -# Open trade without SL/TP, with 'volume=dollars/price' and you specify dollar size of trade, volume is rounded to accomotade 'lotStep' multiply -client.open_trade('buy', 'VWCE.DE', dollars=1000, custom_Message="buy") +client.open_trade('buy', 'CARDANO', dollars=1000, custom_message="buy") +# Open trade without SL/TP, with 'volume=dollars/price' and you specify dollar size of trade, volume is rounded to accomotade 'lotStep' multiply +client.open_trade('buy', 'VWCE.DE', dollars=1000, custom_message="buy") +# Open trade with SL/TP, with volume 10 on instrument O.US CFD - internal XTB ticker is O.US_4 +client.open_trade('buy', 'O.US', type_of_instrument='cfd', volume=10, custom_message="buy",tp_per = 0.05, sl_per= 0.05) +# Open trade without SL/TP, with volume 10 on instrument O.US - internal XTB ticker is O.US_9 +client.open_trade('buy', 'O.US', type_of_instrument='stc',volume=10, custom_message="buy") ``` # Api Reference diff --git a/XTBApi/api.py b/XTBApi/api.py index c43874b..be860d7 100644 --- a/XTBApi/api.py +++ b/XTBApi/api.py @@ -431,7 +431,7 @@ def get_trade_profit(self, trans_id): return profit def open_trade(self, mode, symbol, volume =0, dollars=0, custom_message ="", - tp_per = 0.00, sl_per= 0.00,order_margin_per = 0, expiration_stamp = 0): + tp_per = 0.00, sl_per= 0.00, type_of_instrument ="", order_margin_per = 0, expiration_stamp = 0): """open trade transaction""" self.logger.debug("dollars = %s", dollars) if mode in [MODES.BUY.value, MODES.SELL.value]: @@ -441,6 +441,10 @@ def open_trade(self, mode, symbol, volume =0, dollars=0, custom_message ="", mode = modes[mode] else: raise ValueError("mode can be buy or sell") + if type_of_instrument == "stc": + symbol = symbol + "_9" + elif type_of_instrument == "cfd": + symbol = symbol + "_4" price, price_2 = self.get_prices_operate(mode, symbol) if order_margin_per != 0: # https://www.xtb.com/int/education/xstation-5-pending-orders From 7a9879f4379c45d322cbfc3c5c93ce89d2dbc23a Mon Sep 17 00:00:00 2001 From: Peter Valachovic Date: Tue, 28 Nov 2023 21:20:30 +0100 Subject: [PATCH 2/7] fix another pylinter findings --- XTBApi/api.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/XTBApi/api.py b/XTBApi/api.py index be860d7..f7f0009 100644 --- a/XTBApi/api.py +++ b/XTBApi/api.py @@ -183,7 +183,7 @@ def get_chart_last_request(self, symbol, period, start): "symbol": symbol } data = _get_data("getChartLastRequest", info=args) - self.logger.info("CMD: get chart last request for %s of period %s from %s ...", + self.logger.info("CMD: get chart last request for %s of period %s from %s ...", symbol, period, start) return self._send_command_with_check(data) @@ -199,7 +199,7 @@ def get_chart_range_request(self, symbol, period, start, end, ticks): "ticks": ticks } data = _get_data("getChartRangeRequest", info=args) - self.logger.info("CMD: get chart range request for %s of %s from %s to %s with ticks of %s", + self.logger.info("CMD: get chart range request for %s of %s from %s to %s with ticks of %s", symbol, period, start, end, ticks) return self._send_command_with_check(data) @@ -233,7 +233,7 @@ def get_profit_calculation(self, symbol, mode, volume, op_price, cl_price): data = _get_data("getProfitCalculation", closePrice=cl_price, cmd=mode, openPrice=op_price, symbol=symbol, volume=volume) - self.logger.info("CMD: get profit calculation for %s of %i from %f to %f in mode %s...", + self.logger.info("CMD: get profit calculation for %s of %i from %f to %f in mode %s...", symbol, volume, op_price, cl_price, mode) return self._send_command_with_check(data) @@ -253,7 +253,7 @@ def get_tick_prices(self, symbols, start, level=0): """getTickPrices command""" data = _get_data("getTickPrices", level=level, symbols=symbols, timestamp=start) - self.logger.info("CMD: get tick prices of %s from %s with level %s...", + self.logger.info("CMD: get tick prices of %s from %s with level %s...", symbols, start, level ) return self._send_command_with_check(data) @@ -325,7 +325,7 @@ def trade_transaction(self, symbol, mode, trans_type, volume, **kwargs): name_of_mode = [x.name for x in MODES if x.value == mode][0] name_of_type = [x.name for x in TRANS_TYPES if x.value == trans_type][0] - self.logger.info("CMD: trade transaction of %s of mode %s with type %s of %i", + self.logger.info("CMD: trade transaction of %s of mode %s with type %s of %i", symbol, name_of_mode, name_of_type, volume) return self._send_command_with_check(data) @@ -430,7 +430,7 @@ def get_trade_profit(self, trans_id): self.logger.info("got trade profit of %s", profit) return profit - def open_trade(self, mode, symbol, volume =0, dollars=0, custom_message ="", + def open_trade(self, mode, symbol, volume =0, dollars=0, custom_message ="", tp_per = 0.00, sl_per= 0.00, type_of_instrument ="", order_margin_per = 0, expiration_stamp = 0): """open trade transaction""" self.logger.debug("dollars = %s", dollars) @@ -452,7 +452,7 @@ def open_trade(self, mode, symbol, volume =0, dollars=0, custom_message ="", else: mode_name = mode.name mode = mode.value - self.logger.debug("opening trade of %s of Dollars: %i with %s Expiration: %s", + self.logger.debug("opening trade of %s of Dollars: %i with %s Expiration: %s", symbol, dollars, mode_name, datetime.fromtimestamp(expiration_stamp/1000)) price = round(price * (1 + order_margin_per) , 2) if dollars != 0: @@ -473,23 +473,23 @@ def open_trade(self, mode, symbol, volume =0, dollars=0, custom_message ="", volume = round(volume, -2) sl, tp = self.get_tp_sl(mode, price, sl_per, tp_per) if tp_per == 0 and sl_per == 0: - response = self.trade_transaction(symbol, mode, trans_type = 0,volume = volume, + response = self.trade_transaction(symbol, mode, trans_type = 0,volume = volume, price=price, customComment=custom_message, expiration = expiration_stamp) #open trade without SL/TP status, status_messg = self.manage_response(expiration_stamp, response) else: - response = self.trade_transaction(symbol, mode, trans_type = 0,volume = volume, + response = self.trade_transaction(symbol, mode, trans_type = 0,volume = volume, price=price, customComment=custom_message, tp=tp, sl=sl,expiration = expiration_stamp) #open trade with SL/TP status, status_messg = self.manage_response(expiration_stamp, response) if status_messg == 'Invalid prices(limit)': self.logger.debug("FAIL. opening trade of %s Message: %s Stock: %s", symbol, status_messg, symbol) - response = self.trade_transaction(symbol, mode, trans_type=0, volume=volume, + response = self.trade_transaction(symbol, mode, trans_type=0, volume=volume, price=price_2,customComment=custom_message, expiration=expiration_stamp) status, status_messg = self.manage_response(expiration_stamp, response) price = price_2 if status_messg == 'Invalid s/l or t/p price': sl, tp = self.get_tp_sl(mode, price, sl_per+ 0.012, tp_per+ 0.012) self.logger.debug("FAIL. opening trade of %s Message: %s Stock: %s", symbol, status_messg, symbol) - response = self.trade_transaction(symbol, mode, trans_type=0, volume=volume, + response = self.trade_transaction(symbol, mode, trans_type=0, volume=volume, price=price,customComment=custom_message, expiration=expiration_stamp) status, status_messg = self.manage_response(expiration_stamp, response) if status_messg == 'SL/TP order not supported' or status_messg == 'Short selling not available': @@ -497,12 +497,12 @@ def open_trade(self, mode, symbol, volume =0, dollars=0, custom_message ="", return response if status_messg == 'Invalid nominal': #if you want to trade something that needs multiple different than 0.01, 0.1, 1.0 or 10.0 self.logger.debug("FAIL. opening trade of %s Message: %s Stock: %s", symbol, status_messg, symbol) - response = self.trade_transaction(symbol, mode, trans_type=0, volume=volume, + response = self.trade_transaction(symbol, mode, trans_type=0, volume=volume, price=price,customComment=custom_message, expiration=expiration_stamp) status, status_messg = self.manage_response(expiration_stamp, response) if status_messg == 'Market closed': self.logger.debug("FAIL. opening trade of %s Message: %s Stock: %s", symbol, status_messg, symbol) - response = self.trade_transaction(symbol, mode, trans_type=0, volume=volume, + response = self.trade_transaction(symbol, mode, trans_type=0, volume=volume, price=price,customComment=custom_message, expiration=expiration_stamp) status, status_messg = self.manage_response(expiration_stamp, response) if status != 3: @@ -542,7 +542,7 @@ def manage_response(self, expiration_stamp, response): status_rep = self.trade_transaction_status(response['order']) status = status_rep['requestStatus'] status_messg = status_rep['message'] - self.logger.debug("open_trade completed with status of %s Message: %s Expiration: %s", + self.logger.debug("open_trade completed with status of %s Message: %s Expiration: %s", status, status_messg, datetime.fromtimestamp(expiration_stamp/1000)) return status, status_messg From a4e6071af80d1ff8e4622258049eb85396425f09 Mon Sep 17 00:00:00 2001 From: Peter Valachovic Date: Tue, 28 Nov 2023 21:46:59 +0100 Subject: [PATCH 3/7] fix another pylinter findings --- XTBApi/api.py | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/XTBApi/api.py b/XTBApi/api.py index f7f0009..3f22976 100644 --- a/XTBApi/api.py +++ b/XTBApi/api.py @@ -431,7 +431,8 @@ def get_trade_profit(self, trans_id): return profit def open_trade(self, mode, symbol, volume =0, dollars=0, custom_message ="", - tp_per = 0.00, sl_per= 0.00, type_of_instrument ="", order_margin_per = 0, expiration_stamp = 0): + tp_per = 0.00, sl_per= 0.00, type_of_instrument ="", + order_margin_per = 0, expiration_stamp = 0): """open trade transaction""" self.logger.debug("dollars = %s", dollars) if mode in [MODES.BUY.value, MODES.SELL.value]: @@ -474,43 +475,53 @@ def open_trade(self, mode, symbol, volume =0, dollars=0, custom_message ="", sl, tp = self.get_tp_sl(mode, price, sl_per, tp_per) if tp_per == 0 and sl_per == 0: response = self.trade_transaction(symbol, mode, trans_type = 0,volume = volume, - price=price, customComment=custom_message, expiration = expiration_stamp) #open trade without SL/TP + price=price, customComment=custom_message, + expiration = expiration_stamp) #open trade without SL/TP status, status_messg = self.manage_response(expiration_stamp, response) else: response = self.trade_transaction(symbol, mode, trans_type = 0,volume = volume, - price=price, customComment=custom_message, tp=tp, sl=sl,expiration = expiration_stamp) #open trade with SL/TP + price=price, customComment=custom_message, tp=tp, sl=sl, + expiration = expiration_stamp) #open trade with SL/TP status, status_messg = self.manage_response(expiration_stamp, response) if status_messg == 'Invalid prices(limit)': - self.logger.debug("FAIL. opening trade of %s Message: %s Stock: %s", symbol, status_messg, symbol) + self.logger.debug("FAIL. opening trade of %s Message: %s Stock: %s", + symbol, status_messg, symbol) response = self.trade_transaction(symbol, mode, trans_type=0, volume=volume, - price=price_2,customComment=custom_message, expiration=expiration_stamp) + price=price_2,customComment=custom_message, expiration=expiration_stamp) status, status_messg = self.manage_response(expiration_stamp, response) price = price_2 if status_messg == 'Invalid s/l or t/p price': sl, tp = self.get_tp_sl(mode, price, sl_per+ 0.012, tp_per+ 0.012) - self.logger.debug("FAIL. opening trade of %s Message: %s Stock: %s", symbol, status_messg, symbol) + self.logger.debug("FAIL. opening trade of %s Message: %s Stock: %s", + symbol, status_messg, symbol) response = self.trade_transaction(symbol, mode, trans_type=0, volume=volume, - price=price,customComment=custom_message, expiration=expiration_stamp) + price=price,customComment=custom_message, expiration=expiration_stamp) status, status_messg = self.manage_response(expiration_stamp, response) - if status_messg == 'SL/TP order not supported' or status_messg == 'Short selling not available': - self.logger.debug("FAIL. opening trade of %s Message: %s Stock: %s", symbol, status_messg, symbol) + if status_messg in ('SL/TP order not supported','Short selling not available'): + self.logger.debug("FAIL. opening trade of %s Message: %s Stock: %s", + symbol, status_messg, symbol) return response - if status_messg == 'Invalid nominal': #if you want to trade something that needs multiple different than 0.01, 0.1, 1.0 or 10.0 - self.logger.debug("FAIL. opening trade of %s Message: %s Stock: %s", symbol, status_messg, symbol) + if status_messg == 'Invalid nominal': + self.logger.debug("FAIL. opening trade of %s Message: %s Stock: %s", + symbol, status_messg, symbol) response = self.trade_transaction(symbol, mode, trans_type=0, volume=volume, - price=price,customComment=custom_message, expiration=expiration_stamp) + price=price,customComment=custom_message, expiration=expiration_stamp) status, status_messg = self.manage_response(expiration_stamp, response) if status_messg == 'Market closed': - self.logger.debug("FAIL. opening trade of %s Message: %s Stock: %s", symbol, status_messg, symbol) + self.logger.debug("FAIL. opening trade of %s Message: %s Stock: %s", + symbol, status_messg, symbol) response = self.trade_transaction(symbol, mode, trans_type=0, volume=volume, - price=price,customComment=custom_message, expiration=expiration_stamp) + price=price,customComment=custom_message, expiration=expiration_stamp) status, status_messg = self.manage_response(expiration_stamp, response) if status != 3: - self.logger.debug("FAIL. opening trade of %s Message: %s Stock: %s of Dollars %i with volume %i with Expiration: %s", - symbol, status_messg, symbol, dollars, volume, datetime.fromtimestamp(expiration_stamp / 1000)) + self.logger.debug("""FAIL. opening trade of %s Message: %s Stock: %s of Dollars: + %i with volume %i with Expiration: %s""", + symbol, status_messg, symbol, dollars, volume, + datetime.fromtimestamp(expiration_stamp / 1000)) else: - self.logger.debug("Successfully. opening trade of %s of Dollars: %i with Expiration: %s", - symbol, dollars, datetime.fromtimestamp(expiration_stamp/1000)) + self.logger.debug("""Successfully. opening trade of %s of Dollars: + %i with Expiration: %s""", + symbol, dollars, datetime.fromtimestamp(expiration_stamp/1000)) return response def get_tp_sl(self, mode, price, sl_per, tp_per): @@ -543,7 +554,7 @@ def manage_response(self, expiration_stamp, response): status = status_rep['requestStatus'] status_messg = status_rep['message'] self.logger.debug("open_trade completed with status of %s Message: %s Expiration: %s", - status, status_messg, datetime.fromtimestamp(expiration_stamp/1000)) + status, status_messg, datetime.fromtimestamp(expiration_stamp/1000)) return status, status_messg def change_to_order_type_mode(self, mode_name): From 86f0dc048ff9b03d8f7ba24dc77a279dc8eaf73e Mon Sep 17 00:00:00 2001 From: Peter Valachovic Date: Tue, 28 Nov 2023 21:56:28 +0100 Subject: [PATCH 4/7] add install of pip packages to gh action --- .github/workflows/pylint.yml | 2 +- XTBApi/api.py | 4 ++-- XTBApi/requirements.txt | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 XTBApi/requirements.txt diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 383e65c..e77e363 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -17,7 +17,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install pylint + pip install pylint enum json logging time datetime websocket_client - name: Analysing the code with pylint run: | pylint $(git ls-files '*.py') diff --git a/XTBApi/api.py b/XTBApi/api.py index 3f22976..8b83e94 100644 --- a/XTBApi/api.py +++ b/XTBApi/api.py @@ -526,10 +526,10 @@ def open_trade(self, mode, symbol, volume =0, dollars=0, custom_message ="", def get_tp_sl(self, mode, price, sl_per, tp_per): self: self@Client - if mode == MODES.BUY.value or mode == MODES.BUY_LIMIT.value: + if mode in (MODES.BUY.value, MODES.BUY_LIMIT.value): tp = round(price * (1 + tp_per), 2) sl = round(price * (1 - sl_per), 2) - elif mode == MODES.SELL.value or mode == MODES.SELL_LIMIT.value: + elif mode in (MODES.SELL.value, MODES.SELL_LIMIT.value): sl = round(price * (1 + sl_per), 2) tp = round(price * (1 - tp_per), 2) return sl, tp diff --git a/XTBApi/requirements.txt b/XTBApi/requirements.txt new file mode 100644 index 0000000..19a0651 --- /dev/null +++ b/XTBApi/requirements.txt @@ -0,0 +1,3 @@ +pytest==7.4.3 +websocket_client==1.6.4 +XTBApi==1.0a7 From 94d6871770a700b2b6811d6eb5a8bb1b3daa60f9 Mon Sep 17 00:00:00 2001 From: Peter Valachovic Date: Tue, 28 Nov 2023 22:02:52 +0100 Subject: [PATCH 5/7] add install of pip packages to gh action --- .github/workflows/pylint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index e77e363..46fef89 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -17,7 +17,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install pylint enum json logging time datetime websocket_client + pip install wheel pylint enum json logging time datetime websocket_client - name: Analysing the code with pylint run: | pylint $(git ls-files '*.py') From 3a19b4ee57a189707811644e84c2d47bc38b580e Mon Sep 17 00:00:00 2001 From: Peter Valachovic Date: Tue, 28 Nov 2023 22:03:37 +0100 Subject: [PATCH 6/7] add install of pip packages to gh action --- .github/workflows/pylint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 46fef89..383e65c 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -17,7 +17,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install wheel pylint enum json logging time datetime websocket_client + pip install pylint - name: Analysing the code with pylint run: | pylint $(git ls-files '*.py') From 0b68317707707a727152eea865a4be55ce0a28c0 Mon Sep 17 00:00:00 2001 From: Peter Valachovic Date: Tue, 28 Nov 2023 22:10:21 +0100 Subject: [PATCH 7/7] add install of pip packages to gh action --- XTBApi/requirements.txt | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 XTBApi/requirements.txt diff --git a/XTBApi/requirements.txt b/XTBApi/requirements.txt deleted file mode 100644 index 19a0651..0000000 --- a/XTBApi/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -pytest==7.4.3 -websocket_client==1.6.4 -XTBApi==1.0a7