Ejemplo n.º 1
0
def test_MOEX_downloader_USD(prepare_db_moex):
    usd_quotes = pd.DataFrame({'Close': [12.02, 11.90],
                               'Date': [datetime(2021, 12, 13), datetime(2021, 12, 14)]})
    usd_quotes = usd_quotes.set_index('Date')
    downloader = QuoteDownloader()
    quotes_downloaded = downloader.MOEX_DataReader(8, 'FXGD', 2, 'IE00B8XB7377', 1639353600, 1639440000, update_symbol=False)
    assert_frame_equal(usd_quotes, quotes_downloaded)
Ejemplo n.º 2
0
def test_Frankfurt_downloader():
    quotes = pd.DataFrame({'Close': [233.40, 234.25],
                           'Date': [datetime(2021, 4, 13), datetime(2021, 4, 14)]})
    quotes = quotes.set_index('Date')

    downloader = QuoteDownloader()
    quotes_downloaded = downloader.YahooFRA_Downloader(0, 'VOW3', 3, '', 1618272000, 1618444800)
    assert_frame_equal(quotes, quotes_downloaded)
Ejemplo n.º 3
0
def test_TMX_downloader():
    quotes = pd.DataFrame({'Close': [117.18, 117.34, 118.02],
                           'Date': [datetime(2021, 4, 13), datetime(2021, 4, 14), datetime(2021, 4, 15)]})
    quotes = quotes.set_index('Date')

    downloader = QuoteDownloader()
    quotes_downloaded = downloader.TMX_Downloader(0, 'RY', 3, '', 1618272000, 1618444800)
    assert_frame_equal(quotes, quotes_downloaded)
Ejemplo n.º 4
0
def test_Euronext_downloader():
    quotes = pd.DataFrame({'Close': [3.4945, 3.5000, 3.4995],
                           'Date': [datetime(2021, 4, 13), datetime(2021, 4, 14), datetime(2021, 4, 15)]})
    quotes = quotes.set_index('Date')

    downloader = QuoteDownloader()
    quotes_downloaded = downloader.Euronext_DataReader(0, '', 3, 'FI0009000681', 1618272000, 1618444800)
    assert_frame_equal(quotes, quotes_downloaded)
Ejemplo n.º 5
0
def test_LSE_downloader():
    quotes = pd.DataFrame({'Close': [73.5, 75.5],
                           'Date': [datetime(2021, 4, 13), datetime(2021, 4, 14)]})
    quotes = quotes.set_index('Date')

    downloader = QuoteDownloader()
    quotes_downloaded = downloader.YahooLSE_Downloader(0, 'TSL', 3, '', 1618272000, 1618444800)
    assert_frame_equal(quotes, quotes_downloaded)
Ejemplo n.º 6
0
def test_NYSE_downloader():
    quotes = pd.DataFrame({'Close': [134.429993, 132.029999],
                           'Date': [datetime(2021, 4, 13), datetime(2021, 4, 14)]})
    quotes = quotes.set_index('Date')

    downloader = QuoteDownloader()
    quotes_downloaded = downloader.Yahoo_Downloader(0, 'AAPL', 2, '', 1618272000, 1618444800)
    assert_frame_equal(quotes, quotes_downloaded)
Ejemplo n.º 7
0
def test_CBR_downloader():
    codes = pd.DataFrame({'ISO_name': ['AUD', 'ATS'], 'CBR_code': ['R01010', 'R01015']})
    rates = pd.DataFrame({'Rate': [77.5104, 77.2535, 75.6826],
                         'Date': [datetime(2021, 4, 13), datetime(2021, 4, 14), datetime(2021, 4, 15)]})
    rates = rates.set_index('Date')

    downloader = QuoteDownloader()
    downloader.PrepareRussianCBReader()
    assert_frame_equal(codes, downloader.CBR_codes.head(2))
    rates_downloaded = downloader.CBR_DataReader(0, 'USD', 1, '', 1618272000, 1618358400)
    assert_frame_equal(rates, rates_downloaded)
