Skip to content

Commit 611b3a2

Browse files
authored
Refactor/event loop (#330)
* Setup event loop refactor * Setup initial event loop service * Refactor to new data providers * Update readme * Update readme * Refactor eventloop for backtesting * Refactor eventloop for backtesting * Refactor eventloop for backtesting * Refactor event loop service * Update readme * Remove unused exception catch * Fix representation
1 parent aaeb16b commit 611b3a2

File tree

309 files changed

+583971
-12424
lines changed

Some content is hidden

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

309 files changed

+583971
-12424
lines changed

README.md

Lines changed: 30 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,18 @@
1313
<img src="static/showcase.svg" alt="Investing Algorithm Framework Logo" style="height: 50vh; max-height: 750px;">
1414
</div>
1515

16-
The investing algorithm framework is a Python framework designed to help you build, backtest, and deploy quantitative trading strategies. It comes with a event-based backtesting engine, ensuring an accurate and realistic evaluation of your strategies. The framework supports live trading with multiple exchanges and has various deployment options including Azure Functions and AWS Lambda.
17-
The framework is designed to be extensible, allowing you to add custom strategies, data providers, and order executors. It also supports multiple data sources, including OHLCV, ticker, and custom data, with integration for both Polars and Pandas.
16+
The Investing Algorithm Framework is a Python-based framework built to streamline the entire lifecycle of quantitative trading strategies from signal generation and backtesting to live deployment.
17+
It offers a complete quantitative workflow, featuring two dedicated backtesting engines:
18+
19+
* A vectorized backtest engine for fast signal research and prototyping
20+
21+
* An event-based backtest engine for realistic and accurate strategy evaluation
22+
23+
The framework supports live trading across multiple exchanges and offers flexible deployment options, including Azure Functions and AWS Lambda.
24+
Designed for extensibility, it allows you to integrate custom strategies, data providers, and order executors, enabling support for any exchange or broker.
25+
It natively supports multiple data formats, including OHLCV, ticker, and custom datasets with seamless compatibility for both Pandas and Polars DataFrames.
26+
27+
1828

1929
## Sponsors
2030

@@ -30,11 +40,14 @@ The framework is designed to be extensible, allowing you to add custom strategie
3040
## 🌟 Features
3141

3242
- [x] Python 3.10+: Cross-platform support for Windows, macOS, and Linux.
33-
- [x] Backtesting: Simulate strategies with detailed performance reports.
43+
- [x] Event-Driven Backtest Engine: Accurate and realistic backtesting with event-driven architecture.
44+
- [x] Vectorized Backtest Engine: Fast signal research and prototyping with vectorized operations.
45+
- [x] Backtest Reporting: Generate detailed reports to analyse and compare backtests.
3446
- [x] Live Trading: Execute trades in real-time with support for multiple exchanges via ccxt.
3547
- [x] Portfolio Management: Manage portfolios, trades, and positions with persistence via SQLite.
3648
- [x] Market Data Sources: Fetch OHLCV, ticker, and custom data with support for Polars and Pandas.
37-
- [x] Azure Functions Support: Deploy stateless trading bots to Azure.
49+
- [x] Azure Functions Support: Deploy trading bots to Azure.
50+
- [x] AWS Lambda Support: Deploy trading bots to AWS Lambda.
3851
- [x] Web API: Interact with your bot via REST API.
3952
- [x] PyIndicators Integration: Perform technical analysis directly on your dataframes.
4053
- [x] Extensibility: Add custom strategies, data providers, order executors so you can connect your trading bot to your favorite exchange or broker.
@@ -86,52 +99,39 @@ the 20, 50 and 100 period exponential moving averages (EMA) and the
8699
import logging.config
87100
from dotenv import load_dotenv
88101

89-
from pyindicators import ema, rsi
102+
from pyindicators import ema, rsi, crossunder, crossover, is_above
90103

91104
from investing_algorithm_framework import create_app, TimeUnit, Context, BacktestDateRange, \
92-
CCXTOHLCVMarketDataSource, CCXTTickerMarketDataSource, DEFAULT_LOGGING_CONFIG, \
93-
TradingStrategy, SnapshotInterval, convert_polars_to_pandas, BacktestReport
105+
DEFAULT_LOGGING_CONFIG, TradingStrategy, SnapshotInterval, BacktestReport, DataSource
94106

95107
load_dotenv()
96108
logging.config.dictConfig(DEFAULT_LOGGING_CONFIG)
97109
logger = logging.getLogger(__name__)
98110

99-
# OHLCV data for candles
100-
bitvavo_btc_eur_ohlcv_2h = CCXTOHLCVMarketDataSource(
101-
identifier="BTC-ohlcv",
102-
market="BITVAVO",
103-
symbol="BTC/EUR",
104-
time_frame="2h",
105-
window_size=200
106-
)
107-
# Ticker data for orders, trades and positions
108-
bitvavo_btc_eur_ticker = CCXTTickerMarketDataSource(
109-
identifier="BTC-ticker",
110-
market="BITVAVO",
111-
symbol="BTC/EUR",
112-
)
113-
114111
app = create_app()
115112
# Registered bitvavo market, credentials are read from .env file by default
116113
app.add_market(market="BITVAVO", trading_symbol="EUR", initial_balance=100)
117114

118115
class MyStrategy(TradingStrategy):
119116
interval = 2
120117
time_unit = TimeUnit.HOUR
121-
data_sources = [bitvavo_btc_eur_ohlcv_2h, bitvavo_btc_eur_ticker]
118+
data_sources = [
119+
DataSource(data_type="OHLCV", market="bitvavo", symbol="BTC/EUR", window_size=200, time_frame="2h", identifier="BTC-ohlcv", pandas=True),
120+
]
121+
symbols = ["BTC/EUR"]
122122

123-
def run_strategy(self, context: Context, market_data):
123+
def run_strategy(self, context: Context, data):
124124

125125
if context.has_open_orders(target_symbol="BTC"):
126126
logger.info("There are open orders, skipping strategy iteration.")
127127
return
128128

129-
print(market_data)
130-
131-
data = convert_polars_to_pandas(market_data["BTC-ohlcv"])
129+
data = data["BTC-ohlcv"]
132130
data = ema(data, source_column="Close", period=20, result_column="ema_20")
133131
data = ema(data, source_column="Close", period=50, result_column="ema_50")
134132
data = ema(data, source_column="Close", period=100, result_column="ema_100")
133+
data = crossunder(data, first_column="ema_50", second_column="ema_100", result_column="crossunder_50_20")
134+
data = crossover(data, first_column="ema_50", second_column="ema_100", result_column="crossover_50_20")
135135
data = rsi(data, source_column="Close", period=14, result_column="rsi_14")
136136

137137
if context.has_position("BTC") and self.sell_signal(data):
@@ -146,20 +146,10 @@ class MyStrategy(TradingStrategy):
146146
)
147147
return
148148

