Snippets

Michael Lee WF Hackathon Workshop #2

Created by Michael Lee
from flask import Flask, request, render_template, jsonify, redirect
import urllib.request, urllib.parse, urllib.error
import json
import datetime

app = Flask(__name__)
app.debug = True

stocks = []
date = datetime.date(2000, 1, 3)
cash = 100000


class Stock:
    def __init__(self, symbol, quantity, purch_date, init_price):
        self.symbol = symbol
        self.quantity = quantity
        self.purch_date = purch_date
        self.init_price = init_price
        self.earnings = 0
        self.last_price = init_price
        self.last_earning = 0


@app.route('/')
def index():
    return render_template('paper.html', date=date, stocks=stocks, cash=cash)


@app.route('/advance/<int:days>')
def advance_day(days):
    global date
    date += datetime.timedelta(days=days)
    while not is_trading_day(date):
        date += datetime.timedelta(days=1)
    update_stocks()
    return jsonify(date=date.isoformat(),
                   stocks=[s.__dict__ for s in stocks])


def update_stocks():
    for s in stocks:
        q = fetch_quotes(s.symbol, date, date)
        if q:
            s.earnings = s.quantity * (float(q['Close']) - s.init_price)
            s.last_earning = s.quantity * (float(q['Close']) - s.last_price)
            s.last_price = float(q['Close'])


@app.route('/trade')
def trade():
    global cash
    symbol = request.args['symbol']
    quantity = int(request.args['quantity'])
    q = fetch_quotes(symbol, date, date)
    if q:
        s = Stock(symbol, quantity, date, float(q['Open']))
        cash -= quantity * s.init_price
        stocks.append(s)
        return jsonify(cash=cash,
                       stocks=[s.__dict__ for s in stocks])
    else:
        return jsonify({})


@app.route('/sell/<int:sidx>')
def sell(sidx):
    global cash
    stock = stocks[sidx]
    cash += stock.quantity * stock.last_price
    del stocks[sidx]
    return jsonify(cash=cash,
                   stocks=[s.__dict__ for s in stocks])


@app.route('/lookup/<sym>')
def lookup(sym):
    quotes = fetch_quotes(sym, date-datetime.timedelta(30), date)
    if quotes:
        return jsonify(quotes)
    else:
        return jsonify({})


def is_trading_day(date):
    return fetch_quotes('YHOO', date, date) is not None


# noinspection SqlNoDataSourceInspection
def fetch_quotes(symbol, start_date, end_date):
    base_url = 'http://query.yahooapis.com/v1/public/yql'
    yql = ('select * from yahoo.finance.historicaldata where symbol = "' + symbol
           + '" and startDate = "' + start_date.isoformat()
           + '" and endDate = "' + end_date.isoformat() + '"')
    req_url = (base_url + '?q=' + urllib.parse.quote(yql)
               + '&env=http%3A%2F%2Fdatatables.org%2Falltables.env&format=json')
    try:
        response = urllib.request.urlopen(req_url)
        results = json.loads(response.read().decode('utf-8'))['query']['results']
        if results:
            return results['quote']
        else:
            return None
    except urllib.error.HTTPError:
        return None

@app.route('/reset', methods=['POST'])
def reset():
    global cash, date
    stocks.clear()
    cash = 100000
    date = datetime.date(2000, 1, 3)
    return redirect('/')


if __name__ == '__main__':
    app.run()
