├── .gitignore ├── README.md ├── data ├── 23-07-2020.csv ├── 30-06-2020.csv ├── 31-12-2019.csv ├── BTCUSDT_ohlcv_5Min.csv.gz ├── EB3M.csv ├── FR_IR_1954_2017.csv ├── IWD_holdings.csv ├── NYSE.txt └── binance_1D.db ├── models ├── 0. Crypto Data Loader.ipynb ├── 1. Daily Gaps Research.ipynb ├── 1.1 Daily Gaps trading on stock market.ipynb ├── 2. Russel Index Tracking.ipynb ├── 3.1 CryptCorrellations.ipynb ├── 3.2 CryptCorrellations.ipynb ├── 4.0 Patterns Identification.ipynb ├── 4.1 Patterns Identification.ipynb ├── 5. Funding rates EDA.ipynb ├── helpers.py ├── initlibs.py ├── kalman_filter_adjust.py ├── patterns.py └── statarb │ ├── 0. FTX data access.ipynb │ ├── 1. Trading Model.ipynb │ ├── algo.py │ ├── coint_pairs_15Min.json │ ├── coint_pairs_5Min.json │ ├── dsconfig.json │ └── utils.py ├── required.txt └── tools ├── analysis ├── __init__.py ├── data.py ├── drawdown.py ├── portfolio.py ├── stats.py ├── stochastic.py ├── timeseries.py └── tools.py ├── charting ├── mpl_finance.py └── plot_helpers.py ├── loaders ├── binance.py ├── blockchain_data.py ├── google_trends.py └── yahoo_finance.py └── utils ├── stat_helpers.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | # SQLite databases (for this project) 9 | *.db 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | pip-wheel-metadata/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | .python-version 88 | 89 | # pipenv 90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 92 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 93 | # install all needed dependencies. 94 | #Pipfile.lock 95 | 96 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 97 | __pypackages__/ 98 | 99 | # Celery stuff 100 | celerybeat-schedule 101 | celerybeat.pid 102 | 103 | # SageMath parsed files 104 | *.sage.py 105 | 106 | # Environments 107 | .env 108 | .venv 109 | env/ 110 | venv/ 111 | ENV/ 112 | env.bak/ 113 | venv.bak/ 114 | 115 | # Spyder project settings 116 | .spyderproject 117 | .spyproject 118 | 119 | # Rope project settings 120 | .ropeproject 121 | 122 | # mkdocs documentation 123 | /site 124 | 125 | # mypy 126 | .mypy_cache/ 127 | .dmypy.json 128 | dmypy.json 129 | 130 | # Pyre type checker 131 | .pyre/ 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QuantTradingResearch 2 | Some quantitative models research and applications to trading. 3 | 4 | - [Gaps trading model for crypto market](https://github.com/dmarienko/QuantTradingResearch/blob/master/models/1.%20Daily%20Gaps%20Research.ipynb) 5 | - [Daily Gaps trading model for stock market](https://github.com/dmarienko/QuantTradingResearch/blob/master/models/1.1%20Daily%20Gaps%20trading%20on%20stock%20market.ipynb) 6 | - [Russel1000 Index tracking and arbitrage trading](https://github.com/dmarienko/QuantTradingResearch/blob/master/models/2.%20Russel%20Index%20Tracking.ipynb) 7 | - [Correlation of bitcoin price series and blockchain data](https://github.com/dmarienko/QuantTradingResearch/blob/master/models/3.1%20CryptCorrellations.ipynb) 8 | - [Google trends data and Bitcoin prices / predictive model](https://github.com/dmarienko/QuantTradingResearch/blob/master/models/3.2%20CryptCorrellations.ipynb) 9 | - [Patterns detecting](https://github.com/dmarienko/QuantTradingResearch/blob/master/models/4.0%20Patterns%20Identification.ipynb) 10 | - [Funding rates arbitrage](https://github.com/dmarienko/QuantTradingResearch/blob/master/models/5.%20Funding%20rates%20EDA.ipynb) 11 | - [Pair arbitrage on FTX futures](https://github.com/dmarienko/QuantTradingResearch/blob/master/models/statarb/1.%20Trading%20Model.ipynb) 12 | - Candlestick patterns statistics 13 | - Different stochastic models calibration experiments 14 | - [Turtles trading model](https://github.com/dmarienko/cryptomomentum) 15 | - Breakout model and risk management [Lustre Chandelier model](https://github.com/dmarienko/Lustre) 16 | - Cash and Carry strategy research materials for crypto assets [Crypto Cash Carry Profiting](https://github.com/dmarienko/c3p) 17 | -------------------------------------------------------------------------------- /data/BTCUSDT_ohlcv_5Min.csv.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmarienko/QuantTradingResearch/8d324b8c54935b36196f157d8dd88469646645bf/data/BTCUSDT_ohlcv_5Min.csv.gz -------------------------------------------------------------------------------- /data/EB3M.csv: -------------------------------------------------------------------------------- 1 | 1994-Jan,6.9100 2 | 1994-Feb,6.8600 3 | 1994-Mar,6.7500 4 | 1994-Apr,6.5700 5 | 1994-May,6.2400 6 | 1994-Jun,6.3000 7 | 1994-Jul,6.3400 8 | 1994-Aug,6.4300 9 | 1994-Sep,6.3800 10 | 1994-Oct,6.4300 11 | 1994-Nov,6.4000 12 | 1994-Dec,6.6900 13 | 1995-Jan,6.6600 14 | 1995-Feb,6.5900 15 | 1995-Mar,7.5800 16 | 1995-Apr,7.2900 17 | 1995-May,7.0400 18 | 1995-Jun,7.0800 19 | 1995-Jul,6.9200 20 | 1995-Aug,6.6600 21 | 1995-Sep,6.5000 22 | 1995-Oct,6.7500 23 | 1995-Nov,6.4500 24 | 1995-Dec,6.3200 25 | 1996-Jan,5.8100 26 | 1996-Feb,5.5800 27 | 1996-Mar,5.5000 28 | 1996-Apr,5.2700 29 | 1996-May,5.0600 30 | 1996-Jun,5.0800 31 | 1996-Jul,5.0600 32 | 1996-Aug,5.0800 33 | 1996-Sep,4.8600 34 | 1996-Oct,4.6900 35 | 1996-Nov,4.5700 36 | 1996-Dec,4.5000 37 | 1997-Jan,4.3900 38 | 1997-Feb,4.4300 39 | 1997-Mar,4.5000 40 | 1997-Apr,4.3900 41 | 1997-May,4.3000 42 | 1997-Jun,4.2900 43 | 1997-Jul,4.3000 44 | 1997-Aug,4.3600 45 | 1997-Sep,4.3100 46 | 1997-Oct,4.4400 47 | 1997-Nov,4.4900 48 | 1997-Dec,4.3700 49 | 1998-Jan,4.2600 50 | 1998-Feb,4.2400 51 | 1998-Mar,4.1100 52 | 1998-Apr,4.0900 53 | 1998-May,4.0600 54 | 1998-Jun,4.0200 55 | 1998-Jul,3.9500 56 | 1998-Aug,3.9300 57 | 1998-Sep,3.9300 58 | 1998-Oct,3.8100 59 | 1998-Nov,3.6900 60 | 1998-Dec,3.3700 61 | 1999-Jan,3.1321 62 | 1999-Feb,3.0934 63 | 1999-Mar,3.0467 64 | 1999-Apr,2.6965 65 | 1999-May,2.5790 66 | 1999-Jun,2.6267 67 | 1999-Jul,2.6765 68 | 1999-Aug,2.6950 69 | 1999-Sep,2.7267 70 | 1999-Oct,3.3757 71 | 1999-Nov,3.4677 72 | 1999-Dec,3.4460 73 | 2000-Jan,3.3431 74 | 2000-Feb,3.5368 75 | 2000-Mar,3.7470 76 | 2000-Apr,3.9253 77 | 2000-May,4.3620 78 | 2000-Jun,4.5017 79 | 2000-Jul,4.5829 80 | 2000-Aug,4.7771 81 | 2000-Sep,4.8528 82 | 2000-Oct,5.0413 83 | 2000-Nov,5.0920 84 | 2000-Dec,4.9392 85 | 2001-Jan,4.7707 86 | 2001-Feb,4.7558 87 | 2001-Mar,4.7086 88 | 2001-Apr,4.6820 89 | 2001-May,4.6367 90 | 2001-Jun,4.4536 91 | 2001-Jul,4.4671 92 | 2001-Aug,4.3535 93 | 2001-Sep,3.9829 94 | 2001-Oct,3.5999 95 | 2001-Nov,3.3857 96 | 2001-Dec,3.3449 97 | 2002-Jan,3.3388 98 | 2002-Feb,3.3571 99 | 2002-Mar,3.3908 100 | 2002-Apr,3.4069 101 | 2002-May,3.4671 102 | 2002-Jun,3.4640 103 | 2002-Jul,3.4100 104 | 2002-Aug,3.3519 105 | 2002-Sep,3.3101 106 | 2002-Oct,3.2613 107 | 2002-Nov,3.1241 108 | 2002-Dec,2.9410 109 | 2003-Jan,2.8318 110 | 2003-Feb,2.6875 111 | 2003-Mar,2.5300 112 | 2003-Apr,2.5333 113 | 2003-May,2.4005 114 | 2003-Jun,2.1519 115 | 2003-Jul,2.1300 116 | 2003-Aug,2.1404 117 | 2003-Sep,2.1473 118 | 2003-Oct,2.1436 119 | 2003-Nov,2.1590 120 | 2003-Dec,2.1463 121 | 2004-Jan,2.0895 122 | 2004-Feb,2.0705 123 | 2004-Mar,2.0288 124 | 2004-Apr,2.0488 125 | 2004-May,2.0859 126 | 2004-Jun,2.1127 127 | 2004-Jul,2.1160 128 | 2004-Aug,2.1143 129 | 2004-Sep,2.1186 130 | 2004-Oct,2.1473 131 | 2004-Nov,2.1703 132 | 2004-Dec,2.1732 133 | 2005-Jan,2.1454 134 | 2005-Feb,2.1383 135 | 2005-Mar,2.1372 136 | 2005-Apr,2.1372 137 | 2005-May,2.1256 138 | 2005-Jun,2.1110 139 | 2005-Jul,2.1194 140 | 2005-Aug,2.1325 141 | 2005-Sep,2.1391 142 | 2005-Oct,2.1966 143 | 2005-Nov,2.3609 144 | 2005-Dec,2.4729 145 | 2006-Jan,2.5117 146 | 2006-Feb,2.6004 147 | 2006-Mar,2.7226 148 | 2006-Apr,2.7938 149 | 2006-May,2.8890 150 | 2006-Jun,2.9857 151 | 2006-Jul,3.1022 152 | 2006-Aug,3.2265 153 | 2006-Sep,3.3354 154 | 2006-Oct,3.5020 155 | 2006-Nov,3.5972 156 | 2006-Dec,3.6842 157 | 2007-Jan,3.7519 158 | 2007-Feb,3.8182 159 | 2007-Mar,3.8909 160 | 2007-Apr,3.9753 161 | 2007-May,4.0714 162 | 2007-Jun,4.1478 163 | 2007-Jul,4.2162 164 | 2007-Aug,4.5436 165 | 2007-Sep,4.7417 166 | 2007-Oct,4.6874 167 | 2007-Nov,4.6385 168 | 2007-Dec,4.8484 169 | 2008-Jan,4.4815 170 | 2008-Feb,4.3621 171 | 2008-Mar,4.5964 172 | 2008-Apr,4.7835 173 | 2008-May,4.8574 174 | 2008-Jun,4.9405 175 | 2008-Jul,4.9610 176 | 2008-Aug,4.9652 177 | 2008-Sep,5.0192 178 | 2008-Oct,5.1131 179 | 2008-Nov,4.2383 180 | 2008-Dec,3.2926 181 | 2009-Jan,2.4565 182 | 2009-Feb,1.9431 183 | 2009-Mar,1.6355 184 | 2009-Apr,1.4223 185 | 2009-May,1.2817 186 | 2009-Jun,1.2279 187 | 2009-Jul,0.9750 188 | 2009-Aug,0.8605 189 | 2009-Sep,0.7721 190 | 2009-Oct,0.7375 191 | 2009-Nov,0.7162 192 | 2009-Dec,0.7120 193 | 2010-Jan,0.6797 194 | 2010-Feb,0.6617 195 | 2010-Mar,0.6450 196 | 2010-Apr,0.6447 197 | 2010-May,0.6865 198 | 2010-Jun,0.7276 199 | 2010-Jul,0.8488 200 | 2010-Aug,0.8955 201 | 2010-Sep,0.8805 202 | 2010-Oct,0.9977 203 | 2010-Nov,1.0420 204 | 2010-Dec,1.0217 205 | 2011-Jan,1.0172 206 | 2011-Feb,1.0867 207 | 2011-Mar,1.1755 208 | 2011-Apr,1.3212 209 | 2011-May,1.4251 210 | 2011-Jun,1.4886 211 | 2011-Jul,1.5976 212 | 2011-Aug,1.5521 213 | 2011-Sep,1.5365 214 | 2011-Oct,1.5759 215 | 2011-Nov,1.4847 216 | 2011-Dec,1.4261 217 | 2012-Jan,1.2222 218 | 2012-Feb,1.0483 219 | 2012-Mar,0.8585 220 | 2012-Apr,0.7443 221 | 2012-May,0.6849 222 | 2012-Jun,0.6589 223 | 2012-Jul,0.4970 224 | 2012-Aug,0.3324 225 | 2012-Sep,0.2463 226 | 2012-Oct,0.2079 227 | 2012-Nov,0.1920 228 | 2012-Dec,0.1855 229 | 2013-Jan,0.2049 230 | 2013-Feb,0.2234 231 | 2013-Mar,0.2061 232 | 2013-Apr,0.2089 233 | 2013-May,0.2012 234 | 2013-Jun,0.2103 235 | 2013-Jul,0.2214 236 | 2013-Aug,0.2259 237 | 2013-Sep,0.2232 238 | 2013-Oct,0.2258 239 | 2013-Nov,0.2234 240 | 2013-Dec,0.2735 241 | 2014-Jan,0.2920 242 | 2014-Feb,0.2881 243 | 2014-Mar,0.3053 244 | 2014-Apr,0.3297 245 | 2014-May,0.3246 246 | 2014-Jun,0.2414 247 | 2014-Jul,0.2050 248 | 2014-Aug,0.1916 249 | 2014-Sep,0.0971 250 | 2014-Oct,0.0826 251 | 2014-Nov,0.0809 252 | 2014-Dec,0.0809 253 | 2015-Jan,0.0627 254 | 2015-Feb,0.0482 255 | 2015-Mar,0.0272 256 | 2015-Apr,0.0047 257 | 2015-May,-0.0104 258 | 2015-Jun,-0.0139 259 | 2015-Jul,-0.0187 260 | 2015-Aug,-0.0277 261 | 2015-Sep,-0.0370 262 | 2015-Oct,-0.0536 263 | 2015-Nov,-0.0876 264 | 2015-Dec,-0.1263 265 | 2016-Jan,-0.1461 266 | 2016-Feb,-0.1836 267 | 2016-Mar,-0.2285 268 | 2016-Apr,-0.2492 269 | 2016-May,-0.2572 270 | 2016-Jun,-0.2679 271 | 2016-Jul,-0.2945 272 | 2016-Aug,-0.2982 273 | 2016-Sep,-0.3016 274 | 2016-Oct,-0.3090 275 | 2016-Nov,-0.3127 276 | 2016-Dec,-0.3158 277 | 2017-Jan,-0.3255 278 | 2017-Feb,-0.3286 279 | 2017-Mar,-0.3293 280 | 2017-Apr,-0.3304 281 | 2017-May,-0.3295 282 | 2017-Jun,-0.3300 283 | 2017-Jul,-0.3304 284 | 2017-Aug,-0.3291 285 | 2017-Sep,-0.3294 286 | 2017-Oct,-0.3295 287 | 2017-Nov,-0.3290 288 | 2017-Dec,-0.3279 289 | 2018-Jan,-0.3285 290 | 2018-Feb,-0.3285 291 | 2018-Mar,-0.3279 292 | 2018-Apr,-0.3285 293 | 2018-May,-0.3252 294 | 2018-Jun,-0.3220 295 | 2018-Jul,-0.3207 296 | 2018-Aug,-0.3190 297 | 2018-Sep,-0.3188 298 | 2018-Oct,-0.3177 299 | 2018-Nov,-0.3164 300 | 2018-Dec,-0.3119 301 | 2019-Jan,-0.3080 302 | 2019-Feb,-0.3084 303 | 2019-Mar,-0.3092 304 | 2019-Apr,-0.3105 305 | 2019-May,-0.3119 306 | 2019-Jun,-0.3289 307 | 2019-Jul,-0.3649 308 | 2019-Aug,-0.4077 309 | 2019-Sep,-0.4176 310 | 2019-Oct,-0.4129 311 | 2019-Nov,-0.4013 312 | 2019-Dec,-0.3947 313 | 2020-Jan,-0.3911 314 | 2020-Feb,-0.4088 315 | 2020-Mar,-0.4166 316 | 2020-Apr,-0.2540 317 | 2020-May,-0.2701 318 | 2020-Jun,-0.3760 -------------------------------------------------------------------------------- /data/FR_IR_1954_2017.csv: -------------------------------------------------------------------------------- 1 | Year,Month,Day,Federal Funds Target Rate,Federal Funds Upper Target,Federal Funds Lower Target,Effective Federal Funds Rate,Real GDP (Percent Change),Unemployment Rate,Inflation Rate 2 | 1954,07,01,,,,0.8,4.6,5.8, 3 | 1954,08,01,,,,1.22,,6, 4 | 1954,09,01,,,,1.06,,6.1, 5 | 1954,10,01,,,,0.85,8,5.7, 6 | 1954,11,01,,,,0.83,,5.3, 7 | 1954,12,01,,,,1.28,,5, 8 | 1955,01,01,,,,1.39,11.9,4.9, 9 | 1955,02,01,,,,1.29,,4.7, 10 | 1955,03,01,,,,1.35,,4.6, 11 | 1955,04,01,,,,1.43,6.7,4.7, 12 | 1955,05,01,,,,1.43,,4.3, 13 | 1955,06,01,,,,1.64,,4.2, 14 | 1955,07,01,,,,1.68,5.5,4, 15 | 1955,08,01,,,,1.96,,4.2, 16 | 1955,09,01,,,,2.18,,4.1, 17 | 1955,10,01,,,,2.24,2.4,4.3, 18 | 1955,11,01,,,,2.35,,4.2, 19 | 1955,12,01,,,,2.48,,4.2, 20 | 1956,01,01,,,,2.45,-1.5,4, 21 | 1956,02,01,,,,2.5,,3.9, 22 | 1956,03,01,,,,2.5,,4.2, 23 | 1956,04,01,,,,2.62,3.4,4, 24 | 1956,05,01,,,,2.75,,4.3, 25 | 1956,06,01,,,,2.71,,4.3, 26 | 1956,07,01,,,,2.75,-0.3,4.4, 27 | 1956,08,01,,,,2.73,,4.1, 28 | 1956,09,01,,,,2.95,,3.9, 29 | 1956,10,01,,,,2.96,6.7,3.9, 30 | 1956,11,01,,,,2.88,,4.3, 31 | 1956,12,01,,,,2.94,,4.2, 32 | 1957,01,01,,,,2.84,2.6,4.2, 33 | 1957,02,01,,,,3,,3.9, 34 | 1957,03,01,,,,2.96,,3.7, 35 | 1957,04,01,,,,3,-0.9,3.9, 36 | 1957,05,01,,,,3,,4.1, 37 | 1957,06,01,,,,3,,4.3, 38 | 1957,07,01,,,,2.99,4,4.2, 39 | 1957,08,01,,,,3.24,,4.1, 40 | 1957,09,01,,,,3.47,,4.4, 41 | 1957,10,01,,,,3.5,-4,4.5, 42 | 1957,11,01,,,,3.28,,5.1, 43 | 1957,12,01,,,,2.98,,5.2, 44 | 1958,01,01,,,,2.72,-10,5.8,3.2 45 | 1958,02,01,,,,1.67,,6.4,3.2 46 | 1958,03,01,,,,1.2,,6.7,2.8 47 | 1958,04,01,,,,1.26,2.6,7.4,2.4 48 | 1958,05,01,,,,0.63,,7.4,2.4 49 | 1958,06,01,,,,0.93,,7.3,2.1 50 | 1958,07,01,,,,0.68,9.6,7.5,2.4 51 | 1958,08,01,,,,1.53,,7.4,2.1 52 | 1958,09,01,,,,1.76,,7.1,1.7 53 | 1958,10,01,,,,1.8,9.7,6.7,1.7 54 | 1958,11,01,,,,2.27,,6.2,1.7 55 | 1958,12,01,,,,2.42,,6.2,1.7 56 | 1959,01,01,,,,2.48,7.7,6,1.7 57 | 1959,02,01,,,,2.43,,5.9,1.7 58 | 1959,03,01,,,,2.8,,5.6,1.7 59 | 1959,04,01,,,,2.96,10.1,5.2,1.7 60 | 1959,05,01,,,,2.9,,5.1,2.0 61 | 1959,06,01,,,,3.39,,5,2.0 62 | 1959,07,01,,,,3.47,-0.8,5.1,2.0 63 | 1959,08,01,,,,3.5,,5.2,2.0 64 | 1959,09,01,,,,3.76,,5.5,2.4 65 | 1959,10,01,,,,3.98,1.6,5.7,2.7 66 | 1959,11,01,,,,4,,5.8,2.0 67 | 1959,12,01,,,,3.99,,5.3,2.0 68 | 1960,01,01,,,,3.99,9.2,5.2,2.0 69 | 1960,02,01,,,,3.97,,4.8,2.3 70 | 1960,03,01,,,,3.84,,5.4,2.0 71 | 1960,04,01,,,,3.92,-1.5,5.2,2.0 72 | 1960,05,01,,,,3.85,,5.1,1.7 73 | 1960,06,01,,,,3.32,,5.4,1.7 74 | 1960,07,01,,,,3.23,1,5.5,1.3 75 | 1960,08,01,,,,2.98,,5.6,1.3 76 | 1960,09,01,,,,2.6,,5.5,1.0 77 | 1960,10,01,,,,2.47,-4.8,6.1,1.0 78 | 1960,11,01,,,,2.44,,6.1,1.0 79 | 1960,12,01,,,,1.98,,6.6,1.0 80 | 1961,01,01,,,,1.45,2.7,6.6,1.0 81 | 1961,02,01,,,,2.54,,6.9,0.7 82 | 1961,03,01,,,,2.02,,6.9,0.7 83 | 1961,04,01,,,,1.49,7.6,7,1.0 84 | 1961,05,01,,,,1.98,,7.1,1.0 85 | 1961,06,01,,,,1.73,,6.9,1.0 86 | 1961,07,01,,,,1.17,6.8,7,1.3 87 | 1961,08,01,,,,2,,6.6,1.3 88 | 1961,09,01,,,,1.88,,6.7,1.6 89 | 1961,10,01,,,,2.26,8.3,6.5,1.3 90 | 1961,11,01,,,,2.61,,6.1,1.3 91 | 1961,12,01,,,,2.33,,6,1.3 92 | 1962,01,01,,,,2.15,7.4,5.8,1.3 93 | 1962,02,01,,,,2.37,,5.5,1.3 94 | 1962,03,01,,,,2.85,,5.6,1.6 95 | 1962,04,01,,,,2.78,4.4,5.6,1.3 96 | 1962,05,01,,,,2.36,,5.5,1.6 97 | 1962,06,01,,,,2.68,,5.5,1.6 98 | 1962,07,01,,,,2.71,3.9,5.4,1.3 99 | 1962,08,01,,,,2.93,,5.7,1.3 100 | 1962,09,01,,,,2.9,,5.6,1.3 101 | 1962,10,01,,,,2.9,1.6,5.4,1.3 102 | 1962,11,01,,,,2.94,,5.7,1.3 103 | 1962,12,01,,,,2.93,,5.5,1.3 104 | 1963,01,01,,,,2.92,4.5,5.7,1.0 105 | 1963,02,01,,,,3,,5.9,1.0 106 | 1963,03,01,,,,2.98,,5.7,1.0 107 | 1963,04,01,,,,2.9,5.3,5.7,1.3 108 | 1963,05,01,,,,3,,5.9,1.0 109 | 1963,06,01,,,,2.99,,5.6,1.3 110 | 1963,07,01,,,,3.02,8,5.6,1.3 111 | 1963,08,01,,,,3.49,,5.4,1.6 112 | 1963,09,01,,,,3.48,,5.5,1.3 113 | 1963,10,01,,,,3.5,2.9,5.5,1.3 114 | 1963,11,01,,,,3.48,,5.7,1.6 115 | 1963,12,01,,,,3.38,,5.5,1.6 116 | 1964,01,01,,,,3.48,8.9,5.6,1.9 117 | 1964,02,01,,,,3.48,,5.4,1.9 118 | 1964,03,01,,,,3.43,,5.4,1.9 119 | 1964,04,01,,,,3.47,4.8,5.3,1.6 120 | 1964,05,01,,,,3.5,,5.1,1.6 121 | 1964,06,01,,,,3.5,,5.2,1.6 122 | 1964,07,01,,,,3.42,5.5,4.9,1.6 123 | 1964,08,01,,,,3.5,,5,0.9 124 | 1964,09,01,,,,3.45,,5.1,1.3 125 | 1964,10,01,,,,3.36,1.4,5.1,1.3 126 | 1964,11,01,,,,3.52,,4.8,1.2 127 | 1964,12,01,,,,3.85,,5,1.2 128 | 1965,01,01,,,,3.9,10.2,4.9,1.6 129 | 1965,02,01,,,,3.98,,5.1,1.6 130 | 1965,03,01,,,,4.04,,4.7,1.2 131 | 1965,04,01,,,,4.09,5.6,4.8,1.6 132 | 1965,05,01,,,,4.1,,4.6,1.6 133 | 1965,06,01,,,,4.04,,4.6,1.2 134 | 1965,07,01,,,,4.09,8.4,4.4,1.2 135 | 1965,08,01,,,,4.12,,4.4,1.6 136 | 1965,09,01,,,,4.01,,4.3,1.5 137 | 1965,10,01,,,,4.08,9.8,4.2,1.5 138 | 1965,11,01,,,,4.1,,4.1,1.2 139 | 1965,12,01,,,,4.32,,4,1.5 140 | 1966,01,01,,,,4.42,10.2,4,0.9 141 | 1966,02,01,,,,4.6,,3.8,1.2 142 | 1966,03,01,,,,4.65,,3.8,1.5 143 | 1966,04,01,,,,4.67,1.6,3.8,1.8 144 | 1966,05,01,,,,4.9,,3.9,2.1 145 | 1966,06,01,,,,5.17,,3.8,2.4 146 | 1966,07,01,,,,5.3,2.9,3.8,2.8 147 | 1966,08,01,,,,5.53,,3.8,3.1 148 | 1966,09,01,,,,5.4,,3.7,3.0 149 | 1966,10,01,,,,5.53,3.5,3.7,3.3 150 | 1966,11,01,,,,5.76,,3.6,3.6 151 | 1966,12,01,,,,5.4,,3.8,3.3 152 | 1967,01,01,,,,4.94,3.7,3.9,3.6 153 | 1967,02,01,,,,5,,3.8,3.6 154 | 1967,03,01,,,,4.53,,3.8,3.6 155 | 1967,04,01,,,,4.05,0.3,3.8,3.3 156 | 1967,05,01,,,,3.94,,3.8,3.3 157 | 1967,06,01,,,,3.98,,3.9,3.3 158 | 1967,07,01,,,,3.79,3.5,3.8,3.3 159 | 1967,08,01,,,,3.9,,3.8,3.3 160 | 1967,09,01,,,,3.99,,3.8,3.6 161 | 1967,10,01,,,,3.88,3.2,4,3.5 162 | 1967,11,01,,,,4.13,,3.9,3.5 163 | 1967,12,01,,,,4.51,,3.8,3.8 164 | 1968,01,01,,,,4.6,8.4,3.7,4.1 165 | 1968,02,01,,,,4.71,,3.8,4.1 166 | 1968,03,01,,,,5.05,,3.7,4.4 167 | 1968,04,01,,,,5.76,6.9,3.5,4.4 168 | 1968,05,01,,,,6.11,,3.5,4.3 169 | 1968,06,01,,,,6.07,,3.7,4.6 170 | 1968,07,01,,,,6.02,2.9,3.7,4.9 171 | 1968,08,01,,,,6.03,,3.5,4.9 172 | 1968,09,01,,,,5.78,,3.4,4.9 173 | 1968,10,01,,,,5.91,1.8,3.4,4.8 174 | 1968,11,01,,,,5.82,,3.4,5.1 175 | 1968,12,01,,,,6.02,,3.4,5.1 176 | 1969,01,01,,,,6.3,6.4,3.4,5.1 177 | 1969,02,01,,,,6.61,,3.4,5.3 178 | 1969,03,01,,,,6.79,,3.4,5.6 179 | 1969,04,01,,,,7.41,1.3,3.4,6.1 180 | 1969,05,01,,,,8.67,,3.4,6.1 181 | 1969,06,01,,,,8.9,,3.5,5.8 182 | 1969,07,01,,,,8.61,2.5,3.5,5.8 183 | 1969,08,01,,,,9.19,,3.5,5.8 184 | 1969,09,01,,,,9.15,,3.7,6.0 185 | 1969,10,01,,,,9,-1.7,3.7,6.0 186 | 1969,11,01,,,,8.85,,3.5,5.9 187 | 1969,12,01,,,,8.97,,3.5,6.2 188 | 1970,01,01,,,,8.98,-0.7,3.9,6.2 189 | 1970,02,01,,,,8.98,,4.2,6.1 190 | 1970,03,01,,,,7.76,,4.4,6.1 191 | 1970,04,01,,,,8.1,0.7,4.6,5.8 192 | 1970,05,01,,,,7.94,,4.8,6.0 193 | 1970,06,01,,,,7.6,,4.9,6.5 194 | 1970,07,01,,,,7.21,3.6,5,6.2 195 | 1970,08,01,,,,6.61,,5.1,6.2 196 | 1970,09,01,,,,6.29,,5.4,6.2 197 | 1970,10,01,,,,6.2,-4,5.5,6.4 198 | 1970,11,01,,,,5.6,,5.9,6.6 199 | 1970,12,01,,,,4.9,,6.1,6.6 200 | 1971,01,01,,,,4.14,11.1,5.9,6.3 201 | 1971,02,01,,,,3.72,,5.9,5.8 202 | 1971,03,01,,,,3.71,,6,5.2 203 | 1971,04,01,,,,4.15,2.3,5.9,5.0 204 | 1971,05,01,,,,4.63,,5.9,5.2 205 | 1971,06,01,,,,4.91,,5.9,4.9 206 | 1971,07,01,,,,5.31,3.2,6,4.9 207 | 1971,08,01,,,,5.56,,6.1,4.6 208 | 1971,09,01,,,,5.55,,6,4.4 209 | 1971,10,01,,,,5.2,1.2,5.8,3.8 210 | 1971,11,01,,,,4.91,,6,3.3 211 | 1971,12,01,,,,4.14,,6,3.1 212 | 1972,01,01,,,,3.5,7.4,5.8,3.1 213 | 1972,02,01,,,,3.29,,5.7,3.3 214 | 1972,03,01,,,,3.83,,5.8,3.3 215 | 1972,04,01,,,,4.17,9.6,5.7,3.3 216 | 1972,05,01,,,,4.27,,5.7,3.1 217 | 1972,06,01,,,,4.46,,5.7,2.8 218 | 1972,07,01,,,,4.55,3.7,5.6,2.8 219 | 1972,08,01,,,,4.8,,5.6,3.3 220 | 1972,09,01,,,,4.87,,5.5,2.8 221 | 1972,10,01,,,,5.04,6.8,5.6,3.0 222 | 1972,11,01,,,,5.06,,5.3,3.0 223 | 1972,12,01,,,,5.33,,5.2,3.0 224 | 1973,01,01,,,,5.94,10.2,4.9,2.8 225 | 1973,02,01,,,,6.58,,5,2.8 226 | 1973,03,01,,,,7.09,,4.9,3.0 227 | 1973,04,01,,,,7.12,4.6,5,3.2 228 | 1973,05,01,,,,7.84,,4.9,3.2 229 | 1973,06,01,,,,8.49,,4.9,3.2 230 | 1973,07,01,,,,10.4,-2.2,4.8,3.2 231 | 1973,08,01,,,,10.5,,4.8,3.2 232 | 1973,09,01,,,,10.78,,4.8,3.8 233 | 1973,10,01,,,,10.01,3.8,4.6,4.3 234 | 1973,11,01,,,,10.03,,4.8,4.5 235 | 1973,12,01,,,,9.95,,4.9,4.7 236 | 1974,01,01,,,,9.65,-3.3,5.1,4.9 237 | 1974,02,01,,,,8.97,,5.2,5.4 238 | 1974,03,01,,,,9.35,,5.1,5.8 239 | 1974,04,01,,,,10.51,1.1,5.1,6.2 240 | 1974,05,01,,,,11.31,,5.1,6.8 241 | 1974,06,01,,,,11.93,,5.4,7.9 242 | 1974,07,01,,,,12.92,-3.8,5.5,8.8 243 | 1974,08,01,,,,12.01,,5.5,9.6 244 | 1974,09,01,,,,11.34,,5.9,10.2 245 | 1974,10,01,,,,10.06,-1.6,6,10.6 246 | 1974,11,01,,,,9.45,,6.6,11.2 247 | 1974,12,01,,,,8.53,,7.2,11.1 248 | 1975,01,01,,,,7.13,-4.7,8.1,11.5 249 | 1975,02,01,,,,6.24,,8.1,11.7 250 | 1975,03,01,,,,5.54,,8.6,11.4 251 | 1975,04,01,,,,5.49,3.1,8.8,11.3 252 | 1975,05,01,,,,5.22,,9,10.5 253 | 1975,06,01,,,,5.55,,8.8,9.6 254 | 1975,07,01,,,,6.1,6.8,8.6,9.1 255 | 1975,08,01,,,,6.14,,8.4,8.2 256 | 1975,09,01,,,,6.24,,8.4,7.7 257 | 1975,10,01,,,,5.82,5.5,8.4,7.0 258 | 1975,11,01,,,,5.22,,8.3,6.8 259 | 1975,12,01,,,,5.2,,8.2,6.7 260 | 1976,01,01,,,,4.87,9.3,7.9,6.7 261 | 1976,02,01,,,,4.77,,7.7,6.5 262 | 1976,03,01,,,,4.84,,7.6,6.6 263 | 1976,04,01,,,,4.82,3.1,7.7,6.4 264 | 1976,05,01,,,,5.29,,7.4,6.5 265 | 1976,06,01,,,,5.48,,7.6,6.5 266 | 1976,07,01,,,,5.31,2.1,7.8,6.7 267 | 1976,08,01,,,,5.29,,7.8,6.8 268 | 1976,09,01,,,,5.25,,7.6,6.8 269 | 1976,10,01,,,,5.02,3,7.7,6.7 270 | 1976,11,01,,,,4.95,,7.8,6.5 271 | 1976,12,01,,,,4.65,,7.8,6.1 272 | 1977,01,01,,,,4.61,4.7,7.5,6.3 273 | 1977,02,01,,,,4.68,,7.6,6.3 274 | 1977,03,01,,,,4.69,,7.4,6.2 275 | 1977,04,01,,,,4.73,8.1,7.2,6.3 276 | 1977,05,01,,,,5.35,,7,6.3 277 | 1977,06,01,,,,5.39,,7.2,6.6 278 | 1977,07,01,,,,5.42,7.3,6.9,6.3 279 | 1977,08,01,,,,5.9,,7,6.2 280 | 1977,09,01,,,,6.14,,6.8,6.2 281 | 1977,10,01,,,,6.47,0,6.8,6.0 282 | 1977,11,01,,,,6.51,,6.8,5.9 283 | 1977,12,01,,,,6.56,,6.4,6.5 284 | 1978,01,01,,,,6.7,1.4,6.4,6.4 285 | 1978,02,01,,,,6.78,,6.3,6.2 286 | 1978,03,01,,,,6.79,,6.3,6.3 287 | 1978,04,01,,,,6.89,16.5,6.1,6.5 288 | 1978,05,01,,,,7.36,,6,6.8 289 | 1978,06,01,,,,7.6,,5.9,7.0 290 | 1978,07,01,,,,7.81,4,6.2,7.4 291 | 1978,08,01,,,,8.04,,5.9,7.5 292 | 1978,09,01,,,,8.45,,6,7.9 293 | 1978,10,01,,,,8.96,5.5,5.8,8.4 294 | 1978,11,01,,,,9.76,,5.9,8.7 295 | 1978,12,01,,,,10.03,,6,8.5 296 | 1979,01,01,,,,10.07,0.8,5.9,8.6 297 | 1979,02,01,,,,10.06,,5.9,9.2 298 | 1979,03,01,,,,10.09,,5.8,9.3 299 | 1979,04,01,,,,10.01,0.5,5.8,9.3 300 | 1979,05,01,,,,10.24,,5.6,9.4 301 | 1979,06,01,,,,10.29,,5.7,9.3 302 | 1979,07,01,,,,10.47,2.9,5.7,9.6 303 | 1979,08,01,,,,10.94,,6,10.0 304 | 1979,09,01,,,,11.43,,5.9,9.9 305 | 1979,10,01,,,,13.77,1,6,10.1 306 | 1979,11,01,,,,13.18,,5.9,10.6 307 | 1979,12,01,,,,13.78,,6,11.3 308 | 1980,01,01,,,,13.82,1.3,6.3,12.0 309 | 1980,02,01,,,,14.13,,6.3,12.0 310 | 1980,03,01,,,,17.19,,6.3,12.5 311 | 1980,04,01,,,,17.61,-7.9,6.9,13.0 312 | 1980,05,01,,,,10.98,,7.5,13.3 313 | 1980,06,01,,,,9.47,,7.6,13.6 314 | 1980,07,01,,,,9.03,-0.6,7.8,12.4 315 | 1980,08,01,,,,9.61,,7.7,11.8 316 | 1980,09,01,,,,10.87,,7.5,12.0 317 | 1980,10,01,,,,12.81,7.6,7.5,12.3 318 | 1980,11,01,,,,15.85,,7.5,12.1 319 | 1980,12,01,,,,18.9,,7.2,12.2 320 | 1981,01,01,,,,19.08,8.5,7.5,11.4 321 | 1981,02,01,,,,15.93,,7.4,10.9 322 | 1981,03,01,,,,14.7,,7.4,10.0 323 | 1981,04,01,,,,15.72,-2.9,7.2,9.5 324 | 1981,05,01,,,,18.52,,7.5,9.5 325 | 1981,06,01,,,,19.1,,7.5,9.4 326 | 1981,07,01,,,,19.04,4.7,7.2,11.1 327 | 1981,08,01,,,,17.82,,7.4,11.6 328 | 1981,09,01,,,,15.87,,7.6,11.8 329 | 1981,10,01,,,,15.08,-4.6,7.9,10.9 330 | 1981,11,01,,,,13.31,,8.3,10.2 331 | 1981,12,01,,,,12.37,,8.5,9.5 332 | 1982,01,01,,,,13.22,-6.5,8.6,9.3 333 | 1982,02,01,,,,14.78,,8.9,9.1 334 | 1982,03,01,,,,14.68,,9,8.8 335 | 1982,04,01,,,,14.94,2.2,9.3,8.9 336 | 1982,05,01,,,,14.45,,9.4,8.7 337 | 1982,06,01,,,,14.15,,9.6,8.6 338 | 1982,07,01,,,,12.59,-1.4,9.8,7.6 339 | 1982,08,01,,,,10.12,,9.8,7.1 340 | 1982,09,01,,,,10.31,,10.1,5.9 341 | 1982,09,27,10.25,,,,,, 342 | 1982,10,01,10,,,9.71,0.4,10.4,5.9 343 | 1982,10,07,9.5,,,,,, 344 | 1982,11,01,9.5,,,9.2,,10.8,5.3 345 | 1982,11,19,9,,,,,, 346 | 1982,12,01,9,,,8.95,,10.8,4.5 347 | 1982,12,14,8.5,,,,,, 348 | 1983,01,01,8.5,,,8.68,5.3,10.4,4.7 349 | 1983,02,01,8.5,,,8.51,,10.4,4.7 350 | 1983,03,01,8.5,,,8.77,,10.3,4.7 351 | 1983,03,31,8.625,,,,,, 352 | 1983,04,01,8.625,,,8.8,9.4,10.2,4.3 353 | 1983,05,01,8.625,,,8.63,,10.1,3.6 354 | 1983,05,25,8.75,,,,,, 355 | 1983,06,01,8.75,,,8.98,,10.1,2.9 356 | 1983,06,24,9,,,,,, 357 | 1983,07,01,9,,,9.37,8.1,9.4,3.0 358 | 1983,07,14,9.25,,,,,, 359 | 1983,07,20,9.4375,,,,,, 360 | 1983,08,01,9.4375,,,9.56,,9.5,3.0 361 | 1983,08,11,9.5625,,,,,, 362 | 1983,08,17,9.5,,,,,, 363 | 1983,09,01,9.5,,,9.45,,9.2,3.5 364 | 1983,09,15,9.375,,,,,, 365 | 1983,10,01,9.375,,,9.48,8.5,8.8,3.7 366 | 1983,11,01,9.375,,,9.34,,8.5,4.3 367 | 1983,12,01,9.375,,,9.47,,8.3,4.8 368 | 1984,01,01,9.375,,,9.56,8.2,8,4.8 369 | 1984,02,01,9.375,,,9.59,,7.8,4.8 370 | 1984,03,01,9.375,,,9.91,,7.8,5.0 371 | 1984,03,29,10.5,,,,,, 372 | 1984,04,01,10.5,,,10.29,7.2,7.7,5.0 373 | 1984,05,01,10.5,,,10.32,,7.4,5.2 374 | 1984,06,01,10.5,,,11.06,,7.2,5.1 375 | 1984,07,01,10.5,,,11.23,4,7.5,5.0 376 | 1984,07,05,11,,,,,, 377 | 1984,07,19,11.25,,,,,, 378 | 1984,08,01,11.25,,,11.64,,7.5,5.1 379 | 1984,08,09,11.5,,,,,, 380 | 1984,09,01,11.5,,,11.3,,7.3,5.1 381 | 1984,09,20,11.25,,,,,, 382 | 1984,09,27,11,,,,,, 383 | 1984,10,01,11,,,9.99,3.2,7.4,4.9 384 | 1984,10,11,10.5,,,,,, 385 | 1984,10,18,10,,,,,, 386 | 1984,11,01,10,,,9.43,,7.2,4.6 387 | 1984,11,08,9.5,,,,,, 388 | 1984,11,23,9,,,,,, 389 | 1984,12,01,9,,,8.38,,7.3,4.7 390 | 1984,12,06,8.75,,,,,, 391 | 1984,12,19,8.5,,,,,, 392 | 1984,12,24,8.125,,,,,, 393 | 1985,01,01,8.125,,,8.35,4,7.3,4.5 394 | 1985,01,24,8.25,,,,,, 395 | 1985,02,01,8.25,,,8.5,,7.2,4.7 396 | 1985,02,14,8.375,,,,,, 397 | 1985,03,01,8.375,,,8.58,,7.2,4.8 398 | 1985,03,28,8.5,,,,,, 399 | 1985,04,01,8.5,,,8.27,3.7,7.3,4.5 400 | 1985,04,25,8.25,,,,,, 401 | 1985,05,01,8.25,,,7.97,,7.2,4.5 402 | 1985,05,20,7.75,,,,,, 403 | 1985,06,01,7.75,,,7.53,,7.4,4.4 404 | 1985,07,01,7.75,,,7.88,6.4,7.4,4.2 405 | 1985,07,11,7.6875,,,,,, 406 | 1985,08,01,7.75,,,7.9,,7.1,4.1 407 | 1985,08,21,7.8125,,,,,, 408 | 1985,09,01,7.8125,,,7.92,,7.1,4.0 409 | 1985,09,06,8,,,,,, 410 | 1985,10,01,8,,,7.99,3,7.1,4.1 411 | 1985,11,01,8,,,8.05,,7,4.4 412 | 1985,12,01,8,,,8.27,,7,4.3 413 | 1985,12,18,7.75,,,,,, 414 | 1986,01,01,7.75,,,8.14,3.8,6.7,4.4 415 | 1986,02,01,7.75,,,7.86,,7.2,4.2 416 | 1986,03,01,7.75,,,7.48,,7.2,4.1 417 | 1986,03,07,7.25,,,,,, 418 | 1986,04,01,7.25,,,6.99,1.9,7.1,4.2 419 | 1986,04,02,7.3125,,,,,, 420 | 1986,04,21,6.75,,,,,, 421 | 1986,05,01,6.75,,,6.85,,7.2,4.0 422 | 1986,05,22,6.8125,,,,,, 423 | 1986,06,01,6.8125,,,6.92,,7.2,4.0 424 | 1986,06,05,6.875,,,,,, 425 | 1986,07,01,6.875,,,6.56,4.1,7,4.1 426 | 1986,07,11,6.375,,,,,, 427 | 1986,08,01,6.375,,,6.17,,6.9,4.0 428 | 1986,08,21,5.875,,,,,, 429 | 1986,09,01,5.875,,,5.89,,7,4.1 430 | 1986,10,01,5.875,,,5.85,2.1,7,4.0 431 | 1986,11,01,5.875,,,6.04,,6.9,3.8 432 | 1986,12,01,5.875,,,6.91,,6.6,3.8 433 | 1987,01,01,5.875,,,6.43,2.8,6.6,3.8 434 | 1987,01,05,6,,,,,, 435 | 1987,02,01,6,,,6.1,,6.6,3.8 436 | 1987,03,01,6,,,6.13,,6.6,4.0 437 | 1987,04,01,6,,,6.37,4.6,6.3,4.2 438 | 1987,04,30,6.5,,,,,, 439 | 1987,05,01,6.5,,,6.85,,6.3,4.2 440 | 1987,05,22,6.75,,,,,, 441 | 1987,06,01,6.75,,,6.73,,6.2,4.1 442 | 1987,07,01,6.75,,,6.58,3.7,6.1,4.0 443 | 1987,07,02,6.625,,,,,, 444 | 1987,08,01,6.625,,,6.73,,6,4.2 445 | 1987,08,27,6.75,,,,,, 446 | 1987,09,01,6.75,,,7.22,,5.9,4.3 447 | 1987,09,03,6.875,,,,,, 448 | 1987,09,04,7.25,,,,,, 449 | 1987,09,24,7.3125,,,,,, 450 | 1987,10,01,7.3125,,,7.29,6.8,6,4.3 451 | 1987,11,01,7.3125,,,6.69,,5.8,4.4 452 | 1987,11,04,6.8125,,,,,, 453 | 1987,12,01,6.8125,,,6.77,,5.7,4.2 454 | 1988,01,01,6.8125,,,6.83,2.3,5.7,4.3 455 | 1988,01,28,6.625,,,,,, 456 | 1988,02,01,6.625,,,6.58,,5.7,4.3 457 | 1988,02,11,6.5,,,,,, 458 | 1988,03,01,6.5,,,6.58,,5.7,4.4 459 | 1988,03,30,6.75,,,,,, 460 | 1988,04,01,6.75,,,6.87,5.4,5.4,4.3 461 | 1988,05,01,6.75,,,7.09,,5.6,4.3 462 | 1988,05,09,7,,,,,, 463 | 1988,05,25,7.25,,,,,, 464 | 1988,06,01,7.25,,,7.51,,5.4,4.5 465 | 1988,06,22,7.4375,,,,,, 466 | 1988,07,01,7.5,,,7.75,2.3,5.4,4.5 467 | 1988,07,19,7.6875,,,,,, 468 | 1988,08,01,7.6875,,,8.01,,5.6,4.4 469 | 1988,08,08,7.75,,,,,, 470 | 1988,08,09,8.125,,,,,, 471 | 1988,09,01,8.125,,,8.19,,5.4,4.4 472 | 1988,10,01,8.125,,,8.3,5.4,5.4,4.5 473 | 1988,11,01,8.125,,,8.35,,5.3,4.4 474 | 1988,11,17,8.3125,,,,,, 475 | 1988,11,22,8.375,,,,,, 476 | 1988,12,01,8.375,,,8.76,,5.3,4.7 477 | 1988,12,15,8.6875,,,,,, 478 | 1989,01,01,8.6875,,,9.12,4.1,5.4,4.6 479 | 1989,01,05,9,,,,,, 480 | 1989,02,01,9,,,9.36,,5.2,4.8 481 | 1989,02,09,9.125,,,,,, 482 | 1989,02,14,9.3125,,,,,, 483 | 1989,02,24,9.75,,,,,, 484 | 1989,03,01,9.75,,,9.85,,5,4.7 485 | 1989,04,01,9.75,,,9.84,3.2,5.2,4.6 486 | 1989,05,01,9.75,,,9.81,,5.2,4.6 487 | 1989,05,17,9.8125,,,,,, 488 | 1989,06,01,9.8125,,,9.53,,5.3,4.5 489 | 1989,06,06,9.5625,,,,,, 490 | 1989,07,01,9.5625,,,9.24,3,5.2,4.6 491 | 1989,07,07,9.3125,,,,,, 492 | 1989,07,27,9.0625,,,,,, 493 | 1989,08,01,9.0625,,,8.99,,5.2,4.4 494 | 1989,09,01,9.0625,,,9.02,,5.3,4.3 495 | 1989,10,01,9.0625,,,8.84,0.9,5.3,4.3 496 | 1989,10,19,8.75,,,,,, 497 | 1989,11,01,8.75,,,8.55,,5.4,4.4 498 | 1989,11,06,8.5,,,,,, 499 | 1989,12,01,8.5,,,8.45,,5.4,4.4 500 | 1989,12,20,8.25,,,,,, 501 | 1990,01,01,8.25,,,8.23,4.5,5.4,4.4 502 | 1990,02,01,8.25,,,8.24,,5.3,4.6 503 | 1990,03,01,8.25,,,8.28,,5.2,4.9 504 | 1990,04,01,8.25,,,8.26,1.6,5.4,4.8 505 | 1990,05,01,8.25,,,8.18,,5.4,4.8 506 | 1990,06,01,8.25,,,8.29,,5.2,4.9 507 | 1990,07,01,8.25,,,8.15,0.1,5.5,5.0 508 | 1990,07,13,8,,,,,, 509 | 1990,08,01,8,,,8.13,,5.7,5.5 510 | 1990,09,01,8,,,8.2,,5.9,5.5 511 | 1990,10,01,8,,,8.11,-3.4,5.9,5.3 512 | 1990,10,29,7.75,,,,,, 513 | 1990,11,01,7.75,,,7.81,,6.2,5.3 514 | 1990,11,14,7.5,,,,,, 515 | 1990,12,01,7.5,,,7.31,,6.3,5.2 516 | 1990,12,07,7.25,,,,,, 517 | 1990,12,19,7,,,,,, 518 | 1991,01,01,7,,,6.91,-1.9,6.4,5.6 519 | 1991,01,09,6.75,,,,,, 520 | 1991,02,01,6.25,,,6.25,,6.6,5.6 521 | 1991,03,01,6.25,,,6.12,,6.8,5.2 522 | 1991,03,08,6,,,,,, 523 | 1991,04,01,6,,,5.91,3.1,6.7,5.1 524 | 1991,04,30,5.75,,,,,, 525 | 1991,05,01,5.75,,,5.78,,6.9,5.1 526 | 1991,06,01,5.75,,,5.9,,6.9,5.0 527 | 1991,07,01,5.75,,,5.82,1.9,6.8,4.8 528 | 1991,08,01,5.75,,,5.66,,6.9,4.6 529 | 1991,08,06,5.5,,,,,, 530 | 1991,09,01,5.5,,,5.45,,6.9,4.5 531 | 1991,09,13,5.25,,,,,, 532 | 1991,10,01,5.25,,,5.21,1.8,7,4.4 533 | 1991,10,31,5,,,,,, 534 | 1991,11,01,5,,,4.81,,7,4.5 535 | 1991,11,06,4.75,,,,,, 536 | 1991,12,01,4.75,,,4.43,,7.3,4.4 537 | 1991,12,06,4.5,,,,,, 538 | 1991,12,20,4,,,,,, 539 | 1992,01,01,4,,,4.03,4.8,7.3,3.9 540 | 1992,02,01,4,,,4.06,,7.4,3.8 541 | 1992,03,01,4,,,3.98,,7.4,3.9 542 | 1992,04,01,4,,,3.73,4.5,7.4,3.9 543 | 1992,04,09,3.75,,,,,, 544 | 1992,05,01,3.75,,,3.82,,7.6,3.8 545 | 1992,06,01,3.75,,,3.76,,7.8,3.8 546 | 1992,07,01,3.75,,,3.25,3.9,7.7,3.7 547 | 1992,07,02,3.25,,,,,, 548 | 1992,08,01,3.25,,,3.3,,7.6,3.5 549 | 1992,09,01,3.25,,,3.22,,7.6,3.3 550 | 1992,09,04,3,,,,,, 551 | 1992,10,01,3,,,3.1,4.1,7.3,3.5 552 | 1992,11,01,3,,,3.09,,7.4,3.4 553 | 1992,12,01,3,,,2.92,,7.4,3.3 554 | 1993,01,01,3,,,3.02,0.8,7.3,3.5 555 | 1993,02,01,3,,,3.03,,7.1,3.6 556 | 1993,03,01,3,,,3.07,,7,3.4 557 | 1993,04,01,3,,,2.96,2.4,7.1,3.5 558 | 1993,05,01,3,,,3,,7.1,3.4 559 | 1993,06,01,3,,,3.04,,7,3.3 560 | 1993,07,01,3,,,3.06,2,6.9,3.2 561 | 1993,08,01,3,,,3.03,,6.8,3.3 562 | 1993,09,01,3,,,3.09,,6.7,3.2 563 | 1993,10,01,3,,,2.99,5.4,6.8,3.0 564 | 1993,11,01,3,,,3.02,,6.6,3.1 565 | 1993,12,01,3,,,2.96,,6.5,3.2 566 | 1994,01,01,3,,,3.05,4,6.6,2.9 567 | 1994,02,01,3,,,3.25,,6.6,2.8 568 | 1994,02,04,3.25,,,,,, 569 | 1994,03,01,3.25,,,3.34,,6.5,2.9 570 | 1994,03,22,3.5,,,,,, 571 | 1994,04,01,3.5,,,3.56,5.6,6.4,2.8 572 | 1994,04,18,3.75,,,,,, 573 | 1994,05,01,3.75,,,4.01,,6.1,2.8 574 | 1994,05,17,4.25,,,,,, 575 | 1994,06,01,4.25,,,4.25,,6.1,2.9 576 | 1994,07,01,4.25,,,4.26,2.4,6.1,2.9 577 | 1994,08,01,4.25,,,4.47,,6,2.9 578 | 1994,08,16,4.75,,,,,, 579 | 1994,09,01,4.75,,,4.73,,5.9,3.0 580 | 1994,10,01,4.75,,,4.76,4.6,5.8,2.9 581 | 1994,11,01,4.75,,,5.29,,5.6,2.8 582 | 1994,11,15,5.5,,,,,, 583 | 1994,12,01,5.5,,,5.45,,5.5,2.6 584 | 1995,01,01,5.5,,,5.53,1.4,5.6,2.9 585 | 1995,02,01,6,,,5.92,,5.4,3.0 586 | 1995,03,01,6,,,5.98,,5.4,3.0 587 | 1995,04,01,6,,,6.05,1.4,5.8,3.1 588 | 1995,05,01,6,,,6.01,,5.6,3.1 589 | 1995,06,01,6,,,6,,5.6,3.0 590 | 1995,07,01,6,,,5.85,3.5,5.7,3.0 591 | 1995,07,06,5.75,,,,,, 592 | 1995,08,01,5.75,,,5.74,,5.7,2.9 593 | 1995,09,01,5.75,,,5.8,,5.6,2.9 594 | 1995,10,01,5.75,,,5.76,2.9,5.5,3.0 595 | 1995,11,01,5.75,,,5.8,,5.6,3.0 596 | 1995,12,01,5.75,,,5.6,,5.6,3.0 597 | 1995,12,19,5.5,,,,,, 598 | 1996,01,01,5.5,,,5.56,2.7,5.6,3.0 599 | 1996,01,31,5.25,,,,,, 600 | 1996,02,01,5.25,,,5.22,,5.5,2.9 601 | 1996,03,01,5.25,,,5.31,,5.5,2.8 602 | 1996,04,01,5.25,,,5.22,7.2,5.6,2.7 603 | 1996,05,01,5.25,,,5.24,,5.6,2.7 604 | 1996,06,01,5.25,,,5.27,,5.3,2.7 605 | 1996,07,01,5.25,,,5.4,3.7,5.5,2.7 606 | 1996,08,01,5.25,,,5.22,,5.1,2.6 607 | 1996,09,01,5.25,,,5.3,,5.2,2.7 608 | 1996,10,01,5.25,,,5.24,4.3,5.2,2.6 609 | 1996,11,01,5.25,,,5.31,,5.4,2.6 610 | 1996,12,01,5.25,,,5.29,,5.4,2.6 611 | 1997,01,01,5.25,,,5.25,3.1,5.3,2.5 612 | 1997,02,01,5.25,,,5.19,,5.2,2.5 613 | 1997,03,01,5.25,,,5.39,,5.2,2.5 614 | 1997,03,25,5.5,,,,,, 615 | 1997,04,01,5.5,,,5.51,6.2,5.1,2.7 616 | 1997,05,01,5.5,,,5.5,,4.9,2.5 617 | 1997,06,01,5.5,,,5.56,,5,2.4 618 | 1997,07,01,5.5,,,5.52,5.2,4.9,2.4 619 | 1997,08,01,5.5,,,5.54,,4.8,2.3 620 | 1997,09,01,5.5,,,5.54,,4.9,2.2 621 | 1997,10,01,5.5,,,5.5,3.1,4.7,2.3 622 | 1997,11,01,5.5,,,5.52,,4.6,2.2 623 | 1997,12,01,5.5,,,5.5,,4.7,2.2 624 | 1998,01,01,5.5,,,5.56,4,4.6,2.2 625 | 1998,02,01,5.5,,,5.51,,4.6,2.3 626 | 1998,03,01,5.5,,,5.49,,4.7,2.1 627 | 1998,04,01,5.5,,,5.45,3.9,4.3,2.1 628 | 1998,05,01,5.5,,,5.49,,4.4,2.2 629 | 1998,06,01,5.5,,,5.56,,4.5,2.2 630 | 1998,07,01,5.5,,,5.54,5.3,4.5,2.2 631 | 1998,08,01,5.5,,,5.55,,4.5,2.5 632 | 1998,09,01,5.5,,,5.51,,4.6,2.5 633 | 1998,09,29,5.25,,,,,, 634 | 1998,10,01,5.25,,,5.07,6.7,4.5,2.3 635 | 1998,10,15,5,,,,,, 636 | 1998,11,01,5,,,4.83,,4.4,2.3 637 | 1998,11,17,4.75,,,,,, 638 | 1998,12,01,4.75,,,4.68,,4.4,2.4 639 | 1999,01,01,4.75,,,4.63,3.2,4.3,2.4 640 | 1999,02,01,4.75,,,4.76,,4.4,2.1 641 | 1999,03,01,4.75,,,4.81,,4.2,2.1 642 | 1999,04,01,4.75,,,4.74,3.3,4.3,2.2 643 | 1999,05,01,4.75,,,4.74,,4.2,2.0 644 | 1999,06,01,4.75,,,4.76,,4.3,2.1 645 | 1999,06,30,5,,,,,, 646 | 1999,07,01,5,,,4.99,5.1,4.3,2.1 647 | 1999,08,01,5,,,5.07,,4.2,1.9 648 | 1999,08,24,5.25,,,,,, 649 | 1999,09,01,5.25,,,5.22,,4.2,2.0 650 | 1999,10,01,5.25,,,5.2,7.1,4.1,2.1 651 | 1999,11,01,5.25,,,5.42,,4.1,2.1 652 | 1999,11,16,5.5,,,,,, 653 | 1999,12,01,5.5,,,5.3,,4,1.9 654 | 2000,01,01,5.5,,,5.45,1.2,4,2.0 655 | 2000,02,01,5.5,,,5.73,,4.1,2.2 656 | 2000,02,02,5.75,,,,,, 657 | 2000,03,01,5.75,,,5.85,,4,2.4 658 | 2000,03,21,6,,,,,, 659 | 2000,04,01,6,,,6.02,7.8,3.8,2.3 660 | 2000,05,01,6,,,6.27,,4,2.4 661 | 2000,05,16,6.5,,,,,, 662 | 2000,06,01,6.5,,,6.53,,4,2.5 663 | 2000,07,01,6.5,,,6.54,0.5,4,2.5 664 | 2000,08,01,6.5,,,6.5,,4.1,2.6 665 | 2000,09,01,6.5,,,6.52,,3.9,2.6 666 | 2000,10,01,6.5,,,6.51,2.3,3.9,2.5 667 | 2000,11,01,6.5,,,6.51,,3.9,2.6 668 | 2000,12,01,6.5,,,6.4,,3.9,2.6 669 | 2001,01,01,6.5,,,5.98,-1.1,4.2,2.6 670 | 2001,01,03,6,,,,,, 671 | 2001,01,31,5.5,,,,,, 672 | 2001,02,01,5.5,,,5.49,,4.2,2.7 673 | 2001,03,01,5.5,,,5.31,,4.3,2.7 674 | 2001,03,20,5,,,,,, 675 | 2001,04,01,5,,,4.8,2.1,4.4,2.6 676 | 2001,04,18,4.5,,,,,, 677 | 2001,05,01,4.5,,,4.21,,4.3,2.5 678 | 2001,05,15,4,,,,,, 679 | 2001,06,01,4,,,3.97,,4.5,2.7 680 | 2001,06,27,3.75,,,,,, 681 | 2001,07,01,3.75,,,3.77,-1.3,4.6,2.7 682 | 2001,08,01,3.75,,,3.65,,4.9,2.7 683 | 2001,08,21,3.5,,,,,, 684 | 2001,09,01,3.5,,,3.07,,5,2.6 685 | 2001,09,17,3,,,,,, 686 | 2001,10,01,3,,,2.49,1.1,5.3,2.6 687 | 2001,10,02,2.5,,,,,, 688 | 2001,11,01,2.5,,,2.09,,5.5,2.8 689 | 2001,11,06,2,,,,,, 690 | 2001,12,01,2,,,1.82,,5.7,2.7 691 | 2001,12,11,1.75,,,,,, 692 | 2002,01,01,1.75,,,1.73,3.7,5.7,2.6 693 | 2002,02,01,1.75,,,1.74,,5.7,2.6 694 | 2002,03,01,1.75,,,1.73,,5.7,2.4 695 | 2002,04,01,1.75,,,1.75,2.2,5.9,2.5 696 | 2002,05,01,1.75,,,1.75,,5.8,2.5 697 | 2002,06,01,1.75,,,1.75,,5.8,2.3 698 | 2002,07,01,1.75,,,1.73,2,5.8,2.2 699 | 2002,08,01,1.75,,,1.74,,5.7,2.4 700 | 2002,09,01,1.75,,,1.75,,5.7,2.2 701 | 2002,10,01,1.75,,,1.75,0.3,5.7,2.2 702 | 2002,11,01,1.75,,,1.34,,5.9,2.0 703 | 2002,11,06,1.25,,,,,, 704 | 2002,12,01,1.25,,,1.24,,6,1.9 705 | 2003,01,01,1.25,,,1.24,2.1,5.8,1.9 706 | 2003,02,01,1.25,,,1.26,,5.9,1.7 707 | 2003,03,01,1.25,,,1.25,,5.9,1.7 708 | 2003,04,01,1.25,,,1.26,3.8,6,1.5 709 | 2003,05,01,1.25,,,1.26,,6.1,1.6 710 | 2003,06,01,1.25,,,1.22,,6.3,1.5 711 | 2003,06,25,1,,,,,, 712 | 2003,07,01,1,,,1.01,6.9,6.2,1.5 713 | 2003,08,01,1,,,1.03,,6.1,1.3 714 | 2003,09,01,1,,,1.01,,6.1,1.2 715 | 2003,10,01,1,,,1.01,4.8,6,1.3 716 | 2003,11,01,1,,,1,,5.8,1.1 717 | 2003,12,01,1,,,0.98,,5.7,1.1 718 | 2004,01,01,1,,,1,2.3,5.7,1.1 719 | 2004,02,01,1,,,1.01,,5.6,1.2 720 | 2004,03,01,1,,,1,,5.8,1.6 721 | 2004,04,01,1,,,1,3,5.6,1.8 722 | 2004,05,01,1,,,1,,5.6,1.7 723 | 2004,06,01,1,,,1.03,,5.6,1.9 724 | 2004,06,30,1.25,,,,,, 725 | 2004,07,01,1.25,,,1.26,3.7,5.5,1.8 726 | 2004,08,01,1.25,,,1.43,,5.4,1.7 727 | 2004,08,10,1.5,,,,,, 728 | 2004,09,01,1.5,,,1.61,,5.4,2.0 729 | 2004,09,21,1.75,,,,,, 730 | 2004,10,01,1.75,,,1.76,3.5,5.5,2.0 731 | 2004,11,01,1.75,,,1.93,,5.4,2.2 732 | 2004,11,10,2,,,,,, 733 | 2004,12,01,2,,,2.16,,5.4,2.2 734 | 2004,12,14,2.25,,,,,, 735 | 2005,01,01,2.25,,,2.28,4.3,5.3,2.3 736 | 2005,02,01,2.25,,,2.5,,5.4,2.4 737 | 2005,02,02,2.5,,,,,, 738 | 2005,03,01,2.5,,,2.63,,5.2,2.3 739 | 2005,03,22,2.75,,,,,, 740 | 2005,04,01,2.75,,,2.79,2.1,5.2,2.2 741 | 2005,05,01,2.75,,,3,,5.1,2.2 742 | 2005,05,03,3,,,,,, 743 | 2005,06,01,3,,,3.04,,5,2.0 744 | 2005,06,30,3.25,,,,,, 745 | 2005,07,01,3.25,,,3.26,3.4,5,2.1 746 | 2005,08,01,3.25,,,3.5,,4.9,2.1 747 | 2005,08,09,3.5,,,,,, 748 | 2005,09,01,3.5,,,3.62,,5,2.0 749 | 2005,09,20,3.75,,,,,, 750 | 2005,10,01,3.75,,,3.78,2.3,5,2.1 751 | 2005,11,01,4,,,4,,5,2.1 752 | 2005,12,01,4,,,4.16,,4.9,2.2 753 | 2005,12,13,4.25,,,,,, 754 | 2006,01,01,4.25,,,4.29,4.9,4.7,2.1 755 | 2006,01,31,4.5,,,,,, 756 | 2006,02,01,4.5,,,4.49,,4.8,2.1 757 | 2006,03,01,4.5,,,4.59,,4.7,2.1 758 | 2006,03,28,4.75,,,,,, 759 | 2006,04,01,4.75,,,4.79,1.2,4.7,2.3 760 | 2006,05,01,4.75,,,4.94,,4.6,2.4 761 | 2006,05,10,5,,,,,, 762 | 2006,06,01,5,,,4.99,,4.6,2.6 763 | 2006,06,29,5.25,,,,,, 764 | 2006,07,01,5.25,,,5.24,0.4,4.7,2.7 765 | 2006,08,01,5.25,,,5.25,,4.7,2.8 766 | 2006,09,01,5.25,,,5.25,,4.5,2.9 767 | 2006,10,01,5.25,,,5.25,3.2,4.4,2.7 768 | 2006,11,01,5.25,,,5.25,,4.5,2.6 769 | 2006,12,01,5.25,,,5.24,,4.4,2.6 770 | 2007,01,01,5.25,,,5.25,0.2,4.6,2.7 771 | 2007,02,01,5.25,,,5.26,,4.5,2.7 772 | 2007,03,01,5.25,,,5.26,,4.4,2.5 773 | 2007,04,01,5.25,,,5.25,3.1,4.5,2.3 774 | 2007,05,01,5.25,,,5.25,,4.4,2.2 775 | 2007,06,01,5.25,,,5.25,,4.6,2.2 776 | 2007,07,01,5.25,,,5.26,2.7,4.7,2.2 777 | 2007,08,01,5.25,,,5.02,,4.6,2.1 778 | 2007,09,01,5.25,,,4.94,,4.7,2.1 779 | 2007,09,18,4.75,,,,,, 780 | 2007,10,01,4.75,,,4.76,1.4,4.7,2.2 781 | 2007,10,31,4.5,,,,,, 782 | 2007,11,01,4.5,,,4.49,,4.7,2.3 783 | 2007,12,01,4.5,,,4.24,,5,2.4 784 | 2007,12,11,4.25,,,,,, 785 | 2008,01,01,4.25,,,3.94,-2.7,5,2.5 786 | 2008,01,22,3.5,,,,,, 787 | 2008,01,30,3,,,,,, 788 | 2008,02,01,3,,,2.98,,4.9,2.3 789 | 2008,03,01,3,,,2.61,,5.1,2.4 790 | 2008,03,18,2.25,,,,,, 791 | 2008,04,01,2.25,,,2.28,2,5,2.3 792 | 2008,04,30,2,,,,,, 793 | 2008,05,01,2,,,1.98,,5.4,2.3 794 | 2008,06,01,2,,,2,,5.6,2.4 795 | 2008,07,01,2,,,2.01,-1.9,5.8,2.5 796 | 2008,08,01,2,,,2,,6.1,2.5 797 | 2008,09,01,2,,,1.81,,6.1,2.5 798 | 2008,10,01,2,,,0.97,-8.2,6.5,2.2 799 | 2008,10,08,1.5,,,,,, 800 | 2008,10,29,1,,,,,, 801 | 2008,11,01,1,,,0.39,,6.8,2.0 802 | 2008,12,01,1,,,0.16,,7.3,1.8 803 | 2008,12,16,,0.25,0,,,, 804 | 2009,01,01,,0.25,0,0.15,-5.4,7.8,1.7 805 | 2009,02,01,,0.25,0,0.22,,8.3,1.8 806 | 2009,03,01,,0.25,0,0.18,,8.7,1.8 807 | 2009,04,01,,0.25,0,0.15,-0.5,9,1.9 808 | 2009,05,01,,0.25,0,0.18,,9.4,1.8 809 | 2009,06,01,,0.25,0,0.21,,9.5,1.7 810 | 2009,07,01,,0.25,0,0.16,1.3,9.5,1.5 811 | 2009,08,01,,0.25,0,0.16,,9.6,1.4 812 | 2009,09,01,,0.25,0,0.15,,9.8,1.5 813 | 2009,10,01,,0.25,0,0.12,3.9,10,1.7 814 | 2009,11,01,,0.25,0,0.12,,9.9,1.7 815 | 2009,12,01,,0.25,0,0.12,,9.9,1.8 816 | 2010,01,01,,0.25,0,0.11,1.7,9.8,1.6 817 | 2010,02,01,,0.25,0,0.13,,9.8,1.3 818 | 2010,03,01,,0.25,0,0.16,,9.9,1.1 819 | 2010,04,01,,0.25,0,0.2,3.9,9.9,0.9 820 | 2010,05,01,,0.25,0,0.2,,9.6,0.9 821 | 2010,06,01,,0.25,0,0.18,,9.4,0.9 822 | 2010,07,01,,0.25,0,0.18,2.7,9.4,0.9 823 | 2010,08,01,,0.25,0,0.19,,9.5,0.9 824 | 2010,09,01,,0.25,0,0.19,,9.5,0.8 825 | 2010,10,01,,0.25,0,0.19,2.5,9.4,0.6 826 | 2010,11,01,,0.25,0,0.19,,9.8,0.8 827 | 2010,12,01,,0.25,0,0.18,,9.3,0.8 828 | 2011,01,01,,0.25,0,0.17,-1.5,9.1,1.0 829 | 2011,02,01,,0.25,0,0.16,,9,1.1 830 | 2011,03,01,,0.25,0,0.14,,9,1.2 831 | 2011,04,01,,0.25,0,0.1,2.9,9.1,1.3 832 | 2011,05,01,,0.25,0,0.09,,9,1.5 833 | 2011,06,01,,0.25,0,0.09,,9.1,1.6 834 | 2011,07,01,,0.25,0,0.07,0.8,9,1.8 835 | 2011,08,01,,0.25,0,0.1,,9,2.0 836 | 2011,09,01,,0.25,0,0.08,,9,2.0 837 | 2011,10,01,,0.25,0,0.07,4.6,8.8,2.1 838 | 2011,11,01,,0.25,0,0.08,,8.6,2.2 839 | 2011,12,01,,0.25,0,0.07,,8.5,2.2 840 | 2012,01,01,,0.25,0,0.08,2.7,8.3,2.3 841 | 2012,02,01,,0.25,0,0.1,,8.3,2.2 842 | 2012,03,01,,0.25,0,0.13,,8.2,2.3 843 | 2012,04,01,,0.25,0,0.14,1.9,8.2,2.3 844 | 2012,05,01,,0.25,0,0.16,,8.2,2.3 845 | 2012,06,01,,0.25,0,0.16,,8.2,2.2 846 | 2012,07,01,,0.25,0,0.16,0.5,8.2,2.1 847 | 2012,08,01,,0.25,0,0.13,,8.1,1.9 848 | 2012,09,01,,0.25,0,0.14,,7.8,2.0 849 | 2012,10,01,,0.25,0,0.16,0.1,7.8,2.0 850 | 2012,11,01,,0.25,0,0.16,,7.7,1.9 851 | 2012,12,01,,0.25,0,0.16,,7.9,1.9 852 | 2013,01,01,,0.25,0,0.14,2.8,8,1.9 853 | 2013,02,01,,0.25,0,0.15,,7.7,2.0 854 | 2013,03,01,,0.25,0,0.14,,7.5,1.9 855 | 2013,04,01,,0.25,0,0.15,0.8,7.6,1.7 856 | 2013,05,01,,0.25,0,0.11,,7.5,1.7 857 | 2013,06,01,,0.25,0,0.09,,7.5,1.6 858 | 2013,07,01,,0.25,0,0.09,3.1,7.3,1.7 859 | 2013,08,01,,0.25,0,0.08,,7.3,1.8 860 | 2013,09,01,,0.25,0,0.08,,7.2,1.7 861 | 2013,10,01,,0.25,0,0.09,4,7.2,1.7 862 | 2013,11,01,,0.25,0,0.08,,6.9,1.7 863 | 2013,12,01,,0.25,0,0.09,,6.7,1.7 864 | 2014,01,01,,0.25,0,0.07,-1.2,6.6,1.6 865 | 2014,02,01,,0.25,0,0.07,,6.7,1.6 866 | 2014,03,01,,0.25,0,0.08,,6.7,1.7 867 | 2014,04,01,,0.25,0,0.09,4,6.2,1.8 868 | 2014,05,01,,0.25,0,0.09,,6.3,2.0 869 | 2014,06,01,,0.25,0,0.1,,6.1,1.9 870 | 2014,07,01,,0.25,0,0.09,5,6.2,1.9 871 | 2014,08,01,,0.25,0,0.09,,6.2,1.7 872 | 2014,09,01,,0.25,0,0.09,,5.9,1.7 873 | 2014,10,01,,0.25,0,0.09,2.3,5.7,1.8 874 | 2014,11,01,,0.25,0,0.09,,5.8,1.7 875 | 2014,12,01,,0.25,0,0.12,,5.6,1.6 876 | 2015,01,01,,0.25,0,0.11,2,5.7,1.6 877 | 2015,02,01,,0.25,0,0.11,,5.5,1.7 878 | 2015,03,01,,0.25,0,0.11,,5.4,1.8 879 | 2015,04,01,,0.25,0,0.12,2.6,5.4,1.8 880 | 2015,05,01,,0.25,0,0.12,,5.5,1.7 881 | 2015,06,01,,0.25,0,0.13,,5.3,1.8 882 | 2015,07,01,,0.25,0,0.13,2,5.2,1.8 883 | 2015,08,01,,0.25,0,0.14,,5.1,1.8 884 | 2015,09,01,,0.25,0,0.14,,5,1.9 885 | 2015,10,01,,0.25,0,0.12,0.9,5,1.9 886 | 2015,11,01,,0.25,0,0.12,,5,2.0 887 | 2015,12,01,,0.25,0,0.24,,5,2.1 888 | 2015,12,16,,0.5,0.25,,,, 889 | 2016,01,01,,0.5,0.25,0.34,0.8,4.9,2.2 890 | 2016,02,01,,0.5,0.25,0.38,,4.9,2.3 891 | 2016,03,01,,0.5,0.25,0.36,,5,2.2 892 | 2016,04,01,,0.5,0.25,0.37,1.4,5,2.1 893 | 2016,05,01,,0.5,0.25,0.37,,4.7,2.2 894 | 2016,06,01,,0.5,0.25,0.38,,4.9,2.2 895 | 2016,07,01,,0.5,0.25,0.39,3.5,4.9,2.2 896 | 2016,08,01,,0.5,0.25,0.4,,4.9,2.3 897 | 2016,09,01,,0.5,0.25,0.4,,4.9,2.2 898 | 2016,10,01,,0.5,0.25,0.4,1.9,4.8,2.1 899 | 2016,11,01,,0.5,0.25,0.41,,4.6,2.1 900 | 2016,12,01,,0.5,0.25,0.54,,4.7,2.2 901 | 2016,12,14,,0.75,0.5,,,, 902 | 2017,01,01,,0.75,0.5,0.65,,4.8,2.3 903 | 2017,02,01,,0.75,0.5,0.66,,4.7,2.2 904 | 2017,03,01,,0.75,0.5,,,, 905 | 2017,03,16,,1,0.75,,,, 906 | -------------------------------------------------------------------------------- /data/binance_1D.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmarienko/QuantTradingResearch/8d324b8c54935b36196f157d8dd88469646645bf/data/binance_1D.db -------------------------------------------------------------------------------- /models/helpers.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | from datetime import datetime 4 | import copy 5 | import matplotlib.pyplot as plt 6 | 7 | from tools.utils.utils import mstruct, red, green, yellow, blue, magenta, cyan, white 8 | from tools.charting.plot_helpers import sbp 9 | from tools.analysis.data import retain_columns_and_join 10 | from tools.analysis.tools import scols, srows, ohlc_resample, roll 11 | 12 | 13 | def tracking_error(benchmark, tracker, mode='returns'): 14 | """ 15 | Tracking error in percents 16 | """ 17 | # we want to compare only common date intervals so bit filtering here 18 | if mode.startswith('price'): 19 | # converting to returns 20 | f = pd.concat((benchmark.pct_change(), tracker.pct_change()), axis=1, keys=['X', 'Y']).dropna() 21 | else: 22 | # data already contains returns so not need to convert it 23 | f = pd.concat((benchmark, tracker), axis=1, keys=['X', 'Y']).dropna() 24 | return 100 * np.std(f.X - f.Y, ddof=1) 25 | 26 | 27 | def prices_to_returns(prices): 28 | return prices.pct_change()[1:] # drop first inf 29 | 30 | 31 | def returns_to_prices(rets): 32 | return (rets + 1).cumprod() - 1 33 | 34 | 35 | def norm(x): 36 | return x / x.iloc[0] 37 | 38 | 39 | class Model: 40 | """ 41 | Abstract class for any tracking models 42 | """ 43 | def __init__(self, description=None): 44 | self.description = description 45 | 46 | def fit(self, x, y, **kwargs): 47 | return self 48 | 49 | def predict(self, x, y=None, **kwargs): 50 | return None 51 | 52 | 53 | class TrackingModel: 54 | def __init__(self, data, index_name, train_date): 55 | self.data = data 56 | self.closes = retain_columns_and_join(data, 'close') 57 | self.index = data[index_name] 58 | self.index_closes = self.index.close 59 | self.X_price = self.closes[self.closes.columns[~self.closes.columns.str.match(index_name)]] 60 | self.Y_price = self.closes[index_name] 61 | self.X_ret, self.Y_ret = prices_to_returns(self.X_price), prices_to_returns(self.Y_price) 62 | self.train_date = pd.Timestamp(train_date) 63 | self.selection = None 64 | 65 | def select(self, selection): 66 | n = copy.copy(self) 67 | n.selection = set(n.data.keys()) & set(selection) 68 | return n 69 | 70 | def get_data(self, mode='prices', where='all data'): 71 | x, y = (self.X_price, self.Y_price) if mode.startswith('price') else (self.X_ret, self.Y_ret) 72 | if where.startswith('train'): 73 | x, y = x[:self.train_date], y[:self.train_date] 74 | elif where.startswith('test'): 75 | x, y = x[self.train_date:], y[self.train_date:] 76 | return (x[self.selection], y) if self.selection is not None else (x, y) 77 | 78 | def train_set(self, mode='prices'): 79 | return self.get_data(mode, 'train') 80 | 81 | def test_set(self, mode='prices'): 82 | return self.get_data(mode, 'test') 83 | 84 | def estimate(self, tracker: Model, on='prices', **kwargs): 85 | xn, yn = self.train_set(mode=on) 86 | m = tracker.fit(xn, yn) 87 | yn_h = m.predict(xn, y=yn, **kwargs) if isinstance(m, Model) else m.predict(xn, **kwargs) 88 | yn_h = pd.Series(yn_h, index=xn.index) if isinstance(yn_h, np.ndarray) else yn_h 89 | 90 | xt, yt = self.test_set(mode=on) 91 | yt_h = m.predict(xt, y=yt, **kwargs) if isinstance(m, Model) else m.predict(xt, **kwargs) 92 | yt_h = pd.Series(yt_h, index=xt.index) if isinstance(yt_h, np.ndarray) else yt_h 93 | 94 | return mstruct( 95 | model=m, 96 | description=tracker.description, 97 | mode=on, 98 | train = mstruct( 99 | x = xn, y = yn, yh = yn_h, w = tracking_error(yn, yn_h, mode=on) 100 | ), 101 | test = mstruct( 102 | x = xt, y = yt, yh = yt_h, w = tracking_error(yt, yt_h, mode=on) 103 | ) 104 | ) 105 | 106 | 107 | def plot_results(m: mstruct): 108 | """ 109 | Plot results for train/test periods with it's tracking errors 110 | """ 111 | yn, yn_h = (m.train.y, m.train.yh) if m.mode.startswith('price') else (norm(returns_to_prices(m.train.y)), norm(returns_to_prices(m.train.yh))) 112 | yt, yt_h = (m.test.y, m.test.yh) if m.mode.startswith('price') else (norm(returns_to_prices(m.test.y)), norm(returns_to_prices(m.test.yh))) 113 | sbp(13, 1, c=2); 114 | plt.plot(yn, lw=1, c='blue', label='Index') 115 | plt.plot(yn_h, lw=1, ls='--', c='g', label='Tracking Model') 116 | plt.legend(loc=2) 117 | plt.title(f'{m.description}: InSample: $\omega$: {m.train.w: 0.2f}') 118 | 119 | sbp(13, 3, c=1); 120 | plt.plot(yt, lw=1, c='blue', label='Index') 121 | plt.plot(yt_h, lw=1, ls='--', c='g', label='Tracking Model') 122 | plt.legend(loc=2) 123 | plt.title(f'{m.description}: OutOfSample: $\omega$: {m.test.w: 0.2f}') 124 | print(yellow(f' -> TE (train): {m.train.w:.2f}%'), ' | ', green(f'TE (test): {m.test.w:.2f}%')) 125 | 126 | 127 | def arbitrage_trading(kfm, Z_entry, Z_exit, traded_size): 128 | """ 129 | Simple arbitrage strategy for KF 130 | """ 131 | 132 | from ira.simulator.utils import shift_signals 133 | # z-score 134 | z = kfm.model.err / kfm.model.var 135 | 136 | # take position on weighted portfolio 137 | shorts, longs = z >= Z_entry, z <= -Z_entry 138 | shorts_exits, longs_exits = ((z <= -Z_exit) & (z > -Z_entry)), ((z >= Z_exit) & (z < Z_entry)) 139 | 140 | # we need to normalize portfolio weights 141 | b_a_w = kfm.model.b.div(kfm.model.b.sum(axis=1), axis=0) 142 | 143 | signals = pd.concat(( 144 | -b_a_w[longs], b_a_w[longs_exits] * 0, 145 | b_a_w[shorts], b_a_w[shorts_exits] * 0, 146 | ), axis=0).sort_index() 147 | 148 | # take opposite position on Index 149 | signals['^RUI'] = 0 150 | signals.loc[longs, '^RUI'] = +1 151 | signals.loc[shorts, '^RUI'] = -1 152 | 153 | # we will trade 1000 shares per stock and execute on daily close 154 | signals = shift_signals(np.floor(traded_size * signals), hours=15, minutes=59) 155 | return signals.astype(int) 156 | -------------------------------------------------------------------------------- /models/initlibs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Just simple starter script to injecting local libraries into notebook. 3 | """ 4 | import os, sys, os.path as path 5 | 6 | if len(sys.argv) > 1: 7 | project = sys.argv[1] 8 | else: 9 | raise ValueError("Project name must be specified !") 10 | 11 | c_path = os.getcwd() 12 | if project in c_path: 13 | __project_path = path.join(path.abspath(c_path[:c_path.find(project)]), project) 14 | sys.path.insert(0, __project_path) 15 | del c_path, project 16 | else: 17 | raise ValueError(f"Can't find path for specified project '{project}'") 18 | 19 | from tools.charting.plot_helpers import * 20 | from tools.analysis.timeseries import * 21 | from tools.analysis.data import make_forward_returns_matrix, permutate_params, retain_columns_and_join 22 | from tools.analysis.tools import ( 23 | scols, srows, drop_duplicated_indexes, apply_to_frame, ohlc_resample, roll 24 | ) 25 | from tools.utils.utils import mstruct, red, green, yellow, blue, magenta, cyan, white, dict2struct 26 | 27 | from tqdm.notebook import tqdm 28 | 29 | import seaborn as sns 30 | import pandas as dp 31 | import numpy as np 32 | 33 | # if second agrument specified we use it as theme name 34 | if len(sys.argv) > 2: 35 | setup_mpl_theme(sys.argv[2]) -------------------------------------------------------------------------------- /models/kalman_filter_adjust.py: -------------------------------------------------------------------------------- 1 | # The state estimate depends on the choice of parameter matrices F, H, Q and R. 2 | # These can be estimated by maximizing the Gaussian log likelihood function. 3 | #$$ 4 | #\mathscr{L} = -\frac{1}{2} (N \ln(2\pi) + \ln(\sigma) + \frac{\varepsilon^2}{\sigma}) 5 | #$$ 6 | #N - number of observations 7 | 8 | pmts = permutate_params({ 9 | 'vb': list(np.logspace(-5, 2, 10)), 10 | 'vm': list(np.logspace(-5, 2, 10)), 11 | }) 12 | 13 | N = len(x_train) 14 | ll_max, best = -np.inf, None 15 | for p in pmts: 16 | b, err, ev = kalman_regression_estimator(x_train.values, y_train.values, **p, intercept=False) 17 | 18 | err = err[50:] 19 | ev = ev[50:] 20 | ll = -0.5*(N * np.log(2*np.pi) + np.log(ev) + err**2/ev).sum() 21 | if ll > ll_max: 22 | ll_max = ll 23 | best = p 24 | best 25 | 26 | b, err, evars = kalman_regression_estimator(X[constituents].values, Y.values, **best, intercept=False) 27 | cut_off = slice(15, None) 28 | e_s = pd.Series(err, index=Y.index)[cut_off] 29 | v_s = pd.Series(np.sqrt(evars), index=Y.index)[cut_off] 30 | b_a = pd.DataFrame(b.T, index=Y.index, columns=constituents)[cut_off] 31 | b_a_w = b_a.div(b_a.sum(axis=1), axis=0) 32 | 33 | fig(16, 5) 34 | k_prediction = pd.DataFrame(X[constituents].values * b.T, index=Y.index)[cut_off].sum(axis=1) 35 | plt.plot(k_prediction) 36 | plt.plot(Y, 'g'); plt.title('Kalman estimator tracking Russell1000'); -------------------------------------------------------------------------------- /models/patterns.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | from ira.analysis.timeseries import find_movements 4 | 5 | 6 | def movements_tail_corrected(h, percentage): 7 | trends = find_movements(h.close, np.inf, use_prev_movement_size_for_percentage=False, 8 | pcntg=percentage/100, t_window=np.inf, 9 | drop_weekends_crossings=True, drop_out_of_market=False, result_as_frame=True, silent=True) 10 | # attach tail 11 | u, d = trends.UpTrends.dropna(), trends.DownTrends.dropna() 12 | t_ends = [u.end[-1], d.end[-1]] 13 | n_last = np.argmax(t_ends) 14 | _empt = {'start_price':np.nan, 'end_price':np.nan, 'delta':np.nan, 'end':np.nan} 15 | e0, t0 = h.close[-1], h.index[-1] 16 | 17 | if n_last == 0: # last is uptrend 18 | s0 = u.end_price[-1] 19 | _y = {'start_price': s0, 'end_price': e0, 'delta':abs(s0-e0), 'end':t0} 20 | _x = _empt 21 | else: # last is downtrend 22 | s0 = d.end_price[-1] 23 | _x = {'start_price': s0, 'end_price': e0, 'delta':abs(s0-e0), 'end':t0} 24 | _y = _empt 25 | 26 | 27 | _r = pd.concat(( 28 | pd.DataFrame({t_ends[n_last]: _x}).T, 29 | pd.DataFrame({t_ends[n_last]: _y}).T 30 | ), axis=1, keys=['UpTrends', 'DownTrends']) 31 | return pd.concat((trends, _r), axis=0) 32 | 33 | 34 | def piecewise_linear(date, h5, threshold, normalize=True, _tf=pd.Timedelta('5Min')): 35 | t = pd.Timestamp(date) if isinstance(date, str) else date 36 | if isinstance(t, (list, tuple)): 37 | dh = pd.Timestamp(t[1]) - pd.Timestamp(t[0]) 38 | h = h5[pd.Timestamp(t[0]) : pd.Timestamp(t[1]) ] 39 | else: 40 | dh = pd.Timedelta('24H') 41 | h = h5[t : t + dh] 42 | 43 | trends = movements_tail_corrected(h, threshold) 44 | 45 | pw = pd.concat((trends.UpTrends.dropna(), trends.DownTrends.dropna()), axis=0).sort_index() 46 | d0 = pd.Timestamp(pw.index[0]) 47 | 48 | x0 = (pw.index - d0).values / _tf 49 | x1 = (pw.end.astype('datetime64[ns]') - d0).values / _tf 50 | y0, y1 = pw.start_price.values, pw.end_price 51 | 52 | n_max = int(dh / _tf) 53 | j = 0 54 | yl = {} 55 | for i in range(n_max + 1): 56 | yl[d0 + i * _tf] = np.interp(i, [x0[j], x1[j]], [y0[j], y1[j]]) 57 | if i + 1 > x1[j]: 58 | j += 1 59 | yl = pd.Series(yl, name='y') 60 | return yl / yl.iloc[0] if normalize else yl 61 | 62 | -------------------------------------------------------------------------------- /models/statarb/algo.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import statsmodels.api as sm 4 | 5 | from ira.analysis.timeseries import infer_series_frequency 6 | from ira.analysis.kalman import kalman_regression_estimator 7 | from ira.strategies.helpers import generate_bands_signals 8 | from ira.simulator.utils import shift_signals 9 | from ira.analysis.tools import scols 10 | 11 | from utils import ksmooth, merge_columns_by_op 12 | from typing import Union 13 | 14 | 15 | class PairsStrategy: 16 | def __init__(self, data: pd.DataFrame, sX: str, sY: str, period: int): 17 | self.data = data 18 | self.sX = sX 19 | self.sY = sY 20 | self.period = period 21 | self.timeframe = infer_series_frequency(data[:100]) 22 | 23 | def positions(self, index, price, direction): 24 | px, py = price 25 | b = self.data.beta.iat[index] 26 | # -b*x, y 27 | return [-direction * b, +direction] 28 | 29 | def zscore(self, xs, period): 30 | m = xs.rolling(window=period).mean() 31 | s = xs.rolling(window=period).std() 32 | return (xs - m) / s 33 | 34 | def get_signals(self, entry, exit, period=None, accurate_time=True): 35 | priceX, priceY = self.data[self.sX], self.data[self.sY] 36 | signals = generate_bands_signals( 37 | scols(priceX, priceY), 38 | self.zscore(self.data.spread, self.period if period is None else period), 39 | entry, 40 | exit, 41 | size_func=self.positions 42 | ) 43 | return shift_signals(signals, self.timeframe - pd.Timedelta('1s')) if accurate_time else signals 44 | 45 | 46 | class PairsPreparation: 47 | """ 48 | Class for preparation pairs model 49 | """ 50 | def __init__(self, closes: pd.DataFrame, end_of_train: Union[str, pd.Timestamp]): 51 | self.closes = closes 52 | self.end_of_train = end_of_train 53 | 54 | def half_life(self, xs, min_period=5): 55 | xs_lag = xs.shift(1).bfill() 56 | xs_ret = xs.diff().bfill() 57 | res = sm.OLS(xs_ret, sm.add_constant(xs_lag)).fit() 58 | return max(int(-np.log(2) / res.params[1]), min_period) 59 | 60 | def get_trader(self, smbX: str, smbY: str, delta=1e-3, pv=0.01, mv=1): 61 | x, y = self.closes[smbX], self.closes[smbY] 62 | xa = ksmooth(x, pv, mv).x 63 | ya = ksmooth(y, pv, mv).x 64 | gamma = delta / (1 - delta) 65 | r = kalman_regression_estimator(xa, ya, gamma, 1, False) 66 | 67 | df = scols(x, y) 68 | beta = pd.Series(r[0][0], index=df.index) 69 | df['beta'] = beta 70 | df['spread'] = df[smbY] - (df[smbX] * beta) 71 | smoothing_period = self.half_life(df['spread'][:self.end_of_train]) 72 | return PairsStrategy(df, smbX, smbY, smoothing_period) 73 | -------------------------------------------------------------------------------- /models/statarb/coint_pairs_15Min.json: -------------------------------------------------------------------------------- 1 | {"pairs": {"ALPHAUSDT,ALGOUSDT": 0.04421189711930667, "AMPLUSDT,ADAUSDT": 0.006363145132563171, "AMPLUSDT,ALPHAUSDT": 0.029811239734956436, "AVAXUSDT,ALGOUSDT": 0.0057209322058213346, "AVAXUSDT,ALPHAUSDT": 0.03081683562404716, "BALUSDT,ADAUSDT": 0.023684530028378864, "BALUSDT,ALGOUSDT": 0.043145089505873306, "BALUSDT,AMPLUSDT": 0.00817878294633404, "BNBUSDT,BANDUSDT": 0.04588272438466195, "BTTUSDT,BANDUSDT": 0.044160883126195656, "BTTUSDT,BNBUSDT": 0.037072347556794304, "CELOUSDT,AAVEUSDT": 0.018844886252636212, "CELOUSDT,BTTUSDT": 0.03434247595220749, "CELUSDT,BSVUSDT": 0.03519766086973086, "CHZUSDT,AAVEUSDT": 0.007967918867855242, "CHZUSDT,AXSUSDT": 0.00768940018949894, "CHZUSDT,BTTUSDT": 0.03786035895195317, "CHZUSDT,CELOUSDT": 0.002467859223032833, "COMPUSDT,ATOMUSDT": 0.03668164989034438, "CROUSDT,BTTUSDT": 0.00881778722946149, "CROUSDT,CELOUSDT": 0.042542319507053374, "CRVUSDT,AAVEUSDT": 0.03942808865336314, "CRVUSDT,BTTUSDT": 0.04120532837692631, "CRVUSDT,CELOUSDT": 0.0035185780187051226, "CRVUSDT,CHZUSDT": 0.006751337108510059, "CVCUSDT,ALGOUSDT": 0.00039901131113112987, "CVCUSDT,ALPHAUSDT": 0.00770988903501726, "CVCUSDT,AVAXUSDT": 0.04655740969443389, "CVCUSDT,AXSUSDT": 0.02712799904071876, "CVCUSDT,BTTUSDT": 0.045345040647069644, "CVCUSDT,CELOUSDT": 0.007431725222591963, "CVCUSDT,CRVUSDT": 0.013043061561311051, "DASHUSDT,ALGOUSDT": 0.03459842318047608, "DASHUSDT,AVAXUSDT": 0.0421072656114064, "DASHUSDT,AXSUSDT": 0.0018312091032341201, "DASHUSDT,CVCUSDT": 0.00768661795244164, "DEFIUSDT,ATOMUSDT": 0.0458438586018537, "DOGEUSDT,AXSUSDT": 0.009663412168167462, "DOGEUSDT,BANDUSDT": 0.04981125378902, "DOGEUSDT,DASHUSDT": 0.0030826535298341703, "DOTUSDT,AXSUSDT": 0.010235996594303689, "ENJUSDT,ATOMUSDT": 0.0070738631936878215, "ENJUSDT,AVAXUSDT": 0.008298429822155349, "ENJUSDT,AXSUSDT": 0.010779464636724186, "ENJUSDT,COMPUSDT": 2.610776306715587e-05, "ENJUSDT,DASHUSDT": 0.030131762316464725, "ENJUSDT,DEFIUSDT": 0.01786267378542848, "EOSUSDT,BTTUSDT": 0.02015215545496973, "EOSUSDT,CHZUSDT": 0.04891362500956551, "EOSUSDT,CROUSDT": 0.04413109734632577, "ETCUSDT,CHZUSDT": 0.03547270556183541, "FIDAUSDT,AAVEUSDT": 0.02950577406128108, "FIDAUSDT,ALGOUSDT": 0.027049138942941888, "FIDAUSDT,ALPHAUSDT": 0.0243008446643769, "FIDAUSDT,BANDUSDT": 0.0343684494197262, "FIDAUSDT,BNBUSDT": 0.0018935453401202607, "FIDAUSDT,BTTUSDT": 0.0016219606791896626, "FIDAUSDT,CELOUSDT": 0.003229256892390987, "FIDAUSDT,CHZUSDT": 0.0005282099458201732, "FIDAUSDT,CROUSDT": 2.1999326641003573e-05, "FIDAUSDT,CRVUSDT": 0.004642936843199272, "FIDAUSDT,CVCUSDT": 0.011450771880615085, "FIDAUSDT,EOSUSDT": 2.8439930825580883e-05, "FIDAUSDT,ETCUSDT": 0.004892742169288049, "FIDAUSDT,ETHUSDT": 0.0020972268986465177, "FILUSDT,AAVEUSDT": 0.01692484888582694, "FILUSDT,AXSUSDT": 0.021325980102606428, "FILUSDT,CELOUSDT": 0.02384157107996383, "FILUSDT,CHZUSDT": 0.0014147769305701912, "FILUSDT,CRVUSDT": 0.017697843952231538, "FILUSDT,CVCUSDT": 0.021172640562238904, "FILUSDT,DOTUSDT": 0.0026676992788780734, "FILUSDT,ETCUSDT": 0.04861582649742182, "FILUSDT,FIDAUSDT": 0.00800207290006181, "FTMUSDT,AXSUSDT": 0.04914553244695383, "FTMUSDT,DASHUSDT": 0.006344478327759629, "FTMUSDT,DOTUSDT": 0.01547209770511818, "FTMUSDT,FLMUSDT": 0.001231674024207501, "FTTUSDT,FLMUSDT": 0.013182694336939579, "GRTUSDT,CELOUSDT": 0.03070614648091912, "GRTUSDT,CHZUSDT": 0.0008793331127827782, "GRTUSDT,EOSUSDT": 0.039589252109515895, "GRTUSDT,FIDAUSDT": 0.002551853295224782, "GRTUSDT,FILUSDT": 0.005806184166079493, "GRTUSDT,FLMUSDT": 0.002241396365762891, "HBARUSDT,DOTUSDT": 0.02816628925155056, "HBARUSDT,FILUSDT": 0.004548022043706353, "HBARUSDT,FLMUSDT": 0.0015997553154002374, "HBARUSDT,GRTUSDT": 0.04501774286999213, "HNTUSDT,FLMUSDT": 0.005718695790515513, "HTUSDT,BTCUSDT": 0.001959671615397399, "HTUSDT,FLMUSDT": 0.0059120877806248745, "ICXUSDT,CELOUSDT": 0.019392170306485414, "ICXUSDT,CHZUSDT": 0.03214638837616862, "ICXUSDT,CRVUSDT": 0.01368481823973873, "ICXUSDT,CVCUSDT": 0.043560823309472314, "ICXUSDT,DASHUSDT": 0.01429258932262155, "ICXUSDT,DOTUSDT": 0.04330223720448817, "ICXUSDT,EOSUSDT": 0.00870727001236089, "ICXUSDT,ETCUSDT": 0.0365488079740054, "ICXUSDT,FIDAUSDT": 0.002654516140727555, "ICXUSDT,FILUSDT": 0.0005974851879755055, "ICXUSDT,FLMUSDT": 0.002254423090123054, "ICXUSDT,HBARUSDT": 0.03025497364392863, "IOSTUSDT,DOGEUSDT": 0.04742855982568449, "IOSTUSDT,FLMUSDT": 0.0019226759625659378, "IOSTUSDT,FTMUSDT": 0.04184389656156298, "IOSTUSDT,HBARUSDT": 0.008323054822985784, "IOTAUSDT,AVAXUSDT": 0.020621515791785377, "IOTAUSDT,AXSUSDT": 0.0059133167897714375, "IOTAUSDT,CRVUSDT": 0.036695185318913484, "IOTAUSDT,DASHUSDT": 0.0008467975276855071, "IOTAUSDT,DEFIUSDT": 0.035673658692371046, "IOTAUSDT,DOGEUSDT": 0.006720845676905643, "IOTAUSDT,DOTUSDT": 0.03533506017695531, "IOTAUSDT,FILUSDT": 0.00627152262765119, "IOTAUSDT,FLMUSDT": 0.001863210425191932, "IOTAUSDT,FTMUSDT": 0.011862685956476614, "IOTAUSDT,HBARUSDT": 0.0439671102071281, "IOTAUSDT,IOSTUSDT": 0.009668757974990467, "KAVAUSDT,ALGOUSDT": 0.03421337475893387, "KAVAUSDT,BANDUSDT": 0.04424709739046672, "KAVAUSDT,BNBUSDT": 0.03231037065861666, "KAVAUSDT,BTTUSDT": 0.006264682865316975, "KAVAUSDT,CELOUSDT": 0.03748334282770742, "KAVAUSDT,CHZUSDT": 0.008905026318488997, "KAVAUSDT,CROUSDT": 0.0007681408341747507, "KAVAUSDT,CRVUSDT": 0.006027416456762274, "KAVAUSDT,CVCUSDT": 0.021693287570213986, "KAVAUSDT,EOSUSDT": 0.002762618954566836, "KAVAUSDT,ETHUSDT": 0.02436428652647413, "KAVAUSDT,FIDAUSDT": 6.997667612137083e-06, "KAVAUSDT,FILUSDT": 0.03282040884104554, "KAVAUSDT,FLMUSDT": 0.0031367933040733795, "KAVAUSDT,ICXUSDT": 0.019906228409307454, "KNCUSDT,BTTUSDT": 0.030177961930059605, "KNCUSDT,CHZUSDT": 0.0014304437477546165, "KNCUSDT,CROUSDT": 0.04770876561014354, "KNCUSDT,CRVUSDT": 0.03724759155163357, "KNCUSDT,CVCUSDT": 0.003399547268638659, "KNCUSDT,FIDAUSDT": 0.0005700085638564302, "KNCUSDT,FILUSDT": 0.02181856063146835, "KNCUSDT,FLMUSDT": 0.002310444359431384, "KNCUSDT,ICXUSDT": 0.009297114410658093, "KNCUSDT,KAVAUSDT": 0.0011966426461972918, "KSMUSDT,AXSUSDT": 0.03164226008297133, "KSMUSDT,DOTUSDT": 0.00210521584656165, "KSMUSDT,FILUSDT": 0.012970566256501318, "KSMUSDT,FLMUSDT": 0.0011054197124161236, "KSMUSDT,HBARUSDT": 0.042337130346937125, "KSMUSDT,ICXUSDT": 0.032657225770369815, "KSMUSDT,IOTAUSDT": 0.013519927256004429, "LEOUSDT,FLMUSDT": 0.002154511177786451, "LINKUSDT,FLMUSDT": 0.005112391990685394, "LRCUSDT,AVAXUSDT": 0.03800914660557137, "LRCUSDT,AXSUSDT": 0.03287460432895121, "LRCUSDT,CELOUSDT": 0.024307962257267956, "LRCUSDT,CHZUSDT": 0.013490997151950821, "LRCUSDT,CRVUSDT": 0.018179299425371387, "LRCUSDT,CVCUSDT": 0.00211845071424484, "LRCUSDT,DOGEUSDT": 0.03208710119485344, "LRCUSDT,DOTUSDT": 0.04227188498228853, "LRCUSDT,FIDAUSDT": 0.00393174388707236, "LRCUSDT,FILUSDT": 0.0010624747598542142, "LRCUSDT,FLMUSDT": 0.0022400257216024268, "LRCUSDT,ICXUSDT": 0.0035537531826956995, "LRCUSDT,IOTAUSDT": 0.014335981600711187, "LRCUSDT,KAVAUSDT": 0.004911308172438319, "LRCUSDT,KNCUSDT": 0.003580894531769152, "LTCUSDT,FLMUSDT": 0.0010102929066190776, "MANAUSDT,FLMUSDT": 0.0005555789634747496, "MATICUSDT,FLMUSDT": 0.004359177377884428, "MKRUSDT,ATOMUSDT": 0.022458481813499107, "MKRUSDT,FLMUSDT": 0.0026349942949628288, "MOBUSDT,BCHUSDT": 0.026165454180261113, "MOBUSDT,FIDAUSDT": 0.04770718683336453, "MOBUSDT,FLMUSDT": 0.004727653534597228, "MTLUSDT,FLMUSDT": 0.001333494618157862, "NEARUSDT,AXSUSDT": 0.014993151003571946, "NEARUSDT,CELOUSDT": 0.013184275736662743, "NEARUSDT,CHZUSDT": 0.0003311268654198646, "NEARUSDT,DOTUSDT": 0.03431024003542943, "NEARUSDT,FIDAUSDT": 0.025625371202666666, "NEARUSDT,FILUSDT": 0.0032373150625572833, "NEARUSDT,FLMUSDT": 0.0020194439495151, "NEARUSDT,KNCUSDT": 0.011114094965841937, "NEARUSDT,KSMUSDT": 0.02137093925447735, "NEARUSDT,LRCUSDT": 0.003205387837164388, "NEOUSDT,AMPLUSDT": 0.0003437347957543472, "NEOUSDT,BALUSDT": 0.00011552200140605612, "NEOUSDT,BNTUSDT": 0.033062922544114734, "NEOUSDT,BTCUSDT": 0.00021920791805576527, "NEOUSDT,BTTUSDT": 0.031076689366908022, "NEOUSDT,CROUSDT": 0.014202845922205541, "NEOUSDT,ETHUSDT": 0.0017944807123472814, "NEOUSDT,FIDAUSDT": 0.035843403688849314, "NEOUSDT,FLMUSDT": 0.007178064435937066, "NEOUSDT,HTUSDT": 7.953955677548487e-05, "NEOUSDT,MOBUSDT": 0.019840119797766568, "OKBUSDT,FLMUSDT": 0.006079599841624604, "OMGUSDT,ADAUSDT": 0.03565965896850741, "OMGUSDT,AMPLUSDT": 0.0009545442759472796, "OMGUSDT,BALUSDT": 0.0013838929616343735, "OMGUSDT,FIDAUSDT": 0.019205125720020925, "OMGUSDT,FLMUSDT": 0.0046943463962774434, "OMGUSDT,NEOUSDT": 0.0006829395281716413, "ONTUSDT,ALGOUSDT": 4.284329916745488e-05, "ONTUSDT,ALPHAUSDT": 0.014632742294598008, "ONTUSDT,AVAXUSDT": 0.002688392161657956, "ONTUSDT,AXSUSDT": 0.03794176259951252, "ONTUSDT,CELOUSDT": 0.041998169341104605, "ONTUSDT,CRVUSDT": 0.007804366742513572, "ONTUSDT,CVCUSDT": 0.0006669489405891346, "ONTUSDT,DASHUSDT": 0.008555797438062588, "ONTUSDT,DOTUSDT": 0.03848678328952776, "ONTUSDT,FIDAUSDT": 0.013876527911188456, "ONTUSDT,FILUSDT": 0.017869397138386724, "ONTUSDT,FLMUSDT": 0.0025595917994886016, "ONTUSDT,ICXUSDT": 0.020449555403797184, "ONTUSDT,IOSTUSDT": 0.04067498947964592, "ONTUSDT,IOTAUSDT": 0.025579099962873324, "ONTUSDT,KAVAUSDT": 0.0313205725117749, "ONTUSDT,KNCUSDT": 0.017329600404985917, "ONTUSDT,KSMUSDT": 0.01555265788568993, "ONTUSDT,LRCUSDT": 0.0008151061765009047, "PAXGUSDT,FLMUSDT": 0.0033897425864419435, "PROMUSDT,AXSUSDT": 0.01105243315364272, "PROMUSDT,BANDUSDT": 0.04215433769984478, "PROMUSDT,CVCUSDT": 0.01240693968250693, "PROMUSDT,DASHUSDT": 0.015481608639041312, "PROMUSDT,DEFIUSDT": 0.014876951039573495, "PROMUSDT,DOGEUSDT": 0.012136984443009945, "PROMUSDT,DOTUSDT": 0.03494448436830933, "PROMUSDT,ENJUSDT": 0.008129447402497445, "PROMUSDT,FIDAUSDT": 0.043328362416568574, "PROMUSDT,FLMUSDT": 0.002979471951282174, "PROMUSDT,FTMUSDT": 0.04482091255903029, "PROMUSDT,GRTUSDT": 0.03839373132492933, "PROMUSDT,IOSTUSDT": 0.0012479994873902125, "PROMUSDT,IOTAUSDT": 0.000525070171007985, "PROMUSDT,KSMUSDT": 0.04865260526088763, "PROMUSDT,LTCUSDT": 0.036017770944169324, "PROMUSDT,MKRUSDT": 0.00019458580822136315, "PROMUSDT,ONTUSDT": 0.0144872398657038, "QTUMUSDT,BTTUSDT": 0.0026765983529207977, "QTUMUSDT,CRVUSDT": 0.022462509835207704, "QTUMUSDT,CVCUSDT": 0.004882276367619868, "QTUMUSDT,EOSUSDT": 0.022405967895111168, "QTUMUSDT,FIDAUSDT": 4.2565016330484663e-05, "QTUMUSDT,FLMUSDT": 0.0031273357425250195, "QTUMUSDT,ICXUSDT": 0.031814950522857516, "QTUMUSDT,KAVAUSDT": 3.12138444450575e-05, "QTUMUSDT,KNCUSDT": 0.021613146355905517, "QTUMUSDT,NEOUSDT": 0.01922027174611439, "QTUMUSDT,ONTUSDT": 0.03688621273517023, "REEFUSDT,AAVEUSDT": 0.045004174662239625, "REEFUSDT,ATOMUSDT": 0.01648471556635819, "REEFUSDT,AVAXUSDT": 0.003420317401439406, "REEFUSDT,AXSUSDT": 0.000341534229988436, "REEFUSDT,CELOUSDT": 0.021450245190444816, "REEFUSDT,CHZUSDT": 0.012337122295122906, "REEFUSDT,CRVUSDT": 0.018777313698553642, "REEFUSDT,DASHUSDT": 0.00019589696499221183, "REEFUSDT,DEFIUSDT": 0.04735329786557391, "REEFUSDT,DOGEUSDT": 0.008398154616751675, "REEFUSDT,DOTUSDT": 0.008380495061836003, "REEFUSDT,ENJUSDT": 0.02136838911228907, "REEFUSDT,FILUSDT": 0.00024368914802143038, "REEFUSDT,FLMUSDT": 0.0012361696998772647, "REEFUSDT,FTMUSDT": 0.0006564096384423826, "REEFUSDT,GRTUSDT": 0.039652817100231275, "REEFUSDT,HBARUSDT": 0.02886671965676352, "REEFUSDT,ICXUSDT": 0.03809945727405782, "REEFUSDT,IOSTUSDT": 0.04394330404579965, "REEFUSDT,IOTAUSDT": 0.009639929168484104, "REEFUSDT,KNCUSDT": 0.00593273178521139, "REEFUSDT,KSMUSDT": 0.0019830031302371528, "REEFUSDT,LRCUSDT": 0.0015563819553956488, "REEFUSDT,NEARUSDT": 0.0017240334428673184, "REEFUSDT,PROMUSDT": 0.03281931992020206, "RENUSDT,ALGOUSDT": 0.0034352181697126184, "RENUSDT,DASHUSDT": 0.008527332199497669, "RENUSDT,DOGEUSDT": 0.002607072907758579, "RENUSDT,FLMUSDT": 0.002140190667562462, "RENUSDT,IOTAUSDT": 0.012294512373320547, "RENUSDT,PROMUSDT": 0.021863986743611515, "RENUSDT,REEFUSDT": 0.04864872715309607, "RSRUSDT,FLMUSDT": 0.006795823322316769, "RUNEUSDT,ALGOUSDT": 0.02440739999298072, "RUNEUSDT,BTTUSDT": 0.03982787104467961, "RUNEUSDT,CVCUSDT": 0.034772483564808804, "RUNEUSDT,FLMUSDT": 0.0039269601650223146, "RUNEUSDT,NEOUSDT": 0.03157669305028824, "RUNEUSDT,ONTUSDT": 0.04886386950282228, "SANDUSDT,ENJUSDT": 0.014784079968186275, "SANDUSDT,FLMUSDT": 0.00236949713025196, "SCRTUSDT,CHZUSDT": 0.04450491821590299, "SCRTUSDT,DOTUSDT": 0.0455455551203894, "SCRTUSDT,FLMUSDT": 0.002186535355707033, "SCRTUSDT,KSMUSDT": 0.04701900190201888, "SCRTUSDT,REEFUSDT": 0.035000328025416236, "SCUSDT,AAVEUSDT": 0.04442697793681866, "SCUSDT,ALGOUSDT": 0.011383904473310202, "SCUSDT,CELOUSDT": 0.008643014408529838, "SCUSDT,CHZUSDT": 0.01694729867291972, "SCUSDT,CRVUSDT": 0.012002309513458383, "SCUSDT,CVCUSDT": 0.030210696190297395, "SCUSDT,DOTUSDT": 0.01774961448844335, "SCUSDT,EOSUSDT": 0.0019120205609131848, "SCUSDT,ETCUSDT": 2.4066553986363398e-05, "SCUSDT,FILUSDT": 0.002551020562241662, "SCUSDT,FLMUSDT": 0.0026836158474221237, "SCUSDT,GRTUSDT": 0.025341473285056076, "SCUSDT,HBARUSDT": 0.00558562932642243, "SCUSDT,ICXUSDT": 0.00013488486378013568, "SCUSDT,IOSTUSDT": 0.03149939664914244, "SCUSDT,IOTAUSDT": 0.02185418194674768, "SCUSDT,KAVAUSDT": 0.00552304350330841, "SCUSDT,KNCUSDT": 0.002343956515900233, "SCUSDT,KSMUSDT": 0.016253303956033513, "SCUSDT,LRCUSDT": 0.014196190621835091, "SCUSDT,NEOUSDT": 0.03440355711567804, "SCUSDT,ONTUSDT": 0.012360451225203515, "SCUSDT,PROMUSDT": 0.006455036611585292, "SCUSDT,QTUMUSDT": 0.017089819571481965, "SCUSDT,REEFUSDT": 0.012300193625486698, "SHIBUSDT,FLMUSDT": 0.0017766351427212878, "SKLUSDT,ADAUSDT": 0.015668544217620012, "SKLUSDT,FLMUSDT": 0.0017877211641490808, "SNXUSDT,FLMUSDT": 0.004089325578480569, "SOLUSDT,BATUSDT": 0.02790667846032705, "SOLUSDT,FLMUSDT": 0.0012511882205262176, "SRMUSDT,AVAXUSDT": 0.037629974925007734, "SRMUSDT,AXSUSDT": 0.022430107066059354, "SRMUSDT,FLMUSDT": 0.002969108132110808, "SRMUSDT,LRCUSDT": 0.0324538447007061, "SRMUSDT,NEARUSDT": 0.036281519512192065, "SRMUSDT,REEFUSDT": 0.03749413169212771, "STORJUSDT,FLMUSDT": 0.004080243910064607, "STXUSDT,BTTUSDT": 0.03463147547786942, "STXUSDT,CRVUSDT": 0.029582259439435037, "STXUSDT,CVCUSDT": 0.005601274042592605, "STXUSDT,DOGEUSDT": 0.042601596216214156, "STXUSDT,EOSUSDT": 0.01558478394611782, "STXUSDT,ETCUSDT": 0.024671188566262582, "STXUSDT,FIDAUSDT": 0.0025693480251068697, "STXUSDT,FILUSDT": 0.009462806710323844, "STXUSDT,FLMUSDT": 0.003307919943656815, "STXUSDT,ICXUSDT": 0.005965890718261486, "STXUSDT,IOTAUSDT": 0.03768394592345073, "STXUSDT,KAVAUSDT": 0.0423280325628626, "STXUSDT,KNCUSDT": 0.028680191563930854, "STXUSDT,KSMUSDT": 0.029335857938877004, "STXUSDT,LRCUSDT": 0.0076151169470949215, "STXUSDT,ONTUSDT": 0.010842844137415257, "STXUSDT,PROMUSDT": 0.0189722771375059, "STXUSDT,QTUMUSDT": 0.02884523920672894, "STXUSDT,REEFUSDT": 0.020920153917021494, "STXUSDT,SCUSDT": 0.0005319384482156916, "STXUSDT,SRMUSDT": 0.0493231519863731, "SUSHIUSDT,DOTUSDT": 0.037139883117079935, "SUSHIUSDT,FLMUSDT": 0.001604497472065764, "SUSHIUSDT,HBARUSDT": 0.03352014316226211, "SUSHIUSDT,IOSTUSDT": 0.0009009634741770236, "SUSHIUSDT,IOTAUSDT": 0.0036834938196965805, "SUSHIUSDT,KSMUSDT": 0.047192370418540706, "SUSHIUSDT,PROMUSDT": 0.017780372032722265, "SUSHIUSDT,REEFUSDT": 0.006966714537096378, "SXPUSDT,ALGOUSDT": 0.04849673146145997, "SXPUSDT,ATOMUSDT": 0.04188459134146532, "SXPUSDT,AXSUSDT": 0.007066224968742648, "SXPUSDT,CELOUSDT": 0.019133267940601623, "SXPUSDT,CHZUSDT": 0.017988391887730087, "SXPUSDT,DASHUSDT": 0.0004415760070588859, "SXPUSDT,DEFIUSDT": 0.04251609868314129, "SXPUSDT,DOGEUSDT": 0.009431999471572187, "SXPUSDT,DOTUSDT": 0.004327033750065196, "SXPUSDT,EOSUSDT": 0.041026430855321795, "SXPUSDT,ETCUSDT": 0.04744064072360018, "SXPUSDT,FILUSDT": 0.0021094094292733335, "SXPUSDT,FLMUSDT": 0.001480732500619725, "SXPUSDT,FTMUSDT": 0.0027132512636885873, "SXPUSDT,GRTUSDT": 0.025149524521051996, "SXPUSDT,HBARUSDT": 0.0004732588159386468, "SXPUSDT,IOSTUSDT": 0.013159897564091232, "SXPUSDT,IOTAUSDT": 0.0002713922755504167, "SXPUSDT,KNCUSDT": 0.005647077715356131, "SXPUSDT,KSMUSDT": 0.00048010708680493375, "SXPUSDT,LRCUSDT": 0.004942896616803109, "SXPUSDT,NEARUSDT": 0.047359342032598326, "SXPUSDT,PROMUSDT": 0.018780515529595216, "SXPUSDT,REEFUSDT": 0.0013029806676927978, "SXPUSDT,RENUSDT": 0.0458098365860255, "SXPUSDT,SCRTUSDT": 0.018756101940352748, "SXPUSDT,SCUSDT": 0.04089451478279351, "SXPUSDT,SRMUSDT": 0.012063524735208024, "SXPUSDT,SUSHIUSDT": 9.689443196515456e-05, "THETAUSDT,FLMUSDT": 0.0045620198509535494, "THETAUSDT,RSRUSDT": 0.031552095169060976, "TOMOUSDT,ALPHAUSDT": 0.038627617994905446, "TOMOUSDT,FLMUSDT": 0.0037308329844875214, "TRUUSDT,AVAXUSDT": 0.030927087676087615, "TRUUSDT,FLMUSDT": 0.004338496134923841, "TRUUSDT,LRCUSDT": 0.04220706303659663, "TRUUSDT,NEOUSDT": 0.018367438557475524, "TRUUSDT,REEFUSDT": 0.028500362250806342, "TRUUSDT,RUNEUSDT": 0.031037186345481523, "TRUUSDT,SRMUSDT": 0.0015007823608785872, "TRXUSDT,BNBUSDT": 0.021823225243069302, "TRXUSDT,BTTUSDT": 0.026011626993337714, "TRXUSDT,CROUSDT": 0.017086244738607106, "TRXUSDT,CRVUSDT": 0.02163050991586057, "TRXUSDT,EOSUSDT": 0.03829446529633826, "TRXUSDT,FIDAUSDT": 0.023649694057406922, "TRXUSDT,FLMUSDT": 0.004992541836994477, "TRXUSDT,KAVAUSDT": 0.04101766433512574, "TRXUSDT,PAXGUSDT": 0.04338403885901439, "TRXUSDT,STXUSDT": 0.014994395577722391, "UNISWAPUSDT,AXSUSDT": 0.0014469163169020377, "UNISWAPUSDT,CHZUSDT": 0.026167405682739314, "UNISWAPUSDT,CVCUSDT": 0.013345067626077955, "UNISWAPUSDT,DASHUSDT": 0.01433030218163112, "UNISWAPUSDT,DOGEUSDT": 0.021551663095475553, "UNISWAPUSDT,DOTUSDT": 0.013387159791810991, "UNISWAPUSDT,ENJUSDT": 0.018454786503854057, "UNISWAPUSDT,FILUSDT": 0.012516686038695215, "UNISWAPUSDT,FLMUSDT": 0.0026511790673508545, "UNISWAPUSDT,IOSTUSDT": 0.03611851275224986, "UNISWAPUSDT,IOTAUSDT": 0.0079598472683229, "UNISWAPUSDT,KSMUSDT": 0.03325039726919017, "UNISWAPUSDT,LRCUSDT": 0.02669874170029831, "UNISWAPUSDT,NEARUSDT": 0.022288803195294065, "UNISWAPUSDT,ONTUSDT": 0.025902384311382916, "UNISWAPUSDT,PROMUSDT": 0.03027473140426678, "UNISWAPUSDT,REEFUSDT": 0.0014811402533723879, "UNISWAPUSDT,RENUSDT": 0.009047239922387441, "UNISWAPUSDT,SRMUSDT": 0.0026293707523519843, "UNISWAPUSDT,SXPUSDT": 0.016852339918272733, "UNIUSDT,BATUSDT": 0.047408348981905164, "UNIUSDT,FLMUSDT": 0.0020569446428575057, "UNIUSDT,SOLUSDT": 0.029590567905869277, "USDTUSDT,AAVEUSDT": 0.030078879040255548, "USDTUSDT,ADAUSDT": 0.03132592125482799, "USDTUSDT,ALGOUSDT": 0.0013574113318718935, "USDTUSDT,ALPHAUSDT": 0.0012152644914464248, "USDTUSDT,ATOMUSDT": 0.00027892256273922774, "USDTUSDT,AVAXUSDT": 0.0015813535342869558, "USDTUSDT,AXSUSDT": 0.0015877266955212474, "USDTUSDT,BALUSDT": 0.02598212234849505, "USDTUSDT,BANDUSDT": 0.009712925822775585, "USDTUSDT,BATUSDT": 0.004341381360718542, "USDTUSDT,BNBUSDT": 0.04941460984447714, "USDTUSDT,CELOUSDT": 0.035523413288182336, "USDTUSDT,CHZUSDT": 0.030187756669458343, "USDTUSDT,COMPUSDT": 0.0007255441622984948, "USDTUSDT,CRVUSDT": 0.02803165237207944, "USDTUSDT,CVCUSDT": 0.005839375562908879, "USDTUSDT,DASHUSDT": 0.000737382924435566, "USDTUSDT,DEFIUSDT": 6.055969759025839e-05, "USDTUSDT,DOGEUSDT": 0.0014827849014149171, "USDTUSDT,DOTUSDT": 0.00302305792071249, "USDTUSDT,EGLDUSDT": 0.017780666109590955, "USDTUSDT,ENJUSDT": 0.0004264494741746971, "USDTUSDT,EOSUSDT": 0.04002416057258866, "USDTUSDT,ETCUSDT": 0.025940736608817683, "USDTUSDT,FIDAUSDT": 0.04817874170591189, "USDTUSDT,FILUSDT": 0.007555526907361981, "USDTUSDT,FLMUSDT": 0.004173403806834242, "USDTUSDT,FTMUSDT": 0.0011234860796510424, "USDTUSDT,FTTUSDT": 0.012136011387955004, "USDTUSDT,GRTUSDT": 0.028343614118498183, "USDTUSDT,HBARUSDT": 0.017158471480670602, "USDTUSDT,ICXUSDT": 0.011012333608538781, "USDTUSDT,IOSTUSDT": 0.0008772399553599359, "USDTUSDT,IOTAUSDT": 0.0006044872420378496, "USDTUSDT,KAVAUSDT": 0.036341601612179225, "USDTUSDT,KNCUSDT": 0.017355321017219455, "USDTUSDT,KSMUSDT": 0.003498130640355974, "USDTUSDT,LINKUSDT": 0.01740769277170006, "USDTUSDT,LRCUSDT": 0.005417576740403045, "USDTUSDT,LTCUSDT": 0.0003182253213937577, "USDTUSDT,MANAUSDT": 0.0008754679175116397, "USDTUSDT,MATICUSDT": 0.0003055798724372231, "USDTUSDT,MKRUSDT": 0.00014279415415125967, "USDTUSDT,NEARUSDT": 0.018111373324715536, "USDTUSDT,OMGUSDT": 0.0273438996674394, "USDTUSDT,ONTUSDT": 0.006588691121636762, "USDTUSDT,PROMUSDT": 0.0002620489623695285, "USDTUSDT,QTUMUSDT": 0.01655227660742516, "USDTUSDT,REEFUSDT": 0.0018393060519173705, "USDTUSDT,RENUSDT": 0.00023180056308486703, "USDTUSDT,RUNEUSDT": 0.0074441368546113214, "USDTUSDT,SANDUSDT": 0.0004782489327208685, "USDTUSDT,SCRTUSDT": 0.03508968034380667, "USDTUSDT,SCUSDT": 0.017434932462774798, "USDTUSDT,SHIBUSDT": 2.2653105623736687e-05, "USDTUSDT,SKLUSDT": 0.00702424537368618, "USDTUSDT,SOLUSDT": 2.064353743648043e-05, "USDTUSDT,SRMUSDT": 0.0062402890416090335, "USDTUSDT,STXUSDT": 0.012588869046520243, "USDTUSDT,SUSHIUSDT": 0.001312581990109235, "USDTUSDT,SXPUSDT": 0.0066434066111938925, "USDTUSDT,TOMOUSDT": 0.0002713515556179492, "USDTUSDT,TRUUSDT": 0.026250213571542915, "USDTUSDT,UNISWAPUSDT": 0.0021674246159074694, "USDTUSDT,UNIUSDT": 0.0005638996579277116, "VETUSDT,AMPLUSDT": 0.031974860112889116, "VETUSDT,BALUSDT": 0.04246550191424213, "VETUSDT,BTTUSDT": 0.0008377405700792812, "VETUSDT,CROUSDT": 0.04143168703653683, "VETUSDT,CRVUSDT": 0.02721023584034961, "VETUSDT,FIDAUSDT": 5.778271264804528e-05, "VETUSDT,FLMUSDT": 0.0036939657266499555, "VETUSDT,KAVAUSDT": 0.0008912871430314121, "VETUSDT,KNCUSDT": 0.03361005401433535, "VETUSDT,NEOUSDT": 0.013939436286483883, "VETUSDT,PROMUSDT": 0.04524796128708324, "VETUSDT,QTUMUSDT": 0.03622279074415667, "VETUSDT,SRMUSDT": 0.030088328847226255, "VETUSDT,TRUUSDT": 0.011971135105829431, "VETUSDT,USDTUSDT": 1.5720979060903952e-05, "WAVESUSDT,AAVEUSDT": 0.02434244284002694, "WAVESUSDT,AXSUSDT": 0.02058574589893754, "WAVESUSDT,CRVUSDT": 0.03551970233907584, "WAVESUSDT,DASHUSDT": 0.03256037948537955, "WAVESUSDT,DOTUSDT": 0.019709099229283985, "WAVESUSDT,FILUSDT": 0.022754545639126558, "WAVESUSDT,FLMUSDT": 0.001563852456419282, "WAVESUSDT,FTMUSDT": 0.021082727803804734, "WAVESUSDT,IOSTUSDT": 0.029938531133589998, "WAVESUSDT,KSMUSDT": 2.948607026261478e-05, "WAVESUSDT,PROMUSDT": 0.008260258738575157, "WAVESUSDT,SCRTUSDT": 0.0005967789264813983, "WAVESUSDT,SUSHIUSDT": 0.00012379391435880384, "WAVESUSDT,UNISWAPUSDT": 0.014601133680230475, "WAVESUSDT,USDTUSDT": 1.4302467677885364e-06, "XAUTUSDT,FLMUSDT": 0.0008471924455841188, "XAUTUSDT,PAXGUSDT": 0.020881932492017793, "XAUTUSDT,USDTUSDT": 0.004154153156083258, "XEMUSDT,AAVEUSDT": 0.007661198270949784, "XEMUSDT,AVAXUSDT": 0.014833729873925191, "XEMUSDT,AXSUSDT": 0.016574080714078322, "XEMUSDT,BTTUSDT": 0.016669419642484237, "XEMUSDT,CELOUSDT": 0.001493999686344346, "XEMUSDT,CHZUSDT": 0.013543134435207578, "XEMUSDT,CRVUSDT": 0.00012021185669894309, "XEMUSDT,CVCUSDT": 0.016041819250969758, "XEMUSDT,DASHUSDT": 0.011474832436462872, "XEMUSDT,DOGEUSDT": 0.005256686219802395, "XEMUSDT,DOTUSDT": 0.007275901598915807, "XEMUSDT,EOSUSDT": 0.030012863489730656, "XEMUSDT,ETCUSDT": 0.005119692803161907, "XEMUSDT,FIDAUSDT": 0.0023932980896485324, "XEMUSDT,FILUSDT": 0.00010740455537935438, "XEMUSDT,FLMUSDT": 0.002195021049213124, "XEMUSDT,FTMUSDT": 0.04799962978400133, "XEMUSDT,GRTUSDT": 0.008072181785676674, "XEMUSDT,HBARUSDT": 0.013161359301102115, "XEMUSDT,ICXUSDT": 4.261506692111342e-05, "XEMUSDT,IOTAUSDT": 0.016501242083136136, "XEMUSDT,KAVAUSDT": 0.004116759514865144, "XEMUSDT,KNCUSDT": 0.003564455082239542, "XEMUSDT,KSMUSDT": 0.005903968216961228, "XEMUSDT,LRCUSDT": 2.010638681952046e-05, "XEMUSDT,NEARUSDT": 0.00084187033246831, "XEMUSDT,ONTUSDT": 0.00498349820415119, "XEMUSDT,PROMUSDT": 0.0035455009603597726, "XEMUSDT,QTUMUSDT": 0.010790022696016342, "XEMUSDT,REEFUSDT": 0.0009058333319293243, "XEMUSDT,SCUSDT": 1.2589137352415234e-05, "XEMUSDT,SRMUSDT": 0.04344917768107298, "XEMUSDT,STXUSDT": 0.0006818509586698447, "XEMUSDT,SXPUSDT": 0.020236711394239375, "XEMUSDT,UNISWAPUSDT": 0.0018222243194928758, "XEMUSDT,USDTUSDT": 5.402438718028339e-06, "XLMUSDT,AVAXUSDT": 0.020298393148139835, "XLMUSDT,AXSUSDT": 0.005230327497155898, "XLMUSDT,CHZUSDT": 0.012115471428924699, "XLMUSDT,DASHUSDT": 0.0015544718180733887, "XLMUSDT,DOGEUSDT": 0.04519897713031685, "XLMUSDT,DOTUSDT": 0.040979962922082155, "XLMUSDT,FIDAUSDT": 0.04775941128588307, "XLMUSDT,FILUSDT": 0.01683968575312706, "XLMUSDT,FLMUSDT": 0.0017828677624197387, "XLMUSDT,KSMUSDT": 0.04557443514332336, "XLMUSDT,NEARUSDT": 0.01601763728816606, "XLMUSDT,REEFUSDT": 0.0006214194213716138, "XLMUSDT,SXPUSDT": 0.013596641152203923, "XLMUSDT,UNISWAPUSDT": 0.012825805354829, "XLMUSDT,USDTUSDT": 7.639869322458454e-07, "XLMUSDT,XEMUSDT": 0.04416769362509395, "XMRUSDT,BNBUSDT": 0.03866768911889363, "XMRUSDT,FIDAUSDT": 0.03308094811292056, "XMRUSDT,FLMUSDT": 0.0050330955861704755, "XMRUSDT,KNCUSDT": 0.03829371261626963, "XMRUSDT,MOBUSDT": 0.0008487664532402536, "XMRUSDT,NEOUSDT": 0.002071708592288759, "XMRUSDT,SRMUSDT": 0.01334252642774747, "XMRUSDT,TRXUSDT": 0.011240356879631107, "XMRUSDT,USDTUSDT": 0.0002842753085255545, "XRPUSDT,ALGOUSDT": 0.0045015103427349604, "XRPUSDT,ALPHAUSDT": 0.007136329877667283, "XRPUSDT,AVAXUSDT": 0.01206783384748728, "XRPUSDT,AXSUSDT": 0.04607278096670816, "XRPUSDT,BANDUSDT": 0.03309981256657341, "XRPUSDT,DASHUSDT": 0.015229824138474105, "XRPUSDT,ENJUSDT": 0.002157055923280402, "XRPUSDT,FLMUSDT": 0.002945786347368966, "XRPUSDT,LRCUSDT": 0.020179507864607413, "XRPUSDT,PROMUSDT": 0.008252367972142807, "XRPUSDT,REEFUSDT": 0.01727058406492732, "XRPUSDT,RENUSDT": 0.011841330038861337, "XRPUSDT,RUNEUSDT": 0.036951400686707575, "XRPUSDT,SRMUSDT": 0.0369108402843042, "XRPUSDT,TOMOUSDT": 0.00928408982025653, "XRPUSDT,TRUUSDT": 0.037454681228878295, "XRPUSDT,UNISWAPUSDT": 0.024185168636331734, "XRPUSDT,USDTUSDT": 6.776650691969322e-08, "XTZUSDT,ADAUSDT": 0.012517597246281669, "XTZUSDT,AMPLUSDT": 0.00399075434096961, "XTZUSDT,BALUSDT": 0.009493815434797529, "XTZUSDT,BANDUSDT": 0.042838886038776766, "XTZUSDT,FIDAUSDT": 0.04816842202582719, "XTZUSDT,FLMUSDT": 0.004967561606547123, "XTZUSDT,NEOUSDT": 0.00046379588756526594, "XTZUSDT,OMGUSDT": 0.0008530167823000295, "XTZUSDT,USDTUSDT": 5.087938338109346e-05, "YFIIUSDT,FLMUSDT": 0.005350947576017663, "YFIIUSDT,THETAUSDT": 0.04593020082822098, "YFIIUSDT,USDTUSDT": 0.009652588345685447, "YFIUSDT,ATOMUSDT": 0.02306759775831435, "YFIUSDT,AVAXUSDT": 0.007459542370675162, "YFIUSDT,AXSUSDT": 0.017151186194210428, "YFIUSDT,BANDUSDT": 0.03514338910987539, "YFIUSDT,COMPUSDT": 0.006026127976822304, "YFIUSDT,ENJUSDT": 0.0076155365079852745, "YFIUSDT,FLMUSDT": 0.0036130263305343122, "YFIUSDT,MATICUSDT": 0.037231326067585176, "YFIUSDT,PROMUSDT": 0.03478650297598366, "YFIUSDT,REEFUSDT": 0.034405567022909075, "YFIUSDT,RUNEUSDT": 0.026546275390969137, "YFIUSDT,SANDUSDT": 0.002736072856783844, "YFIUSDT,SRMUSDT": 0.013991143838430534, "YFIUSDT,TRUUSDT": 0.011879331673479127, "YFIUSDT,USDTUSDT": 7.447998434873033e-08, "YFIUSDT,XRPUSDT": 2.224551972922017e-05, "ZECUSDT,BTTUSDT": 0.02619815590310174, "ZECUSDT,EOSUSDT": 0.04161667328296345, "ZECUSDT,ETHUSDT": 0.014711077447110765, "ZECUSDT,FIDAUSDT": 0.00033075225290667734, "ZECUSDT,FLMUSDT": 0.004555767208119395, "ZECUSDT,KAVAUSDT": 0.03541575354772483, "ZECUSDT,NEOUSDT": 0.0060814254940773, "ZECUSDT,OMGUSDT": 0.02902216363606294, "ZECUSDT,TRXUSDT": 0.019343746777168252, "ZECUSDT,USDTUSDT": 0.00011182357723664609, "ZILUSDT,ATOMUSDT": 0.03447472764320901, "ZILUSDT,FLMUSDT": 0.00190479631732687, "ZILUSDT,MANAUSDT": 0.008481513916580998, "ZILUSDT,MKRUSDT": 0.048388881642679515, "ZILUSDT,PROMUSDT": 0.04571271366713594, "ZILUSDT,USDTUSDT": 3.9375418721012696e-10, "ZILUSDT,YFIUSDT": 0.025328843608740954, "ZRXUSDT,ALGOUSDT": 0.003993377244512988, "ZRXUSDT,AXSUSDT": 0.04986015354500274, "ZRXUSDT,CELOUSDT": 0.03932457038558502, "ZRXUSDT,CHZUSDT": 0.009271874668851807, "ZRXUSDT,CRVUSDT": 0.02070430489274618, "ZRXUSDT,CVCUSDT": 0.006429736962719219, "ZRXUSDT,DOTUSDT": 0.021446534612853212, "ZRXUSDT,EOSUSDT": 0.027937083399893113, "ZRXUSDT,FIDAUSDT": 0.01674745878782901, "ZRXUSDT,FILUSDT": 0.006003153974048688, "ZRXUSDT,FLMUSDT": 0.0024517696832467902, "ZRXUSDT,HBARUSDT": 0.032434678054432446, "ZRXUSDT,ICXUSDT": 0.0003302692192694374, "ZRXUSDT,IOTAUSDT": 0.04311384058518442, "ZRXUSDT,KAVAUSDT": 0.01398228453673757, "ZRXUSDT,KNCUSDT": 0.0003509387992077225, "ZRXUSDT,KSMUSDT": 0.027430487720979644, "ZRXUSDT,LRCUSDT": 0.0001301778277193952, "ZRXUSDT,NEARUSDT": 0.02368938231867861, "ZRXUSDT,ONTUSDT": 0.015231695894057777, "ZRXUSDT,QTUMUSDT": 0.0160664145233447, "ZRXUSDT,REEFUSDT": 0.0008367126776779305, "ZRXUSDT,SCUSDT": 0.005963903958103391, "ZRXUSDT,SRMUSDT": 0.03031353532350368, "ZRXUSDT,STXUSDT": 0.0200019542181131, "ZRXUSDT,UNISWAPUSDT": 0.01546810932579257, "ZRXUSDT,USDTUSDT": 2.2438346044989432e-06, "ZRXUSDT,VETUSDT": 0.03852354093204926, "ZRXUSDT,XEMUSDT": 3.3312032765929474e-07, "ZRXUSDT,XLMUSDT": 0.02946255614481721}, "timeframe": "15Min", "start": "2022-06-01 00:00:00", "end": "2022-06-26 11:45:00"} -------------------------------------------------------------------------------- /models/statarb/coint_pairs_5Min.json: -------------------------------------------------------------------------------- 1 | {"pairs": {"ALPHAUSDT,ALGOUSDT": 0.0005875311662804974, "AMPLUSDT,ADAUSDT": 0.00581234426985206, "AMPLUSDT,ALGOUSDT": 0.03707731956651287, "AMPLUSDT,ALPHAUSDT": 0.02528838302530158, "AVAXUSDT,ALGOUSDT": 0.0020753392643493825, "AVAXUSDT,ALPHAUSDT": 0.040370030192834756, "AXSUSDT,AVAXUSDT": 0.01842247970513982, "BALUSDT,ADAUSDT": 0.03743811205223332, "BALUSDT,ALGOUSDT": 0.03807866314674557, "BALUSDT,ALPHAUSDT": 0.049521398658615386, "BALUSDT,AMPLUSDT": 0.0036662653283307416, "BANDUSDT,ADAUSDT": 0.03476903725708306, "BTTUSDT,BNBUSDT": 0.002513834022437087, "CELOUSDT,AAVEUSDT": 0.0031020324631658375, "CELOUSDT,BTTUSDT": 0.026245368155435468, "CHZUSDT,AAVEUSDT": 0.009275462209952366, "CHZUSDT,AXSUSDT": 0.016884714850671032, "CHZUSDT,BTTUSDT": 0.03366183900227525, "CHZUSDT,CELOUSDT": 0.0017213610282783274, "COMPUSDT,ATOMUSDT": 0.04034917908869912, "CROUSDT,BNBUSDT": 0.01303860926642918, "CROUSDT,BTTUSDT": 0.0019288581555017299, "CRVUSDT,AAVEUSDT": 0.04157353272357002, "CRVUSDT,BTTUSDT": 0.00947041219905589, "CRVUSDT,CELOUSDT": 0.0007367891943026691, "CRVUSDT,CHZUSDT": 0.004727092555568634, "CVCUSDT,ALGOUSDT": 3.751597781676788e-05, "CVCUSDT,ALPHAUSDT": 0.005256393607595932, "CVCUSDT,AVAXUSDT": 0.011315811872287201, "CVCUSDT,AXSUSDT": 0.048147364882341066, "CVCUSDT,BTTUSDT": 0.016493967363711295, "CVCUSDT,CELOUSDT": 0.015723822277983458, "CVCUSDT,CRVUSDT": 0.008875290876510802, "DASHUSDT,ALGOUSDT": 0.020757364034287167, "DASHUSDT,AXSUSDT": 0.00409892253295299, "DASHUSDT,CHZUSDT": 0.0342561784394495, "DASHUSDT,CVCUSDT": 0.008348824230943994, "DOGEUSDT,ALGOUSDT": 0.02681043358411831, "DOGEUSDT,AXSUSDT": 0.033625465302541, "DOGEUSDT,DASHUSDT": 0.002464406543927089, "DOTUSDT,AAVEUSDT": 0.026894433551209806, "DOTUSDT,AXSUSDT": 0.011111205390197578, "DOTUSDT,CHZUSDT": 0.023798090418121042, "DOTUSDT,DOGEUSDT": 0.03025982959928432, "ENJUSDT,ATOMUSDT": 0.019140936683961003, "ENJUSDT,AVAXUSDT": 0.005904365096966091, "ENJUSDT,AXSUSDT": 0.010191457717864463, "ENJUSDT,COMPUSDT": 1.3570850389803137e-05, "ENJUSDT,DASHUSDT": 0.02329158819318756, "ENJUSDT,DEFIUSDT": 0.023227561212806853, "EOSUSDT,BTTUSDT": 0.012681479560421392, "EOSUSDT,CHZUSDT": 0.010157675781483005, "EOSUSDT,CROUSDT": 0.04300436446098669, "EOSUSDT,CVCUSDT": 0.01678818987559672, "ETCUSDT,CHZUSDT": 0.0312977505504257, "ETHUSDT,BTTUSDT": 0.014597337502178506, "FIDAUSDT,AAVEUSDT": 0.02607180408088679, "FIDAUSDT,ADAUSDT": 0.039841249445231715, "FIDAUSDT,ALGOUSDT": 0.004686934869237632, "FIDAUSDT,ALPHAUSDT": 0.0012632057320123465, "FIDAUSDT,BANDUSDT": 0.047326740821397845, "FIDAUSDT,BNBUSDT": 0.0009314550617221253, "FIDAUSDT,BTTUSDT": 0.00027187863386119354, "FIDAUSDT,CELOUSDT": 0.0010455396773007418, "FIDAUSDT,CHZUSDT": 0.0007655628156177343, "FIDAUSDT,CROUSDT": 1.1167528604016944e-05, "FIDAUSDT,CRVUSDT": 0.005165796120825441, "FIDAUSDT,CVCUSDT": 0.002436922754893853, "FIDAUSDT,DOTUSDT": 0.03660120512094784, "FIDAUSDT,EOSUSDT": 9.218811347134042e-07, "FIDAUSDT,ETCUSDT": 0.004078875767552575, "FIDAUSDT,ETHUSDT": 0.0017462335745831617, "FILUSDT,AAVEUSDT": 0.0027197709867721174, "FILUSDT,AXSUSDT": 0.01769374931921074, "FILUSDT,CELOUSDT": 0.03408623245653148, "FILUSDT,CHZUSDT": 0.0013094854727851248, "FILUSDT,CRVUSDT": 0.011814034521790816, "FILUSDT,CVCUSDT": 0.017866341720759016, "FILUSDT,DOTUSDT": 0.0047520289472321635, "FILUSDT,FIDAUSDT": 0.0029818102257630494, "FTMUSDT,AXSUSDT": 0.007285949709592754, "FTMUSDT,DASHUSDT": 0.009565211480371165, "FTMUSDT,DOTUSDT": 0.01082058501304483, "FTMUSDT,FLMUSDT": 0.0009184989608104638, "FTTUSDT,FLMUSDT": 0.008263104011677546, "GRTUSDT,CHZUSDT": 0.0009415337862346009, "GRTUSDT,CRVUSDT": 0.02995045587212177, "GRTUSDT,EOSUSDT": 0.026368996745488444, "GRTUSDT,ETCUSDT": 0.043523597460894445, "GRTUSDT,FIDAUSDT": 0.002192170790716915, "GRTUSDT,FILUSDT": 0.004228871139586494, "GRTUSDT,FLMUSDT": 0.0017327650480363933, "HBARUSDT,DOTUSDT": 0.025377742542767458, "HBARUSDT,FILUSDT": 0.023687020018417576, "HBARUSDT,FLMUSDT": 0.0013876997556548957, "HNTUSDT,FLMUSDT": 0.00448910791745065, "HTUSDT,BTCUSDT": 0.0005251789848268784, "HTUSDT,FLMUSDT": 0.00465783168385468, "ICXUSDT,ALGOUSDT": 0.012461981844875104, "ICXUSDT,CELOUSDT": 0.03681380638083961, "ICXUSDT,CHZUSDT": 0.02456562494664192, "ICXUSDT,CRVUSDT": 0.009482777152297447, "ICXUSDT,CVCUSDT": 0.034112858290387196, "ICXUSDT,DASHUSDT": 0.029262052637051374, "ICXUSDT,DOTUSDT": 0.039955441024816275, "ICXUSDT,EOSUSDT": 0.008583809611291853, "ICXUSDT,ETCUSDT": 0.013251071597788511, "ICXUSDT,FIDAUSDT": 0.0020064475935531096, "ICXUSDT,FILUSDT": 0.0013756742875045444, "ICXUSDT,FLMUSDT": 0.0018377793503532942, "ICXUSDT,GRTUSDT": 0.02732680712874822, "ICXUSDT,HBARUSDT": 0.03730026801028139, "IOSTUSDT,DASHUSDT": 0.04734587536631577, "IOSTUSDT,DOGEUSDT": 0.04003339103813041, "IOSTUSDT,FLMUSDT": 0.0016387658053342153, "IOSTUSDT,FTMUSDT": 0.033538385042227156, "IOSTUSDT,HBARUSDT": 0.008195461059935384, "IOTAUSDT,ALGOUSDT": 0.004386492048298383, "IOTAUSDT,AVAXUSDT": 0.02475047525303243, "IOTAUSDT,AXSUSDT": 0.01892687200141271, "IOTAUSDT,CRVUSDT": 0.039074729466295456, "IOTAUSDT,DASHUSDT": 0.0001610170848215057, "IOTAUSDT,DEFIUSDT": 0.018399557162998244, "IOTAUSDT,DOGEUSDT": 0.0006880203530127919, "IOTAUSDT,DOTUSDT": 0.005632214246298324, "IOTAUSDT,FILUSDT": 0.007425752804531973, "IOTAUSDT,FLMUSDT": 0.001734647254068439, "IOTAUSDT,FTMUSDT": 0.015909212606079477, "IOTAUSDT,IOSTUSDT": 0.013584892012567639, "KAVAUSDT,ALGOUSDT": 0.02723438317171343, "KAVAUSDT,AMPLUSDT": 0.038301422524783045, "KAVAUSDT,BNBUSDT": 0.020789215815160203, "KAVAUSDT,BTTUSDT": 0.011193115006452531, "KAVAUSDT,CELOUSDT": 0.021747151228106304, "KAVAUSDT,CHZUSDT": 0.005667770922244731, "KAVAUSDT,CROUSDT": 0.00042586560682778555, "KAVAUSDT,CRVUSDT": 0.003128683009474366, "KAVAUSDT,CVCUSDT": 0.024751752241724486, "KAVAUSDT,EOSUSDT": 0.0004233228545810594, "KAVAUSDT,ETHUSDT": 0.019260482382616125, "KAVAUSDT,FIDAUSDT": 1.5111953628751415e-06, "KAVAUSDT,FILUSDT": 0.014930359890815415, "KAVAUSDT,FLMUSDT": 0.0023631001774207694, "KAVAUSDT,ICXUSDT": 0.00748751155849265, "KNCUSDT,AAVEUSDT": 0.011681093028199805, "KNCUSDT,BTTUSDT": 0.026579369891826476, "KNCUSDT,CHZUSDT": 0.0006904700895250954, "KNCUSDT,CROUSDT": 0.0047241136143801555, "KNCUSDT,CRVUSDT": 0.007618932818203557, "KNCUSDT,CVCUSDT": 0.014723667353871692, "KNCUSDT,EOSUSDT": 0.024927441468314132, "KNCUSDT,FIDAUSDT": 3.203425826544602e-05, "KNCUSDT,FILUSDT": 0.02008476254027424, "KNCUSDT,FLMUSDT": 0.001820125713498256, "KNCUSDT,ICXUSDT": 0.000818997996811582, "KNCUSDT,KAVAUSDT": 0.002784595680602588, "KSMUSDT,AXSUSDT": 0.0374155867264329, "KSMUSDT,CHZUSDT": 0.036721986664142836, "KSMUSDT,CVCUSDT": 0.03197715404135863, "KSMUSDT,DOTUSDT": 0.0001730642180395629, "KSMUSDT,FILUSDT": 0.0289025200721938, "KSMUSDT,FLMUSDT": 0.000943062704682751, "KSMUSDT,FTMUSDT": 0.03989087921052376, "KSMUSDT,HBARUSDT": 0.026985860215821302, "KSMUSDT,ICXUSDT": 0.027414828500278505, "KSMUSDT,IOSTUSDT": 0.026910081052887048, "KSMUSDT,IOTAUSDT": 0.032136425767043335, "LEOUSDT,FLMUSDT": 0.004022715191849461, "LINKUSDT,BANDUSDT": 0.03660688140698413, "LINKUSDT,FLMUSDT": 0.004026012457436778, "LRCUSDT,ALGOUSDT": 0.017903265499557477, "LRCUSDT,AVAXUSDT": 0.04821994472590802, "LRCUSDT,BTTUSDT": 0.023622877454411252, "LRCUSDT,CELOUSDT": 0.02902731075785045, "LRCUSDT,CHZUSDT": 0.0093867636090792, "LRCUSDT,CRVUSDT": 0.015400370081745962, "LRCUSDT,CVCUSDT": 0.0018678551781022793, "LRCUSDT,FIDAUSDT": 0.0011969659117975853, "LRCUSDT,FILUSDT": 0.0008929448490321879, "LRCUSDT,FLMUSDT": 0.002035549265828945, "LRCUSDT,ICXUSDT": 0.0010859452483831483, "LRCUSDT,IOTAUSDT": 0.01789249528567003, "LRCUSDT,KAVAUSDT": 0.0006258520385885222, "LRCUSDT,KNCUSDT": 0.0018405403982760732, "LTCUSDT,FLMUSDT": 0.0010131788026211629, "MANAUSDT,FLMUSDT": 0.0006384694922957697, "MATICUSDT,FLMUSDT": 0.0035669090640833294, "MKRUSDT,ATOMUSDT": 0.020477498011294597, "MKRUSDT,DEFIUSDT": 0.041870730878772314, "MKRUSDT,FLMUSDT": 0.0019924378437844122, "MOBUSDT,BCHUSDT": 0.0017994983868401802, "MOBUSDT,ETHUSDT": 0.040794550690082844, "MOBUSDT,FLMUSDT": 0.0038227683818031185, "MOBUSDT,HTUSDT": 0.010596029670915049, "MTLUSDT,FLMUSDT": 0.0014494487166060352, "NEARUSDT,AXSUSDT": 0.013023232770434795, "NEARUSDT,CELOUSDT": 0.010851570877618923, "NEARUSDT,CHZUSDT": 0.0004006176254115215, "NEARUSDT,DOTUSDT": 0.021984166816854285, "NEARUSDT,FIDAUSDT": 0.02001581814971851, "NEARUSDT,FILUSDT": 0.003006430217578686, "NEARUSDT,FLMUSDT": 0.001798738636271838, "NEARUSDT,KNCUSDT": 0.012321806030624804, "NEARUSDT,KSMUSDT": 0.02642484045546354, "NEARUSDT,LRCUSDT": 0.002283667545570626, "NEOUSDT,AMPLUSDT": 0.001448966864871151, "NEOUSDT,BALUSDT": 0.0015845065115341838, "NEOUSDT,BNTUSDT": 0.03801233114407577, "NEOUSDT,BTCUSDT": 0.005353173775464759, "NEOUSDT,ETHUSDT": 0.014278536860172322, "NEOUSDT,FIDAUSDT": 0.024170252420580164, "NEOUSDT,FLMUSDT": 0.006172671289416948, "NEOUSDT,HTUSDT": 0.0005206573709253244, "NEOUSDT,MOBUSDT": 0.028254271822687065, "OKBUSDT,FLMUSDT": 0.004801341758185223, "OMGUSDT,ADAUSDT": 0.035585336004874964, "OMGUSDT,AMPLUSDT": 0.0026636916941234295, "OMGUSDT,BALUSDT": 0.0013809176340263377, "OMGUSDT,BANDUSDT": 0.02106794684841616, "OMGUSDT,FIDAUSDT": 0.014577869701101106, "OMGUSDT,FLMUSDT": 0.003848302355726763, "OMGUSDT,KAVAUSDT": 0.036819601177695845, "OMGUSDT,NEOUSDT": 0.009431799199661726, "ONTUSDT,ALGOUSDT": 2.036769233478871e-05, "ONTUSDT,ALPHAUSDT": 0.02030361402188638, "ONTUSDT,AVAXUSDT": 0.0019089955250258792, "ONTUSDT,AXSUSDT": 0.041522883344868984, "ONTUSDT,BTTUSDT": 0.04468097459546838, "ONTUSDT,CELOUSDT": 0.04067106324543098, "ONTUSDT,CRVUSDT": 0.012312231319240573, "ONTUSDT,CVCUSDT": 0.0006646743850279007, "ONTUSDT,DASHUSDT": 0.0067342420462220085, "ONTUSDT,FIDAUSDT": 0.0011858496411692662, "ONTUSDT,FILUSDT": 0.011335880923643044, "ONTUSDT,FLMUSDT": 0.0020824396071673942, "ONTUSDT,ICXUSDT": 0.02236865726716677, "ONTUSDT,IOSTUSDT": 0.03099580656080898, "ONTUSDT,IOTAUSDT": 0.025785737027254445, "ONTUSDT,KAVAUSDT": 0.027896537566406913, "ONTUSDT,KNCUSDT": 0.003913696835836616, "ONTUSDT,KSMUSDT": 0.015332128447354704, "ONTUSDT,LRCUSDT": 0.003217027290072997, "PAXGUSDT,FLMUSDT": 0.004768567299030607, "PROMUSDT,ALGOUSDT": 0.03404319178471443, "PROMUSDT,ALPHAUSDT": 0.012447149043697078, "PROMUSDT,ATOMUSDT": 0.03612202786546335, "PROMUSDT,AVAXUSDT": 0.045122729297696895, "PROMUSDT,AXSUSDT": 0.012668654152889702, "PROMUSDT,BANDUSDT": 0.03299252889185528, "PROMUSDT,CVCUSDT": 0.03292763985817738, "PROMUSDT,DASHUSDT": 0.00604604706862167, "PROMUSDT,DEFIUSDT": 0.0025486290860320653, "PROMUSDT,DOGEUSDT": 0.002922718548456963, "PROMUSDT,DOTUSDT": 0.017811158470610478, "PROMUSDT,ENJUSDT": 0.02843009708553566, "PROMUSDT,FILUSDT": 0.014981359883064973, "PROMUSDT,FLMUSDT": 0.0021521150123676846, "PROMUSDT,FTMUSDT": 0.006212347669144764, "PROMUSDT,HBARUSDT": 0.012992953215321536, "PROMUSDT,IOSTUSDT": 0.0010494848953822545, "PROMUSDT,IOTAUSDT": 0.0010338914538743768, "PROMUSDT,KSMUSDT": 0.013244360818348714, "PROMUSDT,LRCUSDT": 0.03856355071803307, "PROMUSDT,LTCUSDT": 0.005661497437781802, "PROMUSDT,MKRUSDT": 0.00018561349015793445, "PROMUSDT,ONTUSDT": 0.022826361440139182, "QTUMUSDT,ALGOUSDT": 0.0488551078690702, "QTUMUSDT,BTTUSDT": 0.0026675679641835885, "QTUMUSDT,CHZUSDT": 0.048400277206336335, "QTUMUSDT,CRVUSDT": 0.0078667211694165, "QTUMUSDT,CVCUSDT": 0.0037634923995179155, "QTUMUSDT,FIDAUSDT": 1.4403998730922407e-06, "QTUMUSDT,FLMUSDT": 0.0025619062913233687, "QTUMUSDT,ICXUSDT": 0.03785598784498522, "QTUMUSDT,KAVAUSDT": 6.15856488814406e-06, "QTUMUSDT,KNCUSDT": 0.004868703303292993, "QTUMUSDT,LRCUSDT": 0.04648756071190772, "QTUMUSDT,ONTUSDT": 0.031235152216226258, "REEFUSDT,AAVEUSDT": 0.04890158049781746, "REEFUSDT,ATOMUSDT": 0.0157500290122172, "REEFUSDT,AVAXUSDT": 0.003697413309401625, "REEFUSDT,AXSUSDT": 9.15508148727188e-06, "REEFUSDT,CELOUSDT": 0.030738402714320354, "REEFUSDT,CHZUSDT": 0.011160998118737088, "REEFUSDT,COMPUSDT": 0.0449983891495594, "REEFUSDT,CRVUSDT": 0.015553976926688097, "REEFUSDT,DASHUSDT": 0.00016069214766072836, "REEFUSDT,DEFIUSDT": 0.041810651197984675, "REEFUSDT,DOGEUSDT": 0.00761790684046117, "REEFUSDT,DOTUSDT": 0.005941409783780225, "REEFUSDT,ENJUSDT": 0.026491932498471153, "REEFUSDT,FILUSDT": 0.00045254454292289103, "REEFUSDT,FLMUSDT": 0.001156619999953199, "REEFUSDT,FTMUSDT": 0.0007152021515656899, "REEFUSDT,GRTUSDT": 0.04765524699450983, "REEFUSDT,HBARUSDT": 0.018683042031635823, "REEFUSDT,ICXUSDT": 0.018454214148720102, "REEFUSDT,IOSTUSDT": 0.02465083234595365, "REEFUSDT,IOTAUSDT": 0.002281186586958181, "REEFUSDT,KNCUSDT": 0.0069380283088876825, "REEFUSDT,KSMUSDT": 0.001723258976258314, "REEFUSDT,LRCUSDT": 0.0018477314046342396, "REEFUSDT,MKRUSDT": 0.038014686799456965, "REEFUSDT,NEARUSDT": 0.002010950280740269, "REEFUSDT,PROMUSDT": 0.008986469007478765, "RENUSDT,ALGOUSDT": 0.0003701841108256967, "RENUSDT,DASHUSDT": 0.01662589934200319, "RENUSDT,DEFIUSDT": 0.04573722300193576, "RENUSDT,DOGEUSDT": 0.005962097724674798, "RENUSDT,FLMUSDT": 0.0020320627073500885, "RENUSDT,IOTAUSDT": 0.0056788087607853935, "RENUSDT,PROMUSDT": 0.0002898028320458063, "RENUSDT,REEFUSDT": 0.02423125541046214, "RSRUSDT,FLMUSDT": 0.005803490538227628, "RUNEUSDT,ALGOUSDT": 0.02333995350062181, "RUNEUSDT,AMPLUSDT": 0.04712627338571015, "RUNEUSDT,BTTUSDT": 0.03710919101335001, "RUNEUSDT,CVCUSDT": 0.03434733722886858, "RUNEUSDT,FLMUSDT": 0.00307131542850565, "RUNEUSDT,LRCUSDT": 0.03456940912672978, "RUNEUSDT,NEOUSDT": 0.04951393353271067, "RUNEUSDT,REEFUSDT": 0.047668541641895636, "SANDUSDT,COMPUSDT": 0.013038862390780531, "SANDUSDT,ENJUSDT": 0.005358833636121233, "SANDUSDT,FLMUSDT": 0.0018326237872974412, "SANDUSDT,REEFUSDT": 0.021654912721489675, "SCRTUSDT,CHZUSDT": 0.036045084382476195, "SCRTUSDT,DOTUSDT": 0.026217310988750374, "SCRTUSDT,FLMUSDT": 0.0017920343299807342, "SCRTUSDT,KSMUSDT": 0.04995134965579745, "SCRTUSDT,REEFUSDT": 0.029556842144645473, "SCUSDT,AAVEUSDT": 0.04380863887919948, "SCUSDT,ALGOUSDT": 0.008178103097814629, "SCUSDT,BTTUSDT": 0.04895686968116287, "SCUSDT,CELOUSDT": 0.017192075688163423, "SCUSDT,CHZUSDT": 0.023917119694976736, "SCUSDT,CRVUSDT": 0.014398667028804522, "SCUSDT,CVCUSDT": 0.007131441665841582, "SCUSDT,DOTUSDT": 0.023104370396982085, "SCUSDT,EOSUSDT": 0.004326038269185382, "SCUSDT,ETCUSDT": 2.6870354037278066e-05, "SCUSDT,FIDAUSDT": 0.00021998581228458014, "SCUSDT,FILUSDT": 0.0015402563242819264, "SCUSDT,FLMUSDT": 0.0022239773986181834, "SCUSDT,GRTUSDT": 0.014235330300512848, "SCUSDT,HBARUSDT": 0.0073174452526144915, "SCUSDT,ICXUSDT": 0.0001640120472720212, "SCUSDT,IOSTUSDT": 0.04098462821245138, "SCUSDT,IOTAUSDT": 0.024769339157323482, "SCUSDT,KAVAUSDT": 0.010052414899447581, "SCUSDT,KNCUSDT": 0.005235702537364501, "SCUSDT,KSMUSDT": 0.008722152313004237, "SCUSDT,LRCUSDT": 0.008423935087033332, "SCUSDT,ONTUSDT": 0.017799887515056893, "SCUSDT,PROMUSDT": 0.003667717295957192, "SCUSDT,QTUMUSDT": 0.013332619634296944, "SCUSDT,REEFUSDT": 0.017020187598056617, "SHIBUSDT,FLMUSDT": 0.001740246156206749, "SKLUSDT,ADAUSDT": 0.017070679202316644, "SKLUSDT,ALGOUSDT": 0.008646279054165052, "SKLUSDT,BALUSDT": 0.03871880715102231, "SKLUSDT,FLMUSDT": 0.0019230658515207674, "SKLUSDT,KNCUSDT": 0.03739099955283449, "SNXUSDT,FLMUSDT": 0.003772535366859357, "SOLUSDT,BATUSDT": 0.020549218909927232, "SOLUSDT,FLMUSDT": 0.001188363838204641, "SRMUSDT,FLMUSDT": 0.0024205934941162847, "SRMUSDT,LRCUSDT": 0.04589065920846142, "SRMUSDT,NEARUSDT": 0.04046386523410474, "SRMUSDT,REEFUSDT": 0.034117865162054554, "STORJUSDT,FLMUSDT": 0.003232836232521096, "STXUSDT,ALGOUSDT": 0.0039239272056957, "STXUSDT,BANDUSDT": 0.04932677251724406, "STXUSDT,BTTUSDT": 0.01952327103436089, "STXUSDT,CHZUSDT": 0.04216338812580686, "STXUSDT,CRVUSDT": 0.04774465246183237, "STXUSDT,CVCUSDT": 0.006694077985224017, "STXUSDT,DOTUSDT": 0.030788816075547863, "STXUSDT,EOSUSDT": 0.011105087232517434, "STXUSDT,ETCUSDT": 0.013664047900470728, "STXUSDT,FIDAUSDT": 0.001442556204940238, "STXUSDT,FILUSDT": 0.016915935639735397, "STXUSDT,FLMUSDT": 0.0024515498857863244, "STXUSDT,GRTUSDT": 0.046518367901828735, "STXUSDT,ICXUSDT": 0.003496854060519769, "STXUSDT,IOTAUSDT": 0.034944394573318986, "STXUSDT,KAVAUSDT": 0.0035167475017309213, "STXUSDT,KNCUSDT": 0.002246501168492723, "STXUSDT,KSMUSDT": 0.021408547929882376, "STXUSDT,LRCUSDT": 0.0031079947032024656, "STXUSDT,ONTUSDT": 0.02484339255298218, "STXUSDT,PROMUSDT": 0.0025504291040420093, "STXUSDT,QTUMUSDT": 0.031161884285801886, "STXUSDT,REEFUSDT": 0.03416099671978516, "STXUSDT,SCUSDT": 0.0019746941175236674, "STXUSDT,SRMUSDT": 0.04977258330857474, "SUSHIUSDT,CVCUSDT": 0.04697606341944317, "SUSHIUSDT,DOTUSDT": 0.022079986753855828, "SUSHIUSDT,FLMUSDT": 0.0015130252215469525, "SUSHIUSDT,IOSTUSDT": 0.000458590554265079, "SUSHIUSDT,IOTAUSDT": 0.0020092889606038253, "SUSHIUSDT,KSMUSDT": 0.014236994964919836, "SUSHIUSDT,PROMUSDT": 0.0050713898444611875, "SUSHIUSDT,REEFUSDT": 0.011554993192654064, "SXPUSDT,AAVEUSDT": 0.035286809617062626, "SXPUSDT,ALGOUSDT": 0.016230010635825756, "SXPUSDT,ATOMUSDT": 0.04847953934508812, "SXPUSDT,AXSUSDT": 0.009829599645690702, "SXPUSDT,CELOUSDT": 0.01269230518535518, "SXPUSDT,CHZUSDT": 0.04405845920115577, "SXPUSDT,CRVUSDT": 0.03386054785382735, "SXPUSDT,DASHUSDT": 3.548477457182712e-05, "SXPUSDT,DEFIUSDT": 0.044879258154963034, "SXPUSDT,DOGEUSDT": 0.00913239917084117, "SXPUSDT,DOTUSDT": 0.0008526178896418169, "SXPUSDT,ETCUSDT": 0.041751797558797175, "SXPUSDT,FILUSDT": 0.0023532259317053557, "SXPUSDT,FLMUSDT": 0.0012686412660553288, "SXPUSDT,FTMUSDT": 0.001301239235068163, "SXPUSDT,GRTUSDT": 0.01490191661352548, "SXPUSDT,HBARUSDT": 0.0015888947155246115, "SXPUSDT,ICXUSDT": 0.04870274730170938, "SXPUSDT,IOSTUSDT": 0.008747031391818094, "SXPUSDT,IOTAUSDT": 0.0023086871522977072, "SXPUSDT,KAVAUSDT": 0.0354836032032417, "SXPUSDT,KNCUSDT": 0.0024165148305755136, "SXPUSDT,KSMUSDT": 0.0003754865281620907, "SXPUSDT,LRCUSDT": 0.008241099057637466, "SXPUSDT,PROMUSDT": 0.004213562065756336, "SXPUSDT,REEFUSDT": 0.0007512756901349505, "SXPUSDT,RENUSDT": 0.04682995176092608, "SXPUSDT,SCRTUSDT": 0.018552770504485096, "SXPUSDT,SUSHIUSDT": 0.0011132266092109071, "THETAUSDT,FLMUSDT": 0.003565698229961193, "THETAUSDT,RSRUSDT": 0.03376235906153123, "TOMOUSDT,ALGOUSDT": 0.03987860388639419, "TOMOUSDT,ALPHAUSDT": 0.02602608635584281, "TOMOUSDT,BANDUSDT": 0.030134395927809962, "TOMOUSDT,FLMUSDT": 0.002814684120418087, "TOMOUSDT,PROMUSDT": 0.015958433519359678, "TRUUSDT,ALPHAUSDT": 0.008481482353211275, "TRUUSDT,AMPLUSDT": 0.04596037818238127, "TRUUSDT,AVAXUSDT": 0.027696137045157387, "TRUUSDT,BANDUSDT": 0.032229547995114634, "TRUUSDT,ENJUSDT": 0.031140929069015257, "TRUUSDT,FLMUSDT": 0.0034581219628025735, "TRUUSDT,LRCUSDT": 0.037770082247172425, "TRUUSDT,NEOUSDT": 0.02822498241686513, "TRUUSDT,REEFUSDT": 0.03816410963594815, "TRUUSDT,RUNEUSDT": 0.03429484644031829, "TRUUSDT,SRMUSDT": 0.0025321116409674155, "TRXUSDT,BNBUSDT": 0.029493712572519072, "TRXUSDT,BTTUSDT": 0.027685593497804487, "TRXUSDT,CROUSDT": 0.023824701671446933, "TRXUSDT,CRVUSDT": 0.03753492131702335, "TRXUSDT,EOSUSDT": 0.014127636422748828, "TRXUSDT,FIDAUSDT": 0.03443031980125684, "TRXUSDT,FLMUSDT": 0.004088627646797044, "TRXUSDT,KAVAUSDT": 0.042635167386125716, "TRXUSDT,KNCUSDT": 0.02092048659795334, "TRXUSDT,STXUSDT": 0.016148769994196375, "UNISWAPUSDT,ALGOUSDT": 0.019800222846896615, "UNISWAPUSDT,AXSUSDT": 0.006062467524597521, "UNISWAPUSDT,CHZUSDT": 0.031832509200753305, "UNISWAPUSDT,CVCUSDT": 0.008015069430314489, "UNISWAPUSDT,DASHUSDT": 0.009544577485524345, "UNISWAPUSDT,DOGEUSDT": 0.016044147029509764, "UNISWAPUSDT,DOTUSDT": 0.010258964238445802, "UNISWAPUSDT,ENJUSDT": 0.012147614433073205, "UNISWAPUSDT,FILUSDT": 0.012108161078862627, "UNISWAPUSDT,FLMUSDT": 0.002115943534373926, "UNISWAPUSDT,FTMUSDT": 0.024102649276850515, "UNISWAPUSDT,IOSTUSDT": 0.04966393414347752, "UNISWAPUSDT,IOTAUSDT": 0.009356864536918603, "UNISWAPUSDT,KSMUSDT": 0.043728209508922936, "UNISWAPUSDT,LRCUSDT": 0.0011521693167488155, "UNISWAPUSDT,NEARUSDT": 0.016734228949822613, "UNISWAPUSDT,ONTUSDT": 0.02041852827601641, "UNISWAPUSDT,PROMUSDT": 0.01162357932427326, "UNISWAPUSDT,REEFUSDT": 0.0006606906925923309, "UNISWAPUSDT,RENUSDT": 0.01692294877074415, "UNISWAPUSDT,SRMUSDT": 0.013628435417689969, "UNISWAPUSDT,STXUSDT": 0.042900548064621034, "UNISWAPUSDT,SXPUSDT": 0.018098046987598575, "UNIUSDT,FLMUSDT": 0.0018506230836658143, "UNIUSDT,SHIBUSDT": 0.03547409583627695, "UNIUSDT,SOLUSDT": 0.031033636986304, "USDTUSDT,AAVEUSDT": 0.029612769225889304, "USDTUSDT,ADAUSDT": 0.020983125960117404, "USDTUSDT,ALGOUSDT": 0.0008069952957693363, "USDTUSDT,ALPHAUSDT": 0.001666595664755716, "USDTUSDT,ATOMUSDT": 0.0007820441286830578, "USDTUSDT,AVAXUSDT": 0.002600154160982968, "USDTUSDT,AXSUSDT": 0.0019808202667148936, "USDTUSDT,BALUSDT": 0.030997149680397438, "USDTUSDT,BANDUSDT": 0.006009222726760649, "USDTUSDT,BATUSDT": 0.0055128847682675685, "USDTUSDT,BNBUSDT": 0.04179469933238322, "USDTUSDT,CELOUSDT": 0.03624534758673706, "USDTUSDT,CHZUSDT": 0.02977584083476047, "USDTUSDT,COMPUSDT": 0.0017699081584441768, "USDTUSDT,CRVUSDT": 0.029736775702223665, "USDTUSDT,CVCUSDT": 0.008762283774224068, "USDTUSDT,DASHUSDT": 0.0011525521256656713, "USDTUSDT,DEFIUSDT": 8.171477263771448e-05, "USDTUSDT,DOGEUSDT": 0.0019385390131724121, "USDTUSDT,DOTUSDT": 0.0043501911267748395, "USDTUSDT,EGLDUSDT": 0.022283821187753596, "USDTUSDT,ENJUSDT": 0.0006951688405739971, "USDTUSDT,EOSUSDT": 0.03742112854728778, "USDTUSDT,ETCUSDT": 0.028235632647395554, "USDTUSDT,FIDAUSDT": 0.03382755935260642, "USDTUSDT,FILUSDT": 0.009229749008954269, "USDTUSDT,FLMUSDT": 0.004852904179896053, "USDTUSDT,FTMUSDT": 0.0016912613801898344, "USDTUSDT,FTTUSDT": 0.007086383067586981, "USDTUSDT,GRTUSDT": 0.033508024106302, "USDTUSDT,HBARUSDT": 0.015495777818817821, "USDTUSDT,ICXUSDT": 0.016597204442088093, "USDTUSDT,IOSTUSDT": 0.00227307963923335, "USDTUSDT,IOTAUSDT": 0.0012134139641067688, "USDTUSDT,KAVAUSDT": 0.028801080338254032, "USDTUSDT,KNCUSDT": 0.01692708493235635, "USDTUSDT,KSMUSDT": 0.003493566131792217, "USDTUSDT,LINKUSDT": 0.01638215028723887, "USDTUSDT,LRCUSDT": 0.005303145021531837, "USDTUSDT,LTCUSDT": 0.00043794220688200933, "USDTUSDT,MANAUSDT": 0.0007913208615307184, "USDTUSDT,MATICUSDT": 0.00047585643314784044, "USDTUSDT,MKRUSDT": 0.00030529836376818936, "USDTUSDT,NEARUSDT": 0.021775641690928726, "USDTUSDT,OMGUSDT": 0.03557984459337077, "USDTUSDT,ONTUSDT": 0.007392439807266153, "USDTUSDT,PROMUSDT": 0.0006447004652566325, "USDTUSDT,QTUMUSDT": 0.01835240089294022, "USDTUSDT,REEFUSDT": 0.0026202660402882163, "USDTUSDT,RENUSDT": 0.00025922942895240494, "USDTUSDT,RUNEUSDT": 0.006607458061907057, "USDTUSDT,SANDUSDT": 0.0008376460516545174, "USDTUSDT,SCRTUSDT": 0.0415857227630281, "USDTUSDT,SCUSDT": 0.02603183807810936, "USDTUSDT,SHIBUSDT": 9.457799046284351e-05, "USDTUSDT,SKLUSDT": 0.007926675163810857, "USDTUSDT,SOLUSDT": 4.401210638388864e-05, "USDTUSDT,SRMUSDT": 0.01374053945169743, "USDTUSDT,STXUSDT": 0.008027320534803335, "USDTUSDT,SUSHIUSDT": 0.0015241332642952471, "USDTUSDT,SXPUSDT": 0.00888308700752016, "USDTUSDT,THETAUSDT": 0.04769147428506783, "USDTUSDT,TOMOUSDT": 0.00027916566275371974, "USDTUSDT,TRUUSDT": 0.018644853426114192, "USDTUSDT,UNISWAPUSDT": 0.0015825086952030044, "USDTUSDT,UNIUSDT": 0.0005581640956771505, "VETUSDT,AMPLUSDT": 0.043299408385414226, "VETUSDT,BALUSDT": 0.04791171338650222, "VETUSDT,BTTUSDT": 0.0010727842567183957, "VETUSDT,CROUSDT": 0.049086699449137766, "VETUSDT,CRVUSDT": 0.018913790761400606, "VETUSDT,FIDAUSDT": 6.781412653868876e-06, "VETUSDT,FLMUSDT": 0.0029660348400762813, "VETUSDT,KAVAUSDT": 0.00010237569324805494, "VETUSDT,KNCUSDT": 0.019257779864674602, "VETUSDT,NEARUSDT": 0.026022045893247504, "VETUSDT,NEOUSDT": 0.03322726711150684, "VETUSDT,TRUUSDT": 0.0058582381358503475, "VETUSDT,USDTUSDT": 5.6368736676228347e-05, "WAVESUSDT,DOTUSDT": 0.018969565646200233, "WAVESUSDT,FILUSDT": 0.03206746831533251, "WAVESUSDT,FLMUSDT": 0.00126867645252369, "WAVESUSDT,FTMUSDT": 0.01261104792259986, "WAVESUSDT,IOSTUSDT": 0.0291101291001367, "WAVESUSDT,KSMUSDT": 0.002840931024247495, "WAVESUSDT,PROMUSDT": 0.01672245159024032, "WAVESUSDT,REEFUSDT": 0.04881602623152364, "WAVESUSDT,SCRTUSDT": 0.0045432185812719775, "WAVESUSDT,SUSHIUSDT": 0.0010112868234988717, "WAVESUSDT,UNISWAPUSDT": 0.023739152904102466, "WAVESUSDT,USDTUSDT": 4.4207161376494747e-05, "XAUTUSDT,FLMUSDT": 0.006826232244958344, "XAUTUSDT,PAXGUSDT": 8.889085382894878e-05, "XAUTUSDT,USDTUSDT": 0.010359589324732826, "XEMUSDT,AAVEUSDT": 0.0008779787032566408, "XEMUSDT,ALGOUSDT": 0.011069608024832398, "XEMUSDT,AVAXUSDT": 0.01926650592294639, "XEMUSDT,AXSUSDT": 0.016026730729969896, "XEMUSDT,BTTUSDT": 0.014836652674088777, "XEMUSDT,CELOUSDT": 0.00187336671968987, "XEMUSDT,CHZUSDT": 0.008557518306819578, "XEMUSDT,CRVUSDT": 9.175113992229717e-05, "XEMUSDT,CVCUSDT": 0.0020723430671544204, "XEMUSDT,DASHUSDT": 0.006603941074226261, "XEMUSDT,DOGEUSDT": 0.006774419652496411, "XEMUSDT,DOTUSDT": 0.0030606761558975753, "XEMUSDT,EOSUSDT": 0.009249785592024985, "XEMUSDT,ETCUSDT": 0.00033522947304002613, "XEMUSDT,FIDAUSDT": 0.0007049861264719959, "XEMUSDT,FILUSDT": 1.3132133488621819e-05, "XEMUSDT,FLMUSDT": 0.0019844562574706827, "XEMUSDT,GRTUSDT": 0.010324545604590419, "XEMUSDT,HBARUSDT": 0.004010379095071215, "XEMUSDT,ICXUSDT": 5.626957139341534e-06, "XEMUSDT,IOSTUSDT": 0.042277677577738725, "XEMUSDT,IOTAUSDT": 0.017837086108252843, "XEMUSDT,KAVAUSDT": 0.00023955874803878002, "XEMUSDT,KNCUSDT": 0.003200270816941797, "XEMUSDT,KSMUSDT": 0.00843484752472213, "XEMUSDT,LRCUSDT": 5.2421172587785265e-06, "XEMUSDT,NEARUSDT": 0.00022971276063142137, "XEMUSDT,ONTUSDT": 0.005009055329290589, "XEMUSDT,PROMUSDT": 0.011485489945233036, "XEMUSDT,QTUMUSDT": 0.0015794281541264238, "XEMUSDT,REEFUSDT": 0.0002469591719159854, "XEMUSDT,RENUSDT": 0.018032395601114855, "XEMUSDT,RUNEUSDT": 0.0437222153877078, "XEMUSDT,SCRTUSDT": 0.04694065303912346, "XEMUSDT,SCUSDT": 3.2484318791278023e-06, "XEMUSDT,STXUSDT": 0.00025134191280878665, "XEMUSDT,SUSHIUSDT": 0.031047697494079308, "XEMUSDT,SXPUSDT": 0.00813071504006638, "XEMUSDT,UNISWAPUSDT": 0.00017099933065898174, "XEMUSDT,USDTUSDT": 2.2734772337163008e-05, "XLMUSDT,AVAXUSDT": 0.026145438258198678, "XLMUSDT,AXSUSDT": 0.007531046640342792, "XLMUSDT,CHZUSDT": 0.01479934752752459, "XLMUSDT,DASHUSDT": 0.0021512228714145143, "XLMUSDT,DOGEUSDT": 0.041925491194354694, "XLMUSDT,DOTUSDT": 0.024916400440595638, "XLMUSDT,FIDAUSDT": 0.03909350781444745, "XLMUSDT,FILUSDT": 0.01701902140105329, "XLMUSDT,FLMUSDT": 0.001420400445769774, "XLMUSDT,GRTUSDT": 0.035188384105845696, "XLMUSDT,IOTAUSDT": 0.013182554203333034, "XLMUSDT,KNCUSDT": 0.037462826450504964, "XLMUSDT,KSMUSDT": 0.04732533044966782, "XLMUSDT,LRCUSDT": 0.020901418673474323, "XLMUSDT,NEARUSDT": 0.0034485719525814704, "XLMUSDT,PROMUSDT": 0.020235883596613675, "XLMUSDT,REEFUSDT": 0.0006876061713117303, "XLMUSDT,SXPUSDT": 0.0043635566030287495, "XLMUSDT,UNISWAPUSDT": 0.015548002389251392, "XLMUSDT,USDTUSDT": 4.399957390829485e-06, "XLMUSDT,XEMUSDT": 0.026989357691582762, "XMRUSDT,BNBUSDT": 0.036334304168913804, "XMRUSDT,BTTUSDT": 0.023688498666928497, "XMRUSDT,FIDAUSDT": 0.02169140749944157, "XMRUSDT,FLMUSDT": 0.003810566016131199, "XMRUSDT,MOBUSDT": 0.0011574258612693281, "XMRUSDT,NEOUSDT": 0.032668534444146385, "XMRUSDT,TRXUSDT": 0.010565646161008744, "XMRUSDT,USDTUSDT": 0.0014375724576957, "XRPUSDT,ALGOUSDT": 0.00728665938445221, "XRPUSDT,ALPHAUSDT": 0.003954467314416081, "XRPUSDT,AVAXUSDT": 0.008129376499657918, "XRPUSDT,CVCUSDT": 0.02898859876188555, "XRPUSDT,DASHUSDT": 0.012087962747909683, "XRPUSDT,ENJUSDT": 0.004953352633264683, "XRPUSDT,FLMUSDT": 0.0024816319042734614, "XRPUSDT,LRCUSDT": 0.04771282304580275, "XRPUSDT,ONTUSDT": 0.033028276490002235, "XRPUSDT,PROMUSDT": 0.007105974111247416, "XRPUSDT,REEFUSDT": 0.0178862652004642, "XRPUSDT,RENUSDT": 0.0177137978411115, "XRPUSDT,RUNEUSDT": 0.03790366702737088, "XRPUSDT,SRMUSDT": 0.04164106593547119, "XRPUSDT,TOMOUSDT": 0.02751373117744566, "XRPUSDT,TRUUSDT": 0.02555354701750726, "XRPUSDT,UNISWAPUSDT": 0.01934033965569992, "XRPUSDT,USDTUSDT": 8.34319466039881e-07, "XTZUSDT,ADAUSDT": 0.013920413358169122, "XTZUSDT,AMPLUSDT": 0.002742009163573591, "XTZUSDT,BALUSDT": 0.0082908316321696, "XTZUSDT,BANDUSDT": 0.012210196988739241, "XTZUSDT,FIDAUSDT": 0.017367243208205704, "XTZUSDT,FLMUSDT": 0.003860781300622418, "XTZUSDT,KAVAUSDT": 0.04808209283042473, "XTZUSDT,NEOUSDT": 0.009459926452814627, "XTZUSDT,OMGUSDT": 0.0011211305519590173, "XTZUSDT,USDTUSDT": 0.0002018750935340851, "YFIIUSDT,FLMUSDT": 0.0043034123630513385, "YFIIUSDT,OKBUSDT": 0.004274562780435172, "YFIIUSDT,THETAUSDT": 0.04906004537393532, "YFIIUSDT,USDTUSDT": 0.01782156253561852, "YFIUSDT,AVAXUSDT": 0.02033344406417847, "YFIUSDT,AXSUSDT": 0.014741237572599356, "YFIUSDT,DASHUSDT": 0.04677668940069246, "YFIUSDT,ENJUSDT": 0.0009877216952445967, "YFIUSDT,FLMUSDT": 0.0029559615453181648, "YFIUSDT,PROMUSDT": 0.030651969808535563, "YFIUSDT,REEFUSDT": 0.03284315589007541, "YFIUSDT,SRMUSDT": 0.022041221817141254, "YFIUSDT,TRUUSDT": 0.02912230005347802, "YFIUSDT,USDTUSDT": 9.94062986348481e-07, "YFIUSDT,XLMUSDT": 0.033404161047018405, "YFIUSDT,XRPUSDT": 0.0002784244559523462, "ZECUSDT,BTTUSDT": 0.011332553626494504, "ZECUSDT,CROUSDT": 0.021007868698347906, "ZECUSDT,EOSUSDT": 0.020133263258580107, "ZECUSDT,ETHUSDT": 0.008343093588150764, "ZECUSDT,FIDAUSDT": 2.61046516943249e-06, "ZECUSDT,FLMUSDT": 0.003675876826334553, "ZECUSDT,KAVAUSDT": 0.010611073430946541, "ZECUSDT,KNCUSDT": 0.02182560300308983, "ZECUSDT,MOBUSDT": 0.0439026247329861, "ZECUSDT,NEOUSDT": 0.013485415965486068, "ZECUSDT,OMGUSDT": 0.03454548111453883, "ZECUSDT,TRXUSDT": 0.020217013560755868, "ZECUSDT,USDTUSDT": 0.0002657856159974692, "ZECUSDT,VETUSDT": 0.03737934493313479, "ZILUSDT,ATOMUSDT": 0.01162317274513925, "ZILUSDT,COMPUSDT": 0.004083301906352081, "ZILUSDT,DEFIUSDT": 0.021891901817455595, "ZILUSDT,ENJUSDT": 0.01734820041218418, "ZILUSDT,FLMUSDT": 0.0015826300686343055, "ZILUSDT,MANAUSDT": 0.010985675395330161, "ZILUSDT,MKRUSDT": 0.03452853364625104, "ZILUSDT,PROMUSDT": 0.02362359807471853, "ZILUSDT,USDTUSDT": 1.9936230745524136e-08, "ZRXUSDT,ALGOUSDT": 0.005248996745967613, "ZRXUSDT,AVAXUSDT": 0.03837937236793114, "ZRXUSDT,AXSUSDT": 0.031729560453571386, "ZRXUSDT,CELOUSDT": 0.033672779225276, "ZRXUSDT,CHZUSDT": 0.017275435752187176, "ZRXUSDT,CRVUSDT": 0.02326468363060266, "ZRXUSDT,CVCUSDT": 0.003580952240659087, "ZRXUSDT,DOGEUSDT": 0.03411608710576751, "ZRXUSDT,DOTUSDT": 0.011435164158064545, "ZRXUSDT,FIDAUSDT": 0.015827128216471274, "ZRXUSDT,FILUSDT": 0.007016893082967584, "ZRXUSDT,FLMUSDT": 0.002126396675638565, "ZRXUSDT,GRTUSDT": 0.030589715825212277, "ZRXUSDT,ICXUSDT": 0.0003222192762778724, "ZRXUSDT,IOTAUSDT": 0.04010314394665651, "ZRXUSDT,KAVAUSDT": 0.03186627180867709, "ZRXUSDT,KNCUSDT": 0.016263955468816683, "ZRXUSDT,KSMUSDT": 0.03437983172565192, "ZRXUSDT,LRCUSDT": 0.0003769243116893601, "ZRXUSDT,NEARUSDT": 0.013798785111293394, "ZRXUSDT,ONTUSDT": 0.01631962300533217, "ZRXUSDT,PROMUSDT": 0.04411577158117662, "ZRXUSDT,QTUMUSDT": 0.027345999271175037, "ZRXUSDT,REEFUSDT": 0.0004083867528717806, "ZRXUSDT,SCUSDT": 0.007650912758668612, "ZRXUSDT,SRMUSDT": 0.04942421225022326, "ZRXUSDT,STXUSDT": 0.00873517537620481, "ZRXUSDT,UNISWAPUSDT": 0.043827701294539945, "ZRXUSDT,USDTUSDT": 1.4533937134265675e-05, "ZRXUSDT,XEMUSDT": 2.051342058473623e-07, "ZRXUSDT,XLMUSDT": 0.027389354109926007}, "start": "2022-06-01 00:00:00", "end": "2022-06-26 12:00:00", "timeframe": "5Min"} -------------------------------------------------------------------------------- /models/statarb/dsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "kdb::ftx.quotes": { 3 | "type" : "kdb", 4 | "host" : "localhost", 5 | "db_path" : "/var/databases/kdb/ftx/quotes", 6 | "ports_from": 6700, 7 | "ports_to": 6790, 8 | "init": "../../../data/databases_scripts/quotes.q", 9 | "timeout" : 30, 10 | "load_by_days":true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /models/statarb/utils.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import os 4 | from os.path import join 5 | from tqdm.auto import tqdm 6 | from ira.datasource import DataSource 7 | from ira.analysis.kalman import kf_smoother, kalman_regression_estimator 8 | from ira.analysis.tools import scols 9 | 10 | 11 | def norm(xs): 12 | """ 13 | Just small helper 14 | """ 15 | return xs / xs.iloc[0] if isinstance(xs, (pd.Series, pd.DataFrame)) else xs / xs[0] 16 | 17 | 18 | def load_ohlc(timeframe, start='2022-06-01', end='2022-07-05'): 19 | """ 20 | Data loader from Kdb/Q 21 | """ 22 | md = {} 23 | tf_sec = pd.Timedelta(timeframe).seconds 24 | with DataSource('kdb::ftx.quotes', join(os.getcwd(), 'dsconfig.json')) as ds: 25 | for s in tqdm(ds.series_list()): 26 | if not s[0].isdigit(): 27 | md[s] = ds.load_data(s, start, end, timeframe=tf_sec)[s] 28 | return md 29 | 30 | 31 | def ksmooth(x, pv, mv): 32 | """ 33 | Kalman filter for smoothing series x 34 | """ 35 | s, cvr = kf_smoother(x, pv, mv) 36 | return pd.DataFrame(np.array([s,cvr]).T, index=x.index, columns=['x', 'cvr']) 37 | 38 | 39 | def merge_columns_by_op(x: pd.DataFrame, y: pd.DataFrame, op): 40 | """ 41 | Merge 2 dataframes into one and performing operation on intersected columns 42 | 43 | merge_columns_by_op( 44 | pd.DataFrame({'A': [1,2,3], 'B': [100,200,300]}), 45 | pd.DataFrame({'B': [5,6,7], 'C': [10,20,30]}), 46 | lambda x,y: x + y 47 | ) 48 | 49 | B A C 50 | 0 105 1 10 51 | 1 206 2 20 52 | 2 307 3 30 53 | 54 | """ 55 | if x is None or x.empty: return y 56 | if y is None: return x 57 | r = [] 58 | uc = set(x.columns & y.columns) 59 | for c in uc: 60 | r.append(op(x[c], y[c])) 61 | 62 | for c in set(x.columns) - uc: 63 | r.append(x[c]) 64 | 65 | for c in set(y.columns) - uc: 66 | r.append(y[c]) 67 | 68 | return scols(*r) -------------------------------------------------------------------------------- /required.txt: -------------------------------------------------------------------------------- 1 | # There is list of required packages 2 | numpy 3 | pandas 4 | pandas-datareader 5 | sklearn 6 | matplotlib 7 | statsmodels 8 | patool 9 | airspeed 10 | bs4 11 | dill 12 | numba 13 | seaborn 14 | tqdm 15 | python-binance 16 | 17 | -------------------------------------------------------------------------------- /tools/analysis/__init__.py: -------------------------------------------------------------------------------- 1 | from .drawdown import absmaxdd, dd_freq_stats 2 | from .tools import add_constant, shift, column_vector, isscalar, sink_nans_down, lift_nans_up, nans 3 | -------------------------------------------------------------------------------- /tools/analysis/data.py: -------------------------------------------------------------------------------- 1 | import types 2 | import pandas as pd 3 | import numpy as np 4 | from typing import Union 5 | 6 | from pandas.core.generic import NDFrame 7 | from itertools import product 8 | 9 | 10 | def make_forward_returns_matrix(x: pd.DataFrame, n_forward_bars=1, use_open_close=True, use_usd_rets=False, shift=-1): 11 | """ 12 | Forward returns matrix generator 13 | -------------------------------- 14 | :param x: OHLC data frame 15 | :param n_forward_bars: number of bars used for forward returns calculations (F1 - 1 bar, F2 - 2 bars, ... ) 16 | :param use_open_close: return = close/open - 1 (True) 17 | :param use_usd_rets: use raw difference as returns instead of percentage (False) 18 | :param shift: number of bars to be shifted (default is -1 - one bar back) 19 | """ 20 | n_forward_bars = max(abs(n_forward_bars),1) 21 | d = {} 22 | 23 | for i in range(1, n_forward_bars + 1): 24 | n_ = 'F%d' % i 25 | if use_open_close: 26 | if use_usd_rets: 27 | d.update({n_ : x.close.shift(-i+1) - x.open}) 28 | else: 29 | d.update({n_ : x.close.shift(-i+1) / x.open - 1}) 30 | else: 31 | if use_usd_rets: 32 | d.update({n_ : x.close.shift(-i) - x.close}) 33 | else: 34 | d.update({n_ : x.close.shift(-i) / x.close - 1}) 35 | 36 | # we need forward return at current moment so shift back it 37 | return pd.DataFrame(d, index=x.index).shift(shift) 38 | 39 | 40 | def retain_columns_and_join(data: dict, columns): 41 | """ 42 | Retains given columns from every value of data dictionary and concatenate them into single data frame 43 | 44 | closes = retain_columns_and_join(data, 'close') 45 | hi_lo = retain_columns_and_join(data, ['high', 'low']) 46 | 47 | :param data: dictionary with dataframes 48 | :param columns: columns names need to be retained 49 | :return: data frame 50 | """ 51 | if not isinstance(data, dict): 52 | raise ValueError('Data must be passed as dictionary') 53 | 54 | return pd.concat([data[k][columns] for k in data.keys()], axis=1, keys=data.keys()) 55 | 56 | 57 | def permutate_params(parameters: dict, conditions:Union[types.FunctionType, list, tuple]=None) -> list([dict]): 58 | """ 59 | Generate list of all permutations for given parameters and it's possible values 60 | 61 | Example: 62 | 63 | >>> def foo(par1, par2): 64 | >>> print(par1) 65 | >>> print(par2) 66 | >>> 67 | >>> # permutate all values and call function for every permutation 68 | >>> [foo(**z) for z in permutate_params({ 69 | >>> 'par1' : [1,2,3], 70 | >>> 'par2' : [True, False] 71 | >>> }, conditions=lambda par1, par2: par1<=2 and par2==True)] 72 | 73 | 1 74 | True 75 | 2 76 | True 77 | 78 | :param conditions: list of filtering functions 79 | :param parameters: dictionary 80 | :return: list of permutations 81 | """ 82 | if conditions is None: 83 | conditions = [] 84 | elif isinstance(conditions, types.FunctionType): 85 | conditions = [conditions] 86 | elif isinstance(conditions, (tuple, list)): 87 | if not all([isinstance(e, types.FunctionType) for e in conditions]): 88 | raise ValueError('every condition must be a function') 89 | else: 90 | raise ValueError('conditions must be of type of function, list or tuple') 91 | 92 | args = [] 93 | vals = [] 94 | for (k, v) in parameters.items(): 95 | args.append(k) 96 | vals.append([v] if not isinstance(v, (list, tuple)) else v) 97 | d = [dict(zip(args, p)) for p in product(*vals)] 98 | result = [] 99 | for params_set in d: 100 | conditions_met = True 101 | for cond_func in conditions: 102 | func_param_args = cond_func.__code__.co_varnames 103 | func_param_values = [params_set[arg] for arg in func_param_args] 104 | if not cond_func(*func_param_values): 105 | conditions_met = False 106 | break 107 | if conditions_met: 108 | result.append(params_set) 109 | return result -------------------------------------------------------------------------------- /tools/analysis/drawdown.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | import pandas as pd 4 | 5 | 6 | def absmaxdd(data): 7 | """ 8 | 9 | Calculates the maximum absolute drawdown of series data. 10 | 11 | Args: 12 | data: vector of doubles. Data may be presented as list, 13 | tuple, numpy array or pandas series object. 14 | 15 | Returns: 16 | (max_abs_dd, d_start, d_peak, d_recovered, dd_data) 17 | 18 | Where: 19 | - max_abs_dd: absolute maximal drawdown value 20 | - d_start: index from data array where drawdown starts 21 | - d_peak: index when drawdown reach it's maximal value 22 | - d_recovered: index when DD is fully recovered 23 | - dd_data: drawdown series 24 | 25 | Example: 26 | 27 | mdd, ds, dp, dr, dd_data = absmaxdd(np.random.randn(1,100).cumsum()) 28 | 29 | 30 | \(c\) 2016, http://www.appliedalpha.com 31 | 32 | """ 33 | 34 | if not isinstance(data, (list, tuple, np.ndarray, pd.Series)): 35 | raise TypeError('Unknown type of input series'); 36 | 37 | datatype = type(data) 38 | 39 | if datatype is pd.Series: 40 | indexes = data.index 41 | data = data.values 42 | elif datatype is not np.ndarray: 43 | data = np.array(data) 44 | 45 | dd = np.maximum.accumulate(data) - data 46 | mdd = dd.max() 47 | d_peak = dd.argmax() 48 | 49 | if mdd == 0: 50 | return 0, 0, 0, 0, [0] 51 | 52 | zeros_ixs = np.where(dd == 0)[0] 53 | zeros_ixs = np.insert(zeros_ixs, 0, 0) 54 | zeros_ixs = np.append(zeros_ixs, dd.size) 55 | 56 | d_start = zeros_ixs[zeros_ixs < d_peak][-1] 57 | d_recover = zeros_ixs[zeros_ixs > d_peak][0] 58 | 59 | if d_recover >= data.__len__(): 60 | d_recover = data.__len__() - 1 61 | 62 | if datatype is pd.Series: 63 | dd = pd.Series(dd, index=indexes) 64 | 65 | return mdd, d_start, d_peak, d_recover, dd 66 | 67 | 68 | def max_drawdown_pct(returns): 69 | """ 70 | Finds the maximum drawdown of a strategy returns in percents 71 | 72 | :param returns: pd.Series or np.ndarray daily returns of the strategy, noncumulative 73 | :return: maximum drawdown in percents 74 | """ 75 | if len(returns) < 1: 76 | return np.nan 77 | 78 | if isinstance(returns, pd.Series): 79 | returns = returns.values 80 | 81 | # drop nans 82 | returns[np.isnan(returns) | np.isinf(returns)] = 0.0 83 | 84 | cumrets = 100*(returns + 1).cumprod(axis=0) 85 | max_return = np.fmax.accumulate(cumrets) 86 | return np.nanmin((cumrets - max_return) / max_return) 87 | 88 | 89 | def dd_freq_stats(draw_down_series): 90 | """ 91 | Calculates drawdown frequencies statistics 92 | 93 | :param draw_down_series: 94 | :return: table with drawdown freqeuencies statistics 95 | 96 | Occurencies AvgMagnitude Max Min 97 | duration 98 | 1 1456.0 1019.695288 165927.04 0.18 99 | 2 707.0 1639.920325 29392.71 27.14 100 | 3 446.0 2244.420987 27785.23 28.96 101 | 4 316.0 2543.957310 15941.31 145.70 102 | 5 240.0 3124.423792 27746.15 242.41 103 | ... 104 | 105 | Example: 106 | 107 | t_pnl = pnl_data.get_field_data('Total_PnL')[0]; 108 | t_pnl = t_pnl[t_pnl.columns[0]] 109 | c_pnl = t_pnl.cumsum(skipna=True).between_time('17:00:01', '16:00').dropna() 110 | 111 | mdd,start,peak,recover,dd_ser = absmaxdd(c_pnl) 112 | dd_stat = dd_freq_stats(dd_ser) 113 | max(dd_stat['Max'])==mdd 114 | 115 | \(c\) 2016, http://www.appliedalpha.com 116 | 117 | """ 118 | f2 = pd.DataFrame(draw_down_series.values, columns=['dd'], index=range(0, len(draw_down_series))) 119 | f2['t'] = f2['dd'] > 0 120 | fst = f2.index[f2['t'] & ~ f2['t'].shift(1).fillna(False)] 121 | lst = f2.index[f2['t'] & ~ f2['t'].shift(-1).fillna(False)] 122 | 123 | dd_periods = pd.DataFrame( 124 | data=np.array([(j - i + 1, max(draw_down_series.iloc[i:j + 1])) for i, j in zip(fst, lst)], 125 | dtype=[('duration', np.uint8), ('m', np.float64)]), columns=['duration', 'm'] 126 | ) 127 | 128 | dx = dd_periods.groupby(by='duration').agg([len, np.average, np.max, np.min]). \ 129 | rename(columns={'len': 'Occurencies', 'average': 'AvgMagnitude', 'amax': 'Max', 'amin': 'Min'}) 130 | 131 | return dx.xs('m', axis=1, drop_level=True) 132 | 133 | 134 | def flat_periods(pnl_series, tolerance): 135 | """ 136 | Flat PnL periods finder. 137 | 138 | TODO: need to port it from Matlab 139 | TODO: also add statistics of found flat periods 140 | 141 | function [p] = flatperiods(data, tolerance) 142 | p = []; 143 | s0 = 1; 144 | sUp = data(s0) + tolerance/2; 145 | sDw = data(s0) - tolerance/2; 146 | for si = 2:length(data) 147 | if data(si) > sUp || data(si) < sDw 148 | if s0 ~= si-1 % skip single point 149 | p = [p; [s0 si-1]]; 150 | end 151 | sUp = data(si) + tolerance/2; 152 | sDw = data(si) - tolerance/2; 153 | s0 = si; 154 | continue 155 | end 156 | end 157 | end 158 | 159 | :param pnl_series: cumulative PnL series (pandas/numpy/list should be accepted) 160 | :param tolerance: channel size considered as flat period 161 | :return: should return set (or list) of indexes where flat periods are found 162 | """ 163 | raise NotImplementedError("Not yet implemented !!!") -------------------------------------------------------------------------------- /tools/analysis/portfolio.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | 4 | from scipy import stats 5 | from scipy.stats import norm 6 | from typing import Union 7 | 8 | from datetime import timedelta 9 | 10 | # Most used annualization factors 11 | YEARLY = 1 12 | MONTHLY = 12 13 | WEEKLY = 52 14 | DAILY = 252 15 | HOURLY = DAILY*6.5 16 | MINUTELY = HOURLY*60 17 | HOURLY_FX = DAILY*24 18 | MINUTELY_FX = HOURLY_FX*60 19 | 20 | 21 | def sharpe_ratio(returns, risk_free=0.0, periods=DAILY): 22 | """ 23 | Calculates the Sharpe ratio. 24 | 25 | :param returns: pd.Series or np.ndarray periodic returns of the strategy, noncumulative 26 | :param risk_free: constant risk-free return throughout the period 27 | :param periods: Defines the periodicity of the 'returns' data for purposes of annualizing. 28 | Daily (252), Hourly (252*6.5), Minutely(252*6.5*60) etc. 29 | :return: Sharpe ratio 30 | """ 31 | if len(returns) < 2: 32 | return np.nan 33 | 34 | returns_risk_adj = returns - risk_free 35 | returns_risk_adj = returns_risk_adj[~np.isnan(returns_risk_adj)] 36 | 37 | if np.std(returns_risk_adj, ddof=1) == 0: 38 | return np.nan 39 | 40 | return np.mean(returns_risk_adj) / np.std(returns_risk_adj, ddof=1) * np.sqrt(periods) -------------------------------------------------------------------------------- /tools/analysis/stats.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | from scipy.special import gamma 4 | 5 | from .tools import isscalar 6 | 7 | 8 | def j_divergence(p, q): 9 | """ 10 | Calculates J-divergence distance between data vectors p and q 11 | 12 | :param p: vector 1 13 | :param q: vector 2 14 | :return: distance 15 | """ 16 | meanP = np.mean(p) 17 | meanQ = np.mean(q) 18 | 19 | sigmaP = np.std(p) 20 | sigmaQ = np.std(q) 21 | 22 | return 0.5 * (((meanP - meanQ) ** 2) * (1 / sigmaP + 1 / sigmaQ) + sigmaQ / sigmaP + sigmaP / sigmaQ - 2) 23 | 24 | 25 | def __divisors(n, n0): 26 | # Find all divisors of the natural number N greater or equal to N0 27 | return [j for j in range(n0, math.floor(n / 2) + 1) if (n / j) == math.floor(n / j)] 28 | 29 | 30 | def __rs_calc(z, n): 31 | # calculate (R/S)_n for given n 32 | m = math.floor(len(z) / n) 33 | Y = np.reshape(z.copy(), (m, n)).T 34 | E = np.mean(Y, axis=0) 35 | S = np.std(Y, axis=0, ddof=1) 36 | for i in range(0, m): 37 | Y[:, i] = Y[:, i] - E[i] 38 | Y = np.cumsum(Y, axis=0) 39 | 40 | # find the ranges of cummulative series 41 | mm = np.max(Y) - np.min(Y) 42 | 43 | # rescale the ranges by the standard deviations 44 | return np.mean(mm / S) 45 | 46 | 47 | def hurst_rs(x, d=50, display_results=True): 48 | """ 49 | Calculate the Hurst exponent using R/S analysis. 50 | It calculates the Hurst exponent of time series X using 51 | the R/S analysis of Hurst [2], corrected for small sample bias [1,3,4]. 52 | If a vector of increasing natural numbers is given as the second input 53 | parameter, i.e. hurst_rs(X,D), then it defines the box sizes that the 54 | sample is divided into (the values in D have to be divisors of the 55 | length of series X). If D is a scalar (default value D = 50) it is 56 | treated as the smallest box size that the sample can be divided into. 57 | In this case the optimal sample size opt_n and the vector of divisors 58 | for this size are automatically computed. 59 | opt_n is defined as the length that possesses the most divisors among 60 | series shorter than X by no more than 1%. The input series X is 61 | truncated at the opt_n-th value. 62 | H,HE,HT,PV95 = hurst_rs(X) returns the uncorrected empirical and theoretical Hurst exponents and the empirical 63 | 95% confidence intervals PV95 (see [4]). 64 | 65 | References: 66 | [1] A.A.Anis, E.H.Lloyd (1976) The expected value of the adjusted rescaled Hurst range of independent normal summands, Biometrica 63,283-298. 67 | [2] H.E.Hurst (1951) Long-term storage capacity of reservoirs, Transactions of the American Society of Civil Engineers 116, 770-808. 68 | [3] E.E.Peters (1994) Fractal Market Analysis, Wiley. 69 | [4] R.Weron (2002) Estimating long range dependence: finite sample properties and confidence intervals, Physica A 312, 285-299. 70 | 71 | :param x: 72 | :param d: 73 | :param display_results: 74 | :return: 75 | """ 76 | if isscalar(d): 77 | # for scalar d set dmin=d and find the 'optimal' vector d 78 | dmin = d 79 | # find such a natural number OptN that possesses the largest number of 80 | # divisors among all natural numbers in the interval [0.99*N,N] 81 | N = len(x) 82 | n0 = math.floor(0.99 * N) 83 | dv = np.zeros(N - n0 + 1) 84 | for i in range(n0, N + 1): 85 | dv[i - n0] = len(__divisors(i + 1, dmin)) 86 | 87 | opt_n = n0 + np.argmax(dv) 88 | # use the first OptN values of x for further analysis 89 | x = x[:opt_n + 1] 90 | # find the divisors of x 91 | d = __divisors(opt_n + 1, dmin) 92 | else: 93 | opt_n = len(x) 94 | 95 | N = len(d) 96 | rs_e = np.zeros(N) 97 | ers = np.zeros(N) 98 | 99 | # calculate empirical R/S 100 | for i in range(N): 101 | rs_e[i] = __rs_calc(x, d[i]) 102 | 103 | for i in range(N): 104 | n = d[i] 105 | K = np.array(range(1, n)) 106 | ratio = (n - 0.5) / n * np.sum(np.sqrt((np.ones((1, n - 1)) * n - K) / K)) 107 | if n > 340: 108 | ers[i] = ratio / np.sqrt(0.5 * np.pi * n) 109 | else: 110 | ers[i] = (gamma(0.5 * (n - 1)) * ratio) / (gamma(0.5 * n) * np.sqrt(np.pi)) 111 | 112 | # calculate the Anis-Lloyd/Peters corrected Hurst exponent 113 | # compute the Hurst exponent as the slope on a loglog scale 114 | ers_al = np.sqrt(0.5 * np.pi * np.array(d, dtype=float)) 115 | pal = np.polyfit(np.log10(d), np.log10(rs_e - ers + ers_al), 1) 116 | hal = pal[0] 117 | 118 | # Calculate the empirical and theoretical Hurst exponents 119 | pe = np.polyfit(np.log10(d), np.log10(rs_e), 1) 120 | he = pe[0] 121 | p = np.polyfit(np.log10(d), np.log10(ers), 1) 122 | ht = p[0] 123 | 124 | # Compute empirical confidence intervals (see [4]) 125 | L = np.log2(opt_n) 126 | # R/S-AL (min(divisor)>50) two-sided empirical confidence intervals 127 | pval95 = [0.5 - np.exp(-7.33 * np.log(np.log(L)) + 4.21), np.exp(-7.20 * np.log(np.log(L)) + 4.04) + 0.5]; 128 | 129 | conf_int = np.array([ 130 | [0.5 - np.exp(-7.35 * np.log(np.log(L)) + 4.06), np.exp(-7.07 * np.log(np.log(L)) + 3.75) + 0.5, .90], 131 | [*pval95, .95], 132 | [0.5 - np.exp(-7.19 * np.log(np.log(L)) + 4.34), np.exp(-7.51 * np.log(np.log(L)) + 4.58) + 0.5, .99]]) 133 | 134 | if display_results: 135 | print('---------------------------------------------------------------') 136 | print('R/S-AL using %d divisors (%d, ..., %d) for a sample of %d values' % (len(d), d[0], d[-1], opt_n)) 137 | print('Corrected theoretical Hurst exponent %0.4f' % 0.5) 138 | print('Corrected empirical Hurst exponent %0.4f' % hal) 139 | print('Theoretical Hurst exponent %0.4f' % ht) 140 | print('Empirical Hurst exponent %0.4f' % he) 141 | print('---------------------------------------------------------------') 142 | 143 | # Display empirical confidence intervals 144 | print('R/S-AL (min(divisor) > 50) two-sided empirical confidence intervals') 145 | print('--- conf_lo conf_hi level ---------------------------------') 146 | print(conf_int) 147 | print('---------------------------------------------------------------') 148 | 149 | return hal, he, ht, pval95 150 | 151 | 152 | def hurst(series, max_lag=20): 153 | """ 154 | Simplest Hurst exponent helps test whether the time series is: 155 | (1) A Random Walk (H ~ 0.5) 156 | (2) Trending (H > 0.5) 157 | (3) Mean reverting (H < 0.5) 158 | """ 159 | tau, lagvec = [], [] 160 | 161 | # Step through the different lags 162 | for lag in range(2, max_lag): 163 | # Produce price different with lag 164 | pp = np.subtract(series[lag:], series[:-lag]) 165 | 166 | # Write the different lags into a vector 167 | lagvec.append(lag) 168 | 169 | # Calculate the variance of the difference 170 | tau.append(np.sqrt(np.std(pp))) 171 | 172 | # Linear fit to a double-log graph to get power 173 | m = np.polyfit(np.log10(lagvec), np.log10(tau), 1) 174 | 175 | # Calculate hurst 176 | return m[0] * 2 177 | 178 | 179 | def percentile_rank(x: np.ndarray, v, pctls=np.arange(1, 101)): 180 | """ 181 | Find percentile rank of value v 182 | :param x: values array 183 | :param v: vakue to be ranked 184 | :param pctls: percentiles 185 | :return: rank 186 | 187 | >>> percentile_rank(np.random.randn(1000), 1.69) 188 | >>> 95 189 | >>> percentile_rank(np.random.randn(1000), 1.69, [10,50,100]) 190 | >>> 2 191 | """ 192 | return np.argmax(np.sign(np.append(np.percentile(x, pctls), np.inf) - v)) 193 | 194 | -------------------------------------------------------------------------------- /tools/analysis/stochastic.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file presents some popular stochastic processes implementations in Python used in quantitative finance. 3 | 4 | Detailed here: 5 | -------------- 6 | 7 | http://www.turingfinance.com/random-walks-down-wall-street-stochastic-processes-in-python/ 8 | http://stuartreid.co.za/interactive-stochastic-processes/ 9 | """ 10 | 11 | import math 12 | import numpy as np 13 | import random 14 | import scipy.linalg 15 | import numpy.random as nrand 16 | import matplotlib.pyplot as plt 17 | 18 | 19 | class ModelParameters: 20 | """ 21 | Stochastic model parameters holder 22 | """ 23 | 24 | def __init__(self, 25 | all_starting_asset_value, all_time, all_delta, all_sigma, gbm_mu, 26 | jumps_lamda=0.0, jumps_sigma=0.0, jumps_mu=0.0, 27 | cir_a=0.0, cir_mu=0.0, all_starting_rates=0.0, cir_rho=0.0, 28 | ou_a=0.0, ou_mu=0.0, 29 | heston_a=0.0, heston_mu=0.0, heston_vol0=0.0): 30 | # This is the starting asset value 31 | self.all_s0 = all_starting_asset_value 32 | # This is the amount of time to simulate for 33 | self.all_time = all_time 34 | # This is the delta, the rate of time e.g. 1/252 = daily, 1/12 = monthly 35 | self.all_delta = all_delta 36 | # This is the volatility of the stochastic processes 37 | self.all_sigma = all_sigma 38 | # This is the annual drift factor for geometric brownian motion 39 | self.gbm_mu = gbm_mu 40 | # This is the probability of a jump happening at each point in time 41 | self.lamda = jumps_lamda 42 | # This is the volatility of the jump size 43 | self.jumps_sigma = jumps_sigma 44 | # This is the average jump size 45 | self.jumps_mu = jumps_mu 46 | # This is the rate of mean reversion for Cox Ingersoll Ross 47 | self.cir_a = cir_a 48 | # This is the long run average interest rate for Cox Ingersoll Ross 49 | self.cir_mu = cir_mu 50 | # This is the starting interest rate value 51 | self.all_r0 = all_starting_rates 52 | # This is the correlation between the wiener processes of the Heston model 53 | self.cir_rho = cir_rho 54 | # This is the rate of mean reversion for Ornstein Uhlenbeck 55 | self.ou_a = ou_a 56 | # This is the long run average interest rate for Ornstein Uhlenbeck 57 | self.ou_mu = ou_mu 58 | # This is the rate of mean reversion for volatility in the Heston model 59 | self.heston_a = heston_a 60 | # This is the long run average volatility for the Heston model 61 | self.heston_mu = heston_mu 62 | # This is the starting volatility value for the Heston model 63 | self.heston_vol0 = heston_vol0 64 | 65 | 66 | def convert_to_returns(log_returns): 67 | """ 68 | This method exponentiates a sequence of log returns to get daily returns. 69 | 70 | :param log_returns: the log returns to exponentiated 71 | :return: the exponentiated returns 72 | """ 73 | return np.exp(log_returns) 74 | 75 | 76 | def convert_to_prices(param, log_returns): 77 | """ 78 | This method converts a sequence of log returns into normal returns (exponentiation) and then computes a price 79 | sequence given a starting price, param.all_s0. 80 | :param param: the model parameters object 81 | :param log_returns: the log returns to exponentiated 82 | :return: 83 | """ 84 | returns = convert_to_returns(log_returns) 85 | 86 | # A sequence of prices starting with param.all_s0 87 | price_sequence = [param.all_s0] 88 | for i in range(1, len(returns)): 89 | # Add the price at t-1 * return at t 90 | price_sequence.append(price_sequence[i - 1] * returns[i - 1]) 91 | return np.array(price_sequence) 92 | 93 | 94 | def plot_stochastic_processes(processes, title='Stochastic Processes'): 95 | """ 96 | This method plots a list of stochastic processes with a specified title 97 | 98 | :return: plots the graph of the two 99 | """ 100 | # plt.style.use(['bmh']) 101 | 102 | fig, ax = plt.subplots(1) 103 | fig.suptitle(title, fontsize=16) 104 | ax.set_xlabel('Time, t') 105 | ax.set_ylabel('Simulated Asset Price') 106 | x_axis = np.arange(0, len(processes[0]), 1) 107 | for i in range(len(processes)): 108 | plt.plot(x_axis, processes[i]) 109 | 110 | plt.show() 111 | 112 | 113 | def brownian_motion_log_returns(param): 114 | """ 115 | This method returns a Wiener process. The Wiener process is also called Brownian motion. For more information 116 | about the Wiener process check out the Wikipedia page: http://en.wikipedia.org/wiki/Wiener_process 117 | 118 | :param param: the model parameters object 119 | :return: brownian motion log returns 120 | """ 121 | sqrt_delta_sigma = math.sqrt(param.all_delta) * param.all_sigma 122 | return nrand.normal(loc=0, scale=sqrt_delta_sigma, size=param.all_time) 123 | 124 | 125 | def brownian_motion_levels(param): 126 | """ 127 | Returns a price sequence whose returns evolve according to a brownian motion 128 | 129 | :param param: model parameters object 130 | :return: returns a price sequence which follows a brownian motion 131 | """ 132 | return convert_to_prices(param, brownian_motion_log_returns(param)) 133 | 134 | 135 | def geometric_brownian_motion_log_returns(param): 136 | """ 137 | This method constructs a sequence of log returns which, when exponentiated, produce a random Geometric Brownian 138 | Motion (GBM). GBM is the stochastic process underlying the Black Scholes options pricing formula. 139 | 140 | :param param: model parameters object 141 | :return: returns the log returns of a geometric brownian motion process 142 | """ 143 | assert isinstance(param, ModelParameters) 144 | wiener_process = np.array(brownian_motion_log_returns(param)) 145 | sigma_pow_mu_delta = (param.gbm_mu - 0.5 * math.pow(param.all_sigma, 2.0)) * param.all_delta 146 | return wiener_process + sigma_pow_mu_delta 147 | 148 | 149 | def geometric_brownian_motion_levels(param): 150 | """ 151 | Returns a sequence of price levels for an asset which evolves according to a geometric brownian motion 152 | 153 | :param param: model parameters object 154 | :return: the price levels for the asset 155 | """ 156 | return convert_to_prices(param, geometric_brownian_motion_log_returns(param)) 157 | 158 | 159 | def jump_diffusion_process(param): 160 | """ 161 | This method produces a sequence of Jump Sizes which represent a jump diffusion process. These jumps are combined 162 | with a geometric brownian motion (log returns) to produce the Merton model. 163 | 164 | :param param: the model parameters object 165 | :return: jump sizes for each point in time (mostly zeroes if jumps are infrequent) 166 | """ 167 | assert isinstance(param, ModelParameters) 168 | s_n = time = 0 169 | small_lamda = -(1.0 / param.lamda) 170 | jump_sizes = [] 171 | for k in range(0, param.all_time): 172 | jump_sizes.append(0.0) 173 | while s_n < param.all_time: 174 | s_n += small_lamda * math.log(random.uniform(0, 1)) 175 | for j in range(0, param.all_time): 176 | if time * param.all_delta <= s_n * param.all_delta <= (j + 1) * param.all_delta: 177 | # print("was true") 178 | jump_sizes[j] += random.normalvariate(param.jumps_mu, param.jumps_sigma) 179 | break 180 | time += 1 181 | return jump_sizes 182 | 183 | 184 | def geometric_brownian_motion_jump_diffusion_log_returns(param): 185 | """ 186 | This method constructs combines a geometric brownian motion process (log returns) with a jump diffusion process 187 | (log returns) to produce a sequence of gbm jump returns. 188 | 189 | :param param: model parameters object 190 | :return: returns a GBM process with jumps in it 191 | """ 192 | assert isinstance(param, ModelParameters) 193 | jump_diffusion = jump_diffusion_process(param) 194 | geometric_brownian_motion = geometric_brownian_motion_log_returns(param) 195 | return np.add(jump_diffusion, geometric_brownian_motion) 196 | 197 | 198 | def geometric_brownian_motion_jump_diffusion_levels(param): 199 | """ 200 | This method converts a sequence of gbm jmp returns into a price sequence which evolves according to a geometric 201 | brownian motion but can contain jumps at any point in time. 202 | 203 | :param param: model parameters object 204 | :return: the price levels 205 | """ 206 | return convert_to_prices(param, geometric_brownian_motion_jump_diffusion_log_returns(param)) 207 | 208 | 209 | def cox_ingersoll_ross_heston(param): 210 | """ 211 | This method returns the rate levels of a mean-reverting cox ingersoll ross process. It is used to model interest 212 | rates as well as stochastic volatility in the Heston model. Because the returns between the underlying and the 213 | stochastic volatility should be correlated we pass a correlated Brownian motion process into the method from which 214 | the interest rate levels are constructed. The other correlated process is used in the Heston model 215 | 216 | :param param: the model parameters objects 217 | :return: the interest rate levels for the CIR process 218 | """ 219 | # We don't multiply by sigma here because we do that in heston 220 | sqrt_delta_sigma = math.sqrt(param.all_delta) * param.all_sigma 221 | brownian_motion_volatility = nrand.normal(loc=0, scale=sqrt_delta_sigma, size=param.all_time) 222 | a, mu, zero = param.heston_a, param.heston_mu, param.heston_vol0 223 | volatilities = [zero] 224 | for i in range(1, param.all_time): 225 | drift = a * (mu - volatilities[i - 1]) * param.all_delta 226 | randomness = math.sqrt(volatilities[i - 1]) * brownian_motion_volatility[i - 1] 227 | volatilities.append(volatilities[i - 1] + drift + randomness) 228 | return np.array(brownian_motion_volatility), np.array(volatilities) 229 | 230 | 231 | def heston_model_levels(param): 232 | """ 233 | NOTE - this method is dodgy! Need to debug! 234 | The Heston model is the geometric brownian motion model with stochastic volatility. This stochastic volatility is 235 | given by the cox ingersoll ross process. Step one on this method is to construct two correlated GBM processes. One 236 | is used for the underlying asset prices and the other is used for the stochastic volatility levels 237 | 238 | :param param: model parameters object 239 | :return: the prices for an underlying following a Heston process 240 | """ 241 | assert isinstance(param, ModelParameters) 242 | # Get two correlated brownian motion sequences for the volatility parameter and the underlying asset 243 | # brownian_motion_market, brownian_motion_vol = get_correlated_paths_simple(param) 244 | brownian, cir_process = cox_ingersoll_ross_heston(param) 245 | brownian, brownian_motion_market = heston_construct_correlated_path(param, brownian) 246 | 247 | heston_market_price_levels = [param.all_s0] 248 | for i in range(1, param.all_time): 249 | drift = param.gbm_mu * heston_market_price_levels[i - 1] * param.all_delta 250 | vol = cir_process[i - 1] * heston_market_price_levels[i - 1] * brownian_motion_market[i - 1] 251 | heston_market_price_levels.append(heston_market_price_levels[i - 1] + drift + vol) 252 | return np.array(heston_market_price_levels), np.array(cir_process) 253 | 254 | 255 | def heston_construct_correlated_path(param, brownian_motion_one): 256 | """ 257 | This method is a simplified version of the Cholesky decomposition method for just two assets. It does not make use 258 | of matrix algebra and is therefore quite easy to implement. 259 | 260 | :param param: model parameters object 261 | :return: a correlated brownian motion path 262 | """ 263 | # We do not multiply by sigma here, we do that in the Heston model 264 | sqrt_delta = math.sqrt(param.all_delta) 265 | # Construct a path correlated to the first path 266 | brownian_motion_two = [] 267 | for i in range(param.all_time - 1): 268 | term_one = param.cir_rho * brownian_motion_one[i] 269 | term_two = math.sqrt(1 - math.pow(param.cir_rho, 2.0)) * random.normalvariate(0, sqrt_delta) 270 | brownian_motion_two.append(term_one + term_two) 271 | return np.array(brownian_motion_one), np.array(brownian_motion_two) 272 | 273 | 274 | def get_correlated_geometric_brownian_motions(param, correlation_matrix, n): 275 | """ 276 | This method can construct a basket of correlated asset paths using the Cholesky decomposition method 277 | 278 | :param param: model parameters object 279 | :param correlation_matrix: nxn correlation matrix 280 | :param n: the number of assets i.e. the number of paths to return 281 | :return: n correlated log return geometric brownian motion processes 282 | """ 283 | assert isinstance(param, ModelParameters) 284 | decomposition = scipy.linalg.cholesky(correlation_matrix, lower=False) 285 | uncorrelated_paths = [] 286 | sqrt_delta_sigma = math.sqrt(param.all_delta) * param.all_sigma 287 | 288 | # Construct uncorrelated paths to convert into correlated paths 289 | for i in range(param.all_time): 290 | uncorrelated_random_numbers = [] 291 | for j in range(n): 292 | uncorrelated_random_numbers.append(random.normalvariate(0, sqrt_delta_sigma)) 293 | uncorrelated_paths.append(np.array(uncorrelated_random_numbers)) 294 | uncorrelated_matrix = np.matrix(uncorrelated_paths) 295 | correlated_matrix = uncorrelated_matrix * decomposition 296 | assert isinstance(correlated_matrix, np.matrix) 297 | 298 | # The rest of this method just extracts paths from the matrix 299 | extracted_paths = [] 300 | for i in range(1, n + 1): 301 | extracted_paths.append([]) 302 | for j in range(0, len(correlated_matrix) * n - n, n): 303 | for i in range(n): 304 | extracted_paths[i].append(correlated_matrix.item(j + i)) 305 | return extracted_paths 306 | 307 | 308 | def cox_ingersoll_ross_levels(param): 309 | """ 310 | This method returns the rate levels of a mean-reverting cox ingersoll ross process. It is used to model interest 311 | rates as well as stochastic volatility in the Heston model. Because the returns between the underlying and the 312 | stochastic volatility should be correlated we pass a correlated Brownian motion process into the method from which 313 | the interest rate levels are constructed. The other correlated process is used in the Heston model 314 | 315 | :param param: the model parameters object 316 | :return: the interest rate levels for the CIR process 317 | """ 318 | brownian_motion = brownian_motion_log_returns(param) 319 | 320 | # Setup the parameters for interest rates 321 | a, mu, zero = param.cir_a, param.cir_mu, param.all_r0 322 | 323 | # Assumes output is in levels 324 | levels = [zero] 325 | for i in range(1, param.all_time): 326 | drift = a * (mu - levels[i - 1]) * param.all_delta 327 | # The main difference between this and the Ornstein Uhlenbeck model is that we multiply the 'random' 328 | # component by the square-root of the previous level i.e. the process has level dependent interest rates. 329 | randomness = math.sqrt(levels[i - 1]) * brownian_motion[i - 1] 330 | levels.append(levels[i - 1] + drift + randomness) 331 | 332 | return np.array(levels) 333 | 334 | 335 | def ornstein_uhlenbeck_levels(param): 336 | """ 337 | This method returns the rate levels of a mean-reverting ornstein uhlenbeck process. 338 | :param param: the model parameters object 339 | :return: the interest rate levels for the Ornstein Uhlenbeck process 340 | """ 341 | ou_levels = [param.all_r0] 342 | brownian_motion_returns = brownian_motion_log_returns(param) 343 | for i in range(1, param.all_time): 344 | drift = param.ou_a * (param.ou_mu - ou_levels[i - 1]) * param.all_delta 345 | randomness = brownian_motion_returns[i - 1] 346 | ou_levels.append(ou_levels[i - 1] + drift + randomness) 347 | return ou_levels 348 | 349 | 350 | def simulate_cointegrating_series(d, n, mu, sigma, start_point_1, start_point_2): 351 | """ 352 | Simulates two cointegrated time series 353 | 354 | :param d: correlation coefficient (becomes a random walk if d = 0) 355 | :param n: number of points to be simulated 356 | :param mu: mean of Gauss process for drawing increments 357 | :param sigma: std of Gauss process 358 | :param start_point_1: start point for first series 359 | :param start_point_2: start point for second series 360 | :return: ser1, ser2, ser1 - ser2 361 | """ 362 | ser1 = np.zeros(n) 363 | ser2 = np.zeros(n) 364 | 365 | # These are the starting points of the random walk 366 | ser1[0] = start_point_1 367 | ser2[0] = start_point_2 368 | for t in range(1, n): 369 | # drunk and his dog cointegration equations 370 | ser1[t] = ser1[t - 1] + random.gauss(mu, sigma) 371 | ser2[t] = d * (ser1[t - 1] - ser2[t - 1]) + ser2[t - 1] + random.gauss(mu, sigma) 372 | return ser1, ser2, ser1 - ser2 373 | -------------------------------------------------------------------------------- /tools/analysis/timeseries.py: -------------------------------------------------------------------------------- 1 | from typing import Union, Tuple, List 2 | import types 3 | 4 | import numpy as np 5 | import pandas as pd 6 | import statsmodels.api as sm 7 | from collections import OrderedDict 8 | 9 | from statsmodels.regression.linear_model import OLS 10 | from datetime import timedelta 11 | from .tools import ( 12 | column_vector, shift, sink_nans_down, 13 | lift_nans_up, nans, rolling_sum, isscalar, apply_to_frame, ohlc_resample 14 | ) 15 | 16 | 17 | try: 18 | from numba import njit 19 | except: 20 | print('numba package is not found !') 21 | 22 | def njit(f): 23 | return f 24 | 25 | 26 | def __wrap_dataframe_decorator(func): 27 | def wrapper(*args, **kwargs): 28 | if isinstance(args[0], (pd.Series, pd.DataFrame)): 29 | return apply_to_frame(func, *args, **kwargs) 30 | else: 31 | return func(*args) 32 | 33 | return wrapper 34 | 35 | 36 | def __empty_smoother(x, *args, **kwargs): 37 | return column_vector(x) 38 | 39 | 40 | def smooth(x, stype: Union[str, types.FunctionType], *args, **kwargs) -> pd.Series: 41 | """ 42 | Smooth series using either given function or find it by name from registered smoothers 43 | """ 44 | smoothers = {'sma': sma, 'ema': ema, 'tema': tema, 'dema': dema, 'zlema': zlema, 'kama': kama} 45 | 46 | f_sm = __empty_smoother 47 | if isinstance(stype, str): 48 | if stype in smoothers: 49 | f_sm = smoothers.get(stype) 50 | else: 51 | raise ValueError("Smoothing method '%s' is not supported !" % stype) 52 | 53 | if isinstance(stype, types.FunctionType): 54 | f_sm = stype 55 | 56 | # smoothing 57 | x_sm = f_sm(x, *args, **kwargs) 58 | 59 | return x_sm if isinstance(x_sm, pd.Series) else pd.Series(x_sm.flatten(), index=x.index) 60 | 61 | 62 | def infer_series_frequency(series): 63 | """ 64 | Infer frequency of given timeseries 65 | 66 | :param series: Series, DataFrame or DatetimeIndex object 67 | :return: timedelta for found frequency 68 | """ 69 | 70 | if not isinstance(series, (pd.DataFrame, pd.Series, pd.DatetimeIndex)): 71 | raise ValueError("infer_series_frequency> Only DataFrame, Series of DatetimeIndex objects are allowed") 72 | 73 | times_index = (series if isinstance(series, pd.DatetimeIndex) else series.index).to_pydatetime() 74 | if times_index.shape[0] < 2: 75 | raise ValueError("Series must have at least 2 points to determ frequency") 76 | 77 | values = np.array(sorted([(x.total_seconds()) for x in np.diff(times_index)])) 78 | diff = np.concatenate(([1], np.diff(values))) 79 | idx = np.concatenate((np.where(diff)[0], [len(values)])) 80 | freqs = dict(zip(values[idx[:-1]], np.diff(idx))) 81 | return timedelta(seconds=max(freqs, key=freqs.get)) 82 | 83 | 84 | def running_view(arr, window, axis=-1): 85 | """ 86 | Produces running view (lagged matrix) from given array. 87 | 88 | Example: 89 | 90 | > running_view(np.array([1,2,3,4,5,6]), 3) 91 | 92 | array([[1, 2, 3], 93 | [2, 3, 4], 94 | [3, 4, 5], 95 | [4, 5, 6]]) 96 | 97 | :param arr: array of numbers 98 | :param window: window length 99 | :param axis: 100 | :return: lagged matrix 101 | """ 102 | shape = list(arr.shape) 103 | shape[axis] -= (window-1) 104 | return np.lib.index_tricks.as_strided(arr, shape + [window], arr.strides + (arr.strides[axis],)) 105 | 106 | 107 | def detrend(y, order): 108 | """ 109 | Removes linear trend from the series y. 110 | detrend computes the least-squares fit of a straight line to the data 111 | and subtracts the resulting function from the data. 112 | 113 | :param y: 114 | :param order: 115 | :return: 116 | """ 117 | if order == -1: return y 118 | return OLS(y, np.vander(np.linspace(-1, 1, len(y)), order + 1)).fit().resid 119 | 120 | 121 | def moving_detrend(y, order, window): 122 | """ 123 | Removes linear trend from the series y by using sliding window. 124 | :param y: series (ndarray or pd.DataFrame/Series) 125 | :param order: trend's polinome order 126 | :param window: sliding window size 127 | :return: (residual, rsquatred, betas) 128 | """ 129 | yy = running_view(column_vector(y).T[0], window=window) 130 | n_pts = len(y) 131 | resid = nans((n_pts)) 132 | r_sqr = nans((n_pts)) 133 | betas = nans((n_pts, order + 1)) 134 | for i, p in enumerate(yy): 135 | n = len(p) 136 | lr = OLS(p, np.vander(np.linspace(-1, 1, n), order + 1)).fit() 137 | r_sqr[n - 1 + i] = lr.rsquared 138 | resid[n - 1 + i] = lr.resid[-1] 139 | betas[n - 1 + i, :] = lr.params 140 | 141 | # return pandas frame if input is series/frame 142 | if isinstance(y, (pd.DataFrame, pd.Series)): 143 | r = pd.DataFrame({'resid': resid, 'r2': r_sqr}, index=y.index, columns=['resid', 'r2']) 144 | betas_fr = pd.DataFrame(betas, index=y.index, columns=['b%d' % i for i in range(order+1)]) 145 | return pd.concat((r, betas_fr), axis=1) 146 | 147 | return resid, r_sqr, betas 148 | 149 | 150 | def moving_ols(y, x, window): 151 | """ 152 | Function for calculating moving linear regression model using sliding window 153 | y = B*x + err 154 | returns array of betas, residuals and standard deviation for residuals 155 | residuals = y - yhat, where yhat = betas * x 156 | 157 | Example: 158 | 159 | x = pd.DataFrame(np.random.randn(100, 5)) 160 | y = pd.Series(np.random.randn(100).cumsum()) 161 | m = moving_ols(y, x, 5) 162 | lr_line = (x * m).sum(axis=1) 163 | 164 | :param y: dependent variable (vector) 165 | :param x: exogenous variables (vector or matrix) 166 | :param window: sliding windowsize 167 | :return: array of betas, residuals and standard deviation for residuals 168 | """ 169 | # if we have any indexes 170 | idx_line = y.index if isinstance(y, (pd.Series, pd.DataFrame)) else None 171 | x_col_names = x.columns if isinstance(y, (pd.Series, pd.DataFrame)) else None 172 | 173 | x = column_vector(x) 174 | y = column_vector(y) 175 | nx = len(x) 176 | if nx != len(y): 177 | raise ValueError('Series must contain equal number of points') 178 | 179 | if y.shape[1] != 1: 180 | raise ValueError('Response variable y must be column array or series object') 181 | 182 | if window > nx: 183 | raise ValueError('Window size must be less than number of observations') 184 | 185 | betas = nans(x.shape); 186 | err = nans((nx)); 187 | sd = nans((nx)); 188 | 189 | for i in range(window, nx + 1): 190 | ys = y[(i - window):i] 191 | xs = x[(i - window):i, :] 192 | lr = OLS(ys, xs).fit() 193 | betas[i - 1, :] = lr.params 194 | err[i - 1] = y[i - 1] - (x[i - 1, :] * lr.params).sum() 195 | sd[i - 1] = lr.resid.std() 196 | 197 | # convert to datafra?e if need 198 | if x_col_names is not None and idx_line is not None: 199 | _non_empy = lambda c, idx: c if c else idx 200 | _bts = pd.DataFrame({ 201 | _non_empy(c, i): betas[:, i] for i, c in enumerate(x_col_names) 202 | }, index=idx_line) 203 | return pd.concat((_bts, pd.DataFrame({'error': err, 'stdev': sd}, index=idx_line)), axis=1) 204 | else: 205 | return betas, err, sd 206 | 207 | 208 | def holt_winters_second_order_ewma(x, span, beta) -> tuple: 209 | """ 210 | The Holt-Winters second order method (aka double exponential smoothing) attempts to incorporate the estimated 211 | trend into the smoothed data, using a {b_{t}} term that keeps track of the slope of the original signal. 212 | The smoothed signal is written to the s_{t} term. 213 | 214 | :param x: series values (DataFrame, Series or numpy array) 215 | :param span: number of data points taken for calculation 216 | :param beta: trend smoothing factor, 0 < beta < 1 217 | :return: tuple of smoothed series and smoothed trend 218 | """ 219 | if span < 0: raise ValueError("Span value must be positive") 220 | 221 | if isinstance(x, (pd.DataFrame, pd.Series)): 222 | x = x.values 223 | 224 | x = np.reshape(x, (x.shape[0], -1)) 225 | alpha = 2.0 / (1 + span) 226 | r_alpha = 1 - alpha 227 | r_beta = 1 - beta 228 | s = np.zeros(x.shape) 229 | b = np.zeros(x.shape) 230 | s[0, :] = x[0,:] 231 | for i in range(1, x.shape[0]): 232 | s[i,:] = alpha * x[i,:] + r_alpha*(s[i-1,:] + b[i-1,:]) 233 | b[i,:] = beta * (s[i,:] - s[i-1,:]) + r_beta * b[i-1,:] 234 | return s, b 235 | 236 | 237 | def sma(x, period): 238 | """ 239 | Classical simple moving average 240 | 241 | :param x: input data (as np.array or pd.DataFrame/Series) 242 | :param period: period of smoothing 243 | :return: smoothed values 244 | """ 245 | if period <= 0: 246 | raise ValueError('Period must be positive and greater than zero !!!') 247 | 248 | x = column_vector(x) 249 | x, ix = sink_nans_down(x, copy=True) 250 | s = rolling_sum(x, period) / period 251 | return lift_nans_up(s, ix) 252 | 253 | @njit 254 | def _calc_kama(x, period, fast_span, slow_span): 255 | x = x.astype(np.float64) 256 | for i in range(0, x.shape[1]): 257 | nan_start = np.where(~np.isnan(x[:, i]))[0][0] 258 | x_s = x[:, i][nan_start:] 259 | if period >= len(x_s): 260 | raise ValueError('Wrong value for period. period parameter must be less than number of input observations') 261 | abs_diff = np.abs(x_s - shift(x_s, 1)) 262 | er = np.abs(x_s - shift(x_s, period)) / rolling_sum(np.reshape(abs_diff, (len(abs_diff), -1)), period)[:,0] 263 | sc = np.square((er * (2.0 / (fast_span + 1) - 2.0 / (slow_span + 1.0)) + 2 / (slow_span + 1.0))) 264 | ama = nans(sc.shape) 265 | 266 | # here ama_0 = x_0 267 | ama[period - 1] = x_s[period - 1] 268 | for n in range(period, len(ama)): 269 | ama[n] = ama[n - 1] + sc[n] * (x_s[n] - ama[n - 1]) 270 | 271 | # drop 1-st kama value (just for compatibility with ta-lib) 272 | ama[period - 1] = np.nan 273 | 274 | x[:, i] = np.concatenate((nans(nan_start), ama)) 275 | 276 | return x 277 | 278 | def kama(x, period, fast_span=2, slow_span=30): 279 | """ 280 | Kaufman Adaptive Moving Average 281 | 282 | :param x: input data (as np.array or pd.DataFrame/Series) 283 | :param period: period of smoothing 284 | :param fast_span: fast period (default is 2 as in canonical impl) 285 | :param slow_span: slow period (default is 30 as in canonical impl) 286 | :return: smoothed values 287 | """ 288 | x = column_vector(x) 289 | return _calc_kama(x, period, fast_span, slow_span) 290 | 291 | @njit 292 | def _calc_ema(x, span, init_mean=True, min_periods=0): 293 | alpha = 2.0 / (1 + span) 294 | x = x.astype(np.float64) 295 | for i in range(0, x.shape[1]): 296 | nan_start = np.where(~np.isnan(x[:, i]))[0][0] 297 | x_s = x[:, i][nan_start:] 298 | a_1 = 1 - alpha 299 | s = np.zeros(x_s.shape) 300 | 301 | start_i = 1 302 | if init_mean: 303 | s += np.nan 304 | if span - 1 >= len(s): 305 | x[:,:] = np.nan 306 | continue 307 | s[span - 1] = np.mean(x_s[:span]) 308 | start_i = span 309 | else: 310 | s[0] = x_s[0] 311 | 312 | for n in range(start_i, x_s.shape[0]): 313 | s[n] = alpha * x_s[n] + a_1 * s[n - 1] 314 | 315 | if min_periods > 0: 316 | s[:min_periods - 1] = np.nan 317 | 318 | x[:, i] = np.concatenate((nans(nan_start), s)) 319 | 320 | return x 321 | 322 | 323 | def ema(x, span, init_mean=True, min_periods=0) -> np.ndarray: 324 | """ 325 | Exponential moving average 326 | 327 | :param x: data to be smoothed 328 | :param span: number of data points for smooth 329 | :param init_mean: use average of first span points as starting ema value (default is true) 330 | :param min_periods: minimum number of observations in window required to have a value (0) 331 | :return: 332 | """ 333 | x = column_vector(x) 334 | return _calc_ema(x, span, init_mean, min_periods) 335 | 336 | 337 | def zlema(x: np.ndarray, n: int, init_mean=True): 338 | """ 339 | 'Zero lag' moving average 340 | :type x: np.array 341 | :param x: 342 | :param n: 343 | :param init_mean: True if initial ema value is average of first n points 344 | :return: 345 | """ 346 | return ema(2 * x - shift(x, n), n, init_mean=init_mean) 347 | 348 | 349 | def dema(x, n: int, init_mean=True): 350 | """ 351 | Double EMA 352 | 353 | :param x: 354 | :param n: 355 | :param init_mean: True if initial ema value is average of first n points 356 | :return: 357 | """ 358 | e1 = ema(x, n, init_mean=init_mean) 359 | return 2 * e1 - ema(e1, n, init_mean=init_mean) 360 | 361 | 362 | def tema(x, n: int, init_mean=True): 363 | """ 364 | Triple EMA 365 | 366 | :param x: 367 | :param n: 368 | :param init_mean: True if initial ema value is average of first n points 369 | :return: 370 | """ 371 | e1 = ema(x, n, init_mean=init_mean) 372 | e2 = ema(e1, n, init_mean=init_mean) 373 | return 3 * e1 - 3 * e2 + ema(e2, n, init_mean=init_mean) 374 | 375 | 376 | def bidirectional_ema(x, span, smoother='ema'): 377 | """ 378 | EMA function is really appropriate for stationary data, i.e., data without trends or seasonality. 379 | In particular, the EMA function resists trends away from the current mean that it’s already “seen”. 380 | So, if you have a noisy hat function that goes from 0, to 1, and then back to 0, then the EMA function will return 381 | low values on the up-hill side, and high values on the down-hill side. 382 | One way to circumvent this is to smooth the signal in both directions, marching forward, 383 | and then marching backward, and then average the two. 384 | 385 | :param x: data 386 | :param span: span for smoothing 387 | :param smoother: smoothing function (default 'ema' or 'tema') 388 | :return: smoohted data 389 | """ 390 | if smoother == 'tema': 391 | fwd = tema(x, span, init_mean=False) # take TEMA in forward direction 392 | bwd = tema(x[::-1], span, init_mean=False) # take TEMA in backward direction 393 | else: 394 | fwd = ema(x, span=span, init_mean=False) # take EMA in forward direction 395 | bwd = ema(x[::-1], span=span, init_mean=False) # take EMA in backward direction 396 | return (fwd + bwd[::-1]) / 2. 397 | 398 | 399 | def series_halflife(series): 400 | """ 401 | Tries to find half-life time for this series. 402 | 403 | Example: 404 | >>> series_halflife(np.array([1,0,2,3,2,1,-1,-2,0,1])) 405 | >>> 2.0 406 | 407 | :param series: series data (np.array or pd.Series) 408 | :return: half-life value rounded to integer 409 | """ 410 | series = column_vector(series) 411 | if series.shape[1] > 1: 412 | raise ValueError("Nultimple series is not supported") 413 | 414 | lag = series[1:] 415 | dY = -np.diff(series, axis=0) 416 | m = OLS(dY, sm.add_constant(lag, prepend=False)) 417 | reg = m.fit() 418 | 419 | return np.ceil(-np.log(2) / reg.params[0]) 420 | 421 | 422 | def rolling_std_with_mean(x, mean, window): 423 | """ 424 | Calculates rolling standard deviation for data from x and already calculated mean series 425 | :param x: series data 426 | :param mean: calculated mean 427 | :param window: window 428 | :return: rolling standard deviation 429 | """ 430 | return np.sqrt((((x - mean) ** 2).rolling(window=window).sum() / (window - 1))) 431 | 432 | 433 | def bollinger(x, window=14, nstd=2, mean='sma', as_frame=False): 434 | """ 435 | Bollinger Bands indicator 436 | 437 | :param x: input data 438 | :param window: lookback window 439 | :param nstd: number of standard devialtions for bands 440 | :param mean: method for calculating mean: sma, ema, tema, dema, zlema, kama 441 | :param as_frame: if true result is returned as DataFrame 442 | :return: mean, upper and lower bands 443 | """ 444 | rolling_mean = smooth(x, mean, window) 445 | rolling_std = rolling_std_with_mean(x, rolling_mean, window) 446 | 447 | upper_band = rolling_mean + (rolling_std * nstd) 448 | lower_band = rolling_mean - (rolling_std * nstd) 449 | 450 | _bb = rolling_mean, upper_band, lower_band 451 | return pd.concat(_bb, axis=1, keys=['Median', 'Upper', 'Lower']) if as_frame else _bb 452 | 453 | 454 | def bollinger_atr(x, window=14, atr_window=14, natr=2, mean='sma', atr_mean='ema', as_frame=False): 455 | """ 456 | Bollinger Bands indicator where ATR is used for bands range estimating 457 | :param x: input data 458 | :param window: window size for averaged price 459 | :param atr_window: atr window size 460 | :param natr: number of ATRs for bands 461 | :param mean: method for calculating mean: sma, ema, tema, dema, zlema, kama 462 | :param atr_mean: method for calculating mean for atr: sma, ema, tema, dema, zlema, kama 463 | :param as_frame: if true result is returned as DataFrame 464 | :return: mean, upper and lower bands 465 | """ 466 | if not (isinstance(x, pd.DataFrame) and sum(x.columns.isin(['open', 'high', 'low', 'close'])) == 4): 467 | raise ValueError("Input series must be DataFrame within 'open', 'high', 'low' and 'close' columns defined !") 468 | 469 | b, _, _ = bollinger(x.close, window, 0, mean, as_frame=False) 470 | a = natr * atr(x, atr_window, atr_mean) 471 | _bb = b, b + a, b - a 472 | 473 | return pd.concat(_bb, axis=1, keys=['Median', 'Upper', 'Lower']) if as_frame else _bb 474 | 475 | 476 | def macd(x, fast=12, slow=26, signal=9, method='ema', signal_method='ema'): 477 | """ 478 | Moving average convergence divergence (MACD) is a trend-following momentum indicator that shows the relationship 479 | between two moving averages of prices. The MACD is calculated by subtracting the 26-day slow moving average from the 480 | 12-day fast MA. A nine-day MA of the MACD, called the "signal line", is then plotted on top of the MACD, 481 | functioning as a trigger for buy and sell signals. 482 | 483 | :param x: input data 484 | :param fast: fast MA period 485 | :param slow: slow MA period 486 | :param signal: signal MA period 487 | :param method: used moving averaging method (sma, ema, tema, dema, zlema, kama) 488 | :param signal_method: used method for averaging signal (sma, ema, tema, dema, zlema, kama) 489 | :return: macd signal 490 | """ 491 | x_diff = smooth(x, method, fast) - smooth(x, method, slow) 492 | 493 | # averaging signal 494 | return smooth(x_diff, signal_method, signal).rename('macd') 495 | 496 | 497 | def atr(x, window=14, smoother='sma'): 498 | """ 499 | Average True Range indicator 500 | 501 | :param x: input series 502 | :param window: smoothing window size 503 | :param smoother: smooting method: sma, ema, zlema, tema, dema, kama 504 | :return: 505 | """ 506 | if not (isinstance(x, pd.DataFrame) and sum(x.columns.isin(['open', 'high', 'low', 'close'])) == 4): 507 | raise ValueError("Input series must be DataFrame within 'open', 'high', 'low' and 'close' columns defined !") 508 | 509 | h_l = abs(x['high'] - x['low']) 510 | h_pc = abs(x['high'] - x['close'].shift(1)) 511 | l_pc = abs(x['low'] - x['close'].shift(1)) 512 | tr = pd.concat((h_l, h_pc, l_pc), axis=1).max(axis=1) 513 | 514 | # smoothing 515 | return smooth(tr, smoother, window).rename('atr') 516 | 517 | 518 | def rolling_atr(x, window, periods, smoother=sma): 519 | """ 520 | Average True Range indicator calculated on rolling window 521 | 522 | :param x: 523 | :param window: windiw size as Timedelta or string 524 | :param periods: number periods for smoothing (applied if > 1) 525 | :param smoother: smoother (sma is default) 526 | :return: ATR 527 | """ 528 | if not (isinstance(x, pd.DataFrame) and sum(x.columns.isin(['open', 'high', 'low', 'close'])) == 4): 529 | raise ValueError("Input series must be DataFrame within 'open', 'high', 'low' and 'close' columns defined !") 530 | 531 | window = pd.Timedelta(window) if isinstance(window, str) else window 532 | tf_orig = pd.Timedelta(infer_series_frequency(x)) 533 | 534 | if window < tf_orig: 535 | raise ValueError('window size must be great or equal to OHLC series timeframe !!!') 536 | 537 | wind_delta = window + tf_orig 538 | n_min_periods = wind_delta // tf_orig 539 | _c_1 = x.rolling(wind_delta, min_periods=n_min_periods).close.apply(lambda y: y[0]) 540 | _l = x.rolling(window, min_periods=n_min_periods - 1).low.apply(lambda y: np.nanmin(y)) 541 | _h = x.rolling(window, min_periods=n_min_periods - 1).high.apply(lambda y: np.nanmax(y)) 542 | 543 | # calculate TR 544 | _tr = pd.concat((abs(_h - _l), abs(_h - _c_1), abs(_l - _c_1)), axis=1).max(axis=1) 545 | 546 | if smoother and periods > 1: 547 | _tr = smooth(_tr.ffill(), smoother, periods * max(1, (n_min_periods - 1))) 548 | 549 | return _tr 550 | 551 | 552 | def denoised_trend(x: pd.DataFrame, period: int, window=0, mean: str='kama', bar_returns: bool=True) -> pd.Series: 553 | """ 554 | Returns denoised trend (T_i). 555 | 556 | ---- 557 | 558 | R_i = C_i - O_i 559 | 560 | D_i = R_i - R_{i - period} 561 | 562 | P_i = sum_{k=i-period-1}^{i} abs(R_k) 563 | 564 | T_i = D_i * abs(D_i) / P_i 565 | 566 | ---- 567 | 568 | :param x: OHLC dataset (must contain .open and .close columns) 569 | :param period: period of filtering 570 | :param window: smothing window size (default 0) 571 | :param mean: smoother 572 | :param bar_returns: if True use R_i = close_i - open_i 573 | :return: trend with removed noise 574 | """ 575 | if bar_returns: 576 | ri = x.close - x.open 577 | di = x.close - x.open.shift(period) 578 | else: 579 | ri = x.close - x.close.shift(1) 580 | di = x.close - x.close.shift(period) 581 | period -= 1 582 | 583 | abs_di = abs(di) 584 | si = abs(ri).rolling(window=period+1).sum() 585 | # for open - close there may be gaps 586 | if bar_returns: 587 | si = np.max(np.concatenate((abs_di[:, np.newaxis], si[:, np.newaxis]), axis=1), axis=1) 588 | filtered_trend = abs_di * (di / si) 589 | filtered_trend = filtered_trend.replace([np.inf, -np.inf], 0.0) 590 | 591 | if window > 0 and mean is not None: 592 | filtered_trend = smooth(filtered_trend, mean, window) 593 | 594 | return filtered_trend 595 | 596 | 597 | def rolling_percentiles(x, window, pctls=(0, 1, 2, 3, 5, 10, 15, 25, 45, 50, 55, 75, 85, 90, 95, 97, 98, 99, 100)): 598 | """ 599 | Calculates percentiles from x on rolling window basis 600 | 601 | :param x: series data 602 | :param window: window size 603 | :param pctls: percentiles 604 | :return: calculated percentiles as DataFrame indexed by time. 605 | Every pctl. is denoted as Qd (where d is taken from pctls) 606 | """ 607 | r = nans((len(x), len(pctls))) 608 | i = window - 1 609 | 610 | for v in running_view(x, window): 611 | r[i, :] = np.percentile(v, pctls) 612 | i += 1 613 | 614 | return pd.DataFrame(r, index=x.index, columns=['Q%d' % q for q in pctls]) 615 | 616 | 617 | def __slope_ols(x): 618 | x = x[~np.isnan(x)] 619 | xs = 2 * (x - min(x)) / (max(x) - min(x)) - 1 620 | m = OLS(xs, np.vander(np.linspace(-1, 1, len(xs)), 2)).fit() 621 | return m.params[0] 622 | 623 | 624 | def __slope_angle(p, t): 625 | return 180 * np.arctan(p / t) / np.pi 626 | 627 | 628 | def rolling_series_slope(x, period, method='ols', scaling='transform', n_bins=5): 629 | """ 630 | Rolling slope indicator. May be used as trend indicator 631 | 632 | :param x: time series 633 | :param period: period for OLS window 634 | :param n_bins: number of bins used for scaling 635 | :param method: method used for metric of regression line slope: ('ols' or 'angle') 636 | :param scaling: how to scale slope 'transform' / 'binarize' / nothing 637 | :return: series slope metric 638 | """ 639 | 640 | def __binarize(_x, n, limits=(None, None), center=False): 641 | n0 = n // 2 if center else 0 642 | _min = np.min(_x) if limits[0] is None else limits[0] 643 | _max = np.max(_x) if limits[1] is None else limits[1] 644 | return np.floor(n * (_x - _min) / (_max - _min)) - n0 645 | 646 | def __scaling_transform(x, n=5, need_round=True, limits=None): 647 | if limits is None: 648 | _lmax = max(abs(x)) 649 | _lmin = -_lmax 650 | else: 651 | _lmax = max(limits) 652 | _lmin = min(limits) 653 | 654 | if need_round: 655 | ni = np.round(np.interp(x, (_lmin, _lmax), (-2 * n, +2 * n))) / 2 656 | else: 657 | ni = np.interp(x, (_lmin, _lmax), (-n, +n)) 658 | return pd.Series(ni, index=x.index) 659 | 660 | if method == 'ols': 661 | slp_meth = lambda z: __slope_ols(z) 662 | _lmts = (-1, 1) 663 | elif method == 'angle': 664 | slp_meth = lambda z: __slope_angle(z[-1] - z[0], len(z)) 665 | _lmts = (-90, 90) 666 | else: 667 | raise ValueError('Unknown Method %s' % method) 668 | 669 | _min_p = period 670 | if isinstance(period, str): 671 | _min_p = pd.Timedelta(period).days 672 | 673 | roll_slope = x.rolling(period, min_periods=_min_p).apply(slp_meth) 674 | 675 | if scaling == 'transform': 676 | return __scaling_transform(roll_slope, n=n_bins, limits=_lmts) 677 | elif scaling == 'binarize': 678 | return __binarize(roll_slope, n=(n_bins - 1) * 4, limits=_lmts, center=True) / 2 679 | 680 | return roll_slope 681 | 682 | 683 | def adx(ohlc, period, smoother=kama): 684 | """ 685 | Average Directional Index. 686 | 687 | ADX = 100 * MA(abs((+DI - -DI) / (+DI + -DI))) 688 | 689 | Where: 690 | -DI = 100 * MA(-DM) / ATR 691 | +DI = 100 * MA(+DM) / ATR 692 | 693 | +DM: if UPMOVE > DWNMOVE and UPMOVE > 0 then +DM = UPMOVE else +DM = 0 694 | -DM: if DWNMOVE > UPMOVE and DWNMOVE > 0 then -DM = DWNMOVE else -DM = 0 695 | 696 | DWNMOVE = L_{t-1} - L_t 697 | UPMOVE = H_t - H_{t-1} 698 | 699 | :param ohlc: DataFrame with ohlc data 700 | :param period: indicator period 701 | :param smoother: smoothing function (kama is default) 702 | :return: adx, DIp, DIm 703 | """ 704 | if not (isinstance(ohlc, pd.DataFrame) and sum(ohlc.columns.isin(['open', 'high', 'low', 'close'])) == 4): 705 | raise ValueError("Input series must be DataFrame within 'open', 'high', 'low' and 'close' columns defined !") 706 | 707 | h, l = ohlc['high'], ohlc['low'] 708 | _atr = atr(ohlc, period, smoother=smoother) 709 | 710 | Mu, Md = h.diff(), -l.diff() 711 | DMp = Mu * (((Mu > 0) & (Mu > Md)) + 0) 712 | DMm = Md * (((Md > 0) & (Md > Mu)) + 0) 713 | DIp = 100 * smooth(DMp, smoother, period) / _atr 714 | DIm = 100 * smooth(DMm, smoother, period) / _atr 715 | _adx = 100 * smooth(abs((DIp - DIm) / (DIp + DIm)), smoother, period) 716 | 717 | return _adx.rename('ADX'), DIp.rename('DIp'), DIm.rename('DIm') 718 | 719 | 720 | def rsi(x, periods, smoother=sma): 721 | """ 722 | U = X_t - X_{t-1}, D = 0 when X_t > X_{t-1} 723 | D = X_{t-1} - X_t, U = 0 when X_t < X_{t-1} 724 | U = 0, D = 0, when X_t = X_{t-1} 725 | 726 | RSI = 100 * E[U, n] / (E[U, n] + E[D, n]) 727 | 728 | """ 729 | xx = pd.concat((x, x.shift(1)), axis=1, keys=['c', 'p']) 730 | df = (xx.c - xx.p) 731 | mu = smooth(df.where(df > 0, 0), smoother, periods) 732 | md = smooth(abs(df.where(df < 0, 0)), smoother, periods) 733 | 734 | return 100 * mu / (mu + md) 735 | 736 | 737 | def pivot_point(data, method='classic', timeframe='D', timezone='EET'): 738 | """ 739 | Pivot points indicator based on {daily, weekly or monthy} data 740 | it supports 'classic', 'woodie' and 'camarilla' species 741 | """ 742 | if timeframe not in ['D', 'W', 'M']: 743 | raise ValueError("Wrong timeframe parameter value, only 'D', 'W' and 'M' allowed") 744 | 745 | tf_resample = f'1{timeframe}' 746 | x = ohlc_resample(data, tf_resample, resample_tz=timezone) 747 | 748 | pp = pd.DataFrame() 749 | if method == 'classic': 750 | pvt = (x.high + x.low + x.close) / 3 751 | _range = x.high - x.low 752 | 753 | pp['R4'] = pvt + 3 * _range 754 | pp['R3'] = pvt + 2 * _range 755 | pp['R2'] = pvt + _range 756 | pp['R1'] = pvt * 2 - x.low 757 | pp['P'] = pvt 758 | pp['S1'] = pvt * 2 - x.high 759 | pp['S2'] = pvt - _range 760 | pp['S3'] = pvt - 2 * _range 761 | pp['S4'] = pvt - 3 * _range 762 | 763 | # rearrange 764 | pp = pp[['R4', 'R3', 'R2', 'R1', 'P', 'S1', 'S2', 'S3', 'S4']] 765 | 766 | elif method == 'woodie': 767 | pvt = (x.high + x.low + x.open + x.open) / 4 768 | _range = x.high - x.low 769 | 770 | pp['R3'] = x.high + 2 * (pvt - x.low) 771 | pp['R2'] = pvt + _range 772 | pp['R1'] = pvt * 2 - x.low 773 | pp['P'] = pvt 774 | pp['S1'] = pvt * 2 - x.high 775 | pp['S2'] = pvt - _range 776 | pp['S3'] = x.low + 2 * (x.high - pvt) 777 | pp = pp[['R3', 'R2', 'R1', 'P', 'S1', 'S2', 'S3']] 778 | 779 | elif method == 'camarilla': 780 | """ 781 | R4 = C + RANGE * 1.1/2 782 | R3 = C + RANGE * 1.1/4 783 | R2 = C + RANGE * 1.1/6 784 | R1 = C + RANGE * 1.1/12 785 | PP = (HIGH + LOW + CLOSE) / 3 786 | S1 = C - RANGE * 1.1/12 787 | S2 = C - RANGE * 1.1/6 788 | S3 = C - RANGE * 1.1/4 789 | S4 = C - RANGE * 1.1/2 790 | """ 791 | pvt = (x.high + x.low + x.close) / 3 792 | _range = x.high - x.low 793 | 794 | pp['R4'] = x.close + _range * 1.1 / 2 795 | pp['R3'] = x.close + _range * 1.1 / 4 796 | pp['R2'] = x.close + _range * 1.1 / 6 797 | pp['R1'] = x.close + _range * 1.1 / 12 798 | pp['P'] = pvt 799 | pp['S1'] = x.close - _range * 1.1 / 12 800 | pp['S2'] = x.close - _range * 1.1 / 6 801 | pp['S3'] = x.close - _range * 1.1 / 4 802 | pp['S4'] = x.close - _range * 1.1 / 2 803 | pp = pp[['R4', 'R3', 'R2', 'R1', 'P', 'S1', 'S2', 'S3', 'S4']] 804 | else: 805 | raise ValueError("Unknown method %s. Available methods are classic, woodie, camarilla" % method) 806 | 807 | pp.index = pp.index + pd.Timedelta(tf_resample) 808 | return data.combine_first(pp).fillna(method='ffill')[pp.columns] 809 | 810 | 811 | def intraday_min_max(data, timezone='EET'): 812 | """ 813 | Intradeay min and max values 814 | :param data: ohlcv series 815 | :return: series with min and max values intraday 816 | """ 817 | 818 | if not (isinstance(data, pd.DataFrame) and sum(data.columns.isin(['open', 'high', 'low', 'close'])) == 4): 819 | raise ValueError("Input series must be DataFrame within 'open', 'high', 'low' and 'close' columns defined !") 820 | 821 | def _day_min_max(d): 822 | _d_min = np.minimum.accumulate(d.low) 823 | _d_max = np.maximum.accumulate(d.high) 824 | return pd.concat((_d_min, _d_max), axis=1, keys=['Min', 'Max']) 825 | 826 | source_tz = data.index.tz 827 | if not source_tz: 828 | x = data.tz_localize('GMT') 829 | else: 830 | x = data 831 | 832 | x = x.tz_convert(timezone) 833 | return x.groupby(x.index.date).apply(_day_min_max).tz_convert(source_tz) 834 | 835 | -------------------------------------------------------------------------------- /tools/analysis/tools.py: -------------------------------------------------------------------------------- 1 | import types 2 | from typing import Union 3 | 4 | import numpy as np 5 | import pandas as pd 6 | from numba import njit 7 | from numpy.lib.stride_tricks import as_strided as stride 8 | 9 | 10 | def column_vector(x): 11 | """ 12 | Convert any vector to column vector. Matrices remain unchanged. 13 | 14 | :param x: vector 15 | :return: column vector 16 | """ 17 | if isinstance(x, (pd.DataFrame, pd.Series)): x = x.values 18 | return np.reshape(x, (x.shape[0], -1)) 19 | 20 | @njit 21 | def shift(xs: np.ndarray, n: int, fill=np.nan) -> np.ndarray: 22 | """ 23 | Shift data in numpy array (aka lag function): 24 | 25 | shift(np.array([[1.,2.], 26 | [11.,22.], 27 | [33.,44.]]), 1) 28 | 29 | >> array([[ nan, nan], 30 | [ 1., 2.], 31 | [ 11., 22.]]) 32 | 33 | :param xs: 34 | :param n: 35 | :param fill: value to use for 36 | :return: 37 | """ 38 | e = np.empty_like(xs) 39 | if n >= 0: 40 | e[:n] = fill 41 | e[n:] = xs[:-n] 42 | else: 43 | e[n:] = fill 44 | e[:n] = xs[-n:] 45 | return e 46 | 47 | 48 | def sink_nans_down(x_in, copy=False) -> (np.ndarray, np.ndarray): 49 | """ 50 | Move all starting nans 'down to the bottom' in every column. 51 | 52 | NaN = np.nan 53 | x = np.array([[NaN, 1, NaN], 54 | [NaN, 2, NaN], 55 | [NaN, 3, NaN], 56 | [10, 4, NaN], 57 | [20, 5, NaN], 58 | [30, 6, 100], 59 | [40, 7, 200]]) 60 | 61 | x1, nx = sink_nans_down(x) 62 | print(x1) 63 | 64 | >> [[ 10. 1. 100.] 65 | [ 20. 2. 200.] 66 | [ 30. 3. nan] 67 | [ 40. 4. nan] 68 | [ nan 5. nan] 69 | [ nan 6. nan] 70 | [ nan 7. nan]] 71 | 72 | :param x_in: numpy 1D/2D array 73 | :param copy: set if need to make copy input to prevent being modified [False by default] 74 | :return: modified x_in and indexes 75 | """ 76 | x = np.copy(x_in) if copy else x_in 77 | n_ix = np.zeros(x.shape[1]) 78 | for i in range(0, x.shape[1]): 79 | f_n = np.where(~np.isnan(x[:, i]))[0] 80 | if len(f_n) > 0: 81 | if f_n[0] != 0: 82 | x[:, i] = np.concatenate((x[f_n[0]:, i], x[:f_n[0], i])) 83 | n_ix[i] = f_n[0] 84 | return x, n_ix 85 | 86 | 87 | def lift_nans_up(x_in, n_ix, copy=False) -> np.ndarray: 88 | """ 89 | Move all ending nans 'up to top' of every column. 90 | 91 | NaN = np.nan 92 | x = np.array([[NaN, 1, NaN], 93 | [NaN, 2, NaN], 94 | [NaN, 3, NaN], 95 | [10, 4, NaN], 96 | [20, 5, NaN], 97 | [30, 6, 100], 98 | [40, 7, 200]]) 99 | 100 | x1, nx = sink_nans_down(x) 101 | print(x1) 102 | 103 | >> [[ 10. 1. 100.] 104 | [ 20. 2. 200.] 105 | [ 30. 3. nan] 106 | [ 40. 4. nan] 107 | [ nan 5. nan] 108 | [ nan 6. nan] 109 | [ nan 7. nan]] 110 | 111 | x2 = lift_nans_up(x1, nx) 112 | print(x2) 113 | 114 | >> [[ nan 1. nan] 115 | [ nan 2. nan] 116 | [ nan 3. nan] 117 | [ 10. 4. nan] 118 | [ 20. 5. nan] 119 | [ 30. 6. 100.] 120 | [ 40. 7. 200.]] 121 | 122 | :param x_in: numpy 1D/2D array 123 | :param n_ix: indexes for every column 124 | :param copy: set if need to make copy input to prevent being modified [False by default] 125 | :return: modified x_in 126 | """ 127 | x = np.copy(x_in) if copy else x_in 128 | for i in range(0, x.shape[1]): 129 | f_n = int(n_ix[i]) 130 | if f_n != 0: 131 | x[:, i] = np.concatenate((nans(f_n), x[:-f_n, i])) 132 | return x 133 | 134 | 135 | def add_constant(x, const=1., prepend=True): 136 | """ 137 | Adds a column of constants to an array 138 | 139 | Parameters 140 | ---------- 141 | :param data: column-ordered design matrix 142 | :param prepend: If true, the constant is in the first column. Else the constant is appended (last column) 143 | :param const: constant value to be appended (default is 1.0) 144 | :return: 145 | """ 146 | x = column_vector(x) 147 | if prepend: 148 | r = (const * np.ones((x.shape[0], 1)), x) 149 | else: 150 | r = (x, const * np.ones((x.shape[0], 1))) 151 | return np.hstack(r) 152 | 153 | 154 | def isscalar(x): 155 | """ 156 | Returns true if x is scalar value 157 | 158 | :param x: 159 | :return: 160 | """ 161 | return not isinstance(x, (list, tuple, dict, np.ndarray)) 162 | 163 | @njit 164 | def nans(dims): 165 | """ 166 | nans((M,N,P,...)) is an M-by-N-by-P-by-... array of NaNs. 167 | 168 | :param dims: dimensions tuple 169 | :return: nans matrix 170 | """ 171 | return np.nan * np.ones(dims) 172 | 173 | @njit 174 | def rolling_sum(x:np.ndarray, n:int) -> np.ndarray: 175 | """ 176 | Fast running sum for numpy array (matrix) along columns. 177 | 178 | Example: 179 | >>> rolling_sum(column_vector(np.array([[1,2,3,4,5,6,7,8,9], [11,22,33,44,55,66,77,88,99]]).T), n=5) 180 | 181 | array([[ nan, nan], 182 | [ nan, nan], 183 | [ nan, nan], 184 | [ nan, nan], 185 | [ 15., 165.], 186 | [ 20., 220.], 187 | [ 25., 275.], 188 | [ 30., 330.], 189 | [ 35., 385.]]) 190 | 191 | :param x: input data 192 | :param n: rolling window size 193 | :return: rolling sum for every column preceded by nans 194 | """ 195 | for i in range(0, x.shape[1]): 196 | ret = np.nancumsum(x[:,i]) 197 | ret[n:] = ret[n:] - ret[:-n] 198 | x[:,i] = np.concatenate((nans(n - 1), ret[n - 1:])) 199 | return x 200 | 201 | 202 | def apply_to_frame(func, x, *args, **kwargs): 203 | """ 204 | Utility applies given function to x and converts result to incoming type 205 | 206 | >>> from ira.analysis.timeseries import ema 207 | >>> apply_to_frame(ema, data['EURUSD'], 50) 208 | >>> apply_to_frame(lambda x, p1: x + p1, data['EURUSD'], 1) 209 | 210 | :param func: function to map 211 | :param x: input data 212 | :param args: arguments of func 213 | :param kwargs: named arguments of func (if it contains keep_names=True it won't change source columns names) 214 | :return: result of function's application 215 | """ 216 | _keep_names = False 217 | if 'keep_names' in kwargs: 218 | _keep_names = kwargs.pop('keep_names') 219 | 220 | if func is None or not isinstance(func, types.FunctionType): 221 | raise ValueError(str(func) + ' must be callable object') 222 | 223 | xp = column_vector(func(x, *args, **kwargs)) 224 | _name = None 225 | if not _keep_names: 226 | _name = func.__name__ + '_' + '_'.join([str(i) for i in args]) 227 | 228 | if isinstance(x, pd.DataFrame): 229 | c_names = x.columns if _keep_names else ['%s_%s' % (c, _name) for c in x.columns] 230 | return pd.DataFrame(xp, index=x.index, columns=c_names) 231 | elif isinstance(x, pd.Series): 232 | return pd.Series(xp.flatten(), index=x.index, name=_name) 233 | 234 | return xp 235 | 236 | 237 | def ohlc_resample(df, new_freq: str = '1H', vmpt: bool = False, resample_tz=None) -> Union[pd.DataFrame, dict]: 238 | """ 239 | Resample OHLCV/tick series to new timeframe. 240 | 241 | Example: 242 | >>> d = pd.DataFrame({ 243 | >>> 'open' : np.random.randn(30), 244 | >>> 'high' : np.random.randn(30), 245 | >>> 'low' : np.random.randn(30), 246 | >>> 'close' : np.random.randn(30) 247 | >>> }, index=pd.date_range('2000-01-01 00:00', freq='5Min', periods=30)) 248 | >>> 249 | >>> ohlc_resample(d, '15Min') 250 | >>> 251 | >>> # if we need to resample quotes 252 | >>> from ira.datasource import DataSource 253 | >>> with DataSource('kdb::dukas') as ds: 254 | >>> quotes = ds.load_data(['EURUSD', 'GBPUSD'], '2018-05-07', '2018-05-11') 255 | >>> ohlc_resample(quotes, '1Min', vmpt=True) 256 | 257 | :param df: input ohlc or bid/ask quotes or dict 258 | :param new_freq: how to resample rule (see pandas.DataFrame::resample) 259 | :param vmpt: use volume weighted price for quotes (if false mid price will be used) 260 | :param resample_tz: timezone for resample. For example, to create daily bars in the EET timezone 261 | :return: resampled ohlc / dict 262 | """ 263 | def __mx_rsmpl(d, freq: str, is_vmpt: bool = False, resample_tz=None) -> pd.DataFrame: 264 | _cols = d.columns 265 | _source_tz = d.index.tz 266 | 267 | # if we have bid/ask frame 268 | if 'ask' in _cols and 'bid' in _cols: 269 | # if sizes are presented we can calc vmpt if need 270 | if is_vmpt and 'askvol' in _cols and 'bidvol' in _cols: 271 | mp = (d.ask * d.bidvol + d.bid * d.askvol) / (d.askvol + d.bidvol) 272 | return mp.resample(freq).agg('ohlc') 273 | 274 | # if there is only asks and bids and we don't need vmpt 275 | result = _tz_convert(d[['ask', 'bid']].mean(axis=1), resample_tz, _source_tz) 276 | result = result.resample(freq).agg('ohlc') 277 | # Convert timezone to back if it changed 278 | return result if not resample_tz else result.tz_convert(_source_tz) 279 | 280 | # for OHLC case or just simple series 281 | if all([i in _cols for i in ['open', 'high', 'low', 'close']]) or isinstance(d, pd.Series): 282 | ohlc_rules = {'open': 'first', 283 | 'high': 'max', 284 | 'low': 'min', 285 | 'close': 'last', 286 | 'ask_vol': 'sum', 287 | 'bid_vol': 'sum', 288 | 'volume': 'sum' 289 | } 290 | result = _tz_convert(d, resample_tz, _source_tz) 291 | result = result.resample(freq).apply(dict(i for i in ohlc_rules.items() if i[0] in d.columns)).dropna() 292 | # Convert timezone to back if it changed 293 | return result if not resample_tz else result.tz_convert(_source_tz) 294 | 295 | raise ValueError("Can't recognize structure of input data !") 296 | 297 | def _tz_convert(df, tz, source_tz): 298 | if tz: 299 | if not source_tz: 300 | df = df.tz_localize('GMT') 301 | return df.tz_convert(tz) 302 | else: 303 | return df 304 | 305 | if isinstance(df, (pd.DataFrame, pd.Series)): 306 | return __mx_rsmpl(df, new_freq, vmpt, resample_tz) 307 | elif isinstance(df, dict): 308 | return {k: __mx_rsmpl(v, new_freq, vmpt, resample_tz) for k, v in df.items()} 309 | else: 310 | raise ValueError('Type [%s] is not supported in ohlc_resample' % str(type(df))) 311 | 312 | 313 | def round_up(x, step): 314 | """ 315 | Round float to nearest greater value by step 316 | 34.23 -> 34.50 etc 317 | 318 | :param x: value to round 319 | :param step: step 320 | :return: rounded value 321 | """ 322 | return int(np.ceil(x / step)) * step 323 | 324 | 325 | def round_down(x, step): 326 | """ 327 | Round float to nearest lesser value by step 328 | 34.67 -> 34.50 etc 329 | 330 | :param x: value to round 331 | :param step: step 332 | :return: rounded value 333 | """ 334 | return (int(x / step)) * step 335 | 336 | 337 | def roll(df: pd.DataFrame, w: int, **kwargs): 338 | """ 339 | Rolling window on dataframe using multiple columns 340 | 341 | >>> roll(pd.DataFrame(np.random.randn(10,3), index=list('ABCDEFGHIJ')), 3).apply(print) 342 | 343 | or alternatively 344 | 345 | >>> pd.DataFrame(np.random.randn(10,3), index=list('ABCDEFGHIJ')).pipe(roll, 3).apply(lambda x: print(x[2])) 346 | 347 | :param df: pandas DataFrame 348 | :param w: window size (only integers) 349 | :return: rolling window 350 | """ 351 | if w > len(df): 352 | raise ValueError("Window size exceeds number of rows !") 353 | 354 | v = df.values 355 | d0, d1 = v.shape 356 | s0, s1 = v.strides 357 | a = stride(v, (d0 - (w - 1), w, d1), (s0, s0, s1)) 358 | rolled_df = pd.concat({ 359 | row: pd.DataFrame(values, columns=df.columns) 360 | for row, values in zip(df.index, a) 361 | }) 362 | 363 | return rolled_df.groupby(level=0, **kwargs) 364 | 365 | 366 | def drop_duplicated_indexes(df, keep='first'): 367 | """ 368 | Drops duplicated indexes in dataframe/series 369 | Keeps either first or last occurence (parameter keep) 370 | """ 371 | return df[~df.index.duplicated(keep=keep)] 372 | 373 | 374 | def scols(*xs, keys=None, names=None, keep='all'): 375 | """ 376 | Concat dataframes/series from xs into single dataframe by axis 1 377 | :param keys: keys of new dataframe (see pd.concat's keys parameter) 378 | :param names: new column names or dict with replacements 379 | :return: combined dataframe 380 | 381 | Example 382 | ------- 383 | >>> scols( 384 | pd.DataFrame([1,2,3,4,-4], list('abcud')), 385 | pd.DataFrame([111,21,31,14], list('xyzu')), 386 | pd.DataFrame([11,21,31,124], list('ertu')), 387 | pd.DataFrame([11,21,31,14], list('WERT')), 388 | names=['x', 'y', 'z', 'w']) 389 | """ 390 | r = pd.concat((xs), axis=1, keys=keys) 391 | if names: 392 | if isinstance(names, (list, tuple)): 393 | if len(names) == len(r.columns): 394 | r.columns = names 395 | else: 396 | raise ValueError(f"if 'names' contains new column names it must have same length as resulting df ({len(r.columns)})") 397 | elif isinstance(names, dict): 398 | r = r.rename(columns=names) 399 | return r 400 | 401 | 402 | def srows(*xs, keep='all', sort=True): 403 | """ 404 | Concat dataframes/series from xs into single dataframe by axis 0 405 | :param sort: if true it sorts resulting dataframe by index (default) 406 | :param keep: how to deal with duplicated indexes. 407 | If set to 'all' it doesn't do anything (default). Otherwise keeps first or last occurences 408 | :return: combined dataframe 409 | 410 | Example 411 | ------- 412 | >>> srows( 413 | pd.DataFrame([1,2,3,4,-4], list('abcud')), 414 | pd.DataFrame([111,21,31,14], list('xyzu')), 415 | pd.DataFrame([11,21,31,124], list('ertu')), 416 | pd.DataFrame([11,21,31,14], list('WERT')), 417 | sort=True, keep='last') 418 | """ 419 | r = pd.concat((xs), axis=0) 420 | r = r.sort_index() if sort else r 421 | if keep != 'all': 422 | r = drop_duplicated_indexes(r, keep=keep) 423 | return r 424 | 425 | 426 | def retain_columns_and_join(data: dict, columns): 427 | """ 428 | Retains given columns from every value of data dictionary and concatenate them into single data frame 429 | 430 | from ira.datasource import DataSource 431 | from ira.analysis.tools import retain_columns_and_join 432 | 433 | ds = DataSource('yahoo::daily') 434 | data = ds.load_data(['aapl', 'msft', 'spy'], '2000-01-01', 'now') 435 | 436 | closes = retain_columns_and_join(data, 'close') 437 | hi_lo = retain_columns_and_join(data, ['high', 'low']) 438 | 439 | :param data: dictionary with dataframes 440 | :param columns: columns names need to be retained 441 | :return: data frame 442 | """ 443 | if not isinstance(data, dict): 444 | raise ValueError('Data must be passed as dictionary') 445 | 446 | return pd.concat([data[k][columns] for k in data.keys()], axis=1, keys=data.keys()) 447 | -------------------------------------------------------------------------------- /tools/charting/plot_helpers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Misc graphics handy utilitites to be used in interactive analysis 3 | """ 4 | import numpy as np 5 | import pandas as pd 6 | import itertools as it 7 | from typing import List, Tuple, Union 8 | 9 | try: 10 | import matplotlib 11 | import matplotlib.dates as mdates 12 | import matplotlib.pyplot as plt 13 | import matplotlib.ticker as mticker 14 | except: 15 | print("Can't import matplotlib modules in charting modlue") 16 | 17 | from tools.analysis.tools import isscalar 18 | from tools.charting.mpl_finance import ohlc_plot 19 | 20 | 21 | def setup_mpl_theme(theme='dark'): 22 | from cycler import cycler 23 | import matplotlib 24 | 25 | DARK_MPL_THEME = [ 26 | ('backend', 'module://ipykernel.pylab.backend_inline'), 27 | ('interactive', True), 28 | ('lines.color', '#5050f0'), 29 | ('text.color', '#d0d0d0'), 30 | ('axes.facecolor', '#000000'), 31 | ('axes.edgecolor', '#404040'), 32 | ('axes.grid', True), 33 | ('axes.labelsize', 'large'), 34 | ('axes.labelcolor', 'green'), 35 | ('axes.prop_cycle', cycler('color', ['#449AcD', 'g', '#f62841', 'y', '#088487', '#E24A33', '#f01010'])), 36 | ('legend.fontsize', 'small'), 37 | ('legend.fancybox', False), 38 | ('legend.edgecolor', '#305030'), 39 | ('legend.shadow', False), 40 | ('lines.antialiased', True), 41 | ('lines.linewidth', 0.8), # reduced line width 42 | ('patch.linewidth', 0.5), 43 | ('patch.antialiased', True), 44 | ('xtick.color', '#909090'), 45 | ('ytick.color', '#909090'), 46 | ('xtick.labelsize', 'small'), 47 | ('ytick.labelsize', 'small'), 48 | ('grid.color', '#404040'), 49 | ('grid.linestyle', '--'), 50 | ('grid.linewidth', 0.5), 51 | ('grid.alpha', 0.8), 52 | ('figure.figsize', [8.0, 5.0]), 53 | ('figure.dpi', 80.0), 54 | ('figure.facecolor', '#000000'), 55 | ('figure.edgecolor', (1, 1, 1, 0)), 56 | ('figure.subplot.bottom', 0.125) 57 | ] 58 | 59 | LIGHT_MPL_THEME = [ 60 | ('backend', 'module://ipykernel.pylab.backend_inline'), 61 | ('interactive', True), 62 | ('lines.color', '#101010'), 63 | ('text.color', '#303030'), 64 | ('lines.antialiased', True), 65 | ('lines.linewidth', 1), 66 | ('patch.linewidth', 0.5), 67 | ('patch.facecolor', '#348ABD'), 68 | ('patch.edgecolor', '#eeeeee'), 69 | ('patch.antialiased', True), 70 | ('axes.facecolor', '#fafafa'), 71 | ('axes.edgecolor', '#d0d0d0'), 72 | ('axes.linewidth', 1), 73 | ('axes.titlesize', 'x-large'), 74 | ('axes.labelsize', 'large'), 75 | ('axes.labelcolor', '#555555'), 76 | ('axes.axisbelow', True), 77 | ('axes.grid', True), 78 | ('axes.prop_cycle', cycler('color', ['#6792E0', '#27ae60', '#c44e52', '#975CC3', '#ff914d', '#77BEDB', 79 | '#303030', '#4168B7', '#93B851', '#e74c3c', '#bc89e0', '#ff711a', 80 | '#3498db', '#6C7A89'])), 81 | ('legend.fontsize', 'small'), 82 | ('legend.fancybox', False), 83 | ('xtick.color', '#707070'), 84 | ('ytick.color', '#707070'), 85 | ('grid.color', '#606060'), 86 | ('grid.linestyle', '--'), 87 | ('grid.linewidth', 0.5), 88 | ('grid.alpha', 0.3), 89 | ('figure.figsize', [8.0, 5.0]), 90 | ('figure.dpi', 80.0), 91 | ('figure.facecolor', '#ffffff'), 92 | ('figure.edgecolor', '#ffffff'), 93 | ('figure.subplot.bottom', 0.1) 94 | ] 95 | 96 | t = DARK_MPL_THEME if 'dark' in theme.lower() else LIGHT_MPL_THEME 97 | for (k, v) in t: 98 | matplotlib.rcParams[k] = v 99 | 100 | 101 | def fig(w=16, h=5, dpi=96, facecolor=None, edgecolor=None, num=None): 102 | """ 103 | Simple helper for creating figure 104 | """ 105 | return plt.figure(num=num, figsize=(w, h), dpi=dpi, facecolor=facecolor, edgecolor=edgecolor) 106 | 107 | 108 | def subplot(shape, loc, rowspan=1, colspan=1): 109 | """ 110 | Some handy grid splitting for plots. Example for 2x2: 111 | 112 | >>> subplot(22, 1); plt.plot([-1,2,-3]) 113 | >>> subplot(22, 2); plt.plot([1,2,3]) 114 | >>> subplot(22, 3); plt.plot([1,2,3]) 115 | >>> subplot(22, 4); plt.plot([3,-2,1]) 116 | 117 | same as following 118 | 119 | >>> subplot((2,2), (0,0)); plt.plot([-1,2,-3]) 120 | >>> subplot((2,2), (0,1)); plt.plot([1,2,3]) 121 | >>> subplot((2,2), (1,0)); plt.plot([1,2,3]) 122 | >>> subplot((2,2), (1,1)); plt.plot([3,-2,1]) 123 | 124 | :param shape: scalar (like matlab subplot) or tuple 125 | :param loc: scalar (like matlab subplot) or tuple 126 | :param rowspan: rows spanned 127 | :param colspan: columns spanned 128 | """ 129 | if isscalar(shape): 130 | if 0 < shape < 100: 131 | shape = (max(shape // 10, 1), max(shape % 10, 1)) 132 | else: 133 | raise ValueError("Wrong scalar value for shape. It should be in range (1...99)") 134 | 135 | if isscalar(loc): 136 | nm = max(shape[0], 1) * max(shape[1], 1) 137 | if 0 < loc <= nm: 138 | x = (loc - 1) // shape[1] 139 | y = loc - 1 - shape[1] * x 140 | loc = (x, y) 141 | else: 142 | raise ValueError("Wrong scalar value for location. It should be in range (1...%d)" % nm) 143 | 144 | return plt.subplot2grid(shape, loc=loc, rowspan=rowspan, colspan=colspan) 145 | 146 | 147 | def sbp(shape, loc, r=1, c=1): 148 | """ 149 | Just shortcut for subplot(...) function 150 | 151 | :param shape: scalar (like matlab subplot) or tuple 152 | :param loc: scalar (like matlab subplot) or tuple 153 | :param r: rows spanned 154 | :param c: columns spanned 155 | :return: 156 | """ 157 | return subplot(shape, loc, rowspan=r, colspan=c) 158 | 159 | 160 | def plot_trends(trends, uc='w--', dc='c--', lw=0.7, ms=5, fmt='%H:%M'): 161 | """ 162 | Plot find_movements function output as trend lines on chart 163 | 164 | >>> from ira.analysis import find_movements 165 | >>> 166 | >>> tx = pd.Series(np.random.randn(500).cumsum() + 100, index=pd.date_range('2000-01-01', periods=500)) 167 | >>> trends = find_movements(tx, np.inf, use_prev_movement_size_for_percentage=False, 168 | >>> pcntg=0.02, 169 | >>> t_window=np.inf, drop_weekends_crossings=False, 170 | >>> drop_out_of_market=False, result_as_frame=True, silent=True) 171 | >>> plot_trends(trends) 172 | 173 | :param trends: find_movements output 174 | :param uc: up trends line spec ('w--') 175 | :param dc: down trends line spec ('c--') 176 | :param lw: line weight (0.7) 177 | :param ms: trends reversals marker size (5) 178 | :param fmt: time format (default is '%H:%M') 179 | """ 180 | if not trends.empty: 181 | u, d = trends.UpTrends.dropna(), trends.DownTrends.dropna() 182 | plt.plot([u.index, u.end], [u.start_price, u.end_price], uc, lw=lw, marker='.', markersize=ms); 183 | plt.plot([d.index, d.end], [d.start_price, d.end_price], dc, lw=lw, marker='.', markersize=ms); 184 | 185 | from matplotlib.dates import num2date 186 | import datetime 187 | ax = plt.gca() 188 | ax.set_xticklabels([datetime.date.strftime(num2date(x), fmt) for x in ax.get_xticks()]) 189 | 190 | setup_mpl_theme('dark') -------------------------------------------------------------------------------- /tools/loaders/binance.py: -------------------------------------------------------------------------------- 1 | from tools.utils.utils import mstruct, green, red, yellow 2 | import numpy as np 3 | import pandas as pd 4 | 5 | from binance.client import Client 6 | from dateutil import parser 7 | # from ira.utils.MongoController import MongoController 8 | from tqdm.notebook import tqdm 9 | import pytz, time, datetime 10 | import sqlite3 11 | from os.path import join 12 | 13 | 14 | def binance_client(api_key, secret_key): 15 | return Client(api_key=api_key, api_secret=secret_key) 16 | 17 | 18 | def __get_database_path(exchange, timeframe, path='./'): 19 | return join(path, f'{exchange}_{timeframe.upper()}.db') 20 | 21 | 22 | def update_binance_data(client, symbol, timeframe='1M', step='4W', timeout_sec=5, path='./'): 23 | # Load binance data from API endpoint and store data to mongo db 24 | with sqlite3.connect(__get_database_path('binance', timeframe, path)) as db: 25 | 26 | m_table = symbol 27 | table_exists = db.execute(f"SELECT name FROM sqlite_master WHERE type='table' AND name='{m_table}'").fetchone() is not None 28 | if table_exists: 29 | ranges = pd.read_sql_query(f"SELECT min(time) as Start, max(time) as End FROM {m_table}", db) 30 | start = ranges.End[0] 31 | else: 32 | start = '1 Jan 2017 00:00:00' 33 | 34 | tD = pd.Timedelta(timeframe) 35 | now = (pd.Timestamp(datetime.datetime.now(pytz.UTC).replace(second=0)) - tD).strftime('%d %b %Y %H:%M:%S') 36 | tlr = pd.DatetimeIndex([start]).append(pd.date_range(start, now, freq=step).append(pd.DatetimeIndex([now]))) 37 | 38 | print(f' >> Loading {green(symbol)} {yellow(timeframe)} for [{red(start)} -> {red(now)}]') 39 | df = pd.DataFrame() 40 | s = tlr[0] 41 | for e in tqdm(tlr[1:]): 42 | if s + tD < e: 43 | _start, _stop = (s + tD).strftime('%d %b %Y %H:%M:%S'), e.strftime('%d %b %Y %H:%M:%S') 44 | nerr = 0 45 | while nerr < 3: 46 | try: 47 | chunk = client.get_historical_klines(symbol, timeframe.lower(), _start, _stop) 48 | nerr = 100 49 | except e as Exception: 50 | nerr +=1 51 | print(red(str(e))) 52 | time.sleep(10) 53 | 54 | if chunk: 55 | # print(_start, _stop) 56 | data = pd.DataFrame(chunk, columns = ['timestamp', 'open', 'high', 'low', 'close', 'volume', 'close_time', 'quote_av', 'trades', 'tb_base_av', 'tb_quote_av', 'ignore' ]) 57 | data.index = pd.to_datetime(data['timestamp'].rename('time'), unit='ms') 58 | data = data.drop(columns=['timestamp', 'close_time']).astype(float).astype({ 59 | 'ignore': bool, 60 | 'trades': int, 61 | }) 62 | df = df.append(data) 63 | # store to db 64 | data.to_sql(m_table, db, if_exists='append', index_label='time') 65 | db.commit() 66 | s = e 67 | time.sleep(timeout_sec) 68 | 69 | return df 70 | 71 | 72 | def load_binance_data(symbols, timeframe, start='2017-01-01', end='2100-01-01', path='../data'): 73 | """ 74 | Loads OHLCV data for symbols (or symbol) from SQLite3 cache 75 | """ 76 | data = {} 77 | symbols = [symbols] if isinstance(symbols, str) else symbols 78 | with sqlite3.connect(__get_database_path('binance', timeframe, path)) as db: 79 | for s in symbols: 80 | ds = pd.read_sql_query(f"SELECT * FROM {s.upper()} where time >= '{start}' and time <= '{end}'", db, index_col='time') 81 | ds.index = pd.DatetimeIndex(ds.index) 82 | data[s] = ds 83 | return data -------------------------------------------------------------------------------- /tools/loaders/blockchain_data.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | 4 | import requests 5 | from io import StringIO 6 | import json 7 | from datetime import datetime as dt, timezone 8 | 9 | from blockchain import statistics 10 | 11 | 12 | def load_blockchain_series(rep, alias=None, timespan='all', api_code='c785d32f-461a-4f27-a1bf-7422544a8ca5'): 13 | print(' > Loading [%s] ...' % rep) 14 | x = statistics.get_chart(rep, time_span=timespan, api_code=api_code) 15 | sx = pd.Series({pd.to_datetime(dt.fromtimestamp(v.x, timezone.utc).date()):v.y for v in x.values}) 16 | if alias is not None: 17 | sx = sx.rename(alias) 18 | return sx 19 | 20 | 21 | def load_blockchain_series_old(rep, alias=None, timespan='all', api_code='c785d32f-461a-4f27-a1bf-7422544a8ca5'): 22 | url = 'https://api.blockchain.info/charts/%s?timespan=%s&format=csv' % (rep, timespan) 23 | if api_code is not None: 24 | url = url + '&api_code=' + api_code 25 | print('Loading [%s] ...' % rep) 26 | r = requests.get(url) 27 | if r.status_code == 200: 28 | c_name = rep if alias is None else alias 29 | data = pd.read_csv(StringIO(r.text), names=['time', c_name], header=None, parse_dates=True, index_col='time') 30 | return data 31 | else: 32 | raise ValueError("Can't load data: %s" % r) 33 | 34 | 35 | def load_coindesk_ohlc(): 36 | n_date = dt.now().date().strftime('%Y-%m-%d') 37 | r = requests.get('https://api.coindesk.com/v1/bpi/historical/ohlcv.json?start=2012-08-01&end=%s' % n_date) 38 | data = json.loads(r.text)['bpi'] 39 | d0 = pd.DataFrame.from_dict(data, orient='index') 40 | d0.index = pd.DatetimeIndex(d0.index).rename('time') 41 | return d0 42 | 43 | 44 | def load_blockchain_data(timespan='all'): 45 | return pd.concat([load_blockchain_series(r, a, timespan) for r,a in 46 | [('market-price', 'close'), 47 | ('trade-volume', 'volume'), 48 | ('n-transactions', 'tr_per_day'), 49 | ('n-unique-addresses', 'uniq_addr'), 50 | ('output-volume', 'outvolume'), 51 | ('estimated-transaction-volume-usd', 'e_tr_vol_usd'), 52 | ('estimated-transaction-volume', 'e_tr_vol'), 53 | ('miners-revenue', 'revenue'), 54 | ('utxo-count', 'utxo_count'), 55 | ]], axis=1) 56 | 57 | 58 | def load_bitcoinity_series(rep, exchange=None, alias=None): 59 | url = "https://data.bitcoinity.org/export_data.csv?data_type=%s&r=day&t=l×pan=all" % rep 60 | print('Loading [%s] ...' % rep) 61 | 62 | r = requests.get(url) 63 | if r.status_code == 200: 64 | data = pd.read_csv(StringIO(r.text), header=0, parse_dates=True, index_col='Time') 65 | data.index = data.index.rename('time') 66 | 67 | if exchange is not None and exchange in data.columns: 68 | if alias is None: 69 | alias = exchange 70 | 71 | data = data[exchange].rename(alias) 72 | else: 73 | alias = rep if alias is None else alias 74 | if data.shape[1]==1: 75 | data = data.rename(columns={data.columns[0]:alias}) 76 | return data 77 | else: 78 | raise ValueError("Can't load data: %s" % rep) 79 | -------------------------------------------------------------------------------- /tools/loaders/google_trends.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | 4 | from datetime import datetime, timedelta 5 | from pytrends.request import TrendReq 6 | from typing import List, Union 7 | 8 | 9 | def load_google_trends(kw_list: Union[List, str], start_date: str, maxstep=269, overlap=40): 10 | if isinstance(kw_list, str): 11 | kw_list = [kw_list] 12 | 13 | step = maxstep - overlap + 1 14 | start_date = pd.to_datetime(start_date).date() 15 | 16 | pytrend = TrendReq() 17 | 18 | # Run the first time (if we want to start from today, otherwise we need to ask for an end_date as well 19 | today = datetime.today().date() 20 | old_date = today 21 | 22 | # Go back in time 23 | new_date = today - timedelta(days=step) 24 | 25 | # Create new timeframe for which we download data 26 | timeframe = new_date.strftime('%Y-%m-%d') + ' ' + old_date.strftime('%Y-%m-%d') 27 | pytrend.build_payload(kw_list=kw_list, timeframe = timeframe) 28 | interest_over_time_df = pytrend.interest_over_time() 29 | 30 | ## RUN ITERATIONS 31 | while new_date > start_date: 32 | 33 | ### Save the new date from the previous iteration. 34 | # Overlap == 1 would mean that we start where we 35 | # stopped on the iteration before, which gives us 36 | # indeed overlap == 1. 37 | old_date = new_date + timedelta(days=overlap-1) 38 | 39 | ### Update the new date to take a step into the past 40 | # Since the timeframe that we can apply for daily data 41 | # is limited, we use step = maxstep - overlap instead of 42 | # maxstep. 43 | new_date = new_date - timedelta(days=step) 44 | 45 | # If we went past our start_date, use it instead 46 | if new_date < start_date: 47 | new_date = start_date 48 | 49 | # New timeframe 50 | timeframe = new_date.strftime('%Y-%m-%d') + ' ' + old_date.strftime('%Y-%m-%d') 51 | print('Loading %s ... ' % timeframe, end='') 52 | 53 | # Download data 54 | pytrend.build_payload(kw_list=kw_list, timeframe=timeframe) 55 | print('[OK]') 56 | 57 | temp_df = pytrend.interest_over_time() 58 | if temp_df.empty: 59 | raise ValueError('Google sent back an empty dataframe. Possibly there were no searches at all during the this period! Set start_date to a later date.') 60 | 61 | # Renormalize the dataset and drop last line 62 | for kw in kw_list: 63 | beg = new_date 64 | end = old_date - timedelta(days=1) 65 | 66 | # Since we might encounter zeros, we loop over the 67 | # overlap until we find a non-zero element 68 | for t in range(1, overlap + 1): 69 | #print('t = ',t) 70 | #print(temp_df[kw].iloc[-t]) 71 | if temp_df[kw].iloc[-t] != 0: 72 | scaling = interest_over_time_df[kw].iloc[t-1] / temp_df[kw].iloc[-t] 73 | #print('Found non-zero overlap!') 74 | break 75 | elif t == overlap: 76 | print('Did not find non-zero overlap, set scaling to zero! Increase Overlap!') 77 | scaling = 0 78 | 79 | # Apply scaling 80 | temp_df.loc[beg:end,kw] = temp_df.loc[beg : end, kw] * scaling 81 | 82 | interest_over_time_df = pd.concat([temp_df[:-overlap], interest_over_time_df]) 83 | 84 | return interest_over_time_df.drop(columns=['isPartial']) -------------------------------------------------------------------------------- /tools/loaders/yahoo_finance.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | from datetime import datetime 4 | from tqdm.notebook import tqdm 5 | import requests 6 | 7 | from tools.utils.utils import mstruct, red, green, yellow, blue, magenta, cyan, white 8 | 9 | 10 | def load_yahoo_daily_data(symbols, start, end=None, use_adj=True): 11 | """ 12 | Loads Yahoo OHLC daily data for requested symbols and returns result as dict 13 | """ 14 | symbols = [symbols] if not isinstance(symbols, (list, tuple, np.ndarray)) else symbols 15 | res_data = {} 16 | for s in tqdm(symbols): 17 | d = load_yahoo_ohlc_data(s, start, end, use_adj=use_adj, timeframe='1d') 18 | if d is not None: 19 | res_data[s] = d 20 | print(f'\n> Loaded {len(res_data)} symbols') 21 | return res_data 22 | 23 | 24 | def load_yahoo_ohlc_data(symbol, start, end=None, use_field=None, use_adj=True, drop_time=True, timeframe='1d'): 25 | """ 26 | Loading daily data from Yahoo 27 | """ 28 | if end is None: 29 | end = datetime.now().strftime('%Y-%m-%d') 30 | 31 | replacements = {'BRKB': 'BRK-B'} 32 | url = "https://query1.finance.yahoo.com/v8/finance/chart/{}".format(replacements.get(symbol, symbol)) 33 | # print('Loading %s ...' % symbol, end='') 34 | 35 | r = requests.get(url, params={ 36 | 'interval': timeframe, 37 | 'period1': int(pd.to_datetime(start).timestamp()), 38 | 'period2': int(pd.to_datetime(end).timestamp()), 39 | }) 40 | data = r.json() 41 | try: 42 | meta = data['chart']['result'][0]['meta'] 43 | ds = data['chart']['result'][0] 44 | qts = ds['indicators']['quote'][0] 45 | a_close = ds['indicators']['adjclose'][0] if 'adjclose' in ds['indicators'] else {} 46 | 47 | df = pd.DataFrame.from_dict({**qts, **a_close}) 48 | df.index = pd.to_datetime(ds['timestamp'], unit="s", yearfirst=True) 49 | df.sort_index(inplace=True) 50 | try: 51 | df.index = df.index + pd.Timedelta('%dS' % meta['gmtoffset']) 52 | except Exception as e: 53 | print(">>> Error: %s !!!" % e) 54 | 55 | if drop_time: 56 | df.index = pd.DatetimeIndex(df.index.date) 57 | 58 | # use only adjusted closes 59 | if use_adj: 60 | af = df['adjclose'] / df['close'] 61 | df['open'] = df['open'] * af 62 | df['high'] = df['high'] * af 63 | df['low'] = df['low'] * af 64 | df['close'] = df['adjclose'] 65 | 66 | # print('[loaded %d bars]' % len(df)) 67 | return df if use_field is None else df[use_field] 68 | except Exception as e: 69 | print(red(symbol), end=' ') 70 | return None 71 | -------------------------------------------------------------------------------- /tools/utils/stat_helpers.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | from tools.charting.plot_helpers import sbp 5 | 6 | import scipy.stats as stats 7 | import seaborn as sns 8 | 9 | 10 | def cmp_to_norm(xs, xranges=None): 11 | """ 12 | Compare distribution from xs against normal using estimated mean and std 13 | """ 14 | _m, _s = np.mean(xs), np.std(xs) 15 | fit = stats.norm.pdf(sorted(xs), _m, _s) #this is a fitting indeed 16 | 17 | sbp(12,1) 18 | plt.plot(sorted(xs), fit, 'r--', lw=2, label='N(%.2f, %.2f)' % (_m, _s)) 19 | plt.legend(loc='upper right') 20 | 21 | sns.kdeplot(xs, color='g', label='Data', shade=True) 22 | if xranges is not None and len(xranges) > 1: 23 | plt.xlim(xranges) 24 | plt.legend(loc='upper right') 25 | 26 | sbp(12,2) 27 | stats.probplot(xs, dist="norm", sparams=(_m, _s), plot=plt) 28 | -------------------------------------------------------------------------------- /tools/utils/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import traceback 3 | import pandas as pd 4 | from collections import OrderedDict, namedtuple 5 | 6 | 7 | def __wrap_with_color(code): 8 | def inner(text, bold=False): 9 | c = code 10 | if bold: 11 | c = "1;%s" % c 12 | return "\033[%sm%s\033[0m" % (c, text) 13 | return inner 14 | 15 | 16 | red, green, yellow, blue, magenta, cyan, white = ( 17 | __wrap_with_color('31'), 18 | __wrap_with_color('32'), 19 | __wrap_with_color('33'), 20 | __wrap_with_color('34'), 21 | __wrap_with_color('35'), 22 | __wrap_with_color('36'), 23 | __wrap_with_color('37'), 24 | ) 25 | 26 | 27 | def is_localhost(host): 28 | return host.lower() == 'localhost' or host == '127.0.0.1' 29 | 30 | 31 | class mstruct: 32 | """ 33 | Dynamic structure (similar to matlab's struct it allows to add new properties dynamically) 34 | 35 | >>> a = mstruct(x=1, y=2) 36 | >>> a.z = 'Hello' 37 | >>> print(a) 38 | 39 | mstruct(x=1, y=2, z='Hello') 40 | 41 | >>> mstruct(a=234, b=mstruct(c=222)).to_dict() 42 | 43 | {'a': 234, 'b': {'c': 222}} 44 | 45 | """ 46 | 47 | def __init__(self, **kwargs): 48 | _odw = OrderedDict(**kwargs) 49 | self.__initialize(_odw.keys(), _odw.values()) 50 | 51 | def __initialize(self, fields, values): 52 | self._fields = list(fields) 53 | self._meta = namedtuple('mstruct', ' '.join(fields)) 54 | self._inst = self._meta(*values) 55 | 56 | def __getattr__(self, k): 57 | return getattr(self._inst, k) 58 | 59 | def __dir__(self): 60 | return self._fields 61 | 62 | def __repr__(self): 63 | return self._inst.__repr__() 64 | 65 | def __setattr__(self, k, v): 66 | if k not in ['_inst', '_meta', '_fields']: 67 | new_vals = {**self._inst._asdict(), **{k: v}} 68 | self.__initialize(new_vals.keys(), new_vals.values()) 69 | else: 70 | super().__setattr__(k, v) 71 | 72 | def __getstate__(self): 73 | return self._inst._asdict() 74 | 75 | def __setstate__(self, state): 76 | self.__init__(**state) 77 | 78 | def __ms2d(self, m): 79 | r = {} 80 | for f in m._fields: 81 | v = m.__getattr__(f) 82 | r[f] = self.__ms2d(v) if isinstance(v, mstruct) else v 83 | return r 84 | 85 | def to_dict(self): 86 | """ 87 | Return this structure as dictionary 88 | """ 89 | return self.__ms2d(self) 90 | 91 | def copy(self): 92 | """ 93 | Returns copy of this structure 94 | """ 95 | return dict2struct(self.to_dict()) 96 | 97 | 98 | def dict2struct(d: dict): 99 | """ 100 | Convert dictionary to structure 101 | >>> s = dict2struct({'f_1_0': 1, 'z': {'x': 1, 'y': 2}}) 102 | >>> print(s.z.x) 103 | 1 104 | 105 | """ 106 | m = mstruct() 107 | for k, v in d.items(): 108 | if isinstance(v, dict): 109 | v = dict2struct(v) 110 | m.__setattr__(k, v) 111 | return m 112 | --------------------------------------------------------------------------------