149-
def buy_signal(self, data):
150-
if len(data) < 100:
151-
return False
152-
last_row = data.iloc[-1]
153-
if last_row["ema_20"] > last_row["ema_50"] and last_row["ema_50"] > last_row["ema_100"]:
154-
return True
149+
def buy_signal(self, data) -> bool:
155150
return False
156151

157-
def sell_signal(self, data):
158-
159-
if data["ema_20"].iloc[-1] < data["ema_50"].iloc[-1] and \
160-
data["ema_20"].iloc[-2] >= data["ema_50"].iloc[-2]:
161-
return True
162-
152+
def sell_signal(self, data) -> bool:
163153
return False
164154

165155
date_range = BacktestDateRange(
@@ -170,7 +160,7 @@ app.add_strategy(MyStrategy)
170160
if __name__ == "__main__":
171161
# Run the backtest with a daily snapshot interval for end-of-day granular reporting
172162
backtest = app.run_backtest(
173-
backtest_date_range=date_range, initial_amount=100, snapshot_interval=SnapshotInterval.STRATEGY_ITERATION
163+
backtest_date_range=date_range, initial_amount=100, snapshot_interval=SnapshotInterval.DAILY
174164
)
175165
backtest_report = BacktestReport(backtests=[backtest])
176166
backtest_report.show()

examples/bitvavo_trading_bot.py

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
import logging.config
33

44
from investing_algorithm_framework import TimeUnit, TradingStrategy, \
5-
CCXTOHLCVMarketDataSource, CCXTTickerMarketDataSource, \
6-
create_app, DEFAULT_LOGGING_CONFIG, Context
5+
create_app, DEFAULT_LOGGING_CONFIG, Context, DataSource
76

87
"""
98
Bitvavo trading bot example with market data sources of bitvavo.
@@ -19,32 +18,25 @@
1918
# Load the environment variables from the .env file
2019
load_dotenv()
2120

22-
# Define your market data sources for coinbase
23-
bitvavo_btc_eur_ohlcv_2h = CCXTOHLCVMarketDataSource(
24-
identifier="BTC/EUR-ohlcv",
25-
market="bitvavo",
26-
symbol="BTC/EUR",
27-
time_frame="2h",
28-
window_size=200
29-
)
30-
bitvavo_btc_eur_ticker = CCXTTickerMarketDataSource(
31-
identifier="BTC/EUR-ticker",
32-
market="bitvavo",
33-
symbol="BTC/EUR",
34-
)
35-
21+
# Define your bitvavo trading strategy and register the data sources
3622
class BitvavoTradingStrategy(TradingStrategy):
3723
time_unit = TimeUnit.SECOND
3824
interval = 10
39-
market_data_sources = [bitvavo_btc_eur_ohlcv_2h, bitvavo_btc_eur_ticker]
25+
data_sources = [
26+
DataSource(data_type="OHLCV", market="bitvavo", symbol="BTC/EUR", window_size=200, time_frame="2h", identifier="BTC/EUR-ohlcv"),
27+
DataSource(data_type="Ticker", market="bitvavo", symbol="BTC/EUR", identifier="BTC/EUR-ticker")
28+
]
4029

41-
def run_strategy(self, context: Context, market_data):
42-
print(market_data["BTC/EUR-ohlcv"])
43-
print(market_data["BTC/EUR-ticker"])
30+
def run_strategy(self, context: Context, data):
31+
print(data["BTC/EUR-ohlcv"])
32+
print(data["BTC/EUR-ticker"])
4433

4534
# Create an app and add the market data sources and market credentials to it
4635
app = create_app()
4736
app.add_strategy(BitvavoTradingStrategy)
37+
38+
# Market credentials for bitvavo for both the portfolio connection and data sources will
39+
# be read from .env file, when not registering a market credential object in the app.
4840
app.add_market(market="bitvavo", trading_symbol="EUR", initial_balance=400)
4941

5042
if __name__ == "__main__":

examples/coinbase_trading_bot.py

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,37 @@
11
from dotenv import load_dotenv
22
import logging.config
33
from investing_algorithm_framework import TimeUnit, \
4-
CCXTOHLCVMarketDataSource, CCXTTickerMarketDataSource, TradingStrategy, \
5-
create_app, DEFAULT_LOGGING_CONFIG, Context
4+
DataSource, TradingStrategy, create_app, DEFAULT_LOGGING_CONFIG, Context
65
"""
7-
Coinbase market data sources example. Coinbase requires you to have an API key
6+
Coinbase trading bot example. Coinbase requires you to have an API key
87
and secret key to access their market data. You can create them here:
98
https://www.coinbase.com/settings/api
10-
11-
You need to add a market credential to the app, and then add market
12-
data sources to the app. You can then use the market data
13-
sources in your trading strategy.
149
"""
1510

1611
logging.config.dictConfig(DEFAULT_LOGGING_CONFIG)
1712

1813
# Load the environment variables from the .env file
1914
load_dotenv()
2015

21-
# Define your market data sources for coinbase
22-
coinbase_btc_eur_ohlcv_2h = CCXTOHLCVMarketDataSource(
23-
identifier="BTC/EUR-ohlcv",
24-
market="coinbase",
25-
symbol="BTC/EUR",
26-
time_frame="2h",
27-
window_size=200
28-
)
29-
coinbase_btc_eur_ticker = CCXTTickerMarketDataSource(
30-
identifier="BTC/EUR-ticker",
31-
market="coinbase",
32-
symbol="BTC/EUR",
33-
)
34-
35-
16+
# Define your coinbase trading strategy and register the data sources
3617
class CoinbaseTradingStrategy(TradingStrategy):
3718
time_unit = TimeUnit.SECOND
3819
interval = 10
39-
market_data_sources = [coinbase_btc_eur_ohlcv_2h, coinbase_btc_eur_ticker]
20+
data_sources = [
21+
DataSource(data_type="OHLCV", market="coinbase", symbol="BTC/EUR", window_size=200, time_frame="2h", identifier="BTC/EUR-ohlcv"),
22+
DataSource(data_type="Ticker", market="coinbase", symbol="BTC/EUR", identifier="BTC/EUR-ticker")
23+
]
4024

41-
def apply_strategy(self, context: Context, market_data):
42-
print(market_data["BTC/EUR-ohlcv"])
43-
print(market_data["BTC/EUR-ticker"])
25+
def apply_strategy(self, context: Context, data):
26+
print(data["BTC/EUR-ohlcv"])
27+
print(data["BTC/EUR-ticker"])
4428

4529
# Create an app and configure it with coinbase
4630
app = create_app()
4731
app.add_strategy(CoinbaseTradingStrategy)
32+
33+
# Market credentials for coinbase for both the portfolio connection and data sources will
34+
# be read from .env file, when not registering a market credential object in the app.
4835
app.add_market(market="coinbase", trading_symbol="EUR", initial_balance=400)
4936

5037
if __name__ == "__main__":
File renamed without changes.

0 commit comments

Comments
 (0)