├── .babelrc ├── .gitignore ├── Binance_bot.sublime-project ├── Binance_bot.sublime-workspace ├── LICENSE ├── Procfile ├── README.md ├── alert.mp3 ├── ichi_cloud.js ├── index.js ├── indicators ├── ichimoku.js ├── ovb.js ├── resistence.js ├── rsi.js └── support.js ├── package-lock.json ├── package.json ├── pump_detector.js └── x.low)y /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node build artifacts 2 | node_modules 3 | npm-debug.log 4 | 5 | # Local development 6 | *.env 7 | *.dev 8 | 9 | .DS_Store 10 | *.lock -------------------------------------------------------------------------------- /Binance_bot.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "path": "." 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /Binance_bot.sublime-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "auto_complete": 3 | { 4 | "selected_items": 5 | [ 6 | [ 7 | "pa", 8 | "pair_data" 9 | ], 10 | [ 11 | "trac", 12 | "tracked_pairs" 13 | ], 14 | [ 15 | "long", 16 | "longResistences" 17 | ], 18 | [ 19 | "ki", 20 | "kijunSen" 21 | ], 22 | [ 23 | "track", 24 | "trackHourlyPrice" 25 | ], 26 | [ 27 | "ex", 28 | "extraMessage" 29 | ], 30 | [ 31 | "resis", 32 | "resistences" 33 | ], 34 | [ 35 | "tra", 36 | "tracked_pairs" 37 | ], 38 | [ 39 | "re", 40 | "require" 41 | ], 42 | [ 43 | "ee", 44 | "eliminateSupports" 45 | ], 46 | [ 47 | "supp", 48 | "supports" 49 | ], 50 | [ 51 | "su", 52 | "support" 53 | ], 54 | [ 55 | "ele", 56 | "eliminateSupports" 57 | ], 58 | [ 59 | "shor", 60 | "shortSupports" 61 | ], 62 | [ 63 | "sho", 64 | "shortSupports" 65 | ], 66 | [ 67 | "get", 68 | "getLongSupports" 69 | ], 70 | [ 71 | "getS", 72 | "getShortSupports" 73 | ], 74 | [ 75 | "tenkan", 76 | "tenkanSen" 77 | ], 78 | [ 79 | "des", 80 | "decreaseDays" 81 | ], 82 | [ 83 | "ins", 84 | "increaseDays" 85 | ], 86 | [ 87 | "le", 88 | "length" 89 | ], 90 | [ 91 | "calle", 92 | "calledOnce" 93 | ], 94 | [ 95 | "padd", 96 | "padding-left" 97 | ], 98 | [ 99 | "margi", 100 | "margin-left" 101 | ], 102 | [ 103 | "mar", 104 | "margin-bottom" 105 | ], 106 | [ 107 | "font", 108 | "font-weight" 109 | ], 110 | [ 111 | "is", 112 | "isSeeingFeaturedModels" 113 | ], 114 | [ 115 | "show", 116 | "showedVehicles" 117 | ], 118 | [ 119 | "trad", 120 | "tradeUpSponsoredVehicleList" 121 | ], 122 | [ 123 | "ad", 124 | "adDetails" 125 | ], 126 | [ 127 | "getE", 128 | "getElementById" 129 | ], 130 | [ 131 | "tr", 132 | "trigger" 133 | ], 134 | [ 135 | "call", 136 | "calledOnce" 137 | ], 138 | [ 139 | "simila", 140 | "similarVehicleSponsorship" 141 | ], 142 | [ 143 | "que", 144 | "querySelectorAll" 145 | ], 146 | [ 147 | "vehic", 148 | "vehicle" 149 | ], 150 | [ 151 | "remo", 152 | "removeChild" 153 | ], 154 | [ 155 | "ve", 156 | "vehicle" 157 | ], 158 | [ 159 | "vehi", 160 | "vehicles" 161 | ], 162 | [ 163 | "fun", 164 | "function" 165 | ], 166 | [ 167 | "se", 168 | "setData" 169 | ], 170 | [ 171 | "getEl", 172 | "getElementsByClassName" 173 | ], 174 | [ 175 | "add", 176 | "addToTrace" 177 | ], 178 | [ 179 | "new", 180 | "newRelic" 181 | ], 182 | [ 183 | "send", 184 | "sendToNewRelic" 185 | ], 186 | [ 187 | "autoPlay", 188 | "autoplayMuted" 189 | ], 190 | [ 191 | "cta-", 192 | "oem-cta-height" 193 | ], 194 | [ 195 | "snow", 196 | "snowfallWindow" 197 | ], 198 | [ 199 | "fon", 200 | "font-family" 201 | ], 202 | [ 203 | "marg", 204 | "margin-left" 205 | ], 206 | [ 207 | "wid", 208 | "width" 209 | ], 210 | [ 211 | "bo", 212 | "bottom" 213 | ], 214 | [ 215 | "margin", 216 | "margin-right" 217 | ], 218 | [ 219 | "w", 220 | "width" 221 | ], 222 | [ 223 | "text", 224 | "text-align" 225 | ], 226 | [ 227 | "inlin", 228 | "inline-block" 229 | ], 230 | [ 231 | "color", 232 | "color-white" 233 | ] 234 | ] 235 | }, 236 | "buffers": 237 | [ 238 | { 239 | "file": "pump_detector.js", 240 | "settings": 241 | { 242 | "buffer_size": 7938, 243 | "encoding": "UTF-8", 244 | "line_ending": "Windows" 245 | } 246 | }, 247 | { 248 | "contents": "\nD:\\Projects\\stuffs\\autotrader-bot>node pump_detector.js\nnode-telegram-bot-api deprecated Automatic enabling of cancellation of promises is deprecated.\nIn the future, you will have to enable it yourself.\nSee https://github.com/yagop/node-telegram-bot-api/issues/319. module.js:652:30\n------------ NBT starting -------------\nNBT api accessable on port 80\n------------------------------\n start get_BTC_price\n------------------------------\n------------------------------\nBTC price: $6,482\n------------------------------\n------------------------------\n get_BTC_pairs start\n------------------------------\n357 total pairs\n------------------------------\nTotal BTC pairs: 1\n------------------------------\n------------------------------\n run detector\n------------------------------\nETHBTC got data\n[ { symbol: 'ETHBTC', data: [ [Object], [Object], [Object] ] } ]\npair ETHBTC\ncurrentPair { symbol: 'ETHBTC',\n data:\n [ { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697764,\n price: '0.07701500',\n volume: '2127.69400000',\n buyVolume: '1602.12500000',\n usdvolume: 1062199.8730307678 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07694500',\n volume: '2568.63000000',\n buyVolume: '1809.32200000',\n usdvolume: 1281161.0037534165 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07689700',\n volume: '1172.49200000',\n buyVolume: '902.75900000',\n usdvolume: 584441.4931064595 } ] }\nticks { t: 1529247600000,\n T: 1529249399999,\n s: 'ETHBTC',\n i: '30m',\n f: 69051724,\n L: 69053174,\n o: '0.07694500',\n c: '0.07689700',\n h: '0.07697900',\n l: '0.07685500',\n v: '1176.59300000',\n n: 1451,\n x: false,\n q: '90.51602562',\n V: '902.75900000',\n Q: '69.45598435',\n B: '0' }\naverageVolumne NaN\npair ETHBTC\ncurrentPair { symbol: 'ETHBTC',\n data:\n [ { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697764,\n price: '0.07701500',\n volume: '2127.69400000',\n buyVolume: '1602.12500000',\n usdvolume: 1062199.8730307678 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07694500',\n volume: '2568.63000000',\n buyVolume: '1809.32200000',\n usdvolume: 1281161.0037534165 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07689700',\n volume: '1172.49200000',\n buyVolume: '902.75900000',\n usdvolume: 584441.4931064595 } ] }\nticks { t: 1529247600000,\n T: 1529249399999,\n s: 'ETHBTC',\n i: '30m',\n f: 69051724,\n L: 69053175,\n o: '0.07694500',\n c: '0.07689700',\n h: '0.07697900',\n l: '0.07685500',\n v: '1176.59600000',\n n: 1452,\n x: false,\n q: '90.51625631',\n V: '902.75900000',\n Q: '69.45598435',\n B: '0' }\naverageVolumne NaN\npair ETHBTC\ncurrentPair { symbol: 'ETHBTC',\n data:\n [ { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697764,\n price: '0.07701500',\n volume: '2127.69400000',\n buyVolume: '1602.12500000',\n usdvolume: 1062199.8730307678 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07694500',\n volume: '2568.63000000',\n buyVolume: '1809.32200000',\n usdvolume: 1281161.0037534165 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07689700',\n volume: '1172.49200000',\n buyVolume: '902.75900000',\n usdvolume: 584441.4931064595 } ] }\nticks { t: 1529247600000,\n T: 1529249399999,\n s: 'ETHBTC',\n i: '30m',\n f: 69051724,\n L: 69053180,\n o: '0.07694500',\n c: '0.07689700',\n h: '0.07697900',\n l: '0.07685500',\n v: '1176.63300000',\n n: 1457,\n x: false,\n q: '90.51910148',\n V: '902.75900000',\n Q: '69.45598435',\n B: '0' }\naverageVolumne NaN\npair ETHBTC\ncurrentPair { symbol: 'ETHBTC',\n data:\n [ { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697764,\n price: '0.07701500',\n volume: '2127.69400000',\n buyVolume: '1602.12500000',\n usdvolume: 1062199.8730307678 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07694500',\n volume: '2568.63000000',\n buyVolume: '1809.32200000',\n usdvolume: 1281161.0037534165 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07689700',\n volume: '1172.49200000',\n buyVolume: '902.75900000',\n usdvolume: 584441.4931064595 } ] }\nticks { t: 1529247600000,\n T: 1529249399999,\n s: 'ETHBTC',\n i: '30m',\n f: 69051724,\n L: 69053183,\n o: '0.07694500',\n c: '0.07689700',\n h: '0.07697900',\n l: '0.07685500',\n v: '1176.70600000',\n n: 1460,\n x: false,\n q: '90.52471495',\n V: '902.75900000',\n Q: '69.45598435',\n B: '0' }\naverageVolumne NaN\npair ETHBTC\ncurrentPair { symbol: 'ETHBTC',\n data:\n [ { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697764,\n price: '0.07701500',\n volume: '2127.69400000',\n buyVolume: '1602.12500000',\n usdvolume: 1062199.8730307678 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07694500',\n volume: '2568.63000000',\n buyVolume: '1809.32200000',\n usdvolume: 1281161.0037534165 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07689700',\n volume: '1172.49200000',\n buyVolume: '902.75900000',\n usdvolume: 584441.4931064595 } ] }\nticks { t: 1529247600000,\n T: 1529249399999,\n s: 'ETHBTC',\n i: '30m',\n f: 69051724,\n L: 69053189,\n o: '0.07694500',\n c: '0.07689700',\n h: '0.07697900',\n l: '0.07685500',\n v: '1176.75300000',\n n: 1466,\n x: false,\n q: '90.52832908',\n V: '902.75900000',\n Q: '69.45598435',\n B: '0' }\naverageVolumne NaN\npair ETHBTC\ncurrentPair { symbol: 'ETHBTC',\n data:\n [ { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697764,\n price: '0.07701500',\n volume: '2127.69400000',\n buyVolume: '1602.12500000',\n usdvolume: 1062199.8730307678 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07694500',\n volume: '2568.63000000',\n buyVolume: '1809.32200000',\n usdvolume: 1281161.0037534165 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07689700',\n volume: '1172.49200000',\n buyVolume: '902.75900000',\n usdvolume: 584441.4931064595 } ] }\nticks { t: 1529247600000,\n T: 1529249399999,\n s: 'ETHBTC',\n i: '30m',\n f: 69051724,\n L: 69053193,\n o: '0.07694500',\n c: '0.07689700',\n h: '0.07697900',\n l: '0.07685500',\n v: '1176.79700000',\n n: 1470,\n x: false,\n q: '90.53171253',\n V: '902.75900000',\n Q: '69.45598435',\n B: '0' }\naverageVolumne NaN\npair ETHBTC\ncurrentPair { symbol: 'ETHBTC',\n data:\n [ { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697764,\n price: '0.07701500',\n volume: '2127.69400000',\n buyVolume: '1602.12500000',\n usdvolume: 1062199.8730307678 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07694500',\n volume: '2568.63000000',\n buyVolume: '1809.32200000',\n usdvolume: 1281161.0037534165 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07689700',\n volume: '1172.49200000',\n buyVolume: '902.75900000',\n usdvolume: 584441.4931064595 } ] }\nticks { t: 1529247600000,\n T: 1529249399999,\n s: 'ETHBTC',\n i: '30m',\n f: 69051724,\n L: 69053195,\n o: '0.07694500',\n c: '0.07689700',\n h: '0.07697900',\n l: '0.07685500',\n v: '1178.02900000',\n n: 1472,\n x: false,\n q: '90.62644963',\n V: '902.75900000',\n Q: '69.45598435',\n B: '0' }\naverageVolumne NaN\npair ETHBTC\ncurrentPair { symbol: 'ETHBTC',\n data:\n [ { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697764,\n price: '0.07701500',\n volume: '2127.69400000',\n buyVolume: '1602.12500000',\n usdvolume: 1062199.8730307678 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07694500',\n volume: '2568.63000000',\n buyVolume: '1809.32200000',\n usdvolume: 1281161.0037534165 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07689700',\n volume: '1172.49200000',\n buyVolume: '902.75900000',\n usdvolume: 584441.4931064595 } ] }\nticks { t: 1529247600000,\n T: 1529249399999,\n s: 'ETHBTC',\n i: '30m',\n f: 69051724,\n L: 69053199,\n o: '0.07694500',\n c: '0.07687900',\n h: '0.07697900',\n l: '0.07685500',\n v: '1178.84400000',\n n: 1476,\n x: false,\n q: '90.68911729',\n V: '902.75900000',\n Q: '69.45598435',\n B: '0' }\naverageVolumne NaN\npair ETHBTC\ncurrentPair { symbol: 'ETHBTC',\n data:\n [ { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697764,\n price: '0.07701500',\n volume: '2127.69400000',\n buyVolume: '1602.12500000',\n usdvolume: 1062199.8730307678 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07694500',\n volume: '2568.63000000',\n buyVolume: '1809.32200000',\n usdvolume: 1281161.0037534165 },\n { date: 'June 17th 2018, 10:18:17 pm',\n timestamp: 1529248697765,\n price: '0.07689700',\n volume: '1172.49200000',\n buyVolume: '902.75900000',\n usdvolume: 584441.4931064595 } ] }\nticks { t: 1529247600000,\n T: 1529249399999,\n s: 'ETHBTC',\n i: '30m',\n f: 69051724,\n L: 69053200,\n o: '0.07694500',\n c: '0.07687900',\n h: '0.07697900',\n l: '0.07685500',\n v: '1178.84600000',\n n: 1477,\n x: false,\n q: '90.68927104',\n V: '902.75900000',\n Q: '69.45598435',\n B: '0' }\naverageVolumne NaN\n", 249 | "settings": 250 | { 251 | "buffer_size": 9890, 252 | "line_ending": "Windows" 253 | } 254 | }, 255 | { 256 | "contents": "\nD:\\Projects\\stuffs\\autotrader-bot>node pump_detector.js\nnode-telegram-bot-api deprecated Automatic enabling of cancellation of promises is deprecated.\nIn the future, you will have to enable it yourself.\nSee https://github.com/yagop/node-telegram-bot-api/issues/319. module.js:652:30\n------------ NBT starting -------------\nNBT api accessable on port 80\n------------------------------\n start get_BTC_price\n------------------------------\n------------------------------\nBTC price: $6,484\n------------------------------\n------------------------------\n get_BTC_pairs start\n------------------------------\n357 total pairs\n------------------------------\nTotal BTC pairs: 1\n------------------------------\n------------------------------\n run detector\n------------------------------\nETHBTC got data\n1529244000000\n1529245800000\n1529247600000\n[ { date: 'June 17th 2018, 10:28:04 pm',\n timestamp: 1529249284361,\n price: '0.07701500',\n volume: '2127.69400000',\n buyVolume: '1602.12500000',\n usdvolume: 1062535.7949552585 },\n { date: 'June 17th 2018, 10:28:04 pm',\n timestamp: 1529249284362,\n price: '0.07694500',\n volume: '2568.63000000',\n buyVolume: '1809.32200000',\n usdvolume: 1281566.172385884 },\n { date: 'June 17th 2018, 10:28:04 pm',\n timestamp: 1529249284362,\n price: '0.07702200',\n volume: '1465.75600000',\n buyVolume: '1114.24100000',\n usdvolume: 732041.2486799597 } ]\n", 257 | "settings": 258 | { 259 | "buffer_size": 1412, 260 | "line_ending": "Windows" 261 | } 262 | }, 263 | { 264 | "file": "ichi_cloud.js", 265 | "settings": 266 | { 267 | "buffer_size": 16557, 268 | "line_ending": "Windows" 269 | } 270 | }, 271 | { 272 | "file": "indicators/ovb.js", 273 | "settings": 274 | { 275 | "buffer_size": 903, 276 | "encoding": "UTF-8", 277 | "line_ending": "Windows" 278 | } 279 | }, 280 | { 281 | "file": "indicators/resistence.js", 282 | "settings": 283 | { 284 | "buffer_size": 1856, 285 | "encoding": "UTF-8", 286 | "line_ending": "Windows" 287 | } 288 | }, 289 | { 290 | "file": "indicators/rsi.js", 291 | "settings": 292 | { 293 | "buffer_size": 2683, 294 | "line_ending": "Windows" 295 | } 296 | }, 297 | { 298 | "contents": "if (brightcove == undefined) { var brightcove = {};\n brightcove.getExperience = function() { alert(\"Please import APIModules_all.js in order to use the API.\"); }; }\nif (brightcove.experiences == undefined) {\n brightcove.servicesURL = 'http://c.brightcove.com/services';\n brightcove.cdnURL = 'http://admin.brightcove.com';\n brightcove.secureCDNURL = 'https://sadmin.brightcove.com';\n brightcove.secureServicesURL = 'https://secure.brightcove.com/services';\n brightcove.USservicesURL = 'http://c.brightcove.com/services';\n brightcove.UScdnURL = 'http://admin.brightcove.com';\n brightcove.USsecureCDNURL = 'https://sadmin.brightcove.com';\n brightcove.USsecureServicesURL = 'https://secure.brightcove.com/services';\n brightcove.pubHost = 'c.$pubcode$.$zoneprefix$$zone$';\n brightcove.pubSecureHost = 'secure.$pubcode$.$zoneprefix$$zone$';\n brightcove.pubSubdomain = 'ariessaucetown.local';\n brightcove.experiences = {};\n brightcove.experienceObjects = {};\n brightcove.renderExperienceInProcess = false;\n brightcove.createExperiencesQueue = [];\n brightcove.renderExperienceQueue = [];\n brightcove.timeouts = {};\n brightcove.flashTimeoutInterval = 10000;\n brightcove.htmlTimeoutInterval = 10000;\n brightcove.experienceNum = 0;\n brightcove.majorVersion = 9;\n brightcove.majorRevision = 0;\n brightcove.minorRevision = 28;\n brightcove.performCdnUrl = { 'development': '//players.brightcove.net/', 'qa': '//players.qa.brightcove.net/', 'staging': '//players.staging.brightcove.net/', 'production': '//players.brightcove.net/' };\n brightcove.metricsBaseUrl = { 'development': '//data.aws-qa.rnatest.brightcove.com', 'qa': '//data.aws-qa.rnatest.brightcove.com', 'staging': '//data.aws-qa.rnatest.brightcove.com', 'production': '//metrics.brightcove.com/tracker' };\n brightcove.analyticsErrors = { 'BAD_PUBLISHER_ID': -100, 'UNEXPECTED_MAPPING_RESPONSE': -101, 'MAPPINGS_CALL_FAILURE': -102 };\n brightcove.servlet = { AS3: \"federated_f9\", HTML: \"htmlFederated\" };\n brightcove.mappingFileData = {};\n brightcove.isLinkDotBrightcoveURL = window.location.hostname.indexOf('link.brightcove.co.jp') >= 0;\n brightcove.playerType = { FLASH: \"flash\", HTML: \"html\", FLASH_IFRAME: \"flashIFrame\", INSTALLER: \"installer\", NO_SUPPORT: \"nosupport\" };\n brightcove.errorCodes = { UNKNOWN: 0, DOMAIN_RESTRICTED: 1, GEO_RESTRICTED: 2, INVALID_ID: 3, NO_CONTENT: 4, UNAVAILABLE_CONTENT: 5, UPGRADE_REQUIRED_FOR_VIDEO: 6, UPGRADE_REQUIRED_FOR_PLAYER: 7, SERVICE_UNAVAILABLE: 8 };\n brightcove.defaultParam = {};\n brightcove.defaultParam.width = '100%';\n brightcove.defaultParam.height = '100%';\n brightcove.defaultFlashParam = {};\n brightcove.defaultFlashParam.allowScriptAccess = 'always';\n brightcove.defaultFlashParam.allowFullScreen = 'true';\n brightcove.defaultFlashParam.seamlessTabbing = false;\n brightcove.defaultFlashParam.swliveconnect = true;\n brightcove.defaultFlashParam.wmode = 'window';\n brightcove.defaultFlashParam.quality = 'high';\n brightcove.defaultFlashParam.bgcolor = '#999999';\n brightcove.hasActiveX = brightcove.isIE = (window.ActiveXObject != undefined);\n brightcove.userAgent = navigator.userAgent;\n brightcove._queuedAPICalls = [];\n var brightcoveJS = brightcove;\n brightcove.createExperiences = function(pEvent, pElementID) {\n var experiences = [];\n var params;\n var experience;\n var flashSupport = brightcove.checkFlashSupport();\n var htmlSupport = brightcove.checkHtmlSupport();\n if (brightcove.renderExperienceInProcess) {\n function createExperiencesWrapper(pEvent, pElementID) { return function() { brightcove.createExperiences(pEvent, pElementID); } }\n brightcove.createExperiencesQueue.push(createExperiencesWrapper(pEvent, pElementID));\n return;\n }\n if (pElementID != null) { experiences.push(document.getElementById(pElementID)); } else { experiences = brightcove.collectExperiences(); }\n if (brightcove.hasActiveX) { params = document.getElementsByTagName('param'); }\n var urlParams = brightcove.cacheUrlParams();\n var numExperiences = experiences.length;\n for (var i = 0; i < numExperiences; i++) {\n experience = experiences[i];\n experience = brightcove.copyDefaultParams(experience);\n experience = brightcove.copySnippetParams(experience, params);\n experience = brightcove.copyUrlParams(experience, urlParams, numExperiences);\n var playerType = brightcove.determinePlayerType(experience, flashSupport, htmlSupport);\n var secureConnections = false;\n if (playerType == brightcove.playerType.HTML) { secureConnections = experience.params.secureHTMLConnections == \"true\"; } else { secureConnections = experience.params.secureConnections == \"true\"; }\n if (playerType == brightcove.playerType.HTML) { delete experience.params.linkBaseURL; } else {\n if (experience.params.includeAPI && experience.params.templateReadyHandler != null) {\n experience.params.originalTemplateReadyHandler = experience.params.templateReadyHandler;\n var handlerName = \"templateReadyHandler\" + experience.id;\n brightcove[handlerName] = (function(id) {\n return function(event) {\n if (brightcove.internal != null && brightcove.internal._instances[id] != null) { brightcove._addModuleToEvent(id, event); }\n var player = brightcove.experienceObjects[id];\n brightcove.callHandlerForPlayer(player, \"originalTemplateReadyHandler\", event);\n };\n })(experience.id);\n experience.params.templateReadyHandler = 'brightcove[\"' + handlerName + '\"]';\n }\n }\n var file = brightcove.generateRequestUrl(experience, playerType, secureConnections);\n if (document.location.protocol == \"http:\") {\n var event = 'http://goku.brightcove.com/1pix.gif?';\n var gokuParams = [\"dcsuri=/viewer/player_load_req\", \"playerType=\" + playerType, \"playerURL=\" + encodeURIComponent(document.location || \"\")];\n var image = brightcove.createElement('image');\n for (var j in experience.params) { gokuParams.push([encodeURIComponent(j) + \"=\" + encodeURIComponent(experience.params[j])]); }\n event += gokuParams.join('&');\n image.src = event;\n }\n brightcove.renderExperience(experience, file, playerType, secureConnections);\n }\n };\n brightcove.collectExperiences = function() {\n var experiences = [];\n var allObjects = document.getElementsByTagName('object');\n var numObjects = allObjects.length;\n for (var i = 0; i < numObjects; i++) { if (/\\bBrightcoveExperience\\b/.test(allObjects[i].className)) { if (allObjects[i].type != 'application/x-shockwave-flash') { experiences.push(allObjects[i]); } } }\n return experiences;\n };\n brightcove.cacheUrlParams = function() {\n var urlParams = {};\n urlParams.playerKey = decodeURIComponent(brightcove.getParameter(\"bckey\"));\n urlParams.playerID = brightcove.getParameter(\"bcpid\");\n urlParams.titleID = brightcove.getParameter(\"bctid\");\n urlParams.lineupID = brightcove.getParameter(\"bclid\");\n urlParams.autoStart = brightcove.getParameter(\"autoStart\");\n urlParams.debuggerID = brightcove.getParameter(\"debuggerID\");\n urlParams.forceHTML = brightcove.getParameter(\"forceHTML\");\n urlParams.forceFlashIFrame = brightcove.getParameter(\"forceFlashIFrame\");\n urlParams.debug = brightcove.getParameter(\"debug\");\n urlParams.showNoContentMessage = brightcove.getParameter(\"showNoContentMessage\");\n urlParams.htmlDefaultBitrate = brightcove.getParameter(\"htmlDefaultBitrate\");\n urlParams.linkSrc = brightcove.getParameter(\"linkSrc\");\n urlParams.enableMapping = brightcove.getParameter(\"enableMapping\");\n if (brightcove.isLinkDotBrightcoveURL) { urlParams.height = brightcove.getParameter(\"height\");\n urlParams.width = brightcove.getParameter(\"width\"); }\n return urlParams;\n };\n brightcove.copyDefaultParams = function(experience) {\n if (!experience.params) experience.params = {};\n if (!experience.flashParams) experience.flashParams = {};\n for (var i in brightcove.defaultParam) { experience.params[i] = brightcove.defaultParam[i]; }\n for (var j in brightcove.defaultFlashParam) { experience.flashParams[j] = brightcove.defaultFlashParam[j]; }\n if (experience.id.length > 0) { experience.params.flashID = experience.id; } else { experience.id = experience.params.flashID = 'bcExperienceObj' + (brightcove.experienceNum++); }\n experience.params.identifierClassName = 'BrightcoveExperienceID_' + Math.floor(Math.random() * 10000);\n return experience;\n };\n brightcove.copySnippetParams = function(experience, params) {\n if (!brightcove.hasActiveX) { params = experience.getElementsByTagName('param'); }\n var numParams = params.length;\n var param;\n for (var j = 0; j < numParams; j++) {\n param = params[j];\n if (brightcove.hasActiveX && param.parentNode.id != experience.id) { continue; }\n experience.params[param.name] = param.value;\n }\n if (experience.params.bgcolor != undefined) experience.flashParams.bgcolor = experience.params.bgcolor;\n if (experience.params.wmode != undefined) experience.flashParams.wmode = experience.params.wmode;\n if (experience.params.seamlessTabbing != undefined) experience.flashParams.seamlessTabbing = experience.params.seamlessTabbing;\n return experience;\n };\n brightcove.copyUrlParams = function(experience, urlParams) {\n if (experience.params.autoStart == undefined && urlParams.autoStart != undefined) { experience.params.autoStart = urlParams.autoStart; }\n if (urlParams.debuggerID != undefined) { experience.params.debuggerID = urlParams.debuggerID; }\n if (urlParams.forceHTML != undefined && urlParams.forceHTML !== '') { experience.params.forceHTML = urlParams.forceHTML; }\n if (urlParams.forceFlashIFrame != undefined && urlParams.forceFlashIFrame !== '') { experience.params.forceFlashIFrame = urlParams.forceFlashIFrame; }\n if (urlParams.enableMapping != undefined && urlParams.enableMapping !== '') { experience.params.enableMapping = urlParams.enableMapping.toLowerCase() === 'true'; }\n if (urlParams.debug != undefined && urlParams.debug !== '') { experience.params.debug = urlParams.debug; }\n if (urlParams.showNoContentMessage != undefined && urlParams.showNoContentMessage != '') { experience.params.showNoContentMessage = urlParams.showNoContentMessage; }\n if (urlParams.htmlDefaultBitrate != undefined && urlParams.htmlDefaultBitrate !== '') { experience.params.htmlDefaultBitrate = urlParams.htmlDefaultBitrate; }\n if (urlParams.linkSrc != undefined && urlParams.linkSrc != '') { experience.params.linkSrc = urlParams.linkSrc; }\n if (urlParams.height != undefined && urlParams.height != '') { experience.params.height = urlParams.height; }\n if (urlParams.width != undefined && urlParams.width != '') { experience.params.width = urlParams.width; }\n var overrideContent = (urlParams.playerID.length < 1 && urlParams.playerKey.length < 1) || (urlParams.playerID == experience.params.playerID) || (urlParams.playerKey == experience.params.playerKey);\n if (overrideContent) {\n if (urlParams.titleID.length > 0) { experience.params.videoID = urlParams.titleID;\n experience.params[\"@videoPlayer\"] = urlParams.titleID;\n experience.params.autoStart = (experience.params.autoStart != \"false\" && urlParams.autoStart != \"false\"); }\n if (urlParams.lineupID.length > 0) { experience.params.lineupID = urlParams.lineupID; }\n }\n return experience;\n };\n brightcove.determinePlayerType = function(experience, flashSupport, htmlSupport) {\n if (flashSupport == null && htmlSupport == false) { return brightcove.playerType.NO_SUPPORT; }\n if (experience.params.forceHTML) {\n if (window.console) { var message = \"The forceHTML parameter was used for the Brightcove player. This value should ONLY be used for\";\n message += \" development and testing purposes and is not supported in production environments.\";\n console.log(message); }\n return brightcove.playerType.HTML;\n }\n if (experience.params.forceFlashIFrame || (brightcove.isMetroIE() && flashSupport == null)) { return brightcove.playerType.FLASH_IFRAME; }\n if (flashSupport != null) { if (brightcove.isFlashVersionSufficient(experience, flashSupport)) { return brightcove.playerType.FLASH; } else { return brightcove.playerType.INSTALLER; } }\n if (htmlSupport) { if (brightcove.isSupportedHTMLDevice() || experience.params.htmlFallback) { return brightcove.playerType.HTML; } }\n return brightcove.playerType.NO_SUPPORT;\n };\n brightcove.isFlashVersionSufficient = function(experience, flashSupport) {\n if (flashSupport == null) return false;\n var setMajorVersion = false;\n var requestedMajorVersion;\n var requestedMajorRevision;\n var requestedMinorRevision;\n if (experience.params.majorVersion != undefined) { requestedMajorVersion = parseInt(experience.params.majorVersion, 10);\n setMajorVersion = true; } else { requestedMajorVersion = brightcove.majorVersion; }\n if (experience.params.majorRevision != undefined) { requestedMajorRevision = parseInt(experience.params.majorRevision, 10); } else { if (setMajorVersion) { requestedMajorRevision = 0; } else { requestedMajorRevision = brightcove.majorRevision; } }\n if (experience.params.minorRevision != undefined) { requestedMinorRevision = parseInt(experience.params.minorRevision, 10); } else { if (setMajorVersion) { requestedMinorRevision = 0; } else { requestedMinorRevision = brightcove.minorRevision; } }\n return (flashSupport.majorVersion > requestedMajorVersion || (flashSupport.majorVersion == requestedMajorVersion && flashSupport.majorRevision > requestedMajorRevision) || (flashSupport.majorVersion == requestedMajorVersion && flashSupport.majorRevision == requestedMajorRevision && flashSupport.minorRevision >= requestedMinorRevision));\n };\n brightcove.generateRequestUrl = function(experience, playerType, secureConnections) {\n var file;\n if (playerType == brightcove.playerType.INSTALLER) { file = brightcove.cdnURL + \"/viewer/playerProductInstall.swf\"; var MMPlayerType = brightcove.hasActiveX ? \"ActiveX\" : \"PlugIn\";\n document.title = document.title.slice(0, 47) + \" - Flash Player Installation\"; var MMdoctitle = document.title;\n file += \"?&MMredirectURL=\" + window.location + '&MMplayerType=' + MMPlayerType + '&MMdoctitle=' + MMdoctitle;\n brightcove.reportUpgradeRequired(experience); } else {\n if (secureConnections) { file = brightcove.getPubURL(brightcove.secureServicesURL, brightcove.pubSecureHost, experience.params.pubCode); } else { file = brightcove.getPubURL(brightcove.servicesURL, brightcove.pubHost, experience.params.pubCode); }\n var servlet = (playerType == brightcove.playerType.HTML) ? brightcove.servlet.HTML : brightcove.servlet.AS3;\n file += '/viewer/' + servlet + '?' + brightcove.getOverrides();\n for (var config in experience.params) { file += '&' + encodeURIComponent(config) + '=' + encodeURIComponent(experience.params[config]); }\n }\n return file;\n };\n brightcove.renderInstallGif = function(experience, secureConnections) { var cdnURL = secureConnections ? brightcove.secureCDNURL : brightcove.cdnURL; var upgradeFlashImage = cdnURL.indexOf('.co.jp') > 0 ? \"upgrade_flash_player_kk.gif\" : \"upgrade_flash_player2.gif\"; var linkHTML = \"Get Flash Player\"; return linkHTML; };\n brightcove.renderExperience = function(experience, file, playerType, secureConnections) {\n var experienceElement;\n var experienceID = experience.id;\n var isPubIdInBlacklist = false;\n var publisherID;\n var dummyElement;\n if (brightcove.renderExperienceInProcess) {\n function wrapRenderExperience(experience, file, playerType, secureConnections) { return function() { brightcove.renderExperience(experience, file, playerType, secureConnections); } }\n brightcove.renderExperienceQueue.push(wrapRenderExperience(experience, file, playerType, secureConnections));\n return;\n }\n brightcove.renderExperienceInProcess = true;\n if (!(experience.params.playerKey || experience.params.playerID || experience.params.playerId || experience.params.playerid)) {\n if (window.console) { console.log(\"No playerID or playerKey was found for the Brightcove player, so it can not be rendered.\"); }\n return;\n }\n brightcove.experienceObjects[experienceID] = experience;\n var unminified = (brightcove.getParameter(\"unminified\") == \"true\") || (experience.params.unminified === \"true\");\n if (experience.params.includeAPI === \"true\" && !(brightcove._apiRequested || brightcove.api)) {\n var source = \"/js/api/\";\n if (unminified) { source += \"unminified/\"; }\n source += \"SmartPlayerAPI.js\";\n var apiInclude = brightcove.createElement('script');\n apiInclude.type = \"text/javascript\";\n var cdnURL = secureConnections ? brightcove.secureCDNURL : brightcove.cdnURL;\n apiInclude.src = cdnURL + source;\n experience.parentNode.appendChild(apiInclude);\n brightcove._apiRequested = true;\n }\n file += \"&startTime=\" + new Date().getTime();\n if (experience.params.playerKey) { publisherID = brightcove.decodePublisherID(experience.params.playerKey); } else { publisherID = experience.params.publisherID; }\n dummyElement = brightcove.createDummyElement(playerType, experience, secureConnections);\n if (experience.params.enableMapping === true || (experience.params.enableMapping !== false && !isPubIdInBlacklist)) {\n brightcove.makeMappingFileRequest(publisherID, function(err, data) {\n if (err) { if (window.console) { console.log(err); } }\n brightcove.generateExperienceElement(experience, publisherID, dummyElement, unminified, file, playerType, experienceID, secureConnections, data);\n });\n } else { brightcove.generateExperienceElement(experience, publisherID, dummyElement, unminified, file, playerType, experienceID, secureConnections, null); }\n };\n brightcove.generateExperienceElement = function(experience, publisherID, dummyElement, unminified, file, playerType, experienceID, secureConnections, data) {\n var playerID;\n var bcPublisherID;\n var bcPlayerID;\n var bcEmbedID;\n var bcNewSmartPlayerID;\n var bcForceRefID;\n var parsedDataObject = {};\n var bcIframe;\n var replaceElement;\n var container;\n var timeout = brightcove.flashTimeoutInterval;\n var cdnURL = secureConnections ? brightcove.secureCDNURL : brightcove.cdnURL;\n var isKKPod = cdnURL.indexOf('.co.jp') > 0;\n var eolExtensionList = [];\n if (experience.params.enableMapping !== false && data && data.statusCode === 200 && data.response && data.response !== \"\") {\n try { if (experience.params.playerKey) { if (window.JSON) { parsedDataObject = JSON.parse(data.response)[experience.params.playerKey]; } else { parsedDataObject = brightcove.json_parse(data.response)[experience.params.playerKey]; } } else { playerID = experience.params.playerId || experience.params.playerID || experience.params.playerid; if (window.JSON) { parsedDataObject = JSON.parse(data.response); } else { parsedDataObject = brightcove.json_parse(data.response); } } } catch (ex) { if (window.console) { console.log('Error: Unable to parse mapping file: ' + ex.message); } }\n if (!experience.params.playerKey) { for (var mappedPlayerKey in parsedDataObject) { var playerMapItem; if (parsedDataObject.hasOwnProperty(mappedPlayerKey)) { playerMapItem = parsedDataObject[mappedPlayerKey]; if (playerMapItem.smart_player_id && playerMapItem.smart_player_id === playerID) { parsedDataObject = playerMapItem; break; } } } }\n if (parsedDataObject && (((!parsedDataObject.hasOwnProperty('enable_mapping') || parsedDataObject.enable_mapping) && experience.params.enableMapping !== false) || ((parsedDataObject.hasOwnProperty('enable_mapping') && parsedDataObject.enable_mapping === false) && experience.params.enableMapping === true))) { bcPublisherID = parsedDataObject.account_id ? parsedDataObject.account_id : publisherID;\n bcPlayerID = parsedDataObject.player_id;\n bcEmbedID = parsedDataObject.embed_id || 'default';\n bcNewSmartPlayerID = parsedDataObject.new_smart_player_id;\n bcForceRefID = parsedDataObject.force_ref_id || false; }\n var isInExtensionList = true;\n if (Array.prototype.indexOf) { isInExtensionList = eolExtensionList.indexOf(String(publisherID)) !== -1; } else { for (var i = 0; i < eolExtensionList.length; i++) { if (eolExtensionList[i] === String(publisherID)) { isInExtensionList = true; break; } } }\n if (!bcPlayerID && (!isInExtensionList || experience.params.enableMapping === true)) { bcPlayerID = 'default';\n bcEmbedID = bcEmbedID || 'default'; }\n }\n if (bcForceRefID) { experience = brightcove.forceRefID(experience);\n file = brightcove.generateRequestUrl(experience, playerType, secureConnections); }\n if (bcNewSmartPlayerID) { delete experience.params.playerKey;\n delete experience.params.playerId;\n delete experience.params.playerid;\n experience.params.playerID = bcNewSmartPlayerID;\n brightcove.servicesURL = brightcove.USservicesURL;\n brightcove.cdnURL = brightcove.UScdnURL;\n brightcove.secureCDNURL = brightcove.USsecureCDNURL;\n brightcove.secureServicesURL = brightcove.USsecureServicesURL;\n file = brightcove.generateRequestUrl(experience, playerType, secureConnections); } else if (bcPlayerID && !bcPublisherID) { bcPublisherID = publisherID; }\n if (isKKPod && (!parsedDataObject || !parsedDataObject.player_id)) {\n var iframeDoc;\n experienceElement = brightcove.createIFrame(experience);\n brightcove.copyNodeProperties(dummyElement, experienceElement);\n replaceElement = brightcove.getElementByClassNameCrossBrowser(experience.params.identifierClassName);\n if (replaceElement && replaceElement.parentNode) { replaceElement.parentNode.replaceChild(experienceElement, replaceElement); }\n brightcove.experiences[experienceID] = experienceElement;\n iframeDoc = experienceElement.contentDocument || experienceElement.contentWindow.document;\n iframeDoc.write('');\n } else if (bcPublisherID && bcPlayerID) {\n bcIframe = brightcove.getBCPlayerIframe(experience, bcPublisherID, bcPlayerID, bcEmbedID);\n experienceElement = brightcove.createIFrame(experience);\n brightcove.copyNodeProperties(dummyElement, experienceElement);\n replaceElement = brightcove.getElementByClassNameCrossBrowser(experience.params.identifierClassName);\n experienceElement.setAttribute('allowFullScreen', '');\n experienceElement.setAttribute('webkitAllowFullScreen', '');\n experienceElement.setAttribute('mozillaAllowFullScreen', '');\n if (replaceElement && replaceElement.parentNode) { replaceElement.parentNode.replaceChild(experienceElement, replaceElement); }\n brightcove.experiences[experienceID] = experienceElement;\n experienceElement.src = bcIframe;\n } else {\n var iframeDoc;\n experienceElement = brightcove.createIFrame(experience);\n brightcove.copyNodeProperties(dummyElement, experienceElement);\n replaceElement = brightcove.getElementByClassNameCrossBrowser(experience.params.identifierClassName);\n if (replaceElement && replaceElement.parentNode) { replaceElement.parentNode.replaceChild(experienceElement, replaceElement); }\n brightcove.experiences[experienceID] = experienceElement;\n iframeDoc = experienceElement.contentDocument || experienceElement.contentWindow.document;\n iframeDoc.write('');\n }\n brightcove.renderExperienceInProcess = false;\n if (brightcove.renderExperienceQueue.length > 0) { brightcove.renderExperienceQueue.shift()(); } else if (brightcove.createExperiencesQueue.length > 0) { brightcove.createExperiencesQueue.shift()(); }\n brightcove.timeouts[experience.id] = setTimeout(function() { brightcove.handleExperienceTimeout(experienceID); }, timeout);\n };\n brightcove.copyNode = function(elementFrom) { var experienceElement = elementFrom.cloneNode(true);\n brightcove.copyNodeProperties(elementFrom, experienceElement); return experienceElement; };\n brightcove.copyNodeProperties = function(elementFrom, elementTo) {\n var propertyItem;\n var propertyList = ['name', 'title', 'height', 'width', 'border', 'onclick', 'ondblclick', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onscroll', 'onwheel'];\n for (propertyItem in propertyList) { if (elementFrom[propertyList[propertyItem]]) { elementTo[propertyList[propertyItem]] = elementFrom[propertyList[propertyItem]]; } }\n if (elementTo.className !== elementFrom.className) { elementTo.className += ' ' + elementFrom.className; }\n };\n brightcove.getElementsByClassName = function(selector) {\n var retnode = [];\n var elem = document.getElementsByTagName('*');\n for (var i = 0; i < elem.length; i++) { if ((' ' + elem[i].className + ' ').indexOf(' ' + selector + ' ') > -1) retnode.push(elem[i]); }\n return retnode;\n };\n brightcove.getElementByClassNameCrossBrowser = function(selector) {\n var searchElement;\n if (document.querySelectorAll) { searchElement = document.querySelectorAll('.' + selector)[0]; } else { searchElement = brightcove.getElementsByClassName(selector)[0]; }\n return searchElement;\n };\n brightcove.createDummyElement = function(playerType, experience, secureConnections) {\n var dummyElement;\n var containerID;\n var flashObjectParams;\n var flashEmbedStr;\n experience.className += ' ' + experience.params.identifierClassName;\n if (playerType === brightcove.playerType.NO_SUPPORT) {\n containerID = '_container' + experience.id;\n dummyElement = brightcove.createElement('span');\n if (experience.params.height.charAt(experience.params.height.length - 1) == \"%\") { dummyElement.style.display = 'block'; } else { dummyElement.style.display = 'inline-block'; }\n dummyElement.className = experience.className;\n dummyElement.id = containerID;\n } else if (playerType === brightcove.playerType.HTML || playerType === brightcove.playerType.FLASH_IFRAME) { dummyElement = brightcove.createIFrame(experience); if (experience && experience.parentNode) { experience.parentNode.replaceChild(dummyElement, experience); } } else { if (brightcove.hasActiveX) { flashEmbedStr = brightcove.getDummyFlashEmbedString(experience);\n containerID = '_container' + experience.id;\n dummyElement = brightcove.createFlashEmbed(containerID, experience.params.height); if (experience && experience.parentNode) { experience.parentNode.replaceChild(dummyElement, experience);\n dummyElement.innerHTML = flashEmbedStr; } } else { flashObjectParams = brightcove.getFlashObjectParams(experience);\n dummyElement = brightcove.createFlashObject(flashObjectParams); if (experience && experience.parentNode) { experience.parentNode.replaceChild(dummyElement, experience); } } }\n return dummyElement;\n };\n brightcove.getDummyFlashEmbedString = function(experience) {\n return '' +\n '';\n };\n brightcove.makeMetricsErrorCall = function(publisherID, errorType) { var img = document.createElement('img'); var metricsUrl = brightcove.metricsBaseUrl['production'];\n img.src = metricsUrl + '?' + 'account=' + publisherID + '&domain=videocloud' + '&platform=as3' + '&event=error' + '&error_code=' + errorType; };\n brightcove.createIFrame = function(experience) { var iframeElement = brightcove.createElement('iframe');\n iframeElement.id = experience.id;\n iframeElement.width = experience.params.width;\n iframeElement.height = experience.params.height;\n iframeElement.className = experience.className;\n iframeElement.frameborder = 0;\n iframeElement.scrolling = \"no\";\n iframeElement.style.borderStyle = \"none\"; return iframeElement; };\n brightcove.getFlashEmbedString = function(experience, secureConnections) {\n var options = '';\n var flashParams = experience.flashParams;\n for (var pOption in flashParams) { options += ''; }\n var protocol = secureConnections ? \"https\" : \"http\";\n return '' +\n options +\n '';\n };\n brightcove.getFlashObjectParams = function(experience, file) {\n var experienceObject = {};\n experienceObject.type = 'application/x-shockwave-flash';\n experienceObject.data = file;\n experienceObject.id = experience.params.flashID;\n experienceObject.width = experience.params.width;\n experienceObject.height = experience.params.height;\n experienceObject.className = experience.className;\n experienceObject.seamlesstabbing = experience.flashParams.seamlessTabbing;\n for (var config in experience.flashParams) { experienceObject[\"flashParam_\" + config] = experience.flashParams[config]; }\n return experienceObject;\n };\n brightcove.createFlashEmbed = function(experienceId, height) {\n var container = brightcove.createElement('span');\n if (height.charAt(height.length - 1) == \"%\") { container.style.display = 'block'; } else { container.style.display = 'inline-block'; }\n container.id = experienceId;\n return container;\n };\n brightcove.createFlashObject = function(playerConfig) {\n var experienceElement = brightcove.createElement('object');\n experienceElement.type = playerConfig.type;\n if (playerConfig.data) { experienceElement.data = playerConfig.data; }\n experienceElement.id = playerConfig.id;\n experienceElement.width = playerConfig.width;\n experienceElement.height = playerConfig.height;\n experienceElement.className = playerConfig.className;\n experienceElement.setAttribute(\"seamlesstabbing\", playerConfig.seamlessTabbing);\n var tempParam;\n var flashParamPrefix = \"flashParam_\";\n for (var config in playerConfig) { var flashParamInd = config.indexOf(flashParamPrefix); if (flashParamInd == 0) { tempParam = brightcove.createElement('param');\n tempParam.name = config.substring(flashParamPrefix.length);\n tempParam.value = playerConfig[config];\n experienceElement.appendChild(tempParam); } }\n return experienceElement;\n };\n brightcove.handleExperienceTimeout = function(pID) { brightcove.executeErrorHandlerForExperience(brightcove.experienceObjects[pID], { type: \"templateError\", errorType: \"serviceUnavailable\", code: brightcove.errorCodes.SERVICE_UNAVAILABLE, info: pID }); };\n brightcove.reportPlayerLoad = function(pID) { var timeout = brightcove.timeouts[pID]; if (timeout) { clearTimeout(timeout); } };\n brightcove.reportUpgradeRequired = function(pExperience) { brightcove.executeErrorHandlerForExperience(pExperience, { type: \"templateError\", errorType: \"upgradeRequiredForPlayer\", code: brightcove.errorCodes.UPGRADE_REQUIRED_FOR_PLAYER, info: pExperience.id }); };\n brightcove.checkFlashSupport = function() { var hasActiveX = (window.ActiveXObject != undefined); return (hasActiveX) ? brightcove.checkFlashSupportIE() : brightcove.checkFlashSupportStandard(); };\n brightcove.checkFlashSupportIE = function() {\n var versions;\n try { var flash = new ActiveXObject(\"ShockwaveFlash.ShockwaveFlash.7\"); var version = flash.GetVariable('$version');\n versions = / ([0-9]+),([0-9]+),([0-9]+),/.exec(version); } catch (exception) { return null; }\n return { majorVersion: versions[1], majorRevision: versions[2], minorRevision: versions[3] };\n };\n brightcove.isMetroIE = function() {\n var version = 0;\n if (navigator.appVersion.indexOf(\"MSIE\") != -1) { var appSplit = navigator.appVersion.split(\"MSIE\"); if (appSplit.length > 1) { version = parseFloat(appSplit[1]); } }\n if (version < 10 || isNaN(version)) { return false; }\n var activeXSupport = false;\n try { activeXSupport = !!new ActiveXObject(\"htmlfile\"); } catch (e) { activeXSupport = false; }\n return !activeXSupport;\n };\n brightcove.checkFlashSupportStandard = function() {\n var versions;\n var majorVersion;\n var majorRevision;\n var minorRevision;\n try {\n if (typeof navigator.plugins != 'undefined' && navigator.plugins.length > 0) {\n if (navigator.plugins[\"Shockwave Flash 2.0\"] || navigator.plugins[\"Shockwave Flash\"]) {\n var swfVersion = navigator.plugins[\"Shockwave Flash 2.0\"] ? \" 2.0\" : \"\";\n var description = navigator.plugins[\"Shockwave Flash\" + swfVersion].description;\n var filename = navigator.plugins[\"Shockwave Flash\" + swfVersion].filename;\n if (filename.match) { if (filename.toLowerCase().match(/lite/)) { throw new Error(); } }\n versions = description.split(\" \");\n majorVersion = versions[2].split(\".\")[0];\n majorRevision = versions[2].split(\".\")[1];\n minorRevision = versions[3];\n if (minorRevision == \"\") { minorRevision = versions[4]; }\n if (minorRevision[0] == \"d\") { minorRevision = minorRevision.substring(1); } else if (minorRevision[0] == \"r\") { minorRevision = minorRevision.substring(1); if (minorRevision.indexOf(\"d\") > 0) { minorRevision = minorRevision.substring(0, minorRevision.indexOf(\"d\")); } }\n } else { throw new Error(); }\n } else { return null; }\n } catch (exception) { return null; }\n return { majorVersion: majorVersion, majorRevision: majorRevision, minorRevision: minorRevision };\n };\n brightcove.checkHtmlSupport = function() {\n var v = brightcove.createElement('video');\n var videoSupport = true;\n if (!brightcove.userAgent.match(new RegExp(\"android\", \"i\"))) { videoSupport = !!(v.canPlayType && v.canPlayType('video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"').replace(/no/, '')); }\n if (brightcove.userAgent.match(/BlackBerry.*Version\\/6\\.0/)) { return false; }\n var canvasSupport = !!brightcove.createElement('canvas').getContext;\n return videoSupport && canvasSupport;\n };\n brightcove.isSupportedHTMLDevice = function(pUAString) {\n var types = [\"iPad\", \"iPhone\", \"iPod\", \"android\", \"Silk\", \"IEMobile\"];\n var numTypes = types.length;\n var uaString = pUAString || brightcove.userAgent;\n for (var i = 0; i < numTypes; i++) { if (uaString.match(new RegExp(types[i], \"i\"))) { return true; } }\n return false;\n };\n brightcove.getTechnology = function(pExperienceId) {\n for (var id in brightcove.experiences) { if (pExperienceId == id) { return (brightcove.experiences[id].tagName == \"object\") ? brightcove.playerType.FLASH : brightcove.playerType.HTML; } }\n return brightcove.playerType.NO_SUPPORT;\n };\n brightcove.respondToMessages = function(pMessage) {\n if (brightcove.verifyMessage(pMessage)) {\n var messageData = pMessage.data;\n if (messageData.charAt(0) == \"\\\"\") { if (window.JSON) { messageData = window.JSON.parse(messageData); } else { messageData = brightcove.json_parse(messageData); } }\n var messageParts = messageData.split(\"::\");\n var type = messageParts[1];\n var messageInfo = \"\";\n for (var index = 2; index < messageParts.length; index++) { messageInfo += messageParts[index]; }\n var messageJson = messageInfo.split(\"\\n\").join(\" \");\n var messageDataObject;\n if (window.JSON) { messageDataObject = window.JSON.parse(messageJson); } else { messageDataObject = brightcove.json_parse(messageJson); }\n switch (type) {\n case \"error\":\n brightcove.executeMessageCallback(messageDataObject, brightcove.executeErrorHandlerForExperience);\n break;\n case \"api\":\n brightcove.handleAPICallForHTML(messageDataObject);\n break;\n case \"handler\":\n var event = brightcove.internal._convertDates(messageDataObject.event);\n try { brightcove.internal._handlers[messageDataObject.handler](event); } catch (e) {}\n break;\n case \"asyncGetter\":\n var data = brightcove.internal._convertDates(messageDataObject.data);\n brightcove.internal._handlers[messageDataObject.handler](data);\n break;\n }\n }\n };\n brightcove.verifyMessage = function(pMessage) { return (/^brightcove\\.player/).test(pMessage.data); };\n brightcove.handleAPICallForHTML = function(pMessageObject) {\n var experience = brightcove.experienceObjects[pMessageObject.id];\n if (experience == null) { return; }\n var id = experience.id;\n var method = pMessageObject.method;\n switch (method) {\n case \"initializeBridge\":\n brightcove.reportPlayerLoad(id);\n if (pMessageObject.arguments[0]) { if (brightcove.internal != null) { brightcove.internal._setAPICallback(id, null, pMessageObject.arguments[1]);\n brightcove.callHandlerForPlayer(experience, \"templateLoadHandler\", id); } else if (brightcove._apiRequested) { brightcove._queuedAPICalls.push(pMessageObject); } }\n break;\n case \"callTemplateReady\":\n if (brightcove._apiRequested && !brightcove.internal) { brightcove._queuedAPICalls.push(pMessageObject); } else { var event = pMessageObject.arguments;\n brightcove._addModuleToEvent(id, event);\n brightcove.callHandlerForPlayer(experience, \"templateReadyHandler\", event); }\n break;\n }\n };\n brightcove._addModuleToEvent = function(pID, pEvent) { if (pEvent.type != null && brightcove.api) { var experience = brightcove.api.getExperience(pID); if (experience) { pEvent.target = experience.getModule(\"experience\"); } } };\n brightcove.callHandlerForPlayer = function(pExperience, pHandler, pArgument) {\n if (pExperience && pExperience.params && pExperience.params[pHandler]) {\n var namespaceArray = pExperience.params[pHandler].split(\".\");\n var namespaces;\n if ((namespaces = namespaceArray.length) > 1) {\n var trace = window;\n for (var i = 0; i < namespaces; i++) { trace = trace[namespaceArray[i]]; }\n if (typeof trace === \"function\") { trace(pArgument); }\n } else { window[pExperience.params[pHandler]](pArgument); }\n }\n };\n brightcove.executeErrorHandlerForExperience = function(pExperience, pErrorObject) { brightcove.callHandlerForPlayer(pExperience, \"templateErrorHandler\", pErrorObject); };\n brightcove.executeMessageCallback = function(pMessageDataObject, pCallback) { var experience; for (var experienceKey in brightcove.experienceObjects) { experience = brightcove.experienceObjects[experienceKey]; if (experience.element.src === pMessageDataObject.__srcUrl) { delete pMessageDataObject.__srcUrl;\n pCallback(experience, pMessageDataObject); break; } } };\n brightcove.createExperience = function(pElement, pParentOrSibling, pAppend) {\n if (!pElement.id || pElement.id.length < 1) { pElement.id = 'bcExperienceObj' + (brightcove.experienceNum++); }\n if (pAppend) { pParentOrSibling.appendChild(pElement); } else { pParentOrSibling.parentNode.insertBefore(pElement, pParentOrSibling); }\n brightcove.createExperiences(null, pElement.id);\n };\n brightcove.removeExperience = function(pID) { if (brightcove.experiences[pID] != null) { brightcove.experiences[pID].parentNode.removeChild(brightcove.experiences[pID]); } };\n brightcove.getURL = function() {\n var url;\n if (typeof window.location.search != 'undefined') { url = window.location.search; } else { url = /(\\?.*)$/.exec(document.location.href); }\n return url;\n };\n brightcove.getOverrides = function() {\n var url = brightcove.getURL();\n var query = new RegExp('@[\\\\w\\\\.]+=[^&]+', 'g');\n var value = query.exec(url);\n var overrides = \"\";\n while (value != null) { overrides += \"&\" + value;\n value = query.exec(url); }\n return overrides;\n };\n brightcove.getParameter = function(pName, pDefaultValue) { if (pDefaultValue == null) pDefaultValue = \"\"; var url = brightcove.getURL(); var query = new RegExp(pName + '=([^&]*)'); var value = query.exec(url); if (value != null) { return value[1]; } else { return pDefaultValue; } };\n brightcove.createElement = function(el) { if (document.createElementNS) { return document.createElementNS('http://www.w3.org/1999/xhtml', el); } else { return document.createElement(el); } };\n brightcove.i18n = { 'BROWSER_TOO_OLD': 'The browser you are using is too old. Please upgrade to the latest version of your browser.' };\n brightcove.removeListeners = function() {\n if (/KHTML/i.test(navigator.userAgent)) { clearInterval(checkLoad);\n document.removeEventListener('load', brightcove.createExperiences, false); }\n if (typeof document.addEventListener != 'undefined') { document.removeEventListener('DOMContentLoaded', brightcove.createExperiences, false);\n document.removeEventListener('load', brightcove.createExperiences, false); } else if (typeof window.attachEvent != 'undefined') { window.detachEvent('onload', brightcove.createExperiences); }\n };\n brightcove.getPubURL = function(source, host, pubCode) { if (!pubCode || pubCode == \"\") return source; var re = /^([htps]{4,5}\\:\\/\\/)([^\\/\\:]+)/i;\n host = host.replace(\"$pubcode$\", pubCode).replace(\"$zoneprefix$$zone$\", brightcove.pubSubdomain); return source.replace(re, \"$1\" + host); };\n brightcove.createExperiencesPostLoad = function() { brightcove.removeListeners();\n brightcove.createExperiences(); };\n brightcove.encode = function(string) { string = escape(string);\n string = string.replace(/\\+/g, \"%2B\");\n string = string.replace(/\\-/g, \"%2D\");\n string = string.replace(/\\*/g, \"%2A\");\n string = string.replace(/\\//g, \"%2F\");\n string = string.replace(/\\./g, \"%2E\");\n string = string.replace(/_/g, \"%5F\");\n string = string.replace(/@/g, \"%40\"); return string; };\n brightcove.decodePublisherID = function(playerKey) {\n var b64;\n var bytes = [];\n var keys = [];\n var byteArrayToLong = function(byteArray, length) {\n var value = 0;\n for (var i = 0; i < length; i++) { value = (value * 256) + byteArray[i]; }\n return value;\n };\n if (!playerKey || playerKey.indexOf(',') < 0) {\n if (window.console) { console.log('The player key is in an unexpected format.  Unable to extract publisher ID for potential use with player migration.'); }\n return;\n }\n playerKey = playerKey.replace(/\\./g, \"~\");\n keys = playerKey.split(',');\n try { b64 = brightcove.base64().toByteArray(keys[1]); } catch (ex) {\n if (window.console) { console.log('The player key is in an unexpected format.  Unable to extract publisher ID for potential use with player migration: ' + ex.message); }\n return;\n }\n return byteArrayToLong(b64, 8);\n };\n brightcove.makeMappingFileRequest = function(publisherID, callback) {\n if (!publisherID) { brightcove.makeMetricsErrorCall('null', brightcove.analyticsErrors.BAD_PUBLISHER_ID); return callback(); } else if (brightcove.mappingFileData.hasOwnProperty(publisherID)) { return callback(null, brightcove.mappingFileData[publisherID]); } else {\n return brightcove.makeAjaxRequest('GET', brightcove.performCdnUrl['production'] + publisherID + '/migration/mappings.json', function(err, data) {\n if (window.console && data && data.statusCode === 404) { console.log('It is safe to ignore 404 messages for mappings.json file requests.'); } else if (data && data.statusCode === 0) { brightcove.makeMetricsErrorCall(publisherID, brightcove.analyticsErrors.MAPPINGS_CALL_FAILURE); } else if (data && data.statusCode !== 200 && data.statusCode !== 404) { brightcove.makeMetricsErrorCall(publisherID, brightcove.analyticsErrors.UNEXPECTED_MAPPING_RESPONSE); }\n brightcove.mappingFileData[publisherID] = data;\n return callback(err, data);\n });\n }\n };\n brightcove.makeAjaxRequest = function(action, url, callback) {\n function createXmlHTTPObject() {\n var XMLHttpFactories = [function() { return new XDomainRequest() }, function() { return new XMLHttpRequest() }, function() { return new ActiveXObject('Msxml2.XMLHTTP') }, function() { return new ActiveXObject('Msxml3.XMLHTTP') }, function() { return new ActiveXObject('Microsoft.XMLHTTP') }];\n var xmlhttp = false;\n for (var i = 0; i < XMLHttpFactories.length; i++) {\n try { xmlhttp = XMLHttpFactories[i](); } catch (e) { continue; }\n break;\n }\n return xmlhttp;\n }\n var xhttp = createXmlHTTPObject();\n if (!xhttp) { return callback(); }\n try {\n xhttp.open(action, url, true);\n xhttp.timeout = 3000;\n if (!window.XDomainRequest) { xhttp.onreadystatechange = function() { if (xhttp.readyState == 4) { return callback(null, { statusCode: xhttp.status, response: xhttp.responseText }); } }; } else {\n xhttp.onload = function() { return callback(null, { statusCode: 200, response: xhttp.responseText }); };\n xhttp.ontimeout = function() { return callback(null, { statusCode: 0 }); }\n xhttp.onprogress = function() {};\n xhttp.onerror = function() { return callback(null, { statusCode: 403 }); };\n }\n xhttp.send();\n } catch (e) { callback(null, null); }\n };\n brightcove.base64 = function() {\n var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\n var Arr = (typeof Uint8Array !== 'undefined') ? Uint8Array : Array\n var PLUS = '+'.charCodeAt(0)\n var SLASH = '/'.charCodeAt(0)\n var NUMBER = '0'.charCodeAt(0)\n var LOWER = 'a'.charCodeAt(0)\n var UPPER = 'A'.charCodeAt(0)\n var PLUS_URL_SAFE = '-'.charCodeAt(0)\n var SLASH_URL_SAFE = '_'.charCodeAt(0)\n\n function decode(elt) {\n var code = elt.charCodeAt(0)\n if (code === PLUS || code === PLUS_URL_SAFE) return 62\n if (code === SLASH || code === SLASH_URL_SAFE) return 63\n if (code < NUMBER) return -1\n if (code < NUMBER + 10) return code - NUMBER + 26 + 26\n if (code < UPPER + 26) return code - UPPER\n if (code < LOWER + 26) return code - LOWER + 26\n }\n\n function b64ToByteArray(b64) {\n var i, j, l, tmp, placeHolders, arr\n if (b64.length % 4 > 0) { throw new Error('Invalid string. Length must be a multiple of 4') }\n var len = b64.length\n placeHolders = b64.charAt(len - 2) === '=' ? 2 : b64.charAt(len - 1) === '=' ? 1 : 0\n arr = new Arr(b64.length * 3 / 4 - placeHolders)\n l = placeHolders > 0 ? b64.length - 4 : b64.length\n var L = 0\n\n function push(v) { arr[L++] = v }\n for (i = 0, j = 0; i < l; i += 4, j += 3) {\n tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))\n push((tmp & 0xFF0000) >> 16)\n push((tmp & 0xFF00) >> 8)\n push(tmp & 0xFF)\n }\n if (placeHolders === 2) {\n tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)\n push(tmp & 0xFF)\n } else if (placeHolders === 1) {\n tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)\n push((tmp >> 8) & 0xFF)\n push(tmp & 0xFF)\n }\n return arr\n }\n return { toByteArray: b64ToByteArray }\n };\n brightcove.forceRefID = function(experience) {\n var videoID = experience.params.videoID;\n var videoPlayer = experience.params['@videoPlayer'];\n var playlistID = experience.params['@videoList'];\n var lineupID = experience.params.lineupID;\n var playlistTabs = experience.params['@playlistTabs'];\n var playlistCombo = experience.params['@playlistCombo'];\n var playlistVideoFeatured = experience.params['@videoList.featured'];\n var playlistTabsFeatured = experience.params['@playlistTabs.featured'];\n var playlistComboFeatured = experience.params['@playlistCombo.featured'];\n var playlistArray;\n var playlistJoined;\n if (playlistTabs) { playlistArray = playlistTabs.split(','); } else if (playlistCombo) { playlistArray = playlistCombo.split(','); }\n if (playlistArray) {\n for (var i = 0; i < playlistArray.length; i++) { if (playlistArray[i].indexOf('ref:') < 0) { playlistArray[i] = 'ref:' + playlistArray[i]; } }\n playlistJoined = playlistArray.join(',');\n }\n if (playlistTabs) { experience.params['@playlistTabs'] = playlistJoined; } else if (playlistCombo) { experience.params['@playlistCombo'] = playlistJoined; }\n if (playlistID && playlistID.indexOf('ref:') < 0) { experience.params['@videoList'] = 'ref:' + playlistID; }\n if (lineupID && lineupID.indexOf('ref:') < 0) { experience.params.lineupID = 'ref:' + lineupID; }\n if (playlistVideoFeatured && playlistVideoFeatured.indexOf('ref:') < 0) { experience.params['@videoList.featured'] = 'ref:' + playlistVideoFeatured; }\n if (playlistTabsFeatured && playlistTabsFeatured.indexOf('ref:') < 0) { experience.params['@playlistTabs.featured'] = 'ref:' + playlistTabsFeatured; }\n if (playlistComboFeatured && playlistComboFeatured.indexOf('ref:') < 0) { experience.params['@playlistCombo.featured'] = 'ref:' + playlistComboFeatured; }\n if (videoID && videoID.indexOf('ref:') < 0) { experience.params.videoID = 'ref:' + videoID; }\n if (videoPlayer && videoPlayer.indexOf('ref:') < 0) { experience.params['@videoPlayer'] = 'ref:' + videoPlayer; }\n return experience;\n };\n brightcove.getBCPlayerIframe = function(experience, accountID, playerID, embedID) {\n var iframeSource = brightcove.performCdnUrl['production'] + accountID + '/' + playerID + '_' + embedID + '/index.html?directedMigration=true&';\n var videoID = experience.params['@videoPlayer'] || experience.params.videoID;\n var playlistID = experience.params['@videoList'] || experience.params.lineupID;\n var playlistTabs = experience.params['@playlistTabs'];\n var playlistCombo = experience.params['@playlistCombo'];\n var playlistArray;\n var playlistVideoID = experience.params['@videoList.featured'] || experience.params.videoID;\n if (playlistTabs) { playlistArray = playlistTabs.split(','); } else if (playlistCombo) { playlistArray = playlistCombo.split(','); }\n if (experience.params['@playlistTabs.featured']) { playlistID = experience.params['@playlistTabs.featured']; } else if (experience.params['@playlistCombo.featured']) { playlistID = experience.params['@playlistCombo.featured']; } else if (playlistArray && playlistArray[0]) { playlistID = playlistArray[0]; }\n if (playlistID && (isNaN(playlistID) && playlistID.indexOf('ref:') < 0)) { playlistID = 'ref:' + playlistID; }\n if (playlistID) { iframeSource += 'playlistId=' + playlistID + '&'; } else if (videoID && (!isNaN(videoID) || videoID.indexOf('ref:') >= 0)) { iframeSource += 'videoId=' + videoID + '&'; } else if (videoID) { iframeSource += 'videoId=ref:' + videoID + '&'; }\n if (playlistID && playlistVideoID) {\n if (playlistVideoID && (isNaN(playlistVideoID) && playlistVideoID.indexOf('ref:') < 0)) { playlistVideoID = 'ref:' + playlistVideoID; }\n iframeSource += 'playlistVideoId=' + playlistVideoID + '&';\n }\n if (experience.params.language && experience.params.language === 'jp') { iframeSource += 'language=ja&'; } else if (experience.params.language) { iframeSource += 'language=' + experience.params.language + '&'; }\n if (experience.params.autoStart && experience.params.autoStart != 'false') { iframeSource += 'autoplay=' + experience.params.autoStart + '&'; }\n return iframeSource;\n };\n if (/KHTML/i.test(navigator.userAgent)) { var checkLoad = setInterval(function() { if (/loaded|complete/.test(document.readyState)) { clearInterval(checkLoad);\n brightcove.createExperiencesPostLoad(); } }, 70);\n document.addEventListener('load', brightcove.createExperiencesPostLoad, false); }\n if (typeof document.addEventListener != 'undefined') { document.addEventListener('DOMContentLoaded', brightcove.createExperiencesPostLoad, false);\n document.addEventListener('load', brightcove.createExperiencesPostLoad, false);\n window.addEventListener(\"message\", brightcove.respondToMessages, false); } else if (typeof window.attachEvent != 'undefined') { window.attachEvent('onload', brightcove.createExperiencesPostLoad); } else { alert(brightcove.i18n.BROWSER_TOO_OLD); }\n}\nbrightcove.json_parse = (function() {\n \"use strict\";\n var state, stack, container, key, value, escapes = { '\\\\': '\\\\', '\"': '\"', '/': '/', 't': '\\t', 'n': '\\n', 'r': '\\r', 'f': '\\f', 'b': '\\b' },\n string = { go: function() { state = 'ok'; }, firstokey: function() { key = value;\n state = 'colon'; }, okey: function() { key = value;\n state = 'colon'; }, ovalue: function() { state = 'ocomma'; }, firstavalue: function() { state = 'acomma'; }, avalue: function() { state = 'acomma'; } },\n number = { go: function() { state = 'ok'; }, ovalue: function() { state = 'ocomma'; }, firstavalue: function() { state = 'acomma'; }, avalue: function() { state = 'acomma'; } },\n action = {\n '{': { go: function() { stack.push({ state: 'ok' });\n container = {};\n state = 'firstokey'; }, ovalue: function() { stack.push({ container: container, state: 'ocomma', key: key });\n container = {};\n state = 'firstokey'; }, firstavalue: function() { stack.push({ container: container, state: 'acomma' });\n container = {};\n state = 'firstokey'; }, avalue: function() { stack.push({ container: container, state: 'acomma' });\n container = {};\n state = 'firstokey'; } },\n '}': { firstokey: function() { var pop = stack.pop();\n value = container;\n container = pop.container;\n key = pop.key;\n state = pop.state; }, ocomma: function() { var pop = stack.pop();\n container[key] = value;\n value = container;\n container = pop.container;\n key = pop.key;\n state = pop.state; } },\n '[': { go: function() { stack.push({ state: 'ok' });\n container = [];\n state = 'firstavalue'; }, ovalue: function() { stack.push({ container: container, state: 'ocomma', key: key });\n container = [];\n state = 'firstavalue'; }, firstavalue: function() { stack.push({ container: container, state: 'acomma' });\n container = [];\n state = 'firstavalue'; }, avalue: function() { stack.push({ container: container, state: 'acomma' });\n container = [];\n state = 'firstavalue'; } },\n ']': { firstavalue: function() { var pop = stack.pop();\n value = container;\n container = pop.container;\n key = pop.key;\n state = pop.state; }, acomma: function() { var pop = stack.pop();\n container.push(value);\n value = container;\n container = pop.container;\n key = pop.key;\n state = pop.state; } },\n ':': {\n colon: function() {\n if (Object.hasOwnProperty.call(container, key)) { throw new SyntaxError('Duplicate key \"' + key + '\"'); }\n state = 'ovalue';\n }\n },\n ',': { ocomma: function() { container[key] = value;\n state = 'okey'; }, acomma: function() { container.push(value);\n state = 'avalue'; } },\n 'true': { go: function() { value = true;\n state = 'ok'; }, ovalue: function() { value = true;\n state = 'ocomma'; }, firstavalue: function() { value = true;\n state = 'acomma'; }, avalue: function() { value = true;\n state = 'acomma'; } },\n 'false': { go: function() { value = false;\n state = 'ok'; }, ovalue: function() { value = false;\n state = 'ocomma'; }, firstavalue: function() { value = false;\n state = 'acomma'; }, avalue: function() { value = false;\n state = 'acomma'; } },\n 'null': { go: function() { value = null;\n state = 'ok'; }, ovalue: function() { value = null;\n state = 'ocomma'; }, firstavalue: function() { value = null;\n state = 'acomma'; }, avalue: function() { value = null;\n state = 'acomma'; } }\n };\n\n function debackslashify(text) { return text.replace(/\\\\(?:u(.{4})|([^u]))/g, function(a, b, c) { return b ? String.fromCharCode(parseInt(b, 16)) : escapes[c]; }); }\n return function(source, reviver) {\n var r, tx = /^[\\x20\\t\\n\\r]*(?:([,:\\[\\]{}]|true|false|null)|(-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)|\"((?:[^\\r\\n\\t\\\\\\\"]|\\\\(?:[\"\\\\\\/trnfb]|u[0-9a-fA-F]{4}))*)\")/;\n state = 'go';\n stack = [];\n try {\n for (;;) {\n r = tx.exec(source);\n if (!r) { break; }\n if (r[1]) { action[r[1]][state](); } else if (r[2]) { value = +r[2];\n number[state](); } else { value = debackslashify(r[3]);\n string[state](); }\n source = source.slice(r[0].length);\n }\n } catch (e) { state = e; }\n if (state !== 'ok' || (/[^\\x20\\t\\n\\r]/).test(source)) { throw state instanceof SyntaxError ? state : new SyntaxError('JSON'); }\n return typeof reviver === 'function' ? (function walk(holder, key) {\n var k, v, value = holder[key];\n if (value && typeof value === 'object') { for (k in value) { if (Object.prototype.hasOwnProperty.call(value, k)) { v = walk(value, k); if (v !== undefined) { value[k] = v; } else { delete value[k]; } } } }\n return reviver.call(holder, key, value);\n }({ '': value }, '')) : value;\n };\n}());", 299 | "settings": 300 | { 301 | "buffer_size": 62336, 302 | "line_ending": "Windows", 303 | "name": "if (brightcove == undefined) { var brightcove = {}" 304 | } 305 | } 306 | ], 307 | "build_system": "", 308 | "build_system_choices": 309 | [ 310 | ], 311 | "build_varint": "", 312 | "command_palette": 313 | { 314 | "height": 392.0, 315 | "last_filter": "insta", 316 | "selected_items": 317 | [ 318 | [ 319 | "insta", 320 | "Package Control: Install Package" 321 | ], 322 | [ 323 | "instal", 324 | "Package Control: Install Package" 325 | ], 326 | [ 327 | "install", 328 | "Package Control: Install Package" 329 | ] 330 | ], 331 | "width": 428.0 332 | }, 333 | "console": 334 | { 335 | "height": 0.0, 336 | "history": 337 | [ 338 | ] 339 | }, 340 | "distraction_free": 341 | { 342 | "menu_visible": true, 343 | "show_minimap": false, 344 | "show_open_files": false, 345 | "show_tabs": false, 346 | "side_bar_visible": false, 347 | "status_bar_visible": false 348 | }, 349 | "expanded_folders": 350 | [ 351 | "/D/Projects/stuffs/autotrader-bot", 352 | "/D/Projects/stuffs/autotrader-bot/indicators" 353 | ], 354 | "file_history": 355 | [ 356 | "/D/Projects/stuffs/autotrader-bot/indicators/support.js", 357 | "/D/Projects/stuffs/autotrader-bot/ichi_cloud.js", 358 | "/D/Projects/stuffs/autotrader-bot/indicators/ichimoku.js", 359 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Pages/Vehicle/CategoryCenter/KBB.Page.Vehicle.CategoryCenter.js", 360 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Common/KBB.Page.Events.js", 361 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/obj/Release/Package/PackageTmp/Styles/OOCss/partials/buyers-hub/_buyer-hub-compare.scss", 362 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/obj/Release/Package/PackageTmp/Styles/OOCss/partials/shared/mixins/_breakpoints.scss", 363 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/partials/owners18/_owners-trade-up-module.scss", 364 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Utilities/jquery.viewed.js", 365 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/partials/shared/variables/_colors.scss", 366 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Areas/KBB.Trident.Web.Areas.Vehicle/Scripts/Modules/Ads/KBB.Ads.SimilarVehicleSponsorship.js", 367 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/tests/modules/ads/KBB.Ads.SimilarVehicleSponsorship.js", 368 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/js/kbb/KBB.js", 369 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Configuration/Web/BestResaleValueAwards/Ads.js", 370 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/js/kbb.js", 371 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Areas/KBB.Trident.Web.Areas.Vehicle/Scripts/Modules/Ads/KBB.Ads.BudgetCalculator.js", 372 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/partials/ads/_monthly-budget-calculator.scss", 373 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/partials/mmymm/components/_similar-vehicles-sponsorship.scss", 374 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/js/ads.js", 375 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Modules/Ads/KBB.Ads.SimilarVehicleSponsorship.js", 376 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/tests/files.js", 377 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/obj/Release/Package/PackageTmp/tests/Pages/Vehicle/Path/PriceType/KBB.Page.Path.ConditionQuiz.js", 378 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Configuration/Vehicle/Ymm/Index/Default/Ads.js", 379 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Configuration/Vehicle/Ymm/Index/Default/Omniture.SimilarVehicle.js", 380 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/obj/Release/Package/PackageTmp/Scripts/Pages/Vehicle/MakeModel/KBB.Page.MakeModel.SimilarVehicles.js", 381 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Modules/KBB.SimilarVehicles.js", 382 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Configuration/Vehicle/Path/Common/CpoAwareness.Omniture.js", 383 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Pages/Vehicle/MakeModel/KBB.Page.MakeModel.SimilarVehicles.js", 384 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/obj/Release/Package/PackageTmp/Styles/OOCss/partials/combined-slp/_price-your-next-car.scss", 385 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/partials/company/_sitemap.scss", 386 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/tests/Utilities/Utils.AdsApi.js", 387 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/kbbtv/appletv/js/Presenter.js", 388 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/kbbtv/appletv/js/applicationAds.js", 389 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Utilities/Utils.AdsApi.js", 390 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Pages/KBB.Page.SnowfallWindow.js", 391 | "/D/Projects/Front-end/rest-apis/database.json", 392 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Common/KBB.NewRelic.js", 393 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/tests/modules/KBB.NewRelic.js", 394 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/js/NewRelic/NewRelic.js", 395 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Modules/Ads/KBB.Ads.Wallpaper.js", 396 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/.eslintignore", 397 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/package.json", 398 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/tests/fixtures/TestUtils.js", 399 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/pages-responsive/homepage/homepage/index/components/_hero-image.scss", 400 | "/D/Projects/Front-end/HTML CSS/html5-css3/public/sass/Partials/_base.scss", 401 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Areas/KBB.Trident.Web.Areas.Prototype/Scripts/Brightcove/videojs.ima3.min.js", 402 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Modules/Videos/KBB.Video.Brightcove.js", 403 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/tests/Pages/KBB.Page.SnowfallWindow.js", 404 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/partials/modules/video/_player-main.scss", 405 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/partials/modules/_kiosk.scss", 406 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/partials/shared/components/_snowfall-window.scss", 407 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/tests/Pages/KBB.Page.SnowfallWindowVideo.js", 408 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Pages/KBB.Page.SnowfallWindowVideo.js", 409 | "//test70.dev.kbb.com/kbb.com/Styles/OOCss/css/vehicle/category.css", 410 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/partials/modules/video/_player-base.scss", 411 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/partials/editorial/latestnews/_lastest-news-snowfall-window.scss", 412 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/partials/classifieds/search-results/_listings-snowfall-window.scss", 413 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/partials/modules/video/_player-sharing.scss", 414 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/pages-responsive/vehicle/category.scss", 415 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Scripts/Pages/Editorial/LatestNews/KBB.Page.Editorial.LatestNews.SnowfallWindowModule.js", 416 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/partials/shared/variables/_font-family.scss", 417 | "//test69.dev.kbb.com/kbb.com/Scripts/Pages/KBB.Page.SnowfallWindowVideo.js", 418 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/partials/modules/video/_player-endscreen.scss", 419 | "/D/Projects/KBB/kbbweb-trident/KBB.Trident.Web/Styles/OOCss/partials/shared/variables/_font-size.scss", 420 | "/C/Windows/System32/drivers/etc/hosts", 421 | "/C/Users/tannguyen/Desktop/1/ad.html", 422 | "/D/Projects/KBB/Ad-templates/Templates/2/Brightcove.txt", 423 | "/D/Projects/KBB/Ad-templates/Templates/1/Akamai.html", 424 | "/D/Projects/KBB/Ad-templates/Templates/1/Akamai.txt", 425 | "/D/Projects/KBB/Ad-templates/Templates/2/ad.html", 426 | "/D/Projects/KBB/Ad-templates/Templates/1/ad.html", 427 | "/D/Projects/Sublime projects config/Kbbweb.sublime-project", 428 | "/D/Projects/Front-end/Redux/Redux_Basic/dist/index.html", 429 | "/D/Projects/Front-end/Redux/Redux_Basic/index.js" 430 | ], 431 | "find": 432 | { 433 | "height": 40.0 434 | }, 435 | "find_in_files": 436 | { 437 | "height": 104.0, 438 | "where_history": 439 | [ 440 | "" 441 | ] 442 | }, 443 | "find_state": 444 | { 445 | "case_sensitive": false, 446 | "find_history": 447 | [ 448 | "console.log", 449 | "pair_data", 450 | "calculate_ticks_data", 451 | "/ 60", 452 | "calculate_ticks_data", 453 | "create_pair_data", 454 | "intervals", 455 | "async", 456 | "mute", 457 | "Object.assign", 458 | "addEventListener", 459 | "bot", 460 | "async", 461 | "handleIchimokuSignal", 462 | "calculateIchimoku", 463 | "shouldBuy", 464 | "calculateIchimoku", 465 | "createIchimokuElements", 466 | "calculatePrevGainLoss", 467 | "handleIchimokuSignal", 468 | "wait_time", 469 | "'BUY'", 470 | "BUY", 471 | "getFilteredResistences", 472 | "getResistences", 473 | "trackHourlyPrice", 474 | "get_candleSticks_for_BTC_pairs", 475 | " console.log", 476 | "console.log", 477 | "support", 478 | "Support", 479 | "get_candleSticks_for_BTC_pairs", 480 | "var ", 481 | "supports", 482 | "calculateRSI", 483 | "console.log", 484 | "createPairData" 485 | ], 486 | "highlight": true, 487 | "in_selection": false, 488 | "preserve_case": false, 489 | "regex": false, 490 | "replace_history": 491 | [ 492 | "resistence", 493 | "Resistence", 494 | "let", 495 | "resistences" 496 | ], 497 | "reverse": false, 498 | "show_context": true, 499 | "use_buffer2": true, 500 | "whole_word": false, 501 | "wrap": true 502 | }, 503 | "groups": 504 | [ 505 | { 506 | "selected": 0, 507 | "sheets": 508 | [ 509 | { 510 | "buffer": 0, 511 | "file": "pump_detector.js", 512 | "semi_transient": false, 513 | "settings": 514 | { 515 | "buffer_size": 7938, 516 | "regions": 517 | { 518 | }, 519 | "selection": 520 | [ 521 | [ 522 | 4894, 523 | 4894 524 | ] 525 | ], 526 | "settings": 527 | { 528 | "SL.64.region_keys": 529 | [ 530 | ], 531 | "syntax": "Packages/JavaScript/JavaScript.sublime-syntax" 532 | }, 533 | "translation.x": 0.0, 534 | "translation.y": 2100.0, 535 | "zoom_level": 1.0 536 | }, 537 | "stack_index": 0, 538 | "type": "text" 539 | }, 540 | { 541 | "buffer": 1, 542 | "semi_transient": false, 543 | "settings": 544 | { 545 | "buffer_size": 9890, 546 | "regions": 547 | { 548 | }, 549 | "selection": 550 | [ 551 | [ 552 | 5805, 553 | 5806 554 | ] 555 | ], 556 | "settings": 557 | { 558 | "auto_name": "", 559 | "syntax": "Packages/Text/Plain text.tmLanguage" 560 | }, 561 | "translation.x": 0.0, 562 | "translation.y": 4340.0, 563 | "zoom_level": 1.0 564 | }, 565 | "stack_index": 1, 566 | "type": "text" 567 | }, 568 | { 569 | "buffer": 2, 570 | "semi_transient": false, 571 | "settings": 572 | { 573 | "buffer_size": 1412, 574 | "regions": 575 | { 576 | }, 577 | "selection": 578 | [ 579 | [ 580 | 817, 581 | 830 582 | ] 583 | ], 584 | "settings": 585 | { 586 | "auto_name": "", 587 | "syntax": "Packages/Text/Plain text.tmLanguage" 588 | }, 589 | "translation.x": 0.0, 590 | "translation.y": 560.0, 591 | "zoom_level": 1.0 592 | }, 593 | "stack_index": 2, 594 | "type": "text" 595 | }, 596 | { 597 | "buffer": 3, 598 | "file": "ichi_cloud.js", 599 | "semi_transient": false, 600 | "settings": 601 | { 602 | "buffer_size": 16557, 603 | "regions": 604 | { 605 | }, 606 | "selection": 607 | [ 608 | [ 609 | 0, 610 | 0 611 | ] 612 | ], 613 | "settings": 614 | { 615 | "SL.65.region_keys": 616 | [ 617 | ], 618 | "syntax": "Packages/JavaScript/JavaScript.sublime-syntax" 619 | }, 620 | "translation.x": 0.0, 621 | "translation.y": 0.0, 622 | "zoom_level": 1.0 623 | }, 624 | "stack_index": 6, 625 | "type": "text" 626 | }, 627 | { 628 | "buffer": 4, 629 | "file": "indicators/ovb.js", 630 | "semi_transient": false, 631 | "settings": 632 | { 633 | "buffer_size": 903, 634 | "regions": 635 | { 636 | }, 637 | "selection": 638 | [ 639 | [ 640 | 399, 641 | 399 642 | ] 643 | ], 644 | "settings": 645 | { 646 | "SL.17.region_keys": 647 | [ 648 | ], 649 | "SL.21.region_keys": 650 | [ 651 | ], 652 | "SL.58.region_keys": 653 | [ 654 | ], 655 | "syntax": "Packages/JavaScript/JavaScript.sublime-syntax" 656 | }, 657 | "translation.x": 0.0, 658 | "translation.y": 0.0, 659 | "zoom_level": 1.0 660 | }, 661 | "stack_index": 5, 662 | "type": "text" 663 | }, 664 | { 665 | "buffer": 5, 666 | "file": "indicators/resistence.js", 667 | "semi_transient": false, 668 | "settings": 669 | { 670 | "buffer_size": 1856, 671 | "regions": 672 | { 673 | }, 674 | "selection": 675 | [ 676 | [ 677 | 830, 678 | 830 679 | ] 680 | ], 681 | "settings": 682 | { 683 | "SL.18.region_keys": 684 | [ 685 | ], 686 | "SL.59.region_keys": 687 | [ 688 | ], 689 | "syntax": "Packages/JavaScript/JavaScript.sublime-syntax" 690 | }, 691 | "translation.x": 0.0, 692 | "translation.y": 0.0, 693 | "zoom_level": 1.0 694 | }, 695 | "stack_index": 4, 696 | "type": "text" 697 | }, 698 | { 699 | "buffer": 6, 700 | "file": "indicators/rsi.js", 701 | "semi_transient": true, 702 | "settings": 703 | { 704 | "buffer_size": 2683, 705 | "regions": 706 | { 707 | }, 708 | "selection": 709 | [ 710 | [ 711 | 0, 712 | 0 713 | ] 714 | ], 715 | "settings": 716 | { 717 | "SL.70.region_keys": 718 | [ 719 | ], 720 | "syntax": "Packages/JavaScript/JavaScript.sublime-syntax" 721 | }, 722 | "translation.x": 0.0, 723 | "translation.y": 700.0, 724 | "zoom_level": 1.0 725 | }, 726 | "stack_index": 3, 727 | "type": "text" 728 | }, 729 | { 730 | "buffer": 7, 731 | "semi_transient": false, 732 | "settings": 733 | { 734 | "buffer_size": 62336, 735 | "regions": 736 | { 737 | }, 738 | "selection": 739 | [ 740 | [ 741 | 58811, 742 | 58811 743 | ] 744 | ], 745 | "settings": 746 | { 747 | "auto_name": "if (brightcove == undefined) { var brightcove = {}", 748 | "syntax": "Packages/Text/Plain text.tmLanguage" 749 | }, 750 | "translation.x": 0.0, 751 | "translation.y": 3647.0, 752 | "zoom_level": 1.0 753 | }, 754 | "stack_index": 7, 755 | "type": "text" 756 | } 757 | ] 758 | } 759 | ], 760 | "incremental_find": 761 | { 762 | "height": 22.0 763 | }, 764 | "input": 765 | { 766 | "height": 35.0 767 | }, 768 | "layout": 769 | { 770 | "cells": 771 | [ 772 | [ 773 | 0, 774 | 0, 775 | 1, 776 | 1 777 | ] 778 | ], 779 | "cols": 780 | [ 781 | 0.0, 782 | 1.0 783 | ], 784 | "rows": 785 | [ 786 | 0.0, 787 | 1.0 788 | ] 789 | }, 790 | "menu_visible": true, 791 | "output.SublimeLinter": 792 | { 793 | "height": 26.0 794 | }, 795 | "output.SublimeLinter Messages": 796 | { 797 | "height": 26.0 798 | }, 799 | "output.exec": 800 | { 801 | "height": 96.0 802 | }, 803 | "output.find_results": 804 | { 805 | "height": 0.0 806 | }, 807 | "pinned_build_system": "", 808 | "project": "Binance_bot.sublime-project", 809 | "replace": 810 | { 811 | "height": 42.0 812 | }, 813 | "save_all_on_build": true, 814 | "select_file": 815 | { 816 | "height": 0.0, 817 | "last_filter": "", 818 | "selected_items": 819 | [ 820 | [ 821 | "kbb.ads.budgetcalculator", 822 | "tests\\modules\\ads\\KBB.Ads.BudgetCalculator.js" 823 | ], 824 | [ 825 | "owner18", 826 | "Scripts\\Configuration\\Vehicle\\Hub\\Index\\Trade-In-Sell\\Ads.Owners18.js" 827 | ], 828 | [ 829 | "categorycent", 830 | "Scripts\\Pages\\Vehicle\\CategoryCenter\\KBB.Page.Vehicle.CategoryCenter.js" 831 | ], 832 | [ 833 | "vehiclespon", 834 | "Areas\\KBB.Trident.Web.Areas.Vehicle\\Scripts\\Modules\\Ads\\KBB.Ads.SimilarVehicleSponsorship.js" 835 | ], 836 | [ 837 | "tradeup", 838 | "Scripts\\Pages\\Vehicle\\Hub\\Index\\Owners18\\Page.TradeUpModule.js" 839 | ], 840 | [ 841 | "kbb.js", 842 | "js\\kbb\\KBB.js" 843 | ], 844 | [ 845 | "budgetcalu", 846 | "Areas\\KBB.Trident.Web.Areas.Vehicle\\Scripts\\Modules\\Ads\\KBB.Ads.BudgetCalculator.js" 847 | ], 848 | [ 849 | "colors.scss", 850 | "Styles\\OOCss\\partials\\shared\\variables\\_colors.scss" 851 | ], 852 | [ 853 | "budgetcalculator.sc", 854 | "Styles\\OOCss\\partials\\owners18\\_owners-budget-calculator.scss" 855 | ], 856 | [ 857 | "budgetcalculator.scs", 858 | "Styles\\OOCss\\partials\\ads\\_monthly-budget-calculator.scss" 859 | ], 860 | [ 861 | "budgetcalculator.js", 862 | "Areas\\KBB.Trident.Web.Areas.Vehicle\\Scripts\\Modules\\Ads\\KBB.Ads.BudgetCalculator.js" 863 | ], 864 | [ 865 | "ads.js", 866 | "js\\ads.js" 867 | ], 868 | [ 869 | "kbb.ads.similarvehiclesponsorship", 870 | "tests\\modules\\ads\\KBB.Ads.SimilarVehicleSponsorship.js" 871 | ], 872 | [ 873 | "tests/files.js", 874 | "tests\\files.js" 875 | ], 876 | [ 877 | "default/omniture.similarvehicle.js", 878 | "Scripts\\Configuration\\Vehicle\\Ymm\\Index\\Default\\Omniture.SimilarVehicle.js" 879 | ], 880 | [ 881 | "ymm/index/default/ads.js", 882 | "Scripts\\Configuration\\Vehicle\\Ymm\\Index\\Default\\Ads.js" 883 | ], 884 | [ 885 | "configuration/vehicle/ymm/index/default/ads.js", 886 | "Scripts\\Configuration\\Vehicle\\Ymm\\Index\\Default\\Ads.js" 887 | ], 888 | [ 889 | "simila", 890 | "Styles\\OOCss\\partials\\mmymm\\components\\_similar-vehicles-sponsorship.scss" 891 | ], 892 | [ 893 | "similarvehicle", 894 | "Scripts\\Modules\\KBB.SimilarVehicles.js" 895 | ], 896 | [ 897 | "similarvehicles", 898 | "Scripts\\Modules\\Ads\\KBB.Ads.SimilarVehicleSponsorship.js" 899 | ], 900 | [ 901 | "kbb.page.makemodel.similarvehicles", 902 | "Scripts\\Pages\\Vehicle\\MakeModel\\KBB.Page.MakeModel.SimilarVehicles.js" 903 | ], 904 | [ 905 | "/ymm/index/default/omniture.similarvehicle.js", 906 | "Scripts\\Configuration\\Vehicle\\Ymm\\Index\\Default\\Omniture.SimilarVehicle.js" 907 | ], 908 | [ 909 | "cpoawareness.omniture.js", 910 | "Scripts\\Configuration\\Vehicle\\Path\\Common\\CpoAwareness.Omniture.js" 911 | ], 912 | [ 913 | "vehiclesponsor", 914 | "Scripts\\Modules\\Ads\\KBB.Ads.SimilarVehicleSponsorship.js" 915 | ], 916 | [ 917 | "similar-vehi", 918 | "Styles\\OOCss\\partials\\mmymm\\components\\_similar-vehicles-sponsorship.scss" 919 | ], 920 | [ 921 | "utils.adsapi.js", 922 | "tests\\Utilities\\Utils.AdsApi.js" 923 | ], 924 | [ 925 | "utils.ad", 926 | "tests\\Utilities\\Utils.AdsApi.js" 927 | ], 928 | [ 929 | "utilsap", 930 | "Scripts\\Utilities\\Utils.AdsApi.js" 931 | ], 932 | [ 933 | "snowfall", 934 | "Scripts\\Pages\\KBB.Page.SnowfallWindow.js" 935 | ], 936 | [ 937 | "files.", 938 | "tests\\files.js" 939 | ], 940 | [ 941 | "kbb.newrel", 942 | "tests\\modules\\KBB.NewRelic.js" 943 | ], 944 | [ 945 | "package", 946 | "package.json" 947 | ], 948 | [ 949 | "newr", 950 | "js\\NewRelic\\NewRelic.js" 951 | ], 952 | [ 953 | "kbb.new", 954 | "Scripts\\Common\\KBB.NewRelic.js" 955 | ], 956 | [ 957 | "kbb.ads.wallpaper.js", 958 | "tests\\modules\\ads\\KBB.Ads.Wallpaper.js" 959 | ], 960 | [ 961 | "packa", 962 | "package.json" 963 | ], 964 | [ 965 | "testutils", 966 | "tests\\fixtures\\TestUtils.js" 967 | ], 968 | [ 969 | "kbb.newre", 970 | "tests\\modules\\KBB.NewRelic.js" 971 | ], 972 | [ 973 | "kbb.newrelic", 974 | "Scripts\\Common\\KBB.NewRelic.js" 975 | ], 976 | [ 977 | "hero-image", 978 | "Styles\\OOCss\\pages-responsive\\homepage\\homepage\\index\\components\\_hero-image.scss" 979 | ], 980 | [ 981 | "brightcove", 982 | "Scripts\\Modules\\Videos\\KBB.Video.Brightcove.js" 983 | ], 984 | [ 985 | "video/", 986 | "Styles\\OOCss\\partials\\modules\\video\\_player-main.scss" 987 | ], 988 | [ 989 | "kbb.page.snowfallwindow.js", 990 | "tests\\Pages\\KBB.Page.SnowfallWindow.js" 991 | ], 992 | [ 993 | "snowfallvideo", 994 | "tests\\Pages\\KBB.Page.SnowfallWindowVideo.js" 995 | ], 996 | [ 997 | "_kiosk.scss", 998 | "Styles\\OOCss\\partials\\modules\\_kiosk.scss" 999 | ], 1000 | [ 1001 | "lasted", 1002 | "Styles\\OOCss\\partials\\editorial\\latestnews\\_lastest-news-snowfall-window.scss" 1003 | ], 1004 | [ 1005 | "player-base", 1006 | "Styles\\OOCss\\partials\\modules\\video\\_player-base.scss" 1007 | ], 1008 | [ 1009 | "snowfallwindowmodule", 1010 | "Scripts\\Pages\\Editorial\\LatestNews\\KBB.Page.Editorial.LatestNews.SnowfallWindowModule.js" 1011 | ], 1012 | [ 1013 | "snowfallwin", 1014 | "Scripts\\Pages\\KBB.Page.SnowfallWindowVideo.js" 1015 | ], 1016 | [ 1017 | "kbb.page.vehicle.categorycenter.js", 1018 | "Scripts\\Pages\\Vehicle\\CategoryCenter\\KBB.Page.Vehicle.CategoryCenter.js" 1019 | ] 1020 | ], 1021 | "width": 0.0 1022 | }, 1023 | "select_project": 1024 | { 1025 | "height": 0.0, 1026 | "last_filter": "", 1027 | "selected_items": 1028 | [ 1029 | ], 1030 | "width": 0.0 1031 | }, 1032 | "select_symbol": 1033 | { 1034 | "height": 0.0, 1035 | "last_filter": "", 1036 | "selected_items": 1037 | [ 1038 | ], 1039 | "width": 0.0 1040 | }, 1041 | "selected_group": 0, 1042 | "settings": 1043 | { 1044 | }, 1045 | "show_minimap": true, 1046 | "show_open_files": false, 1047 | "show_tabs": true, 1048 | "side_bar_visible": true, 1049 | "side_bar_width": 258.0, 1050 | "status_bar_visible": true, 1051 | "template_settings": 1052 | { 1053 | } 1054 | } 1055 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 JSapp.me 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node index.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | 4 |

