from flask import (request, render_template, redirect, url_for, send_from_directory, send_file, g, jsonify) from .models import ModelForm, BondDeal, CDSDeal, Counterparties from sqlalchemy.exc import IntegrityError from wtforms.fields import BooleanField import pandas as pd from pandas.tseries.offsets import CustomBusinessDay from pandas.tseries.holiday import get_calendar, HolidayCalendarFactory, GoodFriday import os import datetime import redis from .utils import load_counterparties, bump_rev, simple_serialize from PyPDF2 import PdfFileMerger from io import BytesIO from Dawn import app import socket import psycopg2 fed_cal = get_calendar('USFederalHolidayCalendar') bond_cal = HolidayCalendarFactory('BondCalendar', fed_cal, GoodFriday) bus_day = CustomBusinessDay(calendar=bond_cal()) def cp_choices(): return Counterparties.query.order_by('name').\ with_entities(Counterparties.code, Counterparties.name) def get_queue(): q = getattr(g, 'queue', None) if q is None: hostname = socket.gethostname() if hostname == 'debian': q = g.queue = redis.Redis(unix_socket_path='/var/run/redis/redis.sock') elif hostname =='gomez': q = g.queue = redis.Redis(host='debian') return q def get_db(): db = getattr(g, '_database', None) if db is None: db = g._database = psycopg2.connect(database="serenitasdb", user="serenitas_user", host="debian") return db @app.teardown_appcontext def close_connection(exception): db = getattr(g, '_database', None) if db is not None: db.close() class CounterpartyForm(ModelForm): class Meta: model = Counterparties class BondForm(ModelForm): upload_globeop = BooleanField(label="Upload to globeop?") class Meta: model = BondDeal include_foreign_keys = True exclude = ['dealid', 'lastupdate', #we generate it with a trigger at the server level 'principal_payment', 'accrued_payment'] class CDSForm(ModelForm): upload_globeop = BooleanField(label="Upload to globeop?") class Meta: model = CDSDeal include_foreign_keys = True exclude = ['dealid', 'lastupdate'] @app.route('/trades/', methods=['GET', 'POST']) @app.route('/trades/', defaults={'tradeid': None}, methods=['GET', 'POST']) def trade_manage(tradeid): trade = BondDeal.query.get(tradeid) if tradeid else BondDeal() if tradeid: bond_form = BondForm(obj = trade) old_ticket_name = trade.ticket else: bond_form = BondForm(trade_date = pd.datetime.today().date(), settle_date = pd.datetime.today().date() + 3 * bus_day) #add extra empty fields bond_form.folder.choices = [(None, '')] + bond_form.folder.choices bond_form.buysell.choices = [(None, '')] + bond_form.buysell.choices bond_form.asset_class.choices = [(None, '')] + bond_form.asset_class.choices bond_form.cp_code.choices = bond_form.cp_code.choices + list(cp_choices()) if bond_form.is_submitted(): if bond_form.validate(): bond_form.populate_obj(trade) session = bond_form.get_session() ticket_file = trade.ticket trade.ticket = None if not tradeid: session.add(trade) if ticket_file.filename == '': if tradeid: trade.ticket = old_ticket_name else: if tradeid and old_ticket_name: trade.ticket = bump_rev(old_ticket_name) else: trade.ticket = "{0} {1}.pdf".format(str(trade.trade_date), trade.description) ticket_file.save(os.path.join(app.config['TICKETS_FOLDER'], trade.ticket)) try: session.commit() except IntegrityError as e: app.logger.error(e) else: if bond_form.upload_globeop.data: q = get_queue() q.rpush('bond_trades', simple_serialize(trade)) return redirect(url_for('list_trades')) else: return str(bond_form.errors) return render_template('trade_entry.html', form=bond_form, trade_id=tradeid) @app.route('/cdstrades/', methods=['GET', 'POST']) @app.route('/cdstrades/', defaults={'tradeid': None}, methods=['GET', 'POST']) def cds_trade_manage(tradeid): trade = CDSDeal.query.get(tradeid) if tradeid else CDSDeal() if tradeid: cds_form = CDSForm(obj = trade) else: today = pd.datetime.today() tomorrow = today + pd.DateOffset(1) cds_form = CDSForm(trade_date = today.date(), effective_date= tomorrow.date(), upfront_settle_date = today.date() + 3 * bus_day) cds_form.cp_code.choices = cds_form.cp_code.choices + list(cp_choices()) if cds_form.is_submitted(): if cds_form.validate(): cds_form.populate_obj(trade) session = cds_form.get_session() if not tradeid: session.add(trade) try: session.commit() except IntegrityError as e: app.logger.error(e) finally: if cds_form.upload_globeop.data: q = get_queue() q.rpush('cds_trades', simple_serialize(trade)) return redirect(url_for('list_cds_trades')) return render_template('cds_trade_entry.html', form=cds_form, trade_id=tradeid) @app.route('/blotter/') def list_trades(): trade_list = BondDeal.query.order_by(BondDeal.trade_date.desc(), BondDeal.id.desc()) return render_template('blotter.html', trades=trade_list.all()) @app.route('/cdsblotter') def list_cds_trades(): trade_list = CDSDeal.query.order_by(CDSDeal.trade_date.desc(), CDSDeal.id.desc()) return render_template('cds_blotter.html', trades=trade_list.all()) @app.route('/tickets/') def download_ticket(tradeid): trade = BondDeal.query.get(tradeid) pdf = PdfFileMerger() pdf.append(os.path.join(app.config['TICKETS_FOLDER'], trade.ticket)) pdf.append(os.path.join(app.config['CP_FOLDER'], trade.counterparty.instructions)) fh = BytesIO() pdf.write(fh) pdf.close() fh.seek(0) return send_file(fh, mimetype='application/pdf') @app.route('/counterparties/', methods=['GET']) @app.route('/counterparties/', defaults={'instr': None}, methods=['GET']) def list_counterparties(instr): if instr: return send_from_directory(filename = instr, directory = app.config['CP_FOLDER'], mimetype='application/pdf') else: cp_list = Counterparties.query.order_by(Counterparties.name) return render_template('counterparties.html', counterparties = cp_list.all()) @app.route('/edit_cp/', methods=['GET', 'POST']) def edit_counterparty(cpcode): cp = Counterparties.query.get(cpcode) cp_form = CounterpartyForm(obj = cp) old_instructions = cp.instructions if cp_form.is_submitted(): if cp_form.validate(): cp_form.populate_obj(cp) session = cp_form.get_session() instructions = cp.instructions if instructions.filename == '': cp.instructions = old_instructions else: cp.instructions = cp.name + '.pdf' instructions.save(os.path.join(app.config['CP_FOLDER'], cp.instructions)) session.commit() return redirect(url_for('list_counterparties')) return render_template('edit_cp.html', form=cp_form, code=cpcode) @app.route('/_ajax', methods=['GET']) def get_bbg_id(): bbg_id = request.args.get('bbg_id') _, indextype, _, series, tenor = bbg_id.split() indextype = indextype[:2] tenor = tenor[:-1] + 'yr' series = int(series[1:]) sqlstr1 = "SELECT * FROM index_redcode(%s::index_type, %s::smallint, %s)" sqlstr2 = "SELECT maturity, coupon FROM index_maturity WHERE index=%s and series=%s and tenor=%s" db = get_db() with db.cursor() as c: print(indextype, series) c.execute(sqlstr1, (indextype, series, datetime.datetime(2015,10, 25).date())) (redcode,) = c.fetchone() c.execute(sqlstr2, (indextype, series, tenor)) maturity, coupon = c.fetchone() return jsonify({'maturity': maturity.strftime('%Y-%m-%d'), 'redcode': redcode, 'coupon': coupon})