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,40 +1,85 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
3
|
-
|
|
4
|
-
from .algorithm import Algorithm
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import List, Dict, Any, Union
|
|
3
|
+
|
|
5
4
|
import pandas as pd
|
|
6
5
|
|
|
6
|
+
from investing_algorithm_framework.domain import OperationalException, \
|
|
7
|
+
Position, PositionSize, TimeUnit, StrategyProfile, Trade, \
|
|
8
|
+
DataSource, OrderSide, StopLossRule, TakeProfitRule, Order, \
|
|
9
|
+
INDEX_DATETIME
|
|
10
|
+
from .context import Context
|
|
11
|
+
|
|
7
12
|
|
|
8
13
|
class TradingStrategy:
|
|
9
|
-
|
|
14
|
+
"""
|
|
15
|
+
TradingStrategy is the base class for all trading strategies. A trading
|
|
16
|
+
strategy is a set of rules that defines when to buy or sell an asset.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
time_unit: TimeUnit - the time unit of the strategy that defines
|
|
20
|
+
when the strategy should run e.g. HOUR, DAY, WEEK, MONTH
|
|
21
|
+
interval: int - the interval of the strategy that defines how often
|
|
22
|
+
the strategy should run within the time unit e.g. every 5 hours,
|
|
23
|
+
every 2 days, every 3 weeks, every 4 months
|
|
24
|
+
worker_id (optional): str - the id of the worker
|
|
25
|
+
strategy_id (optional): str - the id of the strategy
|
|
26
|
+
decorated (optional): function - the decorated function
|
|
27
|
+
data_sources (List[DataSource] optional): the list of data
|
|
28
|
+
sources to use for the strategy. The data sources will be used
|
|
29
|
+
to indentify data providers that will be called to gather data
|
|
30
|
+
and pass to the strategy before its run.
|
|
31
|
+
metadata (optional): Dict[str, Any] - a dictionary
|
|
32
|
+
containing metadata about the strategy. This can be used to
|
|
33
|
+
store additional information about the strategy, such as its
|
|
34
|
+
author, version, description, params etc.
|
|
35
|
+
"""
|
|
36
|
+
time_unit: TimeUnit = None
|
|
10
37
|
interval: int = None
|
|
11
38
|
worker_id: str = None
|
|
12
39
|
strategy_id: str = None
|
|
13
40
|
decorated = None
|
|
14
|
-
|
|
41
|
+
data_sources: List[DataSource] = []
|
|
15
42
|
traces = None
|
|
43
|
+
context: Context = None
|
|
44
|
+
metadata: Dict[str, Any] = None
|
|
45
|
+
position_sizes: List[PositionSize] = []
|
|
46
|
+
stop_loss_rules: List[StopLossRule] = []
|
|
47
|
+
take_profit_rules: List[TakeProfitRule] = []
|
|
48
|
+
symbols: List[str] = []
|
|
49
|
+
trading_symbol: str = None
|
|
16
50
|
|
|
17
51
|
def __init__(
|
|
18
52
|
self,
|
|
19
53
|
strategy_id=None,
|
|
20
54
|
time_unit=None,
|
|
21
55
|
interval=None,
|
|
22
|
-
|
|
56
|
+
data_sources=None,
|
|
57
|
+
metadata=None,
|
|
58
|
+
position_sizes=None,
|
|
59
|
+
symbols=None,
|
|
60
|
+
trading_symbol=None,
|
|
23
61
|
worker_id=None,
|
|
24
62
|
decorated=None
|
|
25
63
|
):
|
|
26
|
-
|
|
27
64
|
if time_unit is not None:
|
|
28
65
|
self.time_unit = TimeUnit.from_value(time_unit)
|
|
66
|
+
else:
|
|
67
|
+
# Check if time_unit is None
|
|
68
|
+
if self.time_unit is None:
|
|
69
|
+
raise OperationalException(
|
|
70
|
+
f"Time unit attribute not set for "
|
|
71
|
+
f"strategy instance {self.strategy_id}"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
self.time_unit = TimeUnit.from_value(self.time_unit)
|
|
29
75
|
|
|
30
76
|
if interval is not None:
|
|
31
77
|
self.interval = interval
|
|
32
78
|
|
|
33
|
-
if
|
|
34
|
-
self.
|
|
79
|
+
if data_sources is not None:
|
|
80
|
+
self.data_sources = data_sources
|
|
35
81
|
|
|
36
|
-
|
|
37
|
-
self.market_data_sources = market_data_sources
|
|
82
|
+
self.metadata = metadata
|
|
38
83
|
|
|
39
84
|
if decorated is not None:
|
|
40
85
|
self.decorated = decorated
|
|
@@ -51,11 +96,14 @@ class TradingStrategy:
|
|
|
51
96
|
else:
|
|
52
97
|
self.strategy_id = self.worker_id
|
|
53
98
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
99
|
+
if position_sizes is not None:
|
|
100
|
+
self.position_sizes = position_sizes
|
|
101
|
+
|
|
102
|
+
if symbols is not None:
|
|
103
|
+
self.symbols = symbols
|
|
104
|
+
|
|
105
|
+
if trading_symbol is not None:
|
|
106
|
+
self.trading_symbol = trading_symbol
|
|
59
107
|
|
|
60
108
|
# Check if interval is None
|
|
61
109
|
if self.interval is None:
|
|
@@ -65,17 +113,229 @@ class TradingStrategy:
|
|
|
65
113
|
|
|
66
114
|
# context initialization
|
|
67
115
|
self._context = None
|
|
116
|
+
self._last_run = None
|
|
117
|
+
self.stop_loss_rules_lookup = {}
|
|
118
|
+
self.take_profit_rules_lookup = {}
|
|
119
|
+
self.position_sizes_lookup = {}
|
|
120
|
+
|
|
121
|
+
def generate_buy_signals(
|
|
122
|
+
self, data: Dict[str, Any]
|
|
123
|
+
) -> Dict[str, pd.Series]:
|
|
124
|
+
"""
|
|
125
|
+
Function that needs to be implemented by the user.
|
|
126
|
+
This function should return a pandas Series containing the buy signals.
|
|
68
127
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
128
|
+
Args:
|
|
129
|
+
data (Dict[str, Any]): All the data that matched the
|
|
130
|
+
data sources of the strategy.
|
|
72
131
|
|
|
73
|
-
|
|
74
|
-
|
|
132
|
+
Returns:
|
|
133
|
+
Dict[str, Series]: A dictionary where the keys are the
|
|
134
|
+
symbols and the values are pandas Series containing
|
|
135
|
+
the buy signals. The series must be a pandas Series with
|
|
136
|
+
a boolean value for each row in the data source, e.g.
|
|
137
|
+
pd.Series([True, False, False, True, ...], index=data.index)
|
|
138
|
+
Also the return dictionary must look like:
|
|
139
|
+
{
|
|
140
|
+
"BTC": pd.Series([...]),
|
|
141
|
+
"ETH": pd.Series([...]),
|
|
142
|
+
...
|
|
143
|
+
}
|
|
144
|
+
where the symbols are exactly the same as defined in the
|
|
145
|
+
symbols attribute of the strategy.
|
|
146
|
+
"""
|
|
147
|
+
raise NotImplementedError(
|
|
148
|
+
"generate_buy_signals method not implemented"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
def generate_sell_signals(
|
|
152
|
+
self, data: Dict[str, Any]
|
|
153
|
+
) -> Dict[str, pd.Series]:
|
|
154
|
+
"""
|
|
155
|
+
Function that needs to be implemented by the user.
|
|
156
|
+
This function should return a pandas Series containing
|
|
157
|
+
the sell signals.
|
|
75
158
|
|
|
76
|
-
|
|
159
|
+
Args:
|
|
160
|
+
data (Dict[str, Any]): All the data that is defined in the
|
|
161
|
+
data sources of the strategy. E.g. if there is a data source
|
|
162
|
+
defined as DataSource(identifier="bitvavo_btc_eur_1h",
|
|
163
|
+
symbol="BTC/EUR", time_frame="1h", data_type=DataType.OHLCV,
|
|
164
|
+
window_size=100, market="BITVAVO"), the data dictionary
|
|
165
|
+
will contain a key "bitvavo_btc_eur_1h"
|
|
166
|
+
with the corresponding data as a polars DataFrame.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Dict[str, Series]: A dictionary where the keys are the
|
|
170
|
+
symbols and the values are pandas Series containing
|
|
171
|
+
the sell signals. The series must be a pandas Series with
|
|
172
|
+
a boolean value for each row in the data source, e.g.
|
|
173
|
+
pd.Series([True, False, False, True, ...], index=data.index)
|
|
174
|
+
Also the return dictionary must look like:
|
|
175
|
+
{
|
|
176
|
+
"BTC": pd.Series([...]),
|
|
177
|
+
"ETH": pd.Series([...]),
|
|
178
|
+
...
|
|
179
|
+
}
|
|
180
|
+
where the symbols are exactly the same as defined in the
|
|
181
|
+
symbols attribute of the strategy.
|
|
182
|
+
"""
|
|
183
|
+
raise NotImplementedError(
|
|
184
|
+
"generate_sell_signals method not implemented"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
def run_strategy(self, context: Context, data: Dict[str, Any]):
|
|
188
|
+
"""
|
|
189
|
+
Main function for running the strategy. This function will be called
|
|
190
|
+
by the framework when the trigger of your strategy is met.
|
|
191
|
+
|
|
192
|
+
The flow of this function is as follows:
|
|
193
|
+
1. Loop through all the symbols defined in the strategy.
|
|
194
|
+
2. For each symbol, check if there are any open orders.
|
|
195
|
+
A. If there are open orders, skip to the next symbol.
|
|
196
|
+
3. If there is no open position, generate buy signals
|
|
197
|
+
A. Generate buy signals
|
|
198
|
+
B. If there is a buy signal, retrieve the position size
|
|
199
|
+
defined for the symbol.
|
|
200
|
+
C. If there is a take profit or stop loss rule defined
|
|
201
|
+
for the symbol, register them for the trade that
|
|
202
|
+
has been created as part of the order execution.
|
|
203
|
+
4. If there is an open position, generate sell signals
|
|
204
|
+
A. Generate sell signals
|
|
205
|
+
B. If there is a sell signal, create a limit order to
|
|
206
|
+
sell the position.
|
|
207
|
+
|
|
208
|
+
During execution of this function, the context and market data
|
|
209
|
+
will be passed to the function. The context is an instance of
|
|
210
|
+
the Context class, this class has various methods to do operations
|
|
211
|
+
with your portfolio, orders, trades, positions and other components.
|
|
212
|
+
|
|
213
|
+
The market data is a dictionary containing all the data retrieved
|
|
214
|
+
from the specified data sources.
|
|
215
|
+
|
|
216
|
+
When buy or sell signals are generated, the strategy will create
|
|
217
|
+
limit orders to buy or sell the assets based on the generated signals.
|
|
218
|
+
For each symbol a corresponding position size must be defined. If
|
|
219
|
+
no position size is defined, an OperationalException will be raised.
|
|
220
|
+
|
|
221
|
+
Before creating new orders, the strategy will check if there are any
|
|
222
|
+
stop losses or take profits for symbol registered. It will
|
|
223
|
+
use the function get_stop_losses and get_take_profits, these functions
|
|
224
|
+
can be overridden by the user to provide custom stop losses and
|
|
225
|
+
take profits logic. The default functions will return the stop losses
|
|
226
|
+
and take profits that are registered for the symbol if any.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
context (Context): The context of the strategy. This is an instance
|
|
230
|
+
of the Context class, this class has various methods to do
|
|
231
|
+
operations with your portfolio, orders, trades, positions and
|
|
232
|
+
other components.
|
|
233
|
+
data (Dict[str, Any]): The data for the strategy.
|
|
234
|
+
This is a dictionary containing all the data retrieved from the
|
|
235
|
+
specified data sources. The keys are either the
|
|
236
|
+
identifiers of the data sources or a generated key, usually
|
|
237
|
+
<target_symbol>_<trading_symbol>_<time_frame> e.g. BTC-EUR_1h.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
None
|
|
241
|
+
"""
|
|
242
|
+
self.context = context
|
|
243
|
+
index_datetime = context.config[INDEX_DATETIME]
|
|
244
|
+
buy_signals = self.generate_buy_signals(data)
|
|
245
|
+
sell_signals = self.generate_sell_signals(data)
|
|
246
|
+
|
|
247
|
+
for symbol in self.symbols:
|
|
248
|
+
|
|
249
|
+
if self.has_open_orders(symbol):
|
|
250
|
+
continue
|
|
251
|
+
|
|
252
|
+
if not self.has_position(symbol):
|
|
253
|
+
|
|
254
|
+
if symbol not in buy_signals:
|
|
255
|
+
continue
|
|
256
|
+
|
|
257
|
+
signals = buy_signals[symbol]
|
|
258
|
+
last_row = signals.iloc[-1]
|
|
259
|
+
|
|
260
|
+
if last_row:
|
|
261
|
+
position_size = self.get_position_size(symbol)
|
|
262
|
+
full_symbol = (f"{symbol}/"
|
|
263
|
+
f"{self.context.get_trading_symbol()}")
|
|
264
|
+
price = self.context.get_latest_price(full_symbol)
|
|
265
|
+
amount = position_size.get_size(
|
|
266
|
+
self.context.get_portfolio(), price
|
|
267
|
+
)
|
|
268
|
+
order_amount = amount / price
|
|
269
|
+
order = self.create_limit_order(
|
|
270
|
+
target_symbol=symbol,
|
|
271
|
+
order_side=OrderSide.BUY,
|
|
272
|
+
amount=order_amount,
|
|
273
|
+
price=price,
|
|
274
|
+
execute=True,
|
|
275
|
+
validate=True,
|
|
276
|
+
sync=True
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# Retrieve stop loss and take profit rules if any
|
|
280
|
+
stop_loss_rule = self.get_stop_loss_rule(symbol)
|
|
281
|
+
take_profit_rule = self.get_take_profit_rule(symbol)
|
|
282
|
+
|
|
283
|
+
if stop_loss_rule is not None:
|
|
284
|
+
trade = self.context.get_trade(
|
|
285
|
+
order_id=order.id
|
|
286
|
+
)
|
|
287
|
+
self.context.add_stop_loss(
|
|
288
|
+
trade=trade,
|
|
289
|
+
percentage=stop_loss_rule.percentage_threshold,
|
|
290
|
+
trailing=stop_loss_rule.trailing,
|
|
291
|
+
sell_percentage=stop_loss_rule.sell_percentage,
|
|
292
|
+
created_at=index_datetime
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
if take_profit_rule is not None:
|
|
296
|
+
trade = self.context.get_trade(
|
|
297
|
+
order_id=order.id
|
|
298
|
+
)
|
|
299
|
+
self.context.add_take_profit(
|
|
300
|
+
trade=trade,
|
|
301
|
+
percentage=take_profit_rule.percentage_threshold,
|
|
302
|
+
trailing=take_profit_rule.trailing,
|
|
303
|
+
sell_percentage=take_profit_rule.sell_percentage,
|
|
304
|
+
created_at=index_datetime
|
|
305
|
+
)
|
|
306
|
+
else:
|
|
307
|
+
# Check in the last row if there is a sell signal
|
|
308
|
+
if symbol not in sell_signals:
|
|
309
|
+
continue
|
|
310
|
+
|
|
311
|
+
signals = sell_signals[symbol]
|
|
312
|
+
last_row = signals.iloc[-1]
|
|
313
|
+
|
|
314
|
+
if last_row:
|
|
315
|
+
position = self.get_position(symbol)
|
|
316
|
+
|
|
317
|
+
if position is None:
|
|
318
|
+
raise OperationalException(
|
|
319
|
+
f"No position found for symbol {symbol} "
|
|
320
|
+
f"in strategy {self.strategy_id}"
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
full_symbol = (f"{symbol}/"
|
|
324
|
+
f"{self.context.get_trading_symbol()}")
|
|
325
|
+
price = self.context.get_latest_price(full_symbol)
|
|
326
|
+
self.create_limit_order(
|
|
327
|
+
target_symbol=symbol,
|
|
328
|
+
order_side=OrderSide.SELL,
|
|
329
|
+
amount=position.amount,
|
|
330
|
+
execute=True,
|
|
331
|
+
validate=True,
|
|
332
|
+
sync=True,
|
|
333
|
+
price=price
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
def apply_strategy(self, context, data):
|
|
77
337
|
if self.decorated:
|
|
78
|
-
self.decorated(
|
|
338
|
+
self.decorated(context=context, data=data)
|
|
79
339
|
else:
|
|
80
340
|
raise NotImplementedError("Apply strategy is not implemented")
|
|
81
341
|
|
|
@@ -85,54 +345,130 @@ class TradingStrategy:
|
|
|
85
345
|
strategy_id=self.worker_id,
|
|
86
346
|
interval=self.interval,
|
|
87
347
|
time_unit=self.time_unit,
|
|
88
|
-
|
|
348
|
+
data_sources=self.data_sources
|
|
89
349
|
)
|
|
90
350
|
|
|
91
|
-
def
|
|
351
|
+
def get_take_profit_rule(self, symbol: str) -> Union[TakeProfitRule, None]:
|
|
352
|
+
"""
|
|
353
|
+
Get the take profit definition for a given symbol.
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
symbol (str): The symbol of the asset.
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
Union[TakeProfitRule, None]: The take profit rule if found,
|
|
360
|
+
None otherwise.
|
|
361
|
+
"""
|
|
362
|
+
|
|
363
|
+
if len(self.take_profit_rules) == 0:
|
|
364
|
+
return None
|
|
365
|
+
|
|
366
|
+
if self.take_profit_rules_lookup == {}:
|
|
367
|
+
for tp in self.take_profit_rules:
|
|
368
|
+
self.take_profit_rules_lookup[tp.symbol] = tp
|
|
369
|
+
|
|
370
|
+
return self.take_profit_rules_lookup.get(symbol, None)
|
|
371
|
+
|
|
372
|
+
def get_stop_loss_rule(self, symbol: str) -> Union[StopLossRule, None]:
|
|
373
|
+
"""
|
|
374
|
+
Get the stop loss definition for a given symbol.
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
symbol (str): The symbol of the asset.
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
Union[StopLossRule, None]: The stop loss rule if found,
|
|
381
|
+
None otherwise.
|
|
382
|
+
"""
|
|
383
|
+
|
|
384
|
+
if len(self.stop_loss_rules) == 0:
|
|
385
|
+
return None
|
|
386
|
+
|
|
387
|
+
if self.stop_loss_rules_lookup == {}:
|
|
388
|
+
for sl in self.stop_loss_rules:
|
|
389
|
+
self.stop_loss_rules_lookup[sl.symbol] = sl
|
|
390
|
+
|
|
391
|
+
return self.stop_loss_rules_lookup.get(symbol, None)
|
|
392
|
+
|
|
393
|
+
def get_position_size(self, symbol: str) -> Union[PositionSize, None]:
|
|
394
|
+
"""
|
|
395
|
+
Get the position size definition for a given symbol.
|
|
396
|
+
|
|
397
|
+
Args:
|
|
398
|
+
symbol (str): The symbol of the asset.
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
Union[PositionSize, None]: The position size if found,
|
|
402
|
+
None otherwise.
|
|
403
|
+
"""
|
|
404
|
+
|
|
405
|
+
if len(self.position_sizes) == 0:
|
|
406
|
+
raise OperationalException(
|
|
407
|
+
f"No position size defined for symbol "
|
|
408
|
+
f"{symbol} in strategy "
|
|
409
|
+
f"{self.strategy_id}"
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
if self.position_sizes_lookup == {}:
|
|
413
|
+
for ps in self.position_sizes:
|
|
414
|
+
self.position_sizes_lookup[ps.symbol] = ps
|
|
415
|
+
|
|
416
|
+
position_size = self.position_sizes_lookup.get(symbol, None)
|
|
417
|
+
|
|
418
|
+
if position_size is None:
|
|
419
|
+
raise OperationalException(
|
|
420
|
+
f"No position size defined for symbol "
|
|
421
|
+
f"{symbol} in strategy "
|
|
422
|
+
f"{self.strategy_id}"
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
return position_size
|
|
426
|
+
|
|
427
|
+
def on_trade_closed(self, context: Context, trade: Trade):
|
|
92
428
|
pass
|
|
93
429
|
|
|
94
|
-
def on_trade_updated(self,
|
|
430
|
+
def on_trade_updated(self, context: Context, trade: Trade):
|
|
95
431
|
pass
|
|
96
432
|
|
|
97
|
-
def on_trade_created(self,
|
|
433
|
+
def on_trade_created(self, context: Context, trade: Trade):
|
|
98
434
|
pass
|
|
99
435
|
|
|
100
|
-
def on_trade_opened(self,
|
|
436
|
+
def on_trade_opened(self, context: Context, trade: Trade):
|
|
101
437
|
pass
|
|
102
438
|
|
|
103
|
-
def on_trade_stop_loss_triggered(self,
|
|
439
|
+
def on_trade_stop_loss_triggered(self, context: Context, trade: Trade):
|
|
104
440
|
pass
|
|
105
441
|
|
|
106
442
|
def on_trade_trailing_stop_loss_triggered(
|
|
107
|
-
self,
|
|
443
|
+
self, context: Context, trade: Trade
|
|
108
444
|
):
|
|
109
445
|
pass
|
|
110
446
|
|
|
111
447
|
def on_trade_take_profit_triggered(
|
|
112
|
-
self,
|
|
448
|
+
self, context: Context, trade: Trade
|
|
113
449
|
):
|
|
114
450
|
pass
|
|
115
451
|
|
|
116
|
-
def on_trade_stop_loss_updated(self,
|
|
452
|
+
def on_trade_stop_loss_updated(self, context: Context, trade: Trade):
|
|
117
453
|
pass
|
|
118
454
|
|
|
119
455
|
def on_trade_trailing_stop_loss_updated(
|
|
120
|
-
self,
|
|
456
|
+
self, context: Context, trade: Trade
|
|
121
457
|
):
|
|
122
458
|
pass
|
|
123
459
|
|
|
124
|
-
def on_trade_take_profit_updated(self,
|
|
460
|
+
def on_trade_take_profit_updated(self, context: Context, trade: Trade):
|
|
125
461
|
pass
|
|
126
462
|
|
|
127
|
-
def on_trade_stop_loss_created(self,
|
|
463
|
+
def on_trade_stop_loss_created(self, context: Context, trade: Trade):
|
|
128
464
|
pass
|
|
129
465
|
|
|
130
466
|
def on_trade_trailing_stop_loss_created(
|
|
131
|
-
self,
|
|
467
|
+
self, context: Context, trade: Trade
|
|
132
468
|
):
|
|
133
469
|
pass
|
|
134
470
|
|
|
135
|
-
def on_trade_take_profit_created(self,
|
|
471
|
+
def on_trade_take_profit_created(self, context: Context, trade: Trade):
|
|
136
472
|
pass
|
|
137
473
|
|
|
138
474
|
@property
|
|
@@ -143,71 +479,315 @@ class TradingStrategy:
|
|
|
143
479
|
|
|
144
480
|
return self.worker_id
|
|
145
481
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
482
|
+
def has_open_orders(
|
|
483
|
+
self, target_symbol=None, identifier=None, market=None
|
|
484
|
+
) -> bool:
|
|
485
|
+
"""
|
|
486
|
+
Check if there are open orders for a given symbol
|
|
149
487
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
488
|
+
Args:
|
|
489
|
+
target_symbol (str): The symbol of the asset e.g BTC if the
|
|
490
|
+
asset is BTC/USDT
|
|
491
|
+
identifier (str): The identifier of the portfolio
|
|
492
|
+
market (str): The market of the asset
|
|
153
493
|
|
|
154
|
-
|
|
494
|
+
Returns:
|
|
495
|
+
bool: True if there are open orders, False otherwise
|
|
496
|
+
"""
|
|
497
|
+
return self.context.has_open_orders(
|
|
498
|
+
target_symbol=target_symbol, identifier=identifier, market=market
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
def create_limit_order(
|
|
155
502
|
self,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
503
|
+
target_symbol,
|
|
504
|
+
price,
|
|
505
|
+
order_side,
|
|
506
|
+
amount=None,
|
|
507
|
+
amount_trading_symbol=None,
|
|
508
|
+
percentage=None,
|
|
509
|
+
percentage_of_portfolio=None,
|
|
510
|
+
percentage_of_position=None,
|
|
511
|
+
precision=None,
|
|
512
|
+
market=None,
|
|
513
|
+
execute=True,
|
|
514
|
+
validate=True,
|
|
515
|
+
sync=True
|
|
516
|
+
) -> Order:
|
|
517
|
+
"""
|
|
518
|
+
Function to create a limit order. This function will create
|
|
519
|
+
a limit order and execute it if the execute parameter is set to True.
|
|
520
|
+
If the validate parameter is set to True, the order will be validated
|
|
521
|
+
|
|
522
|
+
Args:
|
|
523
|
+
target_symbol: The symbol of the asset to trade
|
|
524
|
+
price: The price of the asset
|
|
525
|
+
order_side: The side of the order
|
|
526
|
+
amount (optional): The amount of the asset to trade
|
|
527
|
+
amount_trading_symbol (optional): The amount of the trading
|
|
528
|
+
symbol to trade
|
|
529
|
+
percentage (optional): The percentage of the portfolio to
|
|
530
|
+
allocate to the order
|
|
531
|
+
percentage_of_portfolio (optional): The percentage of
|
|
532
|
+
the portfolio to allocate to the order
|
|
533
|
+
percentage_of_position (optional): The percentage of
|
|
534
|
+
the position to allocate to the order.
|
|
535
|
+
(Only supported for SELL orders)
|
|
536
|
+
precision (optional): The precision of the amount
|
|
537
|
+
market (optional): The market to trade the asset
|
|
538
|
+
execute (optional): Default True. If set to True, the order
|
|
539
|
+
will be executed
|
|
540
|
+
validate (optional): Default True. If set to True, the order
|
|
541
|
+
will be validated
|
|
542
|
+
sync (optional): Default True. If set to True, the created
|
|
543
|
+
order will be synced with the portfolio of the context
|
|
544
|
+
|
|
545
|
+
Returns:
|
|
546
|
+
Order: Instance of the order created
|
|
547
|
+
"""
|
|
548
|
+
return self.context.create_limit_order(
|
|
549
|
+
target_symbol=target_symbol,
|
|
550
|
+
price=price,
|
|
551
|
+
order_side=order_side,
|
|
552
|
+
amount=amount,
|
|
553
|
+
amount_trading_symbol=amount_trading_symbol,
|
|
554
|
+
percentage=percentage,
|
|
555
|
+
percentage_of_portfolio=percentage_of_portfolio,
|
|
556
|
+
percentage_of_position=percentage_of_position,
|
|
557
|
+
precision=precision,
|
|
558
|
+
market=market,
|
|
559
|
+
execute=execute,
|
|
560
|
+
validate=validate,
|
|
561
|
+
sync=sync
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
def close_position(
|
|
565
|
+
self, symbol, market=None, identifier=None, precision=None
|
|
566
|
+
) -> Order:
|
|
160
567
|
"""
|
|
161
|
-
|
|
568
|
+
Function to close a position. This function will close a position
|
|
569
|
+
by creating a market order to sell the position. If the precision
|
|
570
|
+
parameter is specified, the amount of the order will be rounded
|
|
571
|
+
down to the specified precision.
|
|
162
572
|
|
|
163
573
|
Args:
|
|
164
|
-
symbol
|
|
165
|
-
|
|
166
|
-
|
|
574
|
+
symbol: The symbol of the asset
|
|
575
|
+
market: The market of the asset
|
|
576
|
+
identifier: The identifier of the portfolio
|
|
577
|
+
precision: The precision of the amount
|
|
167
578
|
|
|
168
579
|
Returns:
|
|
169
580
|
None
|
|
170
581
|
"""
|
|
582
|
+
return self.context.close_position(
|
|
583
|
+
symbol=symbol,
|
|
584
|
+
market=market,
|
|
585
|
+
identifier=identifier,
|
|
586
|
+
precision=precision
|
|
587
|
+
)
|
|
171
588
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
589
|
+
def get_positions(
|
|
590
|
+
self,
|
|
591
|
+
market=None,
|
|
592
|
+
identifier=None,
|
|
593
|
+
amount_gt=None,
|
|
594
|
+
amount_gte=None,
|
|
595
|
+
amount_lt=None,
|
|
596
|
+
amount_lte=None
|
|
597
|
+
) -> List[Position]:
|
|
598
|
+
"""
|
|
599
|
+
Function to get all positions. This function will return all
|
|
600
|
+
positions that match the specified query parameters. If the
|
|
601
|
+
market parameter is specified, the positions of the specified
|
|
602
|
+
market will be returned. If the identifier parameter is
|
|
603
|
+
specified, the positions of the specified portfolio will be
|
|
604
|
+
returned. If the amount_gt parameter is specified, the positions
|
|
605
|
+
with an amount greater than the specified amount will be returned.
|
|
606
|
+
If the amount_gte parameter is specified, the positions with an
|
|
607
|
+
amount greater than or equal to the specified amount will be
|
|
608
|
+
returned. If the amount_lt parameter is specified, the positions
|
|
609
|
+
with an amount less than the specified amount will be returned.
|
|
610
|
+
If the amount_lte parameter is specified, the positions with an
|
|
611
|
+
amount less than or equal to the specified amount will be returned.
|
|
612
|
+
|
|
613
|
+
Args:
|
|
614
|
+
market: The market of the portfolio where the positions are
|
|
615
|
+
identifier: The identifier of the portfolio
|
|
616
|
+
amount_gt: The amount of the asset must be greater than this
|
|
617
|
+
amount_gte: The amount of the asset must be greater than or
|
|
618
|
+
equal to this
|
|
619
|
+
amount_lt: The amount of the asset must be less than this
|
|
620
|
+
amount_lte: The amount of the asset must be less than or equal
|
|
621
|
+
to this
|
|
622
|
+
|
|
623
|
+
Returns:
|
|
624
|
+
List[Position]: A list of positions that match the query parameters
|
|
625
|
+
"""
|
|
626
|
+
return self.context.get_positions(
|
|
627
|
+
market=market,
|
|
628
|
+
identifier=identifier,
|
|
629
|
+
amount_gt=amount_gt,
|
|
630
|
+
amount_gte=amount_gte,
|
|
631
|
+
amount_lt=amount_lt,
|
|
632
|
+
amount_lte=amount_lte
|
|
633
|
+
)
|
|
178
634
|
|
|
179
|
-
|
|
635
|
+
def get_trades(self, market=None) -> List[Trade]:
|
|
636
|
+
"""
|
|
637
|
+
Function to get all trades. This function will return all trades
|
|
638
|
+
that match the specified query parameters. If the market parameter
|
|
639
|
+
is specified, the trades with the specified market will be returned.
|
|
180
640
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
raise ValueError("Dataframe Index must be a datetime object.")
|
|
641
|
+
Args:
|
|
642
|
+
market: The market of the asset
|
|
184
643
|
|
|
185
|
-
|
|
186
|
-
|
|
644
|
+
Returns:
|
|
645
|
+
List[Trade]: A list of trades that match the query parameters
|
|
646
|
+
"""
|
|
647
|
+
return self.context.get_trades(market)
|
|
187
648
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
649
|
+
def get_closed_trades(self) -> List[Trade]:
|
|
650
|
+
"""
|
|
651
|
+
Function to get all closed trades. This function will return all
|
|
652
|
+
closed trades of the context.
|
|
653
|
+
|
|
654
|
+
Returns:
|
|
655
|
+
List[Trade]: A list of closed trades
|
|
656
|
+
"""
|
|
657
|
+
return self.context.get_closed_trades()
|
|
658
|
+
|
|
659
|
+
def get_open_trades(self, target_symbol=None, market=None) -> List[Trade]:
|
|
660
|
+
"""
|
|
661
|
+
Function to get all open trades. This function will return all
|
|
662
|
+
open trades that match the specified query parameters. If the
|
|
663
|
+
target_symbol parameter is specified, the open trades with the
|
|
664
|
+
specified target symbol will be returned. If the market parameter
|
|
665
|
+
is specified, the open trades with the specified market will be
|
|
666
|
+
returned.
|
|
667
|
+
|
|
668
|
+
Args:
|
|
669
|
+
target_symbol: The symbol of the asset
|
|
670
|
+
market: The market of the asset
|
|
671
|
+
|
|
672
|
+
Returns:
|
|
673
|
+
List[Trade]: A list of open trades that match the query parameters
|
|
674
|
+
"""
|
|
675
|
+
return self.context.get_open_trades(target_symbol, market)
|
|
676
|
+
|
|
677
|
+
def close_trade(self, trade, market=None, precision=None) -> None:
|
|
678
|
+
"""
|
|
679
|
+
Function to close a trade. This function will close a trade by
|
|
680
|
+
creating a market order to sell the position. If the precision
|
|
681
|
+
parameter is specified, the amount of the order will be rounded
|
|
682
|
+
down to the specified precision.
|
|
683
|
+
|
|
684
|
+
Args:
|
|
685
|
+
trade: Trade - The trade to close
|
|
686
|
+
market: str - The market of the trade
|
|
687
|
+
precision: float - The precision of the amount
|
|
688
|
+
|
|
689
|
+
Returns:
|
|
690
|
+
None
|
|
691
|
+
"""
|
|
692
|
+
self.context.close_trade(
|
|
693
|
+
trade=trade, market=market, precision=precision
|
|
694
|
+
)
|
|
695
|
+
|
|
696
|
+
def get_number_of_positions(self):
|
|
697
|
+
"""
|
|
698
|
+
Returns the number of positions that have a positive amount.
|
|
699
|
+
|
|
700
|
+
Returns:
|
|
701
|
+
int: The number of positions
|
|
702
|
+
"""
|
|
703
|
+
return self.context.get_number_of_positions()
|
|
704
|
+
|
|
705
|
+
def get_position(
|
|
706
|
+
self, symbol, market=None, identifier=None
|
|
707
|
+
) -> Position:
|
|
708
|
+
"""
|
|
709
|
+
Function to get a position. This function will return the
|
|
710
|
+
position that matches the specified query parameters. If the
|
|
711
|
+
market parameter is specified, the position of the specified
|
|
712
|
+
market will be returned. If the identifier parameter is
|
|
713
|
+
specified, the position of the specified portfolio will be
|
|
714
|
+
returned.
|
|
715
|
+
|
|
716
|
+
Args:
|
|
717
|
+
symbol: The symbol of the asset that represents the position
|
|
718
|
+
market: The market of the portfolio where the position is located
|
|
719
|
+
identifier: The identifier of the portfolio
|
|
720
|
+
|
|
721
|
+
Returns:
|
|
722
|
+
Position: The position that matches the query parameters
|
|
723
|
+
"""
|
|
724
|
+
return self.context.get_position(
|
|
725
|
+
symbol=symbol,
|
|
726
|
+
market=market,
|
|
727
|
+
identifier=identifier
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
def has_position(
|
|
731
|
+
self,
|
|
732
|
+
symbol,
|
|
733
|
+
market=None,
|
|
734
|
+
identifier=None,
|
|
735
|
+
amount_gt=0,
|
|
736
|
+
amount_gte=None,
|
|
737
|
+
amount_lt=None,
|
|
738
|
+
amount_lte=None
|
|
739
|
+
):
|
|
740
|
+
"""
|
|
741
|
+
Function to check if a position exists. This function will return
|
|
742
|
+
True if a position exists, False otherwise. This function will check
|
|
743
|
+
if the amount > 0 condition by default.
|
|
744
|
+
|
|
745
|
+
Args:
|
|
746
|
+
param symbol: The symbol of the asset
|
|
747
|
+
param market: The market of the asset
|
|
748
|
+
param identifier: The identifier of the portfolio
|
|
749
|
+
param amount_gt: The amount of the asset must be greater than this
|
|
750
|
+
param amount_gte: The amount of the asset must be greater than
|
|
751
|
+
or equal to this
|
|
752
|
+
param amount_lt: The amount of the asset must be less than this
|
|
753
|
+
param amount_lte: The amount of the asset must be less than
|
|
754
|
+
or equal to this
|
|
197
755
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
756
|
+
Returns:
|
|
757
|
+
Boolean: True if a position exists, False otherwise
|
|
758
|
+
"""
|
|
759
|
+
return self.context.has_position(
|
|
760
|
+
symbol=symbol,
|
|
761
|
+
market=market,
|
|
762
|
+
identifier=identifier,
|
|
763
|
+
amount_gt=amount_gt,
|
|
764
|
+
amount_gte=amount_gte,
|
|
765
|
+
amount_lt=amount_lt,
|
|
766
|
+
amount_lte=amount_lte
|
|
767
|
+
)
|
|
768
|
+
|
|
769
|
+
def has_balance(self, symbol, amount, market=None):
|
|
770
|
+
"""
|
|
771
|
+
Function to check if the portfolio has enough balance to
|
|
772
|
+
create an order. This function will return True if the
|
|
773
|
+
portfolio has enough balance to create an order, False
|
|
774
|
+
otherwise.
|
|
201
775
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
776
|
+
Args:
|
|
777
|
+
symbol: The symbol of the asset
|
|
778
|
+
amount: The amount of the asset
|
|
779
|
+
market: The market of the asset
|
|
780
|
+
|
|
781
|
+
Returns:
|
|
782
|
+
Boolean: True if the portfolio has enough balance
|
|
783
|
+
"""
|
|
784
|
+
return self.context.has_balance(symbol, amount, market)
|
|
205
785
|
|
|
206
|
-
def
|
|
786
|
+
def last_run(self) -> datetime:
|
|
207
787
|
"""
|
|
208
|
-
|
|
788
|
+
Function to get the last run of the strategy
|
|
209
789
|
|
|
210
790
|
Returns:
|
|
211
|
-
|
|
791
|
+
DateTime: The last run of the strategy
|
|
212
792
|
"""
|
|
213
|
-
return self.
|
|
793
|
+
return self.context.last_run()
|