Back to Community
Trading idea: Buy on last day of month and sell first day of month end of day [under construction]

Hi,
From the trading ideas post I am working on "buy on last day of month and sell first day of month end of day".

I used zipline to set it up since open price cannot be retrieved in Quantopian. I want to implement this still in Quantopian. For the moment I just want to share the code of the date calculations, which are a bit more work than in .NET.

For the iteration I would like to show the results in a heatmap. But no clue so far how that works.

It seems the maximum profit is about 22K over more than 10 years with 50K investment.

J.

from zipline import TradingAlgorithm  
from zipline.transforms import MovingAverage  
from zipline.utils.factory import load_from_yahoo  
#from zipline.utils.factory import load_bars_from_yahoo  
import zipline as zp  
import zipline.finance.performance as pf

from zipline.finance import trading

from datetime import datetime  
from datetime import timedelta  
from dateutil.relativedelta import relativedelta

from dateutil.rrule import DAILY, rrule, MO, TU, WE, TH, FR

import pytz  
import matplotlib.pyplot as plt  
import pandas as pd  
from pandas.io.data import *  
from pandas import Panel, DataFrame, Series, read_csv, concat

import numpy as np

import math

import sys

class BuyLastDayOfMonthSellFirstDayOfMonth(TradingAlgorithm):  
    """Buy the last day of the month and sell the first month, the end of the day"""

    def initialize(self, stock, buyDaysBeforeEndOfMonth=0, sellDayAfterFirstOfMonth=0):  
        self.max_notional = self.capital_base*.50  
        self.STK =  stock  
        self.invested = False  
        self.set_commission(zp.finance.commission.PerTrade(cost=6.00))

        self.investment = 0

        self.scheduled_sell_date = None

        self.profit = None

        self.totalProfit = 0  
    def handle_data(self, data):  
        current_date = data['Open'].dt # data[self.STK]['date']  
        open_price = data['Open'].price # data[self.STK]['open']  
        close_price = data['Close'].price #data[self.STK]['close']  
        buy = False  
        sell = False  
        #current_STK_shares = self.portfolio.positions[self.STK].amount  
        #current_STK_value = self.portfolio.positions_value  
        #current_accountvalue = self.portfolio.cash + self.portfolio.positions_value

    # Has short mavg crossed long mavg?

        if(not self.invested):  
            if(current_date.month == 12):  
                last_day_of_month = datetime(current_date.year+1, 1, 1, 0, 0, 0, 0, pytz.utc) - timedelta(days=1)  
            else:  
                last_day_of_month = datetime(current_date.year, current_date.month+1, 1, 0, 0, 0, 0, pytz.utc) - timedelta(days=1)  
            last_day_of_month_minus_days_before = last_day_of_month - timedelta(days=buyDaysBeforeEndOfMonth)

            if(current_date.day >= last_day_of_month_minus_days_before.day): # buy  
                self.invested = True;  
                self.scheduled_sell_date = rrule(DAILY, dtstart=last_day_of_month, byweekday=(MO,TU,WE,TH,FR))[1+sellDayAfterFirstOfMonth]  
                print 'buy on %s day: %s.' % (current_date, current_date.strftime('%A'))  
                print 'sell planned for: %s day %s.' % (self.scheduled_sell_date, self.scheduled_sell_date.strftime('%A')) 

                self.num_shares = math.floor(self.max_notional / open_price)  
                self.order(self.STK, self.num_shares)

                buy = True  
                self.investment = self.num_shares * open_price

        elif(self.invested and current_date >= self.scheduled_sell_date):  
            self.invested = False;  
            print 'sold on %s day: %s.' % (current_date, current_date.strftime('%A'))

            #self.num_shares = math.floor(self.max_notional / close_price)  
            self.order(self.STK, -1*self.num_shares)

            sell = True

            self.profit = (self.num_shares * close_price) - self.investment  
            self.totalProfit = self.totalProfit  + self.profit

            print 'profit %f on investment of %f. Total: %f' % (self.profit, self.investment, self.totalProfit)

            self.investment = 0


        self.record(open_price=open_price,  
            close_price=close_price,  
            buy=buy,  
            sell=sell,  
            investment=self.investment,  
            num_shares=self.portfolio.positions[self.STK].amount,  
            profit = self.profit)

def daterange(start_date, end_date):  
  return rrule(DAILY, dtstart=start_date, until=end_date, byweekday=(MO,TU,WE,TH,FR))

#start of main()  
SYMBOL = 'SPY'

buyDaysBeforeEndOfMonth=1  
sellDayAfterFirstOfMonth=0

start = datetime(2000, 1, 1, 0, 0, 0, 0, pytz.utc)  
end =  datetime(2013, 12, 1, 0, 0, 0, 0, pytz.utc)  
date_range = daterange(start, end)

#data = pd.io.data.get_data_yahoo(SYMBOL,start=start, end=end, adjust_price=True)  
#data.to_csv('c:\\data\\test-long.csv', encoding='utf-8')  
data = pd.DataFrame.from_csv('c:\\data\\test-long.csv', index_col=0, parse_dates=True, encoding='utf-8')

