Can you lose 10 times the ammount with a long position?

bg823037bg823037 Posts: 8
edited April 2019 in Onboarding Help

Hello,
I'm working on a multiday strategy and the results of my backtests are completely puzzling to me.
My algorithm entered a long position for 'HK' on 2011-07-27 with 10,000 shares at $ 2.27, so the initial position was roughly $30,000.
On 2012-02-09 the portfolio shows this symbol with -$346,000.
How can that be possible?
My confusion got even bigger when I looked up the chart for HK at yahoo finance. On the day of my entry the symbol noted at about $ 550 and on 2012-02-09 at over $ 2,000. So instead of losing 10 times your entry-ammount you would have won 3 times your initial capital. There was a split around this time, but exactly one day after my big loss.
Is there a bug or am I missing something?

Here a small script to reproduce this phenomenon (running as multiday for the above mentioned period):

`from cloudquant.interfaces import Strategy

class CQc50990bb39a74be6920996b0a2c38aa3(Strategy):

@classmethod
def is_symbol_qualified(cls, symbol, md, service, account):
    return symbol == 'HK'

def on_minute_bar(self, event, md, order, service, account, bar):
    if event.timestamp > md.market_open_time:
        if account[self.symbol].position.shares == 0:
            order.algo_buy(self.symbol, algorithm="market", intent="init",
                            order_quantity=10000)

`

Thanks,
Ben

Edit: The code actually doesn't reproduce the result, sorry for not checking properly. It shows an entry price of $ 38, so it would theoretically be possible to lose that much. But it does show no price data from 2011-08-26 to 2012-02-08.
The reason I thought the entry price was $ 3.27 is this line in the positions-file from the detailed report (please excuse the bad formatting):

symbol side    shares  price   time    remaining_shares    mtm_price   entry_pl    mtm_pl  account
HK  1   9216    3.2700328005    2011-07-27T13:30:05.006700  9216    7.7899999619    40273.6164805   -1382.40087891  bbi-long

Further more, my algo should only place orders for position sizes of $ 30,000 (order-quantity = 30,000/close-of-latest-minute-bar) and 9216 shares for $ 3.27 would match exactly.
When I looked at the result page again, there was no order shown for HK on that day (and neither several days before nor after).
Where did this position come from?
It really gets more mysterious the more I look into it...

Best Answer

  • ptunneyptunney Posts: 246
    Answer ✓

    Great question, splits are always complicated and sometimes we don't have them logged correctly and even when we do (which is most of the time) they cause confusion for new and old traders, even me!

    A great site for split info... https://www.splithistory.com/?symbol=HK

    HK 1 for 3 reverse split 2/10/2012

    Let's take your script, run it from 2/9/2012 to 2/14/2012, run as a single multiday job so each day completes in turn.
    Enter on 2/9, check the date and exit on 2/14, and examine the results...

    On the day of the split, and after the close on the day before, the price can be erratic. You would be surprised at the number of people who do not know that the split has occurred and assume the symbol has shot up or dropped heavily.

    Here is the script I ran (run as a single mutiday job and custom hours from 9:30 to 16:10 to allow time for the close)..

    # ran from 2/9/2012 to 2/14/2012 as a single multiday job.
    # HK split on 2/10/2012 1 for 3 reverse split
    # model goes long 10000 shares, split occurs, model ends up with 3333 shares (and .333333* which it exits on the day of the split)
    # model exits 3333 shares on 2/14/2012
    from cloudquant.interfaces import Strategy
    class ben_ques(Strategy):
        @classmethod
        def is_symbol_qualified(cls, symbol, md, service, account):
            return symbol == 'HK'
        def on_minute_bar(self, event, md, order, service, account, bar):
            Today = service.time_to_string(service.get_market_hours( date_offset = 0)[0],format="%Y-%m-%d")        
            if Today == "2012-02-09" and event.timestamp > md.market_open_time:
                if account[self.symbol].position.shares == 0:
                    order.algo_buy(self.symbol, algorithm="market", intent="init",order_quantity=10000)
            if Today == "2012-02-14" and account[self.symbol].position.shares != 0:
                order.algo_sell(self.symbol, algorithm="market", intent="exit")
    

    And here is an image I compiled from the tables for the various dates to help you follow it...

    Hopefully this helps!

