wbportfolio 1.52.0__py2.py3-none-any.whl → 1.52.2rc0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of wbportfolio might be problematic. Click here for more details.

Files changed (86) hide show
  1. wbportfolio/admin/__init__.py +1 -1
  2. wbportfolio/admin/transactions/__init__.py +0 -1
  3. wbportfolio/admin/transactions/dividends.py +40 -4
  4. wbportfolio/admin/transactions/fees.py +24 -14
  5. wbportfolio/admin/transactions/trades.py +34 -12
  6. wbportfolio/defaults/fees/default.py +7 -15
  7. wbportfolio/factories/__init__.py +0 -1
  8. wbportfolio/factories/dividends.py +8 -3
  9. wbportfolio/factories/fees.py +8 -4
  10. wbportfolio/factories/trades.py +10 -3
  11. wbportfolio/filters/transactions/__init__.py +1 -2
  12. wbportfolio/filters/transactions/fees.py +5 -10
  13. wbportfolio/filters/transactions/trades.py +17 -8
  14. wbportfolio/filters/transactions/utils.py +42 -0
  15. wbportfolio/import_export/handlers/dividend.py +7 -7
  16. wbportfolio/import_export/handlers/fees.py +11 -21
  17. wbportfolio/import_export/handlers/trade.py +5 -7
  18. wbportfolio/import_export/parsers/jpmorgan/fees.py +2 -2
  19. wbportfolio/import_export/parsers/leonteq/customer_trade.py +5 -5
  20. wbportfolio/import_export/parsers/leonteq/fees.py +11 -7
  21. wbportfolio/import_export/parsers/leonteq/trade.py +0 -5
  22. wbportfolio/import_export/parsers/natixis/d1_fees.py +2 -2
  23. wbportfolio/import_export/parsers/natixis/dividend.py +4 -9
  24. wbportfolio/import_export/parsers/natixis/fees.py +7 -9
  25. wbportfolio/import_export/parsers/sg_lux/customer_trade_pending_slk.py +1 -1
  26. wbportfolio/import_export/parsers/sg_lux/fees.py +2 -2
  27. wbportfolio/import_export/parsers/sg_lux/perf_fees.py +2 -2
  28. wbportfolio/import_export/parsers/sg_lux/utils.py +2 -2
  29. wbportfolio/import_export/parsers/ubs/api/fees.py +2 -2
  30. wbportfolio/import_export/parsers/vontobel/customer_trade.py +2 -3
  31. wbportfolio/import_export/parsers/vontobel/historical_customer_trade.py +0 -1
  32. wbportfolio/import_export/parsers/vontobel/management_fees.py +7 -7
  33. wbportfolio/import_export/parsers/vontobel/performance_fees.py +3 -3
  34. wbportfolio/jinja2/wbportfolio/sql/aum_nnm.sql +2 -2
  35. wbportfolio/migrations/0059_fees_unique_fees.py +1 -1
  36. wbportfolio/migrations/0077_remove_transaction_currency_and_more.py +622 -0
  37. wbportfolio/models/mixins/liquidity_stress_test.py +3 -3
  38. wbportfolio/models/transactions/__init__.py +0 -2
  39. wbportfolio/models/transactions/claim.py +1 -1
  40. wbportfolio/models/transactions/dividends.py +41 -5
  41. wbportfolio/models/transactions/fees.py +55 -22
  42. wbportfolio/models/transactions/trade_proposals.py +26 -6
  43. wbportfolio/models/transactions/trades.py +111 -50
  44. wbportfolio/models/transactions/transactions.py +60 -156
  45. wbportfolio/serializers/signals.py +15 -10
  46. wbportfolio/serializers/transactions/__init__.py +0 -5
  47. wbportfolio/serializers/transactions/dividends.py +37 -9
  48. wbportfolio/serializers/transactions/fees.py +39 -10
  49. wbportfolio/serializers/transactions/trades.py +56 -16
  50. wbportfolio/tasks.py +2 -2
  51. wbportfolio/tests/conftest.py +2 -8
  52. wbportfolio/tests/models/test_imports.py +2 -7
  53. wbportfolio/tests/models/transactions/test_fees.py +7 -13
  54. wbportfolio/tests/models/transactions/test_trade_proposals.py +4 -2
  55. wbportfolio/urls.py +3 -6
  56. wbportfolio/viewsets/configs/buttons/__init__.py +1 -0
  57. wbportfolio/viewsets/configs/buttons/mixins.py +2 -2
  58. wbportfolio/viewsets/configs/buttons/trades.py +8 -0
  59. wbportfolio/viewsets/configs/display/__init__.py +2 -3
  60. wbportfolio/viewsets/configs/display/fees.py +3 -3
  61. wbportfolio/viewsets/configs/endpoints/__init__.py +3 -4
  62. wbportfolio/viewsets/configs/endpoints/fees.py +2 -2
  63. wbportfolio/viewsets/configs/menu/__init__.py +0 -1
  64. wbportfolio/viewsets/configs/titles/__init__.py +2 -3
  65. wbportfolio/viewsets/configs/titles/fees.py +4 -8
  66. wbportfolio/viewsets/mixins.py +5 -1
  67. wbportfolio/viewsets/products.py +6 -6
  68. wbportfolio/viewsets/transactions/__init__.py +2 -7
  69. wbportfolio/viewsets/transactions/fees.py +22 -22
  70. wbportfolio/viewsets/transactions/trade_proposals.py +1 -0
  71. wbportfolio/viewsets/transactions/trades.py +2 -0
  72. {wbportfolio-1.52.0.dist-info → wbportfolio-1.52.2rc0.dist-info}/METADATA +1 -1
  73. {wbportfolio-1.52.0.dist-info → wbportfolio-1.52.2rc0.dist-info}/RECORD +75 -84
  74. wbportfolio/admin/transactions/transactions.py +0 -38
  75. wbportfolio/factories/transactions.py +0 -22
  76. wbportfolio/filters/transactions/transactions.py +0 -99
  77. wbportfolio/models/transactions/expiry.py +0 -7
  78. wbportfolio/serializers/transactions/expiry.py +0 -18
  79. wbportfolio/serializers/transactions/transactions.py +0 -85
  80. wbportfolio/viewsets/configs/display/transactions.py +0 -55
  81. wbportfolio/viewsets/configs/endpoints/transactions.py +0 -14
  82. wbportfolio/viewsets/configs/menu/transactions.py +0 -9
  83. wbportfolio/viewsets/configs/titles/transactions.py +0 -9
  84. wbportfolio/viewsets/transactions/transactions.py +0 -122
  85. {wbportfolio-1.52.0.dist-info → wbportfolio-1.52.2rc0.dist-info}/WHEEL +0 -0
  86. {wbportfolio-1.52.0.dist-info → wbportfolio-1.52.2rc0.dist-info}/licenses/LICENSE +0 -0
