Skip to content

Commit f7c5a12

Browse files
committed
Add azure storage state handler support
1 parent d6baab6 commit f7c5a12

File tree

88 files changed

+3279
-2522
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+3279
-2522
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
### Python template
33
# Byte-compiled / optimized / DLL files
44
__pycache__/
5+
*__pycache__/
56
*.py[cod]
67
*$py.class
8+
.DS_Store
79

810
# C extensions
911
*.so

README.md

Lines changed: 13 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
[![GitHub stars](https://img.shields.io/github/stars/coding-kitties/investing-algorithm-framework.svg?style=social&label=Star&maxAge=1)](https://github.com/SeaQL/sea-orm/stargazers/) If you like what we do, consider starring, sharing and contributing!
88

99
###### Sponsors
10+
1011
<p align="left">
1112
<a href="https://finterion.com">
1213
<img alt="Finterion" src="static/sponsors/finterion.png" width="200px" />
@@ -36,30 +37,13 @@ Features:
3637
The following algorithm connects to binance and buys BTC every 5 seconds. It also exposes an REST API that allows you to interact with the algorithm.
3738

3839
```python
39-
import pathlib
40+
import logging
4041
from investing_algorithm_framework import create_app, PortfolioConfiguration, \
41-
RESOURCE_DIRECTORY, TimeUnit, CCXTOHLCVMarketDataSource, Algorithm, \
42-
CCXTTickerMarketDataSource, MarketCredential, SYMBOLS
43-
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
46-
# symbols on the market
47-
symbols = ["BTC/EUR"]
48-
49-
# Define resource directory and the symbols you want to trade
50-
config = {
51-
RESOURCE_DIRECTORY: pathlib.Path(__file__).parent.resolve(),
52-
SYMBOLS: symbols
53-
}
54-
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-
)
42+
TimeUnit, CCXTOHLCVMarketDataSource, Algorithm, \
43+
CCXTTickerMarketDataSource, MarketCredential, DEFAULT_LOGGING_CONFIG
44+
45+
logging.config.dictConfig(DEFAULT_LOGGING_CONFIG)
6146

62-
# Define market data sources
6347
# OHLCV data for candles
6448
bitvavo_btc_eur_ohlcv_2h = CCXTOHLCVMarketDataSource(
6549
identifier="BTC-ohlcv",
@@ -74,17 +58,10 @@ bitvavo_btc_eur_ticker = CCXTTickerMarketDataSource(
7458
market="BITVAVO",
7559
symbol="BTC/EUR",
7660
)
77-
app = create_app(
78-
config=config,
79-
sync_portfolio=True,
80-
state_manager=state_manager
81-
)
61+
app = create_app()
8262
algorithm = Algorithm()
83-
app.add_market_credential(MarketCredential(
84-
market="bitvavo",
85-
api_key="<your api key>",
86-
secret_key="<your secret key>",
87-
))
63+
# Bitvavo market credentials are read from .env file
64+
app.add_market_credential(MarketCredential(market="bitvavo"))
8865
app.add_portfolio_configuration(
8966
PortfolioConfiguration(
9067
market="bitvavo",
@@ -94,21 +71,18 @@ app.add_portfolio_configuration(
9471
)
9572
app.add_algorithm(algorithm)
9673

74+
# Run every two hours and register the data sources
9775
@algorithm.strategy(
98-
# Run every two hours
9976
time_unit=TimeUnit.HOUR,
10077
interval=2,
101-
# Specify market data sources that need to be passed to the strategy
10278
market_data_sources=[bitvavo_btc_eur_ticker, bitvavo_btc_eur_ohlcv_2h]
10379
)
10480
def perform_strategy(algorithm: Algorithm, market_data: dict):
105-
# By default, ohlcv data is passed as polars df in the form of
106-
# {"<identifier>": <dataframe>} https://pola.rs/,
107-
# call to_pandas() to convert to pandas
81+
# Access the data sources with the indentifier
10882
polars_df = market_data["BTC-ohlcv"]
109-
print(f"I have access to {len(polars_df)} candles of ohlcv data")
11083

111-
# Ticker data is passed as {"<identifier>": <ticker dict>}
84+
# Convert the polars dataframe to a pandas dataframe
85+
pandas_df = polars_df.to_pandas()
11286
ticker_data = market_data["BTC-ticker"]
11387
unallocated_balance = algorithm.get_unallocated()
11488
positions = algorithm.get_positions()
@@ -257,7 +231,6 @@ app.add_portfolio_configuration(
257231
PortfolioConfiguration(
258232
market="<your market>",
259233
initial_balance=400,
260-
track_from="01/01/2022",
261234
trading_symbol="EUR"
262235
)
263236
)

examples/app.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from dotenv import load_dotenv
2+
3+
from investing_algorithm_framework import create_app, PortfolioConfiguration, \
4+
TimeUnit, CCXTOHLCVMarketDataSource, Algorithm, \
5+
CCXTTickerMarketDataSource, MarketCredential, AzureBlobStorageStateHandler
6+
7+
load_dotenv()
8+
9+
# Define market data sources
10+
# OHLCV data for candles
11+
bitvavo_btc_eur_ohlcv_2h = CCXTOHLCVMarketDataSource(
12+
identifier="BTC-ohlcv",
13+
market="BITVAVO",
14+
symbol="BTC/EUR",
15+
time_frame="2h",
16+
window_size=200
17+
)
18+
# Ticker data for orders, trades and positions
19+
bitvavo_btc_eur_ticker = CCXTTickerMarketDataSource(
20+
identifier="BTC-ticker",
21+
market="BITVAVO",
22+
symbol="BTC/EUR",
23+
)
24+
app = create_app(state_handler=AzureBlobStorageStateHandler())
25+
app.add_market_data_source(bitvavo_btc_eur_ohlcv_2h)
26+
algorithm = Algorithm()
27+
app.add_market_credential(MarketCredential(market="bitvavo"))
28+
app.add_portfolio_configuration(
29+
PortfolioConfiguration(
30+
market="bitvavo",
31+
trading_symbol="EUR",
32+
initial_balance=20
33+
)
34+
)
35+
app.add_algorithm(algorithm)
36+
37+
@algorithm.strategy(
38+
# Run every two hours
39+
time_unit=TimeUnit.HOUR,
40+
interval=2,
41+
# Specify market data sources that need to be passed to the strategy
42+
market_data_sources=[bitvavo_btc_eur_ticker, "BTC-ohlcv"]
43+
)
44+
def perform_strategy(algorithm: Algorithm, market_data: dict):
45+
# By default, ohlcv data is passed as polars df in the form of
46+
# {"<identifier>": <dataframe>} https://pola.rs/,
47+
# call to_pandas() to convert to pandas
48+
polars_df = market_data["BTC-ohlcv"]
49+
print(f"I have access to {len(polars_df)} candles of ohlcv data")

examples/bitvavo_trading_bot/bitvavo.py

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,28 @@
1-
import os
2-
31
from investing_algorithm_framework import MarketCredential, TimeUnit, \
42
CCXTOHLCVMarketDataSource, CCXTTickerMarketDataSource, TradingStrategy, \
5-
create_app, PortfolioConfiguration, Algorithm, SYMBOLS, RESOURCE_DIRECTORY
3+
create_app, PortfolioConfiguration, Algorithm
64

75
"""
86
Bitvavo trading bot example with market data sources of bitvavo.
9-
Bitvavo does not requires you to have an API key and secret key to access
10-
their market data. If you just want to backtest your strategy,
7+
Bitvavo does not requires you to have an API key and secret key to access
8+
their market data. If you just want to backtest your strategy,
119
you don't need to add a market credential. If your running your strategy live,
12-
you need to add a market credential to the app, that accesses your
10+
you need to add a market credential to the app, that accesses your
1311
account on bitvavo.
1412
"""
1513

16-
1714
# Define your market credential for bitvavo
1815
bitvavo_market_credential = MarketCredential(
19-
api_key="<your_api_key>",
20-
secret_key="<your_secret_key>",
2116
market="bitvavo",
17+
api_key="your_api_key",
18+
secret_key="your_secret_key"
2219
)
2320
# Define your market data sources for coinbase
2421
bitvavo_btc_eur_ohlcv_2h = CCXTOHLCVMarketDataSource(
2522
identifier="BTC/EUR-ohlcv",
2623
market="bitvavo",
2724
symbol="BTC/EUR",
28-
timeframe="2h",
25+
time_frame="2h",
2926
window_size=200
3027
)
3128
bitvavo_btc_eur_ticker = CCXTTickerMarketDataSource(
@@ -36,26 +33,20 @@
3633

3734

3835
class BitvavoTradingStrategy(TradingStrategy):
39-
time_unit = TimeUnit.HOUR
40-
interval = 2
36+
time_unit = TimeUnit.SECOND
37+
interval = 10
4138
market_data_sources = [bitvavo_btc_eur_ohlcv_2h, bitvavo_btc_eur_ticker]
4239

4340
def apply_strategy(self, algorithm, market_data):
4441
print(market_data["BTC/EUR-ohlcv"])
4542
print(market_data["BTC/EUR-ticker"])
4643

47-
48-
config = {
49-
SYMBOLS: ["BTC/EUR"],
50-
RESOURCE_DIRECTORY: os.path.join(os.path.dirname(__file__), "resources")
51-
}
52-
5344
# Create an algorithm and link your trading strategy to it
5445
algorithm = Algorithm()
5546
algorithm.add_strategy(BitvavoTradingStrategy)
5647

5748
# Create an app and add the market data sources and market credentials to it
58-
app = create_app(config=config)
49+
app = create_app()
5950
app.add_market_credential(bitvavo_market_credential)
6051
app.add_market_data_source(bitvavo_btc_eur_ohlcv_2h)
6152
app.add_market_data_source(bitvavo_btc_eur_ticker)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
bin
2+
obj
3+
csx
4+
.vs
5+
edge
6+
Publish
7+
8+
*.user
9+
*.suo
10+
*.cscfg
11+
*.Cache
12+
project.lock.json
13+
14+
/packages
15+
/TestResults
16+
17+
/tools/NuGet.exe
18+
/App_Data
19+
/secrets
20+
/data
21+
.secrets
22+
appsettings.json
23+
local.settings.json
24+
25+
node_modules
26+
dist
27+
28+
# Local python packages
29+
.python_packages/
30+
31+
# Python Environments
32+
.env
33+
.venv
34+
env/
35+
venv/
36+
ENV/
37+
env.bak/
38+
venv.bak/
39+
40+
# Byte-compiled / optimized / DLL files
41+
__pycache__/
42+
*.py[cod]
43+
*$py.class
44+
45+
# Azurite artifacts
46+
__blobstorage__
47+
__queuestorage__
48+
__azurite_db*__.json
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Investing Algorithm Framework App Deployment to Azure Functions
2+
3+
This article demonstrates how to deploy your trading bot to Azure Functions.
4+
We will deploy an example bot that uses the investing algorithm framework to
5+
azure functions. In order to do that we will do the following:
6+
7+
1. Create a new app using the framework with the azure blob storage state handler.
8+
2. Use the framework provided ci tools to create a azure functions ready application.
9+
3. Use the framework provided ci tools to deploy the application to azure functions.
10+
11+
## Prerequisites
12+
13+
For this example, you need to have the following:
14+
15+
- An Azure account with an active subscription. [Create an account](https://azure.microsoft.com/en-us/free/)
16+
- The Azure CLI installed. [Install the Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli)
17+
- The Azure Functions Core Tools installed. [Install the Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local)
18+
- The investing algorithm framework installed. [Install the framework]() or simply run `pip install investing_algorithm_framework`
19+
20+
### Creating a new app
21+
22+
First run the following command to create a new app azure functions ready app:
23+
24+
```bash
25+
create_azure_function_trading_bot_app
26+
```
27+
28+
This command will create a new app with the following structure:
29+
30+
```yaml
31+
.
32+
├── function_app.py
33+
├── host.json
34+
├── local.settings.json
35+
└── requirements.txt
36+
```
37+
38+
The function_app.py while import the app from the app.py file in the root of the project and run it as an azure function. It is therefore important that you have a file named `app.py` in the root of your project that defines the application in a variable named `app`.
39+
40+
Additionaly, because Azure Functions are stateless, you need to use a state storage solution. In this example, we will use Azure Blob Storage state handler provided by the framework. This state handler is specifically designed to work with Azure Functions and Azure Blob Storage.
41+
42+
The reason why we need a state storage solution is that Azure Functions are stateless. This means that each function execution is independent of the previous one. This is a problem for trading bots because they need to keep track of their state between executions (portfolios, order, positions and trades). In order to solve this problem, we need to use a state storage solution that can store the bot's databases between executions.
43+
44+
Combining all of this, the `app.py` file should look like this:
45+
46+
> When you are using the cli command 'deploy_trading_bot_to_azure_function' (which we will use later) you don't need to provide any connection strings. The command will take care of provisioning all
47+
> resourses and configuration of all required parameters for the state handler.
48+
49+
```python
50+
from investing_algorithm_framework import AzureBlobStorageStateHandler, create_app
51+
52+
app = create_app(state_handler=AzureBlobStorageStateHandler)
53+
54+
# Write here your code where your register your portfolio configurations, strategies and data providers
55+
....
56+
```
57+
58+
## Deployment to Azure
59+
60+
To deploy your trading bot to Azure Functions, you need to run the following command:
61+
62+
```bash
63+
deploy_trading_bot_to_azure_function
64+
```
65+
66+
This command will do the following:
67+
68+
- Create a new resource group in your Azure account.
69+
- Create a new storage account in the resource group.
70+
- Create a new blob container in the storage account.
71+
- Create a new function app in the resource group.
72+
- Deploy the trading bot to the function app.
73+
- Configure the function app to use the blob container as the state handler.
74+
- Print the URL of the function app.
75+
76+
After running the command, you will see the URL of the function app. You can use this URL to access your trading bot. Now your trading bot is running on Azure Functions!

0 commit comments

Comments
 (0)