diff options
Diffstat (limited to 'python')
| -rw-r--r-- | python/Dawn/models.py | 33 | ||||
| -rw-r--r-- | python/Dawn/templates/base.html | 8 | ||||
| -rw-r--r-- | python/Dawn/templates/wire_blotter.html | 40 | ||||
| -rw-r--r-- | python/Dawn/templates/wire_entry.html | 51 | ||||
| -rw-r--r-- | python/Dawn/views.py | 65 |
5 files changed, 188 insertions, 9 deletions
diff --git a/python/Dawn/models.py b/python/Dawn/models.py index 4bda22d7..fd33926b 100644 --- a/python/Dawn/models.py +++ b/python/Dawn/models.py @@ -29,6 +29,14 @@ class Counterparties(db.Model): notes = db.Column(db.String) instructions = db.Column(db.String, info={'form_field_class': FileField}) +class Accounts(db.Model): + __tablename__ = 'accounts' + code = db.Column(db.String(5), primary_key=True) + name = db.Column(db.String) + custodian = db.Column(db.String) + cash_account = db.Column(db.String) + counterpaty = db.Column(db.String, db.ForeignKey('counterparties.code')) + BOND_STRAT = ENUM('M_STR_MAV', 'M_STR_MEZZ', 'CSO_TRANCH', 'M_CLO_BB20', 'M_CLO_AAA', 'M_CLO_BBB', 'M_MTG_IO', 'M_MTG_THRU', 'M_MTG_GOOD', 'M_MTG_B4PR', 'M_MTG_RW', 'M_MTG_FP', 'M_MTG_LMG', @@ -45,6 +53,10 @@ SWAPTION_STRAT = ENUM('IGPAYER', 'IGREC', 'HYPAYER', 'HYREC', FUTURE_STRAT = ENUM('M_STR_MAV', 'M_MTG_IO', 'M_STR_MEZZ', 'M_MTG_RW', 'SER_ITRXCURVE', name='future_strat') +CASH_STRAT = ENUM('M_CSH_CASH', 'MBSCDSCSH', 'SER_IGCVECSH', 'SER_ITRXCVCSH', 'CSOCDSCSH', + 'IGCDSCSH', 'HYCDSCSH', 'CLOCDSCSH', 'IGTCDSCSH', 'MACCDSCSH', + name='cash_strat') + SWAPTION_TYPE = ENUM('PAYER', 'RECEIVER', name='swaption_type') @@ -83,10 +95,10 @@ class BondDeal(db.Model): cashaccount = db.Column(db.String(10), default='V0NSCLMAMB', nullable=False) cp_code = db.Column(db.String(12), db.ForeignKey('counterparties.code'), info={'choices': [(None, '')], - 'label': 'counterparty'}, nullable = False) + 'label': 'counterparty'}, nullable=False) trade_date = db.Column(db.Date, nullable = False) settle_date = db.Column(db.Date, nullable = False) - cusip = db.Column(db.String(9), info={'validators': Length(9,9), + cusip = db.Column(db.String(9), info={'validators': Length(9, 9), 'filters': [lambda x: x or None,], 'trim': True}) isin = db.Column(db.String(12), info={'validators': Length(12, 12), @@ -197,7 +209,7 @@ class SwaptionDeal(db.Model): trade_date = db.Column(db.Date, nullable=False) settle_date = db.Column(db.Date, nullable=False) buysell = db.Column(db.Boolean, nullable=False, info={'choices':[(0, 'sell'), (1, 'buy')], - 'coerce': lambda x: bool(int(x)) \ + 'coerce': lambda x: bool(int(x)) \ if x is not None else x}) notional = db.Column(db.Float, nullable=False) swaption_type = db.Column(SWAPTION_TYPE, nullable=False) @@ -241,6 +253,21 @@ class FutureDeal(db.Model): exchange = db.Column(db.String(3), default='CME', nullable=False) counterparty = db.relationship(Counterparties) +class CashFlowDeal(db.Model): + __tablename__ = 'wires' + id = db.Column('id', db.Integer, primary_key=True) + dealid = db.Column(db.String(28)) + lastupdate = db.Column(db.DateTime, server_default=db.func.now(), onupdate=db.func.now()) + action = db.Column(ACTION) + folder = db.Column(CASH_STRAT, nullable=False) + code = db.Column(db.String(5), db.ForeignKey('accounts.code'), + info={'choices': [('IB', 'pomme')], + 'label': 'account'}, nullable=False) + amount = db.Column(db.Float, nullable=False) + trade_date = db.Column(db.Date, nullable=False) + settle_date = db.Column(db.Date, nullable=False) + account = db.relationship(Accounts) + BaseModelForm = model_form_factory(FlaskForm) class ModelForm(BaseModelForm): @classmethod diff --git a/python/Dawn/templates/base.html b/python/Dawn/templates/base.html index a17f98d3..ab2c005d 100644 --- a/python/Dawn/templates/base.html +++ b/python/Dawn/templates/base.html @@ -21,13 +21,14 @@ data-toggle="dropdown" role="button" aria-haspopup="true" - aria-expanded="false">Blotter <span class="caret"></span> + aria-expanded="false">Blotter<span class="caret"></span> </a> <ul class="dropdown-menu"> <li><a href="{{url_for('list_trades', kind='bond')}}">Bonds</a></li> <li><a href="{{url_for('list_trades', kind='cds')}}">CDS</a></li> <li><a href="{{url_for('list_trades', kind='swaption')}}">Swaptions</a></li> <li><a href="{{url_for('list_trades', kind='future')}}">Futures</a></li> + <li><a href="{{url_for('list_trades', kind='wire')}}">Wires</a></li> </ul> </li> <li class="dropdown"> @@ -35,13 +36,14 @@ data-toggle="dropdown" role="button" aria-haspopup="true" - aria-expanded="false">Book <span class="caret"></span> + aria-expanded="false">Book<span class="caret"></span> </a> <ul class="dropdown-menu"> <li><a href="{{url_for('trade_manage', kind='bond')}}">Bonds</a></li> <li><a href="{{url_for('trade_manage', kind='cds')}}">CDS</a></li> <li><a href="{{url_for('trade_manage', kind='swaption')}}">Swaptions</a></li> <li><a href="{{url_for('trade_manage', kind='future')}}">Futures</a></li> + <li><a href="{{url_for('wire_manage')}}">Wires</a></li> </ul> </li> <li class="dropdown"> @@ -49,7 +51,7 @@ data-toggle="dropdown" role="button" aria-haspopup="true" - aria-expanded="false">Counterparties <span class="caret"></span> + aria-expanded="false">Counterparties<span class="caret"></span> </a> <ul class="dropdown-menu"> <li><a href="../counterparties">List</a></li> diff --git a/python/Dawn/templates/wire_blotter.html b/python/Dawn/templates/wire_blotter.html new file mode 100644 index 00000000..8397e5df --- /dev/null +++ b/python/Dawn/templates/wire_blotter.html @@ -0,0 +1,40 @@ +{% extends "base.html" %} +{% block content %} +<table class="table table-striped"> + <thead> + <tr> + <td>Deal ID</td> + <td>Trade Date</td> + <td>Settle Date</td> + <td>Buy/Sell</td> + <td>Quantity</td> + <td>Type</td> + <td>Maturity</td> + <td>Price</td> + <td>Commission</td> + <td>Description</td> + <td>Ticker</td> + <td>Counterparty</td> + <td>Strategy</td> + </tr> + </thead> + {% for trade in trades %} + <tr> + <td><a href="{{url_for('wire_manage', wire_id=wire.id)}}">{{wire.dealid}}</a></td> + <td>{{trade.trade_date}}</td> + <td>{{trade.settle_date}}</td> + <td>{% if trade.buysell %}Buy{% else %}Sell{% endif %}</td> + <td>{{trade.quantity}}</td> + <td>{{trade.swap_type}}</td> + <td>{{trade.maturity}}</td> + <td>{{trade.price}}</td> + <td>{{trade.commission}}</td> + <td>{{trade.security_desc}}</td> + <td>{{trade.bbg_ticker}}</td> + <td><a href="{{url_for('edit_counterparty', + cpcode=trade.counterparty.code)}}">{{trade.counterparty.name}}</a></td> + <td>{{trade.folder}}</td> + </tr> + {% endfor %} +</table> +{% endblock %} diff --git a/python/Dawn/templates/wire_entry.html b/python/Dawn/templates/wire_entry.html new file mode 100644 index 00000000..a5da6b2b --- /dev/null +++ b/python/Dawn/templates/wire_entry.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css"> + </head> + <body style="max-width:1024px; margin:0 auto"> + <datalist id="index_list"></datalist> + <form method="POST" class="form-horizontal" + action="{{action_url}}" enctype="multipart/form-data"> + {% for field in form if field.type != 'BooleanField' %} + <div class="form-group {% if field.id in form.errors %}has-error{% endif %}"> + {% if field.type != 'CSRFTokenField' %} + <label class="control-label col-md-2" for="{{ field.id }}"> + {{ field.label.text }} + </label> + {% endif %} + <div class="col-md-3"> + {{ field(class_="form-control") }} + </div> + {% if field.id in form.errors %} + <div class="col-md-3"> + {{form.errors[field.id][0]}} + </div>{% endif %} + </div> + {% endfor %} + <div class="form-group"> + <div class="col-md-offset-2 col-md-3"> + <div class="checkbox"> + <label> + <input id="upload_globeop" name="upload_globeop" type="checkbox" value="y">Upload to globeop? + </label> + </div> + </div> + </div> + <div class="form-group"> + <div class="col-md-offset-2 col-md-3"> + <button type="submit" class="btn btn-default">Submit</button> + </div> + </div> + </form> + {% if 'cds' or 'swaption' in action_url %} + <script type="text/javascript" src="https://code.jquery.com/jquery-2.2.0.min.js"></script> + <script type="text/javascript" + src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" + integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" + crossorigin="anonymous"></script> + <script type="text/javascript" src="{{ url_for('static', filename='dawn.js') }}"></script> + {% endif %} + </body> +</html> diff --git a/python/Dawn/views.py b/python/Dawn/views.py index 2d1d55d8..761fdd5b 100644 --- a/python/Dawn/views.py +++ b/python/Dawn/views.py @@ -1,6 +1,9 @@ from flask import (request, render_template, redirect, url_for, send_from_directory, send_file, g, jsonify) -from .models import ModelForm, BondDeal, CDSDeal, SwaptionDeal, FutureDeal, Counterparties +from .models import (ModelForm, + BondDeal, CDSDeal, SwaptionDeal, FutureDeal, CashFlowDeal, + Counterparties, Accounts) + from sqlalchemy.exc import IntegrityError from wtforms.fields import BooleanField import pandas as pd @@ -32,6 +35,9 @@ def cp_choices(kind='bond'): filter(Counterparties.name.ilike('%CDS%')). with_entities(Counterparties.code, Counterparties.name)) +def account_codes(): + return Accounts.query.order_by('code').with_entities(Accounts.code, Accounts.code) + def get_queue(): q = getattr(g, 'queue', None) if q is None: @@ -90,6 +96,13 @@ class FutureForm(ModelForm): include_foreign_keys = True exclude = ['dealid', 'lastupdate'] +class WireForm(ModelForm): + upload_globeop = BooleanField(label="Upload to globeop?") + class Meta: + model = CashFlowDeal + include_foreign_keys = True + exclude = ['dealid', 'lastupdate'] + def get_deal(kind): if kind == 'cds': return CDSDeal @@ -139,10 +152,21 @@ def get_form(trade, kind): continue return form +def get_wire_form(wire): + if wire.id: + return WireForm(obj=trade) + else: + today = pd.datetime.today() + form = WireForm(trade_date=today.date()) + return form + def get_trade(tradeid, kind): Deal = get_deal(kind) return Deal.query.get(tradeid) if tradeid else Deal() +def get_wire(wiredid): + CashFlowDeal.query.get(wireid) + def save_ticket(trade, old_ticket_name): if trade.ticket: if old_ticket_name: @@ -156,6 +180,39 @@ def save_ticket(trade, old_ticket_name): else: trade.ticket = old_ticket_name +@app.route('/wires/<int:wire_id>', methods = ['GET', 'POST']) +@app.route('/wires/', defaults = {'wire_id': None}, methods = ['GET', 'POST']) +def wire_manage(wire_id): + if wire_id is None: + wire = CashFlowDeal() + else: + wire = CashFlowDeal().query.get(wire_id) + form = WireForm() + form.code.choices = form.code.choices + list(account_codes()) + if form.validate_on_submit(): + form.populate_obj(wire) + session = form.get_session() + if not wire_id: + session.add(wire) + try: + session.commit() + except IntegrityError as e: + app.logger.error(e) + session.rollback() + return render_template('wire_entry.html', form=form, + action_url= + url_for('wire_manage', wire_id=wire_id)) + else: + if form.upload_globeop.data: + q = get_queue() + q.rpush('wires', simple_serialize(wire)) + return redirect(url_for('list_trades')) + else: + form = get_wire_form(wire) + form.code.choices = form.code.choices + list(account_codes()) + return render_template('wire_entry.html', form=form, + action_url = url_for('wire_manage', wire_id=wire_id)) + @app.route('/trades/<kind>/<int:tradeid>', methods = ['GET', 'POST']) @app.route('/trades/<kind>/', defaults = {'tradeid': None}, methods = ['GET', 'POST']) @app.route('/trades/', defaults = {'tradeid': None, 'kind': 'bond'}, methods = ['GET', 'POST']) @@ -196,11 +253,13 @@ def trade_manage(tradeid, kind): @app.route('/blotter/<kind>') @app.route('/blotter/', defaults={'kind': 'bond'}) def list_trades(kind): - Deal = get_deal(kind) + if kind == 'wire': + Deal = CashFlowDeal() + else: + Deal = get_deal(kind) trade_list = Deal.query.order_by(Deal.trade_date.desc(), Deal.id.desc()) return render_template('{}_blotter.html'.format(kind), trades=trade_list.all()) - @app.route('/tickets/<int:tradeid>') def download_ticket(tradeid): trade = BondDeal.query.get(tradeid) |
