backtesting.py icon indicating copy to clipboard operation
backtesting.py copied to clipboard

Open a limit order in certain time frame

Open sword134 opened this issue 4 years ago • 5 comments

Hello, I am trying to open a long limit order when the time is between 2:55pm and 3pm and only in that timeframe.

In raw python it would look like this, where x is my index column in my dataframe.

def time_in_range(x):
    start = time(14, 55)
    end = time(15 0)
    x = datetime.strptime(x, '%Y-%m-%d %H:%M:%S')
    """Return true if x is in the range [start, end]"""
    if start <= end:
        return start <= x <= end
    else:
        return start <= x or x <= end

I am however unsure how I would make it in the backtesting library? How do I open a long limit order when the time is between 2:55pm and 3pm and then close it say 10 minutes later if it hasn't been filled?

sword134 avatar Oct 10 '21 19:10 sword134

Strategy.data.index is an instance of pd.DatetimeIndex and its values are pd.Timestamp. So something like this might work:

    time = self.data.index[-1].time()
    if start <= time <= end:
        self.buy(...)
    ...
    for order in self.orders:
        if time >= time(15, 10):
            order.cancel()

kernc avatar Oct 10 '21 22:10 kernc

Strategy.data.index is an instance of pd.DatetimeIndex and it's values are pd.Timestamp. So something like this might work:

    time = self.data.index[-1].time()
    if start <= time <= end:
        self.buy(...)
    ...
    for order in self.orders:
        if time >= time(15, 10):
            order.cancel()

Thanks this seems to work. I've got a another question though. If I place 2 limit orders a long and a short and I want to cancel the opposite order when one of them is filled, how can I do it in backtesting? Say my long order is filled, I then want to cancel my short order.

sword134 avatar Oct 11 '21 07:10 sword134

You can test the order via Order.is_long. The active Trade has a similar property.

You can also just loop over for order in self.orders, because once the order fills and becomes a trade, it is removed from Strategy.orders and put into Strategy.trades.

kernc avatar Oct 11 '21 10:10 kernc

You can test the order via Order.is_long. The active Trade has a similar property.

You can also just loop over for order in self.orders, because once the order fills and becomes a trade, it is removed from Strategy.orders and put into Strategy.trades.

This also cancels the stoploss on the current trade though. Is there a way to disguinish between the stoploss which is attached to the current trade and the order for a limit short (in case the current trade is a long). I only want to cancel the short order, not the stoploss attached to the current active long trade

def next(self):
        time = self.data.index[-1].time()
        if self.start <= time < self.end:
            self.buy(limit=self.highest_high, sl=self.highest_high-self.sl1, tp=self.highest_high+self.tp1)
            self.sell(limit=self.lowest_low, sl=self.lowest_low+self.sl1, tp=self.lowest_low-self.tp1)

        for order in self.orders:
            if time >= self.cancel_order_time:
                order.cancel()

        for trade in self.trades:
            if trade.is_long or trade.is_short:
                for order in self.orders:
                    if order.is_short:
                        order.cancel()

sword134 avatar Nov 14 '21 20:11 sword134

Is there a way to disguinish between the stoploss which is attached to the current trade and the order for a limit short

Orders have .is_contingent property which is True for SL/TP orders.

kernc avatar Nov 14 '21 23:11 kernc

@kernc How can I make cancel a order when it exceed certain time ? I tend to close an order if it is not filled in certain time interval , but Order class doesn't contain entry_time .

eromoe avatar Jan 08 '23 09:01 eromoe

Whatever is not provided by the library, you can code yourself. Something like:

order = self.buy(...)
self.order_times[order] = self.data.index[-1]

for order in self.orders:
    if self.data.index[-1] - self.order_times[order] > pd.Timedelta('3 days'):
        order.cancel()

If you think Order needs a new property .placed_time (or somesuch), open a new feature request for it.

kernc avatar Jan 08 '23 17:01 kernc