Ejemplo n.º 8
0
 def asset_id(self, asset_info) -> int:
     asset = None
     asset_info = {k: v for k, v in asset_info.items() if v}  # drop keys with empty values
     if 'isin' in asset_info:
         asset = self._find_in_list(self._data[FOF.ASSETS], 'isin', asset_info['isin'])
     if asset is None and 'reg_number' in asset_info:
         asset_data = self._find_in_list(self._data[FOF.ASSETS_DATA], 'reg_number', asset_info['reg_number'])
         if asset_data is not None:
             asset = self._find_in_list(self._data[FOF.ASSETS], 'id', asset_data['asset'])
     if asset is None and 'symbol' in asset_info:
         symbol = self._find_in_list(self._data[FOF.SYMBOLS], 'symbol', asset_info['symbol'])
         if symbol is not None:
             asset = self._find_in_list(self._data[FOF.ASSETS], 'id', symbol['asset'])
             if 'isin' in asset and 'isin' in asset_info and asset['isin'] != asset_info['isin']:
                 asset = None
     if asset is None and 'search_online' in asset_info:
         if asset_info['search_online'] == "MOEX":
             search_data = {}
             self._uppend_keys_from(search_data, asset_info, ['isin', 'reg_number'])
             symbol = QuoteDownloader.MOEX_find_secid(**search_data)
             if not symbol and 'symbol' in asset_info:
                 symbol = asset_info['symbol']
             currency = asset_info['currency'] if 'currency' in asset_info else None  # Keep currency
             asset_info = QuoteDownloader.MOEX_info(symbol=symbol)
             asset_info['type'] = FOF.convert_predefined_asset_type(asset_info['type'])
             if currency is not None:
                 asset_info['currency'] = currency
             asset_info['note'] = "MOEX"
             return self.asset_id(asset_info)  # Call itself once again to cross-check downloaded data
     if asset is None:
         if 'should_exist' in asset_info and asset_info['should_exist']:
             raise Statement_ImportError(self.tr("Can't locate asset in statement data: ") + f"'{asset_info}'")
         asset_id = max([0] + [x['id'] for x in self._data[FOF.ASSETS]]) + 1
         asset = {"id": asset_id}
         self._uppend_keys_from(asset, asset_info, ['type', 'name', 'isin', 'country'])
         self._data[FOF.ASSETS].append(asset)
         if 'symbol' in asset_info:
             symbol_id = max([0] + [x['id'] for x in self._data[FOF.SYMBOLS]]) + 1
             symbol = {"id": symbol_id, "asset": asset_id}
             self._uppend_keys_from(symbol, asset_info, ['symbol', 'currency', 'note'])
             self._data[FOF.SYMBOLS].append(symbol)
         data = {}
         self._uppend_keys_from(data, asset_info, ['reg_number', 'expiry', 'principal'])
         if data:
             data_id = max([0] + [x['id'] for x in self._data[FOF.ASSETS_DATA]]) + 1
             data['id'] = data_id
             data['asset'] = asset_id
             self._data[FOF.ASSETS_DATA].append(data)
     else:
         if 'type' in asset and asset['type'] != FOF.ASSET_MONEY:
             self.update_asset_data(asset['id'], asset_info)
     return asset['id']
