# Copyright(C) 2010-2016 Romain Bignon
#
# This file is part of woob.
#
# woob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# woob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with woob. If not, see <http://www.gnu.org/licenses/>.
# flake8: compatible
from woob.capabilities.base import (
BaseObject, Field, StringField, DecimalField, IntField,
EnumField, Enum,
)
from woob.capabilities.date import DateField
from .base import Account, CapBank
__all__ = [
'PerVersion', 'PerProviderType', 'Per', 'Investment', 'Pocket',
'MarketOrderType', 'MarketOrderDirection', 'MarketOrderPayment',
'MarketOrder', 'CapBankWealth',
]
[docs]class PerVersion(Enum):
PERIN = 'perin' # "PER individuel", subscribed by the account holder
PERCOL = 'percol' # "PER collectif", subscribed by the employer for all employees
PERCAT = 'percat' # "PER catégoriel", subscribed by the employer for a category of employees (for example managers)
[docs]class PerProviderType(Enum):
BANK = 'bank'
INSURER = 'insurer'
[docs]class Per(Account):
"""
Account type dedicated to PER retirement savings plans.
"""
version = EnumField('Version of PER', PerVersion)
provider_type = EnumField('Type of account provider', PerProviderType)
[docs]class Investment(BaseObject):
"""
Investment in a financial market.
"""
CODE_TYPE_ISIN = 'ISIN'
CODE_TYPE_AMF = 'AMF'
label = StringField('Label of stocks')
code = StringField('Identifier of the stock')
code_type = StringField('Type of stock code (ISIN or AMF)')
stock_symbol = StringField('Alternative identifier of the stock (different from ISIN)')
stock_market = StringField('Stock market related to the stock')
description = StringField('Short description of the stock')
quantity = DecimalField('Quantity of stocks')
unitprice = DecimalField('Buy price of one stock')
unitvalue = DecimalField('Current value of one stock')
valuation = DecimalField('Total current valuation of the Investment')
vdate = DateField('Value date of the valuation amount')
diff = DecimalField('Difference between the buy cost and the current valuation')
diff_ratio = DecimalField('Difference in ratio (1 meaning 100%) between the buy cost and the current valuation')
portfolio_share = DecimalField('Ratio (1 meaning 100%) of the current amount relative to the total')
performance_history = Field('History of the performances of the stock (key=years, value=diff_ratio)', dict)
srri = IntField('Synthetic Risk and Reward Indicator of the stock (from 1 to 7)')
asset_category = StringField('Category of the stock')
recommended_period = StringField('Recommended investment period of the stock')
# International
original_currency = StringField('Currency of the original amount')
original_valuation = DecimalField('Original valuation (in another currency)')
original_unitvalue = DecimalField('Original unitvalue (in another currency)')
original_unitprice = DecimalField('Original unitprice (in another currency)')
original_diff = DecimalField('Original diff (in another currency)')
def __repr__(self):
return '<Investment label=%r code=%r valuation=%r>' % (self.label, self.code, self.valuation)
# compatibility alias
@property
def diff_percent(self):
return self.diff_ratio
@diff_percent.setter
def diff_percent(self, value):
self.diff_ratio = value
class PocketCondition(Enum):
UNKNOWN = 0
DATE = 1
# If 'availability_date' has passed
AVAILABLE = 2
RETIREMENT = 3
# the following conditions are used if :
# 'acquisition_date' and 'transferability_date' are displayed on the website.
# If 'acquisition_date' has not passed.
DATE_WHEN_ACQUIRED = 4
# If 'acquisition_date' has passed and 'transferability_date' has not.
DATE_WHEN_TRANSFERABLE = 5
# the following conditions are irrelevant, don't use them
WEDDING = 6
DEATH = 7
INDEBTEDNESS = 8
DIVORCE = 9
DISABILITY = 10
BUSINESS_CREATION = 11
BREACH_EMPLOYMENT_CONTRACT = 12
UNLOCKING_EXCEPTIONAL = 13
THIRD_CHILD = 14
EXPIRATION_UNEMPLOYMENT = 15
PURCHASE_APARTMENT = 16
[docs]class Pocket(BaseObject):
"""
Pocket
"""
CONDITION_UNKNOWN = PocketCondition.UNKNOWN
CONDITION_DATE = PocketCondition.DATE
CONDITION_DATE_WHEN_ACQUIRED = PocketCondition.DATE_WHEN_ACQUIRED
CONDITION_DATE_WHEN_TRANSFERABLE = PocketCondition.DATE_WHEN_TRANSFERABLE
CONDITION_AVAILABLE = PocketCondition.AVAILABLE
CONDITION_RETIREMENT = PocketCondition.RETIREMENT
# the following conditions are irrelevant, don't use them
CONDITION_WEDDING = PocketCondition.WEDDING
CONDITION_DEATH = PocketCondition.DEATH
CONDITION_INDEBTEDNESS = PocketCondition.INDEBTEDNESS
CONDITION_DIVORCE = PocketCondition.DIVORCE
CONDITION_DISABILITY = PocketCondition.DISABILITY
CONDITION_BUSINESS_CREATION = PocketCondition.BUSINESS_CREATION
CONDITION_BREACH_EMPLOYMENT_CONTRACT = PocketCondition.BREACH_EMPLOYMENT_CONTRACT
CONDITION_UNLOCKING_EXCEPTIONAL = PocketCondition.UNLOCKING_EXCEPTIONAL
CONDITION_THIRD_CHILD = PocketCondition.THIRD_CHILD
CONDITION_EXPIRATION_UNEMPLOYMENT = PocketCondition.EXPIRATION_UNEMPLOYMENT
CONDITION_PURCHASE_APARTMENT = PocketCondition.PURCHASE_APARTMENT
label = StringField('Label of pocket')
amount = DecimalField('Amount of the pocket')
quantity = DecimalField('Quantity of stocks')
availability_date = DateField('Availability date of the pocket')
condition = EnumField('Withdrawal condition of the pocket', PocketCondition, default=CONDITION_UNKNOWN)
investment = Field('Reference to the investment of the pocket', Investment)
[docs]class MarketOrderType(Enum):
UNKNOWN = 0
MARKET = 1
"""Order executed at the current market price"""
LIMIT = 2
"""Order executed with a maximum or minimum price limit"""
TRIGGER = 3
"""Order executed when the price reaches a specific value"""
[docs]class MarketOrderDirection(Enum):
UNKNOWN = 0
BUY = 1
SALE = 2
[docs]class MarketOrderPayment(Enum):
UNKNOWN = 0
CASH = 1
DEFERRED = 2
[docs]class MarketOrder(BaseObject):
"""
Market order
"""
# Important: a Market Order always corresponds to one (and only one) investment
label = StringField('Label of the market order')
# MarketOrder values
unitprice = DecimalField('Value of the stock at the moment of the market order')
unitvalue = DecimalField('Current value of the stock associated with the market order')
ordervalue = DecimalField('Limit value or trigger value, only relevant if the order type is LIMIT or TRIGGER')
currency = StringField('Currency of the market order - not always the same as account currency')
quantity = DecimalField('Quantity of stocks in the market order')
amount = DecimalField('Total amount that has been bought or sold')
# MarketOrder additional information
order_type = EnumField('Type of market order', MarketOrderType, default=MarketOrderType.UNKNOWN)
direction = EnumField(
'Direction of the market order (buy or sale)', MarketOrderDirection, default=MarketOrderDirection.UNKNOWN
)
payment_method = EnumField(
'Payment method of the market order', MarketOrderPayment, default=MarketOrderPayment.UNKNOWN
)
date = DateField('Creation date of the market order')
validity_date = DateField('Validity date of the market order')
execution_date = DateField('Execution date of the market order (only for market orders that are completed)')
state = StringField('Current state of the market order (e.g. executed)')
code = StringField('Identifier of the stock related to the order')
stock_symbol = StringField('Alternative identifier of the stock related to the order (different from ISIN)')
stock_market = StringField('Stock market on which the order was executed')
[docs]class CapBankWealth(CapBank):
"""
Capability of bank websites to see investments and pockets.
"""
[docs] def iter_investment(self, account):
"""
Iter investment of a market account
:param account: account to get investments
:type account: :class:`Account`
:rtype: iter[:class:`Investment`]
:raises: :class:`AccountNotFound`
"""
raise NotImplementedError()
[docs] def iter_pocket(self, account):
"""
Iter pocket
:param account: account to get pockets
:type account: :class:`Account`
:rtype: iter[:class:`Pocket`]
:raises: :class:`AccountNotFound`
"""
raise NotImplementedError()
[docs] def iter_market_orders(self, account):
"""
Iter market orders
:param account: account to get market orders
:type account: :class:`Account`
:rtype: iter[:class:`MarketOrder`]
:raises: :class:`AccountNotFound`
"""
raise NotImplementedError()