├── .gitignore ├── .pypirc ├── LICENSE ├── README.md ├── pytvc ├── __init__.py ├── cli.py ├── core.py ├── html │ └── template.html └── json │ ├── params.json │ └── tv_indicators.json ├── requirements.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /.pypirc: -------------------------------------------------------------------------------- 1 | [distutils] 2 | index-servers = 3 | pypi 4 | pypitest 5 | 6 | [pypi] 7 | repository = https://pypi.python.org/pypi 8 | username = your_username 9 | password = your_password 10 | 11 | [pypitest] 12 | repository = https://testpypi.python.org/pypi 13 | username = havocesp 14 | password = your_password -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyTVC: Python TradingView Chart 2 | 3 | - **Author**: Daniel J. Umpierrez 4 | - **License**: UNLICENSE 5 | 6 | ## Description 7 | 8 | TradingView chart launcher from CLI using the Chrome (or similar) "desktop like" app launch feature 9 | 10 | Some features: 11 | - Set as many technical indicators as you need. 12 | - Watchlist 24h volume filtering. 13 | - Watchlist base market filtering. 14 | 15 | ## Installation 16 | 17 | ### From GitHub repo using `git` 18 | 19 | ```sh 20 | git clone https://github.com/havocesp/pytvc 21 | cd pytvc 22 | pip install -r requirements.txt 23 | pip install -r https://raw.githubusercontent.com/havocesp/pytvc/master/requirements.txt 24 | pip install . 25 | ``` 26 | 27 | ### From GitHub repo using `pip` 28 | 29 | ```sh 30 | pip install git+https://github.com/havocesp/pytvc 31 | ``` 32 | 33 | ## Usage 34 | 35 | ### From CLI `pytvc` command 36 | 37 | The following command will retrieve and display the last hour "gainers" currencies listed on supplied exchanges every 60 seconds. 38 | 39 | ```sh 40 | pytvc binance cryptopia hitbtc --loop 41 | ``` 42 | 43 | ## TODO 44 | 45 | - [ ] Add watchlist symbols as arguments. 46 | - [ ] Add arg to set initial initial symbol (default BTC/USDT) 47 | 48 | ## ChangeLog 49 | 50 | ### 0.1.4 51 | - CLI params simplified 52 | - Added Brave browser 53 | - Many errors fixed 54 | 55 | ### 0.1.3 56 | 57 | - Added min volume filtering 58 | 59 | ### 0.1.2 60 | 61 | - Removed duplicate "pytvc" dir 62 | - Added some lines to README.md 63 | - Minor errors fixed 64 | 65 | ### 0.1.1 66 | 67 | - Removed duplicate "pytvc" dir 68 | - Added some lines to README.md 69 | - Minor errors fixed 70 | 71 | ### 0.1.0 72 | 73 | - Initial version 74 | -------------------------------------------------------------------------------- /pytvc/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """PyTVC 3 | 4 | Python TradingView Chart 5 | 6 | - Author: Daniel J. Umpierrez 7 | - Created: 03-11-2018 8 | - License: UNLICENSE 9 | """ 10 | from pytvc.core import TradingViewChart 11 | 12 | __project__ = 'PyTVC' 13 | __package__ = 'pytvc' 14 | __author__ = 'Daniel J. Umpierrez' 15 | __license__ = 'UNLICENSE' 16 | __version__ = '0.1.4' 17 | __description__ = __doc__ 18 | __site__ = f'https://github.com/havocesp/{__package__}' 19 | __email__ = 'umpierrez@pm.me' 20 | __keywords__ = ['altcoins', 'altcoin', 'exchange', 'pandas', 'bitcoin', 'trading', 'tradingview', 'chart', 'finance'] 21 | __dependencies__ = ['ccxt', 'begins'] 22 | 23 | __all__ = ['__description__', '__author__', '__license__', '__version__', '__project__', '__site__', '__email__', 24 | '__keywords__', 'TradingViewChart'] 25 | -------------------------------------------------------------------------------- /pytvc/cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """PyTVC 4 | 5 | - Author: Daniel J. Umpierrez 6 | - Created: 04-11-2018 7 | - License: UNLICENSE 8 | """ 9 | import argparse 10 | import json 11 | import sys 12 | from argparse import Namespace 13 | from pathlib import Path 14 | 15 | from .core import TradingViewChart 16 | 17 | 18 | def load_indicators(): 19 | """Load indicators from file. 20 | 21 | :return: 22 | """ 23 | ti = list() 24 | try: 25 | ti_file: Path = Path(__file__).with_name('json').joinpath('tv_indicators.json') 26 | if ti_file.exists() and ti_file.is_file(): 27 | text = ti_file.read_text() 28 | ti = json.loads(text) 29 | except (TypeError, ValueError, IOError,) as err: 30 | print(str(err)) 31 | raise err 32 | return ti 33 | 34 | 35 | def list_indicators() -> int: 36 | """List all supported indicators. 37 | 38 | :return: 0 if all was fine. 39 | """ 40 | ti = load_indicators() 41 | indicators = [f'- {v:<30}' for v in ti] 42 | indicators.sort() 43 | for i in indicators: 44 | print(i) 45 | return 0 46 | 47 | 48 | def main(args) -> int: 49 | """TradingView Chart parserr. 50 | 51 | :param Namespace args: 52 | :return: 53 | """ 54 | tvc = TradingViewChart() 55 | tvc.launch(**vars(args)) 56 | return 0 57 | 58 | 59 | def run(): 60 | """As CLI starting point, this function dispatch argument parsing to be supplied to main function.""" 61 | base_markets = ['BTC', 'TUSD', 'USDT', 'USD', 'EUR', 'PAX', 'USDS'] 62 | exchanges = ['binance', 'hitbtc2', 'poloniex', 'kraken', 'coinbase', 'cexio'] 63 | exchanges = {e: e.strip('_12345 ') for e in exchanges} 64 | 65 | parser = argparse.ArgumentParser() 66 | 67 | parser.add_argument('-l, --list-indicators', 68 | action='store_true', 69 | dest='list_indicators', 70 | help='List all supported technical indicators') 71 | 72 | parser.add_argument('-e, --exchange', 73 | default='binance', 74 | dest='exchange', 75 | nargs='?', 76 | choices=exchanges, 77 | help='Exchange used for watch list symbols.') 78 | 79 | parser.add_argument('-q', '--quote-currency', 80 | nargs='?', 81 | default='BTC', 82 | choices=base_markets, 83 | help='Base market currency (default BTC)') 84 | 85 | parser.add_argument('-i', '--indicator', 86 | metavar='TI', 87 | nargs='*', 88 | choices=list(load_indicators().keys()), 89 | help='Technical analysis indicators to be showed within chart.') 90 | 91 | parser.add_argument('-m', '--min-volume', 92 | type=float, 93 | nargs='?', 94 | dest='min_volume', 95 | default=0.0, 96 | help='Min. 24h volume filter.') 97 | 98 | args = parser.parse_args() 99 | 100 | if args.list_indicators is True: 101 | r_code = list_indicators() 102 | else: 103 | r_code = main(args) 104 | 105 | sys.exit(r_code) 106 | 107 | if __name__ == '__main__': 108 | run() 109 | -------------------------------------------------------------------------------- /pytvc/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Core module.""" 3 | import json 4 | import shutil 5 | import subprocess 6 | from pathlib import Path 7 | 8 | import ccxt 9 | 10 | _CHROME_LIKE_BROWSERS = ['brave-browser', 'chromium', 'chromium-browser', 'google-chrome', 'chrome'] 11 | _BROWSERS = ['firefox', 'firefox-esr', 'mozilla', 'epiphany', 'konqueror', 'safari', 'opera', 'edge'] 12 | _BROWSERS += _CHROME_LIKE_BROWSERS 13 | 14 | 15 | class Symbol: 16 | """Symbol class.""" 17 | 18 | def __init__(self, ref): 19 | if '/' in str(ref): 20 | self.base, self.quote = ref.split('/') 21 | else: 22 | self.base = None 23 | self.quote = None 24 | 25 | def __str__(self): 26 | return f'{self.base or ""}/{self.quote or ""}' 27 | 28 | def __repr__(self): 29 | return f'{self.base or ""}/{self.quote or ""}' 30 | 31 | 32 | def watchlist_formatter(exchange, symbols): 33 | """Returns symbols formatted using TradingView specs. 34 | 35 | :param str exchange: exchange name used as prefix. 36 | :param list symbols: symbols str list. 37 | :return list: symbols list after formatting process. 38 | """ 39 | exchange = str(exchange).upper() 40 | result = [f'{exchange}:{s.base}{s.quote}' 41 | for s in map(Symbol, symbols) 42 | if s.base not in ['PAX']] 43 | return result 44 | 45 | 46 | def open_browser(url): 47 | """Open url using a "chrome like" found (or any when chrome like not found)""" 48 | global _BROWSERS, _CHROME_LIKE_BROWSERS 49 | for browser in _BROWSERS: 50 | if shutil.which(browser): 51 | if browser in _CHROME_LIKE_BROWSERS: 52 | return subprocess.call([browser, f'--app={url}', '--new-window']) 53 | else: 54 | return subprocess.call([browser, f'{url}']) 55 | return 1 56 | 57 | 58 | def get_volume_average(tickers) -> float: 59 | """Get volume average from tickers data. 60 | 61 | :param dict tickers: tickers data. 62 | :return: calculated volume average from tickers data. 63 | """ 64 | all_volumes = [v['quoteVolume'] for v in tickers.values()] 65 | avg = sum(all_volumes) / len(all_volumes) 66 | return avg 67 | 68 | 69 | class TradingViewChart: 70 | """TradingView settings handler.""" 71 | 72 | def get_watchlist(self, exchange, market=None, min_volume=None): 73 | """Returns a formatted symbols list belonging to "market" will be returned after apply a format process based 74 | on TradingView specs 75 | 76 | :param str exchange: a valid exchange name (example: BINANCE) 77 | :param str market: if set, only symbols on specific market will be return, this is, if market is set as USDT, 78 | only symbols ending in USDT (like BTC/USDT) will be return. 79 | :param float min_volume: min volume filter (default 0.0) 80 | :return list: a formatted symbols list belonging to "market" will be returned after apply a format process based 81 | on tradingview specs. 82 | """ 83 | exchange = str(exchange).lower() 84 | 85 | for api_version in range(2, 5): 86 | api_version2check = f'{exchange}{api_version:d}' 87 | 88 | if api_version2check in ccxt.exchanges: 89 | exchange = api_version2check 90 | 91 | api = getattr(ccxt, exchange)({ 92 | 'timeout': 15000, 93 | 'substituteCommonCurrencyCodes': True 94 | }) 95 | 96 | api.load_markets() 97 | 98 | base_markets = {Symbol(s).quote for s in api.symbols} 99 | market = str(market or '').upper() 100 | market = market if market in base_markets else 'BTC' 101 | 102 | filtered_symbols = [f'{api.common_currency_code(s.base)}/{market}' 103 | for s in map(Symbol, api.symbols) 104 | if s.quote in market and s.base != s.quote] 105 | 106 | tickers = api.fetch_tickers(filtered_symbols) 107 | 108 | if min_volume is None: 109 | min_volume = get_volume_average(tickers) 110 | elif isinstance(min_volume or 0, str): 111 | try: 112 | min_volume = float(min_volume) 113 | except ValueError: 114 | pass 115 | elif isinstance(min_volume, (float, int)): 116 | min_volume = get_volume_average(tickers) 117 | else: 118 | raise ValueError(f'{min_volume} invalid min. volume value.') 119 | 120 | symbols_selection = {k: v for k, v in tickers.items() if v['quoteVolume'] > min_volume} 121 | sort_criteria = lambda k: symbols_selection[k]['quoteVolume'] 122 | symbols_selection = sorted(symbols_selection, key=sort_criteria, reverse=True) 123 | 124 | return watchlist_formatter(exchange, symbols_selection) 125 | 126 | def launch(self, exchange, indicators=None, quote_currency=None, min_volume=0.0, **options): 127 | """Launch an embedded "tradingview.com" widget in "app" mode (if available) with default web browser. 128 | 129 | :param str exchange: a valid exchange name (example: BINANCE) 130 | :param indicators: list of indicators short names to show. 131 | :param str quote_currency: a valid quote currency. 132 | :param float min_volume: min volume filter (default 0.0) 133 | :param options: [interval, theme, details, hotlist, calendar, news, hide_side_toolbar, locale, withdateranges] 134 | """ 135 | quote_currency = quote_currency if quote_currency else 'BTC' 136 | 137 | json_dir: Path = Path(__file__).with_name('json') 138 | html_dir: Path = Path(__file__).with_name('html') 139 | 140 | params = json_dir.joinpath('params.json') 141 | params = json.loads(params.read_text(encoding='utf-8')) 142 | params.update(options) 143 | 144 | tv_indicators = json_dir.joinpath('tv_indicators.json') 145 | tv_indicators = json.loads(tv_indicators.read_text()) 146 | 147 | if len(indicators or []): 148 | indicators = [ind.upper() for ind in indicators] 149 | indicators = [tv_indicators[i] for i in tv_indicators if i.upper() in indicators] 150 | else: 151 | indicators = [ 152 | tv_indicators['ChaikinOscillator'], 153 | tv_indicators['ROC'], 154 | tv_indicators['MAExp'], 155 | tv_indicators['MAExp'], 156 | tv_indicators['MASimple'], 157 | tv_indicators['StochasticRSI'], 158 | tv_indicators['LinearRegression'] 159 | ] 160 | 161 | watchlist = self.get_watchlist(exchange, quote_currency, min_volume=min_volume) 162 | symbol = options.get('symbol') or 'BINANCE:BTCUSDT' 163 | 164 | params.update(symbol=symbol, watchlist=watchlist, studies=indicators) 165 | 166 | html_template_code = html_dir.joinpath('template.html').read_text() 167 | 168 | params_json = json.dumps(params) 169 | html_template_code = html_template_code.replace('@PARAMETERS', params_json) 170 | 171 | html_generated_file = html_dir.joinpath('generated.html') 172 | html_generated_file.touch(exist_ok=True) 173 | html_generated_file.write_text(html_template_code) 174 | 175 | generated_file_url = f'file://{html_generated_file}' 176 | 177 | open_browser(generated_file_url) 178 | -------------------------------------------------------------------------------- /pytvc/html/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | PyTVC: Python TradingView Chart 4 | 5 | 6 |
7 |
8 | 13 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /pytvc/json/params.json: -------------------------------------------------------------------------------- 1 | { 2 | "allow_symbol_change": true, 3 | "autosize": true, 4 | "calendar": false, 5 | "container_id": "tradingview_496c6", 6 | "details": true, 7 | "enable_publishing": false, 8 | "hide_side_toolbar": false, 9 | "hotlist": false, 10 | "interval": "15", 11 | "locale": "en", 12 | "news": [ 13 | "headlines" 14 | ], 15 | "popup_height": "650", 16 | "popup_width": "1000", 17 | "show_popup_button": true, 18 | "studies": [], 19 | "style": "1", 20 | "symbol": "BINANCE:BTCUSDT", 21 | "theme": "Light", 22 | "timezone": "Etc/UTC", 23 | "toolbar_bg": "#f1f3f6", 24 | "watchlist": [], 25 | "withdateranges": false 26 | } 27 | -------------------------------------------------------------------------------- /pytvc/json/tv_indicators.json: -------------------------------------------------------------------------------- 1 | { 2 | "ACCD": "ACCD@tv-basicstudies", 3 | "AD": "ACCD@tv-basicstudies", 4 | "studyADR": "studyADR@tv-basicstudies", 5 | "ADR": "studyADR@tv-basicstudies", 6 | "AROON": "AROON@tv-basicstudies", 7 | "ATR": "ATR@tv-basicstudies", 8 | "AwesomeOscillator": "AwesomeOscillator@tv-basicstudies", 9 | "AO": "AwesomeOscillator@tv-basicstudies", 10 | "BB": "BB@tv-basicstudies", 11 | "BollingerBandsR": "BollingerBandsR@tv-basicstudies", 12 | "BollingerBandsWidth": "BollingerBandsWidth@tv-basicstudies", 13 | "CMF": "CMF@tv-basicstudies", 14 | "ChaikinOscillator": "ChaikinOscillator@tv-basicstudies", 15 | "ADOSC": "ChaikinOscillator@tv-basicstudies", 16 | "chandeMO": "chandeMO@tv-basicstudies", 17 | "ChoppinessIndex": "ChoppinessIndex@tv-basicstudies", 18 | "CCI": "CCI@tv-basicstudies", 19 | "CRSI": "CRSI@tv-basicstudies", 20 | "CorrelationCoefficient": "CorrelationCoefficient@tv-basicstudies", 21 | "CORREL": "CorrelationCoefficient@tv-basicstudies", 22 | "DetrendedPriceOscillator": "DetrendedPriceOscillator@tv-basicstudies", 23 | "DPO": "DetrendedPriceOscillator@tv-basicstudies", 24 | "DM": "DM@tv-basicstudies", 25 | "DONCH": "DONCH@tv-basicstudies", 26 | "DoubleEMA": "DoubleEMA@tv-basicstudies", 27 | "DEMA": "DoubleEMA@tv-basicstudies", 28 | "EaseOfMovement": "EaseOfMovement@tv-basicstudies", 29 | "EFI": "EFI@tv-basicstudies", 30 | "ENV": "ENV@tv-basicstudies", 31 | "FisherTransform": "FisherTransform@tv-basicstudies", 32 | "HV": "HV@tv-basicstudies", 33 | "hullMA": "hullMA@tv-basicstudies", 34 | "HMA": "hullMA@tv-basicstudies", 35 | "IchimokuCloud": "IchimokuCloud@tv-basicstudies", 36 | "ICHIMOKU": "IchimokuCloud@tv-basicstudies", 37 | "KLTNR": "KLTNR@tv-basicstudies", 38 | "KST": "KST@tv-basicstudies", 39 | "LinearRegression": "LinearRegression@tv-basicstudies", 40 | "LINREG": "LinearRegression@tv-basicstudies", 41 | "LINEARREG": "LinearRegression@tv-basicstudies", 42 | "MACD": "MACD@tv-basicstudies", 43 | "MOM": "MOM@tv-basicstudies", 44 | "MF": "MF@tv-basicstudies", 45 | "MoonPhases": "MoonPhases@tv-basicstudies", 46 | "MASimple": "MASimple@tv-basicstudies", 47 | "MA": "MASimple@tv-basicstudies", 48 | "SMA": "MASimple@tv-basicstudies", 49 | "MAExp": "MAExp@tv-basicstudies", 50 | "EMA": "MAExp@tv-basicstudies", 51 | "MAWeighted": "MAWeighted@tv-basicstudies", 52 | "OBV": "OBV@tv-basicstudies", 53 | "PSAR": "PSAR@tv-basicstudies", 54 | "PivotPointsHighLow": "PivotPointsHighLow@tv-basicstudies", 55 | "PivotPointsStandard": "PivotPointsStandard@tv-basicstudies", 56 | "PriceOsc": "PriceOsc@tv-basicstudies", 57 | "PriceVolumeTrend": "PriceVolumeTrend@tv-basicstudies", 58 | "PVT": "PriceVolumeTrend@tv-basicstudies", 59 | "ROC": "ROC@tv-basicstudies", 60 | "RSI": "RSI@tv-basicstudies", 61 | "VigorIndex": "VigorIndex@tv-basicstudies", 62 | "VolatilityIndex": "VolatilityIndex@tv-basicstudies", 63 | "SMIErgodicIndicator": "SMIErgodicIndicator@tv-basicstudies", 64 | "SMIErgodicOscillator": "SMIErgodicOscillator@tv-basicstudies", 65 | "Stochastic": "Stochastic@tv-basicstudies", 66 | "STOCH": "Stochastic@tv-basicstudies", 67 | "StochasticRSI": "StochasticRSI@tv-basicstudies", 68 | "STOCHRSI": "StochasticRSI@tv-basicstudies", 69 | "TripleEMA": "TripleEMA@tv-basicstudies", 70 | "TEMA": "TripleEMA@tv-basicstudies", 71 | "Trix": "Trix@tv-basicstudies", 72 | "TRIX": "Trix@tv-basicstudies", 73 | "UltimateOsc": "UltimateOsc@tv-basicstudies", 74 | "UO": "UltimateOsc@tv-basicstudies", 75 | "VSTOP": "VSTOP@tv-basicstudies", 76 | "Volume": "Volume@tv-basicstudies", 77 | "VOL": "Volume@tv-basicstudies", 78 | "VWAP": "VWAP@tv-basicstudies", 79 | "MAVolumeWeighted": "MAVolumeWeighted@tv-basicstudies", 80 | "WilliamR": "WilliamR@tv-basicstudies", 81 | "WILLR": "WilliamR@tv-basicstudies", 82 | "WilliamsAlligator": "WilliamsAlligator@tv-basicstudies", 83 | "WILLA": "WilliamsAlligator@tv-basicstudies", 84 | "WilliamsFractal": "WilliamsFractal@tv-basicstudies", 85 | "WILLF": "WilliamsFractal@tv-basicstudies", 86 | "ZigZag": "ZigZag@tv-basicstudies", 87 | "ZIGZAG": "ZigZag@tv-basicstudies" 88 | } 89 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ccxt 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from setuptools import setup, find_packages 3 | from pathlib import Path 4 | 5 | import pytvc 6 | 7 | classifiers = [ 8 | 'Development Status :: 5 - Production', 9 | 'License :: OSI Approved :: MIT License', 10 | 'Programming Language :: Python :: 3.6', 11 | 'Programming Language :: Python :: 3.7', 12 | ] 13 | 14 | exclude = ['.idea*', 'build*', f'{pytvc.__package__}.egg-info*', 'dist*', 'venv*', 'doc*', 'lab*'] 15 | 16 | readme_file = Path(__file__).with_name('README.md') 17 | 18 | setup( 19 | name=pytvc.__package__, 20 | version=pytvc.__version__, 21 | packages=find_packages(exclude=exclude), 22 | package_dir={pytvc.__package__: pytvc.__package__}, 23 | package_data={pytvc.__package__: ['html/*.*', 'json/*.*']}, 24 | entry_points={ 25 | 'console_scripts': [ 26 | 'pytvc = pytvc.cli:run' 27 | ] 28 | }, 29 | url=pytvc.__site__, 30 | long_description=readme_file.read_text(encoding='utf-8'), 31 | long_description_content_type='text/markdown', 32 | license=pytvc.__license__, 33 | author=pytvc.__author__, 34 | author_email=pytvc.__email__, 35 | description=pytvc.__description__, 36 | keywords=pytvc.__keywords__, 37 | classifiers=classifiers, 38 | install_requires=pytvc.__dependencies__ 39 | ) 40 | --------------------------------------------------------------------------------