Example #1
0
def orders():
    exchanges = db.session.query(Exchange).filter_by(enabled=True)
    open_orders = {}
    closed_orders = {}

    for exchange in exchanges:
        log.info(exchange.name)
        for api_key in exchange.api_keys:
            if api_key.user_id == current_user.id and api_key.exchange_id == exchange.id:
                resp = data_helper.get_open_orders(exchange)
                log.info(resp)
                html = json2html.convert(
                    json=json.dumps(resp),
                    table_attributes=
                    'class="table table-condensed table-bordered table-hover"')
                if html:
                    open_orders[exchange.name] = Markup(html)
                if exchange.ccxt_name in ['binance']:
                    resp = data_helper.get_orders(exchange)
                else:
                    resp = data_helper.get_closed_orders(exchange)
                html = json2html.convert(
                    json=json.dumps(resp),
                    table_attributes=
                    'class="table table-condensed table-bordered table-hover"')
                if html:
                    closed_orders[exchange.name] = Markup(html)

    return render_template('orders.html',
                           open_orders=open_orders,
                           closed_orders=closed_orders)
Example #2
0
def set_boolean_values(form, item):
    """
    Checkboxes are not submitted unless they are checked. So, un-checking a WTFForm.BooleanField will not return a
    false value. The workaround is to test if the name of the checkbox made it into the request. If not, we assume
    that the checkbox is un-checked. If so, we can read the value of the checkbox, but it doesn't matter because it
    should be True anyway.
    NOTE: with 2+ checkboxes with the same name, if any checkbox is checked item's attribute will be set to True.
    :param form:
    :param item:
    :return:
    """
    if request.method == "POST":
        for attr, value in vars(item).items():
            if isinstance(value, bool) and hasattr(form, attr):
                checkbox = getattr(form, attr)
                checkbox_value = request.form.get(
                    attr
                ) is not None  # check if the checkbox name is in the request
                log.info("{}.{} = {}".format(item, attr, checkbox_value))
                if checkbox and hasattr(checkbox, 'data'):
                    setattr(checkbox, 'data', checkbox_value)
    else:
        for attr, value in vars(item).items():
            if isinstance(value, bool) and hasattr(form, attr):
                field = getattr(form, attr)
                if field and hasattr(field, 'data'):
                    setattr(field, 'data', value)
                if field and hasattr(field, ''):
                    setattr(field, 'default', value)
Example #3
0
 def register(cls, app, db_session, *args, **kwargs):
     cls.db_session = db_session
     for key, value in kwargs.items():
         if hasattr(cls, key):
             cls.key = value
             log.info('{}={}'.format(key, cls.key))
     super(CrudView, cls).register(app, *args, **kwargs)
Example #4
0
def login():
    session_token = None

    form = LoginForm()
    if form.remember.data:
        form.email.data = request.form.get('email')

    if form.validate_on_submit():
        email = request.form.get('email')
        password = request.form.get('password')
        remember = True if request.form.get('remember') else False
        user = User.query.filter_by(email=email).first()
        if not user or not check_password_hash(user.password, password):
            flash('Please check your login details and try again.', 'danger')
            return redirect(
                url_for('auth.login')
            )  # if user doesn't exist or password is wrong, reload the page
        login_user(user, remember=remember, force=True)

        # load/generate token to encrypt sensitive date with
        if user.token is None:  # if the user doesn't have a token, then save it
            session_token = Fernet.generate_key()
            user.token = encrypt(session_token, password)
            user.save()
        else:
            try:
                session_token = decrypt(user.token, password)
            except InvalidToken as e:
                log.exception(e)
                pass

        log.info("session_token: {}".format(session_token))
        session['token'] = session_token
        assert ('token' in session and session['token'])

        # redirect to the previous page (if any), or to the user role's default
        if user.is_admin:
            next_page = redirect_url(url_for('admin.dashboard'))
            # exclude auth.login as referrer
            if next_page.endswith(url_for('auth.login')):
                next_page = url_for('admin.dashboard')
        else:
            next_page = redirect_url(url_for('user.index'))
            # exclude auth.login as referrer
            if next_page.endswith(url_for('auth.login')):
                next_page = url_for('user.index')
        resp = make_response(redirect(next_page))

        # update user preferences cookies
        if user.theme:
            resp.set_cookie('chronos-preference-theme', user.theme)
        return resp
    return render_template('login.html', form=form)
Example #5
0
def list_pagekite(filepath, kite):
    """
    Create a command line PageKite process to open up Chronos to the cruel outside world.
    You will need to download 'pagekite.py' which you can find at https://pagekite.net/downloads and perform a first
    time setup. Please, read their quickstart guide on how to do this.
    :param filepath: path to 'pagekite.py' which you can download
    :param kite: the name of your (sub)kite
    :return:
    """
    pagekite = subprocess.run(["python", filepath, kite],
                              stdout=sys.stdout,
                              stderr=sys.stderr)
    log.info("PageKite stopped with exit code was: %d" % pagekite.returncode)