@@ -8,5 +8,5 @@ from .product_groups import ProductGroupAdmin
8
8
  from .portfolio import PortfolioModelAdmin
9
9
  from .registers import RegisterModelAdmin
10
10
  from .roles import PortfolioRoleAdmin
11
- from .transactions import DividendAdmin, FeesAdmin, TradeAdmin, TransactionModelAdmin
11
+ from .transactions import DividendAdmin, FeesAdmin, TradeAdmin
12
12
  from .reconciliations import AccountReconciliationAdmin
@@ -1,4 +1,3 @@
1
- from .transactions import TransactionModelAdmin # isort:skip
2
1
  from .claim import ClaimModelAdmin
3
2
  from .dividends import DividendAdmin
4
3
  from .fees import FeesAdmin
@@ -1,15 +1,51 @@
1
1
  from django.contrib import admin
2
2
 
3
- from wbportfolio.admin.transactions import TransactionModelAdmin
4
3
  from wbportfolio.models import DividendTransaction
5
4
 
6
5
 
7
6
  @admin.register(DividendTransaction)
8
- class DividendAdmin(TransactionModelAdmin):
7
+ class DividendAdmin(admin.ModelAdmin):
8
+ search_fields = ["portfolio__name", "underlying_instrument__computed_str"]
9
+ list_filter = ("distribution_method", "portfolio")
10
+ list_display = (
11
+ "distribution_method",
12
+ "portfolio",
13
+ "underlying_instrument",
14
+ "value_date",
15
+ "currency",
16
+ "currency_fx_rate",
17
+ "total_value",
18
+ "total_value_fx_portfolio",
19
+ )
9
20
  fieldsets = (
10
- ("Transactions Information", TransactionModelAdmin.fieldsets[0][1]),
11
21
  (
12
22
  "Dividend Information",
13
- {"fields": (("retrocession",),)},
23
+ {
24
+ "fields": (
25
+ (
26
+ "portfolio",
27
+ "underlying_instrument",
28
+ "import_source",
29
+ ),
30
+ ("ex_date", "record_date", "value_date"),
31
+ ("currency", "currency_fx_rate"),
32
+ ("price", "shares", "retrocession"),
33
+ ("total_value", "total_value_gross"),
34
+ ("total_value_fx_portfolio", "total_value_gross_fx_portfolio"),
35
+ ("created", "updated"),
36
+ ("comment",),
37
+ )
38
+ },
14
39
  ),
15
40
  )
