def draw_trade_chart(request, trade_id): ''' Generate a plot of a stock chart with the trade <trade_id> on it as a line. The trade may be open (a position) or closed. ''' trade = Trade.objects.get(id=trade_id) #TODO: gcl = obsolete lookback = trade.method.rank.get_channel_lookback(trade=trade) if not lookback: lookback = Channel.YEAR latest_date = trade.stock.get_latest_date() stoplossdate = next_weekday(latest_date) exitdate = trade.date_exit if trade.rule_exit else latest_date trade_length = (exitdate - trade.date_entry).days # show at least 30 days of the chart before entry daysbefore = max(30, trade_length // 10) startdate = trade.date_entry - datetime.timedelta(days=daysbefore) # adjust the startdate i if trade.rule_exit: # This is a closed trade # show up to 6 months of the chart after exit daysafter = max(30, trade_length // 2) enddate = trade.date_exit + datetime.timedelta(days=daysafter) exitprice = float(trade.price_exit) else: # this is an open trade (portfolio position) enddate = trade.stock.get_latest_date() exitprice = trade.stock.price.close[exitdate] if trade.date_exit: # check if the date for custom stoploss is earlier startdate = min(startdate, trade.date_exit) trade.stock.date_range = (startdate, enddate) chart = trade.stock.chart(style='candle', size=(1024,600)) chart.add_trade((trade.date_entry, exitdate), (float(trade.price_entry), exitprice), colour='black') if not trade.rule_exit: if trade.price_exit and trade.date_exit: # This is a position with a custom stoploss stoploss = trade.stoploss() stoploss_startdate = trade.date_exit stoploss_startprice = trade.stock.price.low[stoploss_startdate] else: # This is a position with the default stoploss stoploss = trade.stock.price.channel.stoploss(lookback=lookback, date=stoplossdate, date_entry=trade.date_entry) stoploss_startdate = trade.stock.price.close.get_date( trade.date_entry, -1) stoploss_startprice = trade.stock.price.channel.bottom(lookback)[ stoploss_startdate] chart.add_trade((stoploss_startdate, latest_date + datetime.timedelta( days=1)), (stoploss_startprice, stoploss), colour='orange') chart.draw_channel_history(lookback) chart.draw_channel_parameters(lookback) chart.draw_channel_indicator(lookback) return chart.plot()
def test_Pool(self): date1 = date(2000,1,1) date2 = date(2010,1,5) date3 = date(2010,1,20) date4 = date(2011,1,5) date5 = date(2011,1,18) date6 = date(2012,1,4) aapl = Stock.objects.get(name='AAPL') intc = Stock.objects.get(name='INTC') fdx = Stock.objects.get(name='FDX') snp = Stock.objects.create(name='^GSPC', description='S&P500', currency=Stock.US_DOLLAR, startdate=date1, enddate=date4) pool = Pool.objects.create(name='test', description='test', index=snp, startdate=date2, enddate=date5) StockPoolDates.objects.create(stock=aapl, pool=pool) StockPoolDates.objects.create(stock=intc, pool=pool) StockPoolDates.objects.create(stock=fdx, pool=pool) print 'SIZE', pool.size() # _get_raw_list temp = StockPoolDates.objects.create(stock=fdx, pool=pool) raw_list = pool._get_raw_list() self.assertEqual(len(raw_list), 4) # does not include index temp.delete() raw_list = pool._get_raw_list() self.assertEqual(len(raw_list), 3) # does not include index #TMP # pool2 = Pool.objects.create(name='test2', description='test2', # index=intc, startdate=date2, enddate=date5) # StockPoolDates.objects.create(stock=intc, pool=pool2) # print 'set prices' # print 'WARNINGS', pool2.set_prices(date2, date5) # print 'INDEX', pool2.index.price.close[date3] # print 'INTC', intc.index.price.close[date3] #/TMP # _get_list # # _get_offset_list dated_stock_list = pool._get_offset_list() self.assertEqual(len(dated_stock_list), 4) #must include index for stockdates in dated_stock_list: self.assertEqual(stockdates[2], date5) self.assertLessEqual(stockdates[1], last_year(date2)) # change the date ranges of the stocks in the pool for st,sd,ed in ((aapl,date3,date6), (intc,date3,date4), (fdx,date1,date4)): spd = StockPoolDates.objects.get(stock=st, pool=pool) spd.startdate = sd spd.enddate = ed spd.save() dated_stock_list = pool._get_offset_list() self.assertEqual(len(dated_stock_list), 4) #must include index for row in ((snp, last_year(date2), date5), (aapl, last_year(date3), date5), (intc, last_year(date3), date4), (fdx, last_year(date2), date4)): self.assertIn(row, dated_stock_list) pool.enddate = None today = date.today() dated_stock_list = pool._get_offset_list() self.assertEqual(len(dated_stock_list), 4) #must include index for row in ((snp, last_year(date2), None), (aapl, last_year(date3), date6), (intc, last_year(date3), date4), (fdx, last_year(date2), date4)): self.assertIn(row, dated_stock_list) # # validate_dates pool.enddate = date5 warnings = pool.check_date_ranges() self.assertEqual(len(warnings), 1) self.assertIn('enddate', warnings[0]) snp.set_dates(date3, date6) warnings = pool.check_date_ranges() self.assertEqual(len(warnings), 1) self.assertIn('startdate', warnings[0]) snp.set_dates(date1, date6) warnings = pool.check_date_ranges() self.assertFalse(warnings) # _set_cache self.assertFalse(hasattr(pool, '_cache')) pool._set_cache() self.assertTrue(hasattr(pool, '_cache')) self.assertEqual(len(pool._cache), 3) for row in ((aapl, date3, date5), (intc, date3, date4), (fdx, date2, date4)): self.assertIn(row, pool._cache) # get_cached_stocklist del pool._cache self.assertFalse(hasattr(pool, '_cache')) self.assertEqual(pool.get_cached_stocklist(last_year(date1)), []) self.assertEqual(pool.get_cached_stocklist(date1), []) self.assertFalse(hasattr(pool, '_cache')) self.assertEqual(pool.get_cached_stocklist(date2), [fdx,]) self.assertTrue(hasattr(pool, '_cache')) self.assertEqual(set(pool.get_cached_stocklist(date3)), set([aapl, intc, fdx])) self.assertEqual(set(pool.get_cached_stocklist(date4)), set([aapl, intc, fdx])) self.assertEqual(pool.get_cached_stocklist(date5), [aapl,]) self.assertEqual(pool.get_cached_stocklist(date6), []) # _get_stock_index_list self.assertEqual(pool._get_stock_index_list(last_year(date1)), []) self.assertEqual(pool._get_stock_index_list(date1), []) self.assertEqual(set(pool._get_stock_index_list(date2)), set([snp, fdx,])) self.assertEqual(set(pool._get_stock_index_list(date3)), set([snp, aapl, intc, fdx])) self.assertEqual(set(pool._get_stock_index_list(date4)), set([snp, aapl, intc, fdx])) self.assertEqual(set(pool._get_stock_index_list(date5)), set([snp, aapl,])) self.assertEqual(pool._get_stock_index_list(date6), []) # # activate self.assertIsNotNone(pool.enddate) pool.activate() self.assertIsNone(pool.enddate) # deactivate self.assertRaises(ValueError, pool.deactivate) # download_prices self.assertEqual(Price.objects.all().count(), 0) snp.set_dates(False, None) pool.download_prices() self.assertRaises(Price.DoesNotExist, Price.objects.get, stock=snp, date=previous_weekday(last_year(date3))) self.assertTrue(Price.objects.get(stock=snp, date=next_weekday(last_year(date3)))) # why use today??? # self.assertTrue(Price.objects.get(stock=snp, # date=previous_weekday(today))) self.assertRaises(Price.DoesNotExist, Price.objects.get, stock=aapl, date=previous_weekday(last_year(date3))) self.assertTrue(Price.objects.get(stock=aapl, date=next_weekday(last_year(date3)))) self.assertTrue(Price.objects.get(stock=aapl, date=previous_weekday(date6))) self.assertRaises(Price.DoesNotExist, Price.objects.get, stock=aapl, date=next_weekday(date6)) self.assertRaises(Price.DoesNotExist, Price.objects.get, stock=intc, date=previous_weekday(last_year(date3))) self.assertTrue(Price.objects.get(stock=intc, date=next_weekday(last_year(date3)))) self.assertTrue(Price.objects.get(stock=intc, date=previous_weekday(date4))) self.assertRaises(Price.DoesNotExist, Price.objects.get, stock=intc, date=next_weekday(date4)) self.assertRaises(Price.DoesNotExist, Price.objects.get, stock=fdx, date=previous_weekday(last_year(date2))) self.assertTrue(Price.objects.get(stock=fdx, date=next_weekday(last_year(date2)))) self.assertTrue(Price.objects.get(stock=fdx, date=previous_weekday(date4))) self.assertRaises(Price.DoesNotExist, Price.objects.get, stock=fdx, date=next_weekday(date4)) # deactivate pool.deactivate() self.assertEqual(pool.enddate, previous_weekday(today)) # check_prices self.assertEqual(pool.check_prices(), []) # intc,date3,date4), spd = StockPoolDates.objects.get(stock=intc, pool=pool) spd.startdate = date3 spd.enddate = date5 spd.save() self.assertEqual(len(pool.check_prices()), 1) spd = StockPoolDates.objects.get(stock=intc, pool=pool) spd.startdate = date2 spd.enddate = date4 spd.save() self.assertEqual(len(pool.check_prices()), 1) # get_todays_stocks_for_download spd = StockPoolDates.objects.get(stock=aapl, pool=pool) spd.enddate = None spd.save() del pool._cache self.assertEqual(Pool.get_todays_stocks_for_download( Stock.US_DOLLAR), []) pool.activate() self.assertEqual(Pool.get_todays_stocks_for_download(Stock.EURO), []) self.assertEqual(set(Pool.get_todays_stocks_for_download( Stock.US_DOLLAR)), set([aapl, snp])) self.assertEqual(Pool.get_todays_stocks_for_download( Stock.BRITISH_POUND), []) # download_prices_today # # import cPickle as pickle # stock_list = Pool.get_todays_stocks_for_download(Stock.US_DOLLAR) # print stock_list # pickle.dump(stock_list, open('save.p', 'wb')) # what if stock does not exist!?? # # export_all # self.assertEqual(Pool.objects.all().count(), 1) # self.assertEqual(Stock.objects.all().count(), 4) # self.assertEqual(StockPoolDates.objects.all().count(), 3) # out = open("test.json", "w") # Pool.export_all(out) # out.close() # Pool.objects.all().delete() # StockPoolDates.objects.all().delete() # self.assertEqual(Pool.objects.all().count(), 0) # self.assertEqual(Stock.objects.all().count(), 4) # self.assertEqual(StockPoolDates.objects.all().count(), 0) # Pool.import_all("test.json") # print Pool.objects.all() # print Stock.objects.all() # print StockPoolDates.objects.all() # self.assertEqual(Pool.objects.all().count(), 1) # self.assertEqual(Stock.objects.all().count(), 4) self.assertEqual(StockPoolDates.objects.all().count(), 3) # copy pool = Pool.objects.get(name='test') new = pool.copy() copy = Pool.objects.get(name=new.name) self.assertEqual(pool.index, copy.index) self.assertEqual(pool.startdate, copy.startdate) self.assertEqual(pool.enddate, copy.enddate) for s_pool,s_copy in zip(pool.members.all(), copy.members.all()): self.assertEqual(s_pool, s_copy) poolmember = s_pool.stockpooldates_set.get(pool=pool) copymember = s_copy.stockpooldates_set.get(pool=copy) self.assertEqual(poolmember.startdate, copymember.startdate) self.assertEqual(poolmember.enddate, copymember.enddate) self.assertEqual(poolmember.stock, copymember.stock)
def show_trade(request, trade_id): ''' Show 2 states of a trade: day before entry and entry + exit (or open trade) ''' trade = Trade.objects.get(id=trade_id) #TODO: gcl = obsolete lookback = trade.method.rank.get_channel_lookback(trade=trade) if not lookback: lookback = Channel.YEAR if trade.rule_exit is not None: enddate = trade.date_exit else: enddate = datetime.date.today() trade.stock.date_range = (last_year(trade.date_entry), enddate) if trade.rule_exit is None: if request.method == 'POST': stoplossform = StoplossForm(request.POST) if stoplossform.is_valid(): trade.date_exit = stoplossform.cleaned_data['startdate'] angle = stoplossform.cleaned_data['angle'] if angle is not None: angle = int(angle) trade.price_exit = angle trade.save() stoplossform = StoplossForm(data={'angle': trade.price_exit, 'startdate': trade.date_exit }) stoplossdate = next_weekday(trade.stock.get_latest_date()) else: stoplossform = stoplossdate = stoploss = None entrychart = reverse('show_stock_chart_from_to_lookback', kwargs={ 'symbol': trade.stock.name, 'startdate_str': date2datestr(trade.date_entry - datetime.timedelta(days=365)), 'enddate_str': date2datestr(trade.date_entry - datetime.timedelta(days=1)), 'lookback': lookback}) exitchart = reverse('draw_trade_chart', kwargs={'trade_id': trade_id}) if not trade.rule_exit: if trade.price_exit and trade.date_exit: # This is an open trade (portfolio position) with a custom stoploss stoploss = trade.stoploss() else: # This is an open trade (portfolio position) with default stoploss stoploss = trade.stock.price.channel.stoploss(lookback=lookback, date=stoplossdate, date_entry=trade.date_entry) # previous, next trade links: trades = trade.system.trade_set.order_by('date_entry') trade_id = int(trade_id) prev_id = prev_trade = next_trade = None for tr in trades: if prev_id == trade_id: next_trade = reverse('show_trade', kwargs={'trade_id': tr.id}) if tr.id == trade_id and prev_id is not None: prev_trade = reverse('show_trade', kwargs={'trade_id': prev_id}) prev_id = tr.id return render(request, 'show_trade.html',{ 'trade': trade, 'entrychart': entrychart, 'exitchart': exitchart, 'entry_data': trade.stock.data_summary( trade.stock.price.close.get_date( trade.date_entry, -1), lookback), 'exit_data': trade.stock.data_summary(enddate, lookback), 'prev_trade': prev_trade, 'next_trade': next_trade, 'stoplossdate': stoplossdate, 'stoploss': stoploss, 'stoplossform': stoplossform})
def edit_system(request, system_id=None): ''' Show System with <system_id> ''' message = '' system = System.objects.get(id=system_id) # generate performance list performance = [] results = [] for param, fmt in OUTPUT_FORMATS.items(): value = getattr(system, param, None) if fmt['type'] == 'performance' and value is not None: # 0 is allowed! performance.append((fmt['name'], fmt['format'].format(value))) results.append((fmt['name'], fmt['format'].format(value))) #generate equity table equity = EquityHistory(system_id=system.id) if equity.exists(): total_equity = equity.get('year') equity_table = EquityTable(total_equity, order_by=('date',)) RequestConfig(request, paginate=None).configure(equity_table) else: equity_table = None #/equity table #generate portfolio table trades = Trade.objects.filter(system=system, rule_exit=None) if len(trades): latest_date = trades[0].stock.get_latest_date() stoploss_date = next_weekday(latest_date) else: stoploss_date = None portfolio_table = PortfolioTable(trades, order_by=('date_entry',)) RequestConfig(request, paginate=None).configure(portfolio_table) #generate trades table trades = Trade.objects.filter(system=system, rule_exit__isnull=False) trades_table = TradesTable(trades, exclude=('id', 'system'), order_by=('date_entry',)) RequestConfig(request, paginate=None).configure(trades_table) #/trades table # copy system to method if required if request.method == 'POST': if request.POST.get('todo') == 'Delete trade': trade_list = request.POST.getlist('tradeid') Trade.objects.filter(id__in=trade_list).delete() tradeform = TradeForm() else: tradeform = TradeForm(data=request.POST) if tradeform.is_valid(): name = tradeform.cleaned_data.get('symbol').upper() volume = tradeform.cleaned_data.get('volume') price = tradeform.cleaned_data.get('price') date = tradeform.cleaned_data.get('date') method = system.metasystem.method_set.all()[0] if request.POST.get('todo') == 'Enter position': if not volume: message = 'volume must be specified for entry' else: try: stock = Stock.objects.get(name=name) print 'NAME', stock.name trade = Trade(system=system, stock=stock, method=method, rule_entry='discret.', price_entry=price, date_entry=date, volume=volume) trade.save() message = 'Position entered' except: message = 'Failed to enter position' elif request.POST.get('todo') == 'Exit position': position_list = request.POST.getlist('positionid') if len(position_list) != 1: message = 'One position must be selected to exit' else: position_id = position_list[0] stock = Trade.objects.get(id=position_id).stock try: trade = Trade.objects.get(id=position_id, system=system, stock=stock, method=method, rule_entry='discret.', rule_exit=None) trade.rule_exit = 'discret.' trade.price_exit = price trade.date_exit = date trade.save() message = 'Position exited' except: message = 'Failed to exit position' else: # this is the first page load tradeform = TradeForm() return render(request, 'edit_system.html', {'system': system, 'trades_table': trades_table, 'stoploss_date': stoploss_date, 'equity_table': equity_table, 'portfolio_table': portfolio_table, 'tradeform': tradeform, 'performance': performance, 'result': results, 'message': message })
def show_system(request, system_id=None): ''' Show System with <system_id> ''' notify = None system = System.objects.get(id=system_id) metasystem = system.metasystem metasystem.make_system(system.params) # generate performance list performance = [] results = [] for param, fmt in OUTPUT_FORMATS.items(): value = getattr(system, param, None) if fmt['type'] == 'performance' and value is not None: # 0 is allowed! performance.append((fmt['name'], fmt['format'].format(value))) if fmt['type'] == 'result' and value is not None: results.append((fmt['name'], fmt['format'].format(value))) #generate equity table equity = EquityHistory(system_id=system.id) if equity.exists(): total_equity = equity.get('year') equity_table = EquityTable(total_equity, order_by=('date',)) RequestConfig(request, paginate=None).configure(equity_table) else: equity_table = None #/equity table # copy system to method if required if request.method == 'POST': copyform = CopyForm(data=request.POST) if request.POST.get('action') == 'Copy System' and copyform.is_valid(): new_ms_id = copyform.cleaned_data['metasystem'] reverse = copyform.cleaned_data['reverse'] for method in metasystem.method_set.all(): parameters = system.get_params(method) method.copy(new_ms_id, parameters, reverse=reverse, comment='copy from system {}'.format(system.id)) else: copyform = CopyForm() # bookmark system if required if request.method == 'POST': bookmarkform = BookmarkForm(data=request.POST) if request.POST.get('action') == 'Bookmark Selected': process_bookmarkform(bookmarkform, request.POST, [system_id]) elif system.bookmark: bookmarkform = BookmarkForm(initial={'bookmark': system.bookmark.id}) else: bookmarkform = BookmarkForm() # process trades/positions tradeform = TradeForm() if request.method == 'POST': if request.POST.get('action') in ('Delete trade', 'Delete position'): trade_list = request.POST.getlist('tradeid') trades = ','.join(Trade.objects.get(id=t).stock.name for t in trade_list) if not len(trade_list): notify = Notify('Delete failed, select position to delete') else: notify = Notify('Delete Trade(s) {}?'.format(trades)) notify.set_replies(request, ('Yes', 'No')) request.session['trade_delete_list'] = trade_list elif request.POST.get('reply'): trade_list = request.session.get('trade_delete_list') if not trade_list: raise AttributeError('session has no valid trade_delete_list') del request.session['trade_delete_list'] if request.POST.get('reply') == 'Yes': trades = ','.join(Trade.objects.get(id=t).stock.name for t in trade_list) Trade.objects.filter(id__in=trade_list).delete() notify = Notify('Trade(s) {} deleted'.format(trades)) elif request.POST.get('reply') == 'No': notify = Notify('Delete cancelled') else: tradeform = TradeForm(data=request.POST) if tradeform.is_valid(): name = tradeform.cleaned_data.get('symbol').upper() volume = tradeform.cleaned_data.get('volume') price = tradeform.cleaned_data.get('price') date = tradeform.cleaned_data.get('date') method = system.metasystem.method_set.all()[0] if request.POST.get('action') == 'Enter position': if not volume: notify = Notify('volume must be specified for entry') else: try: stock = Stock.objects.get(name=name) print 'NAME', stock.name trade = Trade(system=system, stock=stock, method=method, rule_entry='discret.', price_entry=price, date_entry=date, volume=volume) trade.save() notify = Notify('Position entered') except: notify = Notify('Failed to enter position') elif request.POST.get('action') == 'Exit position': position_list = request.POST.getlist('positionid') if len(position_list) != 1: notify = Notify('One position must be selected to exit') else: position_id = position_list[0] stock = Trade.objects.get(id=position_id).stock try: trade = Trade.objects.get(id=position_id, system=system, stock=stock, method=method, rule_entry='discret.', rule_exit=None) trade.rule_exit = 'discret.' trade.price_exit = price trade.date_exit = date trade.save() notify = Notify('Position exited') except: notify = Notify('Failed to exit position') else: # this is the first page load tradeform = TradeForm() #generate portfolio table trades = Trade.objects.filter(system=system, rule_exit=None) if len(trades): latest_date = trades[0].stock.get_latest_date() stoploss_date = next_weekday(latest_date) else: stoploss_date = None portfolio_table = PortfolioTable(trades, order_by=('date_entry',)) RequestConfig(request, paginate=None).configure(portfolio_table) #generate trades table trades = Trade.objects.filter(system=system).exclude(rule_exit=None) trades_table = TradesTable(trades, order_by=('date_entry',)) RequestConfig(request, paginate=None).configure(trades_table) #/trades table return render(request, 'show_system.html', { 'metasystem': metasystem, 'system': system, 'trades_table': trades_table, 'equity_table': equity_table, 'performance': performance, 'result': results, 'copyform': copyform, 'bookmarkform': bookmarkform, 'portfolio_table': portfolio_table, 'stoploss_date': stoploss_date, 'tradeform': tradeform, 'notify': notify, })
def test_next_weekday(self): for day1, day2 in ((6, 7), (7, 10), (8,10), (9,10), (10,11), (11,12), (12,13), (13,14), (14,17), (15,17), (16,17)): self.assertEqual(du.next_weekday(dt.date(2000,1,day1)), dt.date(2000,1,day2))