Ejemplo n.º 9
0
    def __init__(self, language):
        QMainWindow.__init__(self, None)
        self.running = False
        self.setupUi(self)
        self.restoreGeometry(base64.decodebytes(JalSettings().getValue('WindowGeometry', '').encode('utf-8')))
        self.restoreState(base64.decodebytes(JalSettings().getValue('WindowState', '').encode('utf-8')))

        self.ledger = Ledger()

        # Customize Status bar and logs
        self.ProgressBar = QProgressBar(self)
        self.StatusBar.addPermanentWidget(self.ProgressBar)
        self.ProgressBar.setVisible(False)
        self.ledger.setProgressBar(self, self.ProgressBar)
        self.Logs.setStatusBar(self.StatusBar)
        self.logger = logging.getLogger()
        self.logger.addHandler(self.Logs)
        log_level = os.environ.get('LOGLEVEL', 'INFO').upper()
        self.logger.setLevel(log_level)

        self.currentLanguage = language

        self.downloader = QuoteDownloader()
        self.statements = Statements(self)
        self.reports = Reports(self, self.mdiArea)
        self.backup = JalBackup(self, get_dbfilename(get_app_path()))
        self.estimator = None
        self.price_chart = None

        self.actionImportSlipRU.setEnabled(dependency_present(['pyzbar', 'PIL']))

        self.actionAbout = QAction(text=self.tr("About"), parent=self)
        self.MainMenu.addAction(self.actionAbout)

        self.langGroup = QActionGroup(self.menuLanguage)
        self.createLanguageMenu()

        self.statementGroup = QActionGroup(self.menuStatement)
        self.createStatementsImportMenu()

        self.reportsGroup = QActionGroup(self.menuReports)
        self.createReportsMenu()

        self.setWindowIcon(load_icon("jal.png"))

        self.connect_signals_and_slots()

        self.actionOperations.trigger()
Ejemplo n.º 10
0
 def _add_asset(self, isin, reg_code, symbol=''):
     if self._find_asset_id(symbol, isin, reg_code) != 0:
         raise Statement_ImportError(
             self.tr("Attempt to recreate existing asset: ") + f"{isin}/{reg_code}")
     asset_id = JalDB().get_asset_id('', isin=isin, reg_code=reg_code, dialog_new=False)
     if asset_id is None:
         asset = QuoteDownloader.MOEX_info(symbol=symbol, isin=isin, regnumber=reg_code)
         if asset:
             asset['id'] = asset_id = max([0] + [x['id'] for x in self._data[FOF.ASSETS]]) + 1
             asset['exchange'] = "MOEX"
             asset['type'] = FOF.convert_predefined_asset_type(asset['type'])
         else:
             raise Statement_ImportError(self.tr("Can't import asset: ") + f"{isin}/{reg_code}")
     else:
         asset = {"id": -asset_id, "symbol": JalDB().get_asset_name(asset_id),
                  "type": FOF.convert_predefined_asset_type(JalDB().get_asset_type(asset_id)),
                  'name': '', "isin": isin, "reg_code": reg_code}
         asset_id = -asset_id
     self._data[FOF.ASSETS].append(asset)
     return asset_id
Ejemplo n.º 11
0
 def load_assets(self, assets):
     cnt = 0
     for asset in assets:
         if asset[
                 'type'] == OpenBroker_AssetType.NotSupported:  # Skip not supported type of asset
             continue
         if asset['exchange'] == "MOEX":
             asset_info = QuoteDownloader.MOEX_info(
                 symbol=asset['symbol'],
                 isin=asset['isin'],
                 regnumber=asset['reg_number'])
             if asset_info:
                 asset.update(asset_info)
                 asset['type'] = FOF.convert_predefined_asset_type(
                     asset['type'])
         if asset['exchange'] != '':  # don't store empty exchange
             asset['note'] = asset['exchange']
         asset.pop('exchange')
         self.asset_id(asset)
         cnt += 1
     logging.info(self.tr("Securities loaded: ") + f"{cnt} ({len(assets)})")
Ejemplo n.º 12
0
 def load_assets(self, assets):
     cnt = 0
     base = max([0] + [x['id'] for x in self._data[FOF.ASSETS]]) + 1
     for i, asset in enumerate(assets):
         if asset[
                 'type'] == OpenBroker_AssetType.NotSupported:  # Skip not supported type of asset
             continue
         asset['id'] = base + i
         if asset['exchange'] == "MOEX":
             asset_info = QuoteDownloader.MOEX_info(
                 symbol=asset['symbol'],
                 isin=asset['isin'],
                 regnumber=asset['reg_code'])
             if asset_info:
                 asset.update(asset_info)
                 asset['type'] = FOF.convert_predefined_asset_type(
                     asset['type'])
         if asset['exchange'] == '':  # don't store empty exchange
             asset.pop('exchange')
         cnt += 1
         self._data[FOF.ASSETS].append(asset)
     logging.info(self.tr("Securities loaded: ") + f"{cnt} ({len(assets)})")