41
+ readonly_fields = [
42
+ "total_value",
43
+ "total_value_gross",
44
+ "total_value_fx_portfolio",
45
+ "total_value_gross_fx_portfolio",
46
+ "created",
47
+ "updated",
48
+ ]
49
+ autocomplete_fields = ["portfolio", "underlying_instrument", "currency"]
50
+ ordering = ("-value_date",)
51
+ raw_id_fields = ["import_source"]
@@ -1,27 +1,36 @@
1
1
  from django.contrib import admin
2
- from django.db.models import Prefetch
3
2
 
4
- from wbportfolio.admin.transactions import TransactionModelAdmin
5
- from wbportfolio.models import Fees, Portfolio
3
+ from wbportfolio.models import Fees
6
4
  from wbportfolio.models.transactions.fees import FeeCalculation
7
5
 
8
6
 
9
7
  @admin.register(Fees)
10
- class FeesAdmin(TransactionModelAdmin):
11
- list_filter = [*TransactionModelAdmin.list_filter, "linked_product"]
12
- list_display = ["transaction_subtype", *TransactionModelAdmin.list_display[1:], "calculated"]
8
+ class FeesAdmin(admin.ModelAdmin):
9
+ list_filter = ["product"]
10
+ list_display = [
11
+ "transaction_subtype",
12
+ "fee_date",
13
+ "product",
14
+ "currency",
15
+ "total_value",
16
+ ]
13
17
  fieldsets = (
14
- ("Transaction Information", TransactionModelAdmin.fieldsets[0][1]),
15
- ("Fees Information", {"fields": (("transaction_subtype", "calculated", "linked_product"),)}),
18
+ (
19
+ "Fees Information",
20
+ {
21
+ "fields": (
22
+ ("transaction_subtype", "calculated", "fee_date", "product"),
23
+ ("currency", "currency_fx_rate"),
24
+ ("total_value", "total_value_fx_portfolio"),
25
+ ("created", "updated"),
26
+ )
27
+ },
28
+ ),
16
29
  )
17
- autocomplete_fields = [*TransactionModelAdmin.autocomplete_fields, "linked_product"]
30
+ autocomplete_fields = ["currency", "product"]
18
31
 
19
32
  def get_queryset(self, request):
20
- return (
21
- super()
22
- .get_queryset(request)
23
- .prefetch_related(Prefetch("portfolio", queryset=Portfolio.objects.all().only("id", "name")))
24
- )
33
+ return super().get_queryset(request).select_related("product", "currency")
25
34
 
26
35
  def calculate(self, request, queryset):
27
36
  for fees in queryset:
@@ -29,6 +38,7 @@ class FeesAdmin(TransactionModelAdmin):
29
38
 
30
39
  calculate.short_description = "Recalculate selected Fees"
31
40
  actions = [calculate]
41
+ readonly_fields = ["total_value_fx_portfolio", "created", "updated"]
32
42
 
33
43
 
34
44
  @admin.register(FeeCalculation)
@@ -1,45 +1,67 @@
1
1
  from django.contrib import admin
2
2
 
3
- from wbportfolio.admin.transactions import TransactionModelAdmin
4
3
  from wbportfolio.models import Trade, TradeProposal
5
4
 
6
5
 
7
6
  @admin.register(Trade)
8
- class TradeAdmin(TransactionModelAdmin):
9
- search_fields = [*TransactionModelAdmin.search_fields, "bank"]
10
- list_filter = (*TransactionModelAdmin.list_filter, "pending")
7
+ class TradeAdmin(admin.ModelAdmin):
8
+ search_fields = ["portfolio__name", "underlying_instrument__computed_str", "bank"]
9
+ list_filter = ("portfolio", "pending")
11
10
  list_display = (
12
- "transaction_subtype",
13
11
  "status",
12
+ "transaction_subtype",
13
+ "transaction_date",
14
+ "underlying_instrument",
15
+ "portfolio",
14
16
  "shares",
15
17
  "price",
18
+ "total_value",
16
19
  "pending",
17
20
  "marked_for_deletion",
18
21
  "exclude_from_history",
19
- *TransactionModelAdmin.list_display[1:],
22
+ "import_source",
20
23
  )
24
+
21
25
  readonly_fields = [
22
26
  "_effective_weight",
23
27
  "_target_weight",
24
28
  "_effective_shares",
25
29
  "_target_shares",
30
+ "total_value",
31
+ "total_value_gross",
32
+ "total_value_fx_portfolio",
33
+ "total_value_gross_fx_portfolio",
34
+ "created",
35
+ "updated",
26
36
  ]
