summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCoprDistGit <infra@openeuler.org>2023-05-18 06:59:56 +0000
committerCoprDistGit <infra@openeuler.org>2023-05-18 06:59:56 +0000
commit78e1e7c7cff07396b8419f9e0f4589f4ab4ed8f8 (patch)
treeb149b2b2064036475a68457875f14cd4c2858267
parent21043fcb79cbc81add3b26d0c52bc075e71a5ff0 (diff)
automatic import of python-testwise
-rw-r--r--.gitignore1
-rw-r--r--python-testwise.spec998
-rw-r--r--sources1
3 files changed, 1000 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index e69de29..19fade4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/testwise-0.0.63.tar.gz
diff --git a/python-testwise.spec b/python-testwise.spec
new file mode 100644
index 0000000..8924976
--- /dev/null
+++ b/python-testwise.spec
@@ -0,0 +1,998 @@
+%global _empty_manifest_terminate_build 0
+Name: python-testwise
+Version: 0.0.63
+Release: 1
+Summary: A backtester (backtest helper) for testing my trading strategies.
+License: MIT License
+URL: https://github.com/aticio/testwise
+Source0: https://mirrors.nju.edu.cn/pypi/web/packages/68/2f/a32200ddc5c3390dda0460de00b824bf742f67adc5c3cf028d87acb75788/testwise-0.0.63.tar.gz
+BuildArch: noarch
+
+Requires: python3-matplotlib
+Requires: python3-pytest
+
+%description
+# Testwise
+
+![Publish Python 🐍 distributions 📦 to PyPI and TestPyPI](https://github.com/aticio/legitindicators/workflows/Publish%20Python%20%F0%9F%90%8D%20distributions%20%F0%9F%93%A6%20to%20PyPI%20and%20TestPyPI/badge.svg)
+
+A backtester (backtest helper) for testing my trading strategies.
+
+It requires a lot of manual processing and coding. Difficult to comprehend. I tried to explain the use of the library with examples as best I could. But writing such automation is quite complex.
+There may still be errors. It is pretty difficult to check but I'm trying to improve the usage.
+
+## Example Usage
+```python
+# Testwise is a backtester library that requires some coding knowledge
+# There is no cli or interface.
+# You should directly execute necessary functions like enter_long() or exit_short()
+# This is a backtesting example of Exponential Moving Average cross strategy.
+# There is a 1.5 ATR stop loss level and a 1 ATR take profit level for every position.
+# Commission rate is 0.1000%.
+# Margin usage is allowed up to 5 times the main capital.
+from datetime import datetime, timedelta
+from testwise import Testwise
+import requests
+from legitindicators import ema, atr
+
+# In this example, daily BTCUSDT kline data is used from binance
+# Let's say you want to backtest your strategy for about 450 days.
+# It would be useful to add some extra days to the specified time interval
+# for the indicators to work properly.
+# (For example 10 days of EMA won't be calculated for the first 9 days of time range)
+# In this example I add 40 extra days. This value can be determined by assigning the TRIM variable
+TRIM = 40
+BINANCE_URL = "https://api.binance.com/api/v3/klines"
+SYMBOL = "BTCUSDT"
+INTERVAL = "1d"
+
+# These are the initial paramters for backtester.
+# You can find a more detailed explanation where the Testwise definition is given below.
+COMMISSION = 0.001
+DYNAMIC_POSITIONING = True
+MARGIN_FACTOR = 5
+LIMIT_FACTOR = 1
+RISK_FACTOR = 1.5
+
+
+def main():
+ # Here we define the start time and end time of backtesting.
+ # Notice usage of TRIM variable to start to backtest a few days earlier for proper indicator use.
+ start_time = datetime(2020, 6, 1, 0, 0, 0)
+ start_time = start_time - timedelta(days=TRIM)
+
+ end_time = datetime(2021, 9, 1, 0, 0, 0)
+
+ # In this example, timestamps are used. (Because binance accept timestamp)
+ start_time_ts = int(datetime.timestamp(start_time) * 1000)
+ end_time_ts = int(datetime.timestamp(end_time) * 1000)
+
+ backtest(start_time_ts, end_time_ts)
+
+
+def backtest(start_time, end_time):
+ # Getting OHLC data
+ # Example binance kline response
+ # [
+ # [
+ # 1499040000000, // Open time
+ # "0.01634790", // Open
+ # "0.80000000", // High
+ # "0.01575800", // Low
+ # "0.01577100", // Close
+ # "148976.11427815", // Volume
+ # 1499644799999, // Close time
+ # "2434.19055334", // Quote asset volume
+ # 308, // Number of trades
+ # "1756.87402397", // Taker buy base asset volume
+ # "28.46694368", // Taker buy quote asset volume
+ # "17928899.62484339" // Ignore.
+ # ]
+ # ]
+ params = {"symbol": SYMBOL, "interval": INTERVAL, "startTime": start_time, "endTime": end_time}
+ data = get_data(params)
+ opn, high, low, close = get_ohlc(data)
+
+ # Again for proper indicator usage number of bars to work on is defined as lookback
+ lookback = len(data) - TRIM
+
+ # These are simply trimmed OHLC data
+ data = data[-lookback:]
+ # Here, a list of close prices kept under different naming conventions than other OHL data
+ # That is because I will use this close data as a parameter
+ # for Exponential Moving Average indicator and then trim the list of EMA values afterward.
+ close_tmp = close[-lookback:]
+ opn = opn[-lookback:]
+ high = high[-lookback:]
+ low = low[-lookback:]
+
+ # Here is the calculation of ATR values historically. I use legitindicators library.
+ atr_input = []
+ for i, _ in enumerate(data):
+ ohlc = [opn[i], high[i], low[i], close_tmp[i]]
+ atr_input.append(ohlc)
+ atrng = atr(atr_input, 14)
+
+ # Backtesting operation starts here.
+ # Following two for loops will check two EMA crosses in the range of 10 to 30
+ for ema_length1 in range(10, 11):
+ for ema_length2 in range(ema_length1 + 1, 30):
+ # When the dynamic_positioning is set to True,
+ # the backtester will work as if the margin usage is available for use.
+ # margin_factor indicates the margin ratio. (In this example, it is 5 times the main capital)
+ # limit_factor is an ATR based take profit level. (it is 1 ATR from the position price)
+ # risk_factor is an ATR based stop loss level. (it is 1.5 ATR from the position price)
+ twise = Testwise(
+ commission=COMMISSION,
+ dynamic_positioning=DYNAMIC_POSITIONING,
+ margin_factor=MARGIN_FACTOR,
+ limit_factor=LIMIT_FACTOR,
+ risk_factor=RISK_FACTOR
+ )
+
+ # Here, two EMA indicators are defined. I use legitindicators library.
+ ema_first = ema(close, ema_length1)
+ ema_second = ema(close, ema_length2)
+ # List of indicator values trimmed accordingly
+ ema_first = ema_first[-lookback:]
+ ema_second = ema_second[-lookback:]
+
+ # Notice that at this point:
+ # open, high, low, close, ema_first and ema_second lists are all trimmed
+ # and all have the same length
+ # Ready for testing
+
+ # Start walking on the data taken from the binance.
+ for i, _ in enumerate(data):
+ # Exclude first price data
+ if i > 1 and i < len(data) - 1:
+ # Here, data[n][0] is the open time of price data
+ # date_open is kept for use if there will be a pose to be opened the next day
+ # date_close is kept for use if the current open position is closed in this iteration
+ date_open = datetime.fromtimestamp(int(data[i+1][0] / 1000)).strftime("%Y-%m-%d %H")
+ date_close = datetime.fromtimestamp(int(data[i][0] / 1000)).strftime("%Y-%m-%d %H")
+
+ # Position exits
+ # On every iteration, position exits checked firstly
+ # Below, if the current position is long (1 means long) and
+ # the ema_first crosses below the ema_second, position exit function triggered
+ if twise.pos == 1 and (ema_first[i] < ema_second[i]):
+ # exit_long function takes closing date,
+ # closing price as open price of next day opn[i + 1],
+ # and amount to close the position.
+ # This amount already kept in twise.current_open_pos["qty"].
+ # This value is set when opening the positions
+ twise.exit_long(date_close, opn[i + 1], twise.current_open_pos["qty"])
+
+ # Closing short position(-1 means short)
+ if twise.pos == -1 and (ema_first[i] > ema_second[i]):
+ twise.exit_short(date_close, opn[i + 1], twise.current_open_pos["qty"])
+
+ # The following if condition simulates price movements inside the bar.
+ # This is crucial if you want to add take profit and stop loss logic to the backtester.
+ # This pine script broker emulator documentation will explain this condition more clearly:
+ # https://www.tradingview.com/pine-script-docs/en/v5/concepts/Strategies.html?highlight=strategy#broker-emulator
+ if abs(high[i] - opn[i]) < abs(low[i] - opn[i]):
+ # Simply, If the bar’s high is closer to bar’s open than the bar’s low,
+ # bar movement will be like:
+ # open - high - low - close
+
+ # In this movement, take profit operation will be checked before stop loss.
+ # This is because, it is assumed that the price will go up first.
+ # For example, if both take profit and stop loss prices are exceeded,
+ # it is assumed that first, take profit is taken, than stop loss price is reached.
+
+ # if current position is long, here is take profit logic:
+ # if current position is long and high is
+ # higher than take proift price (twise.current_open_pos["tp"])
+ # and take profit is not taken (twise.current_open_pos["tptaken"] is False)
+ if twise.pos == 1 and high[i] > twise.current_open_pos["tp"] and twise.current_open_pos["tptaken"] is False:
+ # Stop loss price will be set to break even with break_even() function
+ twise.break_even()
+ # Take profit operation is simply a partially position closing operation.
+ # Here, half of the position is closed. (twise.current_open_pos["qty"] / 2)
+ twise.exit_long(date_close, twise.current_open_pos["tp"], twise.current_open_pos["qty"] / 2, True)
+
+ # if current position is long, here is stop loss logic:
+ # if current position is long and low is
+ # lower than stop loss price (twise.current_open_pos["sl"])
+ if twise.pos == 1 and low[i] < twise.current_open_pos["sl"]:
+ twise.exit_long(date_close, twise.current_open_pos["sl"], twise.current_open_pos["qty"])
+
+ # if current position is short, here is take profit logic:
+ if twise.pos == -1 and high[i] > twise.current_open_pos["sl"]:
+ twise.exit_short(date_close, twise.current_open_pos["sl"], twise.current_open_pos["qty"])
+
+ # if current position is short, here is stop loss logic:
+ if twise.pos == -1 and low[i] < twise.current_open_pos["tp"] and twise.current_open_pos["tptaken"] is False:
+ twise.break_even()
+ twise.exit_short(date_close, twise.current_open_pos["tp"], twise.current_open_pos["qty"] / 2, True)
+ else:
+ # If the bar’s low is closer to bar’s open than the bar’s high,
+ # bar movement will be like:
+ # open - low - high - close
+
+ # In this movement, stop loss operation will be checked before take profit.
+ # This is because, it is assumed that the price will go down firstly.
+ # For example, if both take profit and stop loss prices are exceeded,
+ # it is assumed that first, stop loss is executed,
+ # then take profit will never be reached because
+ # if the position is fully closed with exit_long,
+ # twise.pos value will be 0 (which means there is no open position).
+
+ # if the current position is long, here is stop loss logic:
+ if twise.pos == 1 and low[i] < twise.current_open_pos["sl"]:
+ twise.exit_long(date_close, twise.current_open_pos["sl"], twise.current_open_pos["qty"])
+
+ # if current position is long, here is take profit logic:
+ if twise.pos == 1 and high[i] > twise.current_open_pos["tp"] and twise.current_open_pos["tptaken"] is False:
+ twise.break_even()
+ twise.exit_long(date_close, twise.current_open_pos["tp"], twise.current_open_pos["qty"] / 2, True)
+
+ # if current position is short, here is take profit logic:
+ if twise.pos == -1 and low[i] < twise.current_open_pos["tp"] and twise.current_open_pos["tptaken"] is False:
+ twise.break_even()
+ twise.exit_short(date_close, twise.current_open_pos["tp"], twise.current_open_pos["qty"] / 2, True)
+
+ # if current position is short, here is stop loss logic:
+ if twise.pos == -1 and high[i] > twise.current_open_pos["sl"]:
+ twise.exit_short(date_close, twise.current_open_pos["sl"], twise.current_open_pos["qty"])
+
+ # Opening long position
+ # If there is no long positions open
+ if twise.pos != 1:
+ # If ema_first crosses over ema_second
+ if ema_first[i] > ema_second[i]:
+ # You can manually set the amount to open position.
+ # But there will be a calculation overhead.
+ # Testwise has a built-in share calculation funciton
+ # In tihs function, share is calculated as:
+ # share = (equity * position risk) / (atr * risk factor)
+ share = twise.calculate_share(atrng[i], custom_position_risk=0.02)
+ # Opening long position with opening date (date_open),
+ # opening price of next day (opn[i + 1]),
+ # amount to buy, and current atr value to define take profit and stop loss prices
+ twise.entry_long(date_open, opn[i + 1], share, atrng[i])
+
+ if twise.pos != -1:
+ if ema_first[i] < ema_second[i]:
+ share = twise.calculate_share(atrng[i], custom_position_risk=0.02)
+ # Opening short position with opening date (date_open),
+ # opening price of next day (opn[i + 1]),
+ # amount to buy, and current atr value to define take profit and stop loss prices
+ twise.entry_short(date_open, opn[i + 1], share, atrng[i])
+ # get_result() function will give you the backtest results
+ print(twise.get_result())
+
+
+def get_data(params):
+ r = requests.get(url=BINANCE_URL, params=params)
+ data = r.json()
+ return data
+
+
+def get_ohlc(data):
+ opn = [float(o[1]) for o in data]
+ close = [float(d[4]) for d in data]
+ high = [float(h[2]) for h in data]
+ low = [float(lo[3]) for lo in data]
+
+ return opn, high, low, close
+
+
+if __name__ == "__main__":
+ main()
+```
+
+```python
+Example backtest result:
+{
+ 'net_profit': 30557.012567638478,
+ 'net_profit_percent': 30.557012567638477,
+ 'gross_profit': 69163.31181062985,
+ 'gross_loss': 36783.34343506002,
+ 'max_drawdown': -13265.365111723615,
+ 'max_drawdown_rate': 2.3035183962356918,
+ 'win_rate': 53.48837209302326,
+ 'risk_reward_ratio': 1.6350338129618904,
+ 'profit_factor': 1.880288884906174,
+ 'ehlers_ratio': 0.1311829454585705,
+ 'return_on_capital': 0.26978249297565415,
+ 'max_capital_required': 113265.36511172361,
+ 'total_trades': 43,
+ 'pearsonsr': 0.8022110890986095,
+ 'number_of_winning_trades': 23,
+ 'number_of_losing_trades': 20,
+ 'largest_winning_trade': ('2021-01-23 03', 34417.71907928039),
+ 'largest_losing_trade': ('2020-09-21 03', -4627.351985682239)}
+```
+
+## Important note:
+Do not rely on a single test result.
+At least do walkforward test with a few iterations.
+
+## Installation
+
+Run the following to install:
+
+```python
+pip install testwise
+```
+
+
+
+
+%package -n python3-testwise
+Summary: A backtester (backtest helper) for testing my trading strategies.
+Provides: python-testwise
+BuildRequires: python3-devel
+BuildRequires: python3-setuptools
+BuildRequires: python3-pip
+%description -n python3-testwise
+# Testwise
+
+![Publish Python 🐍 distributions 📦 to PyPI and TestPyPI](https://github.com/aticio/legitindicators/workflows/Publish%20Python%20%F0%9F%90%8D%20distributions%20%F0%9F%93%A6%20to%20PyPI%20and%20TestPyPI/badge.svg)
+
+A backtester (backtest helper) for testing my trading strategies.
+
+It requires a lot of manual processing and coding. Difficult to comprehend. I tried to explain the use of the library with examples as best I could. But writing such automation is quite complex.
+There may still be errors. It is pretty difficult to check but I'm trying to improve the usage.
+
+## Example Usage
+```python
+# Testwise is a backtester library that requires some coding knowledge
+# There is no cli or interface.
+# You should directly execute necessary functions like enter_long() or exit_short()
+# This is a backtesting example of Exponential Moving Average cross strategy.
+# There is a 1.5 ATR stop loss level and a 1 ATR take profit level for every position.
+# Commission rate is 0.1000%.
+# Margin usage is allowed up to 5 times the main capital.
+from datetime import datetime, timedelta
+from testwise import Testwise
+import requests
+from legitindicators import ema, atr
+
+# In this example, daily BTCUSDT kline data is used from binance
+# Let's say you want to backtest your strategy for about 450 days.
+# It would be useful to add some extra days to the specified time interval
+# for the indicators to work properly.
+# (For example 10 days of EMA won't be calculated for the first 9 days of time range)
+# In this example I add 40 extra days. This value can be determined by assigning the TRIM variable
+TRIM = 40
+BINANCE_URL = "https://api.binance.com/api/v3/klines"
+SYMBOL = "BTCUSDT"
+INTERVAL = "1d"
+
+# These are the initial paramters for backtester.
+# You can find a more detailed explanation where the Testwise definition is given below.
+COMMISSION = 0.001
+DYNAMIC_POSITIONING = True
+MARGIN_FACTOR = 5
+LIMIT_FACTOR = 1
+RISK_FACTOR = 1.5
+
+
+def main():
+ # Here we define the start time and end time of backtesting.
+ # Notice usage of TRIM variable to start to backtest a few days earlier for proper indicator use.
+ start_time = datetime(2020, 6, 1, 0, 0, 0)
+ start_time = start_time - timedelta(days=TRIM)
+
+ end_time = datetime(2021, 9, 1, 0, 0, 0)
+
+ # In this example, timestamps are used. (Because binance accept timestamp)
+ start_time_ts = int(datetime.timestamp(start_time) * 1000)
+ end_time_ts = int(datetime.timestamp(end_time) * 1000)
+
+ backtest(start_time_ts, end_time_ts)
+
+
+def backtest(start_time, end_time):
+ # Getting OHLC data
+ # Example binance kline response
+ # [
+ # [
+ # 1499040000000, // Open time
+ # "0.01634790", // Open
+ # "0.80000000", // High
+ # "0.01575800", // Low
+ # "0.01577100", // Close
+ # "148976.11427815", // Volume
+ # 1499644799999, // Close time
+ # "2434.19055334", // Quote asset volume
+ # 308, // Number of trades
+ # "1756.87402397", // Taker buy base asset volume
+ # "28.46694368", // Taker buy quote asset volume
+ # "17928899.62484339" // Ignore.
+ # ]
+ # ]
+ params = {"symbol": SYMBOL, "interval": INTERVAL, "startTime": start_time, "endTime": end_time}
+ data = get_data(params)
+ opn, high, low, close = get_ohlc(data)
+
+ # Again for proper indicator usage number of bars to work on is defined as lookback
+ lookback = len(data) - TRIM
+
+ # These are simply trimmed OHLC data
+ data = data[-lookback:]
+ # Here, a list of close prices kept under different naming conventions than other OHL data
+ # That is because I will use this close data as a parameter
+ # for Exponential Moving Average indicator and then trim the list of EMA values afterward.
+ close_tmp = close[-lookback:]
+ opn = opn[-lookback:]
+ high = high[-lookback:]
+ low = low[-lookback:]
+
+ # Here is the calculation of ATR values historically. I use legitindicators library.
+ atr_input = []
+ for i, _ in enumerate(data):
+ ohlc = [opn[i], high[i], low[i], close_tmp[i]]
+ atr_input.append(ohlc)
+ atrng = atr(atr_input, 14)
+
+ # Backtesting operation starts here.
+ # Following two for loops will check two EMA crosses in the range of 10 to 30
+ for ema_length1 in range(10, 11):
+ for ema_length2 in range(ema_length1 + 1, 30):
+ # When the dynamic_positioning is set to True,
+ # the backtester will work as if the margin usage is available for use.
+ # margin_factor indicates the margin ratio. (In this example, it is 5 times the main capital)
+ # limit_factor is an ATR based take profit level. (it is 1 ATR from the position price)
+ # risk_factor is an ATR based stop loss level. (it is 1.5 ATR from the position price)
+ twise = Testwise(
+ commission=COMMISSION,
+ dynamic_positioning=DYNAMIC_POSITIONING,
+ margin_factor=MARGIN_FACTOR,
+ limit_factor=LIMIT_FACTOR,
+ risk_factor=RISK_FACTOR
+ )
+
+ # Here, two EMA indicators are defined. I use legitindicators library.
+ ema_first = ema(close, ema_length1)
+ ema_second = ema(close, ema_length2)
+ # List of indicator values trimmed accordingly
+ ema_first = ema_first[-lookback:]
+ ema_second = ema_second[-lookback:]
+
+ # Notice that at this point:
+ # open, high, low, close, ema_first and ema_second lists are all trimmed
+ # and all have the same length
+ # Ready for testing
+
+ # Start walking on the data taken from the binance.
+ for i, _ in enumerate(data):
+ # Exclude first price data
+ if i > 1 and i < len(data) - 1:
+ # Here, data[n][0] is the open time of price data
+ # date_open is kept for use if there will be a pose to be opened the next day
+ # date_close is kept for use if the current open position is closed in this iteration
+ date_open = datetime.fromtimestamp(int(data[i+1][0] / 1000)).strftime("%Y-%m-%d %H")
+ date_close = datetime.fromtimestamp(int(data[i][0] / 1000)).strftime("%Y-%m-%d %H")
+
+ # Position exits
+ # On every iteration, position exits checked firstly
+ # Below, if the current position is long (1 means long) and
+ # the ema_first crosses below the ema_second, position exit function triggered
+ if twise.pos == 1 and (ema_first[i] < ema_second[i]):
+ # exit_long function takes closing date,
+ # closing price as open price of next day opn[i + 1],
+ # and amount to close the position.
+ # This amount already kept in twise.current_open_pos["qty"].
+ # This value is set when opening the positions
+ twise.exit_long(date_close, opn[i + 1], twise.current_open_pos["qty"])
+
+ # Closing short position(-1 means short)
+ if twise.pos == -1 and (ema_first[i] > ema_second[i]):
+ twise.exit_short(date_close, opn[i + 1], twise.current_open_pos["qty"])
+
+ # The following if condition simulates price movements inside the bar.
+ # This is crucial if you want to add take profit and stop loss logic to the backtester.
+ # This pine script broker emulator documentation will explain this condition more clearly:
+ # https://www.tradingview.com/pine-script-docs/en/v5/concepts/Strategies.html?highlight=strategy#broker-emulator
+ if abs(high[i] - opn[i]) < abs(low[i] - opn[i]):
+ # Simply, If the bar’s high is closer to bar’s open than the bar’s low,
+ # bar movement will be like:
+ # open - high - low - close
+
+ # In this movement, take profit operation will be checked before stop loss.
+ # This is because, it is assumed that the price will go up first.
+ # For example, if both take profit and stop loss prices are exceeded,
+ # it is assumed that first, take profit is taken, than stop loss price is reached.
+
+ # if current position is long, here is take profit logic:
+ # if current position is long and high is
+ # higher than take proift price (twise.current_open_pos["tp"])
+ # and take profit is not taken (twise.current_open_pos["tptaken"] is False)
+ if twise.pos == 1 and high[i] > twise.current_open_pos["tp"] and twise.current_open_pos["tptaken"] is False:
+ # Stop loss price will be set to break even with break_even() function
+ twise.break_even()
+ # Take profit operation is simply a partially position closing operation.
+ # Here, half of the position is closed. (twise.current_open_pos["qty"] / 2)
+ twise.exit_long(date_close, twise.current_open_pos["tp"], twise.current_open_pos["qty"] / 2, True)
+
+ # if current position is long, here is stop loss logic:
+ # if current position is long and low is
+ # lower than stop loss price (twise.current_open_pos["sl"])
+ if twise.pos == 1 and low[i] < twise.current_open_pos["sl"]:
+ twise.exit_long(date_close, twise.current_open_pos["sl"], twise.current_open_pos["qty"])
+
+ # if current position is short, here is take profit logic:
+ if twise.pos == -1 and high[i] > twise.current_open_pos["sl"]:
+ twise.exit_short(date_close, twise.current_open_pos["sl"], twise.current_open_pos["qty"])
+
+ # if current position is short, here is stop loss logic:
+ if twise.pos == -1 and low[i] < twise.current_open_pos["tp"] and twise.current_open_pos["tptaken"] is False:
+ twise.break_even()
+ twise.exit_short(date_close, twise.current_open_pos["tp"], twise.current_open_pos["qty"] / 2, True)
+ else:
+ # If the bar’s low is closer to bar’s open than the bar’s high,
+ # bar movement will be like:
+ # open - low - high - close
+
+ # In this movement, stop loss operation will be checked before take profit.
+ # This is because, it is assumed that the price will go down firstly.
+ # For example, if both take profit and stop loss prices are exceeded,
+ # it is assumed that first, stop loss is executed,
+ # then take profit will never be reached because
+ # if the position is fully closed with exit_long,
+ # twise.pos value will be 0 (which means there is no open position).
+
+ # if the current position is long, here is stop loss logic:
+ if twise.pos == 1 and low[i] < twise.current_open_pos["sl"]:
+ twise.exit_long(date_close, twise.current_open_pos["sl"], twise.current_open_pos["qty"])
+
+ # if current position is long, here is take profit logic:
+ if twise.pos == 1 and high[i] > twise.current_open_pos["tp"] and twise.current_open_pos["tptaken"] is False:
+ twise.break_even()
+ twise.exit_long(date_close, twise.current_open_pos["tp"], twise.current_open_pos["qty"] / 2, True)
+
+ # if current position is short, here is take profit logic:
+ if twise.pos == -1 and low[i] < twise.current_open_pos["tp"] and twise.current_open_pos["tptaken"] is False:
+ twise.break_even()
+ twise.exit_short(date_close, twise.current_open_pos["tp"], twise.current_open_pos["qty"] / 2, True)
+
+ # if current position is short, here is stop loss logic:
+ if twise.pos == -1 and high[i] > twise.current_open_pos["sl"]:
+ twise.exit_short(date_close, twise.current_open_pos["sl"], twise.current_open_pos["qty"])
+
+ # Opening long position
+ # If there is no long positions open
+ if twise.pos != 1:
+ # If ema_first crosses over ema_second
+ if ema_first[i] > ema_second[i]:
+ # You can manually set the amount to open position.
+ # But there will be a calculation overhead.
+ # Testwise has a built-in share calculation funciton
+ # In tihs function, share is calculated as:
+ # share = (equity * position risk) / (atr * risk factor)
+ share = twise.calculate_share(atrng[i], custom_position_risk=0.02)
+ # Opening long position with opening date (date_open),
+ # opening price of next day (opn[i + 1]),
+ # amount to buy, and current atr value to define take profit and stop loss prices
+ twise.entry_long(date_open, opn[i + 1], share, atrng[i])
+
+ if twise.pos != -1:
+ if ema_first[i] < ema_second[i]:
+ share = twise.calculate_share(atrng[i], custom_position_risk=0.02)
+ # Opening short position with opening date (date_open),
+ # opening price of next day (opn[i + 1]),
+ # amount to buy, and current atr value to define take profit and stop loss prices
+ twise.entry_short(date_open, opn[i + 1], share, atrng[i])
+ # get_result() function will give you the backtest results
+ print(twise.get_result())
+
+
+def get_data(params):
+ r = requests.get(url=BINANCE_URL, params=params)
+ data = r.json()
+ return data
+
+
+def get_ohlc(data):
+ opn = [float(o[1]) for o in data]
+ close = [float(d[4]) for d in data]
+ high = [float(h[2]) for h in data]
+ low = [float(lo[3]) for lo in data]
+
+ return opn, high, low, close
+
+
+if __name__ == "__main__":
+ main()
+```
+
+```python
+Example backtest result:
+{
+ 'net_profit': 30557.012567638478,
+ 'net_profit_percent': 30.557012567638477,
+ 'gross_profit': 69163.31181062985,
+ 'gross_loss': 36783.34343506002,
+ 'max_drawdown': -13265.365111723615,
+ 'max_drawdown_rate': 2.3035183962356918,
+ 'win_rate': 53.48837209302326,
+ 'risk_reward_ratio': 1.6350338129618904,
+ 'profit_factor': 1.880288884906174,
+ 'ehlers_ratio': 0.1311829454585705,
+ 'return_on_capital': 0.26978249297565415,
+ 'max_capital_required': 113265.36511172361,
+ 'total_trades': 43,
+ 'pearsonsr': 0.8022110890986095,
+ 'number_of_winning_trades': 23,
+ 'number_of_losing_trades': 20,
+ 'largest_winning_trade': ('2021-01-23 03', 34417.71907928039),
+ 'largest_losing_trade': ('2020-09-21 03', -4627.351985682239)}
+```
+
+## Important note:
+Do not rely on a single test result.
+At least do walkforward test with a few iterations.
+
+## Installation
+
+Run the following to install:
+
+```python
+pip install testwise
+```
+
+
+
+
+%package help
+Summary: Development documents and examples for testwise
+Provides: python3-testwise-doc
+%description help
+# Testwise
+
+![Publish Python 🐍 distributions 📦 to PyPI and TestPyPI](https://github.com/aticio/legitindicators/workflows/Publish%20Python%20%F0%9F%90%8D%20distributions%20%F0%9F%93%A6%20to%20PyPI%20and%20TestPyPI/badge.svg)
+
+A backtester (backtest helper) for testing my trading strategies.
+
+It requires a lot of manual processing and coding. Difficult to comprehend. I tried to explain the use of the library with examples as best I could. But writing such automation is quite complex.
+There may still be errors. It is pretty difficult to check but I'm trying to improve the usage.
+
+## Example Usage
+```python
+# Testwise is a backtester library that requires some coding knowledge
+# There is no cli or interface.
+# You should directly execute necessary functions like enter_long() or exit_short()
+# This is a backtesting example of Exponential Moving Average cross strategy.
+# There is a 1.5 ATR stop loss level and a 1 ATR take profit level for every position.
+# Commission rate is 0.1000%.
+# Margin usage is allowed up to 5 times the main capital.
+from datetime import datetime, timedelta
+from testwise import Testwise
+import requests
+from legitindicators import ema, atr
+
+# In this example, daily BTCUSDT kline data is used from binance
+# Let's say you want to backtest your strategy for about 450 days.
+# It would be useful to add some extra days to the specified time interval
+# for the indicators to work properly.
+# (For example 10 days of EMA won't be calculated for the first 9 days of time range)
+# In this example I add 40 extra days. This value can be determined by assigning the TRIM variable
+TRIM = 40
+BINANCE_URL = "https://api.binance.com/api/v3/klines"
+SYMBOL = "BTCUSDT"
+INTERVAL = "1d"
+
+# These are the initial paramters for backtester.
+# You can find a more detailed explanation where the Testwise definition is given below.
+COMMISSION = 0.001
+DYNAMIC_POSITIONING = True
+MARGIN_FACTOR = 5
+LIMIT_FACTOR = 1
+RISK_FACTOR = 1.5
+
+
+def main():
+ # Here we define the start time and end time of backtesting.
+ # Notice usage of TRIM variable to start to backtest a few days earlier for proper indicator use.
+ start_time = datetime(2020, 6, 1, 0, 0, 0)
+ start_time = start_time - timedelta(days=TRIM)
+
+ end_time = datetime(2021, 9, 1, 0, 0, 0)
+
+ # In this example, timestamps are used. (Because binance accept timestamp)
+ start_time_ts = int(datetime.timestamp(start_time) * 1000)
+ end_time_ts = int(datetime.timestamp(end_time) * 1000)
+
+ backtest(start_time_ts, end_time_ts)
+
+
+def backtest(start_time, end_time):
+ # Getting OHLC data
+ # Example binance kline response
+ # [
+ # [
+ # 1499040000000, // Open time
+ # "0.01634790", // Open
+ # "0.80000000", // High
+ # "0.01575800", // Low
+ # "0.01577100", // Close
+ # "148976.11427815", // Volume
+ # 1499644799999, // Close time
+ # "2434.19055334", // Quote asset volume
+ # 308, // Number of trades
+ # "1756.87402397", // Taker buy base asset volume
+ # "28.46694368", // Taker buy quote asset volume
+ # "17928899.62484339" // Ignore.
+ # ]
+ # ]
+ params = {"symbol": SYMBOL, "interval": INTERVAL, "startTime": start_time, "endTime": end_time}
+ data = get_data(params)
+ opn, high, low, close = get_ohlc(data)
+
+ # Again for proper indicator usage number of bars to work on is defined as lookback
+ lookback = len(data) - TRIM
+
+ # These are simply trimmed OHLC data
+ data = data[-lookback:]
+ # Here, a list of close prices kept under different naming conventions than other OHL data
+ # That is because I will use this close data as a parameter
+ # for Exponential Moving Average indicator and then trim the list of EMA values afterward.
+ close_tmp = close[-lookback:]
+ opn = opn[-lookback:]
+ high = high[-lookback:]
+ low = low[-lookback:]
+
+ # Here is the calculation of ATR values historically. I use legitindicators library.
+ atr_input = []
+ for i, _ in enumerate(data):
+ ohlc = [opn[i], high[i], low[i], close_tmp[i]]
+ atr_input.append(ohlc)
+ atrng = atr(atr_input, 14)
+
+ # Backtesting operation starts here.
+ # Following two for loops will check two EMA crosses in the range of 10 to 30
+ for ema_length1 in range(10, 11):
+ for ema_length2 in range(ema_length1 + 1, 30):
+ # When the dynamic_positioning is set to True,
+ # the backtester will work as if the margin usage is available for use.
+ # margin_factor indicates the margin ratio. (In this example, it is 5 times the main capital)
+ # limit_factor is an ATR based take profit level. (it is 1 ATR from the position price)
+ # risk_factor is an ATR based stop loss level. (it is 1.5 ATR from the position price)
+ twise = Testwise(
+ commission=COMMISSION,
+ dynamic_positioning=DYNAMIC_POSITIONING,
+ margin_factor=MARGIN_FACTOR,
+ limit_factor=LIMIT_FACTOR,
+ risk_factor=RISK_FACTOR
+ )
+
+ # Here, two EMA indicators are defined. I use legitindicators library.
+ ema_first = ema(close, ema_length1)
+ ema_second = ema(close, ema_length2)
+ # List of indicator values trimmed accordingly
+ ema_first = ema_first[-lookback:]
+ ema_second = ema_second[-lookback:]
+
+ # Notice that at this point:
+ # open, high, low, close, ema_first and ema_second lists are all trimmed
+ # and all have the same length
+ # Ready for testing
+
+ # Start walking on the data taken from the binance.
+ for i, _ in enumerate(data):
+ # Exclude first price data
+ if i > 1 and i < len(data) - 1:
+ # Here, data[n][0] is the open time of price data
+ # date_open is kept for use if there will be a pose to be opened the next day
+ # date_close is kept for use if the current open position is closed in this iteration
+ date_open = datetime.fromtimestamp(int(data[i+1][0] / 1000)).strftime("%Y-%m-%d %H")
+ date_close = datetime.fromtimestamp(int(data[i][0] / 1000)).strftime("%Y-%m-%d %H")
+
+ # Position exits
+ # On every iteration, position exits checked firstly
+ # Below, if the current position is long (1 means long) and
+ # the ema_first crosses below the ema_second, position exit function triggered
+ if twise.pos == 1 and (ema_first[i] < ema_second[i]):
+ # exit_long function takes closing date,
+ # closing price as open price of next day opn[i + 1],
+ # and amount to close the position.
+ # This amount already kept in twise.current_open_pos["qty"].
+ # This value is set when opening the positions
+ twise.exit_long(date_close, opn[i + 1], twise.current_open_pos["qty"])
+
+ # Closing short position(-1 means short)
+ if twise.pos == -1 and (ema_first[i] > ema_second[i]):
+ twise.exit_short(date_close, opn[i + 1], twise.current_open_pos["qty"])
+
+ # The following if condition simulates price movements inside the bar.
+ # This is crucial if you want to add take profit and stop loss logic to the backtester.
+ # This pine script broker emulator documentation will explain this condition more clearly:
+ # https://www.tradingview.com/pine-script-docs/en/v5/concepts/Strategies.html?highlight=strategy#broker-emulator
+ if abs(high[i] - opn[i]) < abs(low[i] - opn[i]):
+ # Simply, If the bar’s high is closer to bar’s open than the bar’s low,
+ # bar movement will be like:
+ # open - high - low - close
+
+ # In this movement, take profit operation will be checked before stop loss.
+ # This is because, it is assumed that the price will go up first.
+ # For example, if both take profit and stop loss prices are exceeded,
+ # it is assumed that first, take profit is taken, than stop loss price is reached.
+
+ # if current position is long, here is take profit logic:
+ # if current position is long and high is
+ # higher than take proift price (twise.current_open_pos["tp"])
+ # and take profit is not taken (twise.current_open_pos["tptaken"] is False)
+ if twise.pos == 1 and high[i] > twise.current_open_pos["tp"] and twise.current_open_pos["tptaken"] is False:
+ # Stop loss price will be set to break even with break_even() function
+ twise.break_even()
+ # Take profit operation is simply a partially position closing operation.
+ # Here, half of the position is closed. (twise.current_open_pos["qty"] / 2)
+ twise.exit_long(date_close, twise.current_open_pos["tp"], twise.current_open_pos["qty"] / 2, True)
+
+ # if current position is long, here is stop loss logic:
+ # if current position is long and low is
+ # lower than stop loss price (twise.current_open_pos["sl"])
+ if twise.pos == 1 and low[i] < twise.current_open_pos["sl"]:
+ twise.exit_long(date_close, twise.current_open_pos["sl"], twise.current_open_pos["qty"])
+
+ # if current position is short, here is take profit logic:
+ if twise.pos == -1 and high[i] > twise.current_open_pos["sl"]:
+ twise.exit_short(date_close, twise.current_open_pos["sl"], twise.current_open_pos["qty"])
+
+ # if current position is short, here is stop loss logic:
+ if twise.pos == -1 and low[i] < twise.current_open_pos["tp"] and twise.current_open_pos["tptaken"] is False:
+ twise.break_even()
+ twise.exit_short(date_close, twise.current_open_pos["tp"], twise.current_open_pos["qty"] / 2, True)
+ else:
+ # If the bar’s low is closer to bar’s open than the bar’s high,
+ # bar movement will be like:
+ # open - low - high - close
+
+ # In this movement, stop loss operation will be checked before take profit.
+ # This is because, it is assumed that the price will go down firstly.
+ # For example, if both take profit and stop loss prices are exceeded,
+ # it is assumed that first, stop loss is executed,
+ # then take profit will never be reached because
+ # if the position is fully closed with exit_long,
+ # twise.pos value will be 0 (which means there is no open position).
+
+ # if the current position is long, here is stop loss logic:
+ if twise.pos == 1 and low[i] < twise.current_open_pos["sl"]:
+ twise.exit_long(date_close, twise.current_open_pos["sl"], twise.current_open_pos["qty"])
+
+ # if current position is long, here is take profit logic:
+ if twise.pos == 1 and high[i] > twise.current_open_pos["tp"] and twise.current_open_pos["tptaken"] is False:
+ twise.break_even()
+ twise.exit_long(date_close, twise.current_open_pos["tp"], twise.current_open_pos["qty"] / 2, True)
+
+ # if current position is short, here is take profit logic:
+ if twise.pos == -1 and low[i] < twise.current_open_pos["tp"] and twise.current_open_pos["tptaken"] is False:
+ twise.break_even()
+ twise.exit_short(date_close, twise.current_open_pos["tp"], twise.current_open_pos["qty"] / 2, True)
+
+ # if current position is short, here is stop loss logic:
+ if twise.pos == -1 and high[i] > twise.current_open_pos["sl"]:
+ twise.exit_short(date_close, twise.current_open_pos["sl"], twise.current_open_pos["qty"])
+
+ # Opening long position
+ # If there is no long positions open
+ if twise.pos != 1:
+ # If ema_first crosses over ema_second
+ if ema_first[i] > ema_second[i]:
+ # You can manually set the amount to open position.
+ # But there will be a calculation overhead.
+ # Testwise has a built-in share calculation funciton
+ # In tihs function, share is calculated as:
+ # share = (equity * position risk) / (atr * risk factor)
+ share = twise.calculate_share(atrng[i], custom_position_risk=0.02)
+ # Opening long position with opening date (date_open),
+ # opening price of next day (opn[i + 1]),
+ # amount to buy, and current atr value to define take profit and stop loss prices
+ twise.entry_long(date_open, opn[i + 1], share, atrng[i])
+
+ if twise.pos != -1:
+ if ema_first[i] < ema_second[i]:
+ share = twise.calculate_share(atrng[i], custom_position_risk=0.02)
+ # Opening short position with opening date (date_open),
+ # opening price of next day (opn[i + 1]),
+ # amount to buy, and current atr value to define take profit and stop loss prices
+ twise.entry_short(date_open, opn[i + 1], share, atrng[i])
+ # get_result() function will give you the backtest results
+ print(twise.get_result())
+
+
+def get_data(params):
+ r = requests.get(url=BINANCE_URL, params=params)
+ data = r.json()
+ return data
+
+
+def get_ohlc(data):
+ opn = [float(o[1]) for o in data]
+ close = [float(d[4]) for d in data]
+ high = [float(h[2]) for h in data]
+ low = [float(lo[3]) for lo in data]
+
+ return opn, high, low, close
+
+
+if __name__ == "__main__":
+ main()
+```
+
+```python
+Example backtest result:
+{
+ 'net_profit': 30557.012567638478,
+ 'net_profit_percent': 30.557012567638477,
+ 'gross_profit': 69163.31181062985,
+ 'gross_loss': 36783.34343506002,
+ 'max_drawdown': -13265.365111723615,
+ 'max_drawdown_rate': 2.3035183962356918,
+ 'win_rate': 53.48837209302326,
+ 'risk_reward_ratio': 1.6350338129618904,
+ 'profit_factor': 1.880288884906174,
+ 'ehlers_ratio': 0.1311829454585705,
+ 'return_on_capital': 0.26978249297565415,
+ 'max_capital_required': 113265.36511172361,
+ 'total_trades': 43,
+ 'pearsonsr': 0.8022110890986095,
+ 'number_of_winning_trades': 23,
+ 'number_of_losing_trades': 20,
+ 'largest_winning_trade': ('2021-01-23 03', 34417.71907928039),
+ 'largest_losing_trade': ('2020-09-21 03', -4627.351985682239)}
+```
+
+## Important note:
+Do not rely on a single test result.
+At least do walkforward test with a few iterations.
+
+## Installation
+
+Run the following to install:
+
+```python
+pip install testwise
+```
+
+
+
+
+%prep
+%autosetup -n testwise-0.0.63
+
+%build
+%py3_build
+
+%install
+%py3_install
+install -d -m755 %{buildroot}/%{_pkgdocdir}
+if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
+if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
+if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
+if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
+pushd %{buildroot}
+if [ -d usr/lib ]; then
+ find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/lib64 ]; then
+ find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/bin ]; then
+ find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/sbin ]; then
+ find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+touch doclist.lst
+if [ -d usr/share/man ]; then
+ find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
+fi
+popd
+mv %{buildroot}/filelist.lst .
+mv %{buildroot}/doclist.lst .
+
+%files -n python3-testwise -f filelist.lst
+%dir %{python3_sitelib}/*
+
+%files help -f doclist.lst
+%{_docdir}/*
+
+%changelog
+* Thu May 18 2023 Python_Bot <Python_Bot@openeuler.org> - 0.0.63-1
+- Package Spec generated
diff --git a/sources b/sources
new file mode 100644
index 0000000..0962ff7
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+59277a5dc17f0a52c3a31c4f55adb887 testwise-0.0.63.tar.gz