Example #6
0
def tunnel():
    """
    Use a 3rd party process to open up Chronos to the cruel outside world
    :return:
    """
    result = False

    if 'pagekite' in config and 'enabled' in config[
            'pagekite'] and config.getboolean('pagekite', 'enabled'):
        if 'path' in config['pagekite'] and config['pagekite']['path']:
            filepath = config.get("pagekite", "path")
        else:
            filepath = os.path.dirname(
                os.path.realpath(__file__)) + os.path.sep + "pagekite.py"
        kite = ''
        if 'kite' in config['pagekite'] and config['pagekite']['kite']:
            kite = str(config.get("pagekite", "kite"))
        if filepath and kite:
            log.info('starting pagekite ...')
            try:
                threading.Thread(target=list_pagekite, args=[filepath,
                                                             kite]).start()
                log.info('pagekite {} started'.format(kite))
                result = True
            except Exception as e:
                log.exception(e)
    elif 'ngrok' in config and 'enabled' in config[
            'ngrok'] and config.getboolean('ngrok', 'enabled'):
        if 'path' in config['ngrok'] and config['ngrok']['path']:
            filepath = config.get("ngrok", "path")
        else:
            filepath = os.path.dirname(
                os.path.realpath(__file__)) + os.path.sep + "ngrok"
        protocol = ''
        port = ''
        if 'protocol' in config['ngrok'] and config['ngrok']['protocol']:
            protocol = str(config.get('ngrok', 'protocol'))
        if 'port' in config['ngrok'] and config['ngrok']['port']:
            port = str(config.get('ngrok', 'port'))
        log.info('starting ngrok ...')
        try:
            threading.Thread(target=list_ngrok,
                             args=[filepath, protocol, port]).start()
            log.info('ngrok started on {}://localhost:{}'.format(
                protocol, port))
            result = True
        except Exception as e:
            log.exception(e)
    else:
        log.warning(
            'no 3rd party tunneling software is installed and is enabled in chronos.cfg'
        )
    return result
Example #7
0
def main():
    from chronos.libs import tools
    password = config.get('security', 'webhook_password')
    key = ""
    if password:
        key = tools.get_token(password)

    # test_kraken()

    output = "{'exchange': '{{exchange}}', 'type': 'limit', 'side': 'buy', 'quantity': 1, 'symbol': '{{ticker}}', 'price': {{close}}, 'key': '" + key + "'}"
    print()
    log.info("Example webhook message: {}".format(output))
    print()
    # start a thread with the server
    # log.info("creating server")
    threading.Thread(target=start_flask).start()
    sleep(1)
    # threading.Thread(target=tunnel()).start()
    if tunnel():
        log.info("Chronos is set up to receive webhook requests")
    else:
        log.warning('Chronos is not set up to receive webhook requests')