27
37
  fieldsets = (
28
- ("Transaction Information", TransactionModelAdmin.fieldsets[0][1]),
29
38
  (
30
- "Trade Information",
39
+ "Transaction Information",
31
40
  {
32
41
  "fields": (
33
- ("transaction_subtype", "status", "pending", "marked_for_deletion", "exclude_from_history"),
34
- ("price", "price_gross"),
35
- ("register", "custodian", "bank", "external_identifier2"),
42
+ ("transaction_subtype", "status"),
43
+ ("pending", "marked_for_deletion", "exclude_from_history"),
44
+ (
45
+ "portfolio",
46
+ "underlying_instrument",
47
+ "import_source",
48
+ ),
49
+ ("transaction_date", "book_date", "value_date"),
50
+ ("price", "currency", "currency_fx_rate"),
51
+ ("total_value", "total_value_gross"),
52
+ ("total_value_fx_portfolio", "total_value_gross_fx_portfolio"),
36
53
  ("_effective_weight", "_target_weight", "weighting"),
37
54
  ("_effective_shares", "_target_shares", "shares"),
55
+ ("register", "custodian", "bank", "external_id", "external_id_alternative"),
56
+ ("created", "updated"),
57
+ ("comment",),
38
58
  )
39
59
  },
40
60
  ),
41
61
  )
42
- autocomplete_fields = [*TransactionModelAdmin.autocomplete_fields, "register", "custodian"]
62
+ autocomplete_fields = ["portfolio", "underlying_instrument", "currency", "register", "custodian"]
63
+ ordering = ("-transaction_date",)
64
+ raw_id_fields = ["import_source", "underlying_instrument", "portfolio", "register", "custodian"]
43
65
 
44
66
 
45
67
  @admin.register(TradeProposal)
@@ -1,4 +1,4 @@
1
- from wbfdm.models import Cash, InstrumentPrice
1
+ from wbfdm.models import InstrumentPrice
2
2
 
3
3
  from wbportfolio.models import FeeProductPercentage, Fees, Product
4
4
 
@@ -8,7 +8,6 @@ def fees_calculation(price_id):
8
8
  if price.calculated:
9
9
  raise ValueError("Cannot compute fees on calculated price")
10
10
  currency = price.instrument.currency
11
- underlying_instrument = Cash.objects.filter(currency=currency).first()
12
11
  product = Product.objects.get(id=price.instrument.id)
13
12
  portfolio = product.portfolio
14
13
  previous_price = price.previous_price
@@ -54,40 +53,33 @@ def fees_calculation(price_id):
54
53
  performance_fees_gross = product_gross_performance_fees * multiplicator
55
54
  base_fields = [
56
55
  "total_value",
57
- "total_value_fx_portfolio",
58
56
  "total_value_gross",
59
- "total_value_gross_fx_portfolio",
60
57
  ]
61
58
  yield {
62
59
  "portfolio": portfolio,
63
- "linked_product": product,
64
- "transaction_date": price.date,
60
+ "product": product,
61
+ "fee_date": price.date,
65
62
  "transaction_subtype": Fees.Type.MANAGEMENT,
66
- "underlying_instrument": underlying_instrument,
67
63
  "currency": currency,
68
64
  "calculated": True,
69
65
  **{f: management_fees for f in base_fields},
70
66
  }
71
67
  yield {
72
68
  "portfolio": portfolio,
73
- "linked_product": product,
74
- "transaction_date": price.date,
69
+ "product": product,
70
+ "fee_date": price.date,
75
71
  "transaction_subtype": Fees.Type.ISSUER,
76
- "underlying_instrument": underlying_instrument,
77
72
  "currency": currency,
78
73
  "calculated": True,
79
74
  **{f: bank_fees for f in base_fields},
80
75
  }
81
76
  yield {
82
77
  "portfolio": portfolio,
83
- "linked_product": product,
84
- "transaction_date": price.date,
78
+ "product": product,
79
+ "fee_date": price.date,
85
80
  "transaction_subtype": Fees.Type.PERFORMANCE,
86
- "underlying_instrument": underlying_instrument,
87
81
  "currency": currency,
88
82
  "calculated": True,
89
83
  "total_value": performance_fees_net,
90
- "total_value_fx_portfolio": performance_fees_net,
91
84
  "total_value_gross": performance_fees_gross,
92
- "total_value_gross_fx_portfolio": performance_fees_gross,
93
85
  }
@@ -22,6 +22,5 @@ from .products import IndexProductFactory, ProductFactory, WhiteLabelProductFact
22
22
  from .reconciliations import AccountReconciliationFactory, AccountReconciliationLineFactory
23
23
  from .roles import ManagerPortfolioRoleFactory, ProductPortfolioRoleFactory
