Thanks a lot!
Latest posts made by Crespecular
-
RE: Shorting values are negative
Thank you for your quick response!
Btw, when you say cash inflow, do you mean pnl? or is there any code that I can retrieve current cash balance + cash generate(or loss) from the transaction?
:)
-
Shorting values are negative
Hi, it is me again. I really appreciate everyone's commitment to this community!
While I was making a long&short strategy with coding helps from people here, I confronted another problem.
My strategy is the following :
For shorting strategy (Which is the code that I wrote below), within the universe, I short 2 stocks (maximum positions that I preset).
If the price reaches a profit-realisation or loss-cut level then I cover the stocks then enter the market again with shorting 2 different positions within the universe.
This portfolio will be rebalanced every Q ends regardless of the current stocks' prices.
This is my code:
#Short class Short(bt.Strategy): params = ( ('losscut', 0.15), # Different params for losscut & profit realisation & maximum stock holdings ('profit', 0.15), ('max_stock', 2), ) def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: self.order = order return if order.status in [order.Completed]: if order.isbuy(): #Trying to track entry price for each stock position we entered self.entry_order.update({order.data._name : order.executed.price}) print('Date : {}, Current Order is Buy : {}, Price : {}, Value : {}, Commission : {}'.format( self.datetime.date(), order.data._name,order.executed.price, order.executed.value, order.executed.comm)) else: print('Date : {}, Current Order is Sell : {}, Price : {}, Value : {}, Commission : {}'.format( self.datetime.date(), order.data._name,order.executed.price, order.executed.value, order.executed.comm)) # Sentinel to None: new orders allowed self.order = None def __init__(self): # __init__ = creates object defined by the Class. INITIALISES the object - sets all attributes # To keep track of pending orders, 현재 보유 중인 종목, 투자되었던 종목, Entry Price self.entry = {} self.entry_order = {} self.Short_list = [] self.Shorted_list = [] self.rebalanced = [] self.order = None def next(self): for i, d in enumerate(self.datas): dt, dn = self.datetime.date(), d._name pos = self.getposition(d).size offset = BMonthEnd() if pos == 0: if dn not in self.Shorted_list: if len(self.Short_list) == self.params.max_stock: return else: self.order_target_percent(data=d, target = -(1/float(self.params.max_stock))) self.Shorted_list.append(dn) self.Short_list.append(dn) self.entry.update({dn : d.close[0]}) else: if dt.month in [3,6,9,12]: #Last day of current month if dt.day == offset.rollforward(dt).day: self.order_target_percent(data=d, target = 0) self.rebalanced.append(dn) self.Short_list.remove(dn) return #Last day of previous month #print(offset.rollback(dt)) if d.close[0] > self.entry[dn]*float(1 + self.params.losscut): self.order_target_percent(data=d, target = 0.0) self.Short_list.remove(dn) elif d.close[0] < self.entry[dn]*float(1 - self.params.profit): self.order_target_percent(data=d, target = 0.0) self.Short_list.remove(dn) # Trade list similar to Amibroker output class open_list(bt.Analyzer): def get_analysis(self): return self.trades def __init__(self): self.trades = [] self.cumprofit = 0.0 def notify_trade(self, trade): if trade.isclosed: dir = 'short' if trade.history[0].event.size > 0: dir = 'Short' pricein = trade.history[len(trade.history)-1].status.price datein = bt.num2date(trade.history[0].status.dt)-timedelta(days=1) if trade.data._timeframe >= bt.TimeFrame.Days: datein = datein.date() pnl = trade.history[len(trade.history)-1].status.pnlcomm barlen = trade.history[len(trade.history)-1].status.barlen pbar = pnl / barlen self.cumprofit += pnl size = value = 0.0 for record in trade.history: if abs(size) < abs(record.status.size): size = record.status.size value = record.status.value highest_in_trade = max(trade.data.high.get(ago=0, size=barlen+1)) lowest_in_trade = min(trade.data.low.get(ago=0, size=barlen+1)) hp = 100 * (highest_in_trade - pricein) / pricein lp = 100 * (lowest_in_trade - pricein) / pricein if dir == 'long': mfe = hp mae = lp if dir == 'short': mfe = -lp mae = -hp self.trades.append({'ref': trade.ref, 'ticker': trade.data._name, 'dir': dir, 'datein': datein, 'pricein': pricein, 'pnl': pnl, 'size': size, 'value': value, 'cumpnl': self.cumprofit, 'nbars': barlen, 'pnl/bar': round(pbar, 2), 'mfe%': round(mfe, 2), 'mae%': round(mae, 2)}) # Trade list similar to Amibroker output class trade_list(bt.Analyzer): def get_analysis(self): return self.trades def __init__(self): self.trades = [] self.cumprofit = 0.0 def notify_trade(self, trade): if trade.isclosed: offset = BMonthEnd() dir = 'short' if trade.history[0].event.size > 0: dir = 'Short' pricein = trade.history[len(trade.history)-1].status.price priceout = trade.history[len(trade.history)-1].event.price datein = bt.num2date(trade.history[0].status.dt)-timedelta(days=1) dateout = bt.num2date(trade.history[len(trade.history)-1].status.dt)-timedelta(days=1) if dateout.weekday() == 5: #if it's Saturday dateout = dateout - timedelta(days = 1) #then make it Friday elif dateout.weekday() == 6: #if it's Sunday dateout = dateout - timedelta(days = 2); #then make it Friday if dateout.strftime("%Y-%m-%d") == offset.rollforward(bt.num2date(trade.history[len(trade.history)-1].status.dt)-timedelta(days=1)).strftime("%Y-%m-%d"): status = 'Rebalanced' else: if pricein > priceout: status = 'Loss-Cut' else: status = 'Profit-realisation' if trade.data._timeframe >= bt.TimeFrame.Days: datein = datein.date() dateout = dateout.date() pcntchange = 100 * priceout / pricein - 100 pnl = trade.history[len(trade.history)-1].status.pnlcomm barlen = trade.history[len(trade.history)-1].status.barlen pbar = pnl / barlen self.cumprofit += pnl size = value = 0.0 for record in trade.history: if abs(size) < abs(record.status.size): size = record.status.size value = record.status.value highest_in_trade = max(trade.data.high.get(ago=0, size=barlen+1)) lowest_in_trade = min(trade.data.low.get(ago=0, size=barlen+1)) hp = 100 * (highest_in_trade - pricein) / pricein lp = 100 * (lowest_in_trade - pricein) / pricein if dir == 'Short': mfe = hp mae = lp if dir == 'short': mfe = -lp mae = -hp self.trades.append({'ref': trade.ref, 'ticker': trade.data._name, 'dir': dir, 'Status' : status, 'datein': datein, 'pricein': pricein, 'dateout': dateout, 'priceout': priceout, 'chng%': round(pcntchange, 2), 'pnl': pnl, 'size': size, 'value': value, 'cumpnl': self.cumprofit, 'nbars': barlen, 'pnl/bar': round(pbar, 2), 'mfe%': round(mfe, 2), 'mae%': round(mae, 2)}) class EIKON_HLOC(bt.feeds.GenericCSVData): params = ( ('nullvalue' , float('NaN')), ('dtformat', '%Y-%m-%d'), ('datetime', 0), ('time',-1), ('open', 1), ('high', 2), ('low', 3), ('close', 4), ('volume', 5), ('openinterest', -1), ) #Variable for our starting cash startcash = 1000000000 #Create an instance of cerebro cerebro = bt.Cerebro(cheat_on_open=False) #Add our strategy cerebro.addstrategy(Short) datalist = [] for i in bascket: datalist.append(('{}.csv'.format(i), i)) for i in range(len(datalist)): data = EIKON_HLOC(dataname = datalist[i][0]) cerebro.adddata(data, name=datalist[i][1]) # Set our desired cash start cerebro.broker.setcash(startcash) cerebro.broker.set_coc(True) cerebro.broker.setcommission(commission=0.005) # add analyzers cerebro.addanalyzer(trade_list, _name='trade_list') cerebro.addanalyzer(open_list, _name='open_list') # run backtest strats = cerebro.run(tradehistory=True) # get analyzers data trade_list = strats[0].analyzers.trade_list.get_analysis() open_list = strats[0].analyzers.open_list.get_analysis() print (tabulate(open_list, headers="keys")) print (tabulate(trade_list, headers="keys")) #Finally plot the end results cerebro.plot()
And the result is following:
Date : 2015-01-05, Current Order is Sell : 000060, Price : 12600.0, Value : -499993200.0, Commission : 2499966.0 Date : 2015-01-05, Current Order is Sell : 000250, Price : 10050.0, Value : -499997550.0, Commission : 2499987.75 Date : 2015-03-16, Current Order is Buy : 000250, Price : 11650.0, Value : -499997550.0, Commission : 2897995.75 Date : 2015-03-16, Current Order is Sell : 005380, Price : 172000.0, Value : -470592000.0, Commission : 2352960.0 Date : 2015-04-01, Current Order is Buy : 000060, Price : 11400.0, Value : -499993200.0, Commission : 2261874.0 Date : 2015-04-02, Current Order is Sell : 005930, Price : 28460.0, Value : -488686660.0, Commission : 2443433.3000000003 Date : 2015-06-03, Current Order is Buy : 005380, Price : 138500.0, Value : -470592000.0, Commission : 1894680.0 Date : 2015-06-03, Current Order is Sell : 006730, Price : 18784.0, Value : -542951520.0, Commission : 2714757.6 Date : 2015-07-01, Current Order is Buy : 005930, Price : 25360.0, Value : -488686660.0, Commission : 2177282.8000000003 Date : 2015-07-02, Current Order is Sell : 034310, Price : 21850.0, Value : -546075200.0, Commission : 2730376.0 Date : 2015-07-23, Current Order is Buy : 034310, Price : 25300.0, Value : -546075200.0, Commission : 3161488.0 Date : 2015-07-23, Current Order is Sell : 034730, Price : 302500.0, Value : -477647500.0, Commission : 2388237.5 Date : 2015-08-21, Current Order is Buy : 034730, Price : 256000.0, Value : -477647500.0, Commission : 2021120.0000000002 Date : 2015-08-21, Current Order is Sell : 078340, Price : 111900.0, Value : -574158900.0, Commission : 2870794.5 Date : 2015-08-25, Current Order is Buy : 006730, Price : 15713.0, Value : -542951520.0, Commission : 2270921.325 Date : 2015-08-25, Current Order is Sell : 098460, Price : 32200.0, Value : -607742800.0, Commission : 3038714.0 Date : 2015-09-01, Current Order is Buy : 098460, Price : 38000.0, Value : -607742800.0, Commission : 3586060.0 Date : 2015-10-01, Current Order is Buy : 078340, Price : 116300.0, Value : -574158900.0, Commission : 2983676.5
The question is... OK, so I take short positions at the beginning and cover them when stocks meet profit-realisation or loss-cut or even rebalancing conditions. Then there must be + values coming from shorting and - values from buying (if the values are calculated based on cash flows).
But why do I get negative values for both sell & buy ? I was expecting a positive value from cash inflows of short selling but the figure here shows a negative value. Also, as you see below, even though prices are different, I still get the same absolute values from sell & buy. I don't know what I am missing here :(
Date : 2015-01-05, Current Order is Sell : 000250, Price : 10050.0, Value : -499997550.0, Commission : 2499987.75 Date : 2015-03-16, Current Order is Buy : 000250, Price : 11650.0, Value : -499997550.0, Commission : 2897995.75
Also, as you can see the order transaction date from the result, sometimes the sell & buy order takes place within the same day but sometimes it doesn't.
Could you guys help me why I am having these issues?
Thank you!!!
-
RE: To get multiple windows for plotting each multiple assets
@backtrader Thanks! I will try reformatting by using matplotlib then :)
-
To get multiple windows for plotting each multiple assets
Hi, I am currently running a simple Long & Short model for multiple equities.
This is my final part of the codes :
#Variable for our starting cash startcash = 1000000000 #Create an instance of cerebro cerebro = bt.Cerebro(cheat_on_open=False) #Add our strategy cerebro.addstrategy(Long) datalist = [] for i in bascket: datalist.append(('{}.csv'.format(i), i)) for i in range(len(datalist)): data = EIKON_HLOC(dataname = datalist[i][0]) cerebro.adddata(data, name=datalist[i][1]) # Set our desired cash start cerebro.broker.setcash(startcash) cerebro.broker.set_coc(True) cerebro.broker.setcommission(commission=0.005) # add analyzers cerebro.addanalyzer(trade_list, _name='trade_list') cerebro.addanalyzer(open_list, _name='open_list') # run backtest strats = cerebro.run(tradehistory=True) # get analyzers data trade_list = strats[0].analyzers.trade_list.get_analysis() open_list = strats[0].analyzers.open_list.get_analysis() print (tabulate(open_list, headers="keys")) print (tabulate(trade_list, headers="keys")) #Finally plot the end results cerebro.plot(numfigs=1)
and I get this packed window
What I want here is to get separate windows for each equity.
I tried :cerebro.plot(numfigs=5)
But it rather gave me 5 different windows for all equities with different time-horizon (ex: 2010-2011, 2011-2012 ...)
I have lots of things to learn about backtrader and python while doing my UG degree and I would really appreciate you guys help!
Thank you :D
-
Rebalance quarterly
Hi, I want to sell (cover for shorting) all the current long(short) holdings on my portfolio every quarter and replace them with the new stocks.
class Short(bt.Strategy): params = ( ('losscut', 0.05), ('profit', 0.05), ('max_stock', 5), ) def __init__(self): self.inds = dict() self.entry = {} self.loss_cut = {} self.profit_realisation = {} self.shorting_list = [] self.shorted_list = [] def next(self): for i, d in enumerate(self.datas): dt, dn = self.datetime.date(), d._name pos = self.getposition(d).size if pos == 0: if dn not in self.shorted_list: if len(self.shorting_list) == self.params.max_stock: return else: self.order_target_percent(data=d, target = -0.1) self.entry.update({dn:d.close[0]}) self.shorted_list.append(dn) self.shorting_list.append(dn) else: if dt.month in [3,6,9,12]: # I need the codes that clears the whole position in the beginning (or at the end) of every quarter (ex: 1st of March or 28th of March)' pass if d.close[0] > self.entry[dn]*float(1 + self.params.losscut) == 1: self.order_target_percent(data=d, target = 0) self.loss_cut.update({dn:d.close[0]}) self.shorting_list.remove(dn) elif d.close[0]<self.entry[dn]*(1 - self.params.profit): self.order_target_percent(data=d, target = 0) self.profit_realisation.update({dn:d.close[0]}) self.shorting_list.remove(dn) pass
If anyone knows something, please enlighten me!
Thank you :D
-
RE: Closed trade list (including MFE/MAE) analyzer
@ab_trader said in Closed trade list (including MFE/MAE) analyzer:
strats[0]
Hi, I have a simple question
On your final lines,
# get analyzers data trade_list = strats[0].analyzers.trade_list.get_analysis() print (tabulate(trade_list, headers="keys"))
What does this code indicate?
strats[0]
I am new here, so this question might be silly :p
Thank you for sharing!
-
RE: Using self CSV data failed
Thanks for your response!
From now on, I will follow your instructions so that the others can contribute in the future.
so I tried two different things:
class EIKON_HLOC(bt.feeds.GenericCSVData):''' params = ( ('nullvalue' , float('NaN')), ('dtformat', '%Y-%m-%d'), ('datetime', 0), ('open', 1), ('high', 2), ('low', 3), ('close', 4), ('volume', 5), ('openinterest', -1), ) #This will load pre-made CSV (using Eikon) data datalist = [] tickers = ['WMT','IBM','C','AAPL.O','MSFT.O'] for ticker in ticker_list: datalist.append(('{}.csv'.format(ticker), ticker)) #First option using class datalist = [] for ticker in ticker_list: datalist.append(('{}.csv'.format(ticker), ticker)) for i in range(len(datalist)): data = EIKON_HLOC(dataname = datalist[i][0]) cerebro.adddata(data, name=datalist[i][1]) #Second Option using Pandas dataframe datalist = [] for ticker in ticker_list: datalist.append(('{}.csv'.format(ticker), ticker)) for i in range(len(datalist)): # Create a Data Feed dataframe = pd.read_csv(datalist[i][0], index_col = 0) dataframe = dataframe.set_index(pd.DatetimeIndex(dataframe.index)) data = bt.feeds.PandasData(dataname=dataframe) # Add the Data Feed to Cerebro cerebro.adddata(data)
After several trials, both worked. So finally I can create my own strategies to digest personal data (but still trying to understand the coding structures).
Is there any example sources that I can refer to ?
Thank you!
-
RE: Using self CSV data failed
@ab_trader yup just noticed that I mixed up my codes. Sorry for this confusion.
This is it :
In fact, I solved this problem using Pandas DataFeed. But I still want to figure out what I am missing here.
Thanks again :)
-
RE: Using self CSV data failed
Oops, wrong code! sorry guys
This is the one that I tried :But still getting none :p