Fix TP/SL calculation and make more tests for profit/loss

This commit is contained in:
Mark Veidemanis 2023-01-05 23:37:50 +00:00
parent 2dfaef324c
commit db870c39c6
Signed by: m
GPG Key ID: 5ACFCEED46C0904F
3 changed files with 170 additions and 81 deletions

View File

@ -20,34 +20,65 @@ def get_balance_hook(user_id, user_name, account_id, account_name, balance):
)
def tp_price_to_percent(tp_price, current_price, current_units, unrealised_pl):
# Is this right?
def tp_price_to_percent(tp_price, side, current_price, current_units, unrealised_pl):
"""
Determine the percent change of the TP price from the initial price.
Positive values indicate a profit, negative values indicate a loss.
"""
pl_per_unit = D(unrealised_pl) / D(current_units)
print("pl_per_unit: ", pl_per_unit)
initial_price = D(current_price) - pl_per_unit
print("initial_price: ", initial_price)
if side == "long":
initial_price = D(current_price) - pl_per_unit
else:
initial_price = D(current_price) + pl_per_unit
# Get the percent change of the TP price from the initial price.
change_percent = ((D(tp_price) - initial_price) / initial_price) * 100
print("change_percent: ", change_percent)
change_percent = ((initial_price - D(tp_price)) / initial_price) * 100
# Doesn't check direction
return abs(round(change_percent, 5))
# If the trade is long, the TP price will be lower than the initial price.
if side == "long":
change_percent *= -1
return round(change_percent, 5)
def sl_price_to_percent(sl_price, current_price, current_units, unrealised_pl):
# Is this right?
def sl_price_to_percent(sl_price, side, current_price, current_units, unrealised_pl):
"""
Determine the percent change of the SL price from the initial price.
Positive values indicate a loss, negative values indicate a profit.
This may seem backwards, but it is important to note that by default,
SL indicates a loss, and positive values should be expected.
Negative values indicate a negative loss, so a profit.
"""
pl_per_unit = D(unrealised_pl) / D(current_units)
print("pl_per_unit: ", pl_per_unit)
initial_price = D(current_price) - pl_per_unit
print("initial_price: ", initial_price)
if side == "long":
initial_price = D(current_price) - pl_per_unit
else:
initial_price = D(current_price) + pl_per_unit
# initial_price = D(current_price) - pl_per_unit
# Get the percent change of the SL price from the initial price.
change_percent = ((D(sl_price) - initial_price) / initial_price) * 100
print("change_percent: ", change_percent)
change_percent = ((initial_price - D(sl_price)) / initial_price) * 100
# Doesn't check direction
return abs(round(change_percent, 5))
# If the trade is long, the SL price will be higher than the initial price.
if side == "long":
change_percent *= -1
if side == "long":
if current_price > initial_price:
profit = True
else:
profit = False
else:
if current_price < initial_price:
profit = True
else:
profit = False
if profit:
change_percent *= -1
return round(change_percent, 5)
def convert_open_trades(open_trades):
@ -59,11 +90,12 @@ def convert_open_trades(open_trades):
current_price = trade["price"]
current_units = trade["currentUnits"]
unrealised_pl = trade["unrealizedPL"]
side = trade["side"]
cast = {
"id": trade["id"],
"symbol": trade["symbol"],
"amount": current_units,
"side": trade["side"],
"side": side,
"state": trade["state"],
"price": current_price,
"pl": unrealised_pl,
@ -73,7 +105,7 @@ def convert_open_trades(open_trades):
if trade["takeProfitOrder"]:
take_profit = trade["takeProfitOrder"]["price"]
take_profit_percent = tp_price_to_percent(
take_profit, current_price, current_units, unrealised_pl
take_profit, side, current_price, current_units, unrealised_pl
)
cast["take_profit"] = take_profit
@ -83,7 +115,7 @@ def convert_open_trades(open_trades):
if trade["stopLossOrder"]:
stop_loss = trade["stopLossOrder"]["price"]
stop_loss_percent = sl_price_to_percent(
stop_loss, current_price, current_units, unrealised_pl
stop_loss, side, current_price, current_units, unrealised_pl
)
cast["stop_loss"] = stop_loss
@ -93,7 +125,11 @@ def convert_open_trades(open_trades):
if trade["trailingStopLossOrder"]:
trailing_stop_loss = trade["trailingStopLossOrder"]["price"]
trailing_stop_loss_percent = sl_price_to_percent(
trailing_stop_loss, current_price, current_units, unrealised_pl
trailing_stop_loss,
side,
current_price,
current_units,
unrealised_pl,
)
cast["trailing_stop_loss"] = trailing_stop_loss

View File

@ -14,7 +14,7 @@ class CommonTestCase(TestCase):
current_units = 1
unrealised_pl = 0
percent = tp_price_to_percent(
tp_price, current_price, current_units, unrealised_pl
tp_price, "long", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 10)
@ -27,7 +27,7 @@ class CommonTestCase(TestCase):
current_units = 1
unrealised_pl = 0
percent = tp_price_to_percent(
tp_price, current_price, current_units, unrealised_pl
tp_price, "short", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 10)
@ -41,23 +41,37 @@ class CommonTestCase(TestCase):
current_units = 1
unrealised_pl = 0.1 # 10%
percent = tp_price_to_percent(
tp_price, current_price, current_units, unrealised_pl
tp_price, "long", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 20)
def test_tp_price_to_percent_change_short(self):
def test_tp_price_to_percent_change_long_loss(self):
"""
Test that the TP price to percent conversion works for long trades
when the price has changed and the TP is at a loss.
"""
tp_price = 0.8 # -20%
current_price = 0.9 # - 10%
current_units = 1
unrealised_pl = -0.1 # -10%
percent = tp_price_to_percent(
tp_price, "long", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, -20)
def test_tp_price_to_percent_change_short_loss(self):
"""
Test that the TP price to percent conversion works for short trades
when the price has changed.
when the price has changed and the TP is at a loss.
"""
tp_price = 0.8 # 20%
current_price = 1.1 # + 10%
tp_price = 1.2 # -20%
current_price = 1.1 # - 10%
current_units = 1
unrealised_pl = 0.1 # 10%
unrealised_pl = -0.1 # -10%
percent = tp_price_to_percent(
tp_price, current_price, current_units, unrealised_pl
tp_price, "short", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 20)
self.assertEqual(percent, -20)
# For multiple units
def test_tp_price_to_percent_initial_long_multi(self):
@ -70,7 +84,7 @@ class CommonTestCase(TestCase):
current_units = 10
unrealised_pl = 0
percent = tp_price_to_percent(
tp_price, current_price, current_units, unrealised_pl
tp_price, "long", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 10)
@ -84,7 +98,7 @@ class CommonTestCase(TestCase):
current_units = 10
unrealised_pl = 0
percent = tp_price_to_percent(
tp_price, current_price, current_units, unrealised_pl
tp_price, "short", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 10)
@ -93,12 +107,12 @@ class CommonTestCase(TestCase):
Test that the TP price to percent conversion works for long trades
when the price has changed, with multiple units.
"""
tp_price = 1.2 # 20%
current_price = 1.1 # + 10%
tp_price = 1.2 # +20%
current_price = 1.1 # +10%
current_units = 10
unrealised_pl = 1 # 10%
percent = tp_price_to_percent(
tp_price, current_price, current_units, unrealised_pl
tp_price, "long", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 20)
@ -107,17 +121,45 @@ class CommonTestCase(TestCase):
Test that the TP price to percent conversion works for short trades
when the price has changed, with multiple units.
"""
tp_price = 0.8 # 20%
current_price = 1.1 # + 10%
tp_price = 0.8 # -20%
current_price = 0.9 # -10%
current_units = 10
unrealised_pl = 1 # 10%
percent = tp_price_to_percent(
tp_price, current_price, current_units, unrealised_pl
tp_price, "short", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 20)
def test_tp_price_to_percent_change_long_multi_loss(self):
"""
Test that the TP price to percent conversion works for long trades
when the price has changed, with multiple units, and the TP is at a loss.
"""
tp_price = 0.8 # -20%
current_price = 0.9 # -10%
current_units = 10
unrealised_pl = -1 # -10%
percent = tp_price_to_percent(
tp_price, "long", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, -20)
def test_tp_price_to_percent_change_short_multi_loss(self):
"""
Test that the TP price to percent conversion works for short trades
when the price has changed, with multiple units, and the TP is at a loss.
"""
tp_price = 1.2 # -20%
current_price = 1.1 # -10%
current_units = 10
unrealised_pl = -1 # 10%
percent = tp_price_to_percent(
tp_price, "short", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, -20)
# SL
def test_SL_price_to_percent_initial_long(self):
def test_sl_price_to_percent_initial_long(self):
"""
Test that the SL price to percent conversion works for long trades.
"""
@ -126,7 +168,7 @@ class CommonTestCase(TestCase):
current_units = 1
unrealised_pl = 0
percent = sl_price_to_percent(
sl_price, current_price, current_units, unrealised_pl
sl_price, "long", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 10)
@ -139,37 +181,37 @@ class CommonTestCase(TestCase):
current_units = 1
unrealised_pl = 0
percent = sl_price_to_percent(
sl_price, current_price, current_units, unrealised_pl
sl_price, "short", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 10)
def test_sl_price_to_percent_change_long(self):
def test_sl_price_to_percent_change_long_profit(self):
"""
Test that the SL price to percent conversion works for long trades
when the price has changed.
when the price has changed and the SL is at a profit.
"""
sl_price = 1.2 # 20%
current_price = 1.1 # + 10%
sl_price = 1.2 # +20%
current_price = 1.1 # +10%
current_units = 1
unrealised_pl = 0.1 # 10%
unrealised_pl = 0.1 # +10%
percent = sl_price_to_percent(
sl_price, current_price, current_units, unrealised_pl
sl_price, "long", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 20)
self.assertEqual(percent, -20)
def test_sl_price_to_percent_change_short(self):
def test_sl_price_to_percent_change_short_profit(self):
"""
Test that the SL price to percent conversion works for short trades
when the price has changed.
when the price has changed and the SL is at a profit.
"""
sl_price = 0.8 # 20%
current_price = 1.1 # + 10%
sl_price = 0.8 # +20%
current_price = 0.9 # +10%
current_units = 1
unrealised_pl = 0.1 # 10%
unrealised_pl = 0.1 # +10%
percent = sl_price_to_percent(
sl_price, current_price, current_units, unrealised_pl
sl_price, "short", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 20)
self.assertEqual(percent, -20)
# For multiple units
def test_sl_price_to_percent_initial_long_multi(self):
@ -177,12 +219,12 @@ class CommonTestCase(TestCase):
Test that the SL price to percent conversion works for long trades
with multiple units.
"""
sl_price = 1.1 # 10%
sl_price = 1.1 # -10%
current_price = 1.0
current_units = 10
unrealised_pl = 0
percent = sl_price_to_percent(
sl_price, current_price, current_units, unrealised_pl
sl_price, "long", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 10)
@ -191,39 +233,39 @@ class CommonTestCase(TestCase):
Test that the SL price to percent conversion works for short trades
with multiple units.
"""
sl_price = 0.9 # 10%
sl_price = 0.9 # -10%
current_price = 1.0
current_units = 10
unrealised_pl = 0
percent = sl_price_to_percent(
sl_price, current_price, current_units, unrealised_pl
sl_price, "short", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 10)
def test_sl_price_to_percent_change_long_multi(self):
def test_sl_price_to_percent_change_long_multi_profit(self):
"""
Test that the SL price to percent conversion works for long trades
when the price has changed, with multiple units.
when the price has changed, with multiple units, and the SL is at a profit.
"""
sl_price = 1.2 # 20%
current_price = 1.1 # + 10%
sl_price = 1.2 # +20%
current_price = 1.1 # +10%
current_units = 10
unrealised_pl = 1 # 10%
unrealised_pl = 1 # +10%
percent = sl_price_to_percent(
sl_price, current_price, current_units, unrealised_pl
sl_price, "long", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 20)
self.assertEqual(percent, -20)
def test_sl_price_to_percent_change_short_multi(self):
def test_sl_price_to_percent_change_short_multi_profit(self):
"""
Test that the SL price to percent conversion works for short trades
when the price has changed, with multiple units.
when the price has changed, with multiple units, and the SL is at a profit.
"""
sl_price = 0.8 # 20%
current_price = 1.1 # + 10%
sl_price = 0.8 # -20%
current_price = 0.9 # +10%
current_units = 10
unrealised_pl = 1 # 10%
unrealised_pl = 1 # +10%
percent = sl_price_to_percent(
sl_price, current_price, current_units, unrealised_pl
sl_price, "short", current_price, current_units, unrealised_pl
)
self.assertEqual(percent, 20)
self.assertEqual(percent, -20)

View File

@ -1,3 +1,5 @@
from decimal import Decimal as D
from django.test import TestCase
from core.exchanges.common import convert_open_trades
@ -18,6 +20,7 @@ class LiveTradingTestCase(ElasticMock, LiveBase, TestCase):
amount=10,
direction="buy",
)
self.commission = 0.025
def test_account_functional(self):
"""
@ -123,11 +126,19 @@ class LiveTradingTestCase(ElasticMock, LiveBase, TestCase):
# trailing_stop_loss=trade_tsl,
)
posted = self.open_trade(complex_trade)
print("OPENED", posted)
self.open_trade(complex_trade)
trades = self.account.client.get_all_open_trades()
print("TRADES", trades)
trades_converted = convert_open_trades(trades["itemlist"])
print("TRADES CONVERTED", trades_converted)
closed = self.close_trade(complex_trade)
print("CLOSED", closed)
self.assertEqual(len(trades_converted), 1)
expected_tp_percent = D(1 - self.commission)
expected_sl_percent = D(2 - self.commission)
actual_tp_percent = trades_converted[0]["take_profit_percent"]
actual_sl_percent = trades_converted[0]["stop_loss_percent"]
tp_percent_difference = abs(expected_tp_percent - actual_tp_percent)
sl_percent_difference = abs(expected_sl_percent - actual_sl_percent)
max_difference = D(0.08) # depends on market conditions
self.assertLess(tp_percent_difference, max_difference)
self.assertLess(sl_percent_difference, max_difference)
self.close_trade(complex_trade)