blankly-finance / blankly

🚀 💸 Easily build, backtest and deploy your algo in just a few lines of code. Trade stocks, cryptos, and forex across exchanges w/ one package.
https://package.blankly.finance
GNU Lesser General Public License v3.0
2.03k stars 261 forks source link

Title:Trailing Stop with limit orders in Blankly Finance paper trading environment #233

Closed Gobo85 closed 1 year ago

Gobo85 commented 1 year ago

Description:

I noticed that in Blankly Finance's paper trading environment, limit orders are immediately changed from "pending" to "filled" on the next call of the price event, even though the limit price is not reached. This behavior is causing an issue with my trading strategy, which involves placing a market buy order, followed by a sell limit order below the market price to act as a trailing stop.

Once the market order is accepted, the limit order gets accepted and indicates "pending." However, on the next call of the price data, the limit order gets filled, even if the limit price has not been reached. This is not the intended behavior and is causing a deviation from my trading strategy. ( Blankly Package is doing the correct behaviour, hence this is not an issue actually)

I suggest that Blankly Finance reviews the way limit orders are handled in their paper trading environment to ensure that they are working as intended and not causing issues for traders who rely on accurate order execution. ( This has been clarified and Blankly Finance is operating as intended)

Thank you for your attention to this matter.

drew2323 commented 1 year ago

Sell Limit order basically says I want to sell at least for price X, which when set for price below market price is by default true, thus filled immediately. Maybe you want bracket orders, OTO or OCO respectively?

Gobo85 commented 1 year ago

Thank you for your response and suggestion. How would I use bracket orders, as you suggested? Currently, I use a sell limit order as it aligns better with my trading strategy.

Regarding the issue I am experiencing, I understand that a sell limit order set below the market price will be immediately filled, is there any way to implement a trailing stop mechanism at the broker level within the Blankly Finance platform, or do you have any other suggestions for achieving this outcome?

Thank you for your help and attention to this matter. I appreciate any further information or suggestions you can provide.

EmersonDove commented 1 year ago

Hey @Gobo85, I just tested using this code:

import time

import blankly

def price_event(price, symbol, state):
    try:
        state.interface.limit_order(symbol, 'buy', 20000, 1)
    except Exception as e:
        pass

    # format epoch time to human readable date
    print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(state.time)), price)

if __name__ == "__main__":
    exchange = blankly.Binance()

    strat = blankly.Strategy(exchange)

    strat.add_price_event(price_event, 'BTC-USD', '1d')

    strat.backtest(to='1y', initial_values={'USD': 1000000})
Screenshot 2023-03-25 at 11 28 33 AM

And it seems like the limit order correctly executes when the price hits 20k as the limit suggests (on the date 2022-06-17). Can you identify any issues with this test I performed or send along one of your snippets so that I can test that out?

Gobo85 commented 1 year ago

Hello,

Thank you for your reply, your example clarifies the usage.

My Intention is to implement a trailing stop ie. If I have a buy market order, would placing a sell limit order below current price achieve this?

I understand the way I place the limit order would not necessarily be best for this scenario.

Which other way can I achieve the intended behavior?

Thank you again

On Sat, Mar 25, 2023, 17:20 Emerson Dove @.***> wrote:

Hey @Gobo85 https://github.com/Gobo85, I just tested using this code:

import time import blankly

def price_event(price, symbol, state): try: state.interface.limit_order(symbol, 'buy', 20000, 1) except Exception as e: pass

# format epoch time to human readable date
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(state.time)), price)

if name == "main": exchange = blankly.Binance()

strat = blankly.Strategy(exchange)

strat.add_price_event(price_event, 'BTC-USD', '1d')

strat.backtest(to='1y', initial_values={'USD': 1000000})

And it seems like the limit order correctly executes when the price hits 20k as the limit suggests. Can you identify any issues with this test I performed or send along one of your snippets so that I can test that out?