#print data.head()  
#print data[['Open', 'Close']].head()

#close_key = 'Close'  
#df = pd.DataFrame({key: d[close_key] for key, d in data.iteritems()})  
data.index = data.index.tz_localize(pytz.utc)

#data = load_from_yahoo(stocks={SYMBOL}, indexes={}, start=start, end=end, adjusted=True)

#print df.head()  
#print df[['Open', 'Close']].head()

buyDay = np.arange(0,25,1)  
sellDay = np.arange(0,25,1)  
SH = np.zeros((len(buyDay),len(sellDay)))

maxSH = 0

for i, smi in enumerate(buyDay):  
    for j, lmi in enumerate(sellDay):  
        print smi, lmi  
        if smi==lmi:  
            continue

        dma = BuyLastDayOfMonthSellFirstDayOfMonth(SYMBOL, buyDaysBeforeEndOfMonth=buyDaysBeforeEndOfMonth, sellDayAfterFirstOfMonth=sellDayAfterFirstOfMonth)  
        perf = dma.run(data)

        print dma.totalProfit

        #sharpe = [risk['sharpe'] for risk in dma.risk_report['twelve_month']]  
        #print "Monthly Sharpe ratios:", sharpe  
        SH[i,j] = dma.totalProfit#dma.portfolio.portfolio_value#max(sharpe)  
        #print "portfolio value:", dma.portfolio.portfolio_value

        if SH[i,j] > maxSH:  
            maxSH = SH[i,j]  
            print 'maxSH:', maxSH  
        else:  
            print 'SH[i,j]:', SH[i,j]  
        #pnl = backtest(ohlc, ccThresh=cc, coThresh=co)  
        #SH[i,j] = sharpe(pnl)        

print 'maxSH:', maxSH

i,j = np.unravel_index(SH.argmax(), SH.shape)  
print "value", SH[i,j]  
print 'Optimum buyDay %.2f' % buyDay[i]  
print 'Optimum sellDay %.2f' % sellDay[j]

input('wait for user key...')  
4 responses

Excellent work Quant Trader! It would really be interesting seeing something like this in a "quantopian" format. I'm new to programming, I know way more about finance in general and I also know more about math then programming. So your skills are much appreciated.

Regarding the date programming - buy at 11.am hold until close - I asked Ed Bartosh in this thread: https://www.quantopian.com/posts/help-with-an-sp-and-500-atr-strategy and he made an example you can see there.

Also this strategy I wrote about is suppose to be a low return strategy but what's interesting is if it has a high Sharpe Ratio.
Because if the Sharpe Ratio is high then we can use a lot of leverage since this is an intraday strategy.

Also the strategy is to look at stocks which are negative the second half of the last day of the month, and negative the first half of the first day of the month and then buy at midday the first day of the month. I understand that you code it to buy at last day of the month and then sell the next day, just playing the shift between months?

Strategy example:
IBM trades negative from 11.am. until close 31th of march.
IBM trades negative from open until 11 a.m. on 1th of april.
Then buy at 11 a.m. and sell at closing time.

From your code it looks like you used SPY my intuition is telling me that a security that's smaller in size and less well known would probably give a better return.
Per Fama & French three factor model small stocks and cheap stocks are our friend, but in this strategy I don't think that will have an effect. But somehow less well-known stocks should have a longer time delay in orders from institutional investors and retail traders. Stocks that are mostly traded manually maybe.

So what would be cool is to run back-tests on single stocks and stocks in a certain industry or sector and see which ones over time had the best return from this strategy. And then perhaps on can model it and optimize it.

Cheers

Dear Patrick,

I am working on Trading idea one:
"Over the past 16 years, buying the close on SPY (the S&P 500 ETF) on the last day of the month and selling one day later would result in a successful trade 63% of the time with an average return of 0.37% (as opposed
to 0.03% and a 50%-50% success rate if you buy any random day during this period)."

I have modified my code for quantopian..but it seems that the import of the dateutil library is not allowed. I have no intention to recode it. So lets wait and see if the dateutil is allowed. Also I am waiting till a new version of zipline is released, which is better in sync with Quantopian. Now I have quite a bit of rework.

J.

Okay, my bad didnt realize.

Sorry to resurrect an old thread, but I implemented Patrick Langstrom's "ATM Machine" algorithm from https://www.quantopian.com/posts/trading-strategy-ideas-thread.

I was inspired by David Edwards in a thread where he uses the Zipline trading calendar, which contains enough data to determine when you are at the first trading day of the month and what the open and close times are for each trading day.

Anyway, here is my backtest. I thought that my simple code might be helpful to those of you trying to find open and close times and trading days. I know this beats the pants off of my old method of importing trading days from Quandl :)

Clone Algorithm
54
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 538b4be08ffb6a0722ee1b1f
There was a runtime error.