Answers

  • bg823037bg823037 Posts: 8

    Thanks. Ok, splits confuse even the backtesting-software - we still have an entry price of $ 38 on 2011-07-27, that could be close to the result of adjusting the original price for only the third split ;)
    In my original backtest the shares weren't sold and the perfomance didn't adjust afterwards.
    So whenever there's a very long bar (red or green) I will check for splits of the shares with the biggest position...

  • bg823037bg823037 Posts: 8
    edited April 2019

    I just realized that splits also make trade executions more complicated. Let's say you have a take-profit or stop-loss based on the difference between entry price and current price. Then after the split you would exit the position for no reason and pay the commission fees. That just happened to me intraday and the "closed trades" tab shows the same price for entry and exit. I thought i had a solution for that but it didn't work. My target profit was + 10%, implemented as
    entry_price * 1.1 < current_price
    There was a reverse split, so naturally the price went up. Since a split not only changes the price but also the ammount of shares in a portfolio I thought the following would work
    account[self.symbol].position.shares * entry_price * 1.1 < account[self.symbol].position.shares * current_price
    But it didn't. Any ideas how to manage this?
    Or better yet, is there an option to run backtests with the prices already adjusted for all splits?

  • bg823037bg823037 Posts: 8
    edited April 2019

    Correction: according to splithistory.com there was no split for that particular share, sorry my bad. My last problem can be explained by the price oscillating arround $ 0.001...
    But I was able to reproduce the phenomenon I mentioned in my first post.
    It seems to have something to do with the Bollinger-Bands I use as an entry signal - none of this happens when I leave them out of the script.
    When you run the script below from May, 3rd to May, 9th 2011 you get a loss of over 150k from a 30k position - twice in five days:

    `
    from cloudquant.interfaces import Strategy
    import talib

    class CQc50990bb39a74be6920996b0a2c38aa3(Strategy):

    @classmethod
    def is_symbol_qualified(cls, symbol, md, service, account):
        return symbol == 'XIDEW'
    
    def on_minute_bar(self, event, md, order, service, account, bar):
        entry = account[self.symbol].position.entry_price
        shares = account[self.symbol].position.shares
        pos = entry * shares
        daily = md.bar.daily(start=-100)
        close = daily.close
    
        m1 = bar.minute()
    
        if len(m1.timestamp) > 0:
            close1 = m1.close
            last = close1[-1]
            now = last * shares
            qty = int(30000 / last)
    
            if event.timestamp > md.market_open_time:
                if len(close) > 0 and close[0] != 0 and last != 0:
                    upperband, middleband, lowerband = talib.BBANDS(close, timeperiod=20, 
                                                                    nbdevup=2, nbdevdn=2,
                                                                    matype=0)
                    low = lowerband[-1]
    
                    if shares > 0:
                        if pos * 1.1 < now:
                            order.algo_sell(self.symbol, algorithm="market", intent="exit")
                    elif shares == 0:
                        if last < low:
                            order.algo_buy(self.symbol, algorithm="market", intent="init",
                                           order_quantity=qty)
    

    `
    (Why are the first few lines always outside the inline-code no matter what I do?)

  • bg823037bg823037 Posts: 8

    Although I don't really understand what happens when running the last example-script (in the trades file it looks to me like an order worth 30 Million was executed), it doesn't matter. I just changed market to limit orders and it seems to work fine . So no questions right now from my side, thanks again!

  • ptunneyptunney Posts: 246

    Good! There is one question outstanding.. why does it not include all your code in the inline code on the post..
    I make sure I remove all empty lines, I start and end with three ticks and put the word python after my opening three ticks.

Sign In or Register to comment.