Commit e5d93113 authored by Quentin Defenouillere's avatar Quentin Defenouillere Committed by Vincent A

[boursorama] Implemented iter_market_orders()

For wealth accounts, we can also fetch the market orders (passages
d'ordres) on the corresponding tab.
parent 278b246a
......@@ -109,7 +109,7 @@ class BoursoramaBrowser(RetryLoginBrowser, TwoFactorBrowser):
'/compte/pea-pme/.*/mouvements', SavingMarketPage)
market = URL('/compte/(?!assurance|cav|epargne).*/(positions|mouvements)',
market = URL('/compte/(?!assurance|cav|epargne).*/(positions|mouvements|ordres)',
'/compte/ord/.*/positions', MarketPage)
loans = URL(r'/credit/paiement-3x/.*/informations',
......@@ -400,18 +400,30 @@ class BoursoramaBrowser(RetryLoginBrowser, TwoFactorBrowser):
for t in sorted(transactions, key=lambda tr:, reverse=True):
yield t
def get_investment(self, account):
if '/compte/derive' in account.url:
return iter([])
if not account.type in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_MARKET, Account.TYPE_PEA):
raise NotImplementedError()
def iter_investment(self, account):
if '/compte/derive' in account.url or account.type not in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_MARKET, Account.TYPE_PEA):
return []
# We might deconnect at this point.
if self.login.is_here():
return self.get_investment(account)
def iter_market_orders(self, account):
# Only Market & PEA accounts have the Market Orders tab
if '/compte/derive' in account.url or account.type not in (Account.TYPE_MARKET, Account.TYPE_PEA):
return []
# Go to Market Orders tab ('Mes ordres')
market_order_link =
if not market_order_link:
self.logger.warning('Could not find market orders link for account "%s".', account.label)
return []
def get_profile(self):
return self.profile.stay_or_go().get_profile()
......@@ -74,7 +74,10 @@ class BoursoramaModule(Module, CapBankWealth, CapBankTransferAddRecipient, CapPr
yield tr
def iter_investment(self, account):
return self.browser.get_investment(account)
return self.browser.iter_investment(account)
def iter_market_orders(self, account):
return self.browser.iter_market_orders(account)
def get_profile(self):
return self.browser.get_profile()
......@@ -33,19 +33,21 @@ from weboob.browser.filters.standard import (
CleanText, CleanDecimal, Field, Format,
Regexp, Date, AsyncLoad, Async, Eval, Env,
Currency as CleanCurrency, Map, Coalesce,
MapIn, Lower,
MapIn, Lower, Base,
from weboob.browser.filters.json import Dict
from weboob.browser.filters.html import Attr, Link, TableCell
from import (
Account, Investment, Recipient, Transfer, AccountNotFound,
Account, Investment, MarketOrder, Recipient, Transfer, AccountNotFound,
AddRecipientBankError, TransferInvalidAmount, Loan, AccountOwnership,
MarketOrderType, MarketOrderDirection,
from import create_french_liquidity
from weboob.capabilities.base import NotAvailable, Currency, find_object, empty
from weboob.capabilities.profile import Person
from import FrenchTransaction
from import is_iban_valid
from import IsinCode
from import Value
from import parse_french_date
from import urljoin, urlencode, urlparse, range
......@@ -691,6 +693,16 @@ def my_pagination(func):
return inner
'LIM': MarketOrderType.LIMIT,
'Achat': MarketOrderDirection.BUY,
'Vente': MarketOrderDirection.SALE,
class MarketPage(LoggedPage, HTMLPage):
def get_balance(self, account_type):
txt = u"Solde au" if account_type is Account.TYPE_LIFE_INSURANCE else u"Total Portefeuille"
......@@ -699,6 +711,9 @@ class MarketPage(LoggedPage, HTMLPage):
span_balance = CleanDecimal('//li/span[contains(text(), "%s")]/following-sibling::span' % txt, replace_dots=True, default=None)(self.doc)
return h_balance or span_balance or None
def get_market_order_link(self):
return Link('//a[contains(@data-url, "orders")]', default=None)(self.doc)
class iter_history(TableElement):
......@@ -778,6 +793,45 @@ class MarketPage(LoggedPage, HTMLPage):
yield t
class iter_market_orders(TableElement):
item_xpath = '//table/tbody/tr[td]'
head_xpath = '//table/thead/tr/th'
col_date = 'Date'
col_label = 'Libellé'
col_direction = 'Sens'
col_state = 'Etat'
col_quantity = 'Qté'
col_order_type = 'Type'
col_unitvalue = 'Cours'
col_validity_date = 'Validité'
col_stock_market = 'Marché'
class item(ItemElement):
klass = MarketOrder
obj_id = Base(TableCell('date'), CleanText('.//a'))
obj_label = CleanText(TableCell('label'), children=False)
obj_direction = Map(CleanText(TableCell('direction')), MARKET_DIRECTIONS, MarketOrderDirection.UNKNOWN)
obj_code = IsinCode(Base(TableCell('label'), CleanText('.//a')))
obj_stock_market = CleanText(TableCell('stock_market'))
obj_currency = CleanCurrency(TableCell('unitvalue'))
# Unitprice may be absent if the order is still ongoing
obj_unitprice = CleanDecimal.US(TableCell('state'), default=NotAvailable)
obj_unitvalue = CleanDecimal.French(TableCell('unitvalue'))
obj_ordervalue = CleanDecimal.French(TableCell('order_type'))
obj_quantity = CleanDecimal.French(TableCell('quantity'))
obj_date = Date(Base(TableCell('date'), CleanText('.//span')), dayfirst=True)
obj_validity_date = Date(CleanText(TableCell('validity_date')), dayfirst=True)
# Text format looks like 'LIM 49,000', we only use the 'LIM' for typing
obj_order_type = Map(Regexp(CleanText(TableCell('order_type')), r'^([^ ]+) '), MARKET_ORDER_TYPES, MarketOrderType.UNKNOWN)
# Text format looks like 'Exécuté 12.345 $' or 'En cours', we only fetch the first words
obj_state = CleanText(Regexp(CleanText(TableCell('state')), r'^(\D+)'))
class SavingMarketPage(MarketPage):
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment