Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions algobot/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,16 @@
LONG = 1
SHORT = -1

TRAILING = 2
STOP = 1

class LossStrategy:
TRAILING = 2
STOP = 1


class ProfitType:
TRAILING = 2
STOP = 1


BACKTEST = 2
SIMULATION = 3
Expand Down
20 changes: 11 additions & 9 deletions algobot/interface/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
QTabWidget)

import algobot.helpers as helpers
from algobot.enums import BACKTEST, LIVE, OPTIMIZER, SIMULATION, STOP, TRAILING
from algobot.enums import (BACKTEST, LIVE, OPTIMIZER, SIMULATION, LossStrategy,
ProfitType)
from algobot.graph_helpers import create_infinite_line
from algobot.interface.config_utils.credential_utils import load_credentials
from algobot.interface.config_utils.slot_utils import load_slots
Expand Down Expand Up @@ -341,9 +342,9 @@ def get_take_profit_settings(self, caller) -> dict:
dictionary = self.takeProfitDict
if dictionary[tab, 'groupBox'].isChecked():
if dictionary[tab, 'takeProfitType'].currentText() == "Trailing":
takeProfitType = TRAILING
takeProfitType = ProfitType.TRAILING
else:
takeProfitType = STOP
takeProfitType = ProfitType.STOP
else:
takeProfitType = None

Expand Down Expand Up @@ -378,21 +379,22 @@ def get_loss_settings(self, caller: int) -> dict:
tab = self.get_category_tab(caller)
dictionary = self.lossDict
if dictionary[tab, 'groupBox'].isChecked():
lossType = TRAILING if dictionary[tab, "lossType"].currentText() == "Trailing" else STOP
loss_type = dictionary[tab, "lossType"].currentText()
loss_strategy = LossStrategy.TRAILING if loss_type == "Trailing" else LossStrategy.STOP
else:
lossType = None
loss_strategy = None

lossSettings = {
'lossType': lossType,
loss_settings = {
'lossType': loss_strategy,
'lossTypeIndex': dictionary[tab, "lossType"].currentIndex(),
'lossPercentage': dictionary[tab, 'lossPercentage'].value(),
'smartStopLossCounter': dictionary[tab, 'smartStopLossCounter'].value()
}

if tab != self.backtestConfigurationTabWidget:
lossSettings['safetyTimer'] = dictionary[tab, 'safetyTimer'].value()
loss_settings['safetyTimer'] = dictionary[tab, 'safetyTimer'].value()

return lossSettings
return loss_settings

def add_strategy_to_config(self, caller: int, strategyName: str, config: dict):
"""
Expand Down
39 changes: 20 additions & 19 deletions algobot/traders/trader.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
This will be the main Trader class that all other Traders will inherit from.
"""
from datetime import datetime
from typing import Dict, List, Union
from typing import Dict, List, Optional, Union

from algobot.enums import (BEARISH, BULLISH, ENTER_LONG, ENTER_SHORT,
EXIT_LONG, EXIT_SHORT, LONG, SHORT, STOP, TRAILING)
EXIT_LONG, EXIT_SHORT, LONG, SHORT, LossStrategy,
ProfitType)
from algobot.helpers import get_label_string, parse_strategy_name
from algobot.strategies.strategy import Strategy

Expand Down Expand Up @@ -224,16 +225,16 @@ def get_stop_loss(self):
if self.currentPosition == SHORT:
if self.smartStopLossEnter and self.previousStopLoss > self.currentPrice:
self.stopLoss = self.previousStopLoss
elif self.lossStrategy == TRAILING:
elif self.lossStrategy == LossStrategy.TRAILING:
self.stopLoss = self.shortTrailingPrice * (1 + self.lossPercentageDecimal)
elif self.lossStrategy == STOP:
elif self.lossStrategy == LossStrategy.STOP:
self.stopLoss = self.sellShortPrice * (1 + self.lossPercentageDecimal)
elif self.currentPosition == LONG:
if self.smartStopLossEnter and self.previousStopLoss < self.currentPrice:
self.stopLoss = self.previousStopLoss
elif self.lossStrategy == TRAILING:
elif self.lossStrategy == LossStrategy.TRAILING:
self.stopLoss = self.longTrailingPrice * (1 - self.lossPercentageDecimal)
elif self.lossStrategy == STOP:
elif self.lossStrategy == LossStrategy.STOP:
self.stopLoss = self.buyLongPrice * (1 - self.lossPercentageDecimal)