24
24
  from .trades import CustomerTradeFactory, TradeFactory, TradeProposalFactory
25
- from .transactions import TransactionFactory
26
25
  from .indexes import IndexFactory
27
26
  from .rebalancing import (RebalancingModelFactory, RebalancerFactory)
@@ -4,13 +4,18 @@ import factory
4
4
 
5
5
  from wbportfolio.models import DividendTransaction
6
6
 
7
- from .transactions import TransactionFactory
8
7
 
9
-
10
- class DividendTransactionsFactory(TransactionFactory):
8
+ class DividendTransactionsFactory(factory.django.DjangoModelFactory):
11
9
  class Meta:
12
10
  model = DividendTransaction
13
11
 
12
+ currency_fx_rate = 1.0
13
+ portfolio = factory.SubFactory("wbportfolio.factories.PortfolioFactory")
14
+ underlying_instrument = factory.SubFactory("wbfdm.factories.InstrumentFactory")
15
+ currency = factory.SubFactory("wbcore.contrib.currency.factories.CurrencyFactory")
16
+ value_date = factory.Faker("date_object")
17
+ ex_date = factory.Faker("date_object")
18
+ record_date = factory.LazyAttribute(lambda o: o.ex_date)
14
19
  retrocession = 1.0
15
20
  shares = factory.LazyAttribute(lambda o: random.randint(10, 10000))
16
21
  price = factory.LazyAttribute(lambda o: random.randint(10, 10000))
@@ -1,16 +1,20 @@
1
+ import random
2
+
1
3
  import factory
2
4
  from faker import Faker
3
5
 
4
6
  from wbportfolio.models import Fees
5
7
 
6
- from .transactions import TransactionFactory
7
-
8
8
  faker = Faker()
9
9
 
10
10
 
11
- class FeesFactory(TransactionFactory):
11
+ class FeesFactory(factory.django.DjangoModelFactory):
12
12
  class Meta:
13
13
  model = Fees
14
14
 
15
+ currency_fx_rate = 1.0
16
+ fee_date = factory.Faker("date_object")
17
+ total_value = factory.LazyAttribute(lambda o: random.randint(1, 1000))
15
18
  transaction_subtype = factory.Faker("random_element", elements=[x[0] for x in Fees.Type.choices])
16
- linked_product = factory.SubFactory("wbportfolio.factories.ProductFactory")
19
+ product = factory.SubFactory("wbportfolio.factories.products.ProductFactory")
20
+ currency = factory.SubFactory("wbcore.contrib.currency.factories.CurrencyFactory")
@@ -1,4 +1,6 @@
1
1
  import random
2
+ from datetime import timedelta
3
+ from decimal import Decimal
2
4
 
3
5
  import factory
4
6
  from faker import Faker
@@ -6,15 +8,20 @@ from pandas._libs.tslibs.offsets import BDay
6
8
 
7
9
  from wbportfolio.models import Trade, TradeProposal
8
10
 
9
- from .transactions import TransactionFactory
10
-
11
11
  fake = Faker()
12
12
 
13
13
 
14
- class TradeFactory(TransactionFactory):
14
+ class TradeFactory(factory.django.DjangoModelFactory):
15
15
  class Meta:
16
16
  model = Trade
17
17
 
18
+ currency_fx_rate = Decimal(1.0)
19
+ fees = Decimal(0.0)
20
+ portfolio = factory.SubFactory("wbportfolio.factories.PortfolioFactory")
21
+ underlying_instrument = factory.SubFactory("wbfdm.factories.InstrumentFactory")
22
+ currency = factory.SubFactory("wbcore.contrib.currency.factories.CurrencyFactory")
23
+ transaction_date = factory.Faker("date_object")
24
+ value_date = factory.LazyAttribute(lambda o: o.transaction_date + timedelta(days=1))
18
25
  bank = factory.Faker("company")
19
26
  marked_for_deletion = False
20
27
  shares = factory.Faker("pydecimal", min_value=10, max_value=1000, right_digits=4)
@@ -10,7 +10,7 @@ from .claim import (
10
10
  NegativeTermimalAccountPerProductFilterSet,
11
11
  ProfitAndLossPandasFilter,
12
12
  )