Ejemplo n.º 13
0
def test_MOEX_downloader(prepare_db_moex):
    stock_quotes = pd.DataFrame({'Close': [287.95, 287.18],
                                 'Date': [datetime(2021, 4, 13), datetime(2021, 4, 14)]})
    stock_quotes = stock_quotes.set_index('Date')
    bond_quotes = pd.DataFrame({'Close': [1001.00, 999.31],
                                'Date': [datetime(2021, 7, 22), datetime(2021, 7, 23)]})
    bond_quotes = bond_quotes.set_index('Date')
    corp_quotes = pd.DataFrame({'Close': [1002.90, 1003.70],
                                'Date': [datetime(2021, 7, 22), datetime(2021, 7, 23)]})
    corp_quotes = corp_quotes.set_index('Date')
    etf_quotes = pd.DataFrame({'Close': [1736.8, 1735.0],
                               'Date': [datetime(2021, 12, 13), datetime(2021, 12, 14)]})
    etf_quotes = etf_quotes.set_index('Date')

    downloader = QuoteDownloader()
    quotes_downloaded = downloader.MOEX_DataReader(4, 'SBER', 1, 'RU0009029540', 1618272000, 1618358400)
    assert_frame_equal(stock_quotes, quotes_downloaded)
    assert readSQL("SELECT * FROM assets_ext WHERE id=4") == [4, PredefinedAsset.Stock, 'SBER', '', 'RU0009029540', 1, 0, -1]
    assert readSQL("SELECT value FROM asset_data WHERE asset_id=4 AND datatype=1") == '10301481B'

    quotes_downloaded = downloader.MOEX_DataReader(6, 'SU26238RMFS4', 1, 'RU000A1038V6', 1626912000, 1626998400)
    assert_frame_equal(bond_quotes, quotes_downloaded)
    assert readSQL("SELECT * FROM assets_ext WHERE id=6") == [6, PredefinedAsset.Bond, 'SU26238RMFS4', '', 'RU000A1038V6', 1, 0, -1]
    assert readSQL("SELECT value FROM asset_data WHERE asset_id=6 AND datatype=1") == '26238RMFS'
    assert readSQL("SELECT value FROM asset_data WHERE asset_id=6 AND datatype=2") == '2252188800'
    assert readSQL("SELECT value FROM asset_data WHERE asset_id=6 AND datatype=3") == '1000.0'

    quotes_downloaded = downloader.MOEX_DataReader(7, 'МКБ 1P2', 1, 'RU000A1014H6', 1626912000, 1626998400)
    assert_frame_equal(corp_quotes, quotes_downloaded)
    assert readSQL("SELECT * FROM assets_ext WHERE id=7") == [7, PredefinedAsset.Bond, 'МКБ 1P2', '', 'RU000A1014H6', 1, 0, -1]
    assert readSQL("SELECT value FROM asset_data WHERE asset_id=7 AND datatype=1") == '4B020901978B001P'
    assert readSQL("SELECT value FROM asset_data WHERE asset_id=7 AND datatype=2") == '1638230400'
    assert readSQL("SELECT value FROM asset_data WHERE asset_id=7 AND datatype=3") == '1000.0'

    quotes_downloaded = downloader.MOEX_DataReader(8, 'ЗПИФ ПНК', 1, 'RU000A1013V9', 1639353600, 1639440000, update_symbol=False)
    assert_frame_equal(etf_quotes, quotes_downloaded)
