investing-algorithm-framework 6.9.1__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 +147 -44
- investing_algorithm_framework/app/__init__.py +23 -6
- investing_algorithm_framework/app/algorithm/algorithm.py +5 -41
- investing_algorithm_framework/app/algorithm/algorithm_factory.py +17 -10
- 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 +1322 -707
- investing_algorithm_framework/app/context.py +196 -88
- investing_algorithm_framework/app/eventloop.py +590 -0
- investing_algorithm_framework/app/reporting/__init__.py +16 -5
- investing_algorithm_framework/app/reporting/ascii.py +57 -202
- investing_algorithm_framework/app/reporting/backtest_report.py +284 -170
- investing_algorithm_framework/app/reporting/charts/__init__.py +10 -2
- 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 +11 -26
- investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
- investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
- investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +1 -1
- investing_algorithm_framework/app/reporting/generate.py +100 -114
- investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +40 -32
- investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +34 -27
- investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +23 -19
- investing_algorithm_framework/app/reporting/tables/trades_table.py +1 -1
- investing_algorithm_framework/app/reporting/tables/utils.py +1 -0
- investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +10 -16
- investing_algorithm_framework/app/strategy.py +315 -175
- investing_algorithm_framework/app/task.py +5 -3
- investing_algorithm_framework/cli/cli.py +30 -12
- investing_algorithm_framework/cli/deploy_to_aws_lambda.py +131 -34
- investing_algorithm_framework/cli/initialize_app.py +20 -1
- investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +18 -6
- 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_requirements.txt.template +2 -2
- investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +1 -1
- investing_algorithm_framework/create_app.py +3 -5
- investing_algorithm_framework/dependency_container.py +25 -39
- investing_algorithm_framework/domain/__init__.py +45 -38
- 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 +27 -0
- investing_algorithm_framework/domain/constants.py +6 -34
- investing_algorithm_framework/domain/data_provider.py +200 -56
- investing_algorithm_framework/domain/exceptions.py +34 -1
- investing_algorithm_framework/domain/models/__init__.py +10 -19
- investing_algorithm_framework/domain/models/base_model.py +0 -6
- 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/{market_data_type.py → data/data_type.py} +7 -7
- investing_algorithm_framework/domain/models/market/market_credential.py +6 -0
- investing_algorithm_framework/domain/models/order/order.py +34 -13
- investing_algorithm_framework/domain/models/order/order_status.py +1 -1
- investing_algorithm_framework/domain/models/order/order_type.py +1 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +14 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +5 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +51 -11
- investing_algorithm_framework/domain/models/position/__init__.py +2 -1
- investing_algorithm_framework/domain/models/position/position.py +9 -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 +0 -1
- investing_algorithm_framework/domain/models/strategy_profile.py +19 -151
- investing_algorithm_framework/domain/models/time_frame.py +7 -0
- investing_algorithm_framework/domain/models/time_interval.py +33 -0
- investing_algorithm_framework/domain/models/time_unit.py +63 -1
- investing_algorithm_framework/domain/models/trade/__init__.py +0 -2
- investing_algorithm_framework/domain/models/trade/trade.py +56 -32
- investing_algorithm_framework/domain/models/trade/trade_status.py +8 -2
- investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +106 -41
- investing_algorithm_framework/domain/models/trade/trade_take_profit.py +161 -99
- investing_algorithm_framework/domain/order_executor.py +19 -0
- investing_algorithm_framework/domain/portfolio_provider.py +20 -1
- investing_algorithm_framework/domain/services/__init__.py +0 -13
- investing_algorithm_framework/domain/strategy.py +1 -29
- investing_algorithm_framework/domain/utils/__init__.py +5 -1
- investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
- investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
- investing_algorithm_framework/domain/utils/polars.py +17 -14
- investing_algorithm_framework/download_data.py +40 -10
- investing_algorithm_framework/infrastructure/__init__.py +13 -25
- investing_algorithm_framework/infrastructure/data_providers/__init__.py +7 -4
- investing_algorithm_framework/infrastructure/data_providers/ccxt.py +811 -546
- investing_algorithm_framework/infrastructure/data_providers/csv.py +433 -122
- 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 +81 -0
- investing_algorithm_framework/infrastructure/models/__init__.py +0 -13
- investing_algorithm_framework/infrastructure/models/order/order.py +9 -3
- investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +27 -8
- investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +21 -7
- investing_algorithm_framework/infrastructure/order_executors/__init__.py +2 -0
- investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +16 -2
- investing_algorithm_framework/infrastructure/repositories/trade_repository.py +2 -2
- investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +6 -0
- investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +6 -0
- investing_algorithm_framework/infrastructure/services/__init__.py +0 -4
- investing_algorithm_framework/services/__init__.py +105 -8
- investing_algorithm_framework/services/backtesting/backtest_service.py +536 -476
- investing_algorithm_framework/services/configuration_service.py +14 -4
- 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/{app/reporting → services}/metrics/__init__.py +48 -17
- investing_algorithm_framework/{app/reporting → services}/metrics/drawdown.py +10 -10
- investing_algorithm_framework/{app/reporting → services}/metrics/equity_curve.py +2 -2
- investing_algorithm_framework/{app/reporting → services}/metrics/exposure.py +60 -2
- investing_algorithm_framework/services/metrics/generate.py +358 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/profit_factor.py +36 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/recovery.py +2 -2
- investing_algorithm_framework/{app/reporting → services}/metrics/returns.py +146 -147
- investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
- investing_algorithm_framework/{app/reporting/metrics/sharp_ratio.py → services/metrics/sharpe_ratio.py} +6 -10
- investing_algorithm_framework/{app/reporting → services}/metrics/sortino_ratio.py +3 -7
- investing_algorithm_framework/services/metrics/trades.py +500 -0
- investing_algorithm_framework/services/metrics/volatility.py +97 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/win_rate.py +70 -3
- investing_algorithm_framework/services/order_service/order_backtest_service.py +21 -31
- investing_algorithm_framework/services/order_service/order_service.py +9 -71
- investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +0 -2
- investing_algorithm_framework/services/portfolios/portfolio_service.py +3 -13
- investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +62 -96
- investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +0 -3
- investing_algorithm_framework/services/repository_service.py +5 -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 +51 -29
- 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-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/RECORD +159 -148
- investing_algorithm_framework/app/reporting/evaluation.py +0 -243
- investing_algorithm_framework/app/reporting/metrics/risk_free_rate.py +0 -8
- investing_algorithm_framework/app/reporting/metrics/volatility.py +0 -69
- investing_algorithm_framework/cli/templates/requirements_azure_function.txt.template +0 -3
- investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -9
- investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -47
- investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -120
- investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -0
- investing_algorithm_framework/domain/models/backtesting/backtest_results.py +0 -440
- investing_algorithm_framework/domain/models/data_source.py +0 -21
- investing_algorithm_framework/domain/models/date_range.py +0 -64
- investing_algorithm_framework/domain/models/trade/trade_risk_type.py +0 -34
- investing_algorithm_framework/domain/models/trading_data_types.py +0 -48
- investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
- investing_algorithm_framework/domain/services/market_data_sources.py +0 -543
- investing_algorithm_framework/domain/services/market_service.py +0 -153
- investing_algorithm_framework/domain/services/observable.py +0 -51
- investing_algorithm_framework/domain/services/observer.py +0 -19
- investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -16
- investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -746
- investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -270
- investing_algorithm_framework/infrastructure/models/market_data_sources/pandas.py +0 -312
- investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -5
- investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -471
- 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 -322
- investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -10
- investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -269
- investing_algorithm_framework/services/market_data_source_service/data_provider_service.py +0 -350
- investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -377
- investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -296
- investing_algorithm_framework-6.9.1.dist-info/METADATA +0 -440
- /investing_algorithm_framework/{app/reporting → services}/metrics/alpha.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/beta.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/cagr.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/calmar_ratio.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/mean_daily_return.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/price_efficiency.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/standard_deviation.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/treynor_ratio.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/ulcer.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/value_at_risk.py +0 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/LICENSE +0 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/WHEEL +0 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/entry_points.txt +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, \
|
|
@@ -38,9 +39,89 @@ def setup_sqlalchemy(app, throw_exception_if_not_set=True):
|
|
|
38
39
|
return app
|
|
39
40
|
|
|
40
41
|
|
|
42
|
+
|
|
41
43
|
class SQLBaseModel(DeclarativeBase):
|
|
42
44
|
pass
|
|
43
45
|
|
|
44
46
|
|
|
45
47
|
def create_all_tables():
|
|
46
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,8 +1,3 @@
|
|
|
1
|
-
from .market_data_sources import CCXTOrderBookMarketDataSource, \
|
|
2
|
-
CCXTTickerMarketDataSource, CCXTOHLCVMarketDataSource, \
|
|
3
|
-
CCXTOHLCVBacktestMarketDataSource, CSVOHLCVMarketDataSource, \
|
|
4
|
-
CSVTickerMarketDataSource, PandasOHLCVBacktestMarketDataSource, \
|
|
5
|
-
PandasOHLCVMarketDataSource
|
|
6
1
|
from .order import SQLOrder, SQLOrderMetadata
|
|
7
2
|
from .portfolio import SQLPortfolio, SQLPortfolioSnapshot
|
|
8
3
|
from .position import SQLPosition, SQLPositionSnapshot
|
|
@@ -14,16 +9,8 @@ __all__ = [
|
|
|
14
9
|
"SQLPortfolio",
|
|
15
10
|
"SQLPositionSnapshot",
|
|
16
11
|
"SQLPortfolioSnapshot",
|
|
17
|
-
"CCXTOHLCVBacktestMarketDataSource",
|
|
18
|
-
"CCXTOrderBookMarketDataSource",
|
|
19
|
-
"CCXTTickerMarketDataSource",
|
|
20
|
-
"CCXTOHLCVMarketDataSource",
|
|
21
|
-
"CSVTickerMarketDataSource",
|
|
22
|
-
"CSVOHLCVMarketDataSource",
|
|
23
12
|
"SQLTrade",
|
|
24
13
|
"SQLTradeStopLoss",
|
|
25
14
|
"SQLTradeTakeProfit",
|
|
26
15
|
"SQLOrderMetadata",
|
|
27
|
-
"PandasOHLCVBacktestMarketDataSource",
|
|
28
|
-
"PandasOHLCVMarketDataSource"
|
|
29
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
|
|
@@ -15,6 +15,10 @@ from investing_algorithm_framework.infrastructure.models.\
|
|
|
15
15
|
logger = logging.getLogger("investing_algorithm_framework")
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
def utcnow():
|
|
19
|
+
return datetime.now(tz=timezone.utc)
|
|
20
|
+
|
|
21
|
+
|
|
18
22
|
class SQLOrder(Order, SQLBaseModel, SQLAlchemyModelExtension):
|
|
19
23
|
"""
|
|
20
24
|
SQLOrder model based on the Order domain model.
|
|
@@ -37,8 +41,10 @@ class SQLOrder(Order, SQLBaseModel, SQLAlchemyModelExtension):
|
|
|
37
41
|
status = Column(String, default=OrderStatus.CREATED.value)
|
|
38
42
|
position_id = Column(Integer, ForeignKey('positions.id'))
|
|
39
43
|
position = relationship("SQLPosition", back_populates="orders")
|
|
40
|
-
created_at = Column(DateTime, default=
|
|
41
|
-
updated_at = Column(
|
|
44
|
+
created_at = Column(DateTime(timezone=True), default=utcnow)
|
|
45
|
+
updated_at = Column(
|
|
46
|
+
DateTime(timezone=True), default=utcnow, onupdate=utcnow
|
|
47
|
+
)
|
|
42
48
|
order_fee = Column(Float, default=None)
|
|
43
49
|
order_fee_currency = Column(String, default=None)
|
|
44
50
|
order_fee_rate = Column(Float, default=None)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from sqlalchemy import Column, Integer, String, Float, ForeignKey, Boolean
|
|
1
|
+
from sqlalchemy import Column, Integer, String, Float, ForeignKey, Boolean, \
|
|
2
|
+
DateTime
|
|
2
3
|
from sqlalchemy.orm import relationship
|
|
3
4
|
|
|
4
5
|
from investing_algorithm_framework.domain import TradeStopLoss
|
|
@@ -14,19 +15,33 @@ class SQLTradeStopLoss(TradeStopLoss, SQLBaseModel, SQLAlchemyModelExtension):
|
|
|
14
15
|
A trade stop loss is a stop loss strategy for a trade.
|
|
15
16
|
|
|
16
17
|
Attributes:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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.
|
|
23
38
|
"""
|
|
24
39
|
|
|
25
40
|
__tablename__ = "trade_stop_losses"
|
|
26
41
|
id = Column(Integer, primary_key=True, unique=True)
|
|
27
42
|
trade_id = Column(Integer, ForeignKey('trades.id'))
|
|
28
43
|
trade = relationship('SQLTrade', back_populates='stop_losses')
|
|
29
|
-
|
|
44
|
+
trailing = Column(Boolean)
|
|
30
45
|
percentage = Column(Float)
|
|
31
46
|
sell_percentage = Column(Float)
|
|
32
47
|
open_price = Column(Float)
|
|
@@ -38,3 +53,7 @@ class SQLTradeStopLoss(TradeStopLoss, SQLBaseModel, SQLAlchemyModelExtension):
|
|
|
38
53
|
sell_amount = Column(Float)
|
|
39
54
|
sold_amount = Column(Float)
|
|
40
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)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from sqlalchemy import Column, Integer, String, Float, ForeignKey, Boolean
|
|
1
|
+
from sqlalchemy import Column, Integer, String, Float, ForeignKey, Boolean, \
|
|
2
|
+
DateTime
|
|
2
3
|
from sqlalchemy.orm import relationship
|
|
3
4
|
|
|
4
5
|
from investing_algorithm_framework.domain import TradeTakeProfit
|
|
@@ -16,18 +17,27 @@ class SQLTradeTakeProfit(
|
|
|
16
17
|
A trade take profit is a take profit strategy for a trade.
|
|
17
18
|
|
|
18
19
|
Attributes:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
- trade (Trade): the trade that the take profit is for
|
|
21
|
+
- percentage (float): the take profit percentage
|
|
22
|
+
- trailing (bool): indicates whether the take profit is trailing
|
|
23
|
+
- sell_percentage (float) the percentage of the trade to sell
|
|
24
|
+
when the take profit is triggered.
|
|
25
|
+
- target_price (float): the target price at which to take profit.
|
|
26
|
+
- sell_prices (String): a serialized list of prices at which
|
|
27
|
+
take profits were executed.
|
|
28
|
+
- sell_dates (String): a serialized list of dates when
|
|
29
|
+
take profits were executed.
|
|
30
|
+
- sell_amount (float): the total amount sold due to take profits.
|
|
31
|
+
- sold_amount (float): the total amount that has been sold due
|
|
32
|
+
to take profits.
|
|
33
|
+
- active (bool): indicates whether the take profit is currently active.
|
|
24
34
|
"""
|
|
25
35
|
|
|
26
36
|
__tablename__ = "trade_take_profits"
|
|
27
37
|
id = Column(Integer, primary_key=True, unique=True)
|
|
28
38
|
trade_id = Column(Integer, ForeignKey('trades.id'))
|
|
29
39
|
trade = relationship('SQLTrade', back_populates='take_profits')
|
|
30
|
-
|
|
40
|
+
trailing = Column(Boolean)
|
|
31
41
|
percentage = Column(Float)
|
|
32
42
|
sell_percentage = Column(Float)
|
|
33
43
|
open_price = Column(Float)
|
|
@@ -39,3 +49,7 @@ class SQLTradeTakeProfit(
|
|
|
39
49
|
sell_dates = Column(String)
|
|
40
50
|
sold_amount = Column(Float)
|
|
41
51
|
active = Column(Boolean)
|
|
52
|
+
triggered = Column(Boolean, default=False)
|
|
53
|
+
triggered_at = Column(DateTime, default=None)
|
|
54
|
+
created_at = Column(DateTime)
|
|
55
|
+
updated_at = Column(DateTime, default=None)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from .ccxt_order_executor import CCXTOrderExecutor
|
|
2
|
+
from .backtest_oder_executor import BacktestOrderExecutor
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
def get_default_order_executors():
|
|
@@ -15,5 +16,6 @@ def get_default_order_executors():
|
|
|
15
16
|
|
|
16
17
|
__all__ = [
|
|
17
18
|
'CCXTOrderExecutor',
|
|
19
|
+
'BacktestOrderExecutor',
|
|
18
20
|
'get_default_order_executors',
|
|
19
21
|
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from investing_algorithm_framework.domain import OrderExecutor, OrderStatus, \
|
|
2
|
+
INDEX_DATETIME, Order
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class BacktestOrderExecutor(OrderExecutor):
|
|
6
|
+
"""
|
|
7
|
+
Backtest implementation of order executor. This executor is used to
|
|
8
|
+
simulate order execution in a backtesting environment.
|
|
9
|
+
|
|
10
|
+
!Important: This executor does not actually execute orders on any market.
|
|
11
|
+
It should be used only for backtesting purposes.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def execute_order(self, portfolio, order, market_credential) -> Order:
|
|
15
|
+
order.status = OrderStatus.OPEN.value
|
|
16
|
+
order.remaining = order.get_amount()
|
|
17
|
+
order.filled = 0
|
|
18
|
+
order.updated_at = self.config[INDEX_DATETIME]
|
|
19
|
+
return order
|
|
20
|
+
|
|
21
|
+
def cancel_order(self, portfolio, order, market_credential) -> Order:
|
|
22
|
+
order.status = OrderStatus.CANCELED.value
|
|
23
|
+
order.remaining = 0
|
|
24
|
+
order.updated_at = self.config[INDEX_DATETIME]
|
|
25
|
+
return order
|
|
26
|
+
|
|
27
|
+
def supports_market(self, market):
|
|
28
|
+
return True
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
3
|
from typing import Callable
|
|
4
|
+
from dateutil.parser import parse
|
|
4
5
|
|
|
5
6
|
from sqlalchemy.exc import SQLAlchemyError
|
|
6
7
|
from werkzeug.datastructures import MultiDict
|
|
@@ -12,6 +13,16 @@ from investing_algorithm_framework.infrastructure.database import Session
|
|
|
12
13
|
logger = logging.getLogger("investing_algorithm_framework")
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
def convert_datetime_fields(data, datetime_fields):
|
|
17
|
+
for field in datetime_fields:
|
|
18
|
+
if field in data and isinstance(data[field], str):
|
|
19
|
+
try:
|
|
20
|
+
data[field] = parse(data[field])
|
|
21
|
+
except Exception:
|
|
22
|
+
pass # Ignore if not a valid datetime string
|
|
23
|
+
return data
|
|
24
|
+
|
|
25
|
+
|
|
15
26
|
class Repository(ABC):
|
|
16
27
|
base_class: Callable
|
|
17
28
|
DEFAULT_NOT_FOUND_MESSAGE = "The requested resource was not found"
|
|
@@ -20,7 +31,6 @@ class Repository(ABC):
|
|
|
20
31
|
|
|
21
32
|
def create(self, data, save=True):
|
|
22
33
|
created_object = self.base_class(**data)
|
|
23
|
-
|
|
24
34
|
if save:
|
|
25
35
|
with Session() as db:
|
|
26
36
|
try:
|
|
@@ -35,7 +45,11 @@ class Repository(ABC):
|
|
|
35
45
|
return created_object
|
|
36
46
|
|
|
37
47
|
def update(self, object_id, data):
|
|
38
|
-
|
|
48
|
+
# List all datetime fields for your model
|
|
49
|
+
datetime_fields = [
|
|
50
|
+
"created_at", "updated_at", "closed_at", "opened_at"
|
|
51
|
+
]
|
|
52
|
+
data = convert_datetime_fields(data, datetime_fields)
|
|
39
53
|
with Session() as db:
|
|
40
54
|
try:
|
|
41
55
|
update_object = self.get(object_id)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from sqlalchemy.exc import SQLAlchemyError
|
|
3
3
|
|
|
4
|
-
from investing_algorithm_framework.domain import
|
|
4
|
+
from investing_algorithm_framework.domain import TradeStatus, ApiException
|
|
5
5
|
from investing_algorithm_framework.infrastructure.models import SQLPosition, \
|
|
6
6
|
SQLPortfolio, SQLTrade, SQLOrder
|
|
7
7
|
from investing_algorithm_framework.infrastructure.database import Session
|
|
@@ -43,7 +43,7 @@ class SQLTradeRepository(Repository):
|
|
|
43
43
|
.filter(SQLPosition.portfolio_id == portfolio.id)
|
|
44
44
|
|
|
45
45
|
if status_query_param:
|
|
46
|
-
status =
|
|
46
|
+
status = TradeStatus.from_value(status_query_param)
|
|
47
47
|
# Explicitly filter on SQLTrade.status
|
|
48
48
|
query = query.filter(SQLTrade.status == status.value)
|
|
49
49
|
|
|
@@ -14,10 +14,16 @@ class SQLTradeStopLossRepository(Repository):
|
|
|
14
14
|
|
|
15
15
|
def _apply_query_params(self, db, query, query_params):
|
|
16
16
|
trade_query_param = self.get_query_param("trade_id", query_params)
|
|
17
|
+
triggered_query_param = self.get_query_param(
|
|
18
|
+
"triggered", query_params
|
|
19
|
+
)
|
|
17
20
|
|
|
18
21
|
if trade_query_param:
|
|
19
22
|
query = query.filter(
|
|
20
23
|
SQLTradeStopLoss.trade_id == trade_query_param
|
|
21
24
|
)
|
|
22
25
|
|
|
26
|
+
if triggered_query_param is not None:
|
|
27
|
+
query = query.filter_by(triggered=triggered_query_param)
|
|
28
|
+
|
|
23
29
|
return query
|
|
@@ -14,10 +14,16 @@ class SQLTradeTakeProfitRepository(Repository):
|
|
|
14
14
|
|
|
15
15
|
def _apply_query_params(self, db, query, query_params):
|
|
16
16
|
trade_query_param = self.get_query_param("trade_id", query_params)
|
|
17
|
+
triggered_query_param = self.get_query_param(
|
|
18
|
+
"triggered", query_params
|
|
19
|
+
)
|
|
17
20
|
|
|
18
21
|
if trade_query_param:
|
|
19
22
|
query = query.filter(
|
|
20
23
|
SQLTradeTakeProfit.trade_id == trade_query_param
|
|
21
24
|
)
|
|
22
25
|
|
|
26
|
+
if triggered_query_param is not None:
|
|
27
|
+
query = query.filter_by(triggered=triggered_query_param)
|
|
28
|
+
|
|
23
29
|
return query
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
from .market_service import CCXTMarketService
|
|
2
|
-
from .performance_service import PerformanceService
|
|
3
1
|
from .azure import AzureBlobStorageStateHandler
|
|
4
2
|
from .aws import AWSS3StorageStateHandler
|
|
5
3
|
|
|
6
4
|
__all__ = [
|
|
7
|
-
"PerformanceService",
|
|
8
|
-
"CCXTMarketService",
|
|
9
5
|
"AzureBlobStorageStateHandler",
|
|
10
6
|
"AWSS3StorageStateHandler"
|
|
11
7
|
]
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from .backtesting import BacktestService
|
|
2
|
+
from .trade_order_evaluator import BacktestTradeOrderEvaluator, \
|
|
3
|
+
TradeOrderEvaluator, DefaultTradeOrderEvaluator
|
|
2
4
|
from .configuration_service import ConfigurationService
|
|
3
5
|
from .market_credential_service import MarketCredentialService
|
|
4
|
-
from .
|
|
5
|
-
BacktestMarketDataSourceService, DataProviderService
|
|
6
|
+
from .data_providers import DataProviderService
|
|
6
7
|
from .order_service import OrderService, OrderBacktestService, \
|
|
7
8
|
OrderExecutorLookup
|
|
8
9
|
from .portfolios import PortfolioService, BacktestPortfolioService, \
|
|
@@ -10,17 +11,41 @@ from .portfolios import PortfolioService, BacktestPortfolioService, \
|
|
|
10
11
|
PortfolioSnapshotService, PortfolioProviderLookup
|
|
11
12
|
from .positions import PositionService, PositionSnapshotService
|
|
12
13
|
from .repository_service import RepositoryService
|
|
13
|
-
from .
|
|
14
|
-
|
|
14
|
+
from .trade_service import TradeService, TradeStopLossService, \
|
|
15
|
+
TradeTakeProfitService
|
|
16
|
+
from .metrics import get_annual_volatility, \
|
|
17
|
+
get_sortino_ratio, get_drawdown_series, get_max_drawdown, \
|
|
18
|
+
get_equity_curve, get_price_efficiency_ratio, get_sharpe_ratio, \
|
|
19
|
+
get_profit_factor, get_cumulative_profit_factor_series, \
|
|
20
|
+
get_rolling_profit_factor_series, \
|
|
21
|
+
get_cagr, get_standard_deviation_returns, \
|
|
22
|
+
get_standard_deviation_downside_returns, \
|
|
23
|
+
get_total_return, get_cumulative_exposure, get_exposure_ratio, \
|
|
24
|
+
get_yearly_returns, get_monthly_returns, get_best_year, \
|
|
25
|
+
get_best_month, get_worst_year, get_worst_month, get_best_trade, \
|
|
26
|
+
get_worst_trade, get_average_yearly_return, get_average_trade_gain, \
|
|
27
|
+
get_average_trade_loss, get_average_monthly_return, \
|
|
28
|
+
get_percentage_winning_months, get_average_trade_duration, \
|
|
29
|
+
get_trade_frequency, get_win_rate, get_win_loss_ratio, \
|
|
30
|
+
get_calmar_ratio, get_max_drawdown_absolute, get_current_win_loss_ratio, \
|
|
31
|
+
get_max_drawdown_duration, get_max_daily_drawdown, get_trades_per_day, \
|
|
32
|
+
get_trades_per_year, get_average_monthly_return_losing_months, \
|
|
33
|
+
get_average_monthly_return_winning_months, get_percentage_winning_years, \
|
|
34
|
+
get_rolling_sharpe_ratio, create_backtest_metrics, get_total_growth, \
|
|
35
|
+
get_total_loss, get_risk_free_rate_us, get_median_trade_return, \
|
|
36
|
+
get_average_trade_return, get_cumulative_return, \
|
|
37
|
+
get_cumulative_return_series, get_average_trade_size, \
|
|
38
|
+
get_positive_trades, get_negative_trades, get_number_of_trades, \
|
|
39
|
+
get_current_win_rate, get_current_average_trade_return, \
|
|
40
|
+
get_current_average_trade_loss, get_current_average_trade_duration, \
|
|
41
|
+
get_current_average_trade_gain, create_backtest_metrics_for_backtest
|
|
15
42
|
|
|
16
43
|
__all__ = [
|
|
17
|
-
"StrategyOrchestratorService",
|
|
18
44
|
"OrderService",
|
|
19
45
|
"RepositoryService",
|
|
20
46
|
"PortfolioService",
|
|
21
47
|
"PositionService",
|
|
22
48
|
"PortfolioConfigurationService",
|
|
23
|
-
"MarketDataSourceService",
|
|
24
49
|
"BacktestService",
|
|
25
50
|
"OrderBacktestService",
|
|
26
51
|
"ConfigurationService",
|
|
@@ -28,11 +53,83 @@ __all__ = [
|
|
|
28
53
|
"PortfolioSnapshotService",
|
|
29
54
|
"PositionSnapshotService",
|
|
30
55
|
"MarketCredentialService",
|
|
31
|
-
"BacktestMarketDataSourceService",
|
|
32
56
|
"BacktestPortfolioService",
|
|
33
57
|
"TradeService",
|
|
34
58
|
"DataProviderService",
|
|
35
59
|
"OrderExecutorLookup",
|
|
36
|
-
"
|
|
60
|
+
"BacktestTradeOrderEvaluator",
|
|
37
61
|
"PortfolioProviderLookup",
|
|
62
|
+
"TradeOrderEvaluator",
|
|
63
|
+
"DefaultTradeOrderEvaluator",
|
|
64
|
+
"get_risk_free_rate_us",
|
|
65
|
+
"get_annual_volatility",
|
|
66
|
+
"get_sortino_ratio",
|
|
67
|
+
"get_drawdown_series",
|
|
68
|
+
"get_max_drawdown",
|
|
69
|
+
"get_equity_curve",
|
|
70
|
+
"get_price_efficiency_ratio",
|
|
71
|
+
"get_sharpe_ratio",
|
|
72
|
+
"get_profit_factor",
|
|
73
|
+
"get_cumulative_profit_factor_series",
|
|
74
|
+
"get_rolling_profit_factor_series",
|
|
75
|
+
"get_sharpe_ratio",
|
|
76
|
+
"get_cagr",
|
|
77
|
+
"get_standard_deviation_returns",
|
|
78
|
+
"get_standard_deviation_downside_returns",
|
|
79
|
+
"get_max_drawdown_absolute",
|
|
80
|
+
"get_total_return",
|
|
81
|
+
"get_cumulative_exposure",
|
|
82
|
+
"get_exposure_ratio",
|
|
83
|
+
"get_average_trade_duration",
|
|
84
|
+
"get_win_rate",
|
|
85
|
+
"get_win_loss_ratio",
|
|
86
|
+
"get_calmar_ratio",
|
|
87
|
+
"get_trade_frequency",
|
|
88
|
+
"get_yearly_returns",
|
|
89
|
+
"get_monthly_returns",
|
|
90
|
+
"get_best_year",
|
|
91
|
+
"get_best_month",
|
|
92
|
+
"get_worst_year",
|
|
93
|
+
"get_worst_month",
|
|
94
|
+
"get_best_trade",
|
|
95
|
+
"get_worst_trade",
|
|
96
|
+
"get_average_yearly_return",
|
|
97
|
+
"get_average_trade_loss",
|
|
98
|
+
"get_average_monthly_return",
|
|
99
|
+
"get_percentage_winning_months",
|
|
100
|
+
"get_average_trade_duration",
|
|
101
|
+
"get_trade_frequency",
|
|
102
|
+
"get_win_rate",
|
|
103
|
+
"get_win_loss_ratio",
|
|
104
|
+
"get_calmar_ratio",
|
|
105
|
+
"get_max_drawdown_duration",
|
|
106
|
+
"get_max_daily_drawdown",
|
|
107
|
+
"get_trades_per_day",
|
|
108
|
+
"get_trades_per_year",
|
|
109
|
+
"get_average_monthly_return_losing_months",
|
|
110
|
+
"get_average_monthly_return_winning_months",
|
|
111
|
+
"get_percentage_winning_years",
|
|
112
|
+
"get_rolling_sharpe_ratio",
|
|
113
|
+
"get_total_growth",
|
|
114
|
+
"create_backtest_metrics",
|
|
115
|
+
"get_total_loss",
|
|
116
|
+
"get_median_trade_return",
|
|
117
|
+
"get_average_trade_gain",
|
|
118
|
+
"get_average_trade_size",
|
|
119
|
+
"get_average_trade_return",
|
|
120
|
+
"get_positive_trades",
|
|
121
|
+
"get_negative_trades",
|
|
122
|
+
"get_number_of_trades",
|
|
123
|
+
"get_cumulative_return",
|
|
124
|
+
"get_cumulative_return_series",
|
|
125
|
+
"get_current_win_loss_ratio",
|
|
126
|
+
"get_current_win_rate",
|
|
127
|
+
"get_current_win_loss_ratio",
|
|
128
|
+
"get_current_average_trade_loss",
|
|
129
|
+
"get_current_average_trade_duration",
|
|
130
|
+
"get_current_average_trade_gain",
|
|
131
|
+
"get_current_average_trade_return",
|
|
132
|
+
"create_backtest_metrics_for_backtest",
|
|
133
|
+
"TradeStopLossService",
|
|
134
|
+
"TradeTakeProfitService"
|
|
38
135
|
]
|