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)
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)
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)
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)
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)
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
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')
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
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)
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)
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)