Ejemplo n.º 14
0
    def __init__(self, language):
        QMainWindow.__init__(self, None)
        self.setupUi(self)

        self.currentLanguage = language
        self.current_index = None  # this is used in onOperationContextMenu() to track item for menu

        self.ledger = Ledger()
        self.downloader = QuoteDownloader()
        self.taxes = TaxesRus()
        self.statements = StatementLoader()
        self.backup = JalBackup(self, get_dbfilename(get_app_path()))
        self.estimator = None
        self.price_chart = None

        self.actionImportSlipRU.setEnabled(
            dependency_present(['pyzbar', 'PIL']))

        self.actionAbout = QAction(text=self.tr("About"), parent=self)
        self.MainMenu.addAction(self.actionAbout)

        self.langGroup = QActionGroup(self.menuLanguage)
        self.createLanguageMenu()

        self.statementGroup = QActionGroup(self.menuStatement)
        self.createStatementsImportMenu()

        # Set icons
        self.setWindowIcon(load_icon("jal.png"))
        self.NewOperationBtn.setIcon(load_icon("new.png"))
        self.CopyOperationBtn.setIcon(load_icon("copy.png"))
        self.DeleteOperationBtn.setIcon(load_icon("delete.png"))

        # Operations view context menu
        self.contextMenu = QMenu(self.OperationsTableView)
        self.actionReconcile = QAction(load_icon("reconcile.png"),
                                       self.tr("Reconcile"), self)
        self.actionCopy = QAction(load_icon("copy.png"), self.tr("Copy"), self)
        self.actionDelete = QAction(load_icon("delete.png"), self.tr("Delete"),
                                    self)
        self.contextMenu.addAction(self.actionReconcile)
        self.contextMenu.addSeparator()
        self.contextMenu.addAction(self.actionCopy)
        self.contextMenu.addAction(self.actionDelete)

        # Customize Status bar and logs
        self.ProgressBar = QProgressBar(self)
        self.StatusBar.addWidget(self.ProgressBar)
        self.ProgressBar.setVisible(False)
        self.ledger.setProgressBar(self, self.ProgressBar)
        self.NewLogEventLbl = QLabel(self)
        self.StatusBar.addWidget(self.NewLogEventLbl)
        self.Logs.setNotificationLabel(self.NewLogEventLbl)
        self.Logs.setFormatter(
            logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
        self.logger = logging.getLogger()
        self.logger.addHandler(self.Logs)
        log_level = os.environ.get('LOGLEVEL', 'INFO').upper()
        self.logger.setLevel(log_level)

        # Setup reports tab
        self.reports = Reports(self.ReportTableView, self.ReportTreeView)

        # Customize UI configuration
        self.balances_model = BalancesModel(self.BalancesTableView)
        self.BalancesTableView.setModel(self.balances_model)
        self.balances_model.configureView()

        self.holdings_model = HoldingsModel(self.HoldingsTableView)
        self.HoldingsTableView.setModel(self.holdings_model)
        self.holdings_model.configureView()
        self.HoldingsTableView.setContextMenuPolicy(Qt.CustomContextMenu)

        self.operations_model = OperationsModel(self.OperationsTableView)
        self.OperationsTableView.setModel(self.operations_model)
        self.operations_model.configureView()
        self.OperationsTableView.setContextMenuPolicy(Qt.CustomContextMenu)

        self.connect_signals_and_slots()

        self.NewOperationMenu = QMenu()
        for i in range(self.OperationsTabs.count()):
            if hasattr(self.OperationsTabs.widget(i), "isCustom"):
                self.OperationsTabs.widget(i).dbUpdated.connect(
                    self.ledger.rebuild)
                self.OperationsTabs.widget(i).dbUpdated.connect(
                    self.operations_model.refresh)
                self.NewOperationMenu.addAction(
                    self.OperationsTabs.widget(i).name,
                    partial(self.createOperation, i))
        self.NewOperationBtn.setMenu(self.NewOperationMenu)

        # Setup balance and holdings parameters
        current_time = QDateTime.currentDateTime()
        current_time.setTimeSpec(
            Qt.UTC)  # We use UTC everywhere so need to force TZ info
        self.BalanceDate.setDateTime(current_time)
        self.BalancesCurrencyCombo.setIndex(
            JalSettings().getValue('BaseCurrency'))
        self.HoldingsDate.setDateTime(current_time)
        self.HoldingsCurrencyCombo.setIndex(
            JalSettings().getValue('BaseCurrency'))

        self.OperationsTabs.setCurrentIndex(TransactionType.NA)
        self.OperationsTableView.selectRow(0)
        self.OnOperationsRangeChange(0)
Ejemplo n.º 15
0
def test_MOEX_details():
    assert QuoteDownloader.MOEX_find_secid(regcode='') == ''
    assert QuoteDownloader.MOEX_find_secid(isin='TEST') == ''
    assert QuoteDownloader.MOEX_find_secid(regcode='2770') == 'RU000A1013V9'
    assert QuoteDownloader.MOEX_find_secid(regcode='1-01-00010-A') == 'AFLT'
    assert QuoteDownloader.MOEX_find_secid(isin='IE00B8XB7377') == 'FXGD'
    assert QuoteDownloader.MOEX_find_secid(isin='JE00B6T5S470') == 'POLY'
    assert QuoteDownloader.MOEX_find_secid(
        isin='RU000A1038V6') == 'SU26238RMFS4'

    assert QuoteDownloader.MOEX_info() == {}
    assert QuoteDownloader.MOEX_info(special=True) == {}
    assert QuoteDownloader.MOEX_info(symbol='AFLT', special=True) == {
        'symbol': 'AFLT',
        'isin': 'RU0009062285',
        'name': 'Аэрофлот-росс.авиалин(ПАО)ао',
        'principal': 1.0,
        'reg_code': '1-01-00010-A',
        'engine': 'stock',
        'market': 'shares',
        'board': 'TQBR',
        'type': PredefinedAsset.Stock
    }
    assert QuoteDownloader.MOEX_info(isin='RU000A0JWUE9', special=True) == {
        'symbol': 'СберБ БО37',
        'isin': 'RU000A0JWUE9',
        'name': 'Сбербанк ПАО БО-37',
        'principal': 1000.0,
        'reg_code': '4B023701481B',
        'expiry': 1632960000,
        'engine': 'stock',
        'market': 'bonds',
        'board': 'TQCB',
        'type': PredefinedAsset.Bond
    }
    assert QuoteDownloader.MOEX_info(symbol='SiZ1', isin='', special=True) == {
        'symbol': 'SiZ1',
        'name': 'Фьючерсный контракт Si-12.21',
        'expiry': 1639612800,
        'engine': 'futures',
        'market': 'forts',
        'board': 'RFUD',
        'type': PredefinedAsset.Derivative
    }
    assert QuoteDownloader.MOEX_info(symbol='', regnumber='2770') == {
        'symbol': 'ЗПИФ ПНК',
        'isin': 'RU000A1013V9',
        'name': 'ЗПИФ Фонд ПНК-Рентал',
        'reg_code': '2770',
        'type': PredefinedAsset.ETF
    }
    assert QuoteDownloader.MOEX_info(isin='IE00B8XB7377',
                                     regnumber='IE00B8XB7377',
                                     symbol='FXGD ETF') == {
                                         'symbol': 'FXGD',
                                         'isin': 'IE00B8XB7377',
                                         'name': 'FinEx Gold ETF USD',
                                         'type': PredefinedAsset.ETF
                                     }
    assert QuoteDownloader.MOEX_info(isin='JE00B6T5S470',
                                     regnumber='',
                                     symbol='') == {
                                         'symbol': 'POLY',
                                         'isin': 'JE00B6T5S470',
                                         'name': 'Polymetal International plc',
                                         'type': PredefinedAsset.Stock
                                     }
    assert QuoteDownloader.MOEX_info(isin='RU000A1038V6') == {
        'symbol': 'SU26238RMFS4',
        'isin': 'RU000A1038V6',
        'name': 'ОФЗ-ПД 26238 15/05/2041',
        'principal': 1000.0,
        'reg_code': '26238RMFS',
        'expiry': 2252188800,
        'type': PredefinedAsset.Bond
    }
Ejemplo n.º 16
0
 def updateExchangeData(self):
     QuoteDownloader().updataData()