investing-algorithm-framework 1.3.1__py3-none-any.whl → 7.25.6__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.
- investing_algorithm_framework/__init__.py +195 -16
- investing_algorithm_framework/analysis/__init__.py +16 -0
- investing_algorithm_framework/analysis/backtest_data_ranges.py +202 -0
- investing_algorithm_framework/analysis/data.py +170 -0
- investing_algorithm_framework/analysis/markdown.py +91 -0
- investing_algorithm_framework/analysis/ranking.py +298 -0
- investing_algorithm_framework/app/__init__.py +31 -4
- investing_algorithm_framework/app/algorithm/__init__.py +7 -0
- investing_algorithm_framework/app/algorithm/algorithm.py +193 -0
- investing_algorithm_framework/app/algorithm/algorithm_factory.py +118 -0
- investing_algorithm_framework/app/app.py +2233 -264
- investing_algorithm_framework/app/app_hook.py +28 -0
- investing_algorithm_framework/app/context.py +1724 -0
- investing_algorithm_framework/app/eventloop.py +620 -0
- investing_algorithm_framework/app/reporting/__init__.py +27 -0
- investing_algorithm_framework/app/reporting/ascii.py +921 -0
- investing_algorithm_framework/app/reporting/backtest_report.py +349 -0
- investing_algorithm_framework/app/reporting/charts/__init__.py +19 -0
- investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
- investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
- investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +74 -0
- investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
- investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +70 -0
- investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
- investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +79 -0
- investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +55 -0
- investing_algorithm_framework/app/reporting/generate.py +185 -0
- investing_algorithm_framework/app/reporting/tables/__init__.py +11 -0
- investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +217 -0
- investing_algorithm_framework/app/reporting/tables/stop_loss_table.py +0 -0
- investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +80 -0
- investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
- investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
- investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
- investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
- investing_algorithm_framework/app/stateless/action_handlers/__init__.py +6 -3
- investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
- investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +2 -1
- investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
- investing_algorithm_framework/app/stateless/exception_handler.py +1 -1
- investing_algorithm_framework/app/strategy.py +873 -52
- investing_algorithm_framework/app/task.py +5 -3
- investing_algorithm_framework/app/web/__init__.py +2 -1
- investing_algorithm_framework/app/web/controllers/__init__.py +2 -2
- investing_algorithm_framework/app/web/controllers/orders.py +4 -3
- investing_algorithm_framework/app/web/controllers/portfolio.py +1 -1
- investing_algorithm_framework/app/web/controllers/positions.py +3 -3
- investing_algorithm_framework/app/web/create_app.py +4 -2
- investing_algorithm_framework/app/web/error_handler.py +1 -1
- investing_algorithm_framework/app/web/schemas/order.py +2 -2
- investing_algorithm_framework/app/web/schemas/position.py +1 -0
- investing_algorithm_framework/cli/__init__.py +0 -0
- investing_algorithm_framework/cli/cli.py +231 -0
- investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -0
- investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
- investing_algorithm_framework/cli/initialize_app.py +603 -0
- investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
- investing_algorithm_framework/cli/templates/app.py.template +18 -0
- investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
- investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
- investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
- investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
- investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
- investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
- investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
- investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
- investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
- investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
- investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
- investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
- investing_algorithm_framework/cli/templates/env.example.template +2 -0
- investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
- investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
- investing_algorithm_framework/cli/templates/readme.md.template +135 -0
- investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
- investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
- investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
- investing_algorithm_framework/cli/validate_backtest_checkpoints.py +197 -0
- investing_algorithm_framework/create_app.py +43 -9
- investing_algorithm_framework/dependency_container.py +121 -33
- investing_algorithm_framework/domain/__init__.py +109 -22
- investing_algorithm_framework/domain/algorithm_id.py +69 -0
- investing_algorithm_framework/domain/backtesting/__init__.py +25 -0
- investing_algorithm_framework/domain/backtesting/backtest.py +548 -0
- investing_algorithm_framework/domain/backtesting/backtest_date_range.py +113 -0
- investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +241 -0
- investing_algorithm_framework/domain/backtesting/backtest_metrics.py +470 -0
- investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
- investing_algorithm_framework/domain/backtesting/backtest_run.py +663 -0
- investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
- investing_algorithm_framework/domain/backtesting/backtest_utils.py +198 -0
- investing_algorithm_framework/domain/backtesting/combine_backtests.py +392 -0
- investing_algorithm_framework/domain/config.py +60 -138
- investing_algorithm_framework/domain/constants.py +23 -34
- investing_algorithm_framework/domain/data_provider.py +334 -0
- investing_algorithm_framework/domain/data_structures.py +42 -0
- investing_algorithm_framework/domain/decimal_parsing.py +40 -0
- investing_algorithm_framework/domain/exceptions.py +51 -1
- investing_algorithm_framework/domain/models/__init__.py +29 -14
- investing_algorithm_framework/domain/models/app_mode.py +34 -0
- investing_algorithm_framework/domain/models/base_model.py +3 -1
- investing_algorithm_framework/domain/models/data/__init__.py +7 -0
- investing_algorithm_framework/domain/models/data/data_source.py +222 -0
- investing_algorithm_framework/domain/models/data/data_type.py +46 -0
- investing_algorithm_framework/domain/models/event.py +35 -0
- investing_algorithm_framework/domain/models/market/__init__.py +5 -0
- investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
- investing_algorithm_framework/domain/models/order/__init__.py +3 -4
- investing_algorithm_framework/domain/models/order/order.py +243 -86
- investing_algorithm_framework/domain/models/order/order_status.py +2 -2
- investing_algorithm_framework/domain/models/order/order_type.py +1 -3
- investing_algorithm_framework/domain/models/portfolio/__init__.py +7 -2
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +134 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +37 -37
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +208 -0
- investing_algorithm_framework/domain/models/position/__init__.py +3 -2
- investing_algorithm_framework/domain/models/position/position.py +29 -0
- investing_algorithm_framework/domain/models/position/position_size.py +41 -0
- investing_algorithm_framework/domain/models/position/{position_cost.py → position_snapshot.py} +16 -8
- investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
- investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
- investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
- investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
- investing_algorithm_framework/domain/models/strategy_profile.py +33 -0
- investing_algorithm_framework/domain/models/time_frame.py +94 -98
- investing_algorithm_framework/domain/models/time_interval.py +33 -0
- investing_algorithm_framework/domain/models/time_unit.py +111 -2
- investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
- investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
- investing_algorithm_framework/domain/models/trade/__init__.py +11 -0
- investing_algorithm_framework/domain/models/trade/trade.py +389 -0
- investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
- investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
- investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
- investing_algorithm_framework/domain/order_executor.py +112 -0
- investing_algorithm_framework/domain/portfolio_provider.py +118 -0
- investing_algorithm_framework/domain/services/__init__.py +11 -0
- investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
- investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
- investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
- investing_algorithm_framework/domain/services/rounding_service.py +27 -0
- investing_algorithm_framework/domain/services/state_handler.py +38 -0
- investing_algorithm_framework/domain/strategy.py +1 -29
- investing_algorithm_framework/domain/utils/__init__.py +16 -4
- investing_algorithm_framework/domain/utils/csv.py +22 -0
- investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
- investing_algorithm_framework/domain/utils/dates.py +57 -0
- investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
- investing_algorithm_framework/domain/utils/polars.py +53 -0
- investing_algorithm_framework/domain/utils/random.py +29 -0
- investing_algorithm_framework/download_data.py +244 -0
- investing_algorithm_framework/infrastructure/__init__.py +39 -11
- investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
- investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1152 -0
- investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
- investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
- investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
- investing_algorithm_framework/infrastructure/database/sql_alchemy.py +87 -13
- investing_algorithm_framework/infrastructure/models/__init__.py +13 -4
- investing_algorithm_framework/infrastructure/models/decimal_parser.py +14 -0
- investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -2
- investing_algorithm_framework/infrastructure/models/order/order.py +73 -73
- investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
- investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
- investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +3 -2
- investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +37 -0
- investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +57 -3
- investing_algorithm_framework/infrastructure/models/position/__init__.py +2 -2
- investing_algorithm_framework/infrastructure/models/position/position.py +16 -11
- investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +23 -0
- investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
- investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +59 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -0
- investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
- investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
- investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
- investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
- investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
- investing_algorithm_framework/infrastructure/repositories/__init__.py +13 -5
- investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
- investing_algorithm_framework/infrastructure/repositories/order_repository.py +32 -19
- investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +2 -2
- investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +56 -0
- investing_algorithm_framework/infrastructure/repositories/position_repository.py +47 -4
- investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +21 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +85 -31
- investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
- investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
- investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
- investing_algorithm_framework/infrastructure/services/__init__.py +9 -2
- investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
- investing_algorithm_framework/infrastructure/services/aws/state_handler.py +193 -0
- investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
- investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
- investing_algorithm_framework/infrastructure/services/backtesting/__init__.py +9 -0
- investing_algorithm_framework/infrastructure/services/backtesting/backtest_service.py +2596 -0
- investing_algorithm_framework/infrastructure/services/backtesting/event_backtest_service.py +285 -0
- investing_algorithm_framework/infrastructure/services/backtesting/vector_backtest_service.py +468 -0
- investing_algorithm_framework/services/__init__.py +127 -10
- investing_algorithm_framework/services/configuration_service.py +95 -0
- investing_algorithm_framework/services/data_providers/__init__.py +5 -0
- investing_algorithm_framework/services/data_providers/data_provider_service.py +1058 -0
- investing_algorithm_framework/services/market_credential_service.py +40 -0
- investing_algorithm_framework/services/metrics/__init__.py +119 -0
- investing_algorithm_framework/services/metrics/alpha.py +0 -0
- investing_algorithm_framework/services/metrics/beta.py +0 -0
- investing_algorithm_framework/services/metrics/cagr.py +60 -0
- investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
- investing_algorithm_framework/services/metrics/drawdown.py +218 -0
- investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
- investing_algorithm_framework/services/metrics/exposure.py +210 -0
- investing_algorithm_framework/services/metrics/generate.py +358 -0
- investing_algorithm_framework/services/metrics/mean_daily_return.py +84 -0
- investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
- investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
- investing_algorithm_framework/services/metrics/recovery.py +113 -0
- investing_algorithm_framework/services/metrics/returns.py +452 -0
- investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
- investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
- investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
- investing_algorithm_framework/services/metrics/standard_deviation.py +156 -0
- investing_algorithm_framework/services/metrics/trades.py +473 -0
- investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
- investing_algorithm_framework/services/metrics/ulcer.py +0 -0
- investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
- investing_algorithm_framework/services/metrics/volatility.py +118 -0
- investing_algorithm_framework/services/metrics/win_rate.py +177 -0
- investing_algorithm_framework/services/order_service/__init__.py +9 -0
- investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
- investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
- investing_algorithm_framework/services/order_service/order_service.py +826 -0
- investing_algorithm_framework/services/portfolios/__init__.py +16 -0
- investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
- investing_algorithm_framework/services/{portfolio_configuration_service.py → portfolios/portfolio_configuration_service.py} +27 -12
- investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
- investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
- investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
- investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
- investing_algorithm_framework/services/positions/__init__.py +7 -0
- investing_algorithm_framework/services/positions/position_service.py +210 -0
- investing_algorithm_framework/services/positions/position_snapshot_service.py +18 -0
- investing_algorithm_framework/services/repository_service.py +8 -2
- investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
- investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +117 -0
- investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
- investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
- investing_algorithm_framework/services/trade_service/__init__.py +9 -0
- investing_algorithm_framework/services/trade_service/trade_service.py +1099 -0
- investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
- investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
- investing_algorithm_framework-7.25.6.dist-info/METADATA +535 -0
- investing_algorithm_framework-7.25.6.dist-info/RECORD +268 -0
- {investing_algorithm_framework-1.3.1.dist-info → investing_algorithm_framework-7.25.6.dist-info}/WHEEL +1 -2
- investing_algorithm_framework-7.25.6.dist-info/entry_points.txt +3 -0
- investing_algorithm_framework/app/algorithm.py +0 -410
- investing_algorithm_framework/domain/models/market_data/__init__.py +0 -11
- investing_algorithm_framework/domain/models/market_data/asset_price.py +0 -50
- investing_algorithm_framework/domain/models/market_data/ohlcv.py +0 -76
- investing_algorithm_framework/domain/models/market_data/order_book.py +0 -63
- investing_algorithm_framework/domain/models/market_data/ticker.py +0 -92
- investing_algorithm_framework/domain/models/order/order_fee.py +0 -45
- investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
- investing_algorithm_framework/domain/models/trading_time_frame.py +0 -205
- investing_algorithm_framework/domain/singleton.py +0 -9
- investing_algorithm_framework/infrastructure/models/order/order_fee.py +0 -21
- investing_algorithm_framework/infrastructure/models/position/position_cost.py +0 -32
- investing_algorithm_framework/infrastructure/repositories/order_fee_repository.py +0 -15
- investing_algorithm_framework/infrastructure/repositories/position_cost_repository.py +0 -16
- investing_algorithm_framework/infrastructure/services/market_service.py +0 -422
- investing_algorithm_framework/services/market_data_service.py +0 -75
- investing_algorithm_framework/services/order_service.py +0 -464
- investing_algorithm_framework/services/portfolio_service.py +0 -105
- investing_algorithm_framework/services/position_cost_service.py +0 -5
- investing_algorithm_framework/services/position_service.py +0 -50
- investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -219
- investing_algorithm_framework/setup_logging.py +0 -40
- investing_algorithm_framework-1.3.1.dist-info/AUTHORS.md +0 -8
- investing_algorithm_framework-1.3.1.dist-info/METADATA +0 -172
- investing_algorithm_framework-1.3.1.dist-info/RECORD +0 -103
- investing_algorithm_framework-1.3.1.dist-info/top_level.txt +0 -1
- {investing_algorithm_framework-1.3.1.dist-info → investing_algorithm_framework-7.25.6.dist-info}/LICENSE +0 -0
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
3
|
from sqlalchemy import create_engine, StaticPool
|
|
4
|
+
from sqlalchemy import inspect
|
|
4
5
|
from sqlalchemy.orm import DeclarativeBase, sessionmaker
|
|
5
6
|
|
|
6
7
|
from investing_algorithm_framework.domain import SQLALCHEMY_DATABASE_URI, \
|
|
7
8
|
OperationalException
|
|
8
9
|
|
|
9
10
|
Session = sessionmaker()
|
|
10
|
-
logger = logging.getLogger(
|
|
11
|
+
logger = logging.getLogger("investing_algorithm_framework")
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class SQLAlchemyAdapter:
|
|
@@ -19,18 +20,11 @@ class SQLAlchemyAdapter:
|
|
|
19
20
|
raise OperationalException("SQLALCHEMY_DATABASE_URI not set")
|
|
20
21
|
|
|
21
22
|
global Session
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
poolclass=StaticPool
|
|
28
|
-
)
|
|
29
|
-
else:
|
|
30
|
-
engine = create_engine(
|
|
31
|
-
app.config[SQLALCHEMY_DATABASE_URI],
|
|
32
|
-
)
|
|
33
|
-
|
|
23
|
+
engine = create_engine(
|
|
24
|
+
app.config[SQLALCHEMY_DATABASE_URI],
|
|
25
|
+
connect_args={'check_same_thread': False},
|
|
26
|
+
poolclass=StaticPool
|
|
27
|
+
)
|
|
34
28
|
Session.configure(bind=engine)
|
|
35
29
|
|
|
36
30
|
|
|
@@ -45,9 +39,89 @@ def setup_sqlalchemy(app, throw_exception_if_not_set=True):
|
|
|
45
39
|
return app
|
|
46
40
|
|
|
47
41
|
|
|
42
|
+
|
|
48
43
|
class SQLBaseModel(DeclarativeBase):
|
|
49
44
|
pass
|
|
50
45
|
|
|
51
46
|
|
|
52
47
|
def create_all_tables():
|
|
53
48
|
SQLBaseModel.metadata.create_all(bind=Session().bind)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
from sqlalchemy import event
|
|
52
|
+
from sqlalchemy.orm import mapper
|
|
53
|
+
from datetime import timezone
|
|
54
|
+
|
|
55
|
+
def clear_db(db_uri):
|
|
56
|
+
"""
|
|
57
|
+
Clear the database by dropping all tables.
|
|
58
|
+
This is useful for testing purposes.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
db_uri (str): The database URI to connect to.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
None
|
|
65
|
+
"""
|
|
66
|
+
# Drop all tables before deleting file
|
|
67
|
+
try:
|
|
68
|
+
engine = create_engine(db_uri)
|
|
69
|
+
inspector = inspect(engine)
|
|
70
|
+
if inspector.get_table_names():
|
|
71
|
+
logger.info("Dropping all tables in backtest database")
|
|
72
|
+
SQLBaseModel.metadata.drop_all(bind=engine)
|
|
73
|
+
except Exception as e:
|
|
74
|
+
logger.error(f"Error dropping tables: {e}")
|
|
75
|
+
|
|
76
|
+
# # Clear mappers (if using classical mappings)
|
|
77
|
+
# try:
|
|
78
|
+
# clear_mappers()
|
|
79
|
+
# except Exception:
|
|
80
|
+
# pass # ignore if not needed
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@event.listens_for(mapper, "load")
|
|
84
|
+
def attach_utc_timezone_on_load(target, context):
|
|
85
|
+
"""
|
|
86
|
+
For each model instance loaded from the database,
|
|
87
|
+
this function will check if one of the following attributes are
|
|
88
|
+
present: created_at, updated_at, closed_at, opened_at, triggered_at.
|
|
89
|
+
If so, it will check if these datetime
|
|
90
|
+
attributes are timezone-naive and, if so, will set them to UTC.
|
|
91
|
+
|
|
92
|
+
Its documented in the contributing guide (https://coding-kitties.github
|
|
93
|
+
.io/investing-algorithm-framework/Contributing%20Guide/contributing)
|
|
94
|
+
that each datetime attribute should be utc timezone-aware.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
target: The model instance being loaded from the database.
|
|
98
|
+
context: The context in which the event is being handled.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
None
|
|
102
|
+
"""
|
|
103
|
+
# This will apply to every model instance loaded from the DB
|
|
104
|
+
if hasattr(target, "created_at"):
|
|
105
|
+
dt = getattr(target, "created_at")
|
|
106
|
+
if dt and dt.tzinfo is None:
|
|
107
|
+
target.created_at = dt.replace(tzinfo=timezone.utc)
|
|
108
|
+
|
|
109
|
+
if hasattr(target, "updated_at"):
|
|
110
|
+
dt = getattr(target, "updated_at")
|
|
111
|
+
if dt and dt.tzinfo is None:
|
|
112
|
+
target.updated_at = dt.replace(tzinfo=timezone.utc)
|
|
113
|
+
|
|
114
|
+
if hasattr(target, "closed_at"):
|
|
115
|
+
dt = getattr(target, "closed_at")
|
|
116
|
+
if dt and dt.tzinfo is None:
|
|
117
|
+
target.closed_at = dt.replace(tzinfo=timezone.utc)
|
|
118
|
+
|
|
119
|
+
if hasattr(target, "opened_at"):
|
|
120
|
+
dt = getattr(target, "opened_at")
|
|
121
|
+
if dt and dt.tzinfo is None:
|
|
122
|
+
target.opened_at = dt.replace(tzinfo=timezone.utc)
|
|
123
|
+
|
|
124
|
+
if hasattr(target, "triggered_at"):
|
|
125
|
+
dt = getattr(target, "triggered_at")
|
|
126
|
+
if dt and dt.tzinfo is None:
|
|
127
|
+
target.triggered_at = dt.replace(tzinfo=timezone.utc)
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
from .order import SQLOrder,
|
|
2
|
-
from .portfolio import SQLPortfolio
|
|
3
|
-
from .position import SQLPosition,
|
|
1
|
+
from .order import SQLOrder, SQLOrderMetadata
|
|
2
|
+
from .portfolio import SQLPortfolio, SQLPortfolioSnapshot
|
|
3
|
+
from .position import SQLPosition, SQLPositionSnapshot
|
|
4
|
+
from .trades import SQLTrade, SQLTradeStopLoss, SQLTradeTakeProfit
|
|
4
5
|
|
|
5
6
|
__all__ = [
|
|
6
|
-
"SQLOrder",
|
|
7
|
+
"SQLOrder",
|
|
8
|
+
"SQLPosition",
|
|
9
|
+
"SQLPortfolio",
|
|
10
|
+
"SQLPositionSnapshot",
|
|
11
|
+
"SQLPortfolioSnapshot",
|
|
12
|
+
"SQLTrade",
|
|
13
|
+
"SQLTradeStopLoss",
|
|
14
|
+
"SQLTradeTakeProfit",
|
|
15
|
+
"SQLOrderMetadata",
|
|
7
16
|
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from decimal import Decimal, getcontext
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def count_number_of_decimals(value) -> int:
|
|
5
|
+
value = str(value)
|
|
6
|
+
if "." in value:
|
|
7
|
+
return len(value) - value.index(".") - 1
|
|
8
|
+
else:
|
|
9
|
+
return 0
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def parse_decimal(value) -> Decimal:
|
|
13
|
+
getcontext().prec = count_number_of_decimals(value)
|
|
14
|
+
return Decimal(value)
|
|
@@ -1,111 +1,105 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from datetime import datetime
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
3
|
|
|
4
|
-
from sqlalchemy import Column, Integer, String, DateTime,
|
|
4
|
+
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Float
|
|
5
5
|
from sqlalchemy.orm import relationship
|
|
6
6
|
|
|
7
|
-
from investing_algorithm_framework.domain
|
|
8
|
-
OrderSide, Order, OrderStatus
|
|
7
|
+
from investing_algorithm_framework.domain import OrderType, \
|
|
8
|
+
OrderSide, Order, OrderStatus
|
|
9
9
|
from investing_algorithm_framework.infrastructure.database import SQLBaseModel
|
|
10
10
|
from investing_algorithm_framework.infrastructure.models.model_extension \
|
|
11
11
|
import SQLAlchemyModelExtension
|
|
12
|
+
from investing_algorithm_framework.infrastructure.models.\
|
|
13
|
+
order_trade_association import order_trade_association
|
|
12
14
|
|
|
13
|
-
logger = logging.getLogger(
|
|
15
|
+
logger = logging.getLogger("investing_algorithm_framework")
|
|
14
16
|
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
def utcnow():
|
|
19
|
+
return datetime.now(tz=timezone.utc)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SQLOrder(Order, SQLBaseModel, SQLAlchemyModelExtension):
|
|
23
|
+
"""
|
|
24
|
+
SQLOrder model based on the Order domain model.
|
|
25
|
+
"""
|
|
17
26
|
__tablename__ = "orders"
|
|
18
27
|
id = Column(Integer, primary_key=True, unique=True)
|
|
19
28
|
external_id = Column(Integer)
|
|
20
29
|
target_symbol = Column(String)
|
|
21
30
|
trading_symbol = Column(String)
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
order_side = Column(String, nullable=False, default=OrderSide.BUY.value)
|
|
32
|
+
order_type = Column(String, nullable=False, default=OrderType.LIMIT.value)
|
|
33
|
+
trades = relationship(
|
|
34
|
+
'SQLTrade', secondary=order_trade_association, back_populates='orders'
|
|
35
|
+
)
|
|
24
36
|
price = Column(Float)
|
|
25
37
|
amount = Column(Float)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
cost = Column(Float)
|
|
29
|
-
status = Column(String)
|
|
38
|
+
remaining = Column(Float, default=None)
|
|
39
|
+
filled = Column(Float, default=None)
|
|
40
|
+
cost = Column(Float, default=0)
|
|
41
|
+
status = Column(String, default=OrderStatus.CREATED.value)
|
|
30
42
|
position_id = Column(Integer, ForeignKey('positions.id'))
|
|
31
43
|
position = relationship("SQLPosition", back_populates="orders")
|
|
32
|
-
created_at = Column(DateTime, default=
|
|
33
|
-
updated_at = Column(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
created_at = Column(DateTime(timezone=True), default=utcnow)
|
|
45
|
+
updated_at = Column(
|
|
46
|
+
DateTime(timezone=True), default=utcnow, onupdate=utcnow
|
|
47
|
+
)
|
|
48
|
+
order_fee = Column(Float, default=None)
|
|
49
|
+
order_fee_currency = Column(String, default=None)
|
|
50
|
+
order_fee_rate = Column(Float, default=None)
|
|
51
|
+
sell_order_metadata_id = Column(Integer, ForeignKey('orders.id'))
|
|
52
|
+
order_metadata = relationship(
|
|
53
|
+
'SQLOrderMetadata', back_populates='order'
|
|
42
54
|
)
|
|
43
55
|
|
|
44
|
-
def
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
position_id=None,
|
|
51
|
-
target_symbol=None,
|
|
52
|
-
trading_symbol=None,
|
|
53
|
-
external_id=None,
|
|
54
|
-
price=None,
|
|
55
|
-
created_at=None,
|
|
56
|
-
updated_at=None,
|
|
57
|
-
trade_closed_at=None,
|
|
58
|
-
trade_closed_price=None,
|
|
59
|
-
net_gain=0,
|
|
60
|
-
filled_amount=None,
|
|
61
|
-
remaining_amount=None,
|
|
62
|
-
cost=None,
|
|
63
|
-
fee=None
|
|
64
|
-
):
|
|
65
|
-
super().__init__(
|
|
66
|
-
target_symbol=target_symbol,
|
|
67
|
-
trading_symbol=trading_symbol,
|
|
68
|
-
type=type,
|
|
69
|
-
side=side,
|
|
70
|
-
status=status,
|
|
71
|
-
amount=amount,
|
|
72
|
-
price=price,
|
|
73
|
-
external_id=external_id,
|
|
74
|
-
position_id=position_id,
|
|
75
|
-
net_gain=net_gain,
|
|
76
|
-
trade_closed_at=trade_closed_at,
|
|
77
|
-
trade_closed_price=trade_closed_price,
|
|
78
|
-
created_at=created_at,
|
|
79
|
-
updated_at=updated_at,
|
|
80
|
-
filled_amount=filled_amount,
|
|
81
|
-
remaining_amount=remaining_amount,
|
|
82
|
-
cost=cost,
|
|
83
|
-
fee=fee
|
|
84
|
-
)
|
|
56
|
+
def update(self, data):
|
|
57
|
+
|
|
58
|
+
if "status" in data and data["status"] is not None:
|
|
59
|
+
data["status"] = OrderStatus.from_value(data["status"]).value
|
|
60
|
+
|
|
61
|
+
super().update(data)
|
|
85
62
|
|
|
86
63
|
@staticmethod
|
|
87
64
|
def from_order(order):
|
|
88
65
|
return SQLOrder(
|
|
89
66
|
external_id=order.external_id,
|
|
90
67
|
amount=order.get_amount(),
|
|
68
|
+
filled=order.get_filled(),
|
|
69
|
+
remaining=order.get_remaining(),
|
|
91
70
|
price=order.price,
|
|
92
|
-
|
|
93
|
-
|
|
71
|
+
order_type=order.get_order_type(),
|
|
72
|
+
order_side=order.get(),
|
|
94
73
|
status=order.get_status(),
|
|
95
74
|
target_symbol=order.get_target_symbol(),
|
|
96
75
|
trading_symbol=order.get_trading_symbol(),
|
|
97
76
|
created_at=order.get_created_at(),
|
|
98
77
|
updated_at=order.get_updated_at(),
|
|
99
|
-
trade_closed_at=order.get_trade_closed_at(),
|
|
100
|
-
trade_closed_price=order.get_trade_closed_price(),
|
|
101
|
-
net_gain=order.get_net_gain(),
|
|
102
78
|
)
|
|
103
79
|
|
|
104
80
|
@staticmethod
|
|
105
81
|
def from_ccxt_order(ccxt_order):
|
|
82
|
+
"""
|
|
83
|
+
Create an Order object from a CCXT order object
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
ccxt_order: CCXT order object
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Order: Order object
|
|
90
|
+
"""
|
|
106
91
|
status = OrderStatus.from_value(ccxt_order["status"])
|
|
107
92
|
target_symbol = ccxt_order.get("symbol").split("/")[0]
|
|
108
93
|
trading_symbol = ccxt_order.get("symbol").split("/")[1]
|
|
94
|
+
ccxt_fee = ccxt_order.get("fee", None)
|
|
95
|
+
order_fee = None
|
|
96
|
+
order_fee_rate = None
|
|
97
|
+
order_fee_currency = None
|
|
98
|
+
|
|
99
|
+
if ccxt_fee is not None:
|
|
100
|
+
order_fee = ccxt_fee.get("cost", None)
|
|
101
|
+
order_fee_rate = ccxt_fee.get("rate", None)
|
|
102
|
+
order_fee_currency = ccxt_fee.get("currency", None)
|
|
109
103
|
|
|
110
104
|
return Order(
|
|
111
105
|
external_id=ccxt_order.get("id", None),
|
|
@@ -114,11 +108,17 @@ class SQLOrder(SQLBaseModel, Order, SQLAlchemyModelExtension):
|
|
|
114
108
|
price=ccxt_order.get("price", None),
|
|
115
109
|
amount=ccxt_order.get("amount", None),
|
|
116
110
|
status=status,
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
111
|
+
order_type=ccxt_order.get("type", None),
|
|
112
|
+
order_side=ccxt_order.get("side", None),
|
|
113
|
+
filled=ccxt_order.get("filled", None),
|
|
114
|
+
remaining=ccxt_order.get("remaining", None),
|
|
121
115
|
cost=ccxt_order.get("cost", None),
|
|
122
|
-
|
|
116
|
+
order_fee=order_fee,
|
|
117
|
+
order_fee_rate=order_fee_rate,
|
|
118
|
+
order_fee_currency=order_fee_currency,
|
|
123
119
|
created_at=ccxt_order.get("datetime", None),
|
|
124
120
|
)
|
|
121
|
+
|
|
122
|
+
def __lt__(self, other):
|
|
123
|
+
# Define the less-than comparison based on created_at attribute
|
|
124
|
+
return self.created_at < other.created_at
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from sqlalchemy import Column, Integer, ForeignKey, Float
|
|
4
|
+
from sqlalchemy.orm import relationship
|
|
5
|
+
|
|
6
|
+
from investing_algorithm_framework.infrastructure.database import SQLBaseModel
|
|
7
|
+
from investing_algorithm_framework.infrastructure.models.model_extension \
|
|
8
|
+
import SQLAlchemyModelExtension
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger("investing_algorithm_framework")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SQLOrderMetadata(SQLBaseModel, SQLAlchemyModelExtension):
|
|
14
|
+
__tablename__ = "sql_order_metadata"
|
|
15
|
+
id = Column(Integer, primary_key=True, unique=True)
|
|
16
|
+
order_id = Column(Integer, ForeignKey('orders.id'))
|
|
17
|
+
order = relationship('SQLOrder', back_populates='order_metadata')
|
|
18
|
+
trade_id = Column(Integer)
|
|
19
|
+
stop_loss_id = Column(Integer)
|
|
20
|
+
take_profit_id = Column(Integer)
|
|
21
|
+
amount = Column(Float)
|
|
22
|
+
amount_pending = Column(Float)
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
order_id,
|
|
27
|
+
amount,
|
|
28
|
+
amount_pending,
|
|
29
|
+
trade_id=None,
|
|
30
|
+
stop_loss_id=None,
|
|
31
|
+
take_profit_id=None,
|
|
32
|
+
):
|
|
33
|
+
self.order_id = order_id
|
|
34
|
+
self.trade_id = trade_id
|
|
35
|
+
self.stop_loss_id = stop_loss_id
|
|
36
|
+
self.take_profit_id = take_profit_id
|
|
37
|
+
self.amount = amount
|
|
38
|
+
self.amount_pending = amount_pending
|
|
39
|
+
|
|
40
|
+
def __repr__(self):
|
|
41
|
+
return f"<SQLOrderMetadata(id={self.id}, order_id={self.order_id}, " \
|
|
42
|
+
f"trade_id={self.trade_id}, stop_loss_id={self.stop_loss_id}, "\
|
|
43
|
+
f"take_profit_id={self.take_profit_id}, amount={self.amount}, "\
|
|
44
|
+
f"amount_pending={self.amount_pending})>"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from sqlalchemy import Table, Column, Integer, ForeignKey
|
|
2
|
+
from investing_algorithm_framework.infrastructure.database import SQLBaseModel
|
|
3
|
+
|
|
4
|
+
# Association table
|
|
5
|
+
order_trade_association = Table(
|
|
6
|
+
'order_trade', # Table name
|
|
7
|
+
SQLBaseModel.metadata,
|
|
8
|
+
Column('order_id', Integer, ForeignKey('orders.id'), primary_key=True),
|
|
9
|
+
Column('trade_id', Integer, ForeignKey('trades.id'), primary_key=True)
|
|
10
|
+
)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from sqlalchemy import Column, Integer, String, DateTime, Float
|
|
2
|
+
from sqlalchemy.orm import relationship
|
|
3
|
+
|
|
4
|
+
from investing_algorithm_framework.domain import PortfolioSnapshot
|
|
5
|
+
from investing_algorithm_framework.infrastructure.database import SQLBaseModel
|
|
6
|
+
from investing_algorithm_framework.infrastructure.models.model_extension \
|
|
7
|
+
import SQLAlchemyModelExtension
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SQLPortfolioSnapshot(
|
|
11
|
+
PortfolioSnapshot, SQLBaseModel, SQLAlchemyModelExtension
|
|
12
|
+
):
|
|
13
|
+
"""
|
|
14
|
+
SQLAlchemy model for portfolio snapshots.
|
|
15
|
+
|
|
16
|
+
Portfolio snapshots represent the state of a portfolio at a specific
|
|
17
|
+
point in time.
|
|
18
|
+
"""
|
|
19
|
+
__tablename__ = "portfolio_snapshots"
|
|
20
|
+
id = Column(Integer, primary_key=True)
|
|
21
|
+
portfolio_id = Column(String, nullable=False)
|
|
22
|
+
trading_symbol = Column(String, nullable=False)
|
|
23
|
+
pending_value = Column(Float, nullable=False, default=0)
|
|
24
|
+
unallocated = Column(Float, nullable=False, default=0)
|
|
25
|
+
net_size = Column(Float, nullable=False, default=0)
|
|
26
|
+
total_net_gain = Column(Float, nullable=False, default=0)
|
|
27
|
+
total_revenue = Column(Float, nullable=False, default=0)
|
|
28
|
+
total_cost = Column(Float, nullable=False, default=0)
|
|
29
|
+
total_value = Column(Float, nullable=False, default=0)
|
|
30
|
+
cash_flow = Column(Float, nullable=False, default=0)
|
|
31
|
+
created_at = Column(DateTime, nullable=False, default=0)
|
|
32
|
+
position_snapshots = relationship(
|
|
33
|
+
"SQLPositionSnapshot",
|
|
34
|
+
back_populates="portfolio_snapshot",
|
|
35
|
+
lazy="dynamic",
|
|
36
|
+
cascade="all,delete",
|
|
37
|
+
)
|
investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py}
RENAMED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
from
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
|
+
|
|
3
|
+
from sqlalchemy import Column, Integer, String, DateTime, Float, Boolean
|
|
2
4
|
from sqlalchemy import UniqueConstraint
|
|
3
5
|
from sqlalchemy.orm import relationship
|
|
4
6
|
from sqlalchemy.orm import validates
|
|
@@ -9,7 +11,7 @@ from investing_algorithm_framework.infrastructure.models.model_extension \
|
|
|
9
11
|
import SQLAlchemyModelExtension
|
|
10
12
|
|
|
11
13
|
|
|
12
|
-
class SQLPortfolio(
|
|
14
|
+
class SQLPortfolio(Portfolio, SQLBaseModel, SQLAlchemyModelExtension):
|
|
13
15
|
__tablename__ = "portfolios"
|
|
14
16
|
id = Column(Integer, primary_key=True)
|
|
15
17
|
identifier = Column(String, nullable=False, unique=True)
|
|
@@ -18,8 +20,10 @@ class SQLPortfolio(SQLBaseModel, Portfolio, SQLAlchemyModelExtension):
|
|
|
18
20
|
total_revenue = Column(Float, nullable=False, default=0)
|
|
19
21
|
total_cost = Column(Float, nullable=False, default=0)
|
|
20
22
|
total_net_gain = Column(Float, nullable=False, default=0)
|
|
23
|
+
total_trade_volume = Column(Float, nullable=False, default=0)
|
|
21
24
|
net_size = Column(Float, nullable=False, default=0)
|
|
22
25
|
unallocated = Column(Float, nullable=False, default=0)
|
|
26
|
+
initial_balance = Column(Float, nullable=True)
|
|
23
27
|
market = Column(String, nullable=False)
|
|
24
28
|
positions = relationship(
|
|
25
29
|
"SQLPosition",
|
|
@@ -27,6 +31,10 @@ class SQLPortfolio(SQLBaseModel, Portfolio, SQLAlchemyModelExtension):
|
|
|
27
31
|
lazy="dynamic",
|
|
28
32
|
cascade="all,delete",
|
|
29
33
|
)
|
|
34
|
+
created_at = Column(DateTime, nullable=False)
|
|
35
|
+
updated_at = Column(DateTime, nullable=False)
|
|
36
|
+
initialized = Column(Boolean, nullable=False, default=False)
|
|
37
|
+
|
|
30
38
|
__table_args__ = (
|
|
31
39
|
UniqueConstraint(
|
|
32
40
|
'trading_symbol',
|
|
@@ -43,11 +51,27 @@ class SQLPortfolio(SQLBaseModel, Portfolio, SQLAlchemyModelExtension):
|
|
|
43
51
|
raise ValueError("{} is write-once".format(key))
|
|
44
52
|
return value
|
|
45
53
|
|
|
46
|
-
def __init__(
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
trading_symbol,
|
|
57
|
+
market,
|
|
58
|
+
unallocated,
|
|
59
|
+
initialized,
|
|
60
|
+
initial_balance=None,
|
|
61
|
+
identifier=None,
|
|
62
|
+
created_at=None,
|
|
63
|
+
updated_at=None,
|
|
64
|
+
):
|
|
47
65
|
|
|
48
66
|
if identifier is None:
|
|
49
67
|
identifier = market
|
|
50
68
|
|
|
69
|
+
if created_at is None:
|
|
70
|
+
created_at = datetime.now(tz=timezone.utc)
|
|
71
|
+
|
|
72
|
+
if updated_at is None:
|
|
73
|
+
updated_at = datetime.now(tz=timezone.utc)
|
|
74
|
+
|
|
51
75
|
super().__init__(
|
|
52
76
|
trading_symbol=trading_symbol,
|
|
53
77
|
market=market,
|
|
@@ -57,4 +81,34 @@ class SQLPortfolio(SQLBaseModel, Portfolio, SQLAlchemyModelExtension):
|
|
|
57
81
|
realized=0,
|
|
58
82
|
total_revenue=0,
|
|
59
83
|
total_cost=0,
|
|
84
|
+
created_at=created_at,
|
|
85
|
+
updated_at=updated_at,
|
|
86
|
+
initialized=initialized,
|
|
87
|
+
initial_balance=initial_balance,
|
|
60
88
|
)
|
|
89
|
+
|
|
90
|
+
def update(self, data):
|
|
91
|
+
|
|
92
|
+
if "net_size" in data:
|
|
93
|
+
self.net_size = data.pop("net_size")
|
|
94
|
+
|
|
95
|
+
if "unallocated" in data:
|
|
96
|
+
self.unallocated = data.pop("unallocated")
|
|
97
|
+
|
|
98
|
+
if "realized" in data:
|
|
99
|
+
self.realized = data.pop("realized")
|
|
100
|
+
|
|
101
|
+
if "total_revenue" in data:
|
|
102
|
+
self.total_revenue = data.pop("total_revenue")
|
|
103
|
+
|
|
104
|
+
if "total_trade_volume" in data:
|
|
105
|
+
self.total_trade_volume = data.pop("total_trade_volume")
|
|
106
|
+
|
|
107
|
+
if "total_cost" in data:
|
|
108
|
+
self.total_cost = data.pop("total_cost")
|
|
109
|
+
|
|
110
|
+
if "total_net_gain" in data:
|
|
111
|
+
self.total_net_gain = data.pop("total_net_gain")
|
|
112
|
+
|
|
113
|
+
super().update(data)
|
|
114
|
+
return self
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
from sqlalchemy import Column, Integer, String,
|
|
1
|
+
from sqlalchemy import Column, Integer, String, ForeignKey, Float
|
|
2
2
|
from sqlalchemy import UniqueConstraint
|
|
3
3
|
from sqlalchemy.orm import relationship, validates
|
|
4
4
|
|
|
5
|
+
from investing_algorithm_framework.domain import Position
|
|
5
6
|
from investing_algorithm_framework.infrastructure.database import SQLBaseModel
|
|
6
7
|
from investing_algorithm_framework.infrastructure.models.model_extension \
|
|
7
8
|
import SQLAlchemyModelExtension
|
|
8
|
-
from investing_algorithm_framework.domain import Position
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class SQLPosition(SQLBaseModel, Position, SQLAlchemyModelExtension):
|
|
@@ -13,18 +13,13 @@ class SQLPosition(SQLBaseModel, Position, SQLAlchemyModelExtension):
|
|
|
13
13
|
id = Column(Integer, primary_key=True, unique=True)
|
|
14
14
|
symbol = Column(String)
|
|
15
15
|
amount = Column(Float)
|
|
16
|
+
cost = Column(Float)
|
|
16
17
|
orders = relationship(
|
|
17
18
|
"SQLOrder",
|
|
18
19
|
back_populates="position",
|
|
19
20
|
lazy="dynamic",
|
|
20
21
|
cascade="all, delete-orphan"
|
|
21
22
|
)
|
|
22
|
-
position_costs = relationship(
|
|
23
|
-
"SQLPositionCost",
|
|
24
|
-
back_populates="position",
|
|
25
|
-
lazy="dynamic",
|
|
26
|
-
cascade="all, delete-orphan"
|
|
27
|
-
)
|
|
28
23
|
portfolio_id = Column(Integer, ForeignKey('portfolios.id'))
|
|
29
24
|
portfolio = relationship("SQLPortfolio", back_populates="positions")
|
|
30
25
|
__table_args__ = (
|
|
@@ -32,19 +27,19 @@ class SQLPosition(SQLBaseModel, Position, SQLAlchemyModelExtension):
|
|
|
32
27
|
'symbol', 'portfolio_id', name='_symbol_portfolio_uc'
|
|
33
28
|
),
|
|
34
29
|
)
|
|
35
|
-
_cost = 0
|
|
36
30
|
|
|
37
31
|
def __init__(
|
|
38
32
|
self,
|
|
39
33
|
symbol,
|
|
40
34
|
amount=0,
|
|
41
|
-
|
|
35
|
+
cost=0,
|
|
36
|
+
portfolio_id=None,
|
|
42
37
|
):
|
|
43
38
|
super(SQLPosition, self).__init__()
|
|
44
39
|
self.symbol = symbol
|
|
45
40
|
self.amount = amount
|
|
46
41
|
self.portfolio_id = portfolio_id
|
|
47
|
-
self.cost =
|
|
42
|
+
self.cost = cost
|
|
48
43
|
|
|
49
44
|
@validates('id', 'symbol')
|
|
50
45
|
def _write_once(self, key, value):
|
|
@@ -53,6 +48,16 @@ class SQLPosition(SQLBaseModel, Position, SQLAlchemyModelExtension):
|
|
|
53
48
|
raise ValueError("{} is write-once".format(key))
|
|
54
49
|
return value
|
|
55
50
|
|
|
51
|
+
def update(self, data):
|
|
52
|
+
|
|
53
|
+
if 'amount' in data:
|
|
54
|
+
self.amount = data.pop('amount')
|
|
55
|
+
|
|
56
|
+
if 'cost' in data:
|
|
57
|
+
self.cost = data.pop('cost')
|
|
58
|
+
|
|
59
|
+
super(SQLPosition, self).update(data)
|
|
60
|
+
|
|
56
61
|
@property
|
|
57
62
|
def ccxt_symbol(self):
|
|
58
63
|
return f"{self.symbol}/{self.portfolio.trading_symbol}"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from sqlalchemy import Column, Integer, String, ForeignKey, Float
|
|
2
|
+
from sqlalchemy.orm import relationship
|
|
3
|
+
|
|
4
|
+
from investing_algorithm_framework.domain import PositionSnapshot
|
|
5
|
+
from investing_algorithm_framework.infrastructure.database import SQLBaseModel
|
|
6
|
+
from investing_algorithm_framework.infrastructure.models.model_extension \
|
|
7
|
+
import SQLAlchemyModelExtension
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SQLPositionSnapshot(
|
|
11
|
+
SQLBaseModel, PositionSnapshot, SQLAlchemyModelExtension
|
|
12
|
+
):
|
|
13
|
+
__tablename__ = "position_snapshots"
|
|
14
|
+
id = Column(Integer, primary_key=True, unique=True)
|
|
15
|
+
symbol = Column(String)
|
|
16
|
+
amount = Column(Float)
|
|
17
|
+
cost = Column(Float)
|
|
18
|
+
portfolio_snapshot_id = Column(
|
|
19
|
+
Integer, ForeignKey('portfolio_snapshots.id')
|
|
20
|
+
)
|
|
21
|
+
portfolio_snapshot = relationship(
|
|
22
|
+
"SQLPortfolioSnapshot", back_populates="position_snapshots"
|
|
23
|
+
)
|