Example #8
0
    def create_order(self, data: dict):
        """
        Create a new order.
        :see https://alpaca.markets/docs/trading-on-alpaca/orders
        :param data: dictionary with any or all of the following keys:
                - symbol: symbol or asset ID
                - quantity: int
                - side: buy or sell
                - order_type: market, limit, stop, stop_limit or trailing_stop
                - limit_price: str of float
                - stop_price: str of float
                - time_in_force: day, gtc, opg, cls, ioc, fok
                    day: for one day during regular trading hours
                    gtc: good until cancelled
                    opg: executes only during market opening auction
                    cls: executes only during market closing auction
                    ioc: immediate or cancel, order needs to be immediately filled; any unfilled portion will be cancelled
                    fok: fill or kill, order needs to be completely filled, otherwise it is cancelled
                - client_order_id:
                - extended_hours: bool. If true, order will be eligible to execute in premarket/afterhours.
                - order_class: simple, bracket, oco or oto
                    bracket: entry, take profit and stop loss order in one
                    oco: one cancels other, e.g. take profit and stoploss; only used in combination with a bracket order
                    oto: one triggers other, e.g. if you only want a stoploss attached to the position; only used in combination with a bracket order
                - take_profit: dict with field "limit_price" e.g {"limit_price": "298.95"}
                - stop_loss: dict with fields "stop_price" and "limit_price" e.g {"stop_price": "297.95", "limit_price": "298.95"}
                - trail_price: str of float
                - instructions: str
                - trail_percent: str of float
        """
        symbol = None
        param_info = ''
        if 'symbol' in data:
            symbol = data['symbol']
            param_info += "symbol: {}".format(symbol)
        quantity = None
        if 'quantity' in data:
            quantity = data['quantity']
            param_info += ", quantity: {}".format(quantity)
        side = None
        if 'side' in data:
            side = data['side']
            param_info += ", side: {}".format(side)
        order_type = None
        if 'type' in data:
            order_type = data['type']
            param_info += ", order_type: {}".format(order_type)
        limit_price = None
        if 'limit_price' in data:
            limit_price = data['limit_price']
            param_info += ", limit_price: {}".format(limit_price)
        stop_price = None
        if 'stop_price' in data:
            stop_price = data['stop_price']
            param_info += ", stop_price: {}".format(stop_price)
        time_in_force = None
        if 'time_in_force' in data:
            time_in_force = data['time_in_force']
            param_info += ", time_in_force: {}".format(time_in_force)
        client_order_id = None
        if 'client_order_id' in data:
            client_order_id = data['client_order_id']
            param_info += ", client_order_id: {}".format(client_order_id)
        extended_hours = None
        if 'extended_hours' in data:
            extended_hours = data['extended_hours']
            param_info += ", extended_hours: {}".format(extended_hours)
        order_class = None
        if 'order_class' in data:
            order_class = data['order_class']
            param_info += ", order_class: {}".format(order_class)
        take_profit = None
        if 'take_profit' in data:
            take_profit = data['take_profit']
            param_info += ", take_profit: {}".format(take_profit)
        stop_loss = None
        if 'stop_loss' in data:
            stop_loss = data['stop_loss']
            param_info += ", stop_loss: {}".format(stop_loss)
        trail_price = None
        if 'trail_price' in data:
            trail_price = data['trail_price']
            param_info += ", trail_price: {}".format(trail_price)
        trail_percent = None
        if 'trail_percent' in data:
            trail_percent = data['trail_percent']
            param_info += ", trail_percent: {}".format(trail_percent)
        instructions = None
        if 'instructions' in data:
            instructions = data['instructions']
            param_info += ", instructions: {}".format(instructions)

        if 'type' == 'market':
            limit_price = None

        result = None
        try:
            result = self.alpaca_rest.submit_order(
                symbol, quantity, side, order_type, time_in_force, limit_price,
                stop_price, client_order_id, extended_hours, order_class,
                take_profit, stop_loss, trail_price, instructions,
                trail_percent)
        except Exception as e:
            log.info(param_info)
            log.exception(e)
        return result
Example #9
0
def webhook():
    """
    Expected request data in JSON format for CCXT:
        chronos_id: int
        exchange: Kraken, Binance, etc
        symbol: symbol or asset ID
        quantity: int
        side: buy or sell
        type: LIMIT, MARKET, STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT, LIMIT_MAKER
        price: limit price

    Expected request data in JSON format for Alpaca:
        chronos_id: int
        exchange: Alpaca or AlpacaPaper
        symbol: symbol or asset ID
        quantity: int
        side: buy or sell
        type: market, limit, stop, stop_limit or trailing_stop
        limit_price: str of float
        stop_price: str of float
        time_in_force: day, gtc, opg, cls, ioc, fok
        client_order_id:
        extended_hours: bool. If true, order will be eligible to execute in premarket/afterhours.
        order_class: simple, bracket, oco or oto
        take_profit: dict with field "limit_price" e.g {"limit_price": "298.95"}
        stop_loss: dict with fields "stop_price" and "limit_price" e.g {"stop_price": "297.95", "limit_price": "298.95"}
        trail_price: str of float
        trail_percent: str of float
        instructions: str

    """
    # password = config.get('authentication', 'password')
    password = config.get('security', 'webhook_password')
    if request.method == 'POST':
        # Parse the string data from tradingview into a python dict
        log.info(request.get_data(as_text=True))
        data = data_helper.parse_webhook(request.get_data(as_text=True))

        # Check that the key is correct
        if (not password) or tools.get_token(password) == data['key']:
            if not ('pwd' in data):
                return "Missing attribute 'pwd'", 401
            if not ('chronos_id' in data):
                return "Missing attribute 'chronos_id'", 401
            # If the user has no session, create a session token
            pwd = data['pwd']
            if isinstance(current_user,
                          mixins.AnonymousUserMixin) or isinstance(
                              current_user, local.LocalProxy):
                session_token = None
                webhook_user = User.query.filter_by(
                    id=data['chronos_id']).first()
                login_user(webhook_user, force=True)

                # load/generate token to encrypt sensitive date with
                if webhook_user.token is None:  # if the user doesn't have a token, then save it
                    session_token = Fernet.generate_key()
                    webhook_user.token = encrypt(session_token, pwd)
                    webhook_user.save()
                else:
                    try:
                        session_token = decrypt(webhook_user.token, pwd)
                    except InvalidToken as e:
                        log.exception(e)
                        pass

                session['token'] = session_token
                assert ('token' in session and session['token'])

            # log.info('POST Received: {}'.format(data))
            data_helper.send_order(data)
            return '', 200
        else:
            abort(403)
    else:
        abort(400)
