Skip to content

Commit

Permalink
Add experimentation functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
MDUYN committed Mar 22, 2024
1 parent e87ac63 commit a953c6b
Show file tree
Hide file tree
Showing 95 changed files with 7,712 additions and 11,537 deletions.
140 changes: 51 additions & 89 deletions README.md

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions examples/backtest/algorithm/algorithm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from investing_algorithm_framework import Algorithm
from .strategy import CrossOverStrategy

algorithm = Algorithm()
algorithm.add_strategy(CrossOverStrategy)
File renamed without changes.
94 changes: 94 additions & 0 deletions examples/backtest/algorithm/strategy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import tulipy as ti

from investing_algorithm_framework import TimeUnit, TradingStrategy, \
Algorithm, OrderSide

"""
This strategy is based on the golden cross strategy. It will buy when the
fast moving average crosses the slow moving average from below. It will sell
when the fast moving average crosses the slow moving average from above.
The strategy will also check if the fast moving average is above the trend
moving average. If it is not above the trend moving average it will not buy.
It uses tulipy indicators to calculate the metrics. You need to
install this library in your environment to run this strategy.
You can find instructions on how to install tulipy here:
https://tulipindicators.org/ or go directly to the pypi page:
https://pypi.org/project/tulipy/
"""
def is_below_trend(fast_series, slow_series):
return fast_series[-1] < slow_series[-1]


def is_above_trend(fast_series, slow_series):
return fast_series[-1] > slow_series[-1]


def is_crossover(fast, slow):
"""
Expect df to have columns: Date, ma_<period_one>, ma_<period_two>.
With the given date time it will check if the ma_<period_one> is a
crossover with the ma_<period_two>
"""
return fast[-2] <= slow[-2] and fast[-1] > slow[-1]


def is_crossunder(fast, slow):
"""
Expect df to have columns: Date, ma_<period_one>, ma_<period_two>.
With the given date time it will check if the ma_<period_one> is a
crossover with the ma_<period_two>
"""
return fast[-2] >= slow[-2] and fast[-1] < slow[-1]


class CrossOverStrategy(TradingStrategy):
time_unit = TimeUnit.HOUR
interval = 2
market_data_sources = [
"BTC/EUR-ohlcv",
"DOT/EUR-ohlcv",
"BTC/EUR-ticker",
"DOT/EUR-ticker"
]
symbols = ["BTC/EUR", "DOT/EUR"]
fast = 9
slow = 50
trend = 100

def apply_strategy(self, algorithm: Algorithm, market_data):

for symbol in self.symbols:
target_symbol = symbol.split('/')[0]

if algorithm.has_open_orders(target_symbol):
continue

df = market_data[f"{symbol}-ohlcv"]
ticker_data = market_data[f"{symbol}-ticker"]
fast = ti.sma(df['Close'].to_numpy(), 9)
slow = ti.sma(df['Close'].to_numpy(), 50)
trend = ti.sma(df['Close'].to_numpy(), 100)
price = ticker_data['bid']

if not algorithm.has_position(target_symbol) \
and is_crossover(fast, slow) \
and is_above_trend(fast, trend):
algorithm.create_limit_order(
target_symbol=target_symbol,
order_side=OrderSide.BUY,
price=price,
percentage_of_portfolio=25,
precision=4,
)

if algorithm.has_position(target_symbol) \
and is_below_trend(fast, slow):

open_trades = algorithm.get_open_trades(
target_symbol=target_symbol
)

for trade in open_trades:
algorithm.close_trade(trade)
6 changes: 0 additions & 6 deletions examples/backtest/app.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import pathlib

from data_sources import bitvavo_btc_eur_ohlcv_2h, bitvavo_dot_eur_ohlcv_2h, \
bitvavo_dot_eur_ticker, bitvavo_btc_eur_ticker
from investing_algorithm_framework import create_app, RESOURCE_DIRECTORY

app = create_app(
config={RESOURCE_DIRECTORY: pathlib.Path(__file__).parent.resolve()}
)
app.add_market_data_source(bitvavo_btc_eur_ohlcv_2h)
app.add_market_data_source(bitvavo_dot_eur_ohlcv_2h)
app.add_market_data_source(bitvavo_btc_eur_ticker)
app.add_market_data_source(bitvavo_dot_eur_ticker)
37 changes: 37 additions & 0 deletions examples/backtest/backtest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from datetime import datetime, timedelta

from investing_algorithm_framework import PortfolioConfiguration, \
pretty_print_backtest

