diff --git a/app/packages/api_client/.openapi-generator/FILES b/app/packages/api_client/.openapi-generator/FILES index c0e6bb6..131d1ad 100644 --- a/app/packages/api_client/.openapi-generator/FILES +++ b/app/packages/api_client/.openapi-generator/FILES @@ -21,7 +21,6 @@ doc/BenchmarksApi.md doc/Broker.md doc/CalendarEntry.md doc/CalendarOut.md -doc/CashAvailable.md doc/CashFlowBrokerMonth.md doc/CashFlowMonth.md doc/CashflowApi.md @@ -166,7 +165,6 @@ lib/src/model/benchmark_return_out.dart lib/src/model/broker.dart lib/src/model/calendar_entry.dart lib/src/model/calendar_out.dart -lib/src/model/cash_available.dart lib/src/model/cash_flow_broker_month.dart lib/src/model/cash_flow_month.dart lib/src/model/category_out.dart @@ -251,47 +249,3 @@ lib/src/model/unmatched_zm_out.dart lib/src/model/user_out.dart lib/src/model/value_day.dart pubspec.yaml -test/benchmark_comparison_out_test.dart -test/benchmark_comparison_test.dart -test/benchmark_create_test.dart -test/benchmark_out_test.dart -test/benchmark_patch_test.dart -test/benchmark_return_out_test.dart -test/benchmarks_api_test.dart -test/calendar_entry_test.dart -test/calendar_out_test.dart -test/cash_available_test.dart -test/cash_flow_broker_month_test.dart -test/forecast_month_test.dart -test/forecast_out_test.dart -test/goal_create_test.dart -test/goal_out_test.dart -test/goal_patch_test.dart -test/goal_progress_out_test.dart -test/goals_api_test.dart -test/history_out_test.dart -test/history_row_test.dart -test/history_totals_test.dart -test/income_api_test.dart -test/link_create_test.dart -test/link_out_test.dart -test/links_api_test.dart -test/price_manual_in_test.dart -test/price_manual_out_test.dart -test/rebalance_api_test.dart -test/rebalance_bucket_out_test.dart -test/rebalance_out_test.dart -test/target_in_test.dart -test/target_out_test.dart -test/targets_in_test.dart -test/targets_out_test.dart -test/tax_account_out_test.dart -test/tax_api_test.dart -test/tax_lot_out_test.dart -test/tax_lots_out_test.dart -test/tax_totals_test.dart -test/tax_year_out_test.dart -test/trade_out_test.dart -test/unmatched_broker_out_test.dart -test/unmatched_out_test.dart -test/unmatched_zm_out_test.dart diff --git a/app/packages/api_client/doc/CashAvailable.md b/app/packages/api_client/doc/CashAvailable.md deleted file mode 100644 index 951ea0e..0000000 --- a/app/packages/api_client/doc/CashAvailable.md +++ /dev/null @@ -1,14 +0,0 @@ -# fintracker_api.model.CashAvailable - -## Load the model package -```dart -import 'package:fintracker_api/api.dart'; -``` - -## Properties -Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- - -[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) - - diff --git a/app/packages/api_client/doc/RebalanceApi.md b/app/packages/api_client/doc/RebalanceApi.md index 76dc89f..4aa6283 100644 --- a/app/packages/api_client/doc/RebalanceApi.md +++ b/app/packages/api_client/doc/RebalanceApi.md @@ -26,7 +26,7 @@ import 'package:fintracker_api/api.dart'; final api = FintrackerApi().getRebalanceApi(); final int portfolioId = 56; // int | final String dimension = dimension_example; // String | asset_class | sector | country | currency -final CashAvailable cashAvailable = ; // CashAvailable | переопределяет остаток на счетах для what-if +final String cashAvailable = cashAvailable_example; // String | переопределяет остаток на счетах для what-if try { final response = api.rebalanceRebalance(portfolioId, dimension, cashAvailable); @@ -42,7 +42,7 @@ Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- **portfolioId** | **int**| | **dimension** | **String**| asset_class | sector | country | currency | [optional] [default to 'asset_class'] - **cashAvailable** | [**CashAvailable**](.md)| переопределяет остаток на счетах для what-if | [optional] + **cashAvailable** | **String**| переопределяет остаток на счетах для what-if | [optional] ### Return type diff --git a/app/packages/api_client/lib/fintracker_api.dart b/app/packages/api_client/lib/fintracker_api.dart index e2eb166..35e64b7 100644 --- a/app/packages/api_client/lib/fintracker_api.dart +++ b/app/packages/api_client/lib/fintracker_api.dart @@ -47,7 +47,6 @@ export 'package:fintracker_api/src/model/benchmark_return_out.dart'; export 'package:fintracker_api/src/model/broker.dart'; export 'package:fintracker_api/src/model/calendar_entry.dart'; export 'package:fintracker_api/src/model/calendar_out.dart'; -export 'package:fintracker_api/src/model/cash_available.dart'; export 'package:fintracker_api/src/model/cash_flow_broker_month.dart'; export 'package:fintracker_api/src/model/cash_flow_month.dart'; export 'package:fintracker_api/src/model/category_out.dart'; diff --git a/app/packages/api_client/lib/src/api/rebalance_api.dart b/app/packages/api_client/lib/src/api/rebalance_api.dart index 1fbacad..f0815ca 100644 --- a/app/packages/api_client/lib/src/api/rebalance_api.dart +++ b/app/packages/api_client/lib/src/api/rebalance_api.dart @@ -9,7 +9,6 @@ import 'dart:convert'; import 'package:fintracker_api/src/deserialize.dart'; import 'package:dio/dio.dart'; -import 'package:fintracker_api/src/model/cash_available.dart'; import 'package:fintracker_api/src/model/problem.dart'; import 'package:fintracker_api/src/model/rebalance_out.dart'; import 'package:fintracker_api/src/model/targets_in.dart'; @@ -40,7 +39,7 @@ class RebalanceApi { Future> rebalanceRebalance({ required int portfolioId, String? dimension = 'asset_class', - CashAvailable? cashAvailable, + String? cashAvailable, CancelToken? cancelToken, Map? headers, Map? extra, diff --git a/app/packages/api_client/lib/src/deserialize.dart b/app/packages/api_client/lib/src/deserialize.dart index 75fcc58..188491e 100644 --- a/app/packages/api_client/lib/src/deserialize.dart +++ b/app/packages/api_client/lib/src/deserialize.dart @@ -11,7 +11,6 @@ import 'package:fintracker_api/src/model/benchmark_patch.dart'; import 'package:fintracker_api/src/model/benchmark_return_out.dart'; import 'package:fintracker_api/src/model/calendar_entry.dart'; import 'package:fintracker_api/src/model/calendar_out.dart'; -import 'package:fintracker_api/src/model/cash_available.dart'; import 'package:fintracker_api/src/model/cash_flow_broker_month.dart'; import 'package:fintracker_api/src/model/cash_flow_month.dart'; import 'package:fintracker_api/src/model/category_out.dart'; @@ -144,8 +143,6 @@ final _regMap = RegExp(r'^Map$'); return CalendarEntry.fromJson(value as Map) as ReturnType; case 'CalendarOut': return CalendarOut.fromJson(value as Map) as ReturnType; - case 'CashAvailable': - return CashAvailable.fromJson(value as Map) as ReturnType; case 'CashFlowBrokerMonth': return CashFlowBrokerMonth.fromJson(value as Map) as ReturnType; case 'CashFlowMonth': diff --git a/app/packages/api_client/lib/src/model/cash_available.dart b/app/packages/api_client/lib/src/model/cash_available.dart deleted file mode 100644 index d983a04..0000000 --- a/app/packages/api_client/lib/src/model/cash_available.dart +++ /dev/null @@ -1,42 +0,0 @@ -// -// AUTO-GENERATED FILE, DO NOT MODIFY! -// - -// ignore_for_file: unused_element -import 'package:copy_with_extension/copy_with_extension.dart'; -import 'package:json_annotation/json_annotation.dart'; - -part 'cash_available.g.dart'; - - -@CopyWith() -@JsonSerializable( - checked: true, - createToJson: true, - disallowUnrecognizedKeys: false, - explicitToJson: true, -) -class CashAvailable { - /// Returns a new [CashAvailable] instance. - CashAvailable({ - }); - - - - @override - bool operator ==(Object other) => identical(this, other) || other is CashAvailable && - - @override - int get hashCode => - - factory CashAvailable.fromJson(Map json) => _$CashAvailableFromJson(json); - - Map toJson() => _$CashAvailableToJson(this); - - @override - String toString() { - return toJson().toString(); - } - -} - diff --git a/app/packages/api_client/test/cash_available_test.dart b/app/packages/api_client/test/cash_available_test.dart deleted file mode 100644 index 67c1a7f..0000000 --- a/app/packages/api_client/test/cash_available_test.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:test/test.dart'; -import 'package:fintracker_api/fintracker_api.dart'; - -// tests for CashAvailable -void main() { - final CashAvailable? instance = /* CashAvailable(...) */ null; - // TODO add properties to the entity - - group(CashAvailable, () { - }); -} diff --git a/backend/src/fintracker/api/routers/rebalance.py b/backend/src/fintracker/api/routers/rebalance.py index bbf0781..31e02d0 100644 --- a/backend/src/fintracker/api/routers/rebalance.py +++ b/backend/src/fintracker/api/routers/rebalance.py @@ -21,6 +21,7 @@ from sqlalchemy import delete, select from fintracker.analytics.rebalance import DEFAULT_DIMENSION, compute_rebalance from fintracker.api.deps import CurrentUser, SessionDep from fintracker.api.errors import Problem +from fintracker.api.schemas.common import MoneyOpt from fintracker.api.schemas.rebalance import ( RebalanceBucketOut, RebalanceOut, @@ -180,7 +181,7 @@ async def get_rebalance( _: CurrentUser, dimension: DimensionParam = DEFAULT_DIMENSION.value, cash_available: Annotated[ - Decimal | None, Query(description="переопределяет остаток на счетах для what-if") + MoneyOpt, Query(description="переопределяет остаток на счетах для what-if") ] = None, ) -> RebalanceOut: await _portfolio(session, portfolio_id) diff --git a/openapi/openapi.json b/openapi/openapi.json index 61f6e72..0326fe3 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -283,6 +283,42 @@ "title": "AccountRole", "type": "string" }, + "AccountSuggestion": { + "description": "A broker account the user may attach an import to when the report's number matched none.", + "properties": { + "broker": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Broker" + }, + "id": { + "title": "Id", + "type": "integer" + }, + "name": { + "title": "Name", + "type": "string" + }, + "source_id": { + "title": "Source Id", + "type": "string" + } + }, + "required": [ + "id", + "name", + "broker", + "source_id" + ], + "title": "AccountSuggestion", + "type": "object" + }, "AllocationBucket": { "properties": { "bucket": { @@ -328,6 +364,439 @@ "title": "AllocationDimension", "type": "string" }, + "BenchmarkComparison": { + "properties": { + "rows": { + "items": { + "$ref": "#/components/schemas/BenchmarkComparisonOut" + }, + "title": "Rows", + "type": "array" + } + }, + "required": [ + "rows" + ], + "title": "BenchmarkComparison", + "type": "object" + }, + "BenchmarkComparisonOut": { + "properties": { + "benchmarks": { + "items": { + "$ref": "#/components/schemas/BenchmarkReturnOut" + }, + "title": "Benchmarks", + "type": "array" + }, + "date_from": { + "format": "date", + "title": "Date From", + "type": "string" + }, + "date_to": { + "format": "date", + "title": "Date To", + "type": "string" + }, + "period": { + "title": "Period", + "type": "string" + }, + "portfolio_days_skipped": { + "title": "Portfolio Days Skipped", + "type": "integer" + }, + "portfolio_twr": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Portfolio Twr" + }, + "portfolio_twr_annualized": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Portfolio Twr Annualized" + } + }, + "required": [ + "period", + "date_from", + "date_to", + "portfolio_twr", + "portfolio_twr_annualized", + "portfolio_days_skipped", + "benchmarks" + ], + "title": "BenchmarkComparisonOut", + "type": "object" + }, + "BenchmarkCreate": { + "properties": { + "code": { + "maxLength": 32, + "minLength": 1, + "title": "Code", + "type": "string" + }, + "currency": { + "default": "RUB", + "title": "Currency", + "type": "string" + }, + "instrument_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Instrument Id" + }, + "is_active": { + "default": true, + "title": "Is Active", + "type": "boolean" + }, + "is_default": { + "default": false, + "title": "Is Default", + "type": "boolean" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "name": { + "maxLength": 128, + "minLength": 1, + "title": "Name", + "type": "string" + }, + "source": { + "default": "moex", + "title": "Source", + "type": "string" + } + }, + "required": [ + "code", + "name", + "kind" + ], + "title": "BenchmarkCreate", + "type": "object" + }, + "BenchmarkOut": { + "properties": { + "code": { + "title": "Code", + "type": "string" + }, + "currency": { + "title": "Currency", + "type": "string" + }, + "history_from": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "History From" + }, + "history_to": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "History To" + }, + "id": { + "title": "Id", + "type": "integer" + }, + "instrument_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Instrument Id" + }, + "is_active": { + "title": "Is Active", + "type": "boolean" + }, + "is_default": { + "title": "Is Default", + "type": "boolean" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "source": { + "title": "Source", + "type": "string" + } + }, + "required": [ + "id", + "code", + "name", + "kind", + "source", + "currency", + "is_default", + "is_active", + "instrument_id", + "history_from", + "history_to" + ], + "title": "BenchmarkOut", + "type": "object" + }, + "BenchmarkPatch": { + "properties": { + "code": { + "anyOf": [ + { + "maxLength": 32, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code" + }, + "currency": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Currency" + }, + "instrument_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Instrument Id" + }, + "is_active": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Is Active" + }, + "is_default": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Is Default" + }, + "kind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Kind" + }, + "name": { + "anyOf": [ + { + "maxLength": 128, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source" + } + }, + "title": "BenchmarkPatch", + "type": "object" + }, + "BenchmarkReturnOut": { + "properties": { + "benchmark_id": { + "title": "Benchmark Id", + "type": "integer" + }, + "code": { + "title": "Code", + "type": "string" + }, + "days_skipped": { + "title": "Days Skipped", + "type": "integer" + }, + "excess": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Excess" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "twr": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Twr" + }, + "twr_annualized": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Twr Annualized" + } + }, + "required": [ + "benchmark_id", + "code", + "name", + "kind", + "twr", + "twr_annualized", + "days_skipped", + "excess" + ], + "title": "BenchmarkReturnOut", + "type": "object" + }, + "Body_imports_create": { + "properties": { + "account_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "target account, if known", + "title": "Account Id" + }, + "file": { + "contentMediaType": "application/octet-stream", + "description": "the report itself", + "title": "File", + "type": "string" + }, + "parser": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "force a parser from registry.names()", + "title": "Parser" + } + }, + "required": [ + "file" + ], + "title": "Body_imports_create", + "type": "object" + }, "Broker": { "enum": [ "tinvest", @@ -338,6 +807,200 @@ "title": "Broker", "type": "string" }, + "CalendarEntry": { + "properties": { + "amount": { + "description": "decimal as string", + "title": "Amount", + "type": "string" + }, + "amount_rub": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Amount Rub" + }, + "basis": { + "title": "Basis", + "type": "string" + }, + "currency": { + "title": "Currency", + "type": "string" + }, + "expected_date": { + "format": "date", + "title": "Expected Date", + "type": "string" + }, + "instrument_id": { + "title": "Instrument Id", + "type": "integer" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "per_unit": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Per Unit" + }, + "qty": { + "description": "decimal as string", + "title": "Qty", + "type": "string" + }, + "record_date": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Record Date" + }, + "tax_withheld": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Tax Withheld" + }, + "ticker": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ticker" + } + }, + "required": [ + "instrument_id", + "ticker", + "name", + "kind", + "expected_date", + "record_date", + "qty", + "per_unit", + "amount", + "currency", + "amount_rub", + "basis", + "tax_withheld" + ], + "title": "CalendarEntry", + "type": "object" + }, + "CalendarOut": { + "properties": { + "as_of": { + "format": "date", + "title": "As Of", + "type": "string" + }, + "by_basis": { + "additionalProperties": { + "description": "decimal as string", + "type": "string" + }, + "title": "By Basis", + "type": "object" + }, + "currency": { + "title": "Currency", + "type": "string" + }, + "entries": { + "items": { + "$ref": "#/components/schemas/CalendarEntry" + }, + "title": "Entries", + "type": "array" + }, + "total_expected_rub": { + "description": "decimal as string", + "title": "Total Expected Rub", + "type": "string" + } + }, + "required": [ + "as_of", + "currency", + "total_expected_rub", + "entries", + "by_basis" + ], + "title": "CalendarOut", + "type": "object" + }, + "CashFlowBrokerMonth": { + "properties": { + "deposits_rub": { + "description": "decimal as string", + "title": "Deposits Rub", + "type": "string" + }, + "event_count": { + "title": "Event Count", + "type": "integer" + }, + "month": { + "format": "date", + "title": "Month", + "type": "string" + }, + "net_rub": { + "description": "decimal as string", + "title": "Net Rub", + "type": "string" + }, + "withdrawals_rub": { + "description": "decimal as string", + "title": "Withdrawals Rub", + "type": "string" + } + }, + "required": [ + "month", + "deposits_rub", + "withdrawals_rub", + "net_rub", + "event_count" + ], + "title": "CashFlowBrokerMonth", + "type": "object" + }, "CashFlowMonth": { "properties": { "baseline_rub": { @@ -470,6 +1133,33 @@ "title": "CategoryOut", "type": "object" }, + "CommitRequest": { + "properties": { + "account_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Account Id" + }, + "confirm_duplicates": { + "default": false, + "title": "Confirm Duplicates", + "type": "boolean" + }, + "dry_run": { + "default": false, + "title": "Dry Run", + "type": "boolean" + } + }, + "title": "CommitRequest", + "type": "object" + }, "DataQualityRow": { "properties": { "check_name": { @@ -762,6 +1452,7 @@ "tinvest_api", "report_sber", "report_vtb", + "csv", "manual" ], "title": "EventSource", @@ -790,6 +1481,426 @@ "title": "FlowType", "type": "string" }, + "ForecastMonth": { + "properties": { + "amount_rub": { + "description": "decimal as string", + "title": "Amount Rub", + "type": "string" + }, + "by_basis": { + "additionalProperties": { + "description": "decimal as string", + "type": "string" + }, + "title": "By Basis", + "type": "object" + }, + "month": { + "format": "date", + "title": "Month", + "type": "string" + } + }, + "required": [ + "month", + "amount_rub", + "by_basis" + ], + "title": "ForecastMonth", + "type": "object" + }, + "ForecastOut": { + "properties": { + "annual_yield_on_value": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Annual Yield On Value" + }, + "months": { + "items": { + "$ref": "#/components/schemas/ForecastMonth" + }, + "title": "Months", + "type": "array" + }, + "total_rub": { + "description": "decimal as string", + "title": "Total Rub", + "type": "string" + }, + "warnings": { + "items": { + "type": "string" + }, + "title": "Warnings", + "type": "array" + } + }, + "required": [ + "months", + "total_rub", + "annual_yield_on_value", + "warnings" + ], + "title": "ForecastOut", + "type": "object" + }, + "GoalCreate": { + "additionalProperties": false, + "properties": { + "archived": { + "default": false, + "title": "Archived", + "type": "boolean" + }, + "currency": { + "default": "RUB", + "maxLength": 3, + "minLength": 3, + "title": "Currency", + "type": "string" + }, + "monthly_contribution": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Monthly Contribution" + }, + "name": { + "maxLength": 128, + "minLength": 1, + "title": "Name", + "type": "string" + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Note" + }, + "scope": { + "default": "all", + "maxLength": 32, + "title": "Scope", + "type": "string" + }, + "target_amount": { + "description": "decimal as string", + "title": "Target Amount", + "type": "string" + }, + "target_date": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Target Date" + } + }, + "required": [ + "name", + "target_amount" + ], + "title": "GoalCreate", + "type": "object" + }, + "GoalOut": { + "properties": { + "archived": { + "title": "Archived", + "type": "boolean" + }, + "currency": { + "title": "Currency", + "type": "string" + }, + "id": { + "title": "Id", + "type": "integer" + }, + "monthly_contribution": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Monthly Contribution" + }, + "name": { + "title": "Name", + "type": "string" + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Note" + }, + "scope": { + "title": "Scope", + "type": "string" + }, + "target_amount": { + "description": "decimal as string", + "title": "Target Amount", + "type": "string" + }, + "target_date": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Target Date" + } + }, + "required": [ + "id", + "name", + "scope", + "target_amount", + "currency", + "target_date", + "monthly_contribution", + "note", + "archived" + ], + "title": "GoalOut", + "type": "object" + }, + "GoalPatch": { + "additionalProperties": false, + "properties": { + "archived": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Archived" + }, + "currency": { + "anyOf": [ + { + "maxLength": 3, + "minLength": 3, + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Currency" + }, + "monthly_contribution": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Monthly Contribution" + }, + "name": { + "anyOf": [ + { + "maxLength": 128, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Note" + }, + "scope": { + "anyOf": [ + { + "maxLength": 32, + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Scope" + }, + "target_amount": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Target Amount" + }, + "target_date": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Target Date" + } + }, + "title": "GoalPatch", + "type": "object" + }, + "GoalProgressOut": { + "properties": { + "as_of": { + "format": "date", + "title": "As Of", + "type": "string" + }, + "assumed_rate": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Assumed Rate" + }, + "basis": { + "title": "Basis", + "type": "string" + }, + "current_value_rub": { + "description": "decimal as string", + "title": "Current Value Rub", + "type": "string" + }, + "goal_id": { + "title": "Goal Id", + "type": "integer" + }, + "monthly_needed_rub": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Monthly Needed Rub" + }, + "on_track": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "On Track" + }, + "progress": { + "description": "decimal as string", + "title": "Progress", + "type": "string" + }, + "projected_date": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Projected Date" + }, + "target_amount_rub": { + "description": "decimal as string", + "title": "Target Amount Rub", + "type": "string" + } + }, + "required": [ + "goal_id", + "as_of", + "current_value_rub", + "target_amount_rub", + "progress", + "projected_date", + "basis", + "assumed_rate", + "monthly_needed_rub", + "on_track" + ], + "title": "GoalProgressOut", + "type": "object" + }, "Health": { "properties": { "database": { @@ -813,6 +1924,100 @@ "title": "Health", "type": "object" }, + "HistoryOut": { + "properties": { + "rows": { + "items": { + "$ref": "#/components/schemas/HistoryRow" + }, + "title": "Rows", + "type": "array" + }, + "totals": { + "$ref": "#/components/schemas/HistoryTotals" + } + }, + "required": [ + "rows", + "totals" + ], + "title": "HistoryOut", + "type": "object" + }, + "HistoryRow": { + "properties": { + "amount": { + "description": "decimal as string", + "title": "Amount", + "type": "string" + }, + "amount_rub": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Amount Rub" + }, + "currency": { + "title": "Currency", + "type": "string" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "month": { + "format": "date", + "title": "Month", + "type": "string" + }, + "payment_count": { + "title": "Payment Count", + "type": "integer" + }, + "tax_withheld": { + "description": "decimal as string", + "title": "Tax Withheld", + "type": "string" + } + }, + "required": [ + "month", + "kind", + "currency", + "amount", + "amount_rub", + "tax_withheld", + "payment_count" + ], + "title": "HistoryRow", + "type": "object" + }, + "HistoryTotals": { + "properties": { + "amount_rub": { + "description": "decimal as string", + "title": "Amount Rub", + "type": "string" + }, + "tax_withheld_rub": { + "description": "decimal as string", + "title": "Tax Withheld Rub", + "type": "string" + } + }, + "required": [ + "amount_rub", + "tax_withheld_rub" + ], + "title": "HistoryTotals", + "type": "object" + }, "HoldingOut": { "properties": { "accrued_interest_rub": { @@ -1100,6 +2305,472 @@ "title": "HoldingOut", "type": "object" }, + "ImportCounts": { + "properties": { + "by_kind": { + "additionalProperties": { + "type": "integer" + }, + "title": "By Kind", + "type": "object" + }, + "events_duplicate": { + "title": "Events Duplicate", + "type": "integer" + }, + "events_new": { + "title": "Events New", + "type": "integer" + }, + "events_pending": { + "title": "Events Pending", + "type": "integer" + }, + "events_shadow": { + "title": "Events Shadow", + "type": "integer" + }, + "events_total": { + "title": "Events Total", + "type": "integer" + }, + "lines": { + "title": "Lines", + "type": "integer" + } + }, + "required": [ + "lines", + "events_total", + "events_new", + "events_duplicate", + "events_shadow", + "events_pending", + "by_kind" + ], + "title": "ImportCounts", + "type": "object" + }, + "ImportPreview": { + "description": "The full preview: `POST /imports` and `GET /imports/{id}` both return this.", + "properties": { + "account_external_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Account External Id" + }, + "account_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Account Id" + }, + "account_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Account Name" + }, + "account_suggestions": { + "items": { + "$ref": "#/components/schemas/AccountSuggestion" + }, + "title": "Account Suggestions", + "type": "array" + }, + "broker": { + "title": "Broker", + "type": "string" + }, + "committed_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Committed At" + }, + "counts": { + "$ref": "#/components/schemas/ImportCounts" + }, + "duplicate_of_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Duplicate Of Id" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error" + }, + "filename": { + "title": "Filename", + "type": "string" + }, + "id": { + "title": "Id", + "type": "integer" + }, + "parse_status": { + "title": "Parse Status", + "type": "string" + }, + "parser_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Parser Name" + }, + "parser_version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Parser Version" + }, + "pending_instruments": { + "items": { + "$ref": "#/components/schemas/PendingInstrumentOut" + }, + "title": "Pending Instruments", + "type": "array" + }, + "period_from": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Period From" + }, + "period_to": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Period To" + }, + "reconciliation": { + "$ref": "#/components/schemas/ReconciliationOut" + }, + "sample_events": { + "items": { + "$ref": "#/components/schemas/SampleEventOut" + }, + "title": "Sample Events", + "type": "array" + }, + "sha256": { + "title": "Sha256", + "type": "string" + }, + "size_bytes": { + "title": "Size Bytes", + "type": "integer" + }, + "uploaded_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Uploaded At" + }, + "warnings": { + "items": { + "type": "string" + }, + "title": "Warnings", + "type": "array" + } + }, + "required": [ + "id", + "broker", + "filename", + "sha256", + "size_bytes", + "parse_status", + "counts", + "reconciliation" + ], + "title": "ImportPreview", + "type": "object" + }, + "ImportResult": { + "properties": { + "committed": { + "title": "Committed", + "type": "boolean" + }, + "events_created": { + "title": "Events Created", + "type": "integer" + }, + "events_shadow": { + "title": "Events Shadow", + "type": "integer" + }, + "events_skipped": { + "title": "Events Skipped", + "type": "integer" + }, + "events_updated": { + "title": "Events Updated", + "type": "integer" + }, + "import_id": { + "title": "Import Id", + "type": "integer" + }, + "metrics_refreshed": { + "title": "Metrics Refreshed", + "type": "boolean" + }, + "pending_instruments": { + "title": "Pending Instruments", + "type": "integer" + }, + "reconciliation": { + "$ref": "#/components/schemas/ReconciliationOut" + } + }, + "required": [ + "import_id", + "committed", + "events_created", + "events_updated", + "events_skipped", + "events_shadow", + "pending_instruments", + "reconciliation", + "metrics_refreshed" + ], + "title": "ImportResult", + "type": "object" + }, + "ImportSummary": { + "description": "One import without the heavy parts — what `GET /imports` lists.", + "properties": { + "account_external_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Account External Id" + }, + "account_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Account Id" + }, + "account_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Account Name" + }, + "broker": { + "title": "Broker", + "type": "string" + }, + "committed_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Committed At" + }, + "counts": { + "$ref": "#/components/schemas/ImportCounts" + }, + "duplicate_of_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Duplicate Of Id" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error" + }, + "filename": { + "title": "Filename", + "type": "string" + }, + "id": { + "title": "Id", + "type": "integer" + }, + "parse_status": { + "title": "Parse Status", + "type": "string" + }, + "parser_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Parser Name" + }, + "parser_version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Parser Version" + }, + "period_from": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Period From" + }, + "period_to": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Period To" + }, + "sha256": { + "title": "Sha256", + "type": "string" + }, + "size_bytes": { + "title": "Size Bytes", + "type": "integer" + }, + "uploaded_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Uploaded At" + } + }, + "required": [ + "id", + "broker", + "filename", + "sha256", + "size_bytes", + "parse_status", + "counts" + ], + "title": "ImportSummary", + "type": "object" + }, "InstrumentDetail": { "properties": { "events": { @@ -1329,6 +3000,86 @@ "title": "JobStatus", "type": "string" }, + "LinkCreate": { + "properties": { + "cash_txn_id": { + "title": "Cash Txn Id", + "type": "integer" + }, + "event_id": { + "title": "Event Id", + "type": "integer" + } + }, + "required": [ + "cash_txn_id", + "event_id" + ], + "title": "LinkCreate", + "type": "object" + }, + "LinkOut": { + "properties": { + "amount_delta": { + "description": "decimal as string", + "title": "Amount Delta", + "type": "string" + }, + "cash_txn_id": { + "title": "Cash Txn Id", + "type": "integer" + }, + "confidence": { + "description": "decimal as string", + "title": "Confidence", + "type": "string" + }, + "currency": { + "title": "Currency", + "type": "string" + }, + "day_gap": { + "title": "Day Gap", + "type": "integer" + }, + "event_id": { + "title": "Event Id", + "type": "integer" + }, + "id": { + "title": "Id", + "type": "integer" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Note" + } + }, + "required": [ + "id", + "cash_txn_id", + "event_id", + "kind", + "confidence", + "amount_delta", + "currency", + "day_gap", + "note" + ], + "title": "LinkOut", + "type": "object" + }, "LoginRequest": { "properties": { "email": { @@ -1552,6 +3303,391 @@ "title": "NetWorthDay", "type": "object" }, + "NewInstrument": { + "description": "The instrument to create from a report's own data (`action = \"create\"`).", + "properties": { + "asset_class": { + "title": "Asset Class", + "type": "string" + }, + "board": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Board" + }, + "currency": { + "default": "RUB", + "title": "Currency", + "type": "string" + }, + "isin": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Isin" + }, + "lot": { + "default": 1, + "title": "Lot", + "type": "integer" + }, + "name": { + "title": "Name", + "type": "string" + }, + "ticker": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ticker" + } + }, + "required": [ + "asset_class", + "name" + ], + "title": "NewInstrument", + "type": "object" + }, + "PendingInstrumentOut": { + "properties": { + "asset_class_hint": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Asset Class Hint" + }, + "board": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Board" + }, + "created_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Created At" + }, + "currency": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Currency" + }, + "first_seen_file_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "First Seen File Id" + }, + "id": { + "title": "Id", + "type": "integer" + }, + "instrument_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Instrument Id" + }, + "isin": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Isin" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "occurrences": { + "title": "Occurrences", + "type": "integer" + }, + "sample_price": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Sample Price" + }, + "sample_quantity": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Sample Quantity" + }, + "source": { + "title": "Source", + "type": "string" + }, + "source_key": { + "title": "Source Key", + "type": "string" + }, + "status": { + "title": "Status", + "type": "string" + }, + "ticker": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ticker" + } + }, + "required": [ + "id", + "source", + "source_key", + "occurrences", + "status" + ], + "title": "PendingInstrumentOut", + "type": "object" + }, + "PendingResolveRequest": { + "properties": { + "action": { + "enum": [ + "link", + "create", + "ignore" + ], + "title": "Action", + "type": "string" + }, + "instrument": { + "anyOf": [ + { + "$ref": "#/components/schemas/NewInstrument" + }, + { + "type": "null" + } + ] + }, + "instrument_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Instrument Id" + } + }, + "required": [ + "action" + ], + "title": "PendingResolveRequest", + "type": "object" + }, + "PendingResolveResult": { + "properties": { + "alias_created": { + "title": "Alias Created", + "type": "boolean" + }, + "events_bound": { + "title": "Events Bound", + "type": "integer" + }, + "id": { + "title": "Id", + "type": "integer" + }, + "instrument_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Instrument Id" + }, + "metrics_refreshed": { + "title": "Metrics Refreshed", + "type": "boolean" + }, + "status": { + "title": "Status", + "type": "string" + } + }, + "required": [ + "id", + "status", + "events_bound", + "alias_created", + "metrics_refreshed" + ], + "title": "PendingResolveResult", + "type": "object" + }, + "PriceManualIn": { + "properties": { + "currency": { + "title": "Currency", + "type": "string" + }, + "d": { + "format": "date", + "title": "D", + "type": "string" + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Note" + }, + "price": { + "description": "decimal as string", + "title": "Price", + "type": "string" + } + }, + "required": [ + "d", + "price", + "currency" + ], + "title": "PriceManualIn", + "type": "object" + }, + "PriceManualOut": { + "properties": { + "currency": { + "title": "Currency", + "type": "string" + }, + "d": { + "format": "date", + "title": "D", + "type": "string" + }, + "id": { + "title": "Id", + "type": "integer" + }, + "instrument_id": { + "title": "Instrument Id", + "type": "integer" + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Note" + }, + "price": { + "description": "decimal as string", + "title": "Price", + "type": "string" + } + }, + "required": [ + "id", + "instrument_id", + "d", + "price", + "currency", + "note" + ], + "title": "PriceManualOut", + "type": "object" + }, "PricePoint": { "properties": { "accrued_interest": { @@ -1579,6 +3715,11 @@ "format": "date", "title": "D", "type": "string" + }, + "source": { + "default": "daily", + "title": "Source", + "type": "string" } }, "required": [ @@ -1614,6 +3755,304 @@ "title": "Problem", "type": "object" }, + "RebalanceBucketOut": { + "properties": { + "bucket": { + "title": "Bucket", + "type": "string" + }, + "current_value_rub": { + "description": "decimal as string", + "title": "Current Value Rub", + "type": "string" + }, + "current_weight": { + "description": "decimal as string", + "title": "Current Weight", + "type": "string" + }, + "delta_value_rub": { + "description": "decimal as string", + "title": "Delta Value Rub", + "type": "string" + }, + "drift": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Drift" + }, + "target_weight": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Target Weight" + }, + "trades": { + "items": { + "$ref": "#/components/schemas/TradeOut" + }, + "title": "Trades", + "type": "array" + }, + "within_band": { + "title": "Within Band", + "type": "boolean" + } + }, + "required": [ + "bucket", + "current_value_rub", + "current_weight", + "target_weight", + "drift", + "within_band", + "delta_value_rub", + "trades" + ], + "title": "RebalanceBucketOut", + "type": "object" + }, + "RebalanceOut": { + "properties": { + "as_of": { + "format": "date", + "title": "As Of", + "type": "string" + }, + "buckets": { + "items": { + "$ref": "#/components/schemas/RebalanceBucketOut" + }, + "title": "Buckets", + "type": "array" + }, + "cash_available_rub": { + "description": "decimal as string", + "title": "Cash Available Rub", + "type": "string" + }, + "dimension": { + "title": "Dimension", + "type": "string" + }, + "portfolio_id": { + "title": "Portfolio Id", + "type": "integer" + }, + "total_value_rub": { + "description": "decimal as string", + "title": "Total Value Rub", + "type": "string" + }, + "warnings": { + "items": { + "type": "string" + }, + "title": "Warnings", + "type": "array" + } + }, + "required": [ + "portfolio_id", + "dimension", + "as_of", + "total_value_rub", + "cash_available_rub", + "buckets", + "warnings" + ], + "title": "RebalanceOut", + "type": "object" + }, + "ReconciliationCash": { + "properties": { + "balance_derived": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Balance Derived" + }, + "balance_report": { + "description": "decimal as string", + "title": "Balance Report", + "type": "string" + }, + "currency": { + "title": "Currency", + "type": "string" + }, + "delta": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Delta" + }, + "matches": { + "title": "Matches", + "type": "boolean" + } + }, + "required": [ + "currency", + "balance_report", + "balance_derived", + "delta", + "matches" + ], + "title": "ReconciliationCash", + "type": "object" + }, + "ReconciliationOut": { + "properties": { + "as_of": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "As Of" + }, + "cash": { + "items": { + "$ref": "#/components/schemas/ReconciliationCash" + }, + "title": "Cash", + "type": "array" + }, + "matches": { + "default": true, + "title": "Matches", + "type": "boolean" + }, + "positions": { + "items": { + "$ref": "#/components/schemas/ReconciliationPosition" + }, + "title": "Positions", + "type": "array" + } + }, + "required": [ + "as_of" + ], + "title": "ReconciliationOut", + "type": "object" + }, + "ReconciliationPosition": { + "properties": { + "instrument_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Instrument Id" + }, + "instrument_name": { + "title": "Instrument Name", + "type": "string" + }, + "isin": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Isin" + }, + "matches": { + "title": "Matches", + "type": "boolean" + }, + "qty_delta": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Qty Delta" + }, + "qty_derived": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Qty Derived" + }, + "qty_report": { + "description": "decimal as string", + "title": "Qty Report", + "type": "string" + }, + "ticker": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ticker" + } + }, + "required": [ + "instrument_id", + "instrument_name", + "ticker", + "isin", + "qty_report", + "qty_derived", + "qty_delta", + "matches" + ], + "title": "ReconciliationPosition", + "type": "object" + }, "RefreshLogOut": { "properties": { "error": { @@ -2062,6 +4501,154 @@ "title": "RunwayOut", "type": "object" }, + "SampleEventOut": { + "properties": { + "amount": { + "description": "decimal as string", + "title": "Amount", + "type": "string" + }, + "currency": { + "title": "Currency", + "type": "string" + }, + "dedupe_key": { + "title": "Dedupe Key", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "fee": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Fee" + }, + "instrument_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Instrument Id" + }, + "instrument_key": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Instrument Key" + }, + "instrument_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Instrument Name" + }, + "is_duplicate": { + "title": "Is Duplicate", + "type": "boolean" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "line_no": { + "title": "Line No", + "type": "integer" + }, + "price": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Price" + }, + "quantity": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Quantity" + }, + "settle_date": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Settle Date" + }, + "trade_date": { + "format": "date", + "title": "Trade Date", + "type": "string" + }, + "trade_no": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Trade No" + } + }, + "required": [ + "line_no", + "kind", + "trade_date", + "amount", + "currency", + "dedupe_key", + "is_duplicate" + ], + "title": "SampleEventOut", + "type": "object" + }, "ScopeOut": { "description": "One reportable set of accounts: `all`, `account:` or `portfolio:`.", "properties": { @@ -2479,6 +5066,496 @@ "title": "SyncRunOut", "type": "object" }, + "TargetIn": { + "additionalProperties": false, + "properties": { + "band": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Band" + }, + "bucket": { + "maxLength": 64, + "minLength": 1, + "title": "Bucket", + "type": "string" + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Note" + }, + "target_weight": { + "description": "decimal as string", + "title": "Target Weight", + "type": "string" + } + }, + "required": [ + "bucket", + "target_weight" + ], + "title": "TargetIn", + "type": "object" + }, + "TargetOut": { + "properties": { + "band": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Band" + }, + "bucket": { + "title": "Bucket", + "type": "string" + }, + "note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Note" + }, + "target_weight": { + "description": "decimal as string", + "title": "Target Weight", + "type": "string" + } + }, + "required": [ + "bucket", + "target_weight", + "band", + "note" + ], + "title": "TargetOut", + "type": "object" + }, + "TargetsIn": { + "additionalProperties": false, + "description": "A complete set for one dimension. Partial updates are not supported: weights only\nmean anything together, and a half-written set could not be checked against 1.", + "properties": { + "dimension": { + "description": "asset_class | sector | country | currency", + "title": "Dimension", + "type": "string" + }, + "targets": { + "items": { + "$ref": "#/components/schemas/TargetIn" + }, + "title": "Targets", + "type": "array" + } + }, + "required": [ + "dimension", + "targets" + ], + "title": "TargetsIn", + "type": "object" + }, + "TargetsOut": { + "properties": { + "dimension": { + "title": "Dimension", + "type": "string" + }, + "portfolio_id": { + "title": "Portfolio Id", + "type": "integer" + }, + "targets": { + "items": { + "$ref": "#/components/schemas/TargetOut" + }, + "title": "Targets", + "type": "array" + }, + "weights_sum": { + "description": "decimal as string", + "title": "Weights Sum", + "type": "string" + } + }, + "required": [ + "portfolio_id", + "dimension", + "targets", + "weights_sum" + ], + "title": "TargetsOut", + "type": "object" + }, + "TaxAccountOut": { + "properties": { + "account_id": { + "title": "Account Id", + "type": "integer" + }, + "account_name": { + "title": "Account Name", + "type": "string" + }, + "coupons_gross_rub": { + "description": "decimal as string", + "title": "Coupons Gross Rub", + "type": "string" + }, + "dividends_gross_rub": { + "description": "decimal as string", + "title": "Dividends Gross Rub", + "type": "string" + }, + "estimated_tax_rub": { + "description": "decimal as string", + "title": "Estimated Tax Rub", + "type": "string" + }, + "ldv_exempt_rub": { + "description": "decimal as string", + "title": "Ldv Exempt Rub", + "type": "string" + }, + "realized_gain_rub": { + "description": "decimal as string", + "title": "Realized Gain Rub", + "type": "string" + }, + "realized_loss_rub": { + "description": "decimal as string", + "title": "Realized Loss Rub", + "type": "string" + }, + "tax_withheld_rub": { + "description": "decimal as string", + "title": "Tax Withheld Rub", + "type": "string" + }, + "taxable_base_rub": { + "description": "decimal as string", + "title": "Taxable Base Rub", + "type": "string" + } + }, + "required": [ + "dividends_gross_rub", + "coupons_gross_rub", + "tax_withheld_rub", + "realized_gain_rub", + "realized_loss_rub", + "ldv_exempt_rub", + "taxable_base_rub", + "estimated_tax_rub", + "account_id", + "account_name" + ], + "title": "TaxAccountOut", + "type": "object" + }, + "TaxLotOut": { + "properties": { + "account_id": { + "title": "Account Id", + "type": "integer" + }, + "account_name": { + "title": "Account Name", + "type": "string" + }, + "cost_rub": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cost Rub" + }, + "days_to_ldv": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Days To Ldv" + }, + "instrument_id": { + "title": "Instrument Id", + "type": "integer" + }, + "ldv_date": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ldv Date" + }, + "ldv_eligible": { + "title": "Ldv Eligible", + "type": "boolean" + }, + "lot_id": { + "title": "Lot Id", + "type": "integer" + }, + "market_value_rub": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Market Value Rub" + }, + "name": { + "title": "Name", + "type": "string" + }, + "open_date": { + "format": "date", + "title": "Open Date", + "type": "string" + }, + "qty_remaining": { + "description": "decimal as string", + "title": "Qty Remaining", + "type": "string" + }, + "tax_if_sold_now_rub": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Tax If Sold Now Rub" + }, + "ticker": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ticker" + }, + "unrealized_gain_rub": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Unrealized Gain Rub" + } + }, + "required": [ + "lot_id", + "instrument_id", + "ticker", + "name", + "account_id", + "account_name", + "open_date", + "qty_remaining", + "cost_rub", + "market_value_rub", + "unrealized_gain_rub", + "ldv_eligible", + "ldv_date", + "days_to_ldv", + "tax_if_sold_now_rub" + ], + "title": "TaxLotOut", + "type": "object" + }, + "TaxLotsOut": { + "properties": { + "as_of": { + "format": "date", + "title": "As Of", + "type": "string" + }, + "disclaimer": { + "title": "Disclaimer", + "type": "string" + }, + "estimated": { + "default": true, + "title": "Estimated", + "type": "boolean" + }, + "lots": { + "items": { + "$ref": "#/components/schemas/TaxLotOut" + }, + "title": "Lots", + "type": "array" + }, + "tax_rate": { + "description": "decimal as string", + "title": "Tax Rate", + "type": "string" + }, + "year": { + "title": "Year", + "type": "integer" + } + }, + "required": [ + "year", + "as_of", + "tax_rate", + "lots", + "disclaimer" + ], + "title": "TaxLotsOut", + "type": "object" + }, + "TaxTotals": { + "properties": { + "coupons_gross_rub": { + "description": "decimal as string", + "title": "Coupons Gross Rub", + "type": "string" + }, + "dividends_gross_rub": { + "description": "decimal as string", + "title": "Dividends Gross Rub", + "type": "string" + }, + "estimated_tax_rub": { + "description": "decimal as string", + "title": "Estimated Tax Rub", + "type": "string" + }, + "ldv_exempt_rub": { + "description": "decimal as string", + "title": "Ldv Exempt Rub", + "type": "string" + }, + "realized_gain_rub": { + "description": "decimal as string", + "title": "Realized Gain Rub", + "type": "string" + }, + "realized_loss_rub": { + "description": "decimal as string", + "title": "Realized Loss Rub", + "type": "string" + }, + "tax_withheld_rub": { + "description": "decimal as string", + "title": "Tax Withheld Rub", + "type": "string" + }, + "taxable_base_rub": { + "description": "decimal as string", + "title": "Taxable Base Rub", + "type": "string" + } + }, + "required": [ + "dividends_gross_rub", + "coupons_gross_rub", + "tax_withheld_rub", + "realized_gain_rub", + "realized_loss_rub", + "ldv_exempt_rub", + "taxable_base_rub", + "estimated_tax_rub" + ], + "title": "TaxTotals", + "type": "object" + }, + "TaxYearOut": { + "properties": { + "accounts": { + "items": { + "$ref": "#/components/schemas/TaxAccountOut" + }, + "title": "Accounts", + "type": "array" + }, + "disclaimer": { + "title": "Disclaimer", + "type": "string" + }, + "estimated": { + "default": true, + "title": "Estimated", + "type": "boolean" + }, + "tax_rate": { + "description": "decimal as string", + "title": "Tax Rate", + "type": "string" + }, + "totals": { + "$ref": "#/components/schemas/TaxTotals" + }, + "year": { + "title": "Year", + "type": "integer" + } + }, + "required": [ + "year", + "tax_rate", + "accounts", + "totals", + "disclaimer" + ], + "title": "TaxYearOut", + "type": "object" + }, "TokenPair": { "properties": { "access_token": { @@ -2507,6 +5584,95 @@ "title": "TokenPair", "type": "object" }, + "TradeOut": { + "properties": { + "action": { + "title": "Action", + "type": "string" + }, + "amount_rub": { + "description": "decimal as string", + "title": "Amount Rub", + "type": "string" + }, + "blocked_by_cash": { + "title": "Blocked By Cash", + "type": "boolean" + }, + "instrument_id": { + "title": "Instrument Id", + "type": "integer" + }, + "lot": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Lot" + }, + "name": { + "title": "Name", + "type": "string" + }, + "price": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Price" + }, + "price_currency": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Price Currency" + }, + "suggested_qty": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Suggested Qty" + }, + "ticker": { + "title": "Ticker", + "type": "string" + } + }, + "required": [ + "instrument_id", + "ticker", + "name", + "action", + "suggested_qty", + "lot", + "price", + "price_currency", + "amount_rub", + "blocked_by_cash" + ], + "title": "TradeOut", + "type": "object" + }, "TransactionOut": { "properties": { "category_id": { @@ -2753,6 +5919,139 @@ "title": "TransactionPage", "type": "object" }, + "UnmatchedBrokerOut": { + "properties": { + "account_id": { + "title": "Account Id", + "type": "integer" + }, + "amount": { + "description": "decimal as string", + "title": "Amount", + "type": "string" + }, + "currency": { + "title": "Currency", + "type": "string" + }, + "date": { + "format": "date", + "title": "Date", + "type": "string" + }, + "direction": { + "title": "Direction", + "type": "string" + }, + "event_id": { + "title": "Event Id", + "type": "integer" + } + }, + "required": [ + "event_id", + "account_id", + "date", + "amount", + "currency", + "direction" + ], + "title": "UnmatchedBrokerOut", + "type": "object" + }, + "UnmatchedOut": { + "properties": { + "broker": { + "items": { + "$ref": "#/components/schemas/UnmatchedBrokerOut" + }, + "title": "Broker", + "type": "array" + }, + "zm": { + "items": { + "$ref": "#/components/schemas/UnmatchedZmOut" + }, + "title": "Zm", + "type": "array" + } + }, + "required": [ + "zm", + "broker" + ], + "title": "UnmatchedOut", + "type": "object" + }, + "UnmatchedZmOut": { + "properties": { + "amount": { + "description": "decimal as string", + "title": "Amount", + "type": "string" + }, + "broker_account_id": { + "title": "Broker Account Id", + "type": "integer" + }, + "cash_txn_id": { + "title": "Cash Txn Id", + "type": "integer" + }, + "comment": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Comment" + }, + "currency": { + "title": "Currency", + "type": "string" + }, + "date": { + "format": "date", + "title": "Date", + "type": "string" + }, + "direction": { + "title": "Direction", + "type": "string" + }, + "payee": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Payee" + }, + "route": { + "title": "Route", + "type": "string" + } + }, + "required": [ + "cash_txn_id", + "date", + "amount", + "currency", + "direction", + "broker_account_id", + "route", + "payee", + "comment" + ], + "title": "UnmatchedZmOut", + "type": "object" + }, "UserOut": { "properties": { "email": { @@ -3033,6 +6332,133 @@ ] } }, + "/api/v1/analytics/benchmarks": { + "get": { + "description": "The portfolio's TWR beside each benchmark's, on the same grid of days.\n\n`days_skipped` is returned on both sides and is not cosmetic: a non-zero value on either\nmeans the two chains did not cover the same days, and the difference is then an\napproximation of an excess return rather than one.", + "operationId": "benchmarks_benchmarks", + "parameters": [ + { + "description": "all | account: | portfolio:", + "in": "query", + "name": "scope", + "required": false, + "schema": { + "default": "all", + "description": "all | account: | portfolio:", + "title": "Scope", + "type": "string" + } + }, + { + "description": "repeatable: 1m 3m 6m ytd 1y 3y all", + "in": "query", + "name": "period", + "required": false, + "schema": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "description": "repeatable: 1m 3m 6m ytd 1y 3y all", + "title": "Period" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BenchmarkComparison" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Benchmarks", + "tags": [ + "benchmarks" + ] + } + }, + "/api/v1/analytics/cashflow-broker": { + "get": { + "description": "Money moved across the brokerage boundary, by month: the other half of cash flow\nthat `metric_cash_flow_monthly` (ZenMoney spending) does not cover.", + "operationId": "analytics_cashflow_broker", + "parameters": [ + { + "description": "all | account: | portfolio:", + "in": "query", + "name": "scope", + "required": false, + "schema": { + "default": "all", + "description": "all | account: | portfolio:", + "title": "Scope", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/CashFlowBrokerMonth" + }, + "title": "Response Analytics Cashflow Broker", + "type": "array" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Cashflow Broker", + "tags": [ + "analytics" + ] + } + }, "/api/v1/analytics/holdings": { "get": { "description": "Open positions, most valuable first; unpriced ones last with null values.", @@ -3476,6 +6902,186 @@ ] } }, + "/api/v1/benchmarks": { + "get": { + "description": "Every benchmark, defaults first — the order the comparison block shows them in.", + "operationId": "benchmarks_list", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/BenchmarkOut" + }, + "title": "Response Benchmarks List", + "type": "array" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "List", + "tags": [ + "benchmarks" + ] + }, + "post": { + "operationId": "benchmarks_create", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BenchmarkCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BenchmarkOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Create", + "tags": [ + "benchmarks" + ] + } + }, + "/api/v1/benchmarks/{benchmark_id}": { + "delete": { + "operationId": "benchmarks_delete", + "parameters": [ + { + "in": "path", + "name": "benchmark_id", + "required": true, + "schema": { + "title": "Benchmark Id", + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Delete", + "tags": [ + "benchmarks" + ] + }, + "patch": { + "operationId": "benchmarks_patch", + "parameters": [ + { + "in": "path", + "name": "benchmark_id", + "required": true, + "schema": { + "title": "Benchmark Id", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BenchmarkPatch" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BenchmarkOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Patch", + "tags": [ + "benchmarks" + ] + } + }, "/api/v1/cashflow/monthly": { "get": { "description": "The last `months` months, oldest first.", @@ -3809,6 +7415,244 @@ ] } }, + "/api/v1/goals": { + "get": { + "operationId": "goals_list", + "parameters": [ + { + "in": "query", + "name": "include_archived", + "required": false, + "schema": { + "default": false, + "title": "Include Archived", + "type": "boolean" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/GoalOut" + }, + "title": "Response Goals List", + "type": "array" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "List", + "tags": [ + "goals" + ] + }, + "post": { + "operationId": "goals_create", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GoalCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GoalOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Create", + "tags": [ + "goals" + ] + } + }, + "/api/v1/goals/{goal_id}": { + "delete": { + "operationId": "goals_delete", + "parameters": [ + { + "in": "path", + "name": "goal_id", + "required": true, + "schema": { + "title": "Goal Id", + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Delete", + "tags": [ + "goals" + ] + }, + "patch": { + "operationId": "goals_patch", + "parameters": [ + { + "in": "path", + "name": "goal_id", + "required": true, + "schema": { + "title": "Goal Id", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GoalPatch" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GoalOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Patch", + "tags": [ + "goals" + ] + } + }, + "/api/v1/goals/{goal_id}/progress": { + "get": { + "operationId": "goals_progress", + "parameters": [ + { + "in": "path", + "name": "goal_id", + "required": true, + "schema": { + "title": "Goal Id", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GoalProgressOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Progress", + "tags": [ + "goals" + ] + } + }, "/api/v1/health": { "get": { "operationId": "health_check", @@ -3840,6 +7684,553 @@ ] } }, + "/api/v1/imports": { + "get": { + "description": "Newest imports first, with the counters each was committed with.", + "operationId": "imports_list", + "parameters": [ + { + "description": "uploaded | parsed | committed | failed", + "in": "query", + "name": "status", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "uploaded | parsed | committed | failed", + "title": "Status" + } + }, + { + "in": "query", + "name": "limit", + "required": false, + "schema": { + "default": 50, + "maximum": 200, + "minimum": 1, + "title": "Limit", + "type": "integer" + } + }, + { + "in": "query", + "name": "offset", + "required": false, + "schema": { + "default": 0, + "minimum": 0, + "title": "Offset", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ImportSummary" + }, + "title": "Response Imports List", + "type": "array" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "List", + "tags": [ + "imports" + ] + }, + "post": { + "description": "Upload a report, parse it and show what committing it would do. Writes no event.", + "operationId": "imports_create", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_imports_create" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ImportPreview" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Create", + "tags": [ + "imports" + ] + } + }, + "/api/v1/imports/{import_id}": { + "delete": { + "description": "Drop an uncommitted import. A committed one stays: `raw_*` is append-only.", + "operationId": "imports_delete", + "parameters": [ + { + "in": "path", + "name": "import_id", + "required": true, + "schema": { + "title": "Import Id", + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Delete", + "tags": [ + "imports" + ] + }, + "get": { + "description": "The preview again — recomputed for an uncommitted import, replayed for a committed one.", + "operationId": "imports_get", + "parameters": [ + { + "in": "path", + "name": "import_id", + "required": true, + "schema": { + "title": "Import Id", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ImportPreview" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Get", + "tags": [ + "imports" + ] + } + }, + "/api/v1/imports/{import_id}/commit": { + "post": { + "description": "Write the file's events into the ledger and rebuild every metric.", + "operationId": "imports_commit", + "parameters": [ + { + "in": "path", + "name": "import_id", + "required": true, + "schema": { + "title": "Import Id", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CommitRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ImportResult" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Commit", + "tags": [ + "imports" + ] + } + }, + "/api/v1/income/calendar": { + "get": { + "description": "Payments in a window, one row each; defaults to the next 12 months, forecast only.", + "operationId": "income_calendar", + "parameters": [ + { + "description": "all | account: | portfolio:", + "in": "query", + "name": "scope", + "required": false, + "schema": { + "default": "all", + "description": "all | account: | portfolio:", + "title": "Scope", + "type": "string" + } + }, + { + "in": "query", + "name": "date_from", + "required": false, + "schema": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Date From" + } + }, + { + "in": "query", + "name": "date_to", + "required": false, + "schema": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Date To" + } + }, + { + "in": "query", + "name": "include_paid", + "required": false, + "schema": { + "default": false, + "title": "Include Paid", + "type": "boolean" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CalendarOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Calendar", + "tags": [ + "income" + ] + } + }, + "/api/v1/income/forecast": { + "get": { + "description": "Expected income per month, split by basis, plus the yield it implies.", + "operationId": "income_forecast", + "parameters": [ + { + "description": "all | account: | portfolio:", + "in": "query", + "name": "scope", + "required": false, + "schema": { + "default": "all", + "description": "all | account: | portfolio:", + "title": "Scope", + "type": "string" + } + }, + { + "in": "query", + "name": "months", + "required": false, + "schema": { + "default": 12, + "maximum": 36, + "minimum": 1, + "title": "Months", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForecastOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Forecast", + "tags": [ + "income" + ] + } + }, + "/api/v1/income/history": { + "get": { + "description": "Income actually received, grouped by month, kind and currency.\n\n`group` exists for the contract's sake and accepts only `month`: the table is stored\nmonthly, and a finer grouping would have to re-read the ledger, which is what the\ncalendar's `paid` rows already do per payment.", + "operationId": "income_history", + "parameters": [ + { + "description": "all | account: | portfolio:", + "in": "query", + "name": "scope", + "required": false, + "schema": { + "default": "all", + "description": "all | account: | portfolio:", + "title": "Scope", + "type": "string" + } + }, + { + "description": "month", + "in": "query", + "name": "group", + "required": false, + "schema": { + "default": "month", + "description": "month", + "title": "Group", + "type": "string" + } + }, + { + "in": "query", + "name": "date_from", + "required": false, + "schema": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Date From" + } + }, + { + "in": "query", + "name": "date_to", + "required": false, + "schema": { + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Date To" + } + }, + { + "description": "dividend | coupon | …", + "in": "query", + "name": "kind", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "dividend | coupon | …", + "title": "Kind" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HistoryOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "History", + "tags": [ + "income" + ] + } + }, "/api/v1/instruments": { "get": { "operationId": "instruments_list", @@ -3942,6 +8333,142 @@ ] } }, + "/api/v1/instruments/pending": { + "get": { + "description": "Instruments a report named that nothing in the master resolves to.", + "operationId": "instruments_pending", + "parameters": [ + { + "description": "pending | resolved | ignored | all", + "in": "query", + "name": "status", + "required": false, + "schema": { + "default": "pending", + "description": "pending | resolved | ignored | all", + "title": "Status", + "type": "string" + } + }, + { + "in": "query", + "name": "limit", + "required": false, + "schema": { + "default": 100, + "maximum": 500, + "minimum": 1, + "title": "Limit", + "type": "integer" + } + }, + { + "in": "query", + "name": "offset", + "required": false, + "schema": { + "default": 0, + "minimum": 0, + "title": "Offset", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PendingInstrumentOut" + }, + "title": "Response Instruments Pending", + "type": "array" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Pending", + "tags": [ + "instruments" + ] + } + }, + "/api/v1/instruments/pending/{pending_id}/resolve": { + "post": { + "description": "Link, create or ignore — then bind the events that were waiting and rebuild the lots.", + "operationId": "instruments_pending_resolve", + "parameters": [ + { + "in": "path", + "name": "pending_id", + "required": true, + "schema": { + "title": "Pending Id", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PendingResolveRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PendingResolveResult" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Pending Resolve", + "tags": [ + "instruments" + ] + } + }, "/api/v1/instruments/{instrument_id}": { "get": { "description": "One instrument with its position, open lots, events and price history.", @@ -4019,6 +8546,189 @@ ] } }, + "/api/v1/instruments/{instrument_id}/prices": { + "post": { + "description": "Set (or replace) the manual price of `instrument_id` on `body.d`.\n\nFor what no exchange quotes: real estate, crypto, a custom holding, an OTC bond nobody\nlists. One row per (instrument, day) — a second call for the same day replaces it rather\nthan piling up duplicates the price series would then have to pick between. Does not\nitself refresh `metric_*`; call `POST /metrics/refresh` after, same as `/rules/apply`.", + "operationId": "instruments_set_manual_price", + "parameters": [ + { + "in": "path", + "name": "instrument_id", + "required": true, + "schema": { + "title": "Instrument Id", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PriceManualIn" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PriceManualOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Set Manual Price", + "tags": [ + "instruments" + ] + } + }, + "/api/v1/links": { + "post": { + "description": "Confirm a pairing the matcher missed. Never touched by the automatic rebuild afterwards.", + "operationId": "links_create", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LinkCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LinkOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Create", + "tags": [ + "links" + ] + } + }, + "/api/v1/links/unmatched": { + "get": { + "description": "Every candidate that survives matching without a link, on either side.", + "operationId": "links_unmatched", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnmatchedOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Unmatched", + "tags": [ + "links" + ] + } + }, + "/api/v1/links/{link_id}": { + "delete": { + "description": "Undo a link, manual or automatic. `flow_type` reverts on the next `POST /metrics/refresh`,\nwhen `classify` rebuilds it from the rules and `matching` no longer overwrites it.", + "operationId": "links_delete", + "parameters": [ + { + "in": "path", + "name": "link_id", + "required": true, + "schema": { + "title": "Link Id", + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Delete", + "tags": [ + "links" + ] + } + }, "/api/v1/metrics/refresh": { "post": { "description": "Rebuild every metric_* table inline (seconds at personal volumes).", @@ -4215,6 +8925,198 @@ ] } }, + "/api/v1/portfolios/{portfolio_id}/rebalance": { + "get": { + "operationId": "rebalance_rebalance", + "parameters": [ + { + "in": "path", + "name": "portfolio_id", + "required": true, + "schema": { + "title": "Portfolio Id", + "type": "integer" + } + }, + { + "description": "asset_class | sector | country | currency", + "in": "query", + "name": "dimension", + "required": false, + "schema": { + "default": "asset_class", + "description": "asset_class | sector | country | currency", + "title": "Dimension", + "type": "string" + } + }, + { + "description": "переопределяет остаток на счетах для what-if", + "in": "query", + "name": "cash_available", + "required": false, + "schema": { + "anyOf": [ + { + "description": "decimal as string", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cash Available" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RebalanceOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Rebalance", + "tags": [ + "rebalance" + ] + } + }, + "/api/v1/portfolios/{portfolio_id}/targets": { + "get": { + "operationId": "rebalance_targets", + "parameters": [ + { + "in": "path", + "name": "portfolio_id", + "required": true, + "schema": { + "title": "Portfolio Id", + "type": "integer" + } + }, + { + "description": "asset_class | sector | country | currency", + "in": "query", + "name": "dimension", + "required": false, + "schema": { + "default": "asset_class", + "description": "asset_class | sector | country | currency", + "title": "Dimension", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TargetsOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Targets", + "tags": [ + "rebalance" + ] + }, + "put": { + "description": "Replace the whole set for one dimension.\n\nThe weights must add up to 1 within `WEIGHT_TOLERANCE`; otherwise the request is\nrefused with the actual sum in the message, so the user can see by how much the plan\nmisses rather than being handed a silently rescaled one.", + "operationId": "rebalance_set_targets", + "parameters": [ + { + "in": "path", + "name": "portfolio_id", + "required": true, + "schema": { + "title": "Portfolio Id", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TargetsIn" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TargetsOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Set Targets", + "tags": [ + "rebalance" + ] + } + }, "/api/v1/rules": { "get": { "operationId": "rules_list", @@ -4735,6 +9637,152 @@ ] } }, + "/api/v1/tax": { + "get": { + "description": "The year's estimated tax position, per account and in total.", + "operationId": "tax_summary", + "parameters": [ + { + "in": "query", + "name": "year", + "required": false, + "schema": { + "anyOf": [ + { + "maximum": 2200, + "minimum": 1990, + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Year" + } + }, + { + "in": "query", + "name": "account_id", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Account Id" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TaxYearOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Summary", + "tags": [ + "tax" + ] + } + }, + "/api/v1/tax/lots": { + "get": { + "description": "Open lots with the date after which a sale falls under the long-term exemption.\n\n`year` selects the rate the \"what if I sold today\" figure is computed at; the lots\nthemselves are always the ones open right now. `lot` is rebuilt from the ledger on every\nrefresh and holds today's state only, so there is no honest way to answer \"which lots\nwere open on 31 December two years ago\" — and inventing one would be worse than the\nlimitation.", + "operationId": "tax_lots", + "parameters": [ + { + "in": "query", + "name": "year", + "required": false, + "schema": { + "anyOf": [ + { + "maximum": 2200, + "minimum": 1990, + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Year" + } + }, + { + "in": "query", + "name": "account_id", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Account Id" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TaxLotsOut" + } + } + }, + "description": "Successful Response" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "HTTPBearer": [] + } + ], + "summary": "Lots", + "tags": [ + "tax" + ] + } + }, "/api/v1/transactions": { "get": { "description": "One page of transactions, newest first, with RUB amounts at each own date's rate.",