Skip to content

Commit 4ff7e0f

Browse files
committed
Add high water mark for stop loss and take profit tests
1 parent fad9ab6 commit 4ff7e0f

File tree

7 files changed

+289
-37
lines changed

7 files changed

+289
-37
lines changed

examples/backtest_example/run_backtest.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -115,16 +115,6 @@ def apply_strategy(self, context: Context, market_data):
115115
if not context.has_position(target_symbol) \
116116
and is_crossover(fast, slow) \
117117
and is_above_trend(fast, trend):
118-
119-
# print(context.config["BACKTESTING_INDEX_DATETIME"])
120-
date = context.config["BACKTESTING_INDEX_DATETIME"]
121-
122-
# Check if date equals 2023-10-30 04:00
123-
if date == datetime(2023, 10, 30, 4, 0):
124-
print("opening trade")
125-
print(price)
126-
print(df[-1]["Close"])
127-
128118
order = context.create_limit_order(
129119
target_symbol=target_symbol,
130120
order_side=OrderSide.BUY,
@@ -135,6 +125,7 @@ def apply_strategy(self, context: Context, market_data):
135125
trade = context.get_trade(order_id=order.id)
136126
context.add_stop_loss(
137127
trade=trade,
128+
trade_risk_type="trailing",
138129
percentage=5,
139130
sell_percentage=50
140131
)

investing_algorithm_framework/domain/models/trade/trade_stop_loss.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ def update_with_last_reported_price(self, current_price: float, date):
9292

9393
if TradeRiskType.FIXED.equals(self.trade_risk_type):
9494
# Check if the current price is less than the high water mark
95+
if current_price > self.high_water_mark:
96+
self.high_water_mark = current_price
9597
return
9698
else:
9799
# Check if the current price is less than the stop loss price
@@ -132,12 +134,6 @@ def has_triggered(self, current_price: float) -> bool:
132134

133135
return False
134136

135-
@property
136-
def active(self):
137-
138-
self.tra
139-
return self._active
140-
141137
def get_sell_amount(self) -> float:
142138
"""
143139
Function to calculate the amount to sell based on the

investing_algorithm_framework/domain/models/trade/trade_take_profit.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,17 @@ def update_with_last_reported_price(self, current_price: float, date):
8989

9090
# Do nothing for fixed take profit
9191
if TradeRiskType.FIXED.equals(self.trade_risk_type):
92+
93+
if self.high_water_mark is not None:
94+
if current_price > self.high_water_mark:
95+
self.high_water_mark = current_price
96+
self.high_water_mark_date = date
97+
else:
98+
if current_price >= self.take_profit_price:
99+
self.high_water_mark = current_price
100+
self.high_water_mark_date = date
101+
return
102+
92103
return
93104
else:
94105

@@ -107,10 +118,6 @@ def update_with_last_reported_price(self, current_price: float, date):
107118

108119
# Check if the current price is less than the take profit price
109120
if current_price < self.take_profit_price:
110-
print("trigger for take profit")
111-
print(self.id)
112-
print(f"current price: {current_price}")
113-
print(f"take profit price: {self.take_profit_price}")
114121
return
115122

116123
# Increase the high water mark and take profit price

investing_algorithm_framework/domain/utils/backtesting.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from tabulate import tabulate
88

99
from investing_algorithm_framework.domain import DATETIME_FORMAT, \
10-
BacktestDateRange, TradeStatus, OrderSide
10+
BacktestDateRange, TradeStatus, OrderSide, TradeRiskType
1111
from investing_algorithm_framework.domain.exceptions import \
1212
OperationalException
1313
from investing_algorithm_framework.domain.models.backtesting import \
@@ -141,12 +141,12 @@ def get_high_water_mark(stop_loss):
141141

142142
def get_stop_loss_price(take_profit):
143143

144-
if take_profit["trade_risk_type"] == "trailing":
144+
if TradeRiskType.TRAILING.equals(take_profit["trade_risk_type"]):
145145
initial_price = take_profit["open_price"]
146146
percentage = take_profit["percentage"]
147147
initial_stop_loss_price = \
148148
initial_price * (1 - (percentage / 100))
149-
return f"{float(take_profit['stop_loss_price']):.{price_precision}f}({take_profit['percentage']})% ({initial_stop_loss_price}:.{price_precision}) {take_profit['trading_symbol']}"
149+
return f"{float(take_profit['stop_loss_price']):.{price_precision}f} ({(initial_stop_loss_price):.{price_precision}f}) ({take_profit['percentage']})% {take_profit['trading_symbol']}"
150150
else:
151151
return f"{float(take_profit['stop_loss_price']):.{price_precision}f}({take_profit['percentage']})% {take_profit['trading_symbol']}"
152152

@@ -217,7 +217,7 @@ def get_stop_loss_price(take_profit):
217217
stop_loss_table["Type"] = [
218218
f"{stop_loss['trade_risk_type']}" for stop_loss in selection
219219
]
220-
stop_loss_table["Stop Loss"] = [
220+
stop_loss_table["Stop Loss (Initial Stop Loss)"] = [
221221
get_stop_loss_price(stop_loss) for stop_loss in selection
222222
]
223223
stop_loss_table["Open price"] = [
@@ -268,12 +268,12 @@ def get_sold_amount(take_profit):
268268

269269
def get_take_profit_price(take_profit):
270270

271-
if take_profit["trade_risk_type"] == "TRAILING":
271+
if TradeRiskType.TRAILING.equals(take_profit["trade_risk_type"]):
272272
initial_price = take_profit["open_price"]
273273
percentage = take_profit["percentage"]
274274
initial_take_profit_price = \
275275
initial_price * (1 + (percentage / 100))
276-
return f"{float(take_profit['take_profit_price']):.{price_precision}f}({take_profit['percentage']})% ({initial_take_profit_price}) {take_profit['trading_symbol']}"
276+
return f"{float(take_profit['take_profit_price']):.{price_precision}f} ({(initial_take_profit_price):.{price_precision}f}) ({take_profit['percentage']})% {take_profit['trading_symbol']}"
277277
else:
278278
return f"{float(take_profit['take_profit_price']):.{price_precision}f}({take_profit['percentage']})% {take_profit['trading_symbol']}"
279279

@@ -355,7 +355,7 @@ def get_status(take_profit):
355355
f"{stop_loss['trade_risk_type']}" for stop_loss
356356
in selection
357357
]
358-
take_profit_table["Take profit"] = [
358+
take_profit_table["Take profit (Initial Take Profit)"] = [
359359
get_take_profit_price(stop_loss) for stop_loss in selection
360360
]
361361
take_profit_table["Open price"] = [
@@ -772,7 +772,7 @@ def pretty_print_backtest(
772772
.:=+#%%%%%*###%%%%#*+#%%%%%%*+-: {COLOR_YELLOW}Initial balance:{COLOR_RESET}{COLOR_GREEN} {backtest_report.initial_unallocated}{COLOR_RESET}
773773
+%%%%%%%%%%%%%%%%%%%= {COLOR_YELLOW}Final balance:{COLOR_RESET}{COLOR_GREEN} {float(backtest_report.total_value):.{price_precision}f}{COLOR_RESET}
774774
:++ .=#%%%%%%%%%%%%%*- {COLOR_YELLOW}Total net gain:{COLOR_RESET}{COLOR_GREEN} {float(backtest_report.total_net_gain):.{price_precision}f} {float(backtest_report.total_net_gain_percentage):.{percentage_precision}}%{COLOR_RESET}
775-
:++: :+%%%%%%#-. {COLOR_YELLOW}Growth:{COLOR_RESET}{COLOR_GREEN} {float(backtest_report.growth):.{price_precision}f} {float(backtest_report.growth_rate):.{percentage_precision}}%{COLOR_RESET}
775+
:++: :+%%%%%%#-. {COLOR_YELLOW}Growth:{COLOR_RESET}{COLOR_GREEN} {float(backtest_report.growth):.{price_precision}f} {float(backtest_report.growth_rate):.{percentage_precision}f}%{COLOR_RESET}
776776
:++: .%%%%%#= {COLOR_YELLOW}Number of trades closed:{COLOR_RESET}{COLOR_GREEN} {backtest_report.number_of_trades_closed}{COLOR_RESET}
777777
:++: .#%%%%%#*= {COLOR_YELLOW}Number of trades open(end of backtest):{COLOR_RESET}{COLOR_GREEN} {backtest_report.number_of_trades_open}{COLOR_RESET}
778778
:++- :%%%%%%%%%+= {COLOR_YELLOW}Percentage positive trades:{COLOR_RESET}{COLOR_GREEN} {backtest_report.percentage_positive_trades}%{COLOR_RESET}

investing_algorithm_framework/services/trade_service/trade_service.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -881,12 +881,6 @@ def get_triggered_take_profit_orders(self):
881881
take_profit.active and
882882
take_profit.has_triggered(open_trade.last_reported_price)
883883
):
884-
print("Triggered take profit")
885-
print(f"take profit id: {take_profit.id}")
886-
print(f"take profit price: {take_profit.take_profit_price}")
887-
print(open_trade.last_reported_price)
888-
print(open_trade.last_reported_price_datetime)
889-
890884
triggered_take_profits.append(take_profit)
891885

892886
to_be_saved_take_profit_objects.append(take_profit)

tests/domain/models/trades/test_trade_take_profit.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ def test_is_triggered_default(self):
5252
self.assertTrue(take_profit.has_triggered(22))
5353

5454
def test_is_triggered_trailing(self):
55+
"""
56+
Test the trailing stop loss
57+
58+
* Open price: 20
59+
* Percentage: 10%
60+
* Sell percentage: 50%
61+
62+
Initial take profit price: 22
63+
64+
"""
5565
take_profit = TradeTakeProfit(
5666
trade_id=1,
5767
trade_risk_type="trailing",

0 commit comments

Comments
 (0)