Skip to content

Commit 3c5910d

Browse files
authored
Merge pull request #425 from coding-kitties/dev
Release v8.0.0
2 parents f3f1071 + 72a3c65 commit 3c5910d

61 files changed

Lines changed: 4979 additions & 1639 deletions

File tree

Some content is hidden

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

README.md

Lines changed: 107 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -99,36 +99,118 @@ The [documentation](https://coding-kitties.github.io/investing-algorithm-framewo
9999
The framework is designed around the `TradingStrategy` class. You define **what data** your strategy needs and **when to buy or sell** — the framework handles execution, position management, and reporting.
100100

101101
```python
102+
from typing import Dict, Any
103+
104+
import pandas as pd
105+
from pyindicators import ema, rsi, crossover, crossunder
106+
102107
from investing_algorithm_framework import (
103-
TradingStrategy, TimeUnit, Context, OrderSide
108+
TradingStrategy, DataSource, TimeUnit, DataType,
109+
PositionSize, ScalingRule, StopLossRule,
104110
)
105111

106-
class MyStrategy(TradingStrategy):
112+
113+
class RSIEMACrossoverStrategy(TradingStrategy):
114+
"""
115+
EMA crossover + RSI filter strategy with position scaling and stop losses.
116+
117+
Buy when RSI is oversold AND a recent EMA crossover occurred.
118+
Sell when RSI is overbought AND a recent EMA crossunder occurred.
119+
Scale into winners, trail a stop loss, and let the framework handle the rest.
120+
"""
107121
time_unit = TimeUnit.HOUR
108122
interval = 2
109-
symbol_pairs = ["BTC/EUR"]
110-
111-
def apply_strategy(self, context: Context, market_data):
112-
for pair in self.symbol_pairs:
113-
symbol = pair.split("/")[0]
114-
ohlcv = market_data[f"{pair}-ohlcv-2h"]
115-
price = ohlcv["Close"].iloc[-1]
116-
117-
if self.should_buy(ohlcv) and not context.has_position(symbol):
118-
context.create_limit_order(
119-
target_symbol=symbol,
120-
order_side=OrderSide.BUY,
121-
price=price,
122-
percentage_of_portfolio=25,
123-
)
124-
125-
if self.should_sell(ohlcv) and context.has_position(symbol):
126-
context.create_limit_order(
127-
target_symbol=symbol,
128-
order_side=OrderSide.SELL,
129-
price=price,
130-
percentage_of_portfolio=100,
131-
)
123+
symbols = ["BTC", "ETH"]
124+
data_sources = [
125+
DataSource(
126+
identifier="BTC_ohlcv", symbol="BTC/EUR",
127+
data_type=DataType.OHLCV, time_frame="2h",
128+
market="BITVAVO", pandas=True, warmup_window=100,
129+
),
130+
DataSource(
131+
identifier="ETH_ohlcv", symbol="ETH/EUR",
132+
data_type=DataType.OHLCV, time_frame="2h",
133+
market="BITVAVO", pandas=True, warmup_window=100,
134+
),
135+
]
136+
137+
# Risk management
138+
position_sizes = [
139+
PositionSize(symbol="BTC", percentage_of_portfolio=20),
140+
PositionSize(symbol="ETH", percentage_of_portfolio=20),
141+
]
142+
scaling_rules = [
143+
ScalingRule(
144+
symbol="BTC", max_entries=3,
145+
scale_in_percentage=[50, 25], cooldown_in_bars=5,
146+
),
147+
ScalingRule(
148+
symbol="ETH", max_entries=3,
149+
scale_in_percentage=[50, 25], cooldown_in_bars=5,
150+
),
151+
]
152+
stop_losses = [
153+
StopLossRule(
154+
symbol="BTC", percentage_threshold=5,
155+
sell_percentage=100, trailing=True,
156+
),
157+
StopLossRule(
158+
symbol="ETH", percentage_threshold=5,
159+
sell_percentage=100, trailing=True,
160+
),
161+
]
162+
163+
def generate_buy_signals(
164+
self, data: Dict[str, Any]
165+
) -> Dict[str, pd.Series]:
166+
signals = {}
167+
168+
for symbol in self.symbols:
169+
df = data[f"{symbol}_ohlcv"]
170+
ema_short = ema(df, period=12, source_column="Close",
171+
result_column="ema_short")
172+
ema_long = ema(ema_short, period=26, source_column="Close",
173+
result_column="ema_long")
174+
ema_cross = crossover(ema_long,
175+
first_column="ema_short",
176+
second_column="ema_long",
177+
result_column="ema_crossover")
178+
rsi_data = rsi(df, period=14, source_column="Close",
179+
result_column="rsi")
180+
181+
rsi_oversold = rsi_data["rsi"] < 30
182+
recent_crossover = (
183+
ema_cross["ema_crossover"].rolling(window=10).max() > 0
184+
)
185+
signals[symbol] = (rsi_oversold & recent_crossover).fillna(False)
186+
187+
return signals
188+
189+
def generate_sell_signals(
190+
self, data: Dict[str, Any]
191+
) -> Dict[str, pd.Series]:
192+
signals = {}
193+
194+
for symbol in self.symbols:
195+
df = data[f"{symbol}_ohlcv"]
196+
ema_short = ema(df, period=12, source_column="Close",
197+
result_column="ema_short")
198+
ema_long = ema(ema_short, period=26, source_column="Close",
199+
result_column="ema_long")
200+
ema_cross = crossunder(ema_long,
201+
first_column="ema_short",
202+
second_column="ema_long",
203+
result_column="ema_crossunder")
204+
rsi_data = rsi(df, period=14, source_column="Close",
205+
result_column="rsi")
206+
207+
rsi_overbought = rsi_data["rsi"] >= 70
208+
recent_crossunder = (
209+
ema_cross["ema_crossunder"].rolling(window=10).max() > 0
210+
)
211+
signals[symbol] = (rsi_overbought & recent_crossunder).fillna(False)
212+
213+
return signals
132214
```
133215

134216
Create as many strategy variants as you want — different parameters, different indicators, different symbols — then backtest them all and compare in a single report.

0 commit comments

Comments
 (0)