Skip to content

Commit 73b997b

Browse files
committed
Fix issue with data sources parameters passing
1 parent a8eaf67 commit 73b997b

File tree

18 files changed

+652
-116
lines changed

18 files changed

+652
-116
lines changed

README.md

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ The Investing Algorithm Framework is a Python framework that enables swift and e
1919

2020
Features:
2121

22-
* Indicators module: A collection of indicators and utility functions that can be used in your trading strategies.
22+
* Indicators module: A collection of indicators and utility functions that can be used in your trading strategies.
2323
* Order execution and tracking
2424
* Broker and exchange connections through [ccxt](https://github.com/ccxt/ccxt)
2525
* Backtesting and performance analysis reports [example](./examples/backtest_example)
@@ -41,8 +41,8 @@ from investing_algorithm_framework import create_app, PortfolioConfiguration, \
4141
RESOURCE_DIRECTORY, TimeUnit, CCXTOHLCVMarketDataSource, Algorithm, \
4242
CCXTTickerMarketDataSource, MarketCredential, SYMBOLS
4343

44-
# Define the symbols you want to trade for optimization, otherwise the
45-
# algorithm will check if you have orders and balances on all available
44+
# Define the symbols you want to trade for optimization, otherwise the
45+
# algorithm will check if you have orders and balances on all available
4646
# symbols on the market
4747
symbols = ["BTC/EUR"]
4848

@@ -52,6 +52,13 @@ config = {
5252
SYMBOLS: symbols
5353
}
5454

55+
state_manager = AzureBlobStorageStateManager(
56+
account_name="<your account name>",
57+
account_key="<your account key>",
58+
container_name="<your container name>",
59+
blob_name="<your blob name>",
60+
)
61+
5562
# Define market data sources
5663
# OHLCV data for candles
5764
bitvavo_btc_eur_ohlcv_2h = CCXTOHLCVMarketDataSource(
@@ -67,7 +74,11 @@ bitvavo_btc_eur_ticker = CCXTTickerMarketDataSource(
6774
market="BITVAVO",
6875
symbol="BTC/EUR",
6976
)
70-
app = create_app(config=config)
77+
app = create_app(
78+
config=config,
79+
sync_portfolio=True,
80+
state_manager=state_manager
81+
)
7182
algorithm = Algorithm()
7283
app.add_market_credential(MarketCredential(
7384
market="bitvavo",
@@ -85,16 +96,16 @@ app.add_algorithm(algorithm)
8596

8697
@algorithm.strategy(
8798
# Run every two hours
88-
time_unit=TimeUnit.HOUR,
89-
interval=2,
99+
time_unit=TimeUnit.HOUR,
100+
interval=2,
90101
# Specify market data sources that need to be passed to the strategy
91102
market_data_sources=[bitvavo_btc_eur_ticker, bitvavo_btc_eur_ohlcv_2h]
92103
)
93104
def perform_strategy(algorithm: Algorithm, market_data: dict):
94105
# By default, ohlcv data is passed as polars df in the form of
95-
# {"<identifier>": <dataframe>} https://pola.rs/,
106+
# {"<identifier>": <dataframe>} https://pola.rs/,
96107
# call to_pandas() to convert to pandas
97-
polars_df = market_data["BTC-ohlcv"]
108+
polars_df = market_data["BTC-ohlcv"]
98109
print(f"I have access to {len(polars_df)} candles of ohlcv data")
99110

100111
# Ticker data is passed as {"<identifier>": <ticker dict>}
@@ -104,21 +115,21 @@ def perform_strategy(algorithm: Algorithm, market_data: dict):
104115
trades = algorithm.get_trades()
105116
open_trades = algorithm.get_open_trades()
106117
closed_trades = algorithm.get_closed_trades()
107-
108-
# Create a buy oder
118+
119+
# Create a buy oder
109120
algorithm.create_limit_order(
110121
target_symbol="BTC/EUR",
111122
order_side="buy",
112123
amount=0.01,
113124
price=ticker_data["ask"],
114125
)
115-
126+
116127
# Close a trade
117128
algorithm.close_trade(trades[0].id)
118-
129+
119130
# Close a position
120131
algorithm.close_position(positions[0].get_symbol())
121-
132+
122133
if __name__ == "__main__":
123134
app.run()
124135
```
@@ -136,14 +147,14 @@ To run a single backtest you can use the example code that can be found [here](.
136147
You can use the ```pretty_print_backtest``` function to print a backtest report.
137148
For example if you run the [moving average example trading bot](./examples/crossover_moving_average_trading_bot)
138149
you will get the following backtesting report:
139-
150+
140151
```bash
141152

142153
:%%%#+- .=*#%%% Backtest report
143154
*%%%%%%%+------=*%%%%%%%- ---------------------------
144155
*%%%%%%%%%%%%%%%%%%%%%%%- Start date: 2023-08-24 00:00:00
145156
.%%%%%%%%%%%%%%%%%%%%%%# End date: 2023-12-02 00:00:00
146-
#%%%####%%%%%%%%**#%%%+ Number of days: 100
157+
#%%%####%%%%%%%%**#%%%+ Number of days: 100
147158
.:-+*%%%%- -+..#%%%+.+- +%%%#*=-: Number of runs: 1201
148159
.:-=*%%%%. += .%%# -+.-%%%%=-:.. Number of orders: 40
149160
.:=+#%%%%%*###%%%%#*+#%%%%%%*+-: Initial balance: 400.0
@@ -156,10 +167,10 @@ you will get the following backtesting report:
156167
.++- -%%%%%%%%%%%+= Percentage negative trades: 70.0%
157168
.++- .%%%%%%%%%%%%%+= Average trade size: 100.9692 EUR
158169
.++- *%%%%%%%%%%%%%*+: Average trade duration: 83.6 hours
159-
.++- %%%%%%%%%%%%%%#+=
160-
=++........:::%%%%%%%%%%%%%%*+-
161-
.=++++++++++**#%%%%%%%%%%%%%++.
162-
170+
.++- %%%%%%%%%%%%%%#+=
171+
=++........:::%%%%%%%%%%%%%%*+-
172+
.=++++++++++**#%%%%%%%%%%%%%++.
173+
163174
Price noise
164175

165176
Positions overview
@@ -220,8 +231,8 @@ Trades overview
220231

221232
### Backtest experiments
222233

223-
The framework also supports backtest experiments. Backtest experiments allows you to
224-
compare multiple algorithms and evaluate their performance. Ideally,
234+
The framework also supports backtest experiments. Backtest experiments allows you to
235+
compare multiple algorithms and evaluate their performance. Ideally,
225236
you would do this by parameterizing your strategy and creating a factory function that
226237
creates the algorithm with the different parameters. You can find an example of this
227238
in the [backtest experiments example](./examples/backtest_experiment).
@@ -237,14 +248,14 @@ from investing_algorithm_framework import PortfolioConfiguration, \
237248
app = create_app()
238249
app.add_market_credential(
239250
MarketCredential(
240-
market="<your market>",
251+
market="<your market>",
241252
api_key="<your api key>",
242253
secret_key="<your secret key>",
243254
)
244255
)
245256
app.add_portfolio_configuration(
246257
PortfolioConfiguration(
247-
market="<your market>",
258+
market="<your market>",
248259
initial_balance=400,
249260
track_from="01/01/2022",
250261
trading_symbol="EUR"
@@ -267,27 +278,27 @@ pip install investing-algorithm-framework
267278

268279
## Disclaimer
269280

270-
If you use this framework for your investments, do not risk money
271-
which you are afraid to lose, until you have clear understanding how
281+
If you use this framework for your investments, do not risk money
282+
which you are afraid to lose, until you have clear understanding how
272283
the framework works. We can't stress this enough:
273284

274-
BEFORE YOU START USING MONEY WITH THE FRAMEWORK, MAKE SURE THAT YOU TESTED
275-
YOUR COMPONENTS THOROUGHLY. USE THE SOFTWARE AT YOUR OWN RISK.
285+
BEFORE YOU START USING MONEY WITH THE FRAMEWORK, MAKE SURE THAT YOU TESTED
286+
YOUR COMPONENTS THOROUGHLY. USE THE SOFTWARE AT YOUR OWN RISK.
276287
THE AUTHORS AND ALL AFFILIATES ASSUME NO RESPONSIBILITY FOR YOUR INVESTMENT RESULTS.
277288

278-
Also, make sure that you read the source code of any plugin you use or
289+
Also, make sure that you read the source code of any plugin you use or
279290
implementation of an algorithm made with this framework.
280291

281292
## Documentation
282293

283-
All the documentation can be found online
294+
All the documentation can be found online
284295
at the [documentation webstie](https://investing-algorithm-framework.com)
285296

286-
In most cases, you'll probably never have to change code on this repo directly
287-
if you are building your algorithm/bot. But if you do, check out the
297+
In most cases, you'll probably never have to change code on this repo directly
298+
if you are building your algorithm/bot. But if you do, check out the
288299
contributing page at the website.
289300

290-
If you'd like to chat with investing-algorithm-framework users
301+
If you'd like to chat with investing-algorithm-framework users
291302
and developers, [join us on Slack](https://inv-algo-framework.slack.com) or [join us on reddit](https://www.reddit.com/r/InvestingBots/)
292303

293304
## Acknowledgements
@@ -302,12 +313,12 @@ first. If it hasn't been reported, please [create a new issue](https://github.co
302313

303314
### Contributing
304315

305-
The investing algorithm framework is a community driven project.
316+
The investing algorithm framework is a community driven project.
306317
We welcome you to participate, contribute and together help build the future trading bots developed in python.
307318

308319
Feel like the framework is missing a feature? We welcome your pull requests!
309320
If you want to contribute to the project roadmap, please take a look at the [project board](https://github.com/coding-kitties/investing-algorithm-framework/projects?query=is%3Aopen).
310-
You can pick up a task by assigning yourself to it.
321+
You can pick up a task by assigning yourself to it.
311322

312323
**Note** before starting any major new feature work, *please open an issue describing what you are planning to do*.
313324
This will ensure that interested parties can give valuable feedback on the feature, and let others know that you are working on it.

investing_algorithm_framework/__init__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,17 @@
1111
pretty_print_backtest_reports_evaluation, load_backtest_reports, \
1212
RESERVED_BALANCES, APP_MODE, AppMode, DATETIME_FORMAT, \
1313
load_backtest_report, BacktestDateRange, convert_polars_to_pandas, \
14-
DateRange
14+
DateRange, get_backtest_report
1515
from investing_algorithm_framework.infrastructure import \
1616
CCXTOrderBookMarketDataSource, CCXTOHLCVMarketDataSource, \
1717
CCXTTickerMarketDataSource, CSVOHLCVMarketDataSource, \
1818
CSVTickerMarketDataSource
1919
from .create_app import create_app
20+
from investing_algorithm_framework.indicators import get_rsi, get_peaks, \
21+
is_uptrend, is_downtrend, is_crossover, is_crossunder, is_above, \
22+
is_below, has_crossed_upward, get_sma, get_up_and_downtrends, \
23+
get_rsi, get_ema, get_adx, has_crossed_downward, get_willr, \
24+
is_divergence
2025

2126
__all__ = [
2227
"Algorithm",
@@ -83,6 +88,6 @@
8388
"get_adx",
8489
"has_crossed_downward",
8590
"get_willr",
86-
"is_bearish_divergence",
87-
"is_bullish_divergence",
91+
"is_divergence",
92+
"get_backtest_report"
8893
]

investing_algorithm_framework/app/app.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,10 @@ def initialize(self, sync=False):
9191
9292
Also, it initializes all required services for the algorithm.
9393
94-
:return: None
94+
Args:
95+
sync (bool): Whether to sync the portfolio with the exchange
96+
Returns:
97+
None
9598
"""
9699
if self.algorithm is None:
97100
raise OperationalException("No algorithm registered")
@@ -162,8 +165,8 @@ def initialize(self, sync=False):
162165
.create_portfolio_from_configuration(
163166
portfolio_configuration
164167
)
165-
self.sync(portfolio)
166-
synced_portfolios.append(portfolio)
168+
# self.sync(portfolio)
169+
# synced_portfolios.append(portfolio)
167170

168171
if sync:
169172
portfolios = portfolio_service.get_all()
@@ -494,16 +497,15 @@ def run(
494497
separate thread.
495498
496499
Args:
497-
payload: The payload to handle if the app is running in
500+
payload (dict): The payload to handle if the app is running in
498501
stateless mode
499-
number_of_iterations: The number of iterations to run the
502+
number_of_iterations (int): The number of iterations to run the
500503
algorithm for
501-
sync: Whether to sync the portfolio with the exchange
504+
sync (bool): Whether to sync the portfolio with the exchange
502505
503506
Returns:
504507
None
505508
"""
506-
507509
# Run all on_initialize hooks
508510
for hook in self._on_after_initialize_hooks:
509511
hook.on_run(self, self.algorithm)

investing_algorithm_framework/domain/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
add_column_headers_to_csv, get_total_amount_of_rows, \
3030
load_backtest_report, convert_polars_to_pandas, \
3131
csv_to_list, StoppableThread, pretty_print_backtest_reports_evaluation, \
32-
pretty_print_backtest, load_csv_into_dict, load_backtest_reports
32+
pretty_print_backtest, load_csv_into_dict, load_backtest_reports, \
33+
get_backtest_report
3334
from .metrics import get_price_efficiency_ratio
3435

3536
__all__ = [
@@ -117,5 +118,6 @@
117118
"load_backtest_report",
118119
"get_price_efficiency_ratio",
119120
"convert_polars_to_pandas",
120-
"DateRange"
121+
"DateRange",
122+
"get_backtest_report"
121123
]

investing_algorithm_framework/domain/models/backtesting/backtest_report.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
.backtesting.backtest_date_range import BacktestDateRange
1111
from investing_algorithm_framework.domain.models.base_model import BaseModel
1212
from investing_algorithm_framework.domain.models.time_unit import TimeUnit
13+
from investing_algorithm_framework.domain.models.position import Position
14+
from investing_algorithm_framework.domain.models.trade import Trade
15+
from investing_algorithm_framework.domain.models.order import Order
1316

1417
logger = getLogger(__name__)
1518

@@ -451,7 +454,7 @@ def from_dict(data):
451454
data["backtest_end_date"], DATETIME_FORMAT)
452455
)
453456

454-
return BacktestReport(
457+
report = BacktestReport(
455458
name=data["name"],
456459
strategy_identifiers=data["strategy_identifiers"],
457460
number_of_runs=data["number_of_runs"],
@@ -477,6 +480,23 @@ def from_dict(data):
477480
average_trade_size=float(data["average_trade_size"]),
478481
)
479482

483+
positions = data["positions"]
484+
485+
if positions is not None:
486+
report.positions = [Position.from_dict(position) for position in positions]
487+
488+
trades = data["trades"]
489+
490+
if trades is not None:
491+
report.trades = [Trade.from_dict(trade) for trade in trades]
492+
493+
orders = data["orders"]
494+
495+
if orders is not None:
496+
report.orders = [Order.from_dict(order) for order in orders]
497+
498+
return report
499+
480500
def get_trades(self, symbol=None):
481501
"""
482502
Function to get trades. If a symbol is provided, it will

investing_algorithm_framework/domain/models/base_model.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,9 @@ def update(self, data):
2323

2424
if value is not None:
2525
setattr(self, attr, value)
26+
27+
@staticmethod
28+
def from_dict(data):
29+
instance = BaseModel()
30+
instance.update(data)
31+
return instance

investing_algorithm_framework/domain/models/position/position.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33

44
class Position(BaseModel):
5+
"""
6+
This class represents a position in a portfolio.
7+
"""
58

69
def __init__(
710
self,

investing_algorithm_framework/domain/models/time_unit.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ def from_string(value: str):
1919
return entry
2020

2121
raise ValueError(
22-
f"Could not convert value {value} to time unit"
22+
f"Could not convert value {value} to time unit," +
23+
" please make sure that the value is either of type string or" +
24+
f"TimeUnit. Its current type is {type(value)}"
2325
)
2426

2527
def equals(self, other):

0 commit comments

Comments
 (0)