ejtraderLabs / ejtraderCT

The best Python Ctrader FIX API Perfect for HFT
MIT License
59 stars 20 forks source link

There seems to be an issue with executing orders after they have been successfully executed twice before. #25

Open amircp opened 1 year ago

amircp commented 1 year ago

Hi, After executing two consecutive trades using BUY and SELL Market types the next ones cannot be executed the log says it were executed but they aren't.

For example i open a Buy order and then i try to close it using the method close_all() everything works fine. (I use this method because the close position by id doesn't work at all).

After that first successful execution if i try to open another one (in the same session) the new buy order executes correctly but if i try to use again the close_all method the buy order still persists in the market.

Another scenery is: I can open many buy market orders but i cannot close them (using the close all method).

my implementations:

Buy ORDER

  if not self.api.isconnected():
            raise CTraderDisconnected("CTrader is not connected")
        try:
            logger.info(f"🕦 Buying ${order.quantity} from {order.symbol}")

            price = self.api.quote()
            logger.info(f"Quote: {price=}")
            price = price[order.symbol]["bid"]

            symbol = order.symbol
            volume = order.quantity
            stop_loss = 0
            take_profit = 0

            oid = self.api.buy(symbol=symbol, volume=volume, stoploss=stop_loss, takeprofit=take_profit)
            logger.info(f"CTrader Position {oid}")

Close all:

  positions = self.api.positions()
            if len(positions) < 1:
                break

            logger.info(positions)
            self.api.close_all()
            time.sleep(5)

Sometimes, the second try produces an unexpected infinite loop with a number of 16 digits.

I have some thoughts on what might be going on, but I need to test and debug those ideas when the market opens with a demo account.

Cheers

traderpedroso commented 1 year ago

First and foremost, my friend, I appreciate your input on the subject of consecutive order placements. I've run into some trouble with Jupyter Notebook and the fix api, however, I plan to run some tests based on your insights. Please know that any PRs are highly encouraged. I apologize for my late response, I've been heavily involved in an AI project aimed at the financial market recently. I intend to perform the tests this week and aim to resolve some bugs. I've observed an issue with the FIX API concerning pauses between successive executions - this hasn't always been the case. I've also noted disparities across different brokers, particularly between demo and real accounts.

Hi, After executing two consecutive trades using BUY and SELL Market types the next ones cannot be executed the log says it were executed but they aren't.

For example i open a Buy order and then i try to close it using the method close_all() everything works fine. (I use this method because the close position by id doesn't work at all).

After that first successful execution if i try to open another one (in the same session) the new buy order executes correctly but if i try to use again the close_all method the buy order still persists in the market.

Another scenery is: I can open many buy market orders but i cannot close them (using the close all method).

my implementations:

Buy ORDER

  if not self.api.isconnected():
            raise CTraderDisconnected("CTrader is not connected")
        try:
            logger.info(f"🕦 Buying ${order.quantity} from {order.symbol}")

            price = self.api.quote()
            logger.info(f"Quote: {price=}")
            price = price[order.symbol]["bid"]

            symbol = order.symbol
            volume = order.quantity
            stop_loss = 0
            take_profit = 0

            oid = self.api.buy(symbol=symbol, volume=volume, stoploss=stop_loss, takeprofit=take_profit)
            logger.info(f"CTrader Position {oid}")

Close all:

  positions = self.api.positions()
            if len(positions) < 1:
                break

            logger.info(positions)
            self.api.close_all()
            time.sleep(5)

Sometimes, the second try produces an unexpected infinite loop with a number of 16 digits.

I have some thoughts on what might be going on, but I need to test and debug those ideas when the market opens with a demo account.

Cheers

First and foremost, my friend, I appreciate your input on the subject of consecutive order placements. I've run into some trouble with Jupyter Notebook and the fix api, however, I plan to run some tests based on your insights. Please know that any PRs are highly encouraged. I apologize for my late response, I've been heavily involved in an AI project aimed at the financial market recently. I intend to perform the tests this week and aim to resolve some bugs. I've observed an issue with the FIX API concerning pauses between successive executions - this hasn't always been the case. I've also noted disparities across different brokers, particularly between demo and real accounts.

amircp commented 1 year ago

First and foremost, my friend, I appreciate your input on the subject of consecutive order placements. I've run into some trouble with Jupyter Notebook and the fix api, however, I plan to run some tests based on your insights. Please know that any PRs are highly encouraged. I apologize for my late response, I've been heavily involved in an AI project aimed at the financial market recently. I intend to perform the tests this week and aim to resolve some bugs. I've observed an issue with the FIX API concerning pauses between successive executions - this hasn't always been the case. I've also noted disparities across different brokers, particularly between demo and real accounts.

Hi, I'm working on my spare time, and currently, I haven't had any success in trying to fix the problems. I am currently trying to understand how the positions are handled in the position list attribute, and it's good to know that PRs are welcome. Once I fix the positions problem, I'll be creating one.

I'd also like to mention that I have noticed the differences related to demo and real accounts.

btw, Congratulations on your AI project, and I wish you the best!

amircp commented 1 year ago

I have found the problem.

When tag 35="AP" is received from the Broker, the parse_trade_message method searches in the binary data for the end of the FIX message, which in all cases is the checksum tag (tag 10)

match = re.search(rb"10=\d{3}\x01", self.tstream.peek(self.tstream.count()))

However, the FIX message received contains various messages grouped into just one response, as shown in the following example:

b'8=FIX.4.4\x019=162\x0135=AP\x0134=2\x0149=CSERVER\x0150=TRADE\x0152=20230804-19:55:06.286\x0156=demo.icmarkets.0000000\x0157=TRADE\x0155=10014\x01710=1\x01721=349730793\x01727=2\x01728=0\x01730=15347.9\x01702=1\x01704=1\x01705=0\x0110=218\x018=FIX.4.4\x019=162\x0135=AP\x0134=3\x0149=CSERVER\x0150=TRADE\x0152=20230804-19:55:06.286\x0156=demo.icmarkets.0000000\x0157=TRADE\x0155=10014\x01710=1\x01721=349731225\x01727=2\x01728=0\x01730=15349.8\x01702=1\x01704=1\x01705=0\x0110=211\x018=FIX.4.4\x019=137\x0135=j\x0134=4\x0149=CSERVER\x0150=TRADE\x0152=20230804-19:55:06.286\x0156=demo.icmarkets.0000000\x0157=TRADE\x0158=ORDER_NOT_FOUND:no orders found\x01379=1\x01380=0\x0110=078\x01'

If we carefully look into the received response, we can split it into 3 FIX messages:

b'8=FIX.4.4\x019=162\x0135=AP\x0134=2\x0149=CSERVER\x0150=TRADE\x0152=20230804-19:55:06.286\x0156=demo.icmarkets.0000000\x0157=TRADE\x0155=10014\x01710=1\x01721=349730793\x01727=2\x01728=0\x01730=15347.9\x01702=1\x01704=1\x01705=0\x0110=218

8=FIX.4.4\x019=162\x0135=AP\x0134=3\x0149=CSERVER\x0150=TRADE\x0152=20230804-19:55:06.286\x0156=demo.icmarkets.0000000\x0157=TRADE\x0155=10014\x01710=1\x01721=349731225\x01727=2\x01728=0\x01730=15349.8\x01702=1\x01704=1\x01705=0\x0110=211

18=FIX.4.4\x019=137\x0135=j\x0134=4\x0149=CSERVER\x0150=TRADE\x0152=20230804-19:55:06.286\x0156=demo.icmarkets.0000000\x0157=TRADE\x0158=ORDER_NOT_FOUND:no orders found\x01379=1\x01380=0\x0110=078\x01'

Unfortunately, the regex is matching just the first one, resulting in parsing only the first message and causing the position list to contain only ONE position. This is why we can't close the other orders. Furthermore, related to this problem, the position list callback prevents the right execution and filling of the position list. (This is why subsequent orders cannot be closed after closing the first one).

I have completed half of the fix, but I think I will need to refactor the callback for positions.

traderpedroso commented 1 year ago

I have found the problem.

When tag 35="AP" is received from the Broker, the parse_trade_message method searches in the binary data for the end of the FIX message, which in all cases is the checksum tag (tag 10)

match = re.search(rb"10=\d{3}\x01", self.tstream.peek(self.tstream.count()))

However, the FIX message received contains various messages grouped into just one response, as shown in the following example:

b'8=FIX.4.4\x019=162\x0135=AP\x0134=2\x0149=CSERVER\x0150=TRADE\x0152=20230804-19:55:06.286\x0156=demo.icmarkets.0000000\x0157=TRADE\x0155=10014\x01710=1\x01721=349730793\x01727=2\x01728=0\x01730=15347.9\x01702=1\x01704=1\x01705=0\x0110=218\x018=FIX.4.4\x019=162\x0135=AP\x0134=3\x0149=CSERVER\x0150=TRADE\x0152=20230804-19:55:06.286\x0156=demo.icmarkets.0000000\x0157=TRADE\x0155=10014\x01710=1\x01721=349731225\x01727=2\x01728=0\x01730=15349.8\x01702=1\x01704=1\x01705=0\x0110=211\x018=FIX.4.4\x019=137\x0135=j\x0134=4\x0149=CSERVER\x0150=TRADE\x0152=20230804-19:55:06.286\x0156=demo.icmarkets.0000000\x0157=TRADE\x0158=ORDER_NOT_FOUND:no orders found\x01379=1\x01380=0\x0110=078\x01'

If we carefully look into the received response, we can split it into 3 FIX messages:

b'8=FIX.4.4\x019=162\x0135=AP\x0134=2\x0149=CSERVER\x0150=TRADE\x0152=20230804-19:55:06.286\x0156=demo.icmarkets.0000000\x0157=TRADE\x0155=10014\x01710=1\x01721=349730793\x01727=2\x01728=0\x01730=15347.9\x01702=1\x01704=1\x01705=0\x0110=218

8=FIX.4.4\x019=162\x0135=AP\x0134=3\x0149=CSERVER\x0150=TRADE\x0152=20230804-19:55:06.286\x0156=demo.icmarkets.0000000\x0157=TRADE\x0155=10014\x01710=1\x01721=349731225\x01727=2\x01728=0\x01730=15349.8\x01702=1\x01704=1\x01705=0\x0110=211

18=FIX.4.4\x019=137\x0135=j\x0134=4\x0149=CSERVER\x0150=TRADE\x0152=20230804-19:55:06.286\x0156=demo.icmarkets.0000000\x0157=TRADE\x0158=ORDER_NOT_FOUND:no orders found\x01379=1\x01380=0\x0110=078\x01'

Unfortunately, the regex is matching just the first one, resulting in parsing only the first message and causing the position list to contain only ONE position. This is why we can't close the other orders. Furthermore, related to this problem, the position list callback prevents the right execution and filling of the position list. (This is why subsequent orders cannot be closed after closing the first one).

I have completed half of the fix, but I think I will need to refactor the callback for positions. Good evening, my friend. I apologize for the delay in responding. Lately, I've been extremely busy and haven't had the time to check. I'm hopeful that next week will be less hectic, and I'll definitely review this matter. I'll revisit the document to see if there have been any updates or if it's an initial project bug. Thank you for your cooperation. I intend to return promptly to focus on the projects at EJTrader.

Greg-Mukuria commented 10 months ago

Nice, work. How about an implementation of the same in rust, for latency's sake?

traderpedroso commented 8 months ago

Nice, work. How about an implementation of the same in rust, for latency's sake?

I conducted some tests in Rust and observed no significant change in speed compared to Python. I believe Python is adequately serving our needs in this context.