<!DOCTYPE html>
<html>
    <head>
        <title>Simple Paper Trader</title>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
        <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
        <script src="https://cdn.rawgit.com/etpinard/plotlyjs-finance/master/plotlyjs-finance.js"></script>
    </head>
    <body>
    <p>
        Date: <span id="date">{{ date.isoformat() }}</span>
        <button id="advance_day">Advance day</button>
        <button id="advance_month">Advance month</button>
        <button id="advance_year">Advance year</button>
        <button id="restart">Restart</button>
    </p>
    <h3>Portfolio</h3>
    <ol id="portfolio">
        {% for s in stocks %}
            <li>
                Symbol: {{ s.symbol.upper() }},
                Quantity: {{ s.quantity }},
                Price: {{ '${:,.2f}'.format(s.last_price) }},
                Gains: {{ '${:,.2f}'.format(s.earnings) }}
                <button class="sell" value="{{ loop.index0 }}">Sell</button>
            </li>
        {% endfor %}
    </ol>
    <p>
        Cash on hand: <span id="cash">{{ "${:,.2f}".format(cash) }}</span>
    </p>

    <hr>

    <h3>Trade</h3>
    <form id="trade">
        <label for="symbol">Symbol</label>
        <input type="text" name="symbol" value="AAPL">
        <label for="quantity">Quantity</label>
        <input type="text" name="quantity" value="1">
        <input type="Submit" value="Buy">
    </form>

    <hr>

    <h3>Lookup</h3>
    <form id="lookup">
        <label for="symbol">Symbol</label>
        <input id="lookup_symbol" type="text" name="symbol" value="AAPL">
        <input type="Submit" value="Search">
    </form>

    <div id="plotly" style="width:700px;height:400px;"></div>

    <script>
        function formatCurrency(value) {
            return '$' + value.toFixed(2).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
        }

        function advance(days) {
            $.getJSON('/advance/' + days, function(data) {
                $('#date').text(data.date);
                updateStocks(data.stocks);
            });
        }

        function updateStocks(stocks) {
            $('#portfolio').html('');
            var idx = 0;
            $.map(stocks, function(s) {
                $('#portfolio').append('<li>'
                                + 'Symbol: ' + s.symbol.toUpperCase() + ', '
                                + 'Quantity: ' + s.quantity + ', '
                                + 'Price: ' + formatCurrency(s.last_price) + ', '
                                + 'Gains: ' + formatCurrency(s.earnings)
                                + '<button class="sell" value="' + idx + '">Sell</button>'
                                + '</li>');
                idx += 1;
            });
            $('.sell').click(function(e) {
                $.getJSON('/sell/' + this.value, function(data) {
                    if (data) {
                        $('#cash').text(formatCurrency(data.cash));
                        updateStocks(data.stocks);
                    }
                });
            });
        }

        $(function() {
            $('.sell').click(function(e) {
                $.getJSON('/sell/' + this.value, function(data) {
                    if (data) {
                        $('#cash').text(formatCurrency(data.cash));
                        updateStocks(data.stocks);
                    }
                });
            });

            $('#advance_day').click(function(e) {
                advance(1);
            });

            $('#advance_month').click(function(e) {
                advance(30);
            });

            $('#advance_year').click(function(e) {
                advance(365);
            });

            $('#trade').submit(function(e) {
                var sym = this.elements.symbol.value;
                var qty = this.elements.quantity.value;
                $.getJSON('/trade', { symbol: sym, quantity: qty }, function(data) {
                    if (data) {
                        $('#cash').text(formatCurrency(data.cash));
                        updateStocks(data.stocks);
                    }
                });
                e.preventDefault();
            });

            $('#lookup').submit(function(e) {
                var sym = $('#lookup_symbol').val();
                console.log(sym);
                $.getJSON('/lookup/' + sym, function(quotes) {
                    if (quotes) {
                        var fig = PlotlyFinance.createCandlestick(
                                {
                                    open: $.map(quotes, function (q) {
                                        return parseFloat(q.Open)
                                    }),
                                    high: $.map(quotes, function (q) {
                                        return parseFloat(q.High)
                                    }),
                                    low: $.map(quotes, function (q) {
                                        return parseFloat(q.Low)
                                    }),
                                    close: $.map(quotes, function (q) {
                                        return parseFloat(q.Close)
                                    }),
                                    dates: $.map(quotes, function (q) {
                                        return new Date(q.Date)
                                    })
                                }
                        );
                        Plotly.newPlot('plotly', fig.data, fig.layout);
                    }
                });
                e.preventDefault();
            });

            $('#restart').click(function() {
                $.post('/reset', function() {
                    window.location.reload();
                });
            });
        });
    </script>
    </body>
</html>

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.