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
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from datetime import datetime, timezone, timedelta
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
from dateutil import parser
|
|
6
|
+
|
|
7
|
+
from investing_algorithm_framework.domain.models.time_frame import TimeFrame
|
|
8
|
+
from .data_type import DataType
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class DataSource:
|
|
13
|
+
"""
|
|
14
|
+
Base class for data sources.
|
|
15
|
+
"""
|
|
16
|
+
identifier: str = None
|
|
17
|
+
data_provider_identifier: str = None
|
|
18
|
+
data_type: Union[DataType, str] = None
|
|
19
|
+
symbol: str = None
|
|
20
|
+
window_size: int = None
|
|
21
|
+
time_frame: Union[TimeFrame, str] = None
|
|
22
|
+
market: str = None
|
|
23
|
+
storage_path: str = None
|
|
24
|
+
pandas: bool = False
|
|
25
|
+
date: Union[datetime, None] = None
|
|
26
|
+
start_date: Union[datetime, None] = None
|
|
27
|
+
end_date: Union[datetime, None] = None
|
|
28
|
+
save: bool = False
|
|
29
|
+
|
|
30
|
+
def __post_init__(self):
|
|
31
|
+
# Convert data_type and time_frame to their respective enums if needed
|
|
32
|
+
if isinstance(self.data_type, str):
|
|
33
|
+
object.__setattr__(self, 'data_type',
|
|
34
|
+
DataType.from_string(self.data_type))
|
|
35
|
+
|
|
36
|
+
if isinstance(self.time_frame, str):
|
|
37
|
+
object.__setattr__(self, 'time_frame',
|
|
38
|
+
TimeFrame.from_string(self.time_frame))
|
|
39
|
+
|
|
40
|
+
start_date = self.start_date
|
|
41
|
+
end_date = self.end_date
|
|
42
|
+
|
|
43
|
+
# Parse the start_date if it is a string and
|
|
44
|
+
# make sure its set to timezone utc
|
|
45
|
+
if start_date is None:
|
|
46
|
+
|
|
47
|
+
if isinstance(self.start_date, str):
|
|
48
|
+
start_date = parser.parse(start_date)
|
|
49
|
+
|
|
50
|
+
if start_date is not None:
|
|
51
|
+
object.__setattr__(
|
|
52
|
+
self,
|
|
53
|
+
'start_date',
|
|
54
|
+
start_date.replace(tzinfo=timezone.utc)
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Parse the end_date if it is a string and
|
|
58
|
+
# make sure its set to timezone utc
|
|
59
|
+
if end_date is None:
|
|
60
|
+
|
|
61
|
+
if isinstance(self.end_date, str):
|
|
62
|
+
end_date = parser.parse(end_date)
|
|
63
|
+
|
|
64
|
+
if end_date is not None:
|
|
65
|
+
object.__setattr__(
|
|
66
|
+
self,
|
|
67
|
+
'end_date',
|
|
68
|
+
end_date.replace(tzinfo=timezone.utc)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if self.market is not None:
|
|
72
|
+
object.__setattr__(self, 'market', self.market.upper())
|
|
73
|
+
|
|
74
|
+
if self.symbol is not None:
|
|
75
|
+
object.__setattr__(self, 'symbol', self.symbol.upper())
|
|
76
|
+
|
|
77
|
+
def get_identifier(self):
|
|
78
|
+
"""
|
|
79
|
+
Returns the identifier or creates a unique identifier for the
|
|
80
|
+
data source based on its attributes.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
if self.identifier is not None:
|
|
84
|
+
return self.identifier
|
|
85
|
+
|
|
86
|
+
if DataType.OHLCV.equals(self.data_type):
|
|
87
|
+
return (f"{self.data_type.value}_{self.market}_"
|
|
88
|
+
f"{self.symbol}_{self.time_frame.value}")
|
|
89
|
+
|
|
90
|
+
elif DataType.CUSTOM.equals(self.data_type):
|
|
91
|
+
identifier = "CUSTOM"
|
|
92
|
+
|
|
93
|
+
if self.symbol is not None:
|
|
94
|
+
identifier += f"_{self.symbol}"
|
|
95
|
+
|
|
96
|
+
if self.time_frame is not None:
|
|
97
|
+
identifier += f"_{self.time_frame.value}"
|
|
98
|
+
|
|
99
|
+
if self.market is not None:
|
|
100
|
+
identifier += f"_{self.market}"
|
|
101
|
+
|
|
102
|
+
if self.window_size is not None:
|
|
103
|
+
identifier += f"_{self.window_size}"
|
|
104
|
+
|
|
105
|
+
return identifier
|
|
106
|
+
|
|
107
|
+
def to_dict(self):
|
|
108
|
+
"""
|
|
109
|
+
Converts the DataSource instance to a dictionary.
|
|
110
|
+
"""
|
|
111
|
+
non_null_attributes = {
|
|
112
|
+
key: value for key, value in self.__dict__.items()
|
|
113
|
+
if value is not None
|
|
114
|
+
}
|
|
115
|
+
# Convert DataType and TimeFrame to their string representations
|
|
116
|
+
if self.data_type is not None:
|
|
117
|
+
non_null_attributes['data_type'] = self.data_type.value
|
|
118
|
+
if self.time_frame is not None:
|
|
119
|
+
non_null_attributes['time_frame'] = self.time_frame.value
|
|
120
|
+
|
|
121
|
+
return non_null_attributes
|
|
122
|
+
|
|
123
|
+
def __repr__(self):
|
|
124
|
+
return (
|
|
125
|
+
f"DataSource(identifier={self.identifier}, "
|
|
126
|
+
f"data_provider_identifier={self.data_provider_identifier}, "
|
|
127
|
+
f"data_type={self.data_type}, symbol={self.symbol}, "
|
|
128
|
+
f"window_size={self.window_size}, time_frame={self.time_frame}, "
|
|
129
|
+
f"market={self.market}, storage_path={self.storage_path}, "
|
|
130
|
+
f"pandas={self.pandas}, date={self.date}, "
|
|
131
|
+
f"start_date={self.start_date}, end_date={self.end_date}, "
|
|
132
|
+
f"save={self.save})"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def __eq__(self, other):
|
|
136
|
+
"""
|
|
137
|
+
Compares two DataSource instances for equality.
|
|
138
|
+
|
|
139
|
+
OHLCV data sources are considered equal if they have:
|
|
140
|
+
- The same data_type (OHLCV), symbol, time_frame, and market.
|
|
141
|
+
- If no market and timeframe is specified, then
|
|
142
|
+
they are considered equal for the same symbol
|
|
143
|
+
and data_type.
|
|
144
|
+
"""
|
|
145
|
+
if DataType.OHLCV.equals(self.data_type):
|
|
146
|
+
|
|
147
|
+
if other.time_frame is None and other.window_size is None:
|
|
148
|
+
return (self.data_type == other.data_type and
|
|
149
|
+
self.symbol == other.symbol)
|
|
150
|
+
elif self.time_frame is None and self.window_size is None:
|
|
151
|
+
return (self.data_type == other.data_type and
|
|
152
|
+
self.symbol == other.symbol)
|
|
153
|
+
|
|
154
|
+
return (self.time_frame == other.time_frame and
|
|
155
|
+
self.market == other.market and
|
|
156
|
+
self.symbol == other.symbol and
|
|
157
|
+
self.data_type == other.data_type)
|
|
158
|
+
|
|
159
|
+
elif DataType.CUSTOM.equals(self.data_type):
|
|
160
|
+
return (self.data_type == other.data_type and
|
|
161
|
+
self.symbol == other.symbol and
|
|
162
|
+
self.window_size == other.window_size and
|
|
163
|
+
self.time_frame == other.time_frame and
|
|
164
|
+
self.market == other.market)
|
|
165
|
+
|
|
166
|
+
elif DataType.TICKER.equals(self.data_type):
|
|
167
|
+
return (self.data_type == other.data_type and
|
|
168
|
+
self.symbol == other.symbol and
|
|
169
|
+
self.market == other.market)
|
|
170
|
+
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
def create_start_date_data(self, index_date: datetime) -> datetime:
|
|
174
|
+
|
|
175
|
+
if self.window_size is None or self.time_frame is None:
|
|
176
|
+
return index_date
|
|
177
|
+
|
|
178
|
+
return index_date - \
|
|
179
|
+
(self.window_size * timedelta(
|
|
180
|
+
minutes=self.time_frame.amount_of_minutes
|
|
181
|
+
))
|
|
182
|
+
|
|
183
|
+
def get_number_of_required_data_points(
|
|
184
|
+
self, start_date: datetime, end_date: datetime
|
|
185
|
+
) -> int:
|
|
186
|
+
"""
|
|
187
|
+
Returns the number of data points required based on the given
|
|
188
|
+
attributes of the data source. If the required number of data points
|
|
189
|
+
can't be determined, it returns None.
|
|
190
|
+
|
|
191
|
+
E.g., for OHLCV data source, it
|
|
192
|
+
calculates the number of data points needed between the
|
|
193
|
+
start_date and end_date based on the time frame.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
start_date (datetime): The start date for the data points.
|
|
197
|
+
end_date (datetime): The end date for the data points.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
int: The number of required data points, or None if it can't
|
|
201
|
+
be determined.
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
if self.time_frame is None:
|
|
205
|
+
return None
|
|
206
|
+
|
|
207
|
+
delta = end_date - start_date
|
|
208
|
+
total_minutes = delta.total_seconds() / 60
|
|
209
|
+
data_points = total_minutes / self.time_frame.amount_of_minutes
|
|
210
|
+
|
|
211
|
+
if self.window_size is not None:
|
|
212
|
+
data_points += self.window_size
|
|
213
|
+
|
|
214
|
+
return int(data_points)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DataType(Enum):
|
|
5
|
+
OHLCV = "OHLCV"
|
|
6
|
+
TICKER = "TICKER"
|
|
7
|
+
ORDER_BOOK = "ORDER_BOOK"
|
|
8
|
+
CUSTOM = "CUSTOM"
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def from_string(value: str):
|
|
12
|
+
|
|
13
|
+
if isinstance(value, str):
|
|
14
|
+
|
|
15
|
+
for entry in DataType:
|
|
16
|
+
|
|
17
|
+
if value.upper() == entry.value:
|
|
18
|
+
return entry
|
|
19
|
+
|
|
20
|
+
raise ValueError(
|
|
21
|
+
f"Could not convert {value} to DataType"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def from_value(value):
|
|
26
|
+
|
|
27
|
+
if isinstance(value, str):
|
|
28
|
+
return DataType.from_string(value)
|
|
29
|
+
|
|
30
|
+
if isinstance(value, DataType):
|
|
31
|
+
|
|
32
|
+
for entry in DataType:
|
|
33
|
+
|
|
34
|
+
if value == entry:
|
|
35
|
+
return entry
|
|
36
|
+
|
|
37
|
+
raise ValueError(
|
|
38
|
+
f"Could not convert {value} to TimeFrame"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def equals(self, other):
|
|
42
|
+
|
|
43
|
+
if isinstance(other, Enum):
|
|
44
|
+
return self.value == other.value
|
|
45
|
+
else:
|
|
46
|
+
return DataType.from_string(other) == self
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Event(Enum):
|
|
5
|
+
PORTFOLIO_CREATED = "PORTFOLIO_CREATED"
|
|
6
|
+
ORDER_CREATED = "ORDER_CREATED"
|
|
7
|
+
TRADE_CLOSED = "TRADE_CLOSED"
|
|
8
|
+
STRATEGY_RUN = "STRATEGY_RUN"
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def from_string(value: str):
|
|
12
|
+
|
|
13
|
+
if isinstance(value, str):
|
|
14
|
+
for status in Event:
|
|
15
|
+
|
|
16
|
+
if value.upper() == status.value:
|
|
17
|
+
return status
|
|
18
|
+
|
|
19
|
+
raise ValueError("Could not convert value to Event")
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def from_value(value):
|
|
23
|
+
|
|
24
|
+
if isinstance(value, Event):
|
|
25
|
+
for status in Event:
|
|
26
|
+
|
|
27
|
+
if value == status:
|
|
28
|
+
return status
|
|
29
|
+
elif isinstance(value, str):
|
|
30
|
+
return Event.from_string(value)
|
|
31
|
+
|
|
32
|
+
raise ValueError(f"Could not convert value {value} to Event")
|
|
33
|
+
|
|
34
|
+
def equals(self, other):
|
|
35
|
+
return Event.from_value(other) == self
|
|
@@ -1,9 +1,63 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from investing_algorithm_framework.domain.exceptions import \
|
|
5
|
+
OperationalException
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger("investing_algorithm_framework")
|
|
8
|
+
|
|
9
|
+
|
|
1
10
|
class MarketCredential:
|
|
2
|
-
|
|
11
|
+
"""
|
|
12
|
+
Market credential model to store the api key and secret key for a market.
|
|
13
|
+
"""
|
|
14
|
+
def __init__(
|
|
15
|
+
self, market: str, api_key: str = None, secret_key: str = None
|
|
16
|
+
):
|
|
3
17
|
self._api_key = api_key
|
|
4
18
|
self._secret_key = secret_key
|
|
5
19
|
self._market = market
|
|
6
20
|
|
|
21
|
+
def initialize(self):
|
|
22
|
+
"""
|
|
23
|
+
Internal helper to initialize the market credential.
|
|
24
|
+
"""
|
|
25
|
+
logger.info(f"Initializing market credential for {self.market}")
|
|
26
|
+
|
|
27
|
+
if self.api_key is None:
|
|
28
|
+
logger.info(
|
|
29
|
+
"Reading api key from environment variable"
|
|
30
|
+
f" {self.market.upper()}_API_KEY"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Check if environment variable is set
|
|
34
|
+
environment_variable = f"{self.market.upper()}_API_KEY"
|
|
35
|
+
self._api_key = os.getenv(environment_variable)
|
|
36
|
+
|
|
37
|
+
if self.api_key is None:
|
|
38
|
+
raise OperationalException(
|
|
39
|
+
f"Market credential for market {self.market}"
|
|
40
|
+
" requires an api key, either"
|
|
41
|
+
" as an argument or as an environment variable"
|
|
42
|
+
f" named as {self._market.upper()}_API_KEY"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if self.secret_key is None:
|
|
46
|
+
logger.info(
|
|
47
|
+
"Reading secret key from environment variable"
|
|
48
|
+
f" {self.market.upper()}_SECRET_KEY"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Check if environment variable is set
|
|
52
|
+
environment_variable = f"{self.market.upper()}_SECRET_KEY"
|
|
53
|
+
self._secret_key = os.getenv(environment_variable)
|
|
54
|
+
|
|
55
|
+
if self.api_key is not None:
|
|
56
|
+
self._api_key = self.api_key.strip()
|
|
57
|
+
|
|
58
|
+
if self.secret_key is not None:
|
|
59
|
+
self._secret_key = self.secret_key.strip()
|
|
60
|
+
|
|
7
61
|
def get_api_key(self):
|
|
8
62
|
return self.api_key
|
|
9
63
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from datetime import datetime, timezone
|
|
2
3
|
|
|
3
4
|
from dateutil.parser import parse
|
|
4
5
|
|
|
@@ -23,26 +24,24 @@ class Order(BaseModel):
|
|
|
23
24
|
self,
|
|
24
25
|
order_type,
|
|
25
26
|
order_side,
|
|
26
|
-
status,
|
|
27
27
|
amount,
|
|
28
|
+
status: OrderStatus | None | str = OrderStatus.CREATED.value,
|
|
28
29
|
target_symbol=None,
|
|
29
30
|
trading_symbol=None,
|
|
30
31
|
price=None,
|
|
31
|
-
net_gain=0,
|
|
32
32
|
created_at=None,
|
|
33
33
|
updated_at=None,
|
|
34
|
-
trade_closed_at=None,
|
|
35
|
-
trade_closed_price=None,
|
|
36
|
-
trade_closed_amount=None,
|
|
37
34
|
external_id=None,
|
|
35
|
+
cost=None,
|
|
38
36
|
filled=None,
|
|
39
37
|
remaining=None,
|
|
40
|
-
cost=None,
|
|
41
38
|
fee=None,
|
|
42
39
|
position_id=None,
|
|
43
40
|
order_fee=None,
|
|
44
41
|
order_fee_currency=None,
|
|
45
|
-
order_fee_rate=None
|
|
42
|
+
order_fee_rate=None,
|
|
43
|
+
id=None,
|
|
44
|
+
metadata=None,
|
|
46
45
|
):
|
|
47
46
|
if target_symbol is None:
|
|
48
47
|
raise OperationalException("Target symbol is not specified")
|
|
@@ -62,6 +61,15 @@ class Order(BaseModel):
|
|
|
62
61
|
if status is None:
|
|
63
62
|
raise OperationalException("Status is not set")
|
|
64
63
|
|
|
64
|
+
self.created_at = created_at
|
|
65
|
+
self.updated_at = updated_at
|
|
66
|
+
|
|
67
|
+
if self.created_at is None:
|
|
68
|
+
self.created_at = datetime.now(tz=timezone.utc)
|
|
69
|
+
|
|
70
|
+
if self.updated_at is None:
|
|
71
|
+
self.updated_at = datetime.now(tz=timezone.utc)
|
|
72
|
+
|
|
65
73
|
self.external_id = external_id
|
|
66
74
|
self.price = price
|
|
67
75
|
self.order_side = OrderSide.from_value(order_side).value
|
|
@@ -69,20 +77,16 @@ class Order(BaseModel):
|
|
|
69
77
|
self.status = OrderStatus.from_value(status).value
|
|
70
78
|
self.position_id = position_id
|
|
71
79
|
self.amount = amount
|
|
72
|
-
self.net_gain = net_gain
|
|
73
|
-
self.trade_closed_at = trade_closed_at
|
|
74
|
-
self.trade_closed_price = trade_closed_price
|
|
75
|
-
self.trade_closed_amount = trade_closed_amount
|
|
76
|
-
self.created_at = created_at
|
|
77
|
-
self.updated_at = updated_at
|
|
78
80
|
self.filled = filled
|
|
79
81
|
self.remaining = remaining
|
|
80
|
-
self.cost = cost
|
|
81
82
|
self.fee = fee
|
|
82
83
|
self._available_amount = self.filled
|
|
83
84
|
self.order_fee = order_fee
|
|
84
85
|
self.order_fee_currency = order_fee_currency
|
|
85
86
|
self.order_fee_rate = order_fee_rate
|
|
87
|
+
self.id = id
|
|
88
|
+
self.cost = cost
|
|
89
|
+
self.metadata = metadata if metadata is not None else {}
|
|
86
90
|
|
|
87
91
|
def get_id(self):
|
|
88
92
|
return self.id
|
|
@@ -147,38 +151,6 @@ class Order(BaseModel):
|
|
|
147
151
|
def set_external_id(self, external_id):
|
|
148
152
|
self.external_id = external_id
|
|
149
153
|
|
|
150
|
-
def get_net_gain(self):
|
|
151
|
-
|
|
152
|
-
if self.net_gain is None:
|
|
153
|
-
return 0
|
|
154
|
-
|
|
155
|
-
return self.net_gain
|
|
156
|
-
|
|
157
|
-
def set_net_gain(self, net_gain):
|
|
158
|
-
self.net_gain = net_gain
|
|
159
|
-
|
|
160
|
-
def get_trade_closed_at(self):
|
|
161
|
-
return self.trade_closed_at
|
|
162
|
-
|
|
163
|
-
def set_trade_closed_at(self, trade_closed_at):
|
|
164
|
-
self.trade_closed_at = trade_closed_at
|
|
165
|
-
|
|
166
|
-
def get_trade_closed_price(self):
|
|
167
|
-
return self.trade_closed_price
|
|
168
|
-
|
|
169
|
-
def set_trade_closed_price(self, trade_closed_price):
|
|
170
|
-
self.trade_closed_price = trade_closed_price
|
|
171
|
-
|
|
172
|
-
def get_trade_closed_amount(self):
|
|
173
|
-
|
|
174
|
-
if self.trade_closed_amount is not None:
|
|
175
|
-
return self.trade_closed_amount
|
|
176
|
-
|
|
177
|
-
return 0
|
|
178
|
-
|
|
179
|
-
def set_trade_closed_amount(self, trade_closed_amount):
|
|
180
|
-
self.trade_closed_amount = trade_closed_amount
|
|
181
|
-
|
|
182
154
|
def get_created_at(self):
|
|
183
155
|
return self.created_at
|
|
184
156
|
|
|
@@ -204,23 +176,13 @@ class Order(BaseModel):
|
|
|
204
176
|
def get_remaining(self):
|
|
205
177
|
|
|
206
178
|
if self.remaining is None:
|
|
207
|
-
return self.
|
|
179
|
+
return self.get_amount() - self.get_filled()
|
|
208
180
|
|
|
209
181
|
return self.remaining
|
|
210
182
|
|
|
211
183
|
def set_remaining(self, remaining):
|
|
212
184
|
self.remaining = remaining
|
|
213
185
|
|
|
214
|
-
def get_cost(self):
|
|
215
|
-
|
|
216
|
-
if self.cost is None:
|
|
217
|
-
return 0
|
|
218
|
-
|
|
219
|
-
return self.cost
|
|
220
|
-
|
|
221
|
-
def set_cost(self, cost):
|
|
222
|
-
self.cost = cost
|
|
223
|
-
|
|
224
186
|
def get_fee(self):
|
|
225
187
|
return self.fee
|
|
226
188
|
|
|
@@ -228,7 +190,13 @@ class Order(BaseModel):
|
|
|
228
190
|
self.fee = order_fee
|
|
229
191
|
|
|
230
192
|
def get_symbol(self):
|
|
231
|
-
return self.get_target_symbol() + "/"
|
|
193
|
+
return (self.get_target_symbol().upper() + "/"
|
|
194
|
+
+ self.get_trading_symbol().upper())
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def symbol(self):
|
|
198
|
+
return (self.get_target_symbol().upper() + "/"
|
|
199
|
+
+ self.get_trading_symbol().upper())
|
|
232
200
|
|
|
233
201
|
def get_available_amount(self):
|
|
234
202
|
|
|
@@ -249,20 +217,33 @@ class Order(BaseModel):
|
|
|
249
217
|
self.set_available_amount(available_amount)
|
|
250
218
|
|
|
251
219
|
def to_dict(self, datetime_format=None):
|
|
220
|
+
"""
|
|
221
|
+
Convert the Order object to a dictionary
|
|
252
222
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
223
|
+
Args:
|
|
224
|
+
datetime_format (str): The format to use for the datetime fields.
|
|
225
|
+
If None, the datetime fields will be returned as is.
|
|
226
|
+
Defaults to None.
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
dict: A dictionary representation of the Order object.
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
def ensure_iso(value):
|
|
233
|
+
if hasattr(value, "isoformat"):
|
|
234
|
+
if value.tzinfo is None:
|
|
235
|
+
value = value.replace(tzinfo=timezone.utc)
|
|
236
|
+
return value.isoformat()
|
|
237
|
+
return value
|
|
238
|
+
|
|
239
|
+
created_at = ensure_iso(self.created_at) if self.created_at else None
|
|
240
|
+
updated_at = ensure_iso(self.updated_at) if self.updated_at else None
|
|
241
|
+
|
|
242
|
+
# Ensure status is a string
|
|
243
|
+
self.status = OrderStatus.from_value(self.status).value
|
|
264
244
|
|
|
265
245
|
return {
|
|
246
|
+
"id": self.id,
|
|
266
247
|
"external_id": self.external_id,
|
|
267
248
|
"target_symbol": self.target_symbol,
|
|
268
249
|
"trading_symbol": self.trading_symbol,
|
|
@@ -271,17 +252,15 @@ class Order(BaseModel):
|
|
|
271
252
|
"status": self.status,
|
|
272
253
|
"price": self.price,
|
|
273
254
|
"amount": self.amount,
|
|
274
|
-
"net_gain": self.net_gain,
|
|
275
|
-
"trade_closed_at": trade_closed_at,
|
|
276
|
-
"trade_closed_price": self.trade_closed_price,
|
|
277
255
|
"created_at": created_at,
|
|
278
256
|
"updated_at": updated_at,
|
|
257
|
+
"cost": self.cost,
|
|
279
258
|
"filled": self.filled,
|
|
280
259
|
"remaining": self.remaining,
|
|
281
|
-
"cost": self.cost,
|
|
282
260
|
"order_fee_currency": self.order_fee_currency,
|
|
283
261
|
"order_fee_rate": self.order_fee_rate,
|
|
284
262
|
"order_fee": self.order_fee,
|
|
263
|
+
"metadata": self.metadata if hasattr(self, 'metadata') else {},
|
|
285
264
|
}
|
|
286
265
|
|
|
287
266
|
@staticmethod
|
|
@@ -303,8 +282,9 @@ class Order(BaseModel):
|
|
|
303
282
|
if updated_at is not None:
|
|
304
283
|
updated_at = parse(updated_at)
|
|
305
284
|
|
|
306
|
-
|
|
307
|
-
|
|
285
|
+
order = Order(
|
|
286
|
+
id=data.get("id", None),
|
|
287
|
+
external_id=data.get("external_id", None),
|
|
308
288
|
target_symbol=target_symbol,
|
|
309
289
|
trading_symbol=trading_symbol,
|
|
310
290
|
price=data.get("price", None),
|
|
@@ -314,14 +294,16 @@ class Order(BaseModel):
|
|
|
314
294
|
order_side=data.get("order_side", None),
|
|
315
295
|
filled=data.get("filled", None),
|
|
316
296
|
remaining=data.get("remaining", None),
|
|
317
|
-
cost=data.get("cost", None),
|
|
318
297
|
fee=data.get("fee", None),
|
|
298
|
+
cost=data.get("cost", None),
|
|
319
299
|
created_at=created_at,
|
|
320
300
|
updated_at=updated_at,
|
|
321
301
|
order_fee=data.get("order_fee", None),
|
|
322
302
|
order_fee_currency=data.get("order_fee_currency", None),
|
|
323
303
|
order_fee_rate=data.get("order_fee_rate", None),
|
|
304
|
+
metadata=data.get("metadata", {}),
|
|
324
305
|
)
|
|
306
|
+
return order
|
|
325
307
|
|
|
326
308
|
@staticmethod
|
|
327
309
|
def from_ccxt_order(ccxt_order):
|
|
@@ -343,6 +325,13 @@ class Order(BaseModel):
|
|
|
343
325
|
order_fee_currency = ccxt_fee.get("currency", None)
|
|
344
326
|
order_fee_rate = ccxt_fee.get("rate", None)
|
|
345
327
|
|
|
328
|
+
created_at = ccxt_order.get("datetime", None)
|
|
329
|
+
created_at = parse(created_at) if created_at else None
|
|
330
|
+
|
|
331
|
+
# Make sure that the created_at is a utc datetime object
|
|
332
|
+
if created_at and created_at.tzinfo is None:
|
|
333
|
+
created_at = created_at.replace(tzinfo=timezone.utc)
|
|
334
|
+
|
|
346
335
|
return Order(
|
|
347
336
|
external_id=ccxt_order.get("id", None),
|
|
348
337
|
target_symbol=target_symbol,
|
|
@@ -350,15 +339,15 @@ class Order(BaseModel):
|
|
|
350
339
|
price=ccxt_order.get("price", None),
|
|
351
340
|
amount=ccxt_order.get("amount", None),
|
|
352
341
|
status=status,
|
|
342
|
+
cost=ccxt_order.get("cost", None),
|
|
353
343
|
order_type=ccxt_order.get("type", None),
|
|
354
344
|
order_side=ccxt_order.get("side", None),
|
|
355
345
|
filled=ccxt_order.get("filled", None),
|
|
356
346
|
remaining=ccxt_order.get("remaining", None),
|
|
357
|
-
cost=ccxt_order.get("cost", None),
|
|
358
347
|
order_fee=order_fee,
|
|
359
348
|
order_fee_currency=order_fee_currency,
|
|
360
349
|
order_fee_rate=order_fee_rate,
|
|
361
|
-
created_at=
|
|
350
|
+
created_at=created_at
|
|
362
351
|
)
|
|
363
352
|
|
|
364
353
|
def __repr__(self):
|
|
@@ -372,7 +361,6 @@ class Order(BaseModel):
|
|
|
372
361
|
id=id_value,
|
|
373
362
|
price=self.get_price(),
|
|
374
363
|
amount=self.get_amount(),
|
|
375
|
-
net_gain=self.get_net_gain(),
|
|
376
364
|
external_id=self.external_id,
|
|
377
365
|
status=self.status,
|
|
378
366
|
target_symbol=self.target_symbol,
|
|
@@ -381,10 +369,16 @@ class Order(BaseModel):
|
|
|
381
369
|
order_type=self.order_type,
|
|
382
370
|
filled=self.get_filled(),
|
|
383
371
|
remaining=self.get_remaining(),
|
|
384
|
-
cost=self.get_cost(),
|
|
385
|
-
trade_closed_at=self.get_trade_closed_at(),
|
|
386
|
-
trade_closed_price=self.get_trade_closed_price(),
|
|
387
|
-
trade_closed_amount=self.get_trade_closed_amount(),
|
|
388
372
|
created_at=self.get_created_at(),
|
|
389
373
|
updated_at=self.get_updated_at(),
|
|
390
374
|
)
|
|
375
|
+
|
|
376
|
+
def get_size(self):
|
|
377
|
+
"""
|
|
378
|
+
Get the size of the order
|
|
379
|
+
|
|
380
|
+
Returns:
|
|
381
|
+
float: The size of the order
|
|
382
|
+
"""
|
|
383
|
+
return self.get_amount() * self.get_price() \
|
|
384
|
+
if self.get_price() is not None else 0
|
|
@@ -18,7 +18,7 @@ class OrderStatus(Enum):
|
|
|
18
18
|
if value.upper() == order_type.value:
|
|
19
19
|
return order_type
|
|
20
20
|
|
|
21
|
-
raise ValueError("Could not convert value to OrderStatus")
|
|
21
|
+
raise ValueError(f"Could not convert value {value} to OrderStatus")
|
|
22
22
|
|
|
23
23
|
@staticmethod
|
|
24
24
|
def from_value(value):
|
|
@@ -31,7 +31,7 @@ class OrderStatus(Enum):
|
|
|
31
31
|
elif isinstance(value, str):
|
|
32
32
|
return OrderStatus.from_string(value)
|
|
33
33
|
|
|
34
|
-
raise ValueError("Could not convert value to OrderStatus")
|
|
34
|
+
raise ValueError(f"Could not convert value: {value} to OrderStatus")
|
|
35
35
|
|
|
36
36
|
def equals(self, other):
|
|
37
37
|
return OrderStatus.from_value(other) == self
|