'Buy/sell strategy with indicators?

I have a dataframe similar to the one below;

Price return indicator
5 0.05 1
6 0.20 -1
5 -0.16 1

Where the indicator is based upon the forecasted return on the following day.

what I would like to achieve is a strategy where when the indicator is positive 1, I buy the stock at the price on that date/row. Then if the indicator is negative we sell at that price. Then I would like to create a new column with represents the value of the portfolio on each day. Assuming I have $1000 to invest the value of the portfolio should equal the holdings and cash amount. Im assuming that any fraction of Stock can be purchased.

Im unsure where to start with this one. I tried calculating a the Buy/Hold strategy using;

df['Holding'] = df['return'].add(1).cumprod().*5000

this worked for a buy hold strategy but to modify it to the new strategy seems difficult.

I tried;

df['HOLDINg'] = (df['return'].add(1).cumprod()* 5000 * df['Indicator]) 
#to get the value of the buy or the sell
#then using 

df['HOLDING'] = np.where(df['HOLDING'] >0, df['HOLDING'] , df['HON HOLDING 2']*-1)
#my logic was, if its positive its the value of the stock holding, and if its negative it is a cash inflow therefore I made it positive as it would be cash.

the issue is, my logic is massively flawed, as if the holding is cash the return shouldn't apply to it. further I don't think using the cumprod is correct with this strategy.

Has anyone used this strategy before and can offer tips of how to make it work?

thank you



Solution 1:[1]

I'm not sure about the returns and prices being in the correct place (they shouldn't really be in the same row if they represent the buying price (presumably yesterday's close), and the daily return (assuming the position was held for the whole day). But anyway...

import pandas as pd
# the data you provided    
df = pd.read_csv("Data.csv", header=0)
# an initial starting row (explanation provided)
starting = pd.DataFrame({'Price': [0], 'return': [0], 'indicator': [0]})
# concatenate so starting is first row
df = pd.concat([starting, df]).reset_index(drop=True)
# setting holding to 0 at start (no shares), and cash at 1000 (therefore portfolio = 1000)
df[["Holding", "Cash", "Portfolio"]] = [0, 1000, 1000]

# buy/sell is the difference (explanation provided)
df["BuySell"] = df["indicator"].diff()

# simulating every day
for i in range(1, len(df)):
    # buying
    if df["BuySell"].iloc[i] > 0:
        df["Holding"].iloc[i] += df["Cash"].iloc[i-1] / df["Price"].iloc[i]
        df["Cash"].iloc[i] = 0
    # selling
    elif df["BuySell"].iloc[i] < 0:
        df["Cash"].iloc[i] = df["Holding"].iloc[i-1] * df["Price"].iloc[i]
        df["Holding"].iloc[i] = 0
    # holding position
    else:
        df["Cash"].iloc[i] = df["Cash"].iloc[i-1]
        df["Holding"].iloc[i] = df["Holding"].iloc[i-1]
    # multiply holding by return (assuming all-in, so holding=0 not affected)
    df["Holding"].iloc[i] *= (1 + df["return"].iloc[i])
    df["Portfolio"].iloc[i] = df["Holding"].iloc[i] * df["Price"].iloc[i] + df["Cash"].iloc[i]

Explanations:

Starting row:

  • This is needed so that the loop can refer to the previous holdings and cash (it would be more of an inconvenience to add in an if statement in the loop if i=0).

Buy/Sell:

  • The difference is necessary here, as if the position changes from buy to sell, then obviously selling the shares (and vice versa). However, if the previous was buy/sell, the same as the current row, there would be no change (diff=0), with no shares bought or sold.

Portfolio:

  • This is an "equivalent" amount (the amount you would hold if you converted all shares to cash at the time).

Holding:

  • This is the number of shares held.

NOTE: from what I understood of your question, this is an all-in strategy - there is no percentage in, which has made this strategy more simplistic, but easier to code.

Output:

#Out: 
#   Price  return  indicator  Holding  Cash  Portfolio  BuySell
#0      0    0.00          0     0.00  1000     1000.0      NaN
#1      5    0.05          1   210.00     0     1050.0      1.0
#2      6    0.20         -1     0.00  1260     1260.0     -2.0
#3      5   -0.16          1   211.68     0     1058.4      2.0

Hopefully this will give you a good starting point to create something more to your specification and more advanced, such as with multiple shares, or being a certain percentage exposed, etc.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1