class StockNewsView(View): template_name = 'finance/stock_news.html' td = TradingData() td.setup() data_provider_url = td.data_provider_url def get(self, request, source, symbol): stock_news = self.td.get_stock_news(symbol) context = { 'source': source, 'symbol': symbol, 'company': self.td.get_company_name(symbol), 'stock_news': stock_news, } return render(request, self.template_name, context) def post(self, request, source, symbol): view_kwargs = { 'source': source, 'symbol': symbol, } return redirect(reverse('stock_news', kwargs=view_kwargs))
class QuoteView(View): stockquote_form = StockQuoteForm template_name = 'finance/stock_quotes.html' td = TradingData() td.setup() markets = ['XNAS', 'XNYS', 'XAMS', 'INDEX'] data_provider_url = td.data_provider_url def get(self, request): user = request.user if user.is_authenticated: # add Person class to user user.__class__ = Person try: default_user = Person.objects.get(username='******') except Person.DoesNotExist: default_user = None quote_string = request.session.get('quote_string', '') selected_portfolio = request.session.get('selected_portfolio', '') markets = request.session.get('markets', self.markets) stockdetail = request.session.get('stockdetail', STOCK_DETAILS[0][0]) form = self.stockquote_form( initial={ 'quote_string': quote_string, 'selected_portfolio': selected_portfolio, 'markets': markets, 'stockdetails': stockdetail, }) portfolios = default_user.get_portfolio_names() if user.is_authenticated: portfolios += user.get_portfolio_names() if selected_portfolio: try: # try if user has selected a portfolio if authenticated if user.is_authenticated: symbols = Portfolio.objects.get( user=user, portfolio_name=selected_portfolio).get_stock() stock_info = self.td.get_stock_trade_info(symbols[0:20]) stock_info += self.td.get_stock_trade_info(symbols[20:40]) else: raise Portfolio.DoesNotExist except Portfolio.DoesNotExist: # try if it is a default portfolio try: symbols = Portfolio.objects.get( user=default_user, portfolio_name=selected_portfolio).get_stock() #pylint: disable=line-too-long stock_info = self.td.get_stock_trade_info(symbols[0:20]) stock_info += self.td.get_stock_trade_info(symbols[20:40]) except Portfolio.DoesNotExist: pass else: symbols = self.td.parse_stock_quote(quote_string, markets=markets) stock_info = self.td.get_stock_trade_info(symbols[0:20]) stock_info = add_display_tokens(stock_info) stock_info = format_and_sort_stocks(stock_info) context = { 'source': source, 'stock_info': stock_info, 'form': form, 'portfolios': sorted(portfolios), 'data_provider_url': self.data_provider_url, } return render(request, self.template_name, context) def post(self, request): user = request.user quote_string = request.session.get('quote_string', '') selected_portfolio = request.session.get('selected_portfolio', '') markets = request.session.get('markets', self.markets) stockdetail = request.session.get('stockdetail', STOCK_DETAILS[0][0]) form = self.stockquote_form(request.POST) if form.is_valid(): form_data = form.cleaned_data new_quote_string = form_data.get('quote_string') new_selected_portfolio = form_data.get('selected_portfolio') markets = form_data.get('markets') stockdetail = form_data.get('stockdetails') if new_selected_portfolio != selected_portfolio: selected_portfolio = new_selected_portfolio quote_string = '' elif new_quote_string != quote_string: quote_string = new_quote_string selected_portfolio = '' else: pass request.session['quote_string'] = quote_string request.session['selected_portfolio'] = selected_portfolio request.session['markets'] = markets request.session['stockdetail'] = stockdetail logger.info(f'user {user} [ip: {get_client_ip(request)}] looking ' f'up: {quote_string} / {selected_portfolio}') else: pass return redirect(reverse('stock_quotes'))
class IntraDayView(View): template_name = 'finance/stock_intraday.html' td = TradingData() td.setup() data_provider_url = td.data_provider_url def get(self, request, source, symbol): user = request.user if not Stock.objects.filter(symbol_ric=symbol): return redirect(reverse('stock_quotes')) intraday_trades = self.td.get_stock_intraday_info(symbol) if not intraday_trades: return redirect( reverse( 'stock_history', kwargs={'symbol': symbol, 'source': source, 'period': PLOT_PERIODS[0]} ) ) chart_data = [] min_price, max_price, max_volume = None, None, None date_format = '%d-%m-%Y %H:%M' for trade in intraday_trades: chart_data.append([ trade.date.strftime(date_format), trade.open, trade.close, trade.low, trade.high, trade.volume, ]) min_price = get_min(trade.low, min_price) max_price = get_max(trade.high, max_price) max_volume = get_max(trade.volume, max_volume) if not min_price or not max_price: min_price, max_price = 0, 0 if not max_volume: max_volume = 0 try: initial_open = intraday_trades[1].open latest_close = intraday_trades[-2].close prc_change = 100 * (float(latest_close) - float(initial_open)) / float(initial_open) except IndexError: initial_open, latest_close, prc_change = None, None, None if initial_open and latest_close and prc_change: if abs(float(prc_change)) < 0.01: txt_color = 'txt_normal' caret = CARET_NO_CHANGE elif float(prc_change) < 0: txt_color = 'txt_red' caret = CARET_DOWN else: txt_color = 'txt_green' caret = CARET_UP subcaption = ''.join([Stock.objects.get(symbol_ric=symbol).currency.currency, f' {float(latest_close):.2f} ({prc_change:.1f}%)', f' {caret}']) else: subcaption = '' txt_color = 'txt_normal' caption = ''.join([Stock.objects.get(symbol_ric=symbol).company, ' (', symbol, ')']) schema = json.dumps(self.td.get_schema(date_format)) chart_data = json.dumps(chart_data) time_series = TimeSeries(FusionTable(schema, chart_data)) time_series.AddAttribute( 'styleDefinition', {'txt_red': { 'fill': font_red, 'font-weight': font_weight, 'font-size': font_size}, 'txt_green': { 'fill': font_green, 'font-weight': font_weight, 'font-size': font_size}, 'txt_normal': { 'fill': font_white, 'font-weight': font_weight, 'font-size': font_size}, }) time_series.AddAttribute( 'chart', {'multicanvas': '0', 'theme': chart_theme, 'showlegend': '0',}) time_series.AddAttribute('caption', {'text': caption, }) time_series.AddAttribute( 'subcaption', {'text': subcaption, 'style': {'text': txt_color}, }) time_series.AddAttribute('navigator', {'enabled': '0'}) time_series.AddAttribute( 'extensions', {'customrangeselector': {'enabled': '0'}}) time_series.AddAttribute( 'yaxis', [ {'plot': [ {'value': { 'open':'open', 'high': 'high', 'low':'low', 'close':'close'}, 'type':'candlestick'}], 'title':'Stock Value', 'min': min_price*0.99, 'max': max_price*1.01, }, {'plot': [{'value': 'volume', 'type': 'column'}], 'title': 'Volume', 'max': max_volume * 3, }, ]) trade_series = FusionCharts('timeseries', 'trades', width, height, 'chart-container', 'json', time_series, ) context = {'chart_js': trade_series.render(), 'data_provider_url': self.data_provider_url, 'stock_symbol': symbol, 'source': source, 'periods': PLOT_PERIODS, } logger.info(f'user {user} [ip: {get_client_ip(request)}] is looking ' f'intraday trades for {symbol}') return render(request, self.template_name, context) def post(self, request, source, symbol): pass
class PortfolioView(View): form_class = PortfolioForm template_name = 'finance/stock_portfolio.html' td = TradingData() td.setup() data_provider_url = td.data_provider_url def get(self, request): currency = request.session.get('currency', 'EUR') selected_portfolio = request.session.get('selected_portfolio', '') user = request.user # add Person class to user if user.is_authenticated: user.__class__ = Person portfolio_name = '' stocks = [] if selected_portfolio: try: self.portfolio = Portfolio.objects.get( user=user, portfolio_name=selected_portfolio) portfolio_name = self.portfolio.portfolio_name stocks = self.get_stock_info(GetStock.YES, currency) request.session['stock_info'] = json.dumps( stocks, cls=DjangoJSONEncoder) except Portfolio.DoesNotExist: selected_portfolio = '' else: pass form = self.form_class(user=user, initial={ 'symbol': '', 'portfolio_name': portfolio_name, 'portfolios': selected_portfolio, 'currencies': currency }) totals_values = format_totals_values( *self.td.calculate_stocks_value(stocks)) stocks = add_display_tokens(stocks) stocks = format_and_sort_stocks(stocks) context = { 'form': form, 'stocks': stocks, 'totals': totals_values, 'source': source, 'data_provider_url': self.data_provider_url, } return render(request, self.template_name, context) def post(self, request): # TODO add refresh button self.request = request self.user = self.request.user # add Person class to user if self.user.is_authenticated: self.user.__class__ = Person currency = request.session.get('currency', 'EUR') form = self.form_class(self.request.POST, user=self.user) if form.is_valid(): form_data = form.cleaned_data currency = form_data.get('currencies') self.selected_portfolio = form_data.get('portfolios') self.portfolio_name = form_data.get('portfolio_name') self.new_portfolio = form_data.get('new_portfolio') self.symbol = form_data.get('symbol').upper() self.btn1_pressed = form_data.get('btn1_pressed') self.change_qty_btn_pressed = form_data.get( 'change_qty_btn_pressed') self.delete_symbol_btn_pressed = form_data.get( 'delete_symbol_btn_pressed') self.previous_selected = request.session.get('selected_portfolio') previous_currency = request.session.get('currency') if (self.previous_selected != self.selected_portfolio or previous_currency != currency): get_stock = GetStock.YES else: get_stock = GetStock.NO try: self.portfolio = Portfolio.objects.get( user=self.user, portfolio_name=self.selected_portfolio) except Portfolio.DoesNotExist: self.portfolio = None self.selected_portfolio = '' self.btn1_pressed = '' self.btn2_pressed = '' get_stock = GetStock.EMPTY if self.new_portfolio: get_stock = self.create_new_portfolio() elif self.btn1_pressed: get_stock = self.rename_or_delete_portfolio_or_add_stock() elif self.change_qty_btn_pressed: get_stock = self.change_quantity_symbol() elif self.delete_symbol_btn_pressed: get_stock = self.delete_symbol() else: pass # set portfolio name except if the portfolio has been deleted or # does not exist try: self.portfolio_name = self.portfolio.portfolio_name except AttributeError: self.portfolio_name = '' stocks = self.get_stock_info(get_stock, currency) request.session['stock_info'] = json.dumps(stocks, cls=DjangoJSONEncoder) request.session['selected_portfolio'] = self.selected_portfolio request.session['currency'] = currency form = self.form_class(user=self.user, initial={ 'portfolio_name': self.portfolio_name, 'portfolios': self.selected_portfolio, 'symbol': self.symbol, 'currencies': currency }) logger.info( f'user {self.user} [ip: {get_client_ip(self.request)}] ' f'views {self.selected_portfolio}') else: form = self.form_class(user=self.user, initial={ 'portfolios': '', 'portfolio_name': '', 'symbol': '', 'currencies': currency }) stocks = [] totals_values = format_totals_values( *self.td.calculate_stocks_value(stocks)) stocks = add_display_tokens(stocks) stocks = format_and_sort_stocks(stocks) context = { 'form': form, 'stocks': stocks, 'totals': totals_values, 'source': source, 'data_provider_url': self.data_provider_url, } return render(self.request, self.template_name, context) def create_new_portfolio(self): try: self.portfolio = Portfolio.objects.create( user=self.user, portfolio_name=self.new_portfolio) self.selected_portfolio = self.new_portfolio get_stock = GetStock.EMPTY except IntegrityError: # patch in case initial choice portfolios is None then for some reason # choicefield will return the first option. In that case reset all if not self.previous_selected: self.portfolio = None self.selected_portfolio = self.previous_selected get_stock = GetStock.EMPTY else: self.new_portfolio = '' get_stock = GetStock.NO return get_stock def rename_or_delete_portfolio_or_add_stock(self): ''' actions when btn1 is pressed ''' if not self.portfolio: logger.warning(f'{self.portfolio}: check existance of portfolio') return GetStock.NO if not self.portfolio_name: return GetStock.NO if self.btn1_pressed == 'rename_portfolio': get_stock = GetStock.NO if self.portfolio_name != self.selected_portfolio: try: self.portfolio.portfolio_name = self.portfolio_name self.portfolio.save() self.selected_portfolio = self.portfolio_name except IntegrityError: pass else: pass elif self.btn1_pressed == 'delete_portfolio': self.portfolio.delete() self.portfolio = None self.selected_portfolio = '' self.portfolio_name = '' get_stock = GetStock.EMPTY elif self.btn1_pressed == 'add_new_symbol': try: StockSelection.objects.create( stock=Stock.objects.get(symbol_ric=self.symbol), quantity=0, portfolio=self.portfolio) self.symbol = '' get_stock = GetStock.YES except (Stock.DoesNotExist, IntegrityError, ValueError): get_stock = GetStock.NO else: logger.warning(f'Invalid value btn1_pressed: {self.btn1_pressed}') return get_stock def change_quantity_symbol(self): ''' change quantiy of symbol when change_quantity_btn is pressed ''' if not self.portfolio: logger.warning(f'{self.portfolio}: check existance of portfolio') return GetStock.NO try: symbol, quantity = self.change_qty_btn_pressed.split(',') symbol = symbol.strip() quantity = quantity.strip() except ValueError: return GetStock.NO portfolio_selected_stock = self.portfolio.stocks.get( stock__symbol_ric=symbol) if portfolio_selected_stock.stock.currency.currency != 'N/A': portfolio_selected_stock.quantity = quantity portfolio_selected_stock.save() get_stock = GetStock.YES else: get_stock = GetStock.NO return get_stock def delete_symbol(self): ''' delete symbol from portfolio when delete symbol btn is pressed ''' if not self.portfolio: logger.warning(f'{self.portfolio}: check existance of portfolio') return GetStock.NO self.portfolio.stocks.get( stock__symbol_ric=self.delete_symbol_btn_pressed).delete() return GetStock.YES def get_stock_info(self, get_stock, currency): ''' get stock info depending of get_stock status''' stocks = [] if self.portfolio: if get_stock == GetStock.YES: stocks = self.td.get_portfolio_stock_info( self.portfolio, currency) elif get_stock == GetStock.NO: try: stocks = json.loads(self.request.session.get('stock_info')) except TypeError: stocks = self.td.get_portfolio_stock_info( self.portfolio, currency) elif get_stock == GetStock.EMPTY: # stocks is empty list already fullfilled pass else: logger.warning(f'invalid selection for get_stock: {get_stock}') else: # stocks is empty list already fullfilled pass return stocks
class HistoryView(View): template_name = 'finance/stock_history.html' td = TradingData() td.setup() data_provider_url = td.data_provider_url def get(self, request, source, symbol, period): user = request.user if period not in PLOT_PERIODS: return redirect(reverse('stock_quotes')) elif period == PLOT_PERIODS[-1]: period_num = 50 else: period_num = float(period) if not Stock.objects.filter(symbol_ric=symbol): return redirect(reverse('stock_quotes')) history_trades = self.td.get_stock_history_info(symbol, period) if history_trades: start_period = history_trades[0].date -\ datetime.timedelta(days=int(period_num*365)) chart_data = [] min_price, max_price, max_volume = None, None, None date_format = '%d-%m-%Y' for trade in history_trades: if trade.date > start_period: chart_data.append([ trade.date.strftime(date_format), trade.open, trade.close, trade.low, trade.high, trade.volume,]) min_price = get_min(trade.low, min_price) max_price = get_max(trade.high, max_price) max_volume = get_max(trade.volume, max_volume) if not min_price or not max_price: min_price, max_price = 0, 0 start_date, end_date = '01-01-1900', '01-01-1900' else: end_date = history_trades[0].date start_date = end_date - datetime.timedelta(days=365) start_date = start_date.strftime(date_format) end_date = end_date.strftime(date_format) if not max_volume: max_volume = 0 subcaption = '' caption = ''.join([Stock.objects.get(symbol_ric=symbol).company, ' (', symbol, ')']) schema = json.dumps(self.td.get_schema(date_format)) chart_data = json.dumps(chart_data) time_series = TimeSeries(FusionTable(schema, chart_data)) time_series.AddAttribute('chart', { 'multicanvas': '0', 'theme': chart_theme, 'showlegend': '0',}) time_series.AddAttribute('caption', {'text': caption, }) time_series.AddAttribute('subcaption', {'text': subcaption,}) time_series.AddAttribute('navigator', {'enabled': '1'}, ) time_series.AddAttribute('extensions', {'customrangeselector': {'enabled': '1'}}) time_series.AddAttribute('xaxis', { 'initialinterval': {'from': start_date, 'to': end_date}}) time_series.AddAttribute('yaxis', [ {'plot': [{'value':{ 'open':'open', 'high':'high', 'low': 'low', 'close': 'close'}, 'type': 'candlestick'}], 'title': 'Stock Value (' + Stock.objects.get(symbol_ric=symbol). currency.currency + ')', 'min': min_price*0.99, 'max': max_price*1.01, }, {'plot': [{'value': 'volume', 'type': 'column'}], 'title': 'Volume', 'max': max_volume * 3, }, ]) trade_series = FusionCharts('timeseries', 'trades', width, height, 'chart-container', 'json', time_series, ) context = {'chart_js': trade_series.render(), 'data_provider_url': self.data_provider_url, 'stock_symbol': symbol, 'source': source, 'period': period, 'periods': PLOT_PERIODS, } logger.info(f'user {user} [ip: {get_client_ip(request)}] is looking ' f'historic trades for {symbol}') return render(request, self.template_name, context) def post(self, request, source, symbol, period): pass