if self.stopLoss is not None: # This is for the smart stop loss to reenter position.
Expand All @@ -246,9 +247,9 @@ def get_stop_loss_strategy_string(self) -> str:
Returns stop loss strategy in string format, instead of integer enum.
:return: Stop loss strategy in string format.
"""
if self.lossStrategy == STOP:
if self.lossStrategy == LossStrategy.STOP:
return 'Stop Loss'
elif self.lossStrategy == TRAILING:
elif self.lossStrategy == LossStrategy.TRAILING:
return 'Trailing Loss'
elif self.lossStrategy is None:
return 'None'
Expand Down Expand Up @@ -322,26 +323,26 @@ def get_profit_percentage(initialNet: float, finalNet: float) -> float:
return -1 * (100 - finalNet / initialNet * 100)

@staticmethod
def get_trailing_or_stop_type_string(stopType: Union[int, None]) -> str:
def get_trailing_or_stop_type_string(profit_type: Optional[int]) -> str:
"""
Returns stop type in string format instead of integer enum.
:return: Stop type in string format.
"""
if stopType == STOP:
if profit_type == ProfitType.STOP:
return 'Stop'
elif stopType == TRAILING:
elif profit_type == ProfitType.TRAILING:
return 'Trailing'
elif stopType is None:
elif profit_type is None:
return 'None'
else:
raise ValueError("Unknown type of exit position type.")

@staticmethod
def get_enum_from_str(string):
if string.lower() == "trailing":
return TRAILING
elif string.lower() == 'stop':
return STOP
def get_enum_from_str(value: str) -> int:
if value.lower() == "trailing":
return ProfitType.TRAILING
elif value.lower() == 'stop':
return ProfitType.STOP

@staticmethod
def get_trend_string(trend) -> str:
Expand Down Expand Up @@ -436,12 +437,12 @@ def get_take_profit(self) -> Union[float, None]:
return None

if self.currentPosition == SHORT:
if self.takeProfitType == STOP:
if self.takeProfitType == ProfitType.STOP:
self.takeProfitPoint = self.sellShortPrice * (1 - self.takeProfitPercentageDecimal)
else:
raise ValueError("Invalid type of take profit type provided.")
elif self.currentPosition == LONG:
if self.takeProfitType == STOP:
if self.takeProfitType == ProfitType.STOP:
self.takeProfitPoint = self.buyLongPrice * (1 + self.takeProfitPercentageDecimal)
else:
raise ValueError("Invalid type of take profit type provided.")
Expand Down
16 changes: 8 additions & 8 deletions tests/test_backtester.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest

from algobot.enums import LONG, SHORT, STOP, TRAILING
from algobot.enums import LONG, SHORT, LossStrategy, ProfitType
from algobot.helpers import convert_all_dates_to_datetime, load_from_csv
from algobot.traders.backtester import Backtester

Expand All @@ -25,8 +25,8 @@ def setUp(self) -> None:
symbol="1INCHUSDT",
marginEnabled=True,
)
self.backtester.apply_take_profit_settings({'takeProfitType': TRAILING, 'takeProfitPercentage': 5})
self.backtester.apply_loss_settings({'lossType': TRAILING, 'lossPercentage': 5})
self.backtester.apply_take_profit_settings({'takeProfitType': ProfitType.TRAILING, 'takeProfitPercentage': 5})
self.backtester.apply_loss_settings({'lossType': LossStrategy.TRAILING, 'lossPercentage': 5})

def test_initialization(self):
"""
Expand Down Expand Up @@ -281,39 +281,39 @@ def test_long_stop_loss(self):
Test backtester stop loss logic in a long position.
"""
backtester = self.backtester
backtester.lossStrategy = STOP
backtester.lossStrategy = LossStrategy.STOP
backtester.set_priced_current_price_and_period(5)
backtester.buy_long("Test purchase.")
self.assertEqual(backtester.get_stop_loss(), 5 * (1 - backtester.lossPercentageDecimal))

backtester.set_priced_current_price_and_period(10)
self.assertEqual(backtester.get_stop_loss(), 5 * (1 - backtester.lossPercentageDecimal))

backtester.lossStrategy = TRAILING
backtester.lossStrategy = LossStrategy.TRAILING
self.assertEqual(backtester.get_stop_loss(), 10 * (1 - backtester.lossPercentageDecimal))

def test_short_stop_loss(self):
"""
Test backtester stop loss logic in a short position.
"""
backtester = self.backtester
backtester.lossStrategy = STOP
backtester.lossStrategy = LossStrategy.STOP
backtester.set_priced_current_price_and_period(5)
backtester.sell_short("Test short.")
self.assertEqual(backtester.get_stop_loss(), 5 * (1 + backtester.lossPercentageDecimal))

backtester.set_priced_current_price_and_period(3)
self.assertEqual(backtester.get_stop_loss(), 5 * (1 + backtester.lossPercentageDecimal))

backtester.lossStrategy = TRAILING
backtester.lossStrategy = LossStrategy.TRAILING
self.assertEqual(backtester.get_stop_loss(), 3 * (1 + backtester.lossPercentageDecimal))

def test_stop_take_profit(self):
"""
Test backtester take profit logic.
"""
backtester = self.backtester
backtester.takeProfitType = STOP
backtester.takeProfitType = ProfitType.STOP
backtester.set_priced_current_price_and_period(10)
backtester.buy_long("Test purchase.")
self.assertEqual(backtester.get_take_profit(), 10 * (1 + backtester.takeProfitPercentageDecimal))
Expand Down
23 changes: 12 additions & 11 deletions tests/test_base_trader.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import pytest

from algobot.enums import BEARISH, BULLISH, LONG, SHORT, STOP, TRAILING
from algobot.enums import (BEARISH, BULLISH, LONG, SHORT, LossStrategy,
ProfitType)
from algobot.strategies.strategy import Strategy
from algobot.traders.trader import Trader

Expand Down Expand Up @@ -149,30 +150,30 @@ def test_set_safety_timer(self):
def test_apply_take_profit_settings(self):
take_profit_settings = {
'takeProfitPercentage': 25,
'takeProfitType': STOP
'takeProfitType': ProfitType.STOP
}
self.trader.apply_take_profit_settings(take_profit_settings)

self.assertEqual(self.trader.takeProfitPercentageDecimal, 0.25)
self.assertEqual(self.trader.takeProfitType, STOP)
self.assertEqual(self.trader.takeProfitType, ProfitType.STOP)

def test_apply_loss_settings(self):
loss_settings = {
'lossType': STOP,
'lossType': LossStrategy.STOP,
'lossPercentage': 5.5,
'smartStopLossCounter': 15,
'safetyTimer': 45
}
self.trader.apply_loss_settings(loss_settings)

self.assertEqual(self.trader.lossStrategy, STOP)
self.assertEqual(self.trader.lossStrategy, LossStrategy.STOP)
self.assertEqual(self.trader.lossPercentageDecimal, 0.055)
self.assertEqual(self.trader.smartStopLossInitialCounter, 15)
self.assertEqual(self.trader.smartStopLossCounter, 15)
self.assertEqual(self.trader.safetyTimer, 45)

def test_get_stop_loss(self):
self.trader.lossStrategy = STOP
self.trader.lossStrategy = LossStrategy.STOP
self.trader.lossPercentageDecimal = 0.1
self.trader.currentPrice = 5

Expand All @@ -190,10 +191,10 @@ def test_get_stop_loss(self):
# TODO implement trailing stop loss test

def test_get_stop_loss_strategy_string(self):
self.trader.lossStrategy = STOP
self.trader.lossStrategy = LossStrategy.STOP
self.assertEqual(self.trader.get_stop_loss_strategy_string(), "Stop Loss")

self.trader.lossStrategy = TRAILING
self.trader.lossStrategy = LossStrategy.TRAILING
self.assertEqual(self.trader.get_stop_loss_strategy_string(), "Trailing Loss")

self.trader.lossStrategy = None
Expand Down Expand Up @@ -274,8 +275,8 @@ def test_get_profit_percentage(self):
self.assertEqual(self.trader.get_profit_percentage(100, 130), 30)

def test_get_trailing_or_stop_loss_string(self):
self.assertEqual(self.trader.get_trailing_or_stop_type_string(STOP), 'Stop')
self.assertEqual(self.trader.get_trailing_or_stop_type_string(TRAILING), 'Trailing')
self.assertEqual(self.trader.get_trailing_or_stop_type_string(ProfitType.STOP), 'Stop')
self.assertEqual(self.trader.get_trailing_or_stop_type_string(ProfitType.TRAILING), 'Trailing')
self.assertEqual(self.trader.get_trailing_or_stop_type_string(None), 'None')

def test_get_trend_string(self):
Expand Down Expand Up @@ -321,7 +322,7 @@ def test_get_safe_rounded_string(self):
multiplier=5), '6.15*')

def test_get_take_profit(self):
self.trader.takeProfitType = STOP
self.trader.takeProfitType = ProfitType.STOP
self.trader.takeProfitPercentageDecimal = 0.05

self.trader.currentPosition = LONG
Expand Down