5 | 6 |

Node-Binance-Trader

7 | 8 |

9 | 10 | 11 | 12 |

13 | 14 |

An efficient cryptocurrency trading bot for Binance using Node.js 💸

15 | 16 | # Why Node-Binance-Trader? 🤔 17 | 18 | My name is Herve Fulchiron, I’m a passionate full stack JS engineer, and I also trade cryptocurrencies on a weekly basis. I mostly use Binance for my altcoins trading. The high volatility of the crypto market makes it an emotional rollercoster that requires high awareness and constant attention. This demands a lot of time to monitor always more trading pairs and more data to process. Like most of the people I used to trade manually via the Binance website. I was slow to execute and beating the market gets more difficult every days. It was time to speed up the testing of my strategies and to automate my trading operations. I tried Gekko, it's a nice tool specially for back testing but I wanted something much more simplier, faster, dedicated to Binance, so I could test my strategies as fast and as easy as possible live on the crypto market. I wrote this one simple but efficient node.js script to: 19 | 20 | * monitor in real time all the BTC trading pairs on Binance 21 | * apply one or multiple strategies to the live market 22 | * record live performances of those strategies over time 23 | * auto trade the most successful strategies 24 | 25 | # What is Node-Binance-Trader? 📡 26 | 27 | Today NBT is an experimental node.js script that asynchronosly run api calls and listen to websockets connected to binance. It can track the 100 btc pairs currently available and apply the predefined strategies for testing purposes. This way I can tell if a strategy is working under the actual market conditions very quickly. The buy and sell signals and their performance can be emailed or even notified via a sound. This is still a work in progress so please take it with a grain of salt. I keep optimizing it so it uses as less cpu as possible. 28 | 29 | # Installation 📦 30 | 31 | ``` 32 | git clone https://github.com/jsappme/node-binance-trader 33 | ``` 34 | 35 | # Configuration 🔑 36 | 37 | 1. Signup Binance ( Referral url: https://www.binance.com/?ref=10177791 ) 38 | 2. Enable Two-factor Authentication 39 | 3. Go API Center, https://www.binance.com/userCenter/createApi.html 40 | 4. Create New Key 41 | [✓] Read Info [✓] Enable Trading [X] Enable Withdrawals 42 | 5. Copy the API key and secret to index.js 43 | 44 | # Usage ⚡️ 45 | 46 | ``` 47 | yarn 48 | yarn start 49 | ``` 50 | or 51 | 52 | ``` 53 | npm install 54 | npm run start 55 | ``` 56 | 57 | # Customization 🛠️ 58 | 59 | To add new strategies to the script, you need to add your own buying/selling conditions by adding new functions to the strategies section in the code. 60 | 61 | In the following example, I wrote two strats, one is buying when the Moving Averages are showing the beginning of an hourly and minutely uptrend and the second one is buying when the asking and bidding first prices are very close to each others. This example is for education purpose to show you the use of the different types of data available as of today. 62 | 63 | ``` 64 | ////////////////////////////////////////////////////////////////////////////////// 65 | // that's where you define your buying conditions: 66 | ////////////////////////////////////////////////////////////////////////////////// 67 | 68 | buying_up_trend = (pair) => { 69 | const ma_s = 3 70 | const ma_m = 13 71 | const ma_l = 99 72 | const ma_h_s = hourly_prices[pair].slice(0,ma_s).reduce((sum, price) => (sum + parseFloat(price)), 0) / parseFloat(hourly_prices[pair].slice(0,ma_s).length) 73 | const ma_h_m = hourly_prices[pair].slice(0,ma_m).reduce((sum, price) => (sum + parseFloat(price)), 0) / parseFloat(hourly_prices[pair].slice(0,ma_m).length) 74 | const ma_h_l = hourly_prices[pair].slice(0,ma_l).reduce((sum, price) => (sum + parseFloat(price)), 0) / parseFloat(hourly_prices[pair].slice(0,ma_l).length) 75 | const ma_m_s = minute_prices[pair].slice(0,ma_s).reduce((sum, price) => (sum + parseFloat(price)), 0) / parseFloat(minute_prices[pair].slice(0,ma_s).length) 76 | const ma_m_m = minute_prices[pair].slice(0,ma_m).reduce((sum, price) => (sum + parseFloat(price)), 0) / parseFloat(minute_prices[pair].slice(0,ma_m).length) 77 | const ma_m_l = minute_prices[pair].slice(0,ma_l).reduce((sum, price) => (sum + parseFloat(price)), 0) / parseFloat(minute_prices[pair].slice(0,ma_l).length) 78 | if ( (ma_h_s >= ma_h_m) && (ma_h_m >= ma_h_l) && (ma_m_s >= ma_m_m) && (ma_m_m >= ma_m_l) ) { 79 | return "BUY" 80 | } 81 | else { 82 | return "SELL" 83 | } 84 | } 85 | 86 | buying_low_depth_diff = (pair) => { 87 | const max_ask_bid_ratio = 3.0 // depth_asks/depth_bids < max_ask_bid_ratio 88 | const min_depth_volume = 2.0 // btc 89 | const max_depth_diff = 0.003 // pourcent(ask-bid/bid) 90 | if ( (parseFloat(depth_bids[pair])>=(parseFloat(depth_asks[pair])*max_ask_bid_ratio)) 91 | && (parseFloat(depth_bids[pair])>=min_depth_volume) 92 | && (parseFloat(depth_diff[pair])<=parseFloat(max_depth_diff)) ) { 93 | return "BUY" 94 | } 95 | else { 96 | return "SELL" 97 | } 98 | } 99 | 100 | let strategies = [ 101 | { name: "UP_TREND", condition: buying_up_trend }, 102 | { name: "LOW_DEPTH_DIFF", condition: buying_low_depth_diff }, 103 | ] 104 | 105 | ////////////////////////////////////////////////////////////////////////////////// 106 | ``` 107 | 108 | # Roadmap 🚧 109 | 110 | * ✔️ All BTC Pair tracking. 111 | * ✔️ Live testing of 1 or n strats. 112 | * Auto Trading. 113 | * Add an AI/ML "brain" to the bot. 114 | 115 | 116 | # Disclaimer 📖 117 | 118 | ``` 119 | I am not responsible for anything done with this bot. 120 | You use it at your own risk. 121 | There are no warranties or guarantees expressed or implied. 122 | You assume all responsibility and liability. 123 | ``` 124 | 125 | # Final Notes 🙏 126 | 127 | Feel free to fork and add new pull request to this repo. 128 | If you have any questions/suggestions, or simply you need some help building your trading bot, or mining historical data or improving your strategies using the latest AI/ML algorithms, please feel free to contact me. 129 | 130 | Special thank you to Jon for his very helpful repo https://github.com/jaggedsoft/node-binance-api 131 | 132 | If this repo helped you in any way, you can always leave me a BNB tip at 0xf0c499c0accddd52d2f96d8afb6778be0659ee0c 133 | 134 | -------------------------------------------------------------------------------- /alert.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tannguyen248/nodejs-binance-trader-bot/b269d162bbeb0f6b4da65ba9b9c2343235d196c5/alert.mp3 -------------------------------------------------------------------------------- /ichi_cloud.js: -------------------------------------------------------------------------------- 1 | const binance = require('node-binance-api') 2 | const express = require('express') 3 | const path = require('path') 4 | var _ = require('lodash') 5 | var moment = require('moment') 6 | var numeral = require('numeral') 7 | var readline = require('readline') 8 | var fs = require('fs') 9 | const TelegramBot = require('node-telegram-bot-api'); 10 | // const play = require('audio-play') 11 | // const load = require('audio-loader') 12 | const nodemailer = require('nodemailer') 13 | const rsi = require('./indicators/rsi')(14); 14 | const ovb = require('./indicators/ovb'); 15 | const support = require('./indicators/support'); 16 | const resistence = require('./indicators/resistence'); 17 | 18 | ////////////////////////////////////////////////////////////////////////////////// 19 | 20 | // https://www.binance.com/restapipub.html 21 | const APIKEY = '7hxRkF3KTID2EOnnTPxEFUjXBQinBMoA8TBFVfFUuBsUgtnTe5Zo3NHtCqqtFO54' 22 | const APISECRET = 'wHGQ9BIKPieC1a82JtPVxnSpBC4MQO4h5F6yLQKc9mHRATz8MNOo9d9hKxFQsgf9' 23 | 24 | const wait_time = 1000 // ms 25 | const trading_fee = 0.1 // pourcent 26 | 27 | 28 | // API initialization // 29 | binance.options({ 30 | 'APIKEY': APIKEY, 31 | 'APISECRET': APISECRET, 32 | 'reconnect': true 33 | }); 34 | 35 | /////////////////////////////////////////////////////////////////////////////////// 36 | 37 | // replace the value below with the Telegram token you receive from @BotFather 38 | const token = '565941385:AAEnnyP1t54NMuKcHla2a2OhhUUWzqx5wwo'; 39 | 40 | // Create a bot that uses 'polling' to fetch new updates 41 | const bot = new TelegramBot(token, {polling: true}); 42 | 43 | const chatId = '-1001245808874'; 44 | 45 | /////////////////////////////////////////////////////////////////////////////////// 46 | 47 | let btc_price = 0 48 | 49 | let pairs = [] 50 | 51 | let depth_bids = {} 52 | let depth_asks = {} 53 | let depth_diff = {} 54 | 55 | let minute_prices = {} 56 | let hourly_prices = {} 57 | 58 | let tracked_pairs = [] 59 | let tracked_pair_status = []; 60 | let tracked_pairs_hourly = []; 61 | let total_pnl = {} 62 | let intervals = ['1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '6h', '8h', '12h', '1d', '3d', '1w', '1M']; 63 | 64 | ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 65 | //----ICHI CLOUD----// 66 | let tenkanSenPeriod = 20; 67 | let kijunSenPeriod = 60; 68 | let senkouSpanBPeriod = 120; 69 | let chikouSpanPeriod = 30; 70 | 71 | getAverage = (ticks, name) => { 72 | let highest = Math.max(...ticks.map(x => x.high)); 73 | let lowest = Math.min(...ticks.map(x => x.low)); 74 | 75 | return (highest + lowest) / 2.0; 76 | } 77 | 78 | getTenkanSen = (symbol) => { 79 | return getAverage(tracked_pairs[symbol].slice(0, tenkanSenPeriod), 'tenkan'); 80 | } 81 | 82 | getKijunSen = (symbol) => { 83 | return getAverage(tracked_pairs[symbol].slice(0, kijunSenPeriod), 'kijun'); 84 | } 85 | 86 | getSenkouSpanA = (symbol) => { 87 | let laggingTenkanSen = getAverage(tracked_pairs[symbol].slice(25, 25 + tenkanSenPeriod), 'tenkenlate'); 88 | let laggingKijunSen = getAverage(tracked_pairs[symbol].slice(25, 25 + kijunSenPeriod), 'kijunlate'); 89 | 90 | return (laggingTenkanSen + laggingKijunSen) / 2.0; 91 | } 92 | 93 | getSenkouSpanB = (symbol) => { 94 | return getAverage(tracked_pairs[symbol].slice(25, 25 + senkouSpanBPeriod)); 95 | } 96 | 97 | createIchimokuElements = (symbol) => { 98 | return { 99 | tenkanSen: getTenkanSen(symbol), // Conversion Line 100 | kijunSen: getKijunSen(symbol), // Base Line 101 | senkouSpanA: getSenkouSpanA(symbol), // Leading line 1 102 | senkouSpanB: getSenkouSpanB(symbol), // Leading line 2 103 | chikouSpan: tracked_pairs[symbol][0].price // Lagging line 104 | } 105 | } 106 | 107 | 108 | shouldBuy = (symbol, tenkanSen, kijunSen, senkouSpanA, senkouSpanB, chikouSpan) => { 109 | //console.log('shouldBuy') 110 | let chikouSpanPreviodPrice = tracked_pairs[symbol][chikouSpanPeriod - 1].price; 111 | let currentPrice = tracked_pairs[symbol][0].price; 112 | let calculatedRSI = rsi.calculateRSI(tracked_pairs[symbol][0]); 113 | let diffTenkanAndKijun = (tenkanSen - kijunSen) * 100.0 / kijunSen; 114 | let currentOVB = Math.round(tracked_pairs[symbol][0].ovb); 115 | var highestOVB = Math.max(...tracked_pairs[symbol].slice(0, 5).map(x => Math.round(x.ovb))); 116 | 117 | if (chikouSpan > chikouSpanPreviodPrice) { 118 | if (tenkanSen >= kijunSen && diffTenkanAndKijun < 1) { 119 | if (currentPrice > tenkanSen && currentPrice > kijunSen) { 120 | //console.log('percent', ((currentPrice - kijunSen) / kijunSen) * 100); 121 | if (((currentPrice - kijunSen) / kijunSen) * 100 < 4) { 122 | if (currentOVB === highestOVB) { 123 | if (calculatedRSI > 45 && calculatedRSI < 70) { 124 | return true; 125 | } 126 | } 127 | } 128 | } 129 | } 130 | } 131 | } 132 | 133 | shouldSell = (symbol, tenkanSen, kijunSen, senkouSpanA, senkouSpanB, chikouSpan) => { 134 | let chikouSpanPreviodPrice = tracked_pairs[symbol][chikouSpanPeriod - 1].price; 135 | let currentPrice = tracked_pairs[symbol][0].price; 136 | let diffKijunAndTenkan = ((kijunSen - tenkanSen) * 100.0) / tenkanSen; 137 | if (chikouSpan < chikouSpanPreviodPrice) { 138 | if (tenkanSen < kijunSen && diffKijunAndTenkan > 0.8) { 139 | return true; 140 | } 141 | } 142 | } 143 | 144 | shouldStopLoss = (symbol, tenkanSen, kijunSen, senkouSpanA, senkouSpanB, chikouSpan) => { 145 | let chikouSpanPreviodPrice = tracked_pairs[symbol][chikouSpanPeriod - 1].price; 146 | let currentPrice = tracked_pairs[symbol][0].price; 147 | if (chikouSpan < chikouSpanPreviodPrice) { 148 | if (tenkanSen < kijunSen) { 149 | if (currentPrice < senkouSpanA && currentPrice < senkouSpanB) { 150 | return true; 151 | } 152 | } 153 | } 154 | } 155 | 156 | calculateIchimoku = (symbol, tenkanSen, kijunSen, senkouSpanA, senkouSpanB, chikouSpan) => { 157 | if (shouldBuy(symbol, tenkanSen, kijunSen, senkouSpanA, senkouSpanB, chikouSpan)) { 158 | return 'BUY'; 159 | } 160 | 161 | if (shouldSell(symbol, tenkanSen, kijunSen, senkouSpanA, senkouSpanB, chikouSpan)) { 162 | return 'SELL'; 163 | } 164 | 165 | if (shouldStopLoss(symbol, tenkanSen, kijunSen, senkouSpanA, senkouSpanB, chikouSpan)) { 166 | return 'STOP_LOSS' 167 | } 168 | } 169 | 170 | 171 | //----END ICHI CLOUD----// 172 | 173 | 174 | ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 175 | 176 | 177 | console.log('------------ NBT starting -------------') 178 | 179 | async function run() { 180 | 181 | //if (sound_alert) load('./alert.mp3').then(play); 182 | // await sleep(2) 183 | 184 | console.log('------------------------------') 185 | console.log(' start get_BTC_price') 186 | console.log('------------------------------') 187 | //btc_price = await get_BTC_price() 188 | console.log('------------------------------') 189 | //console.log('BTC price: $' + numeral(btc_price).format('0,0')) 190 | console.log('------------------------------') 191 | 192 | await sleep(2) 193 | 194 | console.log('------------------------------') 195 | console.log(' get_BTC_pairs start') 196 | console.log('------------------------------') 197 | pairs = await get_BTC_pairs() 198 | //pairs.unshift('BTCUSDT') 199 | console.log('------------------------------') 200 | 201 | //pairs = pairs.slice(0, 1) //for debugging purpose 202 | //pairs = ['HSRBTC']; 203 | console.log("Total BTC pairs: " + pairs.length) 204 | console.log('------------------------------') 205 | 206 | await sleep(2) 207 | 208 | console.log('------------------------------') 209 | console.log(' run detector') 210 | console.log('------------------------------') 211 | await get_candleSticks_for_BTC_pairs(intervals[5], intervals[11]); 212 | } 213 | 214 | 215 | sleep = (x) => { 216 | return new Promise(resolve => { 217 | setTimeout(() => { resolve(true) }, x ) 218 | }); 219 | } 220 | 221 | get_BTC_price = () => { 222 | return new Promise(resolve => { 223 | binance.websockets.candlesticks(['BTCUSDT'], "15m", (candlesticks) => { 224 | let { e:eventType, E:eventTime, s:symbol, k:ticks } = candlesticks; 225 | let { o:open, h:high, l:low, c:close, v:volume, n:trades, i:interval, x:isFinal, q:quoteVolume, V:buyVolume, Q:quoteBuyVolume } = ticks; 226 | btc_price = close 227 | resolve(btc_price) 228 | }) 229 | }) 230 | } 231 | 232 | get_BTC_pairs = () => { 233 | return new Promise(resolve => { 234 | binance.exchangeInfo((error, data) => { 235 | if (error) { 236 | console.log( error ) 237 | resolve([]) 238 | } 239 | if (data) { 240 | console.log( data.symbols.length + " total pairs") 241 | resolve( data.symbols.filter(pair => pair.symbol.endsWith('BTC') || pair.symbol.endsWith('USDT')).map(pair=>pair.symbol) ) 242 | } 243 | }) 244 | }) 245 | } 246 | 247 | 248 | get_candleSticks_for_BTC_pairs = async (interval, longInterval) => { 249 | console.log(`Run on ${interval} chart`); 250 | for (var i = 0; i < pairs.length; i++) { 251 | await getPrevHourPrices(pairs[i], longInterval); 252 | 253 | await sleep(wait_time) 254 | 255 | await trackHourlyPrice(pairs[i], longInterval); 256 | 257 | await sleep(wait_time) 258 | 259 | await getPrevMinutePrices(pairs[i], interval); 260 | //await getPrevMinutePrices('GASBTC', interval); 261 | await sleep(wait_time) 262 | 263 | await trackPrice(pairs[i], interval); 264 | //await trackPrice('GASBTC', interval); 265 | await sleep(wait_time) 266 | } 267 | } 268 | 269 | getPrevMinutePrices = (pair, interval) => { 270 | return new Promise(resolve => { 271 | binance.candlesticks(pair, interval, (error, ticks, symbol) => { 272 | if (error) { 273 | console.log( pair + " getPrevMinutePrices ERROR " + error ) 274 | resolve(true) 275 | } 276 | 277 | if (ticks) { 278 | console.log(pair + ' got ' + interval + ' data') 279 | for (var i = 0; i < ticks.length; i++) { 280 | let [time, open, high, low, close, volume, closeTime, assetVolume, trades, buyBaseVolume, buyAssetVolume, ignored] = ticks[i] 281 | let data = createPairData(time, parseFloat(open), parseFloat(close), parseFloat(volume), parseFloat(buyBaseVolume), parseFloat(high), parseFloat(low)); 282 | 283 | if (!tracked_pairs[symbol]) { 284 | // for 1st element 285 | tracked_pairs[symbol] = []; 286 | tracked_pairs[symbol].unshift(data); 287 | } else { 288 | tracked_pairs[symbol].unshift(data); 289 | } 290 | } 291 | 292 | if (tracked_pairs[symbol] && tracked_pairs[symbol].length >= 300) { 293 | tracked_pairs[symbol] = rsi.calculatePrevGainLoss(tracked_pairs[symbol]); 294 | tracked_pairs[symbol] = ovb.calculatePreOVB(tracked_pairs[symbol]); 295 | handleIchimokuSignal(symbol); 296 | } else { 297 | console.log(pair + ' is empty on ' + interval); 298 | } 299 | 300 | resolve(true) 301 | } 302 | }, {limit: 300}) 303 | }) 304 | } 305 | 306 | trackPrice = (pair, interval) => { 307 | return new Promise(resolve => { 308 | binance.websockets.candlesticks(pair, interval, (candlesticks) => { 309 | let { e:eventType, E:eventTime, s:symbol, k:ticks } = candlesticks; 310 | let { o:open, h:high, l:low, c:close, v:volume, n:trades, i:interval, x:isFinal, q:quoteVolume, V:buyBaseVolume, Q:quoteBuyVolume } = ticks; 311 | 312 | if (isFinal) { 313 | let data = createPairData(Date.now(), parseFloat(open), parseFloat(close), volume, buyBaseVolume, parseFloat(high), parseFloat(low)); 314 | 315 | if (tracked_pairs[symbol] && tracked_pairs[symbol].length >= 300) { 316 | tracked_pairs[symbol].pop(); 317 | tracked_pairs[symbol].unshift(data); 318 | 319 | let gainLoss = rsi.calculateCurrentGainLoss(tracked_pairs[symbol][1], tracked_pairs[symbol][0]) 320 | tracked_pairs[symbol][0].avgGain = gainLoss.avgGain; 321 | tracked_pairs[symbol][0].avgLoss = gainLoss.avgLoss; 322 | tracked_pairs[symbol][0].ovb = ovb.calculateOVB(tracked_pairs[symbol][1], tracked_pairs[symbol][0]); 323 | 324 | handleIchimokuSignal(symbol); 325 | } else { 326 | //getPrevMinutePrices(pair, interval); 327 | } 328 | } 329 | 330 | resolve(true); 331 | }); 332 | }) 333 | } 334 | 335 | getPrevHourPrices = (pair, interval) => { 336 | return new Promise(resolve => { 337 | binance.candlesticks(pair, interval, (error, ticks, symbol) => { 338 | if (error) { 339 | console.log( pair + " getPrevHourPrices ERROR " + error ) 340 | resolve(true) 341 | } 342 | 343 | if (ticks) { 344 | console.log(pair + ' got ' + interval + ' data') 345 | for (var i = 0; i < ticks.length; i++) { 346 | let [time, open, high, low, close, volume, closeTime, assetVolume, trades, buyBaseVolume, buyAssetVolume, ignored] = ticks[i] 347 | let data = createPairData(time, parseFloat(open), parseFloat(close), parseFloat(volume), parseFloat(buyBaseVolume), parseFloat(high), parseFloat(low)); 348 | 349 | if (!tracked_pairs_hourly[symbol]) { 350 | // for 1st element 351 | tracked_pairs_hourly[symbol] = []; 352 | tracked_pairs_hourly[symbol].unshift(data); 353 | } else { 354 | tracked_pairs_hourly[symbol].unshift(data); 355 | } 356 | } 357 | 358 | if (tracked_pairs_hourly[symbol] && tracked_pairs_hourly[symbol].length > 0) { 359 | //tracked_pairs_hourly[symbol] = rsi.calculatePrevGainLoss(tracked_pairs_hourly[symbol]); 360 | //tracked_pairs_hourly[symbol] = ovb.calculatePreOVB(tracked_pairs_hourly[symbol]); 361 | } else { 362 | console.log(pair + ' is empty on ' + interval); 363 | } 364 | 365 | 366 | resolve(true) 367 | } 368 | }, {limit: 300}) 369 | }) 370 | } 371 | 372 | trackHourlyPrice = (pair, interval) => { 373 | return new Promise(resolve => { 374 | binance.websockets.candlesticks(pair, interval, (candlesticks) => { 375 | let { e:eventType, E:eventTime, s:symbol, k:ticks } = candlesticks; 376 | let { o:open, h:high, l:low, c:close, v:volume, n:trades, i:interval, x:isFinal, q:quoteVolume, V:buyBaseVolume, Q:quoteBuyVolume } = ticks; 377 | 378 | if (isFinal) { 379 | let data = createPairData(Date.now(), parseFloat(open), parseFloat(close), volume, buyBaseVolume, parseFloat(high), parseFloat(low)); 380 | 381 | if (tracked_pairs_hourly[symbol] && tracked_pairs_hourly[symbol].length > 0) { 382 | tracked_pairs_hourly[symbol].pop(); 383 | tracked_pairs_hourly[symbol].unshift(data); 384 | 385 | //let gainLoss = rsi.calculateCurrentGainLoss(tracked_pairs_hourly[symbol][1], tracked_pairs_hourly[symbol][0]) 386 | //tracked_pairs_hourly[symbol][0].avgGain = gainLoss.avgGain; 387 | //tracked_pairs_hourly[symbol][0].avgLoss = gainLoss.avgLoss; 388 | //tracked_pairs_hourly[symbol][0].ovb = ovb.calculateOVB(tracked_pairs_hourly[symbol][1], tracked_pairs_hourly[symbol][0]); 389 | } else { 390 | getPrevHourPrices(pair, interval); 391 | } 392 | 393 | } 394 | 395 | resolve(true); 396 | }); 397 | }) 398 | } 399 | 400 | 401 | handleIchimokuSignal = (symbol) => { 402 | let chimokuElements = createIchimokuElements(symbol); 403 | var message = calculateIchimoku(symbol, chimokuElements.tenkanSen, chimokuElements.kijunSen, chimokuElements.senkouSpanA, chimokuElements.senkouSpanB, chimokuElements.chikouSpan); 404 | let extraMessage = ''; 405 | 406 | if (message) { 407 | if (tracked_pair_status[symbol] != message) { 408 | tracked_pair_status[symbol] = message; 409 | var a = new Date(); 410 | 411 | if (message === 'BUY' && tracked_pairs_hourly[symbol]) { 412 | let supports = support.getFilteredSupports(tracked_pairs_hourly[symbol], 8, 21); 413 | let resistences = resistence.getFilteredResistences(tracked_pairs_hourly[symbol], 8, 21); 414 | 415 | extraMessage = `TARGET (Sell BEFORE): ${resistences.slice(0, 5).join(', ')} \n` + 416 | `STOP LOSS (Sell BELOW): ${supports.slice(0, 5).join(', ')} \n`; 417 | } 418 | 419 | message = `${message} ${symbol} at ${chimokuElements.chikouSpan}-${chimokuElements.kijunSen} \n` + 420 | extraMessage + 421 | `${a.getDate()}/${a.getMonth()}/${a.getFullYear()} ${a.getHours()}:${a.getMinutes()} \n` 422 | 423 | console.log(message); 424 | bot.sendMessage(chatId, message); 425 | } 426 | } 427 | } 428 | 429 | createPairData = (time, open, close, volume, buyBaseVolume, high, low) => { 430 | return { 431 | time, 432 | price: close, 433 | high, 434 | low, 435 | volume, 436 | buyVolume: buyBaseVolume, 437 | close, 438 | open 439 | } 440 | } 441 | 442 | run() 443 | 444 | const app = express() 445 | app.get('/', (req, res) => res.send(tracked_pairs)) 446 | app.listen(9784, () => console.log('NBT api accessable on port 80')) 447 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* ============================================================ 2 | * node-binance-trader 3 | * https://github.com/jsappme/node-binance-trader 4 | * ============================================================ 5 | * Copyright 2018, Herve Fulchiron - contact@jsapp.me 6 | * Released under the MIT License 7 | * ============================================================ */ 8 | 9 | const binance = require('node-binance-api') 10 | const express = require('express') 11 | const path = require('path') 12 | var _ = require('lodash') 13 | var moment = require('moment') 14 | var numeral = require('numeral') 15 | var readline = require('readline') 16 | var fs = require('fs') 17 | // const play = require('audio-play') 18 | // const load = require('audio-loader') 19 | const nodemailer = require('nodemailer') 20 | 21 | ////////////////////////////////////////////////////////////////////////////////// 22 | 23 | // https://www.binance.com/restapipub.html 24 | const APIKEY = '7hxRkF3KTID2EOnnTPxEFUjXBQinBMoA8TBFVfFUuBsUgtnTe5Zo3NHtCqqtFO54' 25 | const APISECRET = 'wHGQ9BIKPieC1a82JtPVxnSpBC4MQO4h5F6yLQKc9mHRATz8MNOo9d9hKxFQsgf9' 26 | 27 | const tracked_max = 10 28 | const depth_limit = 10 29 | const wait_time = 1000 // ms 30 | const trading_fee = 0.1 // pourcent 31 | 32 | const sound_alert = false 33 | 34 | // https://medium.com/@manojsinghnegi/sending-an-email-using-nodemailer-gmail-7cfa0712a799 35 | const send_email = false 36 | const gmail_address = 'tanbaobu02@gmail.com' 37 | const gmail_password = 'tanbaobu' 38 | const gmailEmail = encodeURIComponent(gmail_address); 39 | const gmailPassword = encodeURIComponent(gmail_password); 40 | const mailTransport = nodemailer.createTransport(`smtps://${gmailEmail}:${gmailPassword}@smtp.gmail.com`); 41 | 42 | ////////////////////////////////////////////////////////////////////////////////// 43 | 44 | let btc_price = 0 45 | 46 | let pairs = [] 47 | 48 | let depth_bids = {} 49 | let depth_asks = {} 50 | let depth_diff = {} 51 | 52 | let minute_prices = {} 53 | let hourly_prices = {} 54 | 55 | let tracked_pairs = [] 56 | let tracked_data = {} 57 | let total_pnl = {} 58 | 59 | ////////////////////////////////////////////////////////////////////////////////// 60 | // that's where you define your buying conditions: 61 | ////////////////////////////////////////////////////////////////////////////////// 62 | 63 | buying_up_trend = (pair) => { 64 | const ma_s = 3 65 | const ma_m = 13 66 | const ma_l = 99 67 | const ma_h_s = hourly_prices[pair].slice(0,ma_s).reduce((sum, price) => (sum + parseFloat(price)), 0) / parseFloat(hourly_prices[pair].slice(0,ma_s).length) 68 | const ma_h_m = hourly_prices[pair].slice(0,ma_m).reduce((sum, price) => (sum + parseFloat(price)), 0) / parseFloat(hourly_prices[pair].slice(0,ma_m).length) 69 | const ma_h_l = hourly_prices[pair].slice(0,ma_l).reduce((sum, price) => (sum + parseFloat(price)), 0) / parseFloat(hourly_prices[pair].slice(0,ma_l).length) 70 | const ma_m_s = minute_prices[pair].slice(0,ma_s).reduce((sum, price) => (sum + parseFloat(price)), 0) / parseFloat(minute_prices[pair].slice(0,ma_s).length) 71 | const ma_m_m = minute_prices[pair].slice(0,ma_m).reduce((sum, price) => (sum + parseFloat(price)), 0) / parseFloat(minute_prices[pair].slice(0,ma_m).length) 72 | const ma_m_l = minute_prices[pair].slice(0,ma_l).reduce((sum, price) => (sum + parseFloat(price)), 0) / parseFloat(minute_prices[pair].slice(0,ma_l).length) 73 | if ( (ma_h_s >= ma_h_m) && (ma_h_m >= ma_h_l) && (ma_m_s >= ma_m_m) && (ma_m_m >= ma_m_l) ) { 74 | return "BUY" 75 | } 76 | else { 77 | return "SELL" 78 | } 79 | } 80 | 81 | buying_low_depth_diff = (pair) => { 82 | const max_ask_bid_ratio = 3.0 // depth_asks/depth_bids < max_ask_bid_ratio 83 | const min_depth_volume = 2.0 // btc 84 | const max_depth_diff = 0.003 // pourcent(ask-bid/bid) 85 | if ( (parseFloat(depth_bids[pair])>=(parseFloat(depth_asks[pair])*max_ask_bid_ratio)) 86 | && (parseFloat(depth_bids[pair])>=min_depth_volume) 87 | && (parseFloat(depth_diff[pair])<=parseFloat(max_depth_diff)) ) { 88 | return "BUY" 89 | } 90 | else { 91 | return "SELL" 92 | } 93 | } 94 | 95 | let strategies = [ 96 | { name: "UP_TREND", condition: buying_up_trend }, 97 | { name: "LOW_DEPTH_DIFF", condition: buying_low_depth_diff }, 98 | ] 99 | 100 | ////////////////////////////////////////////////////////////////////////////////// 101 | 102 | // API initialization // 103 | binance.options({ 'APIKEY': APIKEY, 'APISECRET': APISECRET, 'reconnect': true }); 104 | 105 | console.log('------------ NBT starting -------------') 106 | 107 | async function run() { 108 | 109 | //if (sound_alert) load('./alert.mp3').then(play); 110 | await sleep(2) 111 | 112 | console.log('------------------------------') 113 | console.log(' start get_BTC_price') 114 | console.log('------------------------------') 115 | btc_price = await get_BTC_price() 116 | console.log('------------------------------') 117 | console.log('BTC price: $' + numeral(btc_price).format('0,0')) 118 | console.log('------------------------------') 119 | 120 | await sleep(2) 121 | 122 | console.log('------------------------------') 123 | console.log(' get_BTC_pairs start') 124 | console.log('------------------------------') 125 | pairs = await get_BTC_pairs() 126 | console.log('------------------------------') 127 | //pairs = pairs.slice(0, tracked_max) //for debugging purpose 128 | console.log("Total BTC pairs: " + pairs.length) 129 | console.log('------------------------------') 130 | 131 | await sleep(2) 132 | 133 | console.log('------------------------------') 134 | console.log(' trackDepthData start') 135 | console.log('------------------------------') 136 | await trackDepthData() 137 | console.log('------------------------------') 138 | 139 | await sleep(2) 140 | 141 | console.log('------------------------------') 142 | console.log(' getHourlyPrevPrices start') 143 | console.log('------------------------------') 144 | await getHourlyPrevPrices() 145 | console.log('------------------------------') 146 | 147 | await sleep(2) 148 | 149 | console.log('------------------------------') 150 | console.log(' trackMinutePrices start') 151 | console.log('------------------------------') 152 | await trackMinutePrices() 153 | console.log('------------------------------') 154 | 155 | console.log('------------ we are ready to track all strategies -------------') 156 | //if (sound_alert) load('./alert.mp3').then(play) 157 | } 158 | 159 | sleep = (x) => { 160 | return new Promise(resolve => { 161 | setTimeout(() => { resolve(true) }, x ) 162 | }); 163 | } 164 | 165 | get_BTC_price = () => { 166 | return new Promise(resolve => { 167 | binance.websockets.candlesticks(['BTCUSDT'], "1m", (candlesticks) => { 168 | let { e:eventType, E:eventTime, s:symbol, k:ticks } = candlesticks; 169 | let { o:open, h:high, l:low, c:close, v:volume, n:trades, i:interval, x:isFinal, q:quoteVolume, V:buyVolume, Q:quoteBuyVolume } = ticks; 170 | btc_price = close 171 | resolve(btc_price) 172 | }) 173 | }) 174 | } 175 | 176 | get_BTC_pairs = () => { 177 | return new Promise(resolve => { 178 | binance.exchangeInfo((error, data) => { 179 | if (error) { 180 | console.log( error ) 181 | resolve([]) 182 | } 183 | if (data) { 184 | console.log( data.symbols.length + " total pairs") 185 | resolve( data.symbols.filter( pair => pair.symbol.endsWith('BTC') ).map(pair=>pair.symbol) ) 186 | } 187 | }) 188 | }) 189 | } 190 | 191 | trackDepthPair = (pair) => { 192 | return new Promise(resolve => { 193 | console.log( "> starting tracking depth data for " + pair ) 194 | binance.websockets.depthCache([pair], (symbol, depth) => { 195 | // Tu viet 196 | var bids = binance.sortBids(depth.bids, depth_limit) 197 | var asks = binance.sortAsks(depth.asks, depth_limit) 198 | depth_asks[pair] = _.sum(_.values(asks).slice(0,depth_limit))*binance.first(asks) 199 | depth_bids[pair] = _.sum(_.values(bids).slice(0,depth_limit))*binance.first(bids) 200 | depth_diff[pair] = 100 * (binance.first(asks) - binance.first(bids)) / (binance.first(bids)) 201 | }, depth_limit); 202 | resolve(true) 203 | }, depth_limit) 204 | } 205 | 206 | async function trackDepthData() { 207 | for (var i = 0, len = pairs.length; i < len; i++) { 208 | var pair = pairs[i] 209 | await trackDepthPair(pair) 210 | await sleep(wait_time) 211 | console.log( (i+1) + " > " + pair + " depth tracked a:" + numeral(depth_asks[pair]).format("0.00") + " / b:" + numeral(depth_bids[pair]).format("0.00") ) 212 | } 213 | } 214 | 215 | getPairHourlyPrices = (pair) => { 216 | return new Promise(resolve => { 217 | binance.candlesticks(pair, "1h", (error, ticks, symbol) => { 218 | if (error) { 219 | console.log( symbol + " > hourly prices ERROR " + error ) 220 | resolve(true) 221 | } 222 | if (ticks) { 223 | //console.log( symbol + " >>>>>> hourly prices retrieved " + ticks[ticks.length-1][4]) 224 | //var last_tick = ticks[ticks.length - 2]; 225 | //let [time, open, high, low, close, volume, closeTime, assetVolume, trades, buyBaseVolume, buyAssetVolume, ignored] = last_tick; 226 | hourly_prices[symbol] = _.drop(_.reverse( ticks.map( tick => (tick[4]) ) ) ) //we use close price 227 | console.log( symbol + " > " + hourly_prices[symbol].length + " hourly prices retrieved p:" + hourly_prices[symbol][0]) 228 | resolve(true) 229 | } 230 | }) 231 | }) 232 | } 233 | 234 | async function getHourlyPrevPrices() { 235 | for (var i = 0, len = pairs.length; i < len; i++) { 236 | await getPairHourlyPrices(pairs[i]) 237 | await sleep(wait_time) 238 | } 239 | } 240 | 241 | getPrevMinutePrices = (pair) => { 242 | return new Promise(resolve => { 243 | binance.candlesticks(pair, "1m", (error, ticks, symbol) => { 244 | if (error) { 245 | console.log( pair + " getPrevMinutePrices ERROR " + error ) 246 | resolve(true) 247 | } 248 | if (ticks) { 249 | minute_prices[symbol] = _.drop(_.reverse( ticks.map( tick => (tick[4]) ) ) ) 250 | resolve(true) 251 | } 252 | }) 253 | }) 254 | } 255 | 256 | async function trackMinutePrices() { 257 | for (var i = 0, len = pairs.length; i < len; i++) { 258 | await getPrevMinutePrices(pairs[i]) 259 | await sleep(wait_time) 260 | console.log( (i+1) + " > " + pairs[i] + " " + minute_prices[pairs[i]].length + " minute prices retrieved") 261 | await trackFutureMinutePrices(pairs[i]) 262 | await sleep(wait_time) 263 | console.log( (i+1) + " > " + pairs[i] + " future prices tracked.") 264 | } 265 | } 266 | 267 | trackFutureMinutePrices = (pair) => { 268 | return new Promise(resolve => { 269 | //console.log( "> starting tracking future prices for " + pair) 270 | binance.websockets.candlesticks([pair], "1m", (candlesticks) => { 271 | let { e:eventType, E:eventTime, s:symbol, k:ticks } = candlesticks 272 | let { o:open, h:high, l:low, c:close, v:volume, n:trades, i:interval, x:isFinal, q:quoteVolume, V:buyVolume, Q:quoteBuyVolume } = ticks 273 | strategies.map( strat => { 274 | var tracked_index = _.findIndex(tracked_pairs, (o) => { return ( (o.strat === strat.name) && (o.symbol === pair) )}) 275 | if ( tracked_index > -1) { 276 | tracked_data[symbol][strat.name].push({ 277 | date: moment().format('h:mm:ss a'), 278 | price: close, 279 | depth_asks: parseFloat(depth_asks[symbol]), 280 | depth_bids: parseFloat(depth_bids[symbol]), 281 | depth_diff: parseFloat(depth_diff[symbol]), 282 | }) 283 | } 284 | }) 285 | if (isFinal) { 286 | minute_prices[symbol].unshift(close) 287 | if ( (moment().format('m')%1 === 0) && (symbol==="ETHBTC") ) { 288 | console.log("------------------ " + moment().format('h:mm:ss') + " - new minute price added ------------------") 289 | } 290 | if ( moment().format('m')==='59' ){ 291 | hourly_prices[symbol].unshift(close) 292 | if (symbol==="ETHBTC") { console.log("------------------ " + moment().format('h:mm:ss') + " - new hourly price added ------------------") } 293 | } 294 | strategies.map( strat => { 295 | if (strat.condition(symbol)==="BUY") { 296 | var tracked_index = _.findIndex(tracked_pairs, (o) => { return ( (o.strat === strat.name) && (o.symbol === symbol) )}) 297 | if ( tracked_index === -1 ) { 298 | console.log(moment().format('h:mm:ss') + " :: " + symbol 299 | + " BUY :: " + strat.name + " :: " 300 | + " A:" + numeral(depth_asks[symbol]).format("0.00") 301 | + " B:" + numeral(depth_bids[symbol]).format("0.00") 302 | + " C:" + close 303 | + " D:%" + numeral(depth_diff[symbol]).format("0.000") 304 | + " https://www.binance.com/tradeDetail.html?symbol=" + symbol.slice(0, -3) + "_BTC") 305 | //if (sound_alert) load('./alert.mp3').then(play) 306 | if ( typeof tracked_data[symbol] === 'undefined' ) { 307 | tracked_data[symbol] = {} 308 | } 309 | tracked_data[symbol][strat.name] = [] 310 | tracked_pairs.push({ 311 | symbol: symbol, 312 | date: moment().format('MMMM Do YYYY, h:mm:ss a'), 313 | timestamp: Date.now(), 314 | price: close, 315 | volume: volume, 316 | usdvolume: volume*close*btc_price, 317 | strat: strat.name 318 | }) 319 | } 320 | } 321 | if (strat.condition(symbol)==="SELL") { 322 | var tracked_index = _.findIndex(tracked_pairs, (o) => { return ( (o.strat === strat.name) && (o.symbol === symbol) )}) 323 | if ( tracked_index > -1) { 324 | if ( typeof total_pnl[strat.name] === 'undefined' ) { 325 | total_pnl[strat.name] = [] 326 | } 327 | total_pnl[strat.name].unshift({ 328 | symbol: symbol, 329 | date: moment().format('MMMM Do YYYY, h:mm:ss a'), 330 | timestamp: Date.now(), 331 | pnl: ( 100.00*((parseFloat(close)/parseFloat(tracked_pairs[tracked_index].price))-1) - trading_fee*2.0), 332 | }) 333 | console.log(moment().format('h:mm:ss') + " :: " + symbol 334 | + " SELL :: " + strat.name + " :: " 335 | //+ " max:%" + numeral(100.00*(parseFloat((_.maxBy(tracked_data[symbol], 'price').price)/parseFloat(tracked_pairs[tracked_index].price))-1)).format("0.000") 336 | + " pnl:%" + numeral(100.00*((parseFloat(close)/parseFloat(tracked_pairs[tracked_index].price))-1)).format("0.000") 337 | + " tpnl:%" + numeral(_.sumBy(total_pnl[strat.name], 'pnl')).format("0.000") 338 | + " :: A:" + numeral(depth_asks[symbol]).format("0.00") 339 | + " B:" + numeral(depth_bids[symbol]).format("0.00") 340 | + " C:" + close 341 | + " D:%" + numeral(depth_diff[symbol]).format("0.000") 342 | + " https://www.binance.com/tradeDetail.html?symbol=" + symbol.slice(0, -3) + "_BTC") 343 | if (send_email) { 344 | const mailOptions = { 345 | from: '"My NBT Bot" ', 346 | to: gmail_address, 347 | subject: symbol + " SELL :: " + strat.name + " :: " 348 | + " pnl:%" + numeral(100.00*((parseFloat(close)/parseFloat(tracked_pairs[tracked_index].price))-1)).format("0.000") 349 | + " tpnl:%" + numeral(_.sumBy(total_pnl[strat.name], 'pnl')).format("0.000") 350 | + " :: A:" + numeral(depth_asks[symbol]).format("0.00") 351 | + " B:" + numeral(depth_bids[symbol]).format("0.00") 352 | + " C:" + close 353 | + " D:%" + numeral(depth_diff[symbol]).format("0.000"), 354 | text: "https://www.binance.com/tradeDetail.html?symbol=" + symbol.slice(0, -3) + "_BTC \n" 355 | + " ------------------------------------------ \n" 356 | + tracked_data[symbol][strat.name].map(item => JSON.stringify(item)+"\n") + "\n" 357 | }; 358 | mailTransport.sendMail(mailOptions).then(() => { 359 | }).catch(error => { 360 | console.error('There was an error while sending the email ... trying again...') 361 | setTimeout(() => { 362 | mailTransport.sendMail(mailOptions).then(() => { 363 | }).catch(error => { console.error('There was an error while sending the email: stopped trying') }) 364 | }, 2000 ) 365 | }); 366 | } 367 | //if (sound_alert) load('./alert.mp3').then(play) 368 | tracked_pairs = tracked_pairs.filter(o => !( (o.strat === strat.name) && (o.symbol === symbol) )) 369 | } 370 | } 371 | }) 372 | } 373 | }); 374 | resolve(true) 375 | }) 376 | } 377 | 378 | run() 379 | 380 | console.log("----------------------") 381 | 382 | const app = express() 383 | app.get('/', (req, res) => res.send(tracked_pairs)) 384 | app.listen(9876, () => console.log('NBT api accessable on port 80')) 385 | -------------------------------------------------------------------------------- /indicators/ichimoku.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tannguyen248/nodejs-binance-trader-bot/b269d162bbeb0f6b4da65ba9b9c2343235d196c5/indicators/ichimoku.js -------------------------------------------------------------------------------- /indicators/ovb.js: -------------------------------------------------------------------------------- 1 | 2 | class OVB { 3 | calculatePreOVB (ticks) { 4 | if (ticks && ticks.length > 0) { 5 | let data = ticks.map(x => ({...x})); 6 | data.reverse(); 7 | data[0].ovb = 0; 8 | 9 | for(let i = 1; i < data.length; i++) { 10 | data[i].ovb = this.calculateOVB(data[i - 1], data[i]); 11 | } 12 | 13 | return data.reverse(); 14 | } 15 | 16 | return []; 17 | } 18 | 19 | calculateOVB (preTick, currentTick) { 20 | if (preTick && currentTick) { 21 | let currentPrice = currentTick.price; 22 | let prePrice = preTick.price; 23 | 24 | if (currentPrice > prePrice) { 25 | return preTick.ovb + currentTick.volume; 26 | } 27 | 28 | if (currentPrice < prePrice) { 29 | return preTick.ovb - currentTick.volume; 30 | } 31 | 32 | return preTick.ovb; 33 | 34 | } 35 | 36 | return 0; 37 | } 38 | }; 39 | 40 | module.exports = new OVB(); 41 | -------------------------------------------------------------------------------- /indicators/resistence.js: -------------------------------------------------------------------------------- 1 | class Resistence { 2 | getResistences (ticks, period) { 3 | if (ticks && ticks.length > 0) { 4 | let resistences = []; 5 | let highs = ticks.map(x => x.high).sort(); 6 | for(let i = 0; i < ticks.length; i += period) { 7 | resistences.push(Math.max(...highs.slice(i, i + period))); 8 | } 9 | 10 | return resistences.filter(x => x > ticks[0].price); 11 | } 12 | 13 | return []; 14 | } 15 | 16 | getFilteredResistences (ticks, shortPeriod = 8, longPeriod = 21) { 17 | if (ticks && ticks.length > 0) { 18 | let shortResistences = this.getResistences(ticks, shortPeriod); 19 | let longResistences = this.getResistences(ticks, longPeriod); 20 | let resistences = []; 21 | let eliminateResistences = []; 22 | shortResistences.push(...longResistences); 23 | 24 | shortResistences.sort((a, b) => a-b); 25 | 26 | for (let i = 0; i < shortResistences.length - 1; i++) { 27 | for (let j = i + 1; j < shortResistences.length; j++) { 28 | let diffPrices = ((shortResistences[j] - shortResistences[i]) * 100) / shortResistences[i]; 29 | 30 | //console.log(shortResistences.length + ' ' + diffPrices + " " + i + " " + shortResistences[i] + " " + j + " " + shortResistences[j]); 31 | 32 | if (diffPrices < 1) { 33 | eliminateResistences.push(shortResistences[j]) 34 | } else { 35 | i = j - 1; 36 | break; 37 | } 38 | } 39 | } 40 | 41 | resistences = shortResistences.filter(x => eliminateResistences.indexOf(x) < 0); 42 | resistences = resistences.filter(x => ((x - ticks[0].price) * 100 / ticks[0].price) > 1); 43 | 44 | return resistences; 45 | } 46 | 47 | return []; 48 | } 49 | } 50 | 51 | module.exports = new Resistence() 52 | -------------------------------------------------------------------------------- /indicators/rsi.js: -------------------------------------------------------------------------------- 1 | class RSI { 2 | constructor (rsiPeriod = 14) { 3 | this.rsiPeriod = rsiPeriod; 4 | } 5 | 6 | calculateFirstGainLoss(ticks) { 7 | let sumGain = 0.0; 8 | let sumLoss = 0.0; 9 | 10 | for(let i = 1; i < ticks.length; i++) { 11 | let prePrice = ticks[i - 1].price; 12 | let currentPrice = ticks[i].price; 13 | let diff = currentPrice - prePrice; 14 | 15 | //console.log(`prePrice: ${prePrice} currentPrice: ${currentPrice}`); 16 | 17 | if (diff > 0) { 18 | sumGain = sumGain + Math.abs(diff); 19 | } else { 20 | sumLoss = sumLoss + Math.abs(diff); 21 | } 22 | } 23 | 24 | return { avgGain: sumGain / 14.0, avgLoss: sumLoss / 14.0 }; 25 | } 26 | 27 | calculateGainLoss(prePairData, currentPairData) { 28 | let diff = currentPairData.price - prePairData.price; 29 | let avgGain = 0.0; 30 | let avgLoss = 0.0; 31 | 32 | if (diff > 0) { 33 | avgGain = ((prePairData.avgGain * 13) + Math.abs(diff)) / 14; 34 | avgLoss = (prePairData.avgLoss * 13) / 14; 35 | } else { 36 | avgLoss = ((prePairData.avgLoss * 13) + Math.abs(diff)) / 14; 37 | avgGain = (prePairData.avgGain * 13) / 14; 38 | } 39 | 40 | return { avgGain: avgGain, avgLoss: avgLoss }; 41 | 42 | //console.log(`price: ${currentPairData.price} avgGain: ${currentPairData.avgGain} avgLoss: ${currentPairData.avgLoss}`) 43 | } 44 | 45 | calculatePrevGainLoss (ticks) { 46 | let rsiPeriod = this.rsiPeriod; 47 | let tempPairData = ticks.map(x => ({...x})); 48 | tempPairData.reverse(); 49 | 50 | let firstRSI = this.calculateFirstGainLoss(tempPairData.slice(0, rsiPeriod)); 51 | 52 | tempPairData[rsiPeriod].avgGain = firstRSI.avgGain; 53 | tempPairData[rsiPeriod].avgLoss = firstRSI.avgLoss; 54 | 55 | for (let i = rsiPeriod + 1; i < tempPairData.length; i++) { 56 | let currentPairData = tempPairData[i]; 57 | let prePairData = tempPairData[i - 1]; 58 | let gainLoss = this.calculateGainLoss(prePairData, currentPairData); 59 | currentPairData.avgGain = gainLoss.avgGain; 60 | currentPairData.avgLoss = gainLoss.avgLoss; 61 | } 62 | 63 | //console.log(tempPairData) 64 | 65 | return tempPairData.reverse(); 66 | } 67 | 68 | calculateCurrentGainLoss(prePairData, currentPairData) { 69 | return this.calculateGainLoss(prePairData, currentPairData); 70 | } 71 | 72 | calculateRSI(pairData) { 73 | if (pairData.avgLoss === 0 && pairData.avgGain !== 0) { 74 | return 100; 75 | } else if (pairData.avgLoss === 0) { 76 | return 0; 77 | } 78 | 79 | return 100.0 - (100.0 / (1.0 + pairData.avgGain / pairData.avgLoss)); 80 | } 81 | } 82 | 83 | module.exports = (rsiPeriod) => (new RSI(rsiPeriod)) 84 | -------------------------------------------------------------------------------- /indicators/support.js: -------------------------------------------------------------------------------- 1 | class Support { 2 | getSupports (ticks, period) { 3 | if (ticks && ticks.length > 0) { 4 | let supports = []; 5 | let lows = ticks.map(x => x.low).sort(); 6 | for(let i = 0; i < ticks.length; i += period) { 7 | supports.push(Math.min(...lows.slice(i, i + period))); 8 | } 9 | 10 | return supports; 11 | } 12 | 13 | return []; 14 | } 15 | 16 | getFilteredSupports (ticks, shortPeriod = 8, longPeriod = 21) { 17 | if (ticks && ticks.length) { 18 | let shortSupports = this.getSupports(ticks, shortPeriod); 19 | let longSupports = this.getSupports(ticks, longPeriod); 20 | let supports = []; 21 | let eliminateSupports = []; 22 | shortSupports.push(...longSupports); 23 | shortSupports = shortSupports.filter(x => x < ticks[0].price); 24 | 25 | shortSupports.sort((a, b) => b-a); 26 | 27 | for (let i = 0; i < shortSupports.length - 1; i++) { 28 | for (let j = i + 1; j < shortSupports.length; j++) { 29 | let diffPrices = ((shortSupports[i] - shortSupports[j]) * 100) / shortSupports[j]; 30 | 31 | if (diffPrices < 3) { 32 | eliminateSupports.push(shortSupports[j]) 33 | } else { 34 | i = j - 1; 35 | break; 36 | } 37 | } 38 | } 39 | 40 | supports = shortSupports.filter(x => eliminateSupports.indexOf(x) < 0 && ((ticks[0].price - x) * 100 / x) > 3 ); 41 | 42 | return supports; 43 | } 44 | 45 | return []; 46 | } 47 | } 48 | 49 | module.exports = new Support() 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-binance-trader", 3 | "version": "0.0.1", 4 | "description": "An efficient cryptocurrency trading bot for Binance using Node.js", 5 | "engines": { 6 | "node": "8.9.1" 7 | }, 8 | "main": "index.js", 9 | "scripts": { 10 | "start": "node index.js", 11 | "pump": "node pump_detector.js" 12 | }, 13 | "dependencies": { 14 | "audio-loader": "^1.0.3", 15 | "audio-play": "^2.2.0", 16 | "express": "^4.15.2", 17 | "lodash": "^4.17.4", 18 | "moment": "^2.20.1", 19 | "node-binance-api": "^0.4.4", 20 | "node-telegram-bot-api": "^0.30.0", 21 | "nodemailer": "^4.4.1", 22 | "numeral": "^2.0.6", 23 | "ws": "^4.0.0" 24 | }, 25 | "devDependencies": { 26 | "babel-core": "^6.26.0", 27 | "babel-preset-env": "^1.6.1", 28 | "request": "^2.81.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pump_detector.js: -------------------------------------------------------------------------------- 1 | const binance = require('node-binance-api') 2 | const express = require('express') 3 | const path = require('path') 4 | var _ = require('lodash') 5 | var moment = require('moment') 6 | var numeral = require('numeral') 7 | var readline = require('readline') 8 | var fs = require('fs') 9 | const TelegramBot = require('node-telegram-bot-api'); 10 | // const play = require('audio-play') 11 | // const load = require('audio-loader') 12 | const nodemailer = require('nodemailer') 13 | 14 | ////////////////////////////////////////////////////////////////////////////////// 15 | 16 | // https://www.binance.com/restapipub.html 17 | const APIKEY = '7hxRkF3KTID2EOnnTPxEFUjXBQinBMoA8TBFVfFUuBsUgtnTe5Zo3NHtCqqtFO54' 18 | const APISECRET = 'wHGQ9BIKPieC1a82JtPVxnSpBC4MQO4h5F6yLQKc9mHRATz8MNOo9d9hKxFQsgf9' 19 | 20 | const wait_time = 1000 // ms 21 | const trading_fee = 0.1 // pourcent 22 | 23 | 24 | // API initialization // 25 | binance.options({ 26 | 'APIKEY': APIKEY, 27 | 'APISECRET': APISECRET, 28 | 'reconnect': true 29 | }); 30 | 31 | /////////////////////////////////////////////////////////////////////////////////// 32 | 33 | // replace the value below with the Telegram token you receive from @BotFather 34 | const token = '565941385:AAEnnyP1t54NMuKcHla2a2OhhUUWzqx5wwo'; 35 | 36 | // Create a bot that uses 'polling' to fetch new updates 37 | const bot = new TelegramBot(token, {polling: true}); 38 | 39 | const chatId = '-1001245808874'; 40 | 41 | /////////////////////////////////////////////////////////////////////////////////// 42 | 43 | let btc_price = 0 44 | 45 | let pairs = [] 46 | 47 | let depth_bids = {} 48 | let depth_asks = {} 49 | let depth_diff = {} 50 | 51 | let minute_prices = {} 52 | let hourly_prices = {} 53 | 54 | let tracked_pairs = [] 55 | let tracked_data = {} 56 | let total_pnl = {} 57 | let intervals = ['1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '6h', '8h', '12h', '1d', '3d', '1w', '1M']; 58 | let interval = '30m'; 59 | let pair_status = [] 60 | 61 | console.log('------------ NBT starting -------------') 62 | 63 | async function run() { 64 | 65 | //if (sound_alert) load('./alert.mp3').then(play); 66 | await sleep(2) 67 | 68 | console.log('------------------------------') 69 | console.log(' start get_BTC_price') 70 | console.log('------------------------------') 71 | btc_price = await get_BTC_price() 72 | console.log('------------------------------') 73 | console.log('BTC price: $' + numeral(btc_price).format('0,0')) 74 | console.log('------------------------------') 75 | 76 | await sleep(2) 77 | 78 | console.log('------------------------------') 79 | console.log(' get_BTC_pairs start') 80 | console.log('------------------------------') 81 | pairs = await get_BTC_pairs() 82 | console.log('------------------------------') 83 | //pairs = pairs.slice(0, 1) //for debugging purpose 84 | console.log("Total BTC pairs: " + pairs.length) 85 | console.log('------------------------------') 86 | 87 | await sleep(2) 88 | 89 | console.log('------------------------------') 90 | console.log(' run detector') 91 | console.log('------------------------------') 92 | await get_candleSticks_for_BTC_pairs(interval); 93 | } 94 | 95 | 96 | sleep = (x) => { 97 | return new Promise(resolve => { 98 | setTimeout(() => { resolve(true) }, x ) 99 | }); 100 | } 101 | 102 | get_BTC_price = () => { 103 | return new Promise(resolve => { 104 | binance.websockets.candlesticks(['BTCUSDT'], "1m", (candlesticks) => { 105 | let { e:eventType, E:eventTime, s:symbol, k:ticks } = candlesticks; 106 | let { o:open, h:high, l:low, c:close, v:volume, n:trades, i:interval, x:isFinal, q:quoteVolume, V:buyVolume, Q:quoteBuyVolume } = ticks; 107 | btc_price = close 108 | resolve(btc_price) 109 | }) 110 | }) 111 | } 112 | 113 | get_BTC_pairs = () => { 114 | return new Promise(resolve => { 115 | binance.exchangeInfo((error, data) => { 116 | if (error) { 117 | console.log( error ) 118 | resolve([]) 119 | } 120 | if (data) { 121 | console.log( data.symbols.length + " total pairs") 122 | resolve( data.symbols.filter( pair => pair.symbol.endsWith('BTC') ).map(pair=>pair.symbol) ) 123 | } 124 | }) 125 | }) 126 | } 127 | 128 | 129 | get_candleSticks_for_BTC_pairs = async (interval) => { 130 | for (var i = 0; i < pairs.length; i++) { 131 | await getPrevMinutePrices(pairs[i], interval); 132 | await sleep(wait_time) 133 | 134 | await trackPrice(pairs[i], interval); 135 | await sleep(wait_time) 136 | } 137 | } 138 | 139 | getPrevMinutePrices = (pair, interval) => { 140 | return new Promise(resolve => { 141 | binance.candlesticks(pair, interval, (error, ticks, symbol) => { 142 | if (error) { 143 | console.log( pair + " getPrevMinutePrices ERROR " + error ) 144 | resolve(true) 145 | } 146 | 147 | if (ticks) { 148 | console.log( pair + ' got data') 149 | for (var i = 0; i < ticks.length; i++) { 150 | let [time, open, high, low, close, volume, closeTime, assetVolume, trades, buyBaseVolume, buyAssetVolume, ignored] = ticks[i]; 151 | let pair_data = create_pair_data(tracked_pairs, symbol, close, volume, buyBaseVolume, btc_price, time); 152 | } 153 | 154 | resolve(true) 155 | } 156 | }, {limit: 3}) 157 | }) 158 | } 159 | 160 | trackPrice = (pair, interval) => { 161 | return new Promise(resolve => { 162 | binance.websockets.candlesticks(pair, interval, (candlesticks) => { 163 | let { e:eventType, E:eventTime, s:symbol, k:ticks } = candlesticks; 164 | let { s:s, o:open, h:high, l:low, c:close, v:volume, n:trades, i:interval, x:isFinal, q:quoteVolume, V:buyVolume, Q:quoteBuyVolume } = ticks; 165 | 166 | let currentPair = tracked_pairs.find(x => x.symbol === s); 167 | 168 | if (currentPair) { 169 | let [candlestick1, candlestick2, candlestick3] = currentPair.data; 170 | let averageVolumne = (parseFloat(candlestick1.volume) + parseFloat(candlestick2.volume) + parseFloat(candlestick3.volume)) / 3; 171 | 172 | if (volume > averageVolumne * 1.3) { 173 | let t = Date.now() - candlestick3.timestamp; 174 | let minuteT = t / 60000; 175 | let status = close > candlestick3.price ? 'pumping' : 'dumping'; 176 | 177 | if (minuteT < 20) { 178 | if (pair_status[s] !== status) { 179 | pair_status[s] = status; 180 | var message = `[TESTING ${interval}] ${s} + is ${status}. Price: ${candlestick3.price} | ${close} 181 | \n Previous volume: ${candlestick3.volume} Current volume: ${volume} 182 | \n https://www.binance.com/tradeDetail.html?symbol=${symbol.slice(0, -3)}_BTC`; 183 | console.log(message); 184 | bot.sendMessage(chatId, message); 185 | } 186 | } 187 | } 188 | } 189 | 190 | if (isFinal) { 191 | let pair_data = create_pair_data(tracked_pairs, symbol, close, volume, buyVolume, btc_price); 192 | } 193 | 194 | resolve(true); 195 | }); 196 | }) 197 | } 198 | 199 | create_pair_data = (pairs, symbol, close, volume, buyBaseVolume, btc_price, time) => { 200 | var pair_data = pairs.filter(x => x.symbol === symbol)[0]; 201 | if (!pair_data) { 202 | pair_data = { 203 | symbol: symbol, 204 | data: [{ 205 | date: moment().format('MMMM Do YYYY, h:mm:ss a'), 206 | timestamp: time || Date.now(), 207 | price: close, 208 | volume: volume, 209 | buyVolume: buyBaseVolume, 210 | usdvolume: volume*close*btc_price 211 | }] 212 | } 213 | 214 | tracked_pairs.push(pair_data) 215 | } else { 216 | if (pair_data.data.length > 2) { 217 | pair_data.data.shift(); 218 | } 219 | 220 | pair_data.data.push({ 221 | date: moment().format('MMMM Do YYYY, h:mm:ss a'), 222 | timestamp: time || Date.now(), 223 | price: close, 224 | volume: volume, 225 | buyVolume: buyBaseVolume, 226 | usdvolume: volume*close*btc_price 227 | }) 228 | } 229 | 230 | return pair_data; 231 | } 232 | 233 | run() 234 | 235 | const app = express() 236 | app.get('/', (req, res) => res.send(tracked_pairs)) 237 | app.listen(9874, () => console.log('NBT api accessable on port 80')) 238 | -------------------------------------------------------------------------------- /x.low)y: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tannguyen248/nodejs-binance-trader-bot/b269d162bbeb0f6b4da65ba9b9c2343235d196c5/x.low)y --------------------------------------------------------------------------------