— Reply to this email directly, view it on GitHub https://github.com/blankly-finance/blankly/issues/233#issuecomment-1483849547, or unsubscribe https://github.com/notifications/unsubscribe-auth/A2FFUNWIJMSUFM6VIBFSSADW54EM5ANCNFSM6AAAAAAWHNTQFI . You are receiving this because you were mentioned.Message ID: @.***>

EmersonDove commented 1 year ago

Hi, placing a limit sell below the market price will immediately execute. The same thing will also happen if you set a limit sell below the market price on a live exchange, so blankly is replicating this behavior.

I would recommend canceling and placing your limit order repeatedly to achieve the desired trailing order. Some of our exchanges also support a stop order which can act as the exit.

Gobo85 commented 1 year ago

Hello, yes correct. I have implemented a condition to cancel a pending order and place a new limit order if the price moves in a favorable direction.

Since the order gets "filled" immidiately, I will not have any pending orders to cancel. And from the documentation, even if I use the stop-loss order, it inherits from the limit order.

Can you show me a simple example of how to implement an initial stop-loss on a system that has a Buy Order filled? The trailing element is well understood.

Below is a snippet of how I am implementing this:

Issue ENTRY as Market Order

        order = interface.market_order(
            symbol, side="buy", size=blankly.trunc(BuySize, 0))

        # Get RESPONSE
        order_response = order.get_response()

        # Update BREAKOUT variable
        variables["BREAKOUT"] = True

        # Append RESPONSE to "ORDERS"
        variables["ORDERS"].append(order_response)

        # Store the Position Size
        variables["POSITIONSIZE"] = BuySize

        if limit_orders_enabled:
            # Get limit order size and price
            limit_order_size = blankly.trunc(BuySize, 0)
            limit_order_price = blankly.trunc(ndExitLong[-2], 5)

            # Issue STOPLOSS as limit order
            stoploss_order = interface.limit_order(
                symbol,
                side="sell",
                price=limit_order_price,
                size=blankly.trunc(limit_order_size, 0),
            )

If price changes in a favourable direction my code does this:

if ( ndExitLong[-2] > variables["TRAILING_PRICE"] and current_status == "pending" ):

Update the Trailing Price

        variables["TRAILING_PRICE"] = ndExitLong[-2]

        # Cancel the current Limit order
        interface.cancel_order(symbol, order_id)

        # Flag Limit orders as None
        variables["ORDER"] = None

        # Get Updated base available
        base_avail = interface.account[state.base_asset].available

        # Get limit order size and price
        updated_limit_order_size = (
            current_size
            if current_size < base_avail
            else base_avail
        )

        # Ensure order is not below minimum
        updated_limit_order_size = max(updated_limit_order_size, min_units)

        updated_limit_order_price = blankly.trunc(
            variables["TRAILING_PRICE"], 5
        )

        # Issue UPDATED STOPLOSS as limit order
        updated_stoploss_order = interface.limit_order(
            symbol,
            side="sell",
            price=updated_limit_order_price,
            size=blankly.trunc(updated_limit_order_size, 0),
        )
Gobo85 commented 1 year ago

Hi @EmersonDove ,

Can you clarify if the stop_loss_order(symbol, price, size) -> StopLossOrder, would be able to achieve the intended purpose for both Buy and Sell stop protection? Rather than using the limit order as I currently use it?

EmersonDove commented 1 year ago

I recommend using a limit order for now actually until you start going live and optimizing it later because as I said above the exchanges have conditional support for stop orders and behavior can be different.

A stop limit can allow you to achieve your intended purpose which is different than a stop loss like you said above. A stop limit just tells the exchange that it should open a limit order on the wrong side of the order book to sell out when the price drops to a certain point.

There are some great videos by coinbase that give a good overview of limit orders and stop limits.

Gobo85 commented 1 year ago

Thanks for the prompt response, I shall continue to explore the options as recommended.