Example #10
0
def balance():
    exchanges = db.session.query(Exchange).filter_by(enabled=True)
    balances = dict()
    open_orders = dict()
    threshold = 0.01  # only show balance higher than the threshold

    # fields: symbol, balance, exchange
    # search field
    # sort symbol, exchange
    for exchange in exchanges:
        # base_pairs = ["USD", "EUR"]
        # if str(exchange.name).lower().index("kraken") >= 0:
        #     base_pairs.append("XBT")
        # else:
        #     base_pairs.append("BTC")
        # market_pairs = []
        # if exchange.is_ccxt():
        #     markets = get_ccxt_exchange(exchange).markets
        #     for market_pair, info in markets.items():
        #         market_pairs.append(market_pair)
        # log.info(market_pairs)
        # log.info(exchange.name)

        for api_key in exchange.api_keys:
            if api_key.user_id == current_user.id and api_key.exchange_id == exchange.id:
                exchange_open_orders = {}
                try:
                    resp = data_helper.get_balance(exchange)
                    for k, v in resp['total'].items():
                        # if k not in base_pairs and v >= threshold:
                        if v >= threshold:
                            balances["{}.{}".format(k, exchange.name)] = v
                            # asset_open_orders = None
                            # for base in base_pairs:
                            #     try:
                            #         asset_open_orders = data_helper.get_open_orders(exchange, "{}/{}".format(k, base))
                            #     except Exception as e:
                            #         log.exception(e)
                            #     log.info(asset_open_orders)
                            #     open_orders["{}.{}".format(k, exchange.name)] = asset_open_orders
                except Exception as e:
                    log.exception(e)
                try:
                    resp = data_helper.get_open_orders(exchange)
                    for order_data in resp:
                        log.info(order_data)
                        if 'info' in order_data and 'descr' in order_data[
                                'info']:
                            pair = order_data['info']['descr']['pair']
                            exchange_open_orders[pair] = []
                            log.info(pair)
                            exchange_open_orders[pair].append({
                                'id':
                                order_data['id'],
                                'order_status':
                                order_data['info']['status'],
                                'exchange':
                                exchange.name,
                                'pair':
                                pair,
                                'type':
                                order_data['info']['descr']['type'],
                                'amount':
                                order_data['info']['vol'],
                                'price':
                                order_data['info']['descr']['price'],
                                'order_type':
                                order_data['info']['descr']['ordertype'],
                                'leverage':
                                order_data['info']['descr']['leverage'],
                                'filled':
                                order_data['info']['vol_exec'],
                                'created':
                                order_data['info']['opentm'],
                                'start':
                                order_data['info']['starttm'],
                                'expire':
                                order_data['info']['expiretm']
                            })
                    open_orders[exchange.name] = exchange_open_orders
                    log.info(open_orders[exchange.name])
                except Exception as e:
                    log.exception(e)
                # resp = dict((k, v)
                # resp = {k + '.' + exchange.name: v for k, v in resp.items()}
                # for key, value in resp['total'].items():
                #     key = key + '.' + exchange.name
                # result = {**result, **resp}
                break

                # log.info(resp['total'])
                # html = json2html.convert(json=json.dumps(resp), table_attributes='class="table table-condensed table-bordered table-hover"')
                # balance = resp['total']
                # log.info(type(resp['total']))
                # result = dict((k, v) for k, v in resp['total'].items() if v >= 0.01)
                # result = {**result, **dict((k, v) for k, v in resp['total'].items() if v >= threshold)}
                # html += '<p>' + json2html.convert(json=json.dumps(result), collapsible_sub_tables=False, table_attributes='class="table-bordered table-hover"') + '</p>'
                # log.info(html)
                # if html:
                #     balances[exchange.name] = Markup(html)

    html = '<table class="table table-condensed table-bordered table-hover js-table"><thead><tr><th>Asset</th><th>Balance</th><th>Exchange</th></tr></thead><tbody>'
    for key, value in balances.items():
        log.info(key)
        asset, exchange_name = key.rsplit('.', 1)
        # exchange = get_ccxt_exchange(exchange_name)
        # open_orders = data_helper.get_open_orders(exchange, asset)
        # log.info(open_orders)
        html += '<tr><td>{}</td><td><span style="float: right">{}</span></td><td>{}</td></tr>'.format(
            asset, value, exchange_name)
    html += '</tbody></table>'
    return render_template('balance.html',
                           balances=balances,
                           open_orders=open_orders,
                           html=html)
Example #11
0
def list_ngrok(filepath='ngrok', protocol='http', port='5000'):
    ngrok = subprocess.run([filepath, protocol, port],
                           stdout=sys.stdout,
                           stderr=sys.stderr,
                           text=True)
    log.info("Ngrok stopped with exit code was: %d" % ngrok.returncode)