def update_balance_ndf(ccy, amount_ccy, amount_pv, amount_brl, revenue_brl, side, dc): def incr_by(key, value): db_key = f'Balance/NDF/{ccy}/{key}' databus.increase_by_float(db_key, value) incr_by('TotalAmount', amount_ccy) incr_by('NetPV', amount_pv) incr_by('NetPV_BRL', amount_brl) incr_by('NetAmount', amount_ccy if side.lower() == 'buy' else -amount_ccy) incr_by('Revenue_BRL', revenue_brl) incr_by(f'{side}/TotalPV', amount_pv) incr_by(f'{side}/TotalAmount', abs(amount_ccy)) partial_pv_key = f'Balance/NDF/{ccy}/{side}/PartialPV' partial_pv_list = databus.get(partial_pv_key) tuple_partial_pvs_max_dcs = (1, 31, 61, 91, 181, 361, 721) idx = bisect.bisect_right(tuple_partial_pvs_max_dcs, dc) - 1 partial_pv_list[idx] += amount_pv databus.set(partial_pv_key, partial_pv_list) partial_amount_key = f'Balance/NDF/{ccy}/{side}/PartialAmount' partial_amount_list = databus.get(partial_amount_key) idx = bisect.bisect_right(tuple_partial_pvs_max_dcs, dc) - 1 partial_amount_list[idx] += abs(amount_ccy) databus.set(partial_amount_key, partial_amount_list)
def blotter_transactions(): blotter_msgs = databus.get('Blotter') l_messages = [] if blotter_msgs: for t in blotter_msgs: msg = databus.get('Blotter/{}'.format(t)) l_messages.append(json.loads(msg)) l_messages = sorted(l_messages, key=lambda x: x['quote_req_id']) return l_messages
def config(): with open(get_data_path('RobotFX_SpotConfig.json')) as json_file: spot_config = json.load(json_file) cutoff_times = spot_config['CutOffTimes'] with open(get_data_path('RobotFX_Currencies.json')) as currencies_json_file: cur_data = json.load(currencies_json_file) for cur in cutoff_times['Primary']: cutoff_times['Primary'][cur]['ViewPriority'] = cur_data[cur]['ViewPriority'] with open(get_data_path('RobotFX_NDFTimeBuckets.json')) as json_file: time_buckets_data = json.load(json_file) time_buckets = time_buckets_data['TimeBuckets'] counterparties = [] with open(get_data_path('RobotFX_LegalEntities.json')) as json_file: legal_entities_data = json.load(json_file) for (key, obj) in legal_entities_data.items(): counterparties.append( { 'Id': len(counterparties) + 1, 'Alias': obj['Alias'], 'Counterparty': obj['CounterpartyName'], 'Cnpj': key, 'MarketType': obj['FXMarketType'], 'DefaultTransaction': obj['DefaultFXTransaction'], 'Products': obj['Products'], } ) counterparties = sorted(counterparties, key=lambda cparty: cparty['Alias']) # groups = [] with open(get_data_path('RobotFX_LegalEntitiesRelationships.json')) as json_file: groups_data = json.load(json_file) groups_data = groups_data.get('Groups_Spreads', {}) spot_groups = groups_data.get('FXSPOT', {}) ndf_groups = groups_data.get('FXNDF', {}) spot_timeout = databus.get('TradingParameters/Engine_Global_Parameters/FXSPOT/RFQ_Timeout') ndf_timeout = databus.get('TradingParameters/Engine_Global_Parameters/FXNDF/RFQ_Timeout') json_sorted_currencies = get_currencies_sorted() return render_template( 'config-main.html', cutoff_times=json.dumps(cutoff_times), time_buckets=json.dumps(time_buckets), counterparties=json.dumps(counterparties), spot_groups=json.dumps(spot_groups), ndf_groups=json.dumps(ndf_groups), currencies=json.dumps(cur_data), spot_timeout=json.dumps(spot_timeout), ndf_timeout=json.dumps(ndf_timeout), sorted_currencies=json_sorted_currencies, )
def blotter_generic_quote(msg): strmodel = databus.get(f'Blotter/{msg.QuoteReqID}') model = json.loads(strmodel) model['mtype'] = 'QUOTE' model['buy'] = '-' if msg.BidPx is None else msg.BidPx model['sell'] = '-' if msg.OfferPx is None else msg.OfferPx return model
def blotter_generic_reject_response(msg): strmodel = databus.get(f'Blotter/{msg.QuoteReqID}') model = json.loads(strmodel) model['mtype'] = 'REJECTED' model['color'] = 'red' model['rejected_text'] = msg.Text return model
def supplier_control_limits_put(): currencies_config = request.json['data'] def is_brl_ok(config): return is_positive_number(config.get('SettlementRate', None)) def is_ccy_ok(ccy, config): return (is_positive_number(config.get('SettlementRate', None)) and is_positive_number(config.get('MaxQuantity', None)) and is_positive_number(config.get('MarkupBUY', None), ge=True) and is_positive_number(config.get('MarkupSELL', None), ge=True)) ccy_valid = False system_currencies = databus.get('Currencies') if set(system_currencies) != set(currencies_config.keys()): return jsonify({'status': 'error - invalid_uploaded_data' }) # TODO: melhorar mensagem for ccy, config in currencies_config.items(): if ccy.lower() == 'brl': ccy_valid = is_brl_ok(config) else: ccy_valid = is_ccy_ok(ccy, config) if not ccy_valid: return jsonify({'status': 'error - invalid_uploaded_data'}) with open(get_data_path('RobotFX_FXSupplierControl.json'), 'w') as json_file_out: json_file_out.write(json.dumps(currencies_config, indent=2)) databus.update_from_file(get_data_path('RobotFX_FXSupplierControl.json'), 'FXSupplierControl') return jsonify({'status': 'ok'})
def get_transaction(msg_type, msg_id): result = databus.get(f'Transactions/Messages/{msg_type}/{msg_id}') if result: j = json.loads(result) msg = messages.parse_from_json(j.get('msg')) return msg, j.get('details') return None, None
def blotter_generic_deal(msg): strmodel = databus.get(f'Blotter/{msg.QuoteReqID}') model = json.loads(strmodel) if msg.Type == messages.ExecReportType.ACCEPT: model['mtype'] = 'EXECUTING' else: model['mtype'] = 'EXP.QUOTE' return model
def increase_cash_limits_spot(ccy, amount, n): db_key = f'CashLimits/SPOT/{ccy}/d{n}' if databus.get(db_key) is None: return False, f"Limite de caixa não definido para {ccy} D+{n}" databus.increase_by_float(db_key, amount) databus.publish('cash-limits-spot', 1) return True, ''
def set_timestamp(msg_id, msg_code, value): if msg_code != 'Quote': result = databus.get(f'Transactions/Metrics/{msg_id}') if result is not None: j = json.loads(result) else: j = {'timestamps': {}} j['timestamps'][msg_code] = value pack = json.dumps(j) databus.update_from_dict({msg_id: pack}, 'Transactions/Metrics') else: # for Quote is a list of timestamps result = databus.get(f'Transactions/Metrics/{msg_id}') j = json.loads(result) if 'Quote' not in j['timestamps'].keys(): j['timestamps'][msg_code] = [] j['timestamps'][msg_code].append(get_local_timestamp()) pack = json.dumps(j) databus.update_from_dict({msg_id: pack}, 'Transactions/Metrics') r.publish('history', json.dumps({"info": "metrics", "publish_data": {'pack': pack, 'msg_id': msg_id}}))
def pre_trading_balance_update(): """ @login_required(roles=['e-sales spot']) atualiza o CashLimits adicionando ou removendo + dinheiro. """ balance_update = json.loads(request.data) balance_update = balance_update.get('balance_update', None) if balance_update is not None: for ccy, limits in balance_update.items(): for maturity, value in limits.items(): if value is None: continue value = float(value) n = int(maturity[1]) if n not in [0, 1]: continue if databus.get(f'CashLimits/SPOT/{ccy}/{maturity}') is None: databus.set(f'CashLimits/SPOT/{ccy}/{maturity}', 0.0) if value > 0: success, error_msg = increase_cash_limits_spot( ccy, value, n) else: success, error_msg = decrease_cash_limits_spot(ccy, value, n, reset=True) ccy_logs = databus.get(f'CashLimits/Logs/{ccy}/{maturity}') if isinstance(ccy_logs, list): ccy_logs.append(value) databus.set(f'CashLimits/Logs/{ccy}/{maturity}', ccy_logs) if not success: return jsonify({'status': 'fail', 'reason': error_msg}) return jsonify({'status': 'ok'})
def decrease_cash_limits_spot(ccy, amount, n, reset=False): db_key = f'CashLimits/SPOT/{ccy}/d{n}' if databus.get(db_key) is None: return False, f"Limite de caixa não definido para {ccy} D+{n}" try: if not databus.decrease_if_greater_than(db_key, amount, reset): return False, f"Fundos insuficientes para {ccy} D+{n}" except RuntimeError: return False, "Operação inválida" databus.publish('cash-limits-spot', 1) return True, ''
def spread_transaction_getter(is_ndf, is_group): basic_key = 'SpreadRegistry/' + ('NDF' if is_ndf else 'SPOT') + '/' + ( 'group' if is_group else 'counterparty') if databus.exists(basic_key): transactions = databus.get(basic_key) transactions = sorted(transactions, key=lambda x: x['ts'], reverse=True) else: transactions = [] return json.dumps(transactions)
def getBlotterSpotPopup(): deal_id = request.args.get('dealid', '') if not deal_id: abort(404, description="Resource not found") try: info = json.loads( databus.get('Blotter/{deal_id}'.format(deal_id=deal_id))) except TypeError: abort(404, description="Resource not found") return render_template('blotter-spot-dealid-popup.html', deal_id=json.dumps(deal_id), info=info)
def engine_control_spot(): with open(get_data_path('RobotFX_TradingParameters.json')) as json_file: trading_parameters = json.load(json_file) engine_parameters = trading_parameters["Engine_Global_Parameters"][ "FXSPOT"] with open(get_data_path('RobotFX_SpotConfig.json')) as json_file: spot_config = json.load(json_file) cutoff_times = spot_config['CutOffTimes'] spot_timeout = databus.get( 'TradingParameters/Engine_Global_Parameters/FXSPOT/RFQ_Timeout') return render_template( 'engine-control-spot.html', engine_parameters=json.dumps(engine_parameters), cutoff_times=json.dumps(cutoff_times), spot_timeout=json.dumps(spot_timeout), )
def update_blotter(msg): model = None if isinstance(msg, messages.QuoteRequest): quoterequest = msg else: quoterequest, _ = get_quoterequest_msg(msg.QuoteReqID) if quoterequest.SecurityType == messages.EnumSecurityType.SPOT: model = update_blotter_spot(msg) if model: cur = model["currency"] root_key = f'Balance/SPOT/{cur}/TotalAmount/' if model['mtype'] == "DEAL": buy_total = sum(float(databus.get(root_key + 'BuyD' + str(i))) for i in range(3)) sell_total = sum(float(databus.get(root_key + 'SellD' + str(i))) for i in range(3)) accounting_supplier_model = { model['currency']: { 'net': buy_total - sell_total, 'buy_total': buy_total, 'buy_d0': float(databus.get(root_key + 'BuyD0')), 'buy_d1': float(databus.get(root_key + 'BuyD1')), 'buy_d2': float(databus.get(root_key + 'BuyD2')), 'sell_total': sell_total, 'sell_d0': float(databus.get(root_key + 'SellD0')), 'sell_d1': float(databus.get(root_key + 'SellD1')), 'sell_d2': float(databus.get(root_key + 'SellD2')), } } accounting_supplier_model_json = json.dumps(json.dumps(accounting_supplier_model)) r.publish('accounting_fxsupplier', accounting_supplier_model_json) elif quoterequest.SecurityType == messages.EnumSecurityType.NDF: model = update_blotter_ndf(msg) if model: model_json = json.dumps(model) entry = {model['quote_req_id']: model_json} databus.update_from_dict(entry, 'Blotter') r.publish('blotter', json.dumps(model_json)) r.publish('blotter_fxsupplier', json.dumps(model_json))
def blotter_generic_quote_request(quoterequest): if quoterequest.SecurityType == messages.EnumSecurityType.SPOT: security_type = 'FXSPOT' elif quoterequest.SecurityType == messages.EnumSecurityType.NDF: security_type = 'FXNDF' model = { 'quote_req_id': quoterequest.QuoteReqID, 'color': 'orange', 'mtype': 'RFQ', 'cnpj': None, 'counterparty': quoterequest.CustomerStr, 'client_side': quoterequest.Side, 'security_type': security_type, 'dealcode': quoterequest.CustomerDealCode, 'amount': quoterequest.OrderQty, 'symbol': quoterequest.Symbol, 'currency': quoterequest.Currency, 'notes': quoterequest.Text, } model['cnpj'] = quoterequest.CustomerID model['counterparty'] = databus.get(f'LegalEntities/{quoterequest.CustomerID}/CounterpartyName') return model
def blotter_generic_quote_cancel(msg): strmodel = databus.get(f'Blotter/{msg.QuoteReqID}') model = json.loads(strmodel) model['mtype'] = 'TIMEOUT' return model
def check_spot_limits_initialized(): status = databus.get('System/Status/Trading/SPOT/Initialized') return json.dumps(status)
def casado_data(): data_json = {} data_json['CasadoBuy'] = databus.get('FXSupplierCasado/Price') return jsonify(data_json)
def supplier_data(security_type): data = {} if security_type not in ('SPOT', 'NDF'): raise Exception('error: security type not found!') user_id = session.get("user_id", None) role = get_user_role(user_id) if role == 'fx-supplier': quoting_not_halted = True elif security_type == 'SPOT': quoting_not_halted = databus.get('System/Status/Quoting/Spot/All') elif security_type == 'NDF': quoting_not_halted = databus.get('System/Status/Quoting/NDF/All') if quoting_not_halted: casado = databus.get('FXSupplierCasado/Price') else: casado = '-' data['Casado'] = {'Price': casado} data['CurrencyPairs'] = {} currency_pairs = databus.get( 'FXSupplierData/{security}'.format(security=security_type)) if currency_pairs is None: currency_pairs = {} for ccy_pair in currency_pairs: ccy = ccy_pair[0:3] precision = databus.get('Currencies/{ccy}/Precision'.format(ccy=ccy)) if quoting_not_halted: bid = databus.get( 'FXSupplierData/{security}/{ccy_pair}/Bid'.format( security=security_type, ccy_pair=ccy_pair)) ask = databus.get( 'FXSupplierData/{security}/{ccy_pair}/Ask'.format( security=security_type, ccy_pair=ccy_pair)) if bid is None or ask is None: bid = "Fail" ask = "Fail" else: bid = round(bid, precision) ask = round(ask, precision) else: bid = '-' ask = '-' view_priority = databus.get( 'Currencies/{ccy}/ViewPriority'.format(ccy=ccy)) data['CurrencyPairs'][ccy_pair] = { 'Bid': bid, 'Ask': ask, 'ViewPriority': view_priority } if quoting_not_halted: fut_usdbrl_bid = databus.get('MarketData/Futures/USDBRL/Active/Bid') fut_usdbrl_ask = databus.get('MarketData/Futures/USDBRL/Active/Ask') if fut_usdbrl_bid is None or fut_usdbrl_ask is None: fut_usdbrl_bid = "Fail" fut_usdbrl_ask = "Fail" else: fut_usdbrl_bid = "-" fut_usdbrl_ask = "-" data['Futures'] = { "USDBRL": { 'Active': { 'Bid': fut_usdbrl_bid, 'Ask': fut_usdbrl_ask } } } return jsonify(data)
def supplier_control_data(): data_json = {} currencies = databus.get('Currencies') for currency in currencies: ccy_info = {} base = 'FXSupplierControl/{ccy}/'.format(ccy=currency) ccy_info['SettlementRate'] = databus.get(base + 'SettlementRate') if currency.upper() != 'BRL': ccy_info['SettlementRateCurveConvention'] = databus.get( base + 'SettlementRateCurveConvention') ccy_info['MaxQuantity'] = databus.get(base + 'MaxQuantity') ccy_info['MarkupSELL'] = databus.get(base + 'MarkupSELL') ccy_info['MarkupBUY'] = databus.get(base + 'MarkupBUY') precision = databus.get( 'Currencies/{ccy}/Precision'.format(ccy=currency)) value_buy = databus.get( 'FXSupplierData/SPOT/{ccy}BRL/Bid'.format(ccy=currency)) value_sell = databus.get( 'FXSupplierData/SPOT/{ccy}BRL/Ask'.format(ccy=currency)) if value_buy is None or value_sell is None: ccy_info['Buy'] = "Fail" ccy_info['Sell'] = "Fail" else: ccy_info['Buy'] = round(value_buy, precision) ccy_info['Sell'] = round(value_sell, precision) ccy_info['CashLimitsD0'] = databus.get( 'CashLimits/SPOT/{ccy}/d0'.format(ccy=currency)) ccy_info['CashLimitsD1'] = databus.get( 'CashLimits/SPOT/{ccy}/d1'.format(ccy=currency)) else: ccy_info['Buy'] = '-' ccy_info['Sell'] = '-' data_json[currency] = ccy_info return jsonify(data_json)
def blotter_generic_neworder(msg): strmodel = databus.get(f'Blotter/{msg.QuoteReqID}') model = json.loads(strmodel) model['mtype'] = 'NEW.ORDER' return model
def get_transaction_status(quoterequest_id): return databus.get(f'Transactions/Status/{quoterequest_id}')
def blotter_generic_reject_request(msg): strmodel = databus.get(f'Blotter/{msg.QuoteReqID}') model = json.loads(strmodel) model['mtype'] = 'NOTH.DONE' model['rejected_text'] = msg.Text return model
def get_user_role(user_id): user_role_databus_key = f"ActiveUsers/{user_id}/Role" user_role = databus.get(user_role_databus_key) return user_role if user_role else None
def blotter_generic_exec_ack(msg): strmodel = databus.get(f'Blotter/{msg.QuoteReqID}') model = json.loads(strmodel) model['color'] = '#49ed6d' model['mtype'] = 'DEAL' return model
def spreads_spot_put(): now = get_local_time() type_update = request.args.get('type', '').lower() update_group = type_update == 'group' key = request.args.get('key', '').upper() status = request.json['status'] currency = status['currency'] spotDay = status['spotday'] side = status['side'] spread = status['spread'] if spread != '-': spread = int( Decimal(spread) * 10_000) # Solucao de caso de spread igual a: 12, 24 ou 48. basic_key = 'SpreadRegistry/SPOT/' + type_update if databus.exists(basic_key): data_list = databus.get(basic_key) else: data_list = [] user_id = session.get("user_id", None) username = get_username(user_id) element = { 'target': key, 'ts': now.strftime('%Y-%m-%d %H:%M:%S'), 'user': str(username), 'ccy': str(currency), 'spotday': str(spotDay), 'side': str(side), 'spread': str(spread), } if not update_group: element['counterparty'] = databus.get( 'LegalEntities/{cnpj}/CounterpartyName'.format(cnpj=key)) basic_group_key = 'LegalEntitiesRelationships/Groups_Spreads_' if databus.exists((basic_group_key + 'FX{type}_Memberships/{cnpj}').format(cnpj=key, type='SPOT')): element['group'] = databus.get( (basic_group_key + 'FX{type}_Memberships/{cnpj}').format( cnpj=key, type="SPOT")) else: element['group'] = '-' data_list.append(element) databus.set(basic_key, data_list) manage_spreads_tables(1, is_ndf=False) with open(get_data_path('RobotFX_Client_Spreads.json')) as json_file: all_spreads = json.load(json_file) entity_type = 'GroupSpreads' if update_group else 'CounterpartySpreads' if key not in all_spreads[entity_type]: all_spreads[entity_type][key] = {} all_spreads[entity_type][key]['FXSPOT'] = request.json['spreads'] with open(get_data_path('RobotFX_Client_Spreads.json'), 'w') as json_file_out: json_file_out.write(json.dumps(all_spreads, indent=2)) databus.update_from_file(get_data_path('RobotFX_Client_Spreads.json'), 'ClientSpreads') return jsonify({'status': 'ok'})
def get_username(user_id): username_databus_key = f"ActiveUsers/{user_id}/Username" username = databus.get(username_databus_key) return username if username else None
def blotter_generic_exec_ack_unknown(msg): strmodel = databus.get(f'Blotter/{msg.QuoteReqID}') model = json.loads(strmodel) model['mtype'] = 'ACK.UNKNOWN' return model