from app import app
from algorithm.algorithm import algorithm
from algorithm.data_sources import bitvavo_btc_eur_ohlcv_2h, \
bitvavo_dot_eur_ohlcv_2h, bitvavo_dot_eur_ticker, bitvavo_btc_eur_ticker


app.add_algorithm(algorithm)
app.add_market_data_source(bitvavo_btc_eur_ohlcv_2h)
app.add_market_data_source(bitvavo_dot_eur_ohlcv_2h)
app.add_market_data_source(bitvavo_btc_eur_ticker)
app.add_market_data_source(bitvavo_dot_eur_ticker)


# Add a portfolio configuration of 400 euro initial balance
app.add_portfolio_configuration(
PortfolioConfiguration(
market="BINANCE",
trading_symbol="EUR",
initial_balance=400,
)
)

if __name__ == "__main__":
end_date = datetime(2023, 12, 2)
start_date = end_date - timedelta(days=100)
backtest_report = app.run_backtest(
algorithm=algorithm,
start_date=start_date,
end_date=end_date,
pending_order_check_interval="2h",
)
pretty_print_backtest(backtest_report)
10 changes: 2 additions & 8 deletions examples/backtest_expirement/algorithms/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
from .algorithm_one import algorithm as algorithm_one
from .algorithm_two import algorithm as algorithm_two
from .algorithm_three import algorithm as algorithm_three
from .algorithm import create_algorithm


__all__ = [
"algorithm_one",
"algorithm_two",
"algorithm_three",
]
__all__ = ["create_algorithm"]
97 changes: 97 additions & 0 deletions examples/backtest_expirement/algorithms/algorithm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from investing_algorithm_framework import Algorithm, TradingStrategy, \
TimeUnit, OrderSide
import tulipy as ti

def is_below_trend(fast_series, slow_series):
return fast_series[-1] < slow_series[-1]


def is_above_trend(fast_series, slow_series):
return fast_series[-1] > slow_series[-1]


def is_crossover(fast, slow):
"""
Expect df to have columns: Date, ma_<period_one>, ma_<period_two>.
With the given date time it will check if the ma_<period_one> is a
crossover with the ma_<period_two>
"""
return fast[-2] <= slow[-2] and fast[-1] > slow[-1]


def is_crossunder(fast, slow):
"""
Expect df to have columns: Date, ma_<period_one>, ma_<period_two>.
With the given date time it will check if the ma_<period_one> is a
crossover with the ma_<period_two>
"""
return fast[-2] >= slow[-2] and fast[-1] < slow[-1]


class Strategy(TradingStrategy):
time_unit = TimeUnit.HOUR
interval = 2
market_data_sources = [
"BTC/EUR-ohlcv",
"DOT/EUR-ohlcv",
"BTC/EUR-ticker",
"DOT/EUR-ticker"
]
symbols = ["BTC/EUR", "DOT/EUR"]

def __init__(self, fast, slow, trend):
self.fast = fast
self.slow = slow
self.trend = trend
super().__init__()

def apply_strategy(self, algorithm: Algorithm, market_data):

for symbol in self.symbols:
target_symbol = symbol.split('/')[0]

if algorithm.has_open_orders(target_symbol):
continue

df = market_data[f"{symbol}-ohlcv"]
ticker_data = market_data[f"{symbol}-ticker"]
fast = ti.sma(df['Close'].to_numpy(), self.fast)
slow = ti.sma(df['Close'].to_numpy(), self.slow)
trend = ti.sma(df['Close'].to_numpy(), self.trend)
price = ticker_data['bid']

if not algorithm.has_position(target_symbol) \
and is_crossover(fast, slow) \
and is_above_trend(fast, trend):
algorithm.create_limit_order(
target_symbol=target_symbol,
order_side=OrderSide.BUY,
price=price,
percentage_of_portfolio=25,
precision=4,
)

if algorithm.has_position(target_symbol) \
and is_below_trend(fast, slow):

open_trades = algorithm.get_open_trades(
target_symbol=target_symbol
)

for trade in open_trades:
algorithm.close_trade(trade)


def create_algorithm(
name,
description,
fast,
slow,
trend
) -> Algorithm:
algorithm = Algorithm(
name=name,
description=description
)
algorithm.add_strategy(Strategy(fast, slow, trend))
return algorithm
15 changes: 0 additions & 15 deletions examples/backtest_expirement/algorithms/algorithm_one.py

This file was deleted.

16 changes: 0 additions & 16 deletions examples/backtest_expirement/algorithms/algorithm_three.py

This file was deleted.

15 changes: 0 additions & 15 deletions examples/backtest_expirement/algorithms/algorithm_two.py

This file was deleted.

Loading

0 comments on commit a953c6b

Please sign in to comment.