investing-algorithm-framework 3.7.0__py3-none-any.whl → 7.19.15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of investing-algorithm-framework might be problematic. Click here for more details.
- investing_algorithm_framework/__init__.py +168 -45
- investing_algorithm_framework/app/__init__.py +32 -1
- investing_algorithm_framework/app/algorithm/__init__.py +7 -0
- investing_algorithm_framework/app/algorithm/algorithm.py +239 -0
- investing_algorithm_framework/app/algorithm/algorithm_factory.py +114 -0
- investing_algorithm_framework/app/analysis/__init__.py +15 -0
- investing_algorithm_framework/app/analysis/backtest_data_ranges.py +121 -0
- investing_algorithm_framework/app/analysis/backtest_utils.py +107 -0
- investing_algorithm_framework/app/analysis/permutation.py +116 -0
- investing_algorithm_framework/app/analysis/ranking.py +297 -0
- investing_algorithm_framework/app/app.py +1933 -589
- investing_algorithm_framework/app/app_hook.py +28 -0
- investing_algorithm_framework/app/context.py +1725 -0
- investing_algorithm_framework/app/eventloop.py +590 -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 +4 -2
- investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
- investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +1 -1
- investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
- investing_algorithm_framework/app/strategy.py +664 -84
- investing_algorithm_framework/app/task.py +5 -3
- investing_algorithm_framework/app/web/__init__.py +2 -1
- investing_algorithm_framework/app/web/create_app.py +4 -2
- investing_algorithm_framework/cli/__init__.py +0 -0
- investing_algorithm_framework/cli/cli.py +226 -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/create_app.py +40 -6
- investing_algorithm_framework/dependency_container.py +72 -56
- investing_algorithm_framework/domain/__init__.py +71 -47
- investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
- investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
- investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
- investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
- investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
- investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
- investing_algorithm_framework/domain/backtesting/backtest_run.py +605 -0
- investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
- investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
- investing_algorithm_framework/domain/config.py +59 -91
- investing_algorithm_framework/domain/constants.py +13 -38
- investing_algorithm_framework/domain/data_provider.py +334 -0
- investing_algorithm_framework/domain/data_structures.py +3 -2
- investing_algorithm_framework/domain/exceptions.py +51 -1
- investing_algorithm_framework/domain/models/__init__.py +17 -12
- investing_algorithm_framework/domain/models/data/__init__.py +7 -0
- investing_algorithm_framework/domain/models/data/data_source.py +214 -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/market_credential.py +55 -1
- investing_algorithm_framework/domain/models/order/order.py +77 -83
- 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/portfolio.py +81 -3
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +26 -3
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +108 -11
- investing_algorithm_framework/domain/models/position/__init__.py +2 -1
- investing_algorithm_framework/domain/models/position/position.py +12 -0
- investing_algorithm_framework/domain/models/position/position_size.py +41 -0
- 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 +19 -151
- investing_algorithm_framework/domain/models/time_frame.py +37 -0
- investing_algorithm_framework/domain/models/time_interval.py +33 -0
- investing_algorithm_framework/domain/models/time_unit.py +66 -2
- investing_algorithm_framework/domain/models/trade/__init__.py +8 -1
- investing_algorithm_framework/domain/models/trade/trade.py +295 -171
- investing_algorithm_framework/domain/models/trade/trade_status.py +9 -2
- 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 +2 -9
- investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +0 -6
- 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 +12 -7
- 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 +108 -0
- investing_algorithm_framework/infrastructure/__init__.py +31 -18
- investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
- investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1143 -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 +86 -12
- investing_algorithm_framework/infrastructure/models/__init__.py +6 -11
- investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -1
- investing_algorithm_framework/infrastructure/models/order/order.py +35 -49
- 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 +1 -1
- investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +8 -0
- investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +17 -5
- 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 +8 -0
- investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
- investing_algorithm_framework/infrastructure/repositories/order_repository.py +5 -0
- investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +1 -1
- investing_algorithm_framework/infrastructure/repositories/position_repository.py +11 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +81 -27
- 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 +4 -4
- investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
- investing_algorithm_framework/infrastructure/services/aws/state_handler.py +113 -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/services/__init__.py +113 -16
- investing_algorithm_framework/services/backtesting/__init__.py +0 -7
- investing_algorithm_framework/services/backtesting/backtest_service.py +566 -359
- investing_algorithm_framework/services/configuration_service.py +77 -11
- investing_algorithm_framework/services/data_providers/__init__.py +5 -0
- investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
- investing_algorithm_framework/services/market_credential_service.py +16 -1
- investing_algorithm_framework/services/metrics/__init__.py +114 -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 +181 -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 +83 -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 +157 -0
- investing_algorithm_framework/services/metrics/trades.py +500 -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 +97 -0
- investing_algorithm_framework/services/metrics/win_rate.py +177 -0
- investing_algorithm_framework/services/order_service/__init__.py +3 -1
- investing_algorithm_framework/services/order_service/order_backtest_service.py +76 -89
- investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
- investing_algorithm_framework/services/order_service/order_service.py +407 -326
- investing_algorithm_framework/services/portfolios/__init__.py +3 -1
- investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +37 -3
- investing_algorithm_framework/services/portfolios/portfolio_configuration_service.py +22 -8
- investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
- investing_algorithm_framework/services/portfolios/portfolio_service.py +96 -28
- investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +97 -28
- investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +116 -313
- investing_algorithm_framework/services/positions/__init__.py +7 -0
- investing_algorithm_framework/services/positions/position_service.py +210 -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 +113 -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 +7 -1
- investing_algorithm_framework/services/trade_service/trade_service.py +1013 -315
- 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.19.15.dist-info/METADATA +537 -0
- investing_algorithm_framework-7.19.15.dist-info/RECORD +263 -0
- investing_algorithm_framework-7.19.15.dist-info/entry_points.txt +3 -0
- investing_algorithm_framework/app/algorithm.py +0 -1105
- investing_algorithm_framework/domain/graphs.py +0 -382
- investing_algorithm_framework/domain/metrics/__init__.py +0 -6
- investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -11
- investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -43
- investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -120
- investing_algorithm_framework/domain/models/backtesting/backtest_report.py +0 -580
- investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -243
- investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
- investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
- investing_algorithm_framework/domain/services/market_data_sources.py +0 -344
- investing_algorithm_framework/domain/services/market_service.py +0 -153
- investing_algorithm_framework/domain/singleton.py +0 -9
- investing_algorithm_framework/domain/utils/backtesting.py +0 -472
- investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -12
- investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -559
- investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -254
- investing_algorithm_framework/infrastructure/models/market_data_sources/us_treasury_yield.py +0 -47
- investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -5
- investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -455
- investing_algorithm_framework/infrastructure/services/performance_service/__init__.py +0 -7
- investing_algorithm_framework/infrastructure/services/performance_service/backtest_performance_service.py +0 -2
- investing_algorithm_framework/infrastructure/services/performance_service/performance_service.py +0 -350
- investing_algorithm_framework/services/backtesting/backtest_report_writer_service.py +0 -53
- investing_algorithm_framework/services/backtesting/graphs.py +0 -61
- investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -8
- investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -150
- investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -189
- investing_algorithm_framework/services/position_service.py +0 -31
- investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -264
- investing_algorithm_framework-3.7.0.dist-info/METADATA +0 -339
- investing_algorithm_framework-3.7.0.dist-info/RECORD +0 -147
- /investing_algorithm_framework/{domain → services}/metrics/price_efficiency.py +0 -0
- /investing_algorithm_framework/services/{position_snapshot_service.py → positions/position_snapshot_service.py} +0 -0
- {investing_algorithm_framework-3.7.0.dist-info → investing_algorithm_framework-7.19.15.dist-info}/LICENSE +0 -0
- {investing_algorithm_framework-3.7.0.dist-info → investing_algorithm_framework-7.19.15.dist-info}/WHEEL +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
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, \
|
|
@@ -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,10 +1,7 @@
|
|
|
1
|
-
from .
|
|
2
|
-
CCXTTickerMarketDataSource, CCXTOHLCVMarketDataSource, \
|
|
3
|
-
CCXTOHLCVBacktestMarketDataSource, CSVOHLCVMarketDataSource, \
|
|
4
|
-
CSVTickerMarketDataSource
|
|
5
|
-
from .order import SQLOrder
|
|
1
|
+
from .order import SQLOrder, SQLOrderMetadata
|
|
6
2
|
from .portfolio import SQLPortfolio, SQLPortfolioSnapshot
|
|
7
3
|
from .position import SQLPosition, SQLPositionSnapshot
|
|
4
|
+
from .trades import SQLTrade, SQLTradeStopLoss, SQLTradeTakeProfit
|
|
8
5
|
|
|
9
6
|
__all__ = [
|
|
10
7
|
"SQLOrder",
|
|
@@ -12,10 +9,8 @@ __all__ = [
|
|
|
12
9
|
"SQLPortfolio",
|
|
13
10
|
"SQLPositionSnapshot",
|
|
14
11
|
"SQLPortfolioSnapshot",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"CSVTickerMarketDataSource",
|
|
20
|
-
"CSVOHLCVMarketDataSource",
|
|
12
|
+
"SQLTrade",
|
|
13
|
+
"SQLTradeStopLoss",
|
|
14
|
+
"SQLTradeTakeProfit",
|
|
15
|
+
"SQLOrderMetadata",
|
|
21
16
|
]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from datetime import datetime
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
3
|
|
|
4
4
|
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Float
|
|
5
5
|
from sqlalchemy.orm import relationship
|
|
@@ -9,11 +9,20 @@ from investing_algorithm_framework.domain import OrderType, \
|
|
|
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
15
|
logger = logging.getLogger("investing_algorithm_framework")
|
|
14
16
|
|
|
15
17
|
|
|
18
|
+
def utcnow():
|
|
19
|
+
return datetime.now(tz=timezone.utc)
|
|
20
|
+
|
|
21
|
+
|
|
16
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)
|
|
@@ -21,59 +30,33 @@ class SQLOrder(Order, SQLBaseModel, SQLAlchemyModelExtension):
|
|
|
21
30
|
trading_symbol = Column(String)
|
|
22
31
|
order_side = Column(String, nullable=False, default=OrderSide.BUY.value)
|
|
23
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
|
-
trade_closed_amount = Column(Float, default=None)
|
|
37
|
-
net_gain = Column(Float, default=0)
|
|
44
|
+
created_at = Column(DateTime(timezone=True), default=utcnow)
|
|
45
|
+
updated_at = Column(
|
|
46
|
+
DateTime(timezone=True), default=utcnow, onupdate=utcnow
|
|
47
|
+
)
|
|
38
48
|
order_fee = Column(Float, default=None)
|
|
39
|
-
order_fee_currency = Column(String)
|
|
49
|
+
order_fee_currency = Column(String, default=None)
|
|
40
50
|
order_fee_rate = Column(Float, default=None)
|
|
41
|
-
|
|
51
|
+
sell_order_metadata_id = Column(Integer, ForeignKey('orders.id'))
|
|
52
|
+
order_metadata = relationship(
|
|
53
|
+
'SQLOrderMetadata', back_populates='order'
|
|
54
|
+
)
|
|
42
55
|
|
|
43
56
|
def update(self, data):
|
|
44
57
|
|
|
45
|
-
if 'amount' in data and data['amount'] is not None:
|
|
46
|
-
amount = data.pop('amount')
|
|
47
|
-
self.amount = amount
|
|
48
|
-
|
|
49
|
-
if 'price' in data and data['price'] is not None:
|
|
50
|
-
price = data.pop('price')
|
|
51
|
-
self.price = price
|
|
52
|
-
|
|
53
|
-
if 'filled' in data and data['filled'] is not None:
|
|
54
|
-
filled = data.pop('filled')
|
|
55
|
-
self.filled = filled
|
|
56
|
-
|
|
57
|
-
if 'remaining' in data and data['remaining'] is not None:
|
|
58
|
-
remaining = data.pop('remaining')
|
|
59
|
-
self.remaining = remaining
|
|
60
|
-
|
|
61
|
-
if 'net_gain' in data and data['net_gain'] is not None:
|
|
62
|
-
net_gain = data.pop('net_gain')
|
|
63
|
-
self.net_gain = net_gain
|
|
64
|
-
|
|
65
|
-
if 'trade_closed_price' in data and \
|
|
66
|
-
data['trade_closed_price'] is not None:
|
|
67
|
-
trade_closed_price = data.pop('trade_closed_price')
|
|
68
|
-
self.trade_closed_price = trade_closed_price
|
|
69
|
-
|
|
70
|
-
if 'trade_closed_amount' in data and \
|
|
71
|
-
data['trade_closed_amount'] is not None:
|
|
72
|
-
trade_closed_amount = data.pop('trade_closed_amount')
|
|
73
|
-
self.trade_closed_amount = trade_closed_amount
|
|
74
|
-
|
|
75
58
|
if "status" in data and data["status"] is not None:
|
|
76
|
-
|
|
59
|
+
data["status"] = OrderStatus.from_value(data["status"]).value
|
|
77
60
|
|
|
78
61
|
super().update(data)
|
|
79
62
|
|
|
@@ -82,6 +65,8 @@ class SQLOrder(Order, SQLBaseModel, SQLAlchemyModelExtension):
|
|
|
82
65
|
return SQLOrder(
|
|
83
66
|
external_id=order.external_id,
|
|
84
67
|
amount=order.get_amount(),
|
|
68
|
+
filled=order.get_filled(),
|
|
69
|
+
remaining=order.get_remaining(),
|
|
85
70
|
price=order.price,
|
|
86
71
|
order_type=order.get_order_type(),
|
|
87
72
|
order_side=order.get(),
|
|
@@ -90,17 +75,18 @@ class SQLOrder(Order, SQLBaseModel, SQLAlchemyModelExtension):
|
|
|
90
75
|
trading_symbol=order.get_trading_symbol(),
|
|
91
76
|
created_at=order.get_created_at(),
|
|
92
77
|
updated_at=order.get_updated_at(),
|
|
93
|
-
trade_closed_at=order.get_trade_closed_at(),
|
|
94
|
-
trade_closed_price=order.get_trade_closed_price(),
|
|
95
|
-
trade_closed_amount=order.get_trade_closed_amount(),
|
|
96
|
-
net_gain=order.get_net_gain(),
|
|
97
78
|
)
|
|
98
79
|
|
|
99
80
|
@staticmethod
|
|
100
81
|
def from_ccxt_order(ccxt_order):
|
|
101
82
|
"""
|
|
102
83
|
Create an Order object from a CCXT order object
|
|
103
|
-
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
ccxt_order: CCXT order object
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Order: Order object
|
|
104
90
|
"""
|
|
105
91
|
status = OrderStatus.from_value(ccxt_order["status"])
|
|
106
92
|
target_symbol = ccxt_order.get("symbol").split("/")[0]
|
|
@@ -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
|
+
)
|
|
@@ -10,15 +10,23 @@ from investing_algorithm_framework.infrastructure.models.model_extension \
|
|
|
10
10
|
class SQLPortfolioSnapshot(
|
|
11
11
|
PortfolioSnapshot, SQLBaseModel, SQLAlchemyModelExtension
|
|
12
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
|
+
"""
|
|
13
19
|
__tablename__ = "portfolio_snapshots"
|
|
14
20
|
id = Column(Integer, primary_key=True)
|
|
15
21
|
portfolio_id = Column(String, nullable=False)
|
|
16
22
|
trading_symbol = Column(String, nullable=False)
|
|
17
23
|
pending_value = Column(Float, nullable=False, default=0)
|
|
18
24
|
unallocated = Column(Float, nullable=False, default=0)
|
|
25
|
+
net_size = Column(Float, nullable=False, default=0)
|
|
19
26
|
total_net_gain = Column(Float, nullable=False, default=0)
|
|
20
27
|
total_revenue = Column(Float, nullable=False, default=0)
|
|
21
28
|
total_cost = Column(Float, nullable=False, default=0)
|
|
29
|
+
total_value = Column(Float, nullable=False, default=0)
|
|
22
30
|
cash_flow = Column(Float, nullable=False, default=0)
|
|
23
31
|
created_at = Column(DateTime, nullable=False, default=0)
|
|
24
32
|
position_snapshots = relationship(
|
investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from datetime import datetime
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
2
|
|
|
3
|
-
from sqlalchemy import Column, Integer, String, DateTime, Float
|
|
3
|
+
from sqlalchemy import Column, Integer, String, DateTime, Float, Boolean
|
|
4
4
|
from sqlalchemy import UniqueConstraint
|
|
5
5
|
from sqlalchemy.orm import relationship
|
|
6
6
|
from sqlalchemy.orm import validates
|
|
@@ -23,6 +23,7 @@ class SQLPortfolio(Portfolio, SQLBaseModel, SQLAlchemyModelExtension):
|
|
|
23
23
|
total_trade_volume = Column(Float, nullable=False, default=0)
|
|
24
24
|
net_size = Column(Float, nullable=False, default=0)
|
|
25
25
|
unallocated = Column(Float, nullable=False, default=0)
|
|
26
|
+
initial_balance = Column(Float, nullable=True)
|
|
26
27
|
market = Column(String, nullable=False)
|
|
27
28
|
positions = relationship(
|
|
28
29
|
"SQLPosition",
|
|
@@ -30,8 +31,10 @@ class SQLPortfolio(Portfolio, SQLBaseModel, SQLAlchemyModelExtension):
|
|
|
30
31
|
lazy="dynamic",
|
|
31
32
|
cascade="all,delete",
|
|
32
33
|
)
|
|
33
|
-
created_at = Column(DateTime, nullable=False
|
|
34
|
-
updated_at = Column(DateTime, nullable=False
|
|
34
|
+
created_at = Column(DateTime, nullable=False)
|
|
35
|
+
updated_at = Column(DateTime, nullable=False)
|
|
36
|
+
initialized = Column(Boolean, nullable=False, default=False)
|
|
37
|
+
|
|
35
38
|
__table_args__ = (
|
|
36
39
|
UniqueConstraint(
|
|
37
40
|
'trading_symbol',
|
|
@@ -53,15 +56,21 @@ class SQLPortfolio(Portfolio, SQLBaseModel, SQLAlchemyModelExtension):
|
|
|
53
56
|
trading_symbol,
|
|
54
57
|
market,
|
|
55
58
|
unallocated,
|
|
59
|
+
initialized,
|
|
60
|
+
initial_balance=None,
|
|
56
61
|
identifier=None,
|
|
57
62
|
created_at=None,
|
|
63
|
+
updated_at=None,
|
|
58
64
|
):
|
|
59
65
|
|
|
60
66
|
if identifier is None:
|
|
61
67
|
identifier = market
|
|
62
68
|
|
|
63
69
|
if created_at is None:
|
|
64
|
-
created_at = datetime.
|
|
70
|
+
created_at = datetime.now(tz=timezone.utc)
|
|
71
|
+
|
|
72
|
+
if updated_at is None:
|
|
73
|
+
updated_at = datetime.now(tz=timezone.utc)
|
|
65
74
|
|
|
66
75
|
super().__init__(
|
|
67
76
|
trading_symbol=trading_symbol,
|
|
@@ -73,6 +82,9 @@ class SQLPortfolio(Portfolio, SQLBaseModel, SQLAlchemyModelExtension):
|
|
|
73
82
|
total_revenue=0,
|
|
74
83
|
total_cost=0,
|
|
75
84
|
created_at=created_at,
|
|
85
|
+
updated_at=updated_at,
|
|
86
|
+
initialized=initialized,
|
|
87
|
+
initial_balance=initial_balance,
|
|
76
88
|
)
|
|
77
89
|
|
|
78
90
|
def update(self, data):
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from sqlalchemy import Column, Integer, String, DateTime, Float
|
|
2
|
+
from sqlalchemy.orm import relationship
|
|
3
|
+
|
|
4
|
+
from investing_algorithm_framework.domain import Trade, TradeStatus
|
|
5
|
+
from investing_algorithm_framework.infrastructure.database import SQLBaseModel
|
|
6
|
+
from investing_algorithm_framework.infrastructure.models.model_extension \
|
|
7
|
+
import SQLAlchemyModelExtension
|
|
8
|
+
from investing_algorithm_framework.infrastructure.models\
|
|
9
|
+
.order_trade_association import order_trade_association
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SQLTrade(Trade, SQLBaseModel, SQLAlchemyModelExtension):
|
|
13
|
+
"""
|
|
14
|
+
SQL Trade model
|
|
15
|
+
|
|
16
|
+
A trade is a combination of a buy and sell order that has been opened or
|
|
17
|
+
closed.
|
|
18
|
+
|
|
19
|
+
A trade is considered opened when a buy order is executed and there is
|
|
20
|
+
no corresponding sell order. A trade is considered closed when a sell
|
|
21
|
+
order is executed and the amount of the sell order is equal or larger
|
|
22
|
+
to the amount of the buy order.
|
|
23
|
+
|
|
24
|
+
A single sell order can close multiple buy orders. Also, a single
|
|
25
|
+
buy order can be closed by multiple sell orders.
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
orders: str, the id of the buy order
|
|
29
|
+
target_symbol: str, the target symbol of the trade
|
|
30
|
+
trading_symbol: str, the trading symbol of the trade
|
|
31
|
+
closed_at: datetime, the datetime when the trade was closed
|
|
32
|
+
amount: float, the amount of the trade
|
|
33
|
+
available_amount: float, the available amount of the trade
|
|
34
|
+
remaining: float, the remaining amount that is not filled by the
|
|
35
|
+
buy order that opened the trade.
|
|
36
|
+
filled_amount: float, the filled amount of the trade by the buy
|
|
37
|
+
order that opened the trade.
|
|
38
|
+
net_gain: float, the net gain of the trade
|
|
39
|
+
last_reported_price: float, the last reported price of the trade
|
|
40
|
+
last_reported_price_datetime: datetime, the datetime when the last
|
|
41
|
+
reported price was reported
|
|
42
|
+
created_at: datetime, the datetime when the trade was created
|
|
43
|
+
updated_at: datetime, the datetime when the trade was last updated
|
|
44
|
+
status: str, the status of the trade
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
__tablename__ = "trades"
|
|
48
|
+
id = Column(Integer, primary_key=True, unique=True)
|
|
49
|
+
orders = relationship(
|
|
50
|
+
'SQLOrder',
|
|
51
|
+
secondary=order_trade_association,
|
|
52
|
+
back_populates='trades',
|
|
53
|
+
lazy='joined'
|
|
54
|
+
)
|
|
55
|
+
target_symbol = Column(String)
|
|
56
|
+
trading_symbol = Column(String)
|
|
57
|
+
closed_at = Column(DateTime, default=None)
|
|
58
|
+
opened_at = Column(DateTime, default=None)
|
|
59
|
+
open_price = Column(Float, default=None)
|
|
60
|
+
amount = Column(Float, default=None)
|
|
61
|
+
available_amount = Column(Float, default=None)
|
|
62
|
+
filled_amount = Column(Float, default=None)
|
|
63
|
+
remaining = Column(Float, default=None)
|
|
64
|
+
net_gain = Column(Float, default=0)
|
|
65
|
+
cost = Column(Float, default=0)
|
|
66
|
+
last_reported_price = Column(Float, default=None)
|
|
67
|
+
last_reported_price_datetime = Column(DateTime, default=None)
|
|
68
|
+
high_water_mark = Column(Float, default=None)
|
|
69
|
+
high_water_mark_datetime = Column(DateTime, default=None)
|
|
70
|
+
updated_at = Column(DateTime, default=None)
|
|
71
|
+
status = Column(String, default=TradeStatus.CREATED.value)
|
|
72
|
+
# Stop losses should be actively loaded
|
|
73
|
+
stop_losses = relationship(
|
|
74
|
+
'SQLTradeStopLoss',
|
|
75
|
+
back_populates='trade',
|
|
76
|
+
lazy='joined'
|
|
77
|
+
)
|
|
78
|
+
# Take profits should be actively loaded
|
|
79
|
+
take_profits = relationship(
|
|
80
|
+
'SQLTradeTakeProfit',
|
|
81
|
+
back_populates='trade',
|
|
82
|
+
lazy='joined'
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def __init__(
|
|
86
|
+
self,
|
|
87
|
+
buy_order,
|
|
88
|
+
target_symbol,
|
|
89
|
+
trading_symbol,
|
|
90
|
+
opened_at,
|
|
91
|
+
amount,
|
|
92
|
+
available_amount,
|
|
93
|
+
filled_amount,
|
|
94
|
+
remaining,
|
|
95
|
+
status=TradeStatus.CREATED.value,
|
|
96
|
+
closed_at=None,
|
|
97
|
+
updated_at=None,
|
|
98
|
+
net_gain=0,
|
|
99
|
+
cost=0,
|
|
100
|
+
last_reported_price=None,
|
|
101
|
+
last_reported_price_datetime=None,
|
|
102
|
+
high_water_mark=None,
|
|
103
|
+
high_water_mark_datetime=None,
|
|
104
|
+
sell_orders=[],
|
|
105
|
+
stop_losses=[],
|
|
106
|
+
take_profits=[],
|
|
107
|
+
):
|
|
108
|
+
self.orders = [buy_order]
|
|
109
|
+
self.open_price = buy_order.price
|
|
110
|
+
self.target_symbol = target_symbol
|
|
111
|
+
self.trading_symbol = trading_symbol
|
|
112
|
+
self.closed_at = closed_at
|
|
113
|
+
self.amount = amount
|
|
114
|
+
self.available_amount = available_amount
|
|
115
|
+
self.filled_amount = filled_amount
|
|
116
|
+
self.remaining = remaining
|
|
117
|
+
self.net_gain = net_gain
|
|
118
|
+
self.cost = cost
|
|
119
|
+
self.last_reported_price = last_reported_price
|
|
120
|
+
self.last_reported_price_datetime = last_reported_price_datetime
|
|
121
|
+
self.high_water_mark = high_water_mark
|
|
122
|
+
self.high_water_mark_datetime = high_water_mark_datetime
|
|
123
|
+
self.opened_at = opened_at
|
|
124
|
+
self.updated_at = updated_at
|
|
125
|
+
self.status = status
|
|
126
|
+
self.stop_losses = stop_losses
|
|
127
|
+
self.take_profits = take_profits
|
|
128
|
+
|
|
129
|
+
if sell_orders is not None:
|
|
130
|
+
self.orders.extend(sell_orders)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from sqlalchemy import Column, Integer, String, Float, ForeignKey, Boolean, \
|
|
2
|
+
DateTime
|
|
3
|
+
from sqlalchemy.orm import relationship
|
|
4
|
+
|
|
5
|
+
from investing_algorithm_framework.domain import TradeStopLoss
|
|
6
|
+
from investing_algorithm_framework.infrastructure.database import SQLBaseModel
|
|
7
|
+
from investing_algorithm_framework.infrastructure.models.model_extension \
|
|
8
|
+
import SQLAlchemyModelExtension
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SQLTradeStopLoss(TradeStopLoss, SQLBaseModel, SQLAlchemyModelExtension):
|
|
12
|
+
"""
|
|
13
|
+
SQLTradeStopLoss model
|
|
14
|
+
|
|
15
|
+
A trade stop loss is a stop loss strategy for a trade.
|
|
16
|
+
|
|
17
|
+
Attributes:
|
|
18
|
+
- trade (Trade): the trade that the take profit is for
|
|
19
|
+
- percentage (float): the stop loss percentage
|
|
20
|
+
- trailing (bool): indicates whether the stop loss is trailing
|
|
21
|
+
- sell_percentage (float) the percentage of the trade to sell
|
|
22
|
+
when the stop loss is triggered.
|
|
23
|
+
- open_price (float): the price at which the trade was opened.
|
|
24
|
+
- high_water_mark (float): the highest price reached since the trade
|
|
25
|
+
was opened.
|
|
26
|
+
- high_water_mark_date (String): the date when the high water mark
|
|
27
|
+
was reached.
|
|
28
|
+
- stop_loss_price (float): the calculated stop loss price based on
|
|
29
|
+
the high watermark and percentage.
|
|
30
|
+
- sell_prices (String): a serialized list of prices at which
|
|
31
|
+
stop losses were executed.
|
|
32
|
+
- sell_dates (String): a serialized list of dates when
|
|
33
|
+
stop losses were executed.
|
|
34
|
+
- sell_amount (float): the total amount sold due to stop losses.
|
|
35
|
+
- sold_amount (float): the total amount that has been sold due
|
|
36
|
+
to stop losses.
|
|
37
|
+
- active (bool): indicates whether the stop loss is currently active.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
__tablename__ = "trade_stop_losses"
|
|
41
|
+
id = Column(Integer, primary_key=True, unique=True)
|
|
42
|
+
trade_id = Column(Integer, ForeignKey('trades.id'))
|
|
43
|
+
trade = relationship('SQLTrade', back_populates='stop_losses')
|
|
44
|
+
trailing = Column(Boolean)
|
|
45
|
+
percentage = Column(Float)
|
|
46
|
+
sell_percentage = Column(Float)
|
|
47
|
+
open_price = Column(Float)
|
|
48
|
+
high_water_mark = Column(Float)
|
|
49
|
+
high_water_mark_date = Column(String)
|
|
50
|
+
stop_loss_price = Column(Float)
|
|
51
|
+
sell_prices = Column(String)
|
|
52
|
+
sell_dates = Column(String)
|
|
53
|
+
sell_amount = Column(Float)
|
|
54
|
+
sold_amount = Column(Float)
|
|
55
|
+
active = Column(Boolean)
|
|
56
|
+
triggered = Column(Boolean, default=False)
|
|
57
|
+
triggered_at = Column(DateTime, default=None)
|
|
58
|
+
created_at = Column(DateTime)
|
|
59
|
+
updated_at = Column(DateTime, default=None)
|