13
- from .fees import FeesAggregatedFilter, FeesFilter, FeesPortfolioFilterSet
13
+ from .fees import FeesAggregatedFilter, FeesFilter, FeesProductFilterSet
14
14
  from .trades import (
15
15
  SubscriptionRedemptionFilterSet,
16
16
  SubscriptionRedemptionPortfolioFilterSet,
@@ -18,4 +18,3 @@ from .trades import (
18
18
  TradeInstrumentFilterSet,
19
19
  TradePortfolioFilter,
20
20
  )
21
- from .transactions import TransactionFilterSet, TransactionPortfolioFilterSet
@@ -4,10 +4,10 @@ from wbcore.pandas.filterset import PandasFilterSetMixin
4
4
 
5
5
  from wbportfolio.models import Fees
6
6
 
7
- from .transactions import TransactionFilterSet, get_transaction_default_date_range
7
+ from .utils import get_transaction_default_date_range
8
8
 
9
9
 
10
- class FeesFilter(TransactionFilterSet):
10
+ class FeesFilter(wb_filters.FilterSet):
11
11
  """FilterSet for Fees
12
12
 
13
13
  Currently filters:
@@ -21,23 +21,20 @@ class FeesFilter(TransactionFilterSet):
21
21
  initial=get_transaction_default_date_range,
22
22
  required=True,
23
23
  )
24
- total_value_usd__gte = total_value_usd__lte = transaction_underlying_type = transaction_date = None
25
24
 
26
25
  class Meta:
27
26
  model = Fees
28
27
  fields = {
29
28
  "transaction_subtype": ["exact"],
30
29
  "currency_fx_rate": ["gte", "exact", "lte"],
31
- "portfolio": ["exact"],
30
+ "product": ["exact"],
32
31
  "currency": ["exact"],
33
32
  "total_value": ["gte", "exact", "lte"],
34
33
  "total_value_fx_portfolio": ["gte", "exact", "lte"],
35
- "total_value_gross": ["gte", "exact", "lte"],
36
- "total_value_gross_fx_portfolio": ["gte", "exact", "lte"],
37
34
  }
38
35
 
39
36
 
40
- class FeesPortfolioFilterSet(FeesFilter):
37
+ class FeesProductFilterSet(FeesFilter):
41
38
  portfolio = None
42
39
 
43
40
  class Meta:
@@ -49,13 +46,11 @@ class FeesPortfolioFilterSet(FeesFilter):
49
46
  "currency": ["exact"],
50
47
  "total_value": ["gte", "exact", "lte"],
51
48
  "total_value_fx_portfolio": ["gte", "exact", "lte"],
52
- "total_value_gross": ["gte", "exact", "lte"],
53
- "total_value_gross_fx_portfolio": ["gte", "exact", "lte"],
54
49
  }
55
50
 
56
51
 
57
52
  class FeesAggregatedFilter(PandasFilterSetMixin, wb_filters.FilterSet):
58
- transaction_date = wb_filters.DateRangeFilter(
53
+ fee_date = wb_filters.DateRangeFilter(
59
54
  label="Date Range",
60
55
  method=wb_filters.DateRangeFilter.base_date_range_filter_method,
61
56
  required=True,
@@ -5,14 +5,28 @@ from wbcore import filters as wb_filters
5
5
  from wbcrm.models.accounts import Account
6
6
  from wbfdm.models import Classification, Instrument
7
7
 
8
- from wbportfolio.models import Product, Trade
8
+ from wbportfolio.models import Portfolio, Product, Trade
9
9
  from wbportfolio.models.transactions.claim import Claim
10
10
 
11
11
  from .mixins import OppositeSharesFieldMethodMixin
12
- from .transactions import TransactionFilterSet
12
+ from .utils import get_transaction_default_date_range
13
13
 
14
14
 
15
- class TradeFilter(OppositeSharesFieldMethodMixin, TransactionFilterSet):
15
+ class TradeFilter(OppositeSharesFieldMethodMixin, wb_filters.FilterSet):
16
+ transaction_date = wb_filters.DateRangeFilter(
17
+ method=wb_filters.DateRangeFilter.base_date_range_filter_method,
18
+ label="Date Range",
19
+ initial=get_transaction_default_date_range,
20
+ )
21
+
22
+ portfolio = wb_filters.ModelChoiceFilter(
23
+ label="Portfolio",
24
+ queryset=Portfolio.objects.all(),
25
+ endpoint=Portfolio.get_representation_endpoint(),
26
+ value_key=Portfolio.get_representation_value_key(),
27
+ label_key=Portfolio.get_representation_label_key(),
28
+ )
29
+
16
30
  # we have to redefine the mixin fields because django_filters does not allow class extension with mixin
17
31
  opposite_shares = wb_filters.NumberFilter(
18
32
  field_name="shares",
@@ -28,10 +42,6 @@ class TradeFilter(OppositeSharesFieldMethodMixin, TransactionFilterSet):
28
42
  lookup_icon="≈±",
29
43
  lookup_label="Opposite Approximate (+- 10%)",
30
44
  )
31
- transaction_date = wb_filters.DateRangeFilter(
32
- method=wb_filters.DateRangeFilter.base_date_range_filter_method,
33
- label="Date Range",
34
- )
35
45
  underlying_instrument = wb_filters.ModelChoiceFilter(
36
46
  label="Instrument",
37
47
  queryset=Instrument.objects.all(),
@@ -69,7 +79,6 @@ class TradeFilter(OppositeSharesFieldMethodMixin, TransactionFilterSet):
69
79
  # value_key=Register.get_representation_value_key(),
70
80
  # label_key = Register.get_representation_label_key(),
71
81
  # )
72
- total_value_usd__gte = total_value_usd__lte = transaction_underlying_type = None
73
82
  marked_for_deletion = wb_filters.BooleanFilter(
74
83
  label="Marked For Deletion", initial=False, field_name="marked_for_deletion", lookup_expr="exact"
75
84
  )
@@ -0,0 +1,42 @@
1
+ from datetime import date, timedelta
2
+
3
+ from psycopg.types.range import DateRange
4
+
5
+ from wbportfolio.models import Fees, Trade
6
+
7
+
8
+ def get_transaction_gte_default(field, request, view):
9
+ filter_date = date.today() - timedelta(days=90)
10
+ qs = Trade.objects.none()
11
+ if "instrument_id" in view.kwargs:
12
+ qs = Trade.objects.filter(underlying_instrument__id=view.kwargs["instrument_id"])
13
+ elif "portfolio_id" in view.kwargs:
14
+ qs = Trade.objects.filter(portfolio__id=view.kwargs["portfolio_id"])
15
+ if qs.exists():
16
+ filter_date = qs.earliest("transaction_date").transaction_date
17
+ return filter_date
18
+
19
+
20
+ def get_transaction_underlying_type_choices(*args):
21
+ models = [Fees, Trade]
22
+ choices = []
23
+ for model in models:
24
+ for choice in model.Type.choices:
25
+ choices.append(choice)
26
+ return choices
27
+
28
+
29
+ def get_transaction_lte_default(field, request, view):
30
+ filter_date = date.today() + timedelta(days=7)
31
+ qs = Trade.objects.none()
32
+ if "instrument_id" in view.kwargs:
33
+ qs = Trade.objects.filter(underlying_instrument__id=view.kwargs["instrument_id"])
34
+ elif "portfolio_id" in view.kwargs:
35
+ qs = Trade.objects.filter(portfolio__id=view.kwargs["portfolio_id"])
36
+ if qs.exists():
37
+ filter_date = qs.latest("transaction_date").transaction_date
38
+ return filter_date
39
+
40
+
41
+ def get_transaction_default_date_range(*args, **kwargs):
42
+ return DateRange(get_transaction_gte_default(*args, **kwargs), get_transaction_lte_default(*args, **kwargs))
@@ -19,7 +19,7 @@ class DividendImportHandler(ImportExportHandler):
19
19
  self.currency_handler = CurrencyImportHandler(self.import_source)
20
20
 
21
21
  def _deserialize(self, data):
22
- data["transaction_date"] = datetime.strptime(data["transaction_date"], "%Y-%m-%d").date()
22
+ data["ex_date"] = datetime.strptime(data["ex_date"], "%Y-%m-%d").date()
23
23
  data["value_date"] = datetime.strptime(data["value_date"], "%Y-%m-%d").date()
24
24
  from wbportfolio.models import Portfolio
25
25
 
@@ -42,12 +42,12 @@ class DividendImportHandler(ImportExportHandler):
42
42
 
43
43
  def _get_instance(self, data, history=None, **kwargs):
44
44
  self.import_source.log += "\nGet DividendTransaction Instance."
45
- self.import_source.log += f"\nParameter: Portfolio={data['portfolio']} Underlying={data['underlying_instrument']} Date={data['transaction_date']}"
45
+ self.import_source.log += f"\nParameter: Portfolio={data['portfolio']} Underlying={data['underlying_instrument']} Date={data['ex_date']}"
46
46
  dividends = history if history is not None else self.model.objects
47
47
 
48
48
  dividends = dividends.filter(
49
49
  portfolio=data["portfolio"],
50
- transaction_date=data["transaction_date"],
50
+ ex_date=data["ex_date"],
51
51
  value_date=data["value_date"],
52
52
  underlying_instrument=data["underlying_instrument"],
53
53
  price_gross=data["price_gross"],
@@ -63,10 +63,10 @@ class DividendImportHandler(ImportExportHandler):
63
63
  def _get_history(self: models.Model, history: Dict[str, Any]) -> models.QuerySet:
64
64
  from wbportfolio.models import Product
65
65
 
66
- val_date = datetime.strptime(history["transaction_date"], "%Y-%m-%d")
66
+ val_date = datetime.strptime(history["value_date"], "%Y-%m-%d")
67
67
  try:
68
68
  product = Product.objects.get(**history.get("product", {}))
69
- dividends = self.model.objects.filter(transaction_date__lte=val_date, portfolio=product.primary_portfolio)
69
+ dividends = self.model.objects.filter(ex_date__lte=val_date, portfolio=product.primary_portfolio)
70
70
  if underlying_instrument_data := history.get("underlying_instrument"):
71
71
  if isinstance(underlying_instrument_data, dict):
72
72
  dividends = dividends.filter(
@@ -83,8 +83,8 @@ class DividendImportHandler(ImportExportHandler):
83
83
  self.import_source.log += (
84
84
  "It was a historical import and the following DividendTransaction have to be deleted:"
85
85
  )
86
- for dividend in history.order_by("transaction_date"):
86
+ for dividend in history.order_by("value_date"):
87
87
  self.import_source.log += (
88
- f"\n{dividend.transaction_date:%d.%m.%Y}: {dividend.shares} {dividend.price} ==> Deleted"
88
+ f"\n{dividend.value_date:%d.%m.%Y}: {dividend.shares} {dividend.price} ==> Deleted"
89
89
  )
90
90
  dividend.delete()
@@ -3,7 +3,6 @@ from datetime import datetime
3
3
  from wbcore.contrib.currency.import_export.handlers import CurrencyImportHandler
4
4
  from wbcore.contrib.io.exceptions import DeserializationError
5
5
  from wbcore.contrib.io.imports import ImportExportHandler
6
- from wbfdm.models.instruments import Cash
7
6
 
8
7
 
9
8
  class FeesImportHandler(ImportExportHandler):
@@ -14,31 +13,22 @@ class FeesImportHandler(ImportExportHandler):
14
13
  self.currency_handler = CurrencyImportHandler(self.import_source)
15
14
 
16
15
  def _deserialize(self, data):
17
- data["transaction_date"] = datetime.strptime(data["transaction_date"], "%Y-%m-%d").date()
18
- data["fee_date"] = data["transaction_date"]
19
- if value_date_str := data.get("value_date", None):
20
- data["value_date"] = datetime.strptime(value_date_str, "%Y-%m-%d").date()
21
- if book_date_str := data.get("book_date", None):
22
- data["book_date"] = datetime.strptime(book_date_str, "%Y-%m-%d").date()
23
-
24
- from wbportfolio.models import Portfolio, Product
16
+ data["fee_date"] = datetime.strptime(
17
+ data.get("fee_date", data.pop("transaction_date", None)), "%Y-%m-%d"
18
+ ).date()
19
+ from wbportfolio.models import Product
25
20
 
26
21
  try:
27
- linked_product_data = data.pop("linked_product", None)
28
- if isinstance(linked_product_data, dict):
29
- data["linked_product"] = Product.objects.get(**linked_product_data)
22
+ product_data = data.pop("product", None)
23
+ if isinstance(product_data, dict):
24
+ data["product"] = Product.objects.get(**product_data)
30
25
  else:
31
- data["linked_product"] = Product.objects.get(id=linked_product_data)
26
+ data["product"] = Product.objects.get(id=product_data)
32
27
  except Product.DoesNotExist:
33
28
  raise DeserializationError("There is no valid linked product for in this row.")
34
29
 
35
- if "porfolio" in data:
36
- data["portfolio"] = Portfolio.all_objects.get(id=data["portfolio"])
37
- else:
38
- data["portfolio"] = data["linked_product"].primary_portfolio
39
- data["underlying_instrument"] = Cash.objects.filter(currency=data["portfolio"].currency).first()
40
30
  if "currency" not in data:
41
- data["currency"] = data["portfolio"].currency
31
+ data["currency"] = data["product"].currency
42
32
  else:
43
33
  data["currency"] = self.currency_handler.process_object(data["currency"], read_only=True)[0]
44
34
  data["currency_fx_rate"] = 1.0
@@ -48,9 +38,9 @@ class FeesImportHandler(ImportExportHandler):
48
38
 
49
39
  def _get_instance(self, data, history=None, **kwargs):
50
40
  self.import_source.log += "\nGet Fees Instance."
51
- self.import_source.log += f"\nParameter: Portfolio={data['portfolio']} Date={data['transaction_date']}"
41
+ self.import_source.log += f"\nParameter: Product={data['product']} Date={data['fee_date']}"
52
42
  fees = self.model.objects.filter(
53
- linked_product=data["linked_product"],
43
+ product=data["product"],
54
44
  fee_date=data["fee_date"],
55
45
  transaction_subtype=data["transaction_subtype"],
56
46
  calculated=data["calculated"],