diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..2bdc722
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..af36f7e
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..f26a588
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/python-tradingview-ta.iml b/.idea/python-tradingview-ta.iml
new file mode 100644
index 0000000..269e1a8
--- /dev/null
+++ b/.idea/python-tradingview-ta.iml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test.py b/test.py
index b5b513c..851db77 100644
--- a/test.py
+++ b/test.py
@@ -1,5 +1,5 @@
from colorama import Fore, Style
-from tradingview_ta import TA_Handler, Interval, get_multiple_analysis
+from tradingview_ta import TA_Handler, Interval, get_multiple_analysis, get_multiple_analysis_with_multiple_intervals
import tradingview_ta, requests, argparse
arg_parser = argparse.ArgumentParser()
@@ -152,6 +152,29 @@
except Exception as e:
print("{}#6{} Get indicators test {}failed{}. An exception occured: {}".format(Fore.BLUE, Style.RESET_ALL, Fore.RED, Style.RESET_ALL, e))
+try:
+ analysis = get_multiple_analysis_with_multiple_intervals(screener="crypto",
+ intervals=[Interval.INTERVAL_1_DAY,
+ Interval.INTERVAL_1_HOUR],
+ symbols=["BINANCE:SOLUSDT", "BINANCE:BTCUSDT"])
+ for key, value in analysis.items():
+ print(
+ "{}#7{} Please compare with {}https://www.tradingview.com/symbols/{}/technicals/{}. (Switch to 1 hour tab)".format(
+ Fore.BLUE, Style.RESET_ALL, Fore.LIGHTMAGENTA_EX, key, Style.RESET_ALL))
+ print("{}#7{} (Summary) Rec: {}, Sell: {}, Neutral: {}, Buy: {}".format(Fore.BLUE, Style.RESET_ALL,
+ value.summary["RECOMMENDATION"],
+ value.summary["SELL"],
+ value.summary["NEUTRAL"],
+ value.summary["BUY"]))
+ if input("{}#7{} Are the results the same? (Y/N) ".format(Fore.BLUE, Style.RESET_ALL)).lower() == "y":
+ print("{}#7{} Multiple analysis test {}success{}.".format(Fore.BLUE, Style.RESET_ALL, Fore.GREEN,
+ Style.RESET_ALL))
+ success += 1
+ else:
+ print("{}#7{} Multiple analysis test {}failed{}".format(Fore.BLUE, Style.RESET_ALL, Fore.RED, Style.RESET_ALL))
+except Exception as e:
+ print("{}#7{} Multiple analysis test {}failed{}. An exception occured: {}".format(Fore.BLUE, Style.RESET_ALL,
+ Fore.RED, Style.RESET_ALL, e))
print("------------------------------------------------")
print("Test finished. Result: {}{}/{}{}.".format(Fore.LIGHTWHITE_EX, success, COUNT, Style.RESET_ALL))
diff --git a/tradingview_ta/__init__.py b/tradingview_ta/__init__.py
index 8ce1275..0397793 100644
--- a/tradingview_ta/__init__.py
+++ b/tradingview_ta/__init__.py
@@ -1,2 +1,3 @@
-from .main import TA_Handler, TradingView, Analysis, Interval, Exchange, get_multiple_analysis, __version__
+from .main import TA_Handler, TradingView, Analysis, Interval, Exchange, get_multiple_analysis, \
+ get_multiple_analysis_with_multiple_intervals, __version__
from .technicals import Recommendation, Compute
diff --git a/tradingview_ta/main.py b/tradingview_ta/main.py
index 0c8638b..029e39f 100644
--- a/tradingview_ta/main.py
+++ b/tradingview_ta/main.py
@@ -2,10 +2,12 @@
# Author: deathlyface (https://github.com/deathlyface)
# License: MIT
-import requests
-import json
import datetime
+import json
import warnings
+
+import requests
+
from .technicals import Compute
__version__ = "3.3.0"
@@ -43,8 +45,21 @@ class Exchange:
class TradingView:
# Note: Please DO NOT modify the order or DELETE existing indicators, it will break the technical analysis. You may APPEND custom indicator to the END of the list.
- indicators = ["Recommend.Other", "Recommend.All", "Recommend.MA", "RSI", "RSI[1]", "Stoch.K", "Stoch.D", "Stoch.K[1]", "Stoch.D[1]", "CCI20", "CCI20[1]", "ADX", "ADX+DI", "ADX-DI", "ADX+DI[1]", "ADX-DI[1]", "AO", "AO[1]", "Mom", "Mom[1]", "MACD.macd", "MACD.signal", "Rec.Stoch.RSI", "Stoch.RSI.K", "Rec.WR", "W.R", "Rec.BBPower", "BBPower", "Rec.UO", "UO", "close", "EMA5", "SMA5", "EMA10", "SMA10", "EMA20", "SMA20", "EMA30", "SMA30", "EMA50", "SMA50", "EMA100", "SMA100", "EMA200", "SMA200", "Rec.Ichimoku", "Ichimoku.BLine", "Rec.VWMA", "VWMA", "Rec.HullMA9", "HullMA9", "Pivot.M.Classic.S3", "Pivot.M.Classic.S2", "Pivot.M.Classic.S1", "Pivot.M.Classic.Middle", "Pivot.M.Classic.R1",
- "Pivot.M.Classic.R2", "Pivot.M.Classic.R3", "Pivot.M.Fibonacci.S3", "Pivot.M.Fibonacci.S2", "Pivot.M.Fibonacci.S1", "Pivot.M.Fibonacci.Middle", "Pivot.M.Fibonacci.R1", "Pivot.M.Fibonacci.R2", "Pivot.M.Fibonacci.R3", "Pivot.M.Camarilla.S3", "Pivot.M.Camarilla.S2", "Pivot.M.Camarilla.S1", "Pivot.M.Camarilla.Middle", "Pivot.M.Camarilla.R1", "Pivot.M.Camarilla.R2", "Pivot.M.Camarilla.R3", "Pivot.M.Woodie.S3", "Pivot.M.Woodie.S2", "Pivot.M.Woodie.S1", "Pivot.M.Woodie.Middle", "Pivot.M.Woodie.R1", "Pivot.M.Woodie.R2", "Pivot.M.Woodie.R3", "Pivot.M.Demark.S1", "Pivot.M.Demark.Middle", "Pivot.M.Demark.R1", "open", "P.SAR", "BB.lower", "BB.upper", "AO[2]", "volume", "change", "low", "high"]
+ indicators = ["Recommend.Other", "Recommend.All", "Recommend.MA", "RSI", "RSI[1]", "Stoch.K", "Stoch.D",
+ "Stoch.K[1]", "Stoch.D[1]", "CCI20", "CCI20[1]", "ADX", "ADX+DI", "ADX-DI", "ADX+DI[1]", "ADX-DI[1]",
+ "AO", "AO[1]", "Mom", "Mom[1]", "MACD.macd", "MACD.signal", "Rec.Stoch.RSI", "Stoch.RSI.K", "Rec.WR",
+ "W.R", "Rec.BBPower", "BBPower", "Rec.UO", "UO", "close", "EMA5", "SMA5", "EMA10", "SMA10", "EMA20",
+ "SMA20", "EMA30", "SMA30", "EMA50", "SMA50", "EMA100", "SMA100", "EMA200", "SMA200", "Rec.Ichimoku",
+ "Ichimoku.BLine", "Rec.VWMA", "VWMA", "Rec.HullMA9", "HullMA9", "Pivot.M.Classic.S3",
+ "Pivot.M.Classic.S2", "Pivot.M.Classic.S1", "Pivot.M.Classic.Middle", "Pivot.M.Classic.R1",
+ "Pivot.M.Classic.R2", "Pivot.M.Classic.R3", "Pivot.M.Fibonacci.S3", "Pivot.M.Fibonacci.S2",
+ "Pivot.M.Fibonacci.S1", "Pivot.M.Fibonacci.Middle", "Pivot.M.Fibonacci.R1", "Pivot.M.Fibonacci.R2",
+ "Pivot.M.Fibonacci.R3", "Pivot.M.Camarilla.S3", "Pivot.M.Camarilla.S2", "Pivot.M.Camarilla.S1",
+ "Pivot.M.Camarilla.Middle", "Pivot.M.Camarilla.R1", "Pivot.M.Camarilla.R2", "Pivot.M.Camarilla.R3",
+ "Pivot.M.Woodie.S3", "Pivot.M.Woodie.S2", "Pivot.M.Woodie.S1", "Pivot.M.Woodie.Middle",
+ "Pivot.M.Woodie.R1", "Pivot.M.Woodie.R2", "Pivot.M.Woodie.R3", "Pivot.M.Demark.S1",
+ "Pivot.M.Demark.Middle", "Pivot.M.Demark.R1", "open", "P.SAR", "BB.lower", "BB.upper", "AO[2]",
+ "volume", "change", "low", "high"]
scan_url = "https://scanner.tradingview.com/"
@@ -97,6 +112,75 @@ def data(symbols, interval, indicators):
return data_json
+ def data_multi_intervals(symbols, intervals, indicators):
+ """Format TradingView's Scanner Post Data
+
+ Args:
+ symbols (list): List of EXCHANGE:SYMBOL (ex: ["NASDAQ:AAPL"] or ["BINANCE:BTCUSDT"])
+ interval (list): Time Interval (ex: 1m, 5m, 15m, 1h, 4h, 1d, 1W, 1M)
+
+ Returns:
+ string: JSON object as a string.
+ """
+ output_intervals = []
+ for interval in intervals:
+ if interval == "1m":
+ # 1 Minute
+ data_interval = "|1"
+ output_intervals.append(data_interval)
+ elif interval == "5m":
+ # 5 Minutes
+ data_interval = "|5"
+ output_intervals.append(data_interval)
+
+ elif interval == "15m":
+ # 15 Minutes
+ data_interval = "|15"
+ output_intervals.append(data_interval)
+
+ elif interval == "30m":
+ # 30 Minutes
+ data_interval = "|30"
+ output_intervals.append(data_interval)
+
+ elif interval == "1h":
+ # 1 Hour
+ data_interval = "|60"
+ output_intervals.append(data_interval)
+
+ elif interval == "2h":
+ # 2 Hours
+ data_interval = "|120"
+ output_intervals.append(data_interval)
+
+ elif interval == "4h":
+ # 4 Hour
+ data_interval = "|240"
+ output_intervals.append(data_interval)
+
+ elif interval == "1W":
+ # 1 Week
+ data_interval = "|1W"
+ output_intervals.append(data_interval)
+
+ elif interval == "1M":
+ # 1 Month
+ data_interval = "|1M"
+ output_intervals.append(data_interval)
+
+ else:
+ if interval != '1d':
+ warnings.warn(
+ "Interval is empty or not valid, defaulting to 1 day.")
+ # Default, 1 Day
+ data_interval = ""
+ output_intervals.append(data_interval)
+
+ data_json = {"symbols": {"tickers": [symbol.upper() for symbol in symbols], "query": {
+ "types": []}}, "columns": [i + j for j in output_intervals for i in indicators]}
+
+ return data_json
+
def search(text, type=None):
"""Search for assets on TradingView. Returns the symbol, exchange, type, and description of the asset.
@@ -228,11 +312,14 @@ def calculate(indicators, indicators_key, screener, symbol, exchange, interval):
analysis.indicators = analysis.indicators.copy()
analysis.oscillators = {"RECOMMENDATION": recommend_oscillators,
- "BUY": oscillators_counter["BUY"], "SELL": oscillators_counter["SELL"], "NEUTRAL": oscillators_counter["NEUTRAL"], "COMPUTE": computed_oscillators}
+ "BUY": oscillators_counter["BUY"], "SELL": oscillators_counter["SELL"],
+ "NEUTRAL": oscillators_counter["NEUTRAL"], "COMPUTE": computed_oscillators}
analysis.moving_averages = {"RECOMMENDATION": recommend_moving_averages,
- "BUY": ma_counter["BUY"], "SELL": ma_counter["SELL"], "NEUTRAL": ma_counter["NEUTRAL"], "COMPUTE": computed_ma}
+ "BUY": ma_counter["BUY"], "SELL": ma_counter["SELL"], "NEUTRAL": ma_counter["NEUTRAL"],
+ "COMPUTE": computed_ma}
analysis.summary = {"RECOMMENDATION": recommend_summary, "BUY": oscillators_counter["BUY"] + ma_counter["BUY"],
- "SELL": oscillators_counter["SELL"] + ma_counter["SELL"], "NEUTRAL": oscillators_counter["NEUTRAL"] + ma_counter["NEUTRAL"]}
+ "SELL": oscillators_counter["SELL"] + ma_counter["SELL"],
+ "NEUTRAL": oscillators_counter["NEUTRAL"] + ma_counter["NEUTRAL"]}
return analysis
@@ -355,8 +442,9 @@ def get_indicators(self, indicators=[]):
# Return False if can't get data
if response.status_code != 200:
- raise Exception("Can't access TradingView's API. HTTP status code: {}. Check for invalid symbol, exchange, or indicators.".format(
- response.status_code))
+ raise Exception(
+ "Can't access TradingView's API. HTTP status code: {}. Check for invalid symbol, exchange, or indicators.".format(
+ response.status_code))
result = json.loads(response.text)["data"]
if result != []:
@@ -384,7 +472,8 @@ def get_analysis(self):
Analysis: Contains information about the analysis.
"""
- return calculate(indicators=self.get_indicators(), indicators_key=self.indicators, screener=self.screener, symbol=self.symbol, exchange=self.exchange, interval=self.interval)
+ return calculate(indicators=self.get_indicators(), indicators_key=self.indicators, screener=self.screener,
+ symbol=self.symbol, exchange=self.exchange, interval=self.interval)
def get_multiple_analysis(screener, interval, symbols, additional_indicators=[], timeout=None, proxies=None):
@@ -430,8 +519,9 @@ def get_multiple_analysis(screener, interval, symbols, additional_indicators=[],
for x in range(len(analysis["d"])):
indicators[indicators_key[x]] = analysis["d"][x]
- final[analysis["s"]] = calculate(indicators=indicators, indicators_key=indicators_key, screener=screener, symbol=analysis["s"].split(
- ":")[1], exchange=analysis["s"].split(":")[0], interval=interval)
+ final[analysis["s"]] = calculate(indicators=indicators, indicators_key=indicators_key, screener=screener,
+ symbol=analysis["s"].split(
+ ":")[1], exchange=analysis["s"].split(":")[0], interval=interval)
for symbol in symbols:
# Add None if there is no analysis for symbol
@@ -439,3 +529,110 @@ def get_multiple_analysis(screener, interval, symbols, additional_indicators=[],
final[symbol.upper()] = None
return final
+
+
+def get_multiple_analysis_with_multiple_intervals(screener, intervals, symbols, additional_indicators=[], timeout=None,
+ proxies=None):
+ """Retrieve multiple technical analysis at once. Note: You can't mix different screener and interval
+
+ Args:
+ screener (str, required): Screener (see documentation and tradingview's site).
+ interval (str, optional): See the interval class and the documentation. Defaults to 1 day.
+ symbols (list, required): List of exchange and ticker symbol separated by a colon. Example: ["NASDAQ:TSLA", "NYSE:DOCN"] or ["BINANCE:BTCUSDT", "BITSTAMP:ETHUSD"].
+ additional_indicators (list, optional): List of additional indicators to be requested from TradingView, see valid indicators on https://pastebin.com/1DjWv2Hd.
+ timeout (float, optional): Timeout for requests (in seconds). Defaults to None.
+ proxies (dict, optional): Proxies to be used for requests. Defaults to None (disabled).
+
+ Returns:
+ dict: dictionary with a format of {"EXCHANGE:SYMBOL": Analysis}.
+ """
+ if not screener or not isinstance(screener, str):
+ raise Exception("Screener is empty or not valid.")
+ if not symbols or not isinstance(symbols, list):
+ raise Exception("Symbols is empty or not valid.")
+ for symbol in symbols:
+ if len(symbol.split(":")) != 2 or "" in symbol.split(":"):
+ raise Exception(
+ "One or more symbols are invalid. Symbols should be a list of exchange and ticker symbol separated by a colon. Example: [\"NASDAQ:TSLA\", \"NYSE:DOCN\"] or [\"BINANCE:BTCUSDT\", \"BITSTAMP:ETHUSD\"].")
+
+ indicators_key = TradingView.indicators.copy()
+
+ if additional_indicators:
+ indicators_key += additional_indicators
+
+ data = TradingView.data_multi_intervals(symbols, intervals, indicators_key)
+ indicators_key = data["columns"]
+ scan_url = f"{TradingView.scan_url}{screener.lower()}/scan"
+ headers = {"User-Agent": "tradingview_ta/{}".format(__version__)}
+ response = requests.post(scan_url, json=data, headers=headers, timeout=timeout, proxies=proxies)
+
+ result = json.loads(response.text)["data"]
+ final = {}
+
+ for analysis in result:
+ # Convert list to dict
+ indicators = {indicators_key[x]: analysis["d"][x] for x in range(len(analysis["d"]))}
+
+ final[analysis["s"]] = calculate(indicators=indicators, indicators_key=indicators_key, screener=screener,
+ symbol=analysis["s"].split(":")[1], exchange=analysis["s"].split(":")[0],
+ interval=intervals)
+
+ for symbol in symbols:
+ # Add None if there is no analysis for symbol
+ if symbol.upper() not in final:
+ final[symbol.upper()] = None
+
+ combain_dict = {}
+ for i in symbols:
+ if Interval.INTERVAL_1_MINUTE in intervals:
+ dict_1m = {key.split("|")[0]: value for key, value in final[i.upper()].indicators.items() if
+ key.endswith('|1')}
+ combain_dict[Interval.INTERVAL_1_MINUTE] = dict_1m
+
+ if Interval.INTERVAL_5_MINUTES in intervals:
+ dict_5m = {key.split("|")[0]: value for key, value in final[i.upper()].indicators.items() if
+ key.endswith('|5')}
+ combain_dict[Interval.INTERVAL_5_MINUTES] = dict_5m
+
+ if Interval.INTERVAL_15_MINUTES in intervals:
+ dict_15m = {key.split("|")[0]: value for key, value in final[i.upper()].indicators.items() if
+ key.endswith('|15')}
+ combain_dict[Interval.INTERVAL_15_MINUTES] = dict_15m
+
+ if Interval.INTERVAL_30_MINUTES in intervals:
+ dict_30m = {key.split("|")[0]: value for key, value in final[i.upper()].indicators.items() if
+ key.endswith('|30')}
+ combain_dict[Interval.INTERVAL_30_MINUTES] = dict_30m
+
+ if Interval.INTERVAL_1_HOUR in intervals:
+ dict_1h = {key.split("|")[0]: value for key, value in final[i.upper()].indicators.items() if
+ key.endswith('|60')}
+ combain_dict[Interval.INTERVAL_1_HOUR] = dict_1h
+
+ if Interval.INTERVAL_2_HOURS in intervals:
+ dict_2h = {key.split("|")[0]: value for key, value in final[i.upper()].indicators.items() if
+ key.endswith('|120')}
+ combain_dict[Interval.INTERVAL_2_HOURS] = dict_2h
+
+ if Interval.INTERVAL_4_HOURS in intervals:
+ dict_4h = {key.split("|")[0]: value for key, value in final[i.upper()].indicators.items() if
+ key.endswith('|240')}
+ combain_dict[Interval.INTERVAL_4_HOURS] = dict_4h
+
+ if Interval.INTERVAL_1_WEEK in intervals:
+ dict_1W = {key.split("|")[0]: value for key, value in final[i.upper()].indicators.items() if
+ key.endswith('|1W')}
+ combain_dict[Interval.INTERVAL_1_WEEK] = dict_1W
+
+ if Interval.INTERVAL_1_MONTH in intervals:
+ dict_1M = {key.split("|")[0]: value for key, value in final[i.upper()].indicators.items() if
+ key.endswith('|1M')}
+ combain_dict[Interval.INTERVAL_1_MONTH] = dict_1M
+
+ if Interval.INTERVAL_1_DAY in intervals:
+ dict_1D = {key.split("|")[0]: value for key, value in final[i.upper()].indicators.items() if '|' not in key}
+ combain_dict[Interval.INTERVAL_1_DAY] = dict_1D
+
+ final[i.upper()].indicators = combain_dict
+
+ return final