├── .ruby-version ├── config.ru ├── data ├── cache_prices.yml ├── agreement.md ├── users_data.yml └── cache_hist.yml ├── Procfile ├── public ├── favicon.ico ├── images │ └── noisy_net_2X.png ├── javascript │ ├── input-conversion.js │ ├── jsapi.js │ └── chartkick.js └── stylesheets │ └── main.css ├── views ├── agreement.erb ├── not_found.erb ├── index.erb ├── signin.erb ├── signup.erb ├── charts.erb ├── layout.erb ├── settings.erb ├── buy.erb ├── sell.erb └── dashboard.erb ├── test ├── data │ └── users_data.yml └── cx_test.rb ├── Gemfile ├── LICENSE.md ├── Gemfile.lock ├── README.md └── cx.rb /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.3.7 2 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require "./cx" 2 | run Sinatra::Application -------------------------------------------------------------------------------- /data/cache_prices.yml: -------------------------------------------------------------------------------- 1 | --- 2 | BTC: 3 | USD: 7032.6 4 | ETH: 5 | USD: 286.97 6 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec puma -t 5:5 -p ${PORT:-3000} -e ${RACK_ENV:-development} 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YingCGooi/coin_exchange/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /views/agreement.erb: -------------------------------------------------------------------------------- 1 |
2 | <%== @markdown.render(@user_agreement) %> 3 |
-------------------------------------------------------------------------------- /public/images/noisy_net_2X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YingCGooi/coin_exchange/HEAD/public/images/noisy_net_2X.png -------------------------------------------------------------------------------- /views/not_found.erb: -------------------------------------------------------------------------------- 1 |

404 Not Found

2 | 3 |

Oops! It looks like the page that you've requested is not found.

4 |

Click here to return.

-------------------------------------------------------------------------------- /test/data/users_data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | admin: 3 | :password: "$2a$10$XQq2o2l8zVCndc9Ol1MpI..T9ckk2quGlRRVdXFeKJ29ySnFkkH5W" 4 | :created: '2017-11-03 22:08:11 -0500' 5 | :new_user: false 6 | :balances: 7 | :btc: 0.987 8 | :eth: 2.896 9 | :usd: 6320 10 | :transactions: [] 11 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | ruby '>= 2.3.4' 4 | 5 | gem 'sinatra' 6 | gem 'sinatra-contrib' 7 | gem 'erubis' 8 | gem 'chartkick' 9 | gem 'minitest' 10 | gem 'minitest-reporters' 11 | gem 'rack-test' 12 | gem 'bcrypt' 13 | gem 'redcarpet' 14 | gem 'pry' 15 | 16 | group :production do 17 | gem "puma" 18 | end -------------------------------------------------------------------------------- /data/agreement.md: -------------------------------------------------------------------------------- 1 | ## User Agreement 2 | This application is built for the purpose of practice. Digital currencies and USD balances in the application's account balances are not real and should not be treated seriously. 3 | 4 | This application is not intended to provide any advice on investment nor to provide any education about cryptocurrencies. 5 | 6 | If you notice any bugs, please post an issue on my [Github Issues page](https://github.com/YingCGooi/coin_exchange_web_app_project/issues). 7 | On top of all, have fun with this small application! -------------------------------------------------------------------------------- /views/index.erb: -------------------------------------------------------------------------------- 1 | 6 | 7 |
8 |

BUY AND SELL CRYPTOCURRENCIES

9 | 10 |

Coin Exchange is a platform to easily trade bitcoin and ethereum.

11 |
12 | 13 | 17 | -------------------------------------------------------------------------------- /views/signin.erb: -------------------------------------------------------------------------------- 1 |
2 |

Sign In

3 | 4 |
5 |
6 | 9 |
10 | 11 |
12 | 15 |
16 | 17 |
18 | 19 |

Don't have an account? Sign up

20 |
21 |
-------------------------------------------------------------------------------- /views/signup.erb: -------------------------------------------------------------------------------- 1 |
2 |

Sign Up

3 | 4 |
5 |
6 | 9 |
10 | 11 |
12 | 15 |
16 | 17 | I agree to the User Agreement 18 | 19 |
20 | 21 |
22 |
-------------------------------------------------------------------------------- /public/javascript/input-conversion.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | 3 | function alertFunds(value, balance) { 4 | $('div#primary-input').toggleClass('red-border', value > balance); 5 | $('p.alert-limit').toggle(value > balance); 6 | }; 7 | 8 | alertFunds(0, balance); // toggle alert message off when page loads 9 | 10 | $('#coin-input').on('keyup', function() { 11 | var input = $(this).val(); 12 | var correspUsdValue = (input * currentCoinPrice); 13 | 14 | $('#usd-input').val(correspUsdValue.toFixed(2)); 15 | if(!$(this).val()) { $('#usd-input').val('') }; 16 | 17 | alertFunds($('input.primary').val(), balance); 18 | }); 19 | 20 | $('#usd-input').on('keyup', function() { 21 | var input = $(this).val(); 22 | var correspCoinValue = (input / currentCoinPrice); 23 | 24 | $('#coin-input').val(correspCoinValue.toFixed(6)); 25 | if(!$(this).val()) { $('#coin-input').val('') }; 26 | 27 | alertFunds($('input.primary').val(), balance); 28 | }); 29 | }); -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Ying Chyi Gooi 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 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | ansi (1.5.0) 5 | backports (3.10.3) 6 | bcrypt (3.1.11) 7 | builder (3.2.3) 8 | chartkick (2.2.5) 9 | coderay (1.1.2) 10 | erubis (2.7.0) 11 | method_source (0.9.0) 12 | minitest (5.10.3) 13 | minitest-reporters (1.1.18) 14 | ansi 15 | builder 16 | minitest (>= 5.0) 17 | ruby-progressbar 18 | multi_json (1.11.2) 19 | mustermann (1.0.2) 20 | pry (0.11.2) 21 | coderay (~> 1.1.0) 22 | method_source (~> 0.9.0) 23 | puma (3.10.0) 24 | rack (2.0.4) 25 | rack-protection (2.0.0) 26 | rack 27 | rack-test (0.7.0) 28 | rack (>= 1.0, < 3) 29 | redcarpet (3.4.0) 30 | ruby-progressbar (1.9.0) 31 | sinatra (2.0.0) 32 | mustermann (~> 1.0) 33 | rack (~> 2.0) 34 | rack-protection (= 2.0.0) 35 | tilt (~> 2.0) 36 | sinatra-contrib (2.0.0) 37 | backports (>= 2.0) 38 | multi_json 39 | mustermann (~> 1.0) 40 | rack-protection (= 2.0.0) 41 | sinatra (= 2.0.0) 42 | tilt (>= 1.3, < 3) 43 | tilt (2.0.8) 44 | 45 | PLATFORMS 46 | ruby 47 | 48 | DEPENDENCIES 49 | bcrypt 50 | chartkick 51 | erubis 52 | minitest 53 | minitest-reporters 54 | pry 55 | puma 56 | rack-test 57 | redcarpet 58 | sinatra 59 | sinatra-contrib 60 | 61 | RUBY VERSION 62 | ruby 2.3.4p301 63 | 64 | BUNDLED WITH 65 | 1.16.1 66 | -------------------------------------------------------------------------------- /views/charts.erb: -------------------------------------------------------------------------------- 1 | <% if session[:signin] %> 2 | 11 | <% end %> 12 | 13 |
14 | 15 |

Bitcoin Price 30-day chart

16 | <%== area_chart( 17 | @historical_bpi, 18 | min: @min_btc_price*0.95, 19 | max: @max_btc_price*1.1, 20 | colors: ['#ffcc00'], 21 | label: 'BTC Price', 22 | library: {chartArea: {height: '80%', width: '85%'}, backgroundColor: 'transparent' } 23 | ) %> 24 | 25 | 26 |
27 | Bitcoin Price: $<%= @current_btc_price %> 28 |

Updated: <%= Time.now %>

29 |
30 | 31 |
32 |

Ethereum Price 30-day chart

33 | 34 | <%== area_chart( 35 | @historical_eth, 36 | min: @min_eth_price*0.95, 37 | max: @max_eth_price*1.1, 38 | colors: ['#85ABF3'], 39 | label: 'ETH Price', 40 | library: {chartArea: {height: '80%', width: '85%'}, backgroundColor: 'transparent' } 41 | ) %> 42 | 43 | 44 |
45 | Ethereum Price: $<%= @current_eth_price %> 46 |

Updated: <%= Time.now %>

47 |
48 | -------------------------------------------------------------------------------- /views/layout.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Coin Exchange | Buy and Sell Digital Coin 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |

Coin Exchange

15 | <% if user_signed_in? %> 16 | 19 | 20 |

Signed in as <%=session[:signin][:username]%>

21 | <% else %> 22 | 23 | 24 | 25 | <% end %> 26 |
27 | 28 |
29 |
30 |
31 | <% if session[:offline] %> 32 |

REAL-TIME DATA OFFLINE

33 | <% end %> 34 |
35 | 36 | <% if session[:success] %> 37 |

<%== session.delete(:success) %>

38 | <% end %> 39 | 40 | <% if session[:failure] %> 41 |

<%== session.delete(:failure) %>

42 | <% end %> 43 | 44 | <%== yield %> 45 | 46 |
47 |
48 | 49 |
50 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /views/settings.erb: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 |
13 |

Change Password

14 | 15 |
16 | 17 | 18 | 19 |
20 |
21 | 22 |
23 |

Delete Account

24 | 25 | ⚠ Warning! You will lose all of your balances. 26 | 27 | 33 |
34 | 35 |
36 |
37 | 38 | -------------------------------------------------------------------------------- /views/buy.erb: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 |

Currency

13 |
14 |
15 | > 16 | 20 | 21 | > 22 | 26 | 27 |
28 | 29 |

Amount

30 |
31 |

Your USD Balance: <%= format_usd(@usd_balance) %>

32 | 33 |
34 |
35 |
36 | 38 | 39 |
40 | 41 |

42 | 43 |
44 | 45 | 46 |
47 | 48 |
49 |

Insufficient funds.

50 |
51 | 52 | 55 |
56 |
57 |
58 | 59 | -------------------------------------------------------------------------------- /views/sell.erb: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 |

Currency

13 |
14 |
15 | > 16 | 20 | 21 | > 22 | 26 | 27 |
28 | 29 |

Amount

30 |
31 |

32 | Your <%=CURRENCY_NAMES[@coin.to_sym]%> Balance: 33 | <%=@coin_balance.round(5)%> <%=@coin.upcase%> 34 |

35 | 36 |
37 |
38 |
39 | 41 | 42 |
43 | 44 |

45 | 46 |
47 | 48 | 49 |
50 | 51 |
52 |

Not enough <%=@coin.upcase%>.

53 |
54 | 55 | 58 |
59 |
60 |
61 | 62 | -------------------------------------------------------------------------------- /views/dashboard.erb: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 |
13 |

Your Portfolio

14 | 15 | <%== pie_chart( 16 | @portfolio_chart_data, 17 | library: {chartArea: {left: '15%', height: '85%', width: '100%'}, 18 | backgroundColor: 'transparent'}, 19 | label: '$', 20 | colors: ['#FFC100', '#6D83BC', '#7FA38F'], 21 | donut: true) %> 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | <% @portfolio.each do |symbol, balance| %> 30 | 31 | 34 | 37 | 40 | 41 | <% end %> 42 |
BALANCECOUNTER VALUE
32 | <%= CURRENCY_NAMES[symbol] %> 33 | 35 | <%= balance.round(5) %> <%= symbol.upcase %> 36 | 38 | <%= format_usd(balance * @counter_values[symbol]) %> 39 |
43 | 44 |
45 | 46 | 47 |
48 |

Recent Transactions

49 | 50 |
51 | 52 | <% @transactions.each do |trx| %> 53 | 54 | 60 | 66 | 67 | 71 | 72 | <% end %> 73 |
55 |

<%= trx.type.capitalize %> <%= CURRENCY_NAMES[trx.coin.to_sym.downcase] %>

56 | <% if trx.type != :deposit %> 57 | @<%= format_usd(trx.price) %> per coin 58 | <% end %> 59 |
61 |

<%= trx.coin_amount %> <%= trx.coin.upcase %>

62 | 63 | <%= format_usd(trx.usd_amount) %> 64 | 65 |
68 |

<%= trx.time.strftime('%l:%M %p') %>

69 | <%= trx.time.strftime('%b %d, %Y') %> 70 |
74 |
75 |
76 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Coin Exchange Web Application 2 | 3 | ![coin exchange dashboard](https://i.imgur.com/I4kQ9q6.png) 4 | 5 | A mock digital currency exchange platform which allows users to buy and sell Bitcoin and Ethereum based on actual real-time market price. This web application is built with Sinatra Ruby framework. Features inspired by Coinbase exchange. 6 | 7 | This app is deployed in Heroku: https://coin-exchange-sinatra.herokuapp.com/ 8 | 9 | The goal of creating this application to practice translating high-level requirements into working code, integrating third-party web APIs and translating them into user-friendly interfaces and charts. Dynamic input forms were rendered through the use of jQuery. 10 | Performance bottlenecks such as slow loading speed were addressed. 11 | 12 | ## Installation 13 | Clone or download this git repository. Within the terminal opening the root of this project, execute the following line to install dependencies: 14 | 15 | ``` 16 | bundle install 17 | ``` 18 | 19 | ## Usage 20 | To run the server locally, execute: 21 | 22 | ``` 23 | bundle exec ruby cx.rb 24 | ``` 25 | 26 | Once Sinatra is running in the background, open up a web browser and enter `localhost:4567` in the URL address bar to begin. 27 | 28 | ## API Utilization 29 | Third-party APIs are used to integrate real-time BTC and ETH prices into the application and to display a 30-day BTC and ETH chart. 30 | - API for real-time BTC and ETH prices: https://min-api.cryptocompare.com/data/pricemulti?fsyms=BTC,ETH&tsyms=USD 31 | - API information for historical hourly price data: https://www.cryptocompare.com/api/#-api-data-histohour 32 | 33 | ## Offline Mode 34 | In the case where the application fails to fetch real-time data, the last retrieved price data will be used. This will apply to buy/sell prices as well as historical chart data. 35 | It is possible to run the application entirely off-line. For the best user experience, it is recommended that you have an active Internet connection. 36 | 37 | ### Sign-up Bonus 38 | User will receive a sign-up bonus funding of virtual USD balance into their account, which can be used to purchase mock BTC or ETH. 39 | 40 | #### Default User 41 | If you do not wish to create a new account, you can use the default credentials: 42 | - username: `admin` 43 | - password: `secret` 44 | 45 | ### Automatic logging out 46 | Signed-in user will be automatically logged out after a certain period of inactivity. On every account action (buy/sell/page navigation), the idle time will be reset. 47 | 48 | ### Numbers 49 | Prices are updated real-time - which means the web app's exchange rates follow the actual markets. A strict price validation is implemented (price swing within 0.5%) so that users may not be able to manipulate the inputs to buy/sell at a false exchange rate. 50 | 51 | ## Tests 52 | To run tests: 53 | ``` 54 | bundle exec ruby test/cx_test.rb 55 | ``` 56 | 57 | Tests will now retrieve current price data from `cache_prices.yml`. This will prevent significant price fluctuations due to API response lagging time. 58 | 59 | ## Credits 60 | Icon made by [Those Icons](https://www.flaticon.com/authors/those-icons) from www.flaticon.com 61 | 62 | [Chartkick](https://www.chartkick.com/), together with Google Charts are used for drawing beautiful charts. 63 | 64 | [Crytocompare API](https://www.cryptocompare.com/api/) is used as an API source for all of the real-time pricing and historical price data. 65 | -------------------------------------------------------------------------------- /data/users_data.yml: -------------------------------------------------------------------------------- 1 | --- 2 | username: password 3 | admin: 4 | :password: "$2a$10$z3Ihcc4jUrWpj/bUk8tE4.4Rjpmwkfy9YAYtMdeI12NXiUDP2iqea" 5 | :created: '2017-11-17 23:28:32 -0600' 6 | :new_user: false 7 | :balances: 8 | :btc: 0.2574380000000001 9 | :eth: 4.039778 10 | :usd: 9815.890000000001 11 | :transactions: 12 | - !ruby/object:Transaction 13 | type: :deposit 14 | coin: USD 15 | coin_amount: 12872 16 | usd_amount: 12872 17 | time: 2017-11-18 00:28:32.284565599 -05:00 18 | - !ruby/object:Transaction 19 | type: :buy 20 | coin: eth 21 | coin_amount: 3.5 22 | usd_amount: 1150.66 23 | time: 2017-11-18 00:28:59.318986673 -05:00 24 | - !ruby/object:Transaction 25 | type: :buy 26 | coin: btc 27 | coin_amount: 0.650421 28 | usd_amount: 5000.0 29 | time: 2017-11-18 00:29:52.694057983 -05:00 30 | - !ruby/object:Transaction 31 | type: :buy 32 | coin: eth 33 | coin_amount: 5.0 34 | usd_amount: 1643.8 35 | time: 2017-11-18 00:30:47.248953707 -05:00 36 | - !ruby/object:Transaction 37 | type: :sell 38 | coin: btc 39 | coin_amount: 0.25 40 | usd_amount: -1932.77 41 | time: 2017-11-18 14:47:09.697135628 -05:00 42 | - !ruby/object:Transaction 43 | type: :buy 44 | coin: btc 45 | coin_amount: 0.384469 46 | usd_amount: 3000.0 47 | time: 2017-11-18 23:48:53.482822176 -05:00 48 | - !ruby/object:Transaction 49 | type: :sell 50 | coin: btc 51 | coin_amount: 0.1 52 | usd_amount: -780.24 53 | time: 2017-11-18 23:49:02.223685206 -05:00 54 | - !ruby/object:Transaction 55 | type: :sell 56 | coin: eth 57 | coin_amount: 4.0 58 | usd_amount: -1393.52 59 | time: 2017-11-18 23:49:24.701845574 -05:00 60 | - !ruby/object:Transaction 61 | type: :sell 62 | coin: btc 63 | coin_amount: 0.2 64 | usd_amount: -1585.68 65 | time: 2017-11-21 00:16:30.775342253 -05:00 66 | - !ruby/object:Transaction 67 | type: :buy 68 | coin: btc 69 | coin_amount: 0.001255 70 | usd_amount: 10.0 71 | time: 2018-04-16 08:58:32.749700193 -04:00 72 | - !ruby/object:Transaction 73 | type: :buy 74 | coin: btc 75 | coin_amount: 0.001255 76 | usd_amount: 10.0 77 | time: 2018-04-16 08:58:33.386961889 -04:00 78 | - !ruby/object:Transaction 79 | type: :buy 80 | coin: btc 81 | coin_amount: 0.001255 82 | usd_amount: 10.0 83 | time: 2018-04-16 08:58:34.325231496 -04:00 84 | - !ruby/object:Transaction 85 | type: :buy 86 | coin: btc 87 | coin_amount: 0.001252 88 | usd_amount: 10.0 89 | time: 2018-04-16 09:03:04.457598685 -04:00 90 | - !ruby/object:Transaction 91 | type: :buy 92 | coin: btc 93 | coin_amount: 0.001251 94 | usd_amount: 10.0 95 | time: 2018-04-16 09:05:11.068509014 -04:00 96 | - !ruby/object:Transaction 97 | type: :buy 98 | coin: btc 99 | coin_amount: 0.001251 100 | usd_amount: 10.0 101 | time: 2018-04-16 09:05:16.168929328 -04:00 102 | - !ruby/object:Transaction 103 | type: :buy 104 | coin: btc 105 | coin_amount: 0.001251 106 | usd_amount: 10.0 107 | time: 2018-04-16 09:05:24.190309561 -04:00 108 | - !ruby/object:Transaction 109 | type: :buy 110 | coin: eth 111 | coin_amount: 0.019853 112 | usd_amount: 10.0 113 | time: 2018-04-16 09:05:36.506311084 -04:00 114 | - !ruby/object:Transaction 115 | type: :buy 116 | coin: btc 117 | coin_amount: 0.013778 118 | usd_amount: 110.0 119 | time: 2018-04-16 09:06:03.499076603 -04:00 120 | - !ruby/object:Transaction 121 | type: :sell 122 | coin: btc 123 | coin_amount: 0.1 124 | usd_amount: -798.17 125 | time: 2018-04-16 09:07:41.674599158 -04:00 126 | - !ruby/object:Transaction 127 | type: :sell 128 | coin: btc 129 | coin_amount: 0.1 130 | usd_amount: -798.17 131 | time: 2018-04-16 09:07:42.603233612 -04:00 132 | - !ruby/object:Transaction 133 | type: :sell 134 | coin: btc 135 | coin_amount: 0.05 136 | usd_amount: -399.04 137 | time: 2018-04-16 09:08:12.932943591 -04:00 138 | - !ruby/object:Transaction 139 | type: :buy 140 | coin: eth 141 | coin_amount: 0.019925 142 | usd_amount: 10.0 143 | time: 2018-04-16 09:15:06.050968464 -04:00 144 | - !ruby/object:Transaction 145 | type: :sell 146 | coin: eth 147 | coin_amount: 0.5 148 | usd_amount: -250.76 149 | time: 2018-04-16 09:15:52.155751832 -04:00 150 | -------------------------------------------------------------------------------- /public/stylesheets/main.css: -------------------------------------------------------------------------------- 1 | /* ======== 2 | GENERAL 3 | =========*/ 4 | * { 5 | box-sizing: border-box; 6 | } 7 | 8 | body { 9 | margin: 0; 10 | background-color: #F2F4F4; 11 | } 12 | 13 | h1 { 14 | font-weight: 900; 15 | } 16 | 17 | h4 { 18 | font-size: 24px; 19 | font-weight: 300; 20 | } 21 | 22 | h3 a { 23 | color: white; 24 | text-decoration: none; 25 | } 26 | 27 | header { 28 | margin: 0px; 29 | padding: 5px 15px; 30 | min-height: 40px; 31 | background-color: #1E4A8B; 32 | } 33 | 34 | header h3 { 35 | display: inline-block; 36 | } 37 | 38 | ul { 39 | list-style: none; 40 | } 41 | 42 | body, button, input { 43 | font-family: Lato, sans-serif; 44 | } 45 | 46 | button:hover { 47 | cursor: pointer; 48 | } 49 | 50 | .landing-head { 51 | color: white; 52 | text-align: center; 53 | } 54 | 55 | .landing-head p { 56 | color: #DBDCDD; 57 | } 58 | 59 | footer p { 60 | font-size: .8em; 61 | color: #828282; 62 | } 63 | 64 | div.agreement { 65 | width: 70%; 66 | margin: auto; 67 | } 68 | 69 | div.content { 70 | padding-bottom: 36px; 71 | } 72 | 73 | main { 74 | padding: 0 20px; 75 | } 76 | 77 | /* ======== 78 | CHARTS 79 | =========*/ 80 | .credit { 81 | font-size: 10px; 82 | text-align: center; 83 | } 84 | 85 | .chart { 86 | display: block; 87 | min-width: 600px; 88 | margin: 10px; 89 | padding-left: 30px; 90 | border: 1px solid #BDBFC0; 91 | border-radius: 4px; 92 | background-color: #FDFDFD; 93 | } 94 | 95 | .chart span { 96 | font-weight: 400; 97 | font-size: 1.5em; 98 | } 99 | 100 | .chart p { 101 | font-weight: 200; 102 | color: #969696; 103 | } 104 | 105 | /* ================ 106 | SIGN-IN/SIGN-UP 107 | =================*/ 108 | div.signin-signup { 109 | width: 300px; 110 | margin: auto; 111 | } 112 | 113 | .signin-signup input { 114 | width: 293px; 115 | padding: 10px 12px; 116 | margin-top: 10px; 117 | font-size: 1.1em; 118 | border-radius: 3px; 119 | border: 1px solid #A9A9A9; 120 | } 121 | 122 | input.certify { 123 | width: 1em; 124 | height: 1em; 125 | margin-bottom: 20px; 126 | } 127 | 128 | .signin-signup button { 129 | width: 292px; 130 | height: 45px; 131 | font-size: 0.9em; 132 | color: white; 133 | background-color: #336699; 134 | border: 0px; 135 | border-radius: 4px; 136 | } 137 | 138 | .signin-signup button:hover { 139 | background-color: #12487F; 140 | } 141 | 142 | /* ================ 143 | ALERT MESSAGES 144 | =================*/ 145 | .success, .failure { 146 | margin: auto; 147 | margin-top: 2px; 148 | width: 93%; 149 | padding: 1em; 150 | border-radius: 5px; 151 | } 152 | 153 | .success { 154 | background-color: #C4FFC0; 155 | color: #348044; 156 | } 157 | 158 | .failure { 159 | background-color: #F8D0CD; 160 | color: #653445; 161 | } 162 | 163 | /* ============== 164 | DASHBOARD 165 | =============== */ 166 | 167 | .dashboard-container { 168 | display: inline-block; 169 | vertical-align: top; 170 | margin: 10px 0; 171 | margin-right: 10px; 172 | width: 49%; 173 | min-height: 650px; 174 | max-height: 800px; 175 | border: 1px solid #BDBFC0; 176 | border-radius: 4px; 177 | background-color: #FDFDFD; 178 | } 179 | 180 | .dashboard-container:last-child { 181 | margin-right: 0; 182 | } 183 | 184 | .dashboard-container h3 { 185 | font-size: 1.5em; 186 | width: auto; 187 | padding: 25px 30px; 188 | margin: 0; 189 | border-bottom: 1px solid #BDBFC0; 190 | } 191 | 192 | .dashboard-container table.transactions { 193 | height: 480px; 194 | } 195 | 196 | table.portfolio, 197 | table.transactions { 198 | border-collapse: collapse; 199 | display: block; 200 | margin: 30px; 201 | margin-top: 5px; 202 | font-size: 20px; 203 | } 204 | 205 | .portfolio tbody, 206 | .transactions tbody { 207 | width: 450px; 208 | } 209 | 210 | .portfolio td { 211 | padding: 1em; 212 | text-align: right; 213 | } 214 | 215 | .transactions td { 216 | padding: 1em 0; 217 | padding-left: 1em; 218 | text-align: right; 219 | } 220 | 221 | .portfolio tr, .transactions tr { 222 | border-bottom: 1px dashed #CFCFCF; 223 | } 224 | 225 | td.name { 226 | font-weight: 900; 227 | padding-left: 0px; 228 | text-align: left; 229 | } 230 | 231 | .head th { 232 | padding: 0 1em 0.3em 1em; 233 | font-weight: 300; 234 | font-size: 0.7em; 235 | text-align: right; 236 | color: #5B5D60; 237 | } 238 | 239 | td.counter-value { 240 | font-weight: 300; 241 | color: #717171; 242 | } 243 | 244 | .transactions td span { 245 | font-size: 16px; 246 | color: #9D9D9D; 247 | } 248 | 249 | .transactions td span { 250 | font-weight: 400; 251 | } 252 | 253 | .transactions td b { 254 | font-weight: 400; 255 | } 256 | 257 | .transactions td p { 258 | font-weight: 200; 259 | margin: 0; 260 | } 261 | 262 | td.buy, 263 | td.deposit { 264 | color: green; 265 | } 266 | 267 | td.sell { 268 | color: red; 269 | } 270 | 271 | td.buy p:before, 272 | td.deposit p:before, 273 | td.deposit span:before, 274 | td.sell span:before { 275 | content: '+'; 276 | } 277 | 278 | td.sell p:before, 279 | td.buy span:before { 280 | content: '-'; 281 | } 282 | 283 | /* ========= 284 | MENU 285 | ============ */ 286 | 287 | .menu { 288 | line-height: 58px; 289 | padding-right: 20px; 290 | display: inline-block; 291 | float: right; 292 | } 293 | 294 | .menu button { 295 | text-decoration: underline; 296 | text-decoration-color: #7987A5; 297 | font-weight: 500; 298 | color: white; 299 | background-color: Transparent; 300 | border: none; 301 | } 302 | 303 | header p { 304 | line-height: 59px; 305 | margin: 0; 306 | padding-right: 20px; 307 | font-size: 14px; 308 | font-style: italic; 309 | color: #C4C4C4; 310 | display: inline-block; 311 | float: right; 312 | } 313 | 314 | div.offline { 315 | height: 18px; 316 | margin: 0; 317 | } 318 | 319 | .offline p { 320 | text-align: center; 321 | background-color: #FEFF91; 322 | margin: 0; 323 | } 324 | 325 | /* ============== 326 | NAVIGATION 327 | =============== */ 328 | 329 | nav.index-links { 330 | margin: auto; 331 | width: 75%; 332 | } 333 | 334 | .index-links button { 335 | display: inline-block; 336 | width: 45%; 337 | padding: 1em; 338 | margin: 10px; 339 | border: 2px solid #385BB2; 340 | border-radius: 5px; 341 | font-size: 18px; 342 | } 343 | 344 | button.signup { 345 | color: white; 346 | background-color: #385BB2; 347 | } 348 | 349 | button.signup:hover, button.view-charts:hover { 350 | color: white; 351 | border: 2px solid #FF9900; 352 | background-color: #FF9900; 353 | } 354 | 355 | button.view-charts { 356 | background-color: transparent; 357 | color: #4272E6; 358 | } 359 | 360 | nav.account-menu { 361 | margin: 0; 362 | text-align: center; 363 | } 364 | 365 | .account-menu ul { 366 | margin: 0; 367 | padding: 0; 368 | } 369 | 370 | .account-menu ul li { 371 | display: inline-block; 372 | padding: 10px; 373 | margin: 0; 374 | text-align: center; 375 | font-size: 20px; 376 | font-weight: 300; 377 | } 378 | 379 | .account-menu li.active { 380 | border-bottom: 3px solid #4172C4; 381 | } 382 | 383 | .account-menu a { 384 | color: #9B9EA3; 385 | text-decoration: none; 386 | } 387 | 388 | .account-menu li.active a { 389 | color: #4172C4; 390 | } 391 | 392 | .account-menu a:hover { 393 | color: #83AAEB; 394 | } 395 | 396 | 397 | /* ================ 398 | BUY/SELL 399 | ================= */ 400 | 401 | .buy h4, .sell h4 { 402 | font-size: 20px; 403 | margin-bottom: 0px; 404 | } 405 | 406 | .buy hr, .sell hr { 407 | height: 1px; 408 | border: 0; 409 | border-top: 1.5px solid #D2D2D2; 410 | } 411 | 412 | .buy-coin, 413 | .sell-coin { 414 | display: flex; 415 | justify-content: space-between; 416 | } 417 | 418 | .buy-coin button, 419 | .sell-coin button { 420 | width: 200px; 421 | padding: 10px; 422 | background-color: transparent; 423 | border: 1.5px solid #B8BAC1; 424 | border-radius: 3px; 425 | font-size: 18px; 426 | } 427 | 428 | .buy-coin button:hover, 429 | .sell-coin button:hover { 430 | background-color: #F3F5F9; 431 | border-color: #B4C5FF; 432 | } 433 | 434 | .buy-coin button.active, 435 | .sell-coin button.active 436 | { 437 | background-color: #FAFAFA; 438 | border-width: 2px; 439 | cursor: default; 440 | } 441 | 442 | .buy-coin button.active { 443 | border-color: #5E84FF; 444 | } 445 | 446 | .sell-coin button.active { 447 | background-color: #FEFBF5; 448 | border-color: #FF9761; 449 | } 450 | 451 | p#counter-value { 452 | margin: 0px; 453 | color: #ABABAB; 454 | } 455 | 456 | .currency-inputs div { 457 | display: inline-block; 458 | width: 180px; 459 | height: 50px; 460 | margin: 5px; 461 | padding: 0px 15px 4px 15px; 462 | background-color: #F9F9F9; 463 | border: 1.5px solid #B8BAC1; 464 | border-radius: 3px; 465 | } 466 | 467 | .currency-inputs input { 468 | width: 75%; 469 | height: 100%; 470 | background-color: transparent; 471 | border: none; 472 | font-size: 20px; 473 | font-weight: 300; 474 | } 475 | 476 | .currency-inputs input:focus { 477 | outline: none; 478 | } 479 | 480 | .currency-inputs label { 481 | color: #B8BAC1; 482 | font-size: 20px; 483 | } 484 | 485 | .buy p, .sell p { 486 | margin-left: 10px; 487 | } 488 | 489 | .buy a, .sell a { 490 | text-decoration: none; 491 | } 492 | 493 | #arrow { 494 | margin: 0px; 495 | display: inline; 496 | color: #B8BAC1; 497 | font-size: 24px; 498 | } 499 | 500 | div.red-border { 501 | border-color: #FF6E78; 502 | } 503 | 504 | .hide { 505 | display: none; 506 | } 507 | 508 | .alert-limit { 509 | display: none; 510 | margin: 0px; 511 | padding: 0px; 512 | font-size: 12px; 513 | color: #FF6E78; 514 | } 515 | 516 | div.alert { 517 | padding: 0px; 518 | display: block; 519 | border: none; 520 | background: transparent; 521 | height: 20px; 522 | } 523 | 524 | button.submit-buy, 525 | button.submit-sell 526 | { 527 | font-size: 18px; 528 | color: white; 529 | margin: 5px; 530 | width: 460px; 531 | height: 50px; 532 | background-color: #3C84D3; 533 | border: 1px solid #316FB4; 534 | border-radius: 2px; 535 | } 536 | 537 | button.submit-sell { 538 | border-color: #A53D04; 539 | background-color: #E26F1E; 540 | } 541 | 542 | button.submit-buy:hover { 543 | background-color: #316FB4; 544 | } 545 | 546 | button.submit-sell:hover { 547 | background-color: #CC5400; 548 | } 549 | 550 | .currency-inputs input { 551 | width: 100px; 552 | } 553 | 554 | main.buy, 555 | main.sell { 556 | padding: 20px; 557 | margin: 0 auto; 558 | max-width: 450px; 559 | background-color: #FEFEFE; 560 | border-radius: 5px; 561 | border: 1px solid #999; 562 | } 563 | 564 | .buy [type=submit], 565 | .sell [type=submit] { 566 | width: 400px; 567 | } 568 | 569 | /*=============== 570 | SETTINGS 571 | ===============*/ 572 | 573 | .container h3 { 574 | margin-top: 0; 575 | } 576 | 577 | .container { 578 | vertical-align: top; 579 | display: inline-block; 580 | min-width: 450px; 581 | height: 200px; 582 | padding: 20px; 583 | margin: 10px; 584 | border: 1px solid #BDBFC0; 585 | border-radius: 4px; 586 | background-color: #F7F7F7; 587 | } 588 | 589 | .container input { 590 | display: block; 591 | padding: 0.8em; 592 | margin: 0.5em 0; 593 | width: 100%; 594 | height: 1em; 595 | font-size: 17px; 596 | border-radius: 2px; 597 | border: 1px solid #CBCBCB; 598 | } 599 | 600 | .container form { 601 | width: 20em; 602 | } 603 | 604 | .container button { 605 | width: 109%; 606 | height: 2.3em; 607 | font-size: 18px; 608 | color: white; 609 | border-radius: 4px; 610 | border: 1px solid #72777F; 611 | background-color: #77808D; 612 | } 613 | 614 | .container button:hover { 615 | background-color: #898A96; 616 | } 617 | 618 | .danger button { 619 | width: 350px; 620 | border-color: #CE2F2F; 621 | background-color: #FF4C4C; 622 | } 623 | 624 | .danger button:hover { 625 | background-color: #C84444; 626 | } 627 | 628 | .danger p { 629 | width: 30em; 630 | } 631 | 632 | span.alert { 633 | display: block; 634 | color: #EA2B41; 635 | } 636 | 637 | /*================== 638 | Media Queries 639 | ==================*/ 640 | @media (max-width: 768px) { 641 | .dashboard-container { 642 | display: block; 643 | width: 100%; 644 | } 645 | } -------------------------------------------------------------------------------- /test/cx_test.rb: -------------------------------------------------------------------------------- 1 | ENV["RACK_ENV"] = "test" 2 | 3 | require 'minitest/autorun' 4 | require 'minitest/reporters' 5 | require 'rack/test' 6 | require 'yaml' 7 | 8 | require_relative '../cx.rb' 9 | 10 | Minitest::Reporters.use! 11 | 12 | class CXTest < Minitest::Test 13 | include Rack::Test::Methods 14 | 15 | def app 16 | Sinatra::Application 17 | end 18 | 19 | def session 20 | last_request.env['rack.session'] 21 | end 22 | 23 | def admin_session 24 | { "rack.session" => { signin: { username: "admin", time: Time.now } } } 25 | end 26 | 27 | BTC_BEG = 0.987 28 | ETH_BEG = 2.896 29 | USD_BEG = 6320 30 | 31 | def setup 32 | Dir.chdir(ROOT) 33 | 34 | admin_data = { 35 | 'admin'=> { 36 | :password=>"$2a$10$XQq2o2l8zVCndc9Ol1MpI..T9ckk2quGlRRVdXFeKJ29ySnFkkH5W", 37 | :created=>"2017-11-03 22:08:11 -0500", 38 | :new_user=> false, 39 | :balances=>{:btc=>BTC_BEG, :eth=>ETH_BEG, :usd=>USD_BEG}, 40 | :transactions=>[] 41 | } 42 | } 43 | 44 | File.write(user_data_file_path, admin_data.to_yaml) 45 | end 46 | 47 | def teardown 48 | session.delete(:signin) if session[:signin] 49 | # File.delete('test/users_data.yml') 50 | end 51 | 52 | def format_number(num) 53 | whole, decimal = format('%.2f', num).split('.') 54 | comma_sliced = whole.reverse.scan(/\d{3}|\d+/).join(',').reverse 55 | comma_sliced + '.' + decimal 56 | end 57 | 58 | def read_users_data_yml 59 | YAML.load_file(user_data_file_path) 60 | end 61 | 62 | def btc_eth_prices 63 | current_prices = YAML.load_file('data/cache_prices.yml') 64 | [current_prices['BTC']['USD'], current_prices['ETH']['USD']] 65 | end 66 | 67 | def test_index 68 | get '/' 69 | assert_equal 200, last_response.status 70 | assert_equal 'text/html;charset=utf-8', last_response['Content-Type'] 71 | [ 72 | /BUY AND SELL CRYPTOCURRENCIES/i, 73 | /Sign In/i, 74 | /Sign Up/i, 75 | /View Charts/i 76 | ] 77 | .each do |pattern| 78 | assert_match pattern, last_response.body 79 | end 80 | end 81 | 82 | def test_chart 83 | historical_bpi = fetch_histohour_chart_data('BTC', limit: 180, aggregate: 4) 84 | get '/charts' 85 | assert_equal 200, last_response.status 86 | assert_equal 'text/html;charset=utf-8', last_response['Content-Type'] 87 | assert_match /Bitcoin Price: \$/, last_response.body 88 | 89 | historical_bpi.each do |date, price| 90 | assert_includes last_response.body, date 91 | assert_includes last_response.body, price.to_s 92 | end 93 | 94 | assert_includes last_response.body, Time.now.year.to_s 95 | end 96 | 97 | def test_signup_page 98 | get '/signup' 99 | assert_equal 200, last_response.status 100 | [ 101 | /Create Account<\/button>/i 104 | ] 105 | .each do |pattern| 106 | assert_match pattern, last_response.body 107 | end 108 | end 109 | 110 | def test_signup_success 111 | post '/user/signup', username: 'hello', password: '12345', agreed: 'true' 112 | assert_equal 302, last_response.status 113 | assert_includes session[:success], "Sign-up bonus!" 114 | assert session[:signin] 115 | 116 | user_data = YAML.load_file(user_data_file_path) 117 | assert_includes user_data, 'hello' 118 | 119 | assert_match /\/dashboard$/, last_response.location 120 | end 121 | 122 | def test_signup_error 123 | post '/user/signup', username: '', password: '', agreed: nil 124 | assert_equal 422, last_response.status 125 | [ 126 | /Please enter a username/, 127 | /Password must contain a non-space character/, 128 | /Please accept the user agreement/ 129 | ] 130 | .each do |pattern| 131 | assert_match pattern, last_response.body 132 | end 133 | 134 | post '/user/signup', username: 'admin', password: '12345', agreed: 'true' 135 | assert_equal 422, last_response.status 136 | assert_match /Username 'admin' is unavailable/, last_response.body 137 | 138 | post '/user/signup', username: 'hello world', password: '123', agreed: 'true' 139 | [ 140 | /Username must not contain spaces/, 141 | /Password too short/, 142 | ] 143 | .each do |pattern| 144 | assert_match pattern, last_response.body 145 | end 146 | 147 | post '/user/signup', username: 'welcome', password: '12345', agreed: 'yes' 148 | assert_match /Please accept the user agreement/, last_response.body 149 | end 150 | 151 | def test_signin_page 152 | get '/signin' 153 | assert_equal 200, last_response.status 154 | [ 155 | /Sign In<\/button>/i 158 | ] 159 | .each do |pattern| 160 | assert_match pattern, last_response.body 161 | end 162 | end 163 | 164 | def test_signin_valid_credentials 165 | post '/user/signin', username: 'admin', password: 'secret' 166 | assert_equal 302, last_response.status 167 | assert_match /signed in as 'admin'./i, session[:success] 168 | assert_equal Time.now.to_s, session[:signin][:time].to_s 169 | assert_equal 'admin', session[:signin][:username] 170 | 171 | assert_match /\/dashboard$/, last_response.location 172 | end 173 | 174 | def test_signin_invalid_credentials 175 | post '/user/signin', username: 'hello', password: 'secret' 176 | assert_equal 422, last_response.status 177 | assert_match /Invalid credentials/, last_response.body 178 | refute session[:success] 179 | refute session[:signin] 180 | 181 | post '/user/signin', username: 'admin', password: '1234' 182 | assert_equal 422, last_response.status 183 | assert_match /Invalid credentials/, last_response.body 184 | refute session[:success] 185 | refute session[:signin] 186 | end 187 | 188 | def test_signout_due_to_inactivity 189 | post '/user/signin', username: 'admin', password: 'secret' 190 | assert_equal 302, last_response.status 191 | 192 | sleep (TIME_OUT_SECONDS + 1) 193 | 194 | get '/dashboard' 195 | assert_equal 302, last_response.status 196 | assert_equal 'You have been logged out due to inactivity.', session[:failure] 197 | 198 | assert_match /\/signin$/, last_response.location 199 | end 200 | 201 | def test_dashboard_portfolio 202 | get '/dashboard', {}, admin_session 203 | assert_equal 200, last_response.status 204 | 205 | btc_price, eth_price = btc_eth_prices 206 | btc_counter_value = format_number((BTC_BEG * btc_price)) 207 | eth_counter_value = format_number((ETH_BEG * eth_price)) 208 | 209 | [ 210 | /Bitcoin[\s\S]+#{btc_counter_value[0..-3]}/, 211 | /Ether[\s\S]+#{eth_counter_value[0..-3]}/, 212 | /US Dollars[\s\S]+6320/, 213 | /Your Portfolio/, 214 | //, 215 | ] 216 | .each do |pattern| 217 | assert_match pattern, last_response.body 218 | end 219 | end 220 | 221 | def test_dashboard_transactions 222 | post '/user/signup', username: 'hello', password: '12345', agreed: 'true' 223 | assert_equal 302, last_response.status 224 | 225 | get '/dashboard' 226 | assert_equal 200, last_response.status 227 | assert_match /Deposit[\s\S]+USD/i, last_response.body 228 | refute_match(/Buy Bitcoin/i, last_response.body) 229 | refute_match(/Sell Bitcoin/i, last_response.body) 230 | 231 | btc_price, _ = btc_eth_prices 232 | usd_amt = 1000 233 | corresp_btc_buy_amt = usd_amt/btc_price 234 | 235 | post '/user/buy/btc', usd_amount: usd_amt, coin_amount: corresp_btc_buy_amt 236 | assert_equal 302, last_response.status 237 | 238 | get '/dashboard' 239 | assert_equal 200, last_response.status 240 | assert_match /Buy Bitcoin[\s\S]+#{corresp_btc_buy_amt}/i, last_response.body 241 | 242 | btc_price, _ = btc_eth_prices 243 | usd_amt = 500 244 | corresp_btc_sell_amt = usd_amt/btc_price 245 | 246 | post '/user/sell/btc', usd_amount: 500, coin_amount: corresp_btc_sell_amt 247 | assert_equal 302, last_response.status 248 | 249 | get '/dashboard' 250 | assert_equal 200, last_response.status 251 | assert_match /Sell Bitcoin[\s\S]+#{corresp_btc_sell_amt}/i, last_response.body 252 | end 253 | 254 | def test_new_user_signin 255 | post '/user/signup', username: 'hello', password: '12345', agreed: 'true' 256 | assert_equal 302, last_response.status 257 | users_data = read_users_data_yml 258 | assert_equal false, users_data['hello'][:new_user] 259 | assert_match /Sign-up bonus.+funded.+\$\d+/, session[:success] 260 | 261 | post '/user/signin', username: 'hello', password: '12345' 262 | assert_equal 302, last_response.status 263 | users_data = read_users_data_yml 264 | refute users_data['hello'][:new_user] 265 | end 266 | 267 | def test_buy_btc_page 268 | get '/buy/btc', {}, admin_session 269 | assert_equal 200, last_response.status 270 | btc_price, eth_price = btc_eth_prices 271 | usd_balance = read_users_data_yml['admin'][:balances][:usd] 272 | 273 | [ 274 | /Bitcoin[\S\s]+@\$#{format_number(btc_price)[0..-3]}/, 275 | /Ether[\S\s]+@\$#{format_number(eth_price)[0..-3]}/, 276 | /USD Balance:.+#{format_number(usd_balance)}/, 277 | /[\S\s]+Buy Bitcoin[\S\s]+<\/button>/ 278 | ] 279 | .each do |pattern| 280 | assert_match pattern, last_response.body 281 | end 282 | end 283 | 284 | def test_buy_eth_page 285 | get '/buy/eth', {}, admin_session 286 | assert_equal 200, last_response.status 287 | pattern = /[\S\s]+Buy Ether[\S\s]+<\/button>/ 288 | assert_match pattern, last_response.body 289 | end 290 | 291 | def test_buy_btc_success 292 | get '/', {}, admin_session 293 | btc_price, _ = btc_eth_prices 294 | usd_amt = 1000 295 | corresp_btc_amt = usd_amt/btc_price 296 | 297 | post '/user/buy/btc', usd_amount: usd_amt, coin_amount: corresp_btc_amt 298 | 299 | assert_equal 302, last_response.status 300 | assert_includes session[:success], "You have successfully purchased #{corresp_btc_amt} BTC!" 301 | 302 | balances = read_users_data_yml['admin'][:balances] 303 | assert_equal USD_BEG - 1000, balances[:usd] 304 | assert_equal BTC_BEG + corresp_btc_amt, balances[:btc] 305 | end 306 | 307 | def test_buy_eth_success 308 | get '/', {}, admin_session 309 | _, eth_price = btc_eth_prices 310 | usd_amt = 1000 311 | corresp_eth_amt = usd_amt/eth_price 312 | 313 | post '/user/buy/eth', usd_amount: usd_amt, coin_amount: corresp_eth_amt 314 | assert_equal 302, last_response.status 315 | assert_includes session[:success], "You have successfully purchased #{corresp_eth_amt} ETH!" 316 | 317 | balances = read_users_data_yml['admin'][:balances] 318 | assert_equal USD_BEG - 1000, balances[:usd] 319 | assert_equal ETH_BEG + corresp_eth_amt, balances[:eth] 320 | end 321 | 322 | def test_buy_btc_failure 323 | get '/', {}, admin_session 324 | btc_price, _ = btc_eth_prices 325 | 326 | post '/user/buy/btc', usd_amount: 1000, coin_amount: 2 327 | assert_equal 302, last_response.status 328 | assert_includes session[:failure], 'Price adjusted.' 329 | 330 | post '/user/buy/btc', usd_amount: 99999999, coin_amount: 99999999/btc_price 331 | assert_equal 302, last_response.status 332 | assert_includes session[:failure], 'Not enough funds' 333 | 334 | balances = read_users_data_yml['admin'][:balances] 335 | assert_equal USD_BEG, balances[:usd] 336 | assert_equal BTC_BEG, balances[:btc] 337 | end 338 | 339 | def test_buy_eth_failure 340 | get '/', {}, admin_session 341 | _, eth_price = btc_eth_prices 342 | 343 | post '/user/buy/eth', usd_amount: 1000, coin_amount: 2000 344 | assert_equal 302, last_response.status 345 | assert_includes session[:failure], 'Price adjusted.' 346 | 347 | post '/user/buy/eth', usd_amount: 99999999, coin_amount: 99999999/eth_price 348 | assert_equal 302, last_response.status 349 | assert_includes session[:failure], 'Not enough funds' 350 | 351 | balances = read_users_data_yml['admin'][:balances] 352 | assert_equal USD_BEG, balances[:usd] 353 | assert_equal ETH_BEG, balances[:eth] 354 | end 355 | 356 | def test_sell_success 357 | get '/', {}, admin_session 358 | btc_price, eth_price = btc_eth_prices 359 | usd_amt = 500 360 | corresp_eth_amt = usd_amt/eth_price 361 | corresp_btc_amt = usd_amt/btc_price 362 | 363 | post '/user/sell/eth', usd_amount: usd_amt, coin_amount: corresp_eth_amt 364 | assert_equal 302, last_response.status 365 | assert_includes session[:success], "successfully sold #{corresp_eth_amt} ETH" 366 | 367 | get '/' # clear session messages 368 | 369 | post '/user/sell/btc', usd_amount: usd_amt, coin_amount: corresp_btc_amt 370 | assert_equal 302, last_response.status 371 | assert_includes session[:success], "successfully sold #{corresp_btc_amt} BTC" 372 | 373 | balances = read_users_data_yml['admin'][:balances] 374 | assert_equal USD_BEG + 1000, balances[:usd] 375 | assert_equal ETH_BEG - corresp_eth_amt, balances[:eth] 376 | assert_equal BTC_BEG - corresp_btc_amt, balances[:btc] 377 | end 378 | 379 | def test_change_password 380 | get '/', {}, admin_session 381 | 382 | post '/user/update-password', old_password: 'secret', new_password: '1234' 383 | assert_equal 302, last_response.status 384 | assert_includes session[:success], 'Password successfully updated!' 385 | 386 | session.delete(:signin) 387 | 388 | post '/user/signin', username: 'admin', password: 'secret' 389 | assert_equal 422, last_response.status 390 | assert_match /invalid credentials/i, last_response.body 391 | 392 | post '/user/signin', username: 'admin', password: '1234' 393 | assert_equal 302, last_response.status 394 | assert_match /signed in as/i, session[:success] 395 | end 396 | 397 | def test_delete_account 398 | get '/', {}, admin_session 399 | 400 | post '/user/delete', password: 'secret' 401 | assert_equal 302, last_response.status 402 | assert_match /user.+admin.+deleted/i, session[:success] 403 | refute read_users_data_yml[:admin] 404 | 405 | get '/dashboard' 406 | assert_equal 302, last_response.status 407 | assert_match /please sign-in to continue/i, session[:failure] 408 | 409 | post '/user/signin', username: 'admin', password: 'secret' 410 | assert_equal 422, last_response.status 411 | assert_match /invalid credentials/i, last_response.body 412 | end 413 | end -------------------------------------------------------------------------------- /cx.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra' 2 | require 'sinatra/reloader' if development? 3 | require 'tilt/erubis' 4 | require 'sinatra/content_for' 5 | require 'chartkick' 6 | require 'bcrypt' 7 | require 'net/http' 8 | require 'json' 9 | require 'yaml' 10 | require 'redcarpet' 11 | require 'pry' 12 | 13 | ROOT = File.expand_path('..', __FILE__) 14 | 15 | CURRENT_PRICES_API = 'https://min-api.cryptocompare.com/data/' \ 16 | 'pricemulti?fsyms=BTC,ETH&tsyms=USD'.freeze 17 | 18 | TIME_OUT_SECONDS = (ENV['RACK_ENV'] == 'test' ? 2 : 1500) 19 | 20 | CURRENCY_NAMES = { 21 | btc: 'Bitcoin', 22 | eth: 'Ether', 23 | usd: 'US Dollars' 24 | }.freeze 25 | 26 | configure do 27 | enable :sessions 28 | set :session_secret, 'secret' 29 | set :erb, escape_html: true 30 | end 31 | 32 | helpers do 33 | def user_signed_in? 34 | session[:signin] && !timed_out? 35 | end 36 | 37 | def format_usd(num) 38 | whole, decimal = format('%.2f', num).split('.') 39 | comma_sliced = whole.reverse.scan(/\d{3}|\d+/).join(',').reverse 40 | '$' + comma_sliced + '.' + decimal 41 | end 42 | 43 | def buy_link(buy_coin, for_coin) 44 | "href=/buy/#{for_coin}" unless buy_coin == for_coin 45 | end 46 | 47 | def sell_link(sell_coin, for_coin) 48 | "href=/sell/#{for_coin}" unless sell_coin == for_coin 49 | end 50 | 51 | def class_active_status(buy_coin, for_coin) 52 | "class='active'" if buy_coin == for_coin 53 | end 54 | end 55 | 56 | before do 57 | @users_data = YAML.load_file(user_data_file_path) 58 | 59 | sign_user_out_if_idle 60 | end 61 | 62 | class Transaction 63 | attr_reader :type, :coin_amount, :usd_amount, :time 64 | 65 | def initialize(type, coin, coin_amount, usd_amount) 66 | @type = type 67 | @coin = coin 68 | @coin_amount = coin_amount 69 | @usd_amount = usd_amount 70 | @time = Time.now 71 | end 72 | 73 | def coin 74 | @coin.upcase 75 | end 76 | 77 | def price 78 | usd_amount / coin_amount 79 | end 80 | end 81 | 82 | def parse_api(url) 83 | uri = URI(url) 84 | response = Net::HTTP.get(uri) 85 | JSON.parse(response) 86 | end 87 | 88 | def cypto_compare_histohour_api(coin, limit, aggregate) 89 | "https://min-api.cryptocompare.com/data/histohour?fsym=#{coin.upcase}" \ 90 | "&tsym=USD&limit=#{limit}&aggregate=#{aggregate}&e=CCCAGG" 91 | end 92 | 93 | def fetch_histohour_chart_data(coin, limit:, aggregate:) 94 | begin 95 | url = cypto_compare_histohour_api(coin, limit, aggregate) 96 | raw_data = parse_api(url) 97 | cache_historical_data(coin, raw_data) 98 | rescue SocketError, Errno 99 | raw_data = YAML.load_file('data/cache_hist.yml')[coin] 100 | end 101 | 102 | parse_historical_data(raw_data) 103 | end 104 | 105 | def cache_historical_data(coin, raw_data) 106 | cache_file = 'data/cache_hist.yml' 107 | cache_data = YAML.load_file(cache_file) 108 | 109 | cache_data[coin] = raw_data 110 | File.write(cache_file, cache_data.to_yaml) 111 | end 112 | 113 | def cache_current_prices(current_prices) 114 | File.write('data/cache_prices.yml', current_prices.to_yaml) 115 | end 116 | 117 | def fetch_current_prices 118 | begin 119 | session[:offline] = false 120 | current_prices = parse_api(CURRENT_PRICES_API) 121 | 122 | cache_current_prices(current_prices) 123 | current_prices 124 | rescue SocketError, Errno 125 | session[:offline] = true 126 | YAML.load_file('data/cache_prices.yml') 127 | end 128 | end 129 | 130 | def user_data_file_path 131 | if ENV['RACK_ENV'] == 'test' 132 | 'test/data/users_data.yml' 133 | else 134 | 'data/users_data.yml' 135 | end 136 | end 137 | 138 | def credential_invalids(username, password, agreed = nil) 139 | { 140 | 'Please enter a username.' => username.empty?, 141 | 'Username must not contain spaces.' => username.include?(' '), 142 | 'Username too long.' => username.size > 30, 143 | "Username '#{username}' is unavailable." => @users_data.key?(username), 144 | 'Password too short.' => (1..3).cover?(password.size), 145 | 'Password must contain a non-space character.' => password.strip.empty?, 146 | 'Please accept the user agreement.' => agreed != 'true' 147 | } 148 | end 149 | 150 | def new_password_invalids(password) 151 | { 152 | 'New password too short.' => (1..3).cover?(password.size), 153 | 'New password must contain a non-space character.' => 154 | password.strip.empty? 155 | } 156 | end 157 | 158 | def build_error_message(errors) 159 | errors.select { |_, condition| condition } 160 | .keys 161 | .join('
') 162 | end 163 | 164 | def create_new_user_data(password) 165 | sign_up_bonus = rand(8999..19_999) 166 | new_trx = Transaction.new(:deposit, 'USD', sign_up_bonus, sign_up_bonus) 167 | { 168 | password: BCrypt::Password.create(password).to_s, 169 | created: Time.now.to_s, 170 | new_user: true, 171 | balances: { btc: 0, eth: 0, usd: sign_up_bonus }, 172 | transactions: [new_trx] 173 | } 174 | end 175 | 176 | def credentials_match?(username, password) 177 | return false unless @users_data.key?(username) 178 | 179 | stored_password = @users_data[username][:password] 180 | BCrypt::Password.new(stored_password) == password 181 | end 182 | 183 | def sign_user_in(username) 184 | session[:signin] = { username: username, time: Time.now } 185 | end 186 | 187 | def reset_idle_time 188 | session[:signin][:time] = Time.now 189 | end 190 | 191 | def sign_user_out 192 | session.delete(:signin) 193 | end 194 | 195 | def timed_out? 196 | session_idle_seconds = Time.now - session[:signin][:time] 197 | session_idle_seconds > TIME_OUT_SECONDS 198 | end 199 | 200 | def require_user_signed_in 201 | unless user_signed_in? 202 | session[:failure] ||= 'Please sign-in to continue.' 203 | redirect '/signin' 204 | end 205 | end 206 | 207 | def sign_user_out_if_idle 208 | if session[:signin] && timed_out? 209 | sign_user_out 210 | session[:failure] = 'You have been logged out due to inactivity.' 211 | end 212 | end 213 | 214 | def usd_funded_message 215 | if signed_in_user_data[:new_user] 216 | 'Sign-up bonus! Your account was funded ' \ 217 | "+#{format_usd(user_balances[:usd])}.
" 218 | end 219 | end 220 | 221 | def sign_in_message 222 | "#{usd_funded_message}" \ 223 | "Signed in as '#{session[:signin][:username]}'.
" \ 224 | "Timestamp: #{session[:signin][:time]}." 225 | end 226 | 227 | def write_new_user_data!(username, password) 228 | @users_data[username] = create_new_user_data(password) 229 | update_users_data! 230 | end 231 | 232 | def update_users_data! 233 | File.write(user_data_file_path, @users_data.to_yaml) 234 | end 235 | 236 | def signed_in_user_data 237 | username = session[:signin][:username] 238 | @users_data[username] 239 | end 240 | 241 | def user_balances 242 | require_user_signed_in 243 | signed_in_user_data[:balances] 244 | end 245 | 246 | def spot_price_range(usd_amt, coin_amt, coin) 247 | current_coin_price = fetch_current_prices[coin]['USD'] 248 | (0.995..1.005).cover?(current_coin_price / (usd_amt / coin_amt)) 249 | end 250 | 251 | def invalid_numbers(*numbers) 252 | numbers.any? { |num| num < 0 || !num.is_a?(Numeric) } 253 | end 254 | 255 | def purchase_validation_errors(usd_amt, coin_amt, coin) 256 | { 257 | 'Price adjusted. Please try again.' => 258 | !spot_price_range(usd_amt, coin_amt, coin), 259 | "Not enough funds to purchase #{coin_amt} #{coin}." => 260 | (usd_amt > user_balances[:usd]), 261 | 'Invalid inputs. Please try again.' => invalid_numbers(usd_amt, coin_amt), 262 | 'Minimum purchase of $1 is required.' => usd_amt < 1 263 | } 264 | end 265 | 266 | def sell_validation_errors(usd_amt, coin_amt, coin) 267 | { 268 | 'Price adjusted. Please try again.' => 269 | !spot_price_range(usd_amt, coin_amt, coin), 270 | "You don't have enough #{coin} to sell." => 271 | (coin_amt > user_balances[coin.downcase.to_sym]), 272 | 'Invalid inputs. Please try again.' => invalid_numbers(usd_amt, coin_amt), 273 | "Minimum sale amount of 0.000001 #{coin} is required." => 274 | coin_amt < 0.000001 275 | } 276 | end 277 | 278 | def falsify_new_user_status! 279 | signed_in_user_data[:new_user] = false 280 | update_users_data! 281 | end 282 | 283 | def create_transaction(type, coin, coin_amt, usd_amt) 284 | new_trx = Transaction.new(type, coin, coin_amt, usd_amt) 285 | signed_in_user_data[:transactions] << new_trx 286 | end 287 | 288 | def format_portfolio_chart_data(portfolio_data, counter_values) 289 | portfolio_data.map do |symbol, balance| 290 | counter_value = balance * counter_values[symbol].round(2) 291 | [symbol.upcase, counter_value] 292 | end.to_h 293 | end 294 | 295 | def sort_trx_by_most_recent 296 | signed_in_user_data[:transactions].sort_by(&:time) 297 | .reverse 298 | end 299 | 300 | def unix_time_to_date(unix_time) 301 | Time.strptime(unix_time.to_s, '%s').to_s 302 | end 303 | 304 | def parse_historical_data(raw_data) 305 | raw_data['Data'].map do |data| 306 | [unix_time_to_date(data['time']), data['close']] 307 | end.to_h 308 | end 309 | 310 | not_found do 311 | erb :not_found 312 | end 313 | 314 | get '/' do 315 | redirect '/dashboard' if user_signed_in? 316 | erb :index 317 | end 318 | 319 | get '/charts' do 320 | @historical_bpi = fetch_histohour_chart_data('BTC', limit: 180, aggregate: 4) 321 | @min_btc_price, @max_btc_price = @historical_bpi.values.minmax 322 | 323 | @historical_eth = fetch_histohour_chart_data('ETH', limit: 180, aggregate: 4) 324 | @min_eth_price, @max_eth_price = @historical_eth.values.minmax 325 | 326 | current_prices = fetch_current_prices 327 | @current_btc_price = current_prices['BTC']['USD'] 328 | @current_eth_price = current_prices['ETH']['USD'] 329 | 330 | erb :charts 331 | end 332 | 333 | get '/signup' do 334 | redirect '/dashboard' if user_signed_in? 335 | erb :signup 336 | end 337 | 338 | post '/user/signup' do 339 | @username = params[:username] 340 | @password = params[:password] 341 | @agreed = params[:agreed] 342 | new_username = @username.strip 343 | 344 | errors = credential_invalids(new_username, @password, @agreed) 345 | 346 | if errors.none? { |_, condition| condition } 347 | write_new_user_data!(@username, @password) 348 | 349 | sign_user_in(@username) 350 | session[:success] = sign_in_message 351 | falsify_new_user_status! 352 | 353 | redirect '/dashboard' 354 | else 355 | session[:failure] = build_error_message(errors) 356 | status 422 357 | erb :signup 358 | end 359 | end 360 | 361 | get '/signin' do 362 | redirect '/' if user_signed_in? 363 | erb :signin 364 | end 365 | 366 | post '/user/signin' do 367 | sign_user_out 368 | 369 | @username = params[:username].strip 370 | @password = params[:password] 371 | 372 | if credentials_match?(@username, @password) 373 | sign_user_in(@username) 374 | session[:success] = sign_in_message 375 | redirect '/dashboard' 376 | else 377 | session[:failure] = 'Invalid credentials. Please try again.' 378 | status 422 379 | erb :signin 380 | end 381 | end 382 | 383 | get '/dashboard' do 384 | require_user_signed_in 385 | reset_idle_time 386 | 387 | @portfolio = signed_in_user_data[:balances] 388 | current_prices = fetch_current_prices 389 | 390 | @counter_values = { 391 | btc: current_prices['BTC']['USD'], 392 | eth: current_prices['ETH']['USD'], 393 | usd: 1 394 | } 395 | 396 | @portfolio_chart_data = 397 | format_portfolio_chart_data(@portfolio, @counter_values) 398 | 399 | @transactions = sort_trx_by_most_recent 400 | 401 | erb :dashboard 402 | end 403 | 404 | post '/user/signout' do 405 | sign_user_out 406 | session.delete(:failure) if session[:failure] 407 | redirect '/' 408 | end 409 | 410 | get '/buy' do 411 | require_user_signed_in 412 | redirect '/buy/btc' 413 | end 414 | 415 | get '/buy/:coin' do 416 | current_prices = fetch_current_prices 417 | require_user_signed_in 418 | reset_idle_time 419 | 420 | @coin = params[:coin] 421 | 422 | @current_btc_price = current_prices['BTC']['USD'] 423 | @current_eth_price = current_prices['ETH']['USD'] 424 | 425 | @usd_balance = user_balances[:usd] 426 | 427 | erb :buy 428 | end 429 | 430 | post '/user/buy/:coin' do 431 | require_user_signed_in 432 | reset_idle_time 433 | 434 | coin = params[:coin] 435 | 436 | @usd_amount = params[:usd_amount].to_f 437 | @coin_amount = params[:coin_amount].to_f 438 | errors = purchase_validation_errors(@usd_amount, @coin_amount, coin.upcase) 439 | 440 | if errors.none? { |_, condition| condition } 441 | session[:success] = 'You have successfully purchased' \ 442 | " #{@coin_amount} #{coin.upcase}!" 443 | 444 | signed_in_user_data[:balances][:usd] -= @usd_amount.round(2) 445 | signed_in_user_data[:balances][coin.to_sym] += @coin_amount 446 | create_transaction(:buy, coin, @coin_amount, @usd_amount) 447 | update_users_data! 448 | 449 | redirect '/dashboard' 450 | else 451 | session[:failure] = build_error_message(errors) 452 | redirect "/buy/#{coin}" 453 | end 454 | end 455 | 456 | get '/sell' do 457 | require_user_signed_in 458 | redirect '/sell/btc' 459 | end 460 | 461 | get '/sell/:coin' do 462 | current_prices = fetch_current_prices 463 | require_user_signed_in 464 | reset_idle_time 465 | 466 | @coin = params[:coin] 467 | 468 | @current_btc_price = current_prices['BTC']['USD'] 469 | @current_eth_price = current_prices['ETH']['USD'] 470 | 471 | @coin_balance = signed_in_user_data[:balances][@coin.to_sym] 472 | 473 | erb :sell 474 | end 475 | 476 | post '/user/sell/:coin' do 477 | require_user_signed_in 478 | reset_idle_time 479 | 480 | coin = params[:coin] 481 | 482 | @usd_amount = params[:usd_amount].to_f 483 | @coin_amount = params[:coin_amount].to_f 484 | errors = sell_validation_errors(@usd_amount, @coin_amount, coin.upcase) 485 | 486 | if errors.none? { |_, condition| condition } 487 | session[:success] = "You have successfully sold #{@coin_amount} " \ 488 | "#{coin.upcase}. Account value +#{format_usd(@usd_amount)}." 489 | 490 | signed_in_user_data[:balances][:usd] += @usd_amount.round(2) 491 | signed_in_user_data[:balances][coin.to_sym] -= @coin_amount 492 | create_transaction(:sell, coin, @coin_amount, -@usd_amount) 493 | update_users_data! 494 | 495 | redirect '/dashboard' 496 | else 497 | session[:failure] = build_error_message(errors) 498 | status 422 499 | redirect "/sell/#{coin}" 500 | end 501 | end 502 | 503 | get '/settings' do 504 | require_user_signed_in 505 | reset_idle_time 506 | 507 | erb :settings 508 | end 509 | 510 | post '/user/update-password' do 511 | require_user_signed_in 512 | reset_idle_time 513 | username = session[:signin][:username] 514 | 515 | @old_password = params[:old_password] 516 | new_password = params[:new_password] 517 | 518 | errors = new_password_invalids(new_password) 519 | 520 | if credentials_match?(username, @old_password) && 521 | errors.none? { |_, condition| condition } 522 | 523 | signed_in_user_data[:password] = BCrypt::Password.create(new_password).to_s 524 | 525 | update_users_data! 526 | 527 | session[:success] = 'Password successfully updated!' 528 | redirect '/dashboard' 529 | else 530 | session[:failure] = build_error_message(errors) 531 | if session[:failure].empty? 532 | session[:failure] = 'Invalid password. Please try again' 533 | end 534 | 535 | status 422 536 | erb :settings 537 | end 538 | end 539 | 540 | post '/user/delete' do 541 | require_user_signed_in 542 | reset_idle_time 543 | username = session[:signin][:username] 544 | 545 | @password = params[:password] 546 | 547 | if credentials_match?(username, @password) 548 | sign_user_out 549 | @users_data.delete(username) 550 | update_users_data! 551 | 552 | session[:success] = "User account '#{username}' has been deleted!" 553 | redirect '/' 554 | else 555 | session[:failure] = 'Invalid password. Please try again' 556 | status 422 557 | erb :settings 558 | end 559 | end 560 | 561 | get '/agreement' do 562 | @markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML) 563 | @user_agreement = File.read('data/agreement.md') 564 | 565 | erb :agreement 566 | end 567 | -------------------------------------------------------------------------------- /public/javascript/jsapi.js: -------------------------------------------------------------------------------- 1 | if(!window['googleLT_']){window['googleLT_']=(new Date()).getTime();}if (!window['google']) { 2 | window['google'] = {}; 3 | } 4 | if (!window['google']['loader']) { 5 | window['google']['loader'] = {}; 6 | google.loader.ServiceBase = 'https://www.google.com/uds'; 7 | google.loader.GoogleApisBase = 'https://ajax.googleapis.com/ajax'; 8 | google.loader.ApiKey = 'notsupplied'; 9 | google.loader.KeyVerified = true; 10 | google.loader.LoadFailure = false; 11 | google.loader.Secure = true; 12 | google.loader.GoogleLocale = 'www.google.com'; 13 | google.loader.ClientLocation = null; 14 | google.loader.AdditionalParams = ''; 15 | (function() {var g=this,l=function(a,b,c){a=a.split(".");c=c||g;a[0]in c||!c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)a.length||void 0===b?c=c[d]&&c[d]!==Object.prototype[d]?c[d]:c[d]={}:c[d]=b},m=function(a,b,c){a[b]=c};var w=function(a,b){if(b)a=a.replace(n,"&").replace(p,"<").replace(q,">").replace(r,""").replace(t,"'").replace(u,"�");else{if(!v.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(n,"&"));-1!=a.indexOf("<")&&(a=a.replace(p,"<"));-1!=a.indexOf(">")&&(a=a.replace(q,">"));-1!=a.indexOf('"')&&(a=a.replace(r,"""));-1!=a.indexOf("'")&&(a=a.replace(t,"'"));-1!=a.indexOf("\x00")&&(a=a.replace(u,"�"))}return a},n=/&/g,p=//g,r=/"/g,t=/'/g,u=/\x00/g,v=/[\x00&<>"']/;var x=/^[\w+/]+[=]{0,2}$/,y=function(a){if((a=(a||g).document.querySelector("script[nonce]"))&&(a=a.nonce||a.getAttribute("nonce"))&&x.test(a))return a};function z(a){return a in A?A[a]:A[a]=-1!=navigator.userAgent.toLowerCase().indexOf(a)}var A={};function C(a,b){var c=function(){};c.prototype=b.prototype;a.da=b.prototype;a.prototype=new c}function D(a,b,c){var d=Array.prototype.slice.call(arguments,2)||[];return function(){return a.apply(b,d.concat(Array.prototype.slice.call(arguments)))}}function E(a){a=Error(a);a.toString=function(){return this.message};return a} 16 | function F(a,b){a=a.split(/\./);for(var c=window,d=0;d\x3c/script>"):(z("safari")||z("konqueror"))&&window.setTimeout(M,10)),J.push(a)):K(window,"load",a)};l("google.setOnLoadCallback",google.ca); 20 | function K(a,b,c){if(a.addEventListener)a.addEventListener(b,c,!1);else if(a.attachEvent)a.attachEvent("on"+b,c);else{var d=a["on"+b];a["on"+b]=null!=d?N([c,d]):c}}function N(a){return function(){for(var b=0;b\x3c/script>")):"css"==a&& 23 | (d='"))};l("google.loader.writeLoadTag",google.loader.f);google.loader.$=function(a){I=a};l("google.loader.rfm",google.loader.$);google.loader.ba=function(a){for(var b in a)"string"==typeof b&&b&&":"==b.charAt(0)&&!H[b]&&(H[b]=new O(b.substring(1),a[b]))};l("google.loader.rpl",google.loader.ba); 24 | google.loader.aa=function(a){if((a=a.specs)&&a.length)for(var b=0;b 25) { 1144 | maxLabelSize = 25; 1145 | } 1146 | options.scales.xAxes[0].ticks.callback = function (value) { 1147 | value = toStr(value); 1148 | if (value.length > maxLabelSize) { 1149 | return value.substring(0, maxLabelSize - 2) + "..."; 1150 | } else { 1151 | return value; 1152 | } 1153 | }; 1154 | }; 1155 | 1156 | var jsOptions = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setMin, setMax, setStacked, setXtitle, setYtitle); 1157 | 1158 | var createDataTable = function (chart, options, chartType) { 1159 | var datasets = []; 1160 | var labels = []; 1161 | 1162 | var colors = chart.options.colors || defaultColors; 1163 | 1164 | var day = true; 1165 | var week = true; 1166 | var dayOfWeek; 1167 | var month = true; 1168 | var year = true; 1169 | var hour = true; 1170 | var minute = true; 1171 | var detectType = (chartType === "line" || chartType === "area") && !chart.discrete; 1172 | 1173 | var series = chart.data; 1174 | 1175 | var sortedLabels = []; 1176 | 1177 | var i, j, s, d, key, rows = []; 1178 | for (i = 0; i < series.length; i++) { 1179 | s = series[i]; 1180 | 1181 | for (j = 0; j < s.data.length; j++) { 1182 | d = s.data[j]; 1183 | key = detectType ? d[0].getTime() : d[0]; 1184 | if (!rows[key]) { 1185 | rows[key] = new Array(series.length); 1186 | } 1187 | rows[key][i] = toFloat(d[1]); 1188 | if (sortedLabels.indexOf(key) === -1) { 1189 | sortedLabels.push(key); 1190 | } 1191 | } 1192 | } 1193 | 1194 | if (detectType || chart.options.xtype === "number") { 1195 | sortedLabels.sort(sortByNumber); 1196 | } 1197 | 1198 | var rows2 = []; 1199 | for (j = 0; j < series.length; j++) { 1200 | rows2.push([]); 1201 | } 1202 | 1203 | var value; 1204 | var k; 1205 | for (k = 0; k < sortedLabels.length; k++) { 1206 | i = sortedLabels[k]; 1207 | if (detectType) { 1208 | value = new Date(toFloat(i)); 1209 | // TODO make this efficient 1210 | day = day && isDay(value); 1211 | if (!dayOfWeek) { 1212 | dayOfWeek = value.getDay(); 1213 | } 1214 | week = week && isWeek(value, dayOfWeek); 1215 | month = month && isMonth(value); 1216 | year = year && isYear(value); 1217 | hour = hour && isHour(value); 1218 | minute = minute && isMinute(value); 1219 | } else { 1220 | value = i; 1221 | } 1222 | labels.push(value); 1223 | for (j = 0; j < series.length; j++) { 1224 | // Chart.js doesn't like undefined 1225 | rows2[j].push(rows[i][j] === undefined ? null : rows[i][j]); 1226 | } 1227 | } 1228 | 1229 | for (i = 0; i < series.length; i++) { 1230 | s = series[i]; 1231 | 1232 | var color = s.color || colors[i]; 1233 | var backgroundColor = chartType !== "line" ? addOpacity(color, 0.5) : color; 1234 | 1235 | var dataset = { 1236 | label: s.name, 1237 | data: rows2[i], 1238 | fill: chartType === "area", 1239 | borderColor: color, 1240 | backgroundColor: backgroundColor, 1241 | pointBackgroundColor: color, 1242 | borderWidth: 2 1243 | }; 1244 | 1245 | if (s.stack) { 1246 | dataset.stack = s.stack; 1247 | } 1248 | 1249 | if (chart.options.curve === false) { 1250 | dataset.lineTension = 0; 1251 | } 1252 | 1253 | if (chart.options.points === false) { 1254 | dataset.pointRadius = 0; 1255 | dataset.pointHitRadius = 5; 1256 | } 1257 | 1258 | datasets.push(merge(dataset, s.library || {})); 1259 | } 1260 | 1261 | if (detectType && labels.length > 0) { 1262 | var minTime = labels[0].getTime(); 1263 | var maxTime = labels[0].getTime(); 1264 | for (i = 1; i < labels.length; i++) { 1265 | value = labels[i].getTime(); 1266 | if (value < minTime) { 1267 | minTime = value; 1268 | } 1269 | if (value > maxTime) { 1270 | maxTime = value; 1271 | } 1272 | } 1273 | 1274 | var timeDiff = (maxTime - minTime) / (86400 * 1000.0); 1275 | 1276 | if (!options.scales.xAxes[0].time.unit) { 1277 | var step; 1278 | if (year || timeDiff > 365 * 10) { 1279 | options.scales.xAxes[0].time.unit = "year"; 1280 | step = 365; 1281 | } else if (month || timeDiff > 30 * 10) { 1282 | options.scales.xAxes[0].time.unit = "month"; 1283 | step = 30; 1284 | } else if (day || timeDiff > 10) { 1285 | options.scales.xAxes[0].time.unit = "day"; 1286 | step = 1; 1287 | } else if (hour || timeDiff > 0.5) { 1288 | options.scales.xAxes[0].time.displayFormats = {hour: "MMM D, h a"}; 1289 | options.scales.xAxes[0].time.unit = "hour"; 1290 | step = 1 / 24.0; 1291 | } else if (minute) { 1292 | options.scales.xAxes[0].time.displayFormats = {minute: "h:mm a"}; 1293 | options.scales.xAxes[0].time.unit = "minute"; 1294 | step = 1 / 24.0 / 60.0; 1295 | } 1296 | 1297 | if (step && timeDiff > 0) { 1298 | var unitStepSize = Math.ceil(timeDiff / step / (chart.element.offsetWidth / 100.0)); 1299 | if (week && step === 1) { 1300 | unitStepSize = Math.ceil(unitStepSize / 7.0) * 7; 1301 | } 1302 | options.scales.xAxes[0].time.unitStepSize = unitStepSize; 1303 | } 1304 | } 1305 | 1306 | if (!options.scales.xAxes[0].time.tooltipFormat) { 1307 | if (day) { 1308 | options.scales.xAxes[0].time.tooltipFormat = "ll"; 1309 | } else if (hour) { 1310 | options.scales.xAxes[0].time.tooltipFormat = "MMM D, h a"; 1311 | } else if (minute) { 1312 | options.scales.xAxes[0].time.tooltipFormat = "h:mm a"; 1313 | } 1314 | } 1315 | } 1316 | 1317 | var data = { 1318 | labels: labels, 1319 | datasets: datasets 1320 | }; 1321 | 1322 | return data; 1323 | }; 1324 | 1325 | this.renderLineChart = function (chart, chartType) { 1326 | if (chart.options.xtype === "number") { 1327 | return self.renderScatterChart(chart, chartType, true); 1328 | } 1329 | 1330 | var chartOptions = {}; 1331 | if (chartType === "area") { 1332 | // TODO fix area stacked 1333 | // chartOptions.stacked = true; 1334 | } 1335 | // fix for https://github.com/chartjs/Chart.js/issues/2441 1336 | if (!chart.options.max && allZeros(chart.data)) { 1337 | chartOptions.max = 1; 1338 | } 1339 | 1340 | var options = jsOptions(chart, merge(chartOptions, chart.options)); 1341 | 1342 | var data = createDataTable(chart, options, chartType || "line"); 1343 | 1344 | options.scales.xAxes[0].type = chart.discrete ? "category" : "time"; 1345 | 1346 | drawChart(chart, "line", data, options); 1347 | }; 1348 | 1349 | this.renderPieChart = function (chart) { 1350 | var options = merge({}, baseOptions); 1351 | if (chart.options.donut) { 1352 | options.cutoutPercentage = 50; 1353 | } 1354 | 1355 | if ("legend" in chart.options) { 1356 | hideLegend(options, chart.options.legend); 1357 | } 1358 | 1359 | if (chart.options.title) { 1360 | setTitle(options, chart.options.title); 1361 | } 1362 | 1363 | options = merge(options, chart.options.library || {}); 1364 | 1365 | var labels = []; 1366 | var values = []; 1367 | for (var i = 0; i < chart.data.length; i++) { 1368 | var point = chart.data[i]; 1369 | labels.push(point[0]); 1370 | values.push(point[1]); 1371 | } 1372 | 1373 | var data = { 1374 | labels: labels, 1375 | datasets: [ 1376 | { 1377 | data: values, 1378 | backgroundColor: chart.options.colors || defaultColors 1379 | } 1380 | ] 1381 | }; 1382 | 1383 | drawChart(chart, "pie", data, options); 1384 | }; 1385 | 1386 | this.renderColumnChart = function (chart, chartType) { 1387 | var options; 1388 | if (chartType === "bar") { 1389 | options = jsOptionsFunc(merge(baseOptions, defaultOptions), hideLegend, setTitle, setBarMin, setBarMax, setStacked, setXtitle, setYtitle)(chart, chart.options); 1390 | } else { 1391 | options = jsOptions(chart, chart.options); 1392 | } 1393 | var data = createDataTable(chart, options, "column"); 1394 | setLabelSize(chart, data, options); 1395 | drawChart(chart, (chartType === "bar" ? "horizontalBar" : "bar"), data, options); 1396 | }; 1397 | 1398 | var self = this; 1399 | 1400 | this.renderAreaChart = function (chart) { 1401 | self.renderLineChart(chart, "area"); 1402 | }; 1403 | 1404 | this.renderBarChart = function (chart) { 1405 | self.renderColumnChart(chart, "bar"); 1406 | }; 1407 | 1408 | this.renderScatterChart = function (chart, chartType, lineChart) { 1409 | chartType = chartType || "line"; 1410 | 1411 | var options = jsOptions(chart, chart.options); 1412 | 1413 | var colors = chart.options.colors || defaultColors; 1414 | 1415 | var datasets = []; 1416 | var series = chart.data; 1417 | for (var i = 0; i < series.length; i++) { 1418 | var s = series[i]; 1419 | var d = []; 1420 | for (var j = 0; j < s.data.length; j++) { 1421 | var point = { 1422 | x: toFloat(s.data[j][0]), 1423 | y: toFloat(s.data[j][1]) 1424 | }; 1425 | if (chartType === "bubble") { 1426 | point.r = toFloat(s.data[j][2]); 1427 | } 1428 | d.push(point); 1429 | } 1430 | 1431 | var color = s.color || colors[i]; 1432 | var backgroundColor = chartType === "area" ? addOpacity(color, 0.5) : color; 1433 | 1434 | datasets.push({ 1435 | label: s.name, 1436 | showLine: lineChart || false, 1437 | data: d, 1438 | borderColor: color, 1439 | backgroundColor: backgroundColor, 1440 | pointBackgroundColor: color, 1441 | fill: chartType === "area" 1442 | }) 1443 | } 1444 | 1445 | if (chartType === "area") { 1446 | chartType = "line"; 1447 | } 1448 | 1449 | var data = {datasets: datasets}; 1450 | 1451 | options.scales.xAxes[0].type = "linear"; 1452 | options.scales.xAxes[0].position = "bottom"; 1453 | 1454 | drawChart(chart, chartType, data, options); 1455 | }; 1456 | 1457 | this.renderBubbleChart = function (chart) { 1458 | this.renderScatterChart(chart, "bubble"); 1459 | }; 1460 | }; 1461 | 1462 | adapters.unshift(ChartjsAdapter); 1463 | } 1464 | } 1465 | 1466 | function renderChart(chartType, chart) { 1467 | callAdapter(chartType, chart); 1468 | if (chart.options.download && !chart.downloadAttached && chart.adapter === "chartjs") { 1469 | addDownloadButton(chart); 1470 | } 1471 | } 1472 | 1473 | // TODO remove chartType if cross-browser way 1474 | // to get the name of the chart class 1475 | function callAdapter(chartType, chart) { 1476 | var i, adapter, fnName, adapterName; 1477 | fnName = "render" + chartType; 1478 | adapterName = chart.options.adapter; 1479 | 1480 | loadAdapters(); 1481 | 1482 | for (i = 0; i < adapters.length; i++) { 1483 | adapter = adapters[i]; 1484 | if ((!adapterName || adapterName === adapter.name) && isFunction(adapter[fnName])) { 1485 | chart.adapter = adapter.name; 1486 | return adapter[fnName](chart); 1487 | } 1488 | } 1489 | throw new Error("No adapter found"); 1490 | } 1491 | 1492 | // process data 1493 | 1494 | var toFormattedKey = function (key, keyType) { 1495 | if (keyType === "number") { 1496 | key = toFloat(key); 1497 | } else if (keyType === "datetime") { 1498 | key = toDate(key); 1499 | } else { 1500 | key = toStr(key); 1501 | } 1502 | return key; 1503 | }; 1504 | 1505 | var formatSeriesData = function (data, keyType) { 1506 | var r = [], key, j; 1507 | for (j = 0; j < data.length; j++) { 1508 | if (keyType === "bubble") { 1509 | r.push([toFloat(data[j][0]), toFloat(data[j][1]), toFloat(data[j][2])]); 1510 | } else { 1511 | key = toFormattedKey(data[j][0], keyType); 1512 | r.push([key, toFloat(data[j][1])]); 1513 | } 1514 | } 1515 | if (keyType === "datetime") { 1516 | r.sort(sortByTime); 1517 | } else if (keyType === "number") { 1518 | r.sort(sortByNumberSeries); 1519 | } 1520 | return r; 1521 | }; 1522 | 1523 | function isMinute(d) { 1524 | return d.getMilliseconds() === 0 && d.getSeconds() === 0; 1525 | } 1526 | 1527 | function isHour(d) { 1528 | return isMinute(d) && d.getMinutes() === 0; 1529 | } 1530 | 1531 | function isDay(d) { 1532 | return isHour(d) && d.getHours() === 0; 1533 | } 1534 | 1535 | function isWeek(d, dayOfWeek) { 1536 | return isDay(d) && d.getDay() === dayOfWeek; 1537 | } 1538 | 1539 | function isMonth(d) { 1540 | return isDay(d) && d.getDate() === 1; 1541 | } 1542 | 1543 | function isYear(d) { 1544 | return isMonth(d) && d.getMonth() === 0; 1545 | } 1546 | 1547 | function isDate(obj) { 1548 | return !isNaN(toDate(obj)) && toStr(obj).length >= 6; 1549 | } 1550 | 1551 | function allZeros(data) { 1552 | var i, j, d; 1553 | for (i = 0; i < data.length; i++) { 1554 | d = data[i].data; 1555 | for (j = 0; j < d.length; j++) { 1556 | if (d[j][1] != 0) { 1557 | return false; 1558 | } 1559 | } 1560 | } 1561 | return true; 1562 | } 1563 | 1564 | function detectDiscrete(series) { 1565 | var i, j, data; 1566 | for (i = 0; i < series.length; i++) { 1567 | data = toArr(series[i].data); 1568 | for (j = 0; j < data.length; j++) { 1569 | if (!isDate(data[j][0])) { 1570 | return true; 1571 | } 1572 | } 1573 | } 1574 | return false; 1575 | } 1576 | 1577 | // creates a shallow copy of each element of the array 1578 | // elements are expected to be objects 1579 | function copySeries(series) { 1580 | var newSeries = [], i, j; 1581 | for (i = 0; i < series.length; i++) { 1582 | var copy = {} 1583 | for (j in series[i]) { 1584 | if (series[i].hasOwnProperty(j)) { 1585 | copy[j] = series[i][j]; 1586 | } 1587 | } 1588 | newSeries.push(copy) 1589 | } 1590 | return newSeries; 1591 | } 1592 | 1593 | function processSeries(chart, keyType) { 1594 | var i; 1595 | 1596 | var opts = chart.options; 1597 | var series = chart.rawData; 1598 | 1599 | // see if one series or multiple 1600 | if (!isArray(series) || typeof series[0] !== "object" || isArray(series[0])) { 1601 | series = [{name: opts.label || "Value", data: series}]; 1602 | chart.hideLegend = true; 1603 | } else { 1604 | chart.hideLegend = false; 1605 | } 1606 | if ((opts.discrete === null || opts.discrete === undefined) && keyType !== "bubble" && keyType !== "number") { 1607 | chart.discrete = detectDiscrete(series); 1608 | } else { 1609 | chart.discrete = opts.discrete; 1610 | } 1611 | if (chart.discrete) { 1612 | keyType = "string"; 1613 | } 1614 | if (chart.options.xtype) { 1615 | keyType = chart.options.xtype; 1616 | } 1617 | 1618 | // right format 1619 | series = copySeries(series); 1620 | for (i = 0; i < series.length; i++) { 1621 | series[i].data = formatSeriesData(toArr(series[i].data), keyType); 1622 | } 1623 | 1624 | return series; 1625 | } 1626 | 1627 | function processSimple(chart) { 1628 | var perfectData = toArr(chart.rawData), i; 1629 | for (i = 0; i < perfectData.length; i++) { 1630 | perfectData[i] = [toStr(perfectData[i][0]), toFloat(perfectData[i][1])]; 1631 | } 1632 | return perfectData; 1633 | } 1634 | 1635 | function processTime(chart) 1636 | { 1637 | var i, data = chart.rawData; 1638 | for (i = 0; i < data.length; i++) { 1639 | data[i][1] = toDate(data[i][1]); 1640 | data[i][2] = toDate(data[i][2]); 1641 | } 1642 | return data; 1643 | } 1644 | 1645 | function processLineData(chart) { 1646 | return processSeries(chart, "datetime"); 1647 | } 1648 | 1649 | function processColumnData(chart) { 1650 | return processSeries(chart, "string"); 1651 | } 1652 | 1653 | function processBarData(chart) { 1654 | return processSeries(chart, "string"); 1655 | } 1656 | 1657 | function processAreaData(chart) { 1658 | return processSeries(chart, "datetime"); 1659 | } 1660 | 1661 | function processScatterData(chart) { 1662 | return processSeries(chart, "number"); 1663 | } 1664 | 1665 | function processBubbleData(chart) { 1666 | return processSeries(chart, "bubble"); 1667 | } 1668 | 1669 | function createChart(chartType, chart, element, dataSource, opts, processData) { 1670 | var elementId; 1671 | if (typeof element === "string") { 1672 | elementId = element; 1673 | element = document.getElementById(element); 1674 | if (!element) { 1675 | throw new Error("No element with id " + elementId); 1676 | } 1677 | } 1678 | 1679 | chart.element = element; 1680 | opts = merge(Chartkick.options, opts || {}); 1681 | chart.options = opts; 1682 | chart.dataSource = dataSource; 1683 | 1684 | if (!processData) { 1685 | processData = function (chart) { 1686 | return chart.rawData; 1687 | } 1688 | } 1689 | 1690 | // getters 1691 | chart.getElement = function () { 1692 | return element; 1693 | }; 1694 | chart.getDataSource = function () { 1695 | return chart.dataSource; 1696 | }; 1697 | chart.getData = function () { 1698 | return chart.data; 1699 | }; 1700 | chart.getOptions = function () { 1701 | return chart.options; 1702 | }; 1703 | chart.getChartObject = function () { 1704 | return chart.chart; 1705 | }; 1706 | chart.getAdapter = function () { 1707 | return chart.adapter; 1708 | }; 1709 | 1710 | var callback = function () { 1711 | chart.data = processData(chart); 1712 | renderChart(chartType, chart); 1713 | }; 1714 | 1715 | // functions 1716 | chart.updateData = function (dataSource, options) { 1717 | chart.dataSource = dataSource; 1718 | if (options) { 1719 | chart.options = merge(Chartkick.options, options); 1720 | } 1721 | fetchDataSource(chart, callback, dataSource); 1722 | }; 1723 | chart.setOptions = function (options) { 1724 | chart.options = merge(Chartkick.options, options); 1725 | chart.redraw(); 1726 | }; 1727 | chart.redraw = function() { 1728 | fetchDataSource(chart, callback, chart.rawData); 1729 | }; 1730 | chart.refreshData = function () { 1731 | if (typeof chart.dataSource === "string") { 1732 | // prevent browser from caching 1733 | var sep = chart.dataSource.indexOf("?") === -1 ? "?" : "&"; 1734 | var url = chart.dataSource + sep + "_=" + (new Date()).getTime(); 1735 | fetchDataSource(chart, callback, url); 1736 | } 1737 | }; 1738 | chart.stopRefresh = function () { 1739 | if (chart.intervalId) { 1740 | clearInterval(chart.intervalId); 1741 | } 1742 | }; 1743 | chart.toImage = function () { 1744 | if (chart.adapter === "chartjs") { 1745 | return chart.chart.toBase64Image(); 1746 | } else { 1747 | return null; 1748 | } 1749 | } 1750 | 1751 | Chartkick.charts[element.id] = chart; 1752 | 1753 | fetchDataSource(chart, callback, dataSource); 1754 | 1755 | if (opts.refresh) { 1756 | chart.intervalId = setInterval( function () { 1757 | chart.refreshData(); 1758 | }, opts.refresh * 1000); 1759 | } 1760 | } 1761 | 1762 | // define classes 1763 | 1764 | Chartkick = { 1765 | LineChart: function (element, dataSource, options) { 1766 | createChart("LineChart", this, element, dataSource, options, processLineData); 1767 | }, 1768 | PieChart: function (element, dataSource, options) { 1769 | createChart("PieChart", this, element, dataSource, options, processSimple); 1770 | }, 1771 | ColumnChart: function (element, dataSource, options) { 1772 | createChart("ColumnChart", this, element, dataSource, options, processColumnData); 1773 | }, 1774 | BarChart: function (element, dataSource, options) { 1775 | createChart("BarChart", this, element, dataSource, options, processBarData); 1776 | }, 1777 | AreaChart: function (element, dataSource, options) { 1778 | createChart("AreaChart", this, element, dataSource, options, processAreaData); 1779 | }, 1780 | GeoChart: function (element, dataSource, options) { 1781 | createChart("GeoChart", this, element, dataSource, options, processSimple); 1782 | }, 1783 | ScatterChart: function (element, dataSource, options) { 1784 | createChart("ScatterChart", this, element, dataSource, options, processScatterData); 1785 | }, 1786 | BubbleChart: function (element, dataSource, options) { 1787 | createChart("BubbleChart", this, element, dataSource, options, processBubbleData); 1788 | }, 1789 | Timeline: function (element, dataSource, options) { 1790 | createChart("Timeline", this, element, dataSource, options, processTime); 1791 | }, 1792 | charts: {}, 1793 | configure: function (options) { 1794 | for (var key in options) { 1795 | if (options.hasOwnProperty(key)) { 1796 | config[key] = options[key]; 1797 | } 1798 | } 1799 | }, 1800 | eachChart: function (callback) { 1801 | for (var chartId in Chartkick.charts) { 1802 | if (Chartkick.charts.hasOwnProperty(chartId)) { 1803 | callback(Chartkick.charts[chartId]); 1804 | } 1805 | } 1806 | }, 1807 | options: {}, 1808 | adapters: adapters, 1809 | createChart: createChart 1810 | }; 1811 | 1812 | if (typeof module === "object" && typeof module.exports === "object") { 1813 | module.exports = Chartkick; 1814 | } else { 1815 | window.Chartkick = Chartkick; 1816 | } 1817 | }(window)); -------------------------------------------------------------------------------- /data/cache_hist.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ETH: 3 | Response: Success 4 | Type: 100 5 | Aggregated: true 6 | Data: 7 | - time: 1533006000 8 | close: 450.49 9 | high: 452.38 10 | low: 449.23 11 | open: 452.35 12 | volumefrom: 32899.53 13 | volumeto: 14852497.01 14 | - time: 1533020400 15 | close: 433.21 16 | high: 450.79 17 | low: 433.21 18 | open: 450.49 19 | volumefrom: 72848.91 20 | volumeto: 32362497.97 21 | - time: 1533034800 22 | close: 430.57 23 | high: 440.23 24 | low: 427.45 25 | open: 433.17 26 | volumefrom: 101217.4 27 | volumeto: 43878516.08 28 | - time: 1533049200 29 | close: 432.92 30 | high: 436.2 31 | low: 428.82 32 | open: 430.57 33 | volumefrom: 69506.7 34 | volumeto: 30034404 35 | - time: 1533063600 36 | close: 430.68 37 | high: 435.29 38 | low: 430.47 39 | open: 432.92 40 | volumefrom: 40342.93 41 | volumeto: 17479617.81 42 | - time: 1533078000 43 | close: 416.69 44 | high: 435.63 45 | low: 411.65 46 | open: 430.68 47 | volumefrom: 129431.43 48 | volumeto: 54812985.11 49 | - time: 1533092400 50 | close: 420.64 51 | high: 421.69 52 | low: 414.9 53 | open: 416.71 54 | volumefrom: 70410.21 55 | volumeto: 29451897.74 56 | - time: 1533106800 57 | close: 423.12 58 | high: 426.49 59 | low: 419.96 60 | open: 420.61 61 | volumefrom: 65255.22 62 | volumeto: 27674939.02 63 | - time: 1533121200 64 | close: 421.87 65 | high: 425.29 66 | low: 421.31 67 | open: 423.12 68 | volumefrom: 43823.82 69 | volumeto: 18591401.82 70 | - time: 1533135600 71 | close: 416.96 72 | high: 422.98 73 | low: 416.61 74 | open: 421.86 75 | volumefrom: 47722.85 76 | volumeto: 20057346.46 77 | - time: 1533150000 78 | close: 420.4 79 | high: 421.33 80 | low: 407.92 81 | open: 416.96 82 | volumefrom: 86106.26 83 | volumeto: 35668639.8 84 | - time: 1533164400 85 | close: 421.52 86 | high: 424.48 87 | low: 417.84 88 | open: 420.4 89 | volumefrom: 62378.9 90 | volumeto: 26308002.65 91 | - time: 1533178800 92 | close: 419.64 93 | high: 421.81 94 | low: 418.24 95 | open: 421.52 96 | volumefrom: 31288.44 97 | volumeto: 13147428.78 98 | - time: 1533193200 99 | close: 414.93 100 | high: 421.68 101 | low: 412.97 102 | open: 419.64 103 | volumefrom: 36647.39 104 | volumeto: 15311740.65 105 | - time: 1533207600 106 | close: 412.47 107 | high: 415.53 108 | low: 409.38 109 | open: 415.02 110 | volumefrom: 54188.69 111 | volumeto: 22367902.74 112 | - time: 1533222000 113 | close: 409.26 114 | high: 415.28 115 | low: 407.8 116 | open: 412.49 117 | volumefrom: 61300.95 118 | volumeto: 25232046.92 119 | - time: 1533236400 120 | close: 411.59 121 | high: 412.06 122 | low: 408.15 123 | open: 409.26 124 | volumefrom: 40835.49 125 | volumeto: 16764292.67 126 | - time: 1533250800 127 | close: 401.74 128 | high: 412.25 129 | low: 398.88 130 | open: 411.59 131 | volumefrom: 89901.16 132 | volumeto: 36246386.89 133 | - time: 1533265200 134 | close: 405.12 135 | high: 405.18 136 | low: 397.77 137 | open: 401.74 138 | volumefrom: 77879.9 139 | volumeto: 31263365.88 140 | - time: 1533279600 141 | close: 409.17 142 | high: 410.56 143 | low: 403.36 144 | open: 405.12 145 | volumefrom: 56841.69 146 | volumeto: 23147318.35 147 | - time: 1533294000 148 | close: 413.09 149 | high: 415.25 150 | low: 409.1 151 | open: 409.17 152 | volumefrom: 61509.98 153 | volumeto: 25370835.85 154 | - time: 1533308400 155 | close: 417.8 156 | high: 419.66 157 | low: 410.45 158 | open: 413.09 159 | volumefrom: 59955.35 160 | volumeto: 24908465.27 161 | - time: 1533322800 162 | close: 417.33 163 | high: 418.58 164 | low: 412.69 165 | open: 417.79 166 | volumefrom: 41767.39 167 | volumeto: 17356658.66 168 | - time: 1533337200 169 | close: 418.73 170 | high: 418.99 171 | low: 414.21 172 | open: 417.33 173 | volumefrom: 34164.04 174 | volumeto: 14240271.94 175 | - time: 1533351600 176 | close: 415.45 177 | high: 418.84 178 | low: 415.43 179 | open: 418.73 180 | volumefrom: 24864.28 181 | volumeto: 10364700.56 182 | - time: 1533366000 183 | close: 414.42 184 | high: 418.35 185 | low: 412.82 186 | open: 415.45 187 | volumefrom: 37894.58 188 | volumeto: 15758541.07 189 | - time: 1533380400 190 | close: 406.12 191 | high: 414.68 192 | low: 401.53 193 | open: 414.42 194 | volumefrom: 70731.7 195 | volumeto: 28866406.62 196 | - time: 1533394800 197 | close: 407.41 198 | high: 409.45 199 | low: 402.08 200 | open: 406.12 201 | volumefrom: 74586.73 202 | volumeto: 30298488.36 203 | - time: 1533409200 204 | close: 406.03 205 | high: 407.44 206 | low: 404.29 207 | open: 407.4 208 | volumefrom: 27511.66 209 | volumeto: 11148457.21 210 | - time: 1533423600 211 | close: 404.99 212 | high: 409.12 213 | low: 400.14 214 | open: 406.04 215 | volumefrom: 58279.3 216 | volumeto: 23524074.64 217 | - time: 1533438000 218 | close: 405.59 219 | high: 409.2 220 | low: 404.75 221 | open: 404.99 222 | volumefrom: 38137.9 223 | volumeto: 15497484.45 224 | - time: 1533452400 225 | close: 406.97 226 | high: 409.07 227 | low: 401.15 228 | open: 405.59 229 | volumefrom: 39145.29 230 | volumeto: 15856153.53 231 | - time: 1533466800 232 | close: 403.89 233 | high: 408.5 234 | low: 403.33 235 | open: 406.97 236 | volumefrom: 30393.88 237 | volumeto: 12332112.87 238 | - time: 1533481200 239 | close: 405.63 240 | high: 407 241 | low: 403.58 242 | open: 403.89 243 | volumefrom: 30748.16 244 | volumeto: 12452926.34 245 | - time: 1533495600 246 | close: 409.97 247 | high: 413.25 248 | low: 405.58 249 | open: 405.63 250 | volumefrom: 44888.15 251 | volumeto: 18405952 252 | - time: 1533510000 253 | close: 411.66 254 | high: 413.03 255 | low: 408.35 256 | open: 409.97 257 | volumefrom: 30260.64 258 | volumeto: 12417007.07 259 | - time: 1533524400 260 | close: 409.6 261 | high: 412.97 262 | low: 409.06 263 | open: 411.66 264 | volumefrom: 33186.98 265 | volumeto: 13646910.06 266 | - time: 1533538800 267 | close: 406.78 268 | high: 410.8 269 | low: 406.15 270 | open: 409.6 271 | volumefrom: 31427.3 272 | volumeto: 12817714.99 273 | - time: 1533553200 274 | close: 405.68 275 | high: 408.24 276 | low: 403.28 277 | open: 406.8 278 | volumefrom: 39756.67 279 | volumeto: 16112303.02 280 | - time: 1533567600 281 | close: 403.82 282 | high: 407.04 283 | low: 403.27 284 | open: 405.74 285 | volumefrom: 32365.1 286 | volumeto: 13104389.49 287 | - time: 1533582000 288 | close: 402.55 289 | high: 404.26 290 | low: 401.26 291 | open: 403.83 292 | volumefrom: 39040.57 293 | volumeto: 15724195.77 294 | - time: 1533596400 295 | close: 405.02 296 | high: 407.04 297 | low: 402.34 298 | open: 402.55 299 | volumefrom: 42456.88 300 | volumeto: 17187410.17 301 | - time: 1533610800 302 | close: 405.4 303 | high: 406.1 304 | low: 404.7 305 | open: 405.02 306 | volumefrom: 24789.9 307 | volumeto: 10042851.57 308 | - time: 1533625200 309 | close: 409.04 310 | high: 410.29 311 | low: 405.27 312 | open: 405.4 313 | volumefrom: 40607.82 314 | volumeto: 16600306.5 315 | - time: 1533639600 316 | close: 408.11 317 | high: 410.2 318 | low: 407.57 319 | open: 409.04 320 | volumefrom: 38862.58 321 | volumeto: 15893141.01 322 | - time: 1533654000 323 | close: 404.55 324 | high: 409.41 325 | low: 404.18 326 | open: 408.11 327 | volumefrom: 60592.63 328 | volumeto: 24653601.13 329 | - time: 1533668400 330 | close: 380.5 331 | high: 404.62 332 | low: 368.69 333 | open: 404.55 334 | volumefrom: 224040.49 335 | volumeto: 86189150.83 336 | - time: 1533682800 337 | close: 378.13 338 | high: 380.54 339 | low: 370.82 340 | open: 380.5 341 | volumefrom: 101042.97 342 | volumeto: 38091952.55 343 | - time: 1533697200 344 | close: 373.68 345 | high: 378.51 346 | low: 367.14 347 | open: 378.13 348 | volumefrom: 109781.29 349 | volumeto: 40934904.57 350 | - time: 1533711600 351 | close: 366.81 352 | high: 373.74 353 | low: 365.53 354 | open: 373.68 355 | volumefrom: 53226.65 356 | volumeto: 19655674.69 357 | - time: 1533726000 358 | close: 364.65 359 | high: 369.02 360 | low: 357.18 361 | open: 366.81 362 | volumefrom: 114803.29 363 | volumeto: 41790542.88 364 | - time: 1533740400 365 | close: 357.74 366 | high: 369.38 367 | low: 347.7 368 | open: 364.65 369 | volumefrom: 261622.96 370 | volumeto: 93297026.73 371 | - time: 1533754800 372 | close: 351.6 373 | high: 363.49 374 | low: 351.1 375 | open: 357.74 376 | volumefrom: 103512.28 377 | volumeto: 37133008.21 378 | - time: 1533769200 379 | close: 359.65 380 | high: 361.21 381 | low: 350.29 382 | open: 351.6 383 | volumefrom: 66999.9 384 | volumeto: 23915479.81 385 | - time: 1533783600 386 | close: 361.4 387 | high: 363.2 388 | low: 358.17 389 | open: 359.65 390 | volumefrom: 47452.01 391 | volumeto: 17118675.16 392 | - time: 1533798000 393 | close: 359.81 394 | high: 361.96 395 | low: 357.85 396 | open: 361.41 397 | volumefrom: 64971.69 398 | volumeto: 23402791.86 399 | - time: 1533812400 400 | close: 365.17 401 | high: 366.05 402 | low: 350.97 403 | open: 359.81 404 | volumefrom: 98450.96 405 | volumeto: 35379125.48 406 | - time: 1533826800 407 | close: 362.89 408 | high: 368.47 409 | low: 361.54 410 | open: 365.17 411 | volumefrom: 70636.08 412 | volumeto: 25739019.09 413 | - time: 1533841200 414 | close: 369.41 415 | high: 370.33 416 | low: 362.01 417 | open: 362.89 418 | volumefrom: 58423.27 419 | volumeto: 21391283.27 420 | - time: 1533855600 421 | close: 359.72 422 | high: 369.41 423 | low: 358.44 424 | open: 369.4 425 | volumefrom: 51913.82 426 | volumeto: 18851926.19 427 | - time: 1533870000 428 | close: 360.34 429 | high: 362.03 430 | low: 357.99 431 | open: 359.72 432 | volumefrom: 48107.04 433 | volumeto: 17322565.67 434 | - time: 1533884400 435 | close: 357.88 436 | high: 361.84 437 | low: 356.21 438 | open: 360.34 439 | volumefrom: 57366.56 440 | volumeto: 20621605.98 441 | - time: 1533898800 442 | close: 359.09 443 | high: 363.83 444 | low: 355.83 445 | open: 357.88 446 | volumefrom: 60236.59 447 | volumeto: 21738313.92 448 | - time: 1533913200 449 | close: 356.24 450 | high: 360.9 451 | low: 355.4 452 | open: 359.09 453 | volumefrom: 44831.73 454 | volumeto: 16064187.85 455 | - time: 1533927600 456 | close: 328.53 457 | high: 356.35 458 | low: 326.48 459 | open: 356.23 460 | volumefrom: 189580.46 461 | volumeto: 64021958.89 462 | - time: 1533942000 463 | close: 324.83 464 | high: 332.86 465 | low: 320.81 466 | open: 328.53 467 | volumefrom: 102869.31 468 | volumeto: 33674006.09 469 | - time: 1533956400 470 | close: 313.91 471 | high: 324.92 472 | low: 312 473 | open: 324.83 474 | volumefrom: 130758.58 475 | volumeto: 41529455.2 476 | - time: 1533970800 477 | close: 316.85 478 | high: 319.16 479 | low: 312.51 480 | open: 313.91 481 | volumefrom: 67462.39 482 | volumeto: 21361087.1 483 | - time: 1533985200 484 | close: 312.54 485 | high: 317.1 486 | low: 305.42 487 | open: 316.85 488 | volumefrom: 122519.48 489 | volumeto: 37957075.81 490 | - time: 1533999600 491 | close: 323.25 492 | high: 327.32 493 | low: 305.34 494 | open: 312.54 495 | volumefrom: 159479.67 496 | volumeto: 50552617.49 497 | - time: 1534014000 498 | close: 327.76 499 | high: 327.8 500 | low: 320.03 501 | open: 323.21 502 | volumefrom: 43437.98 503 | volumeto: 14092742.49 504 | - time: 1534028400 505 | close: 321.77 506 | high: 331.03 507 | low: 314.31 508 | open: 327.76 509 | volumefrom: 94458.94 510 | volumeto: 30332983.27 511 | - time: 1534042800 512 | close: 326.75 513 | high: 327.33 514 | low: 321.33 515 | open: 321.74 516 | volumefrom: 40127.54 517 | volumeto: 13007114.61 518 | - time: 1534057200 519 | close: 322.42 520 | high: 327 521 | low: 320.8 522 | open: 326.75 523 | volumefrom: 27740.6 524 | volumeto: 8972586.36 525 | - time: 1534071600 526 | close: 319.6 527 | high: 322.98 528 | low: 316.46 529 | open: 322.42 530 | volumefrom: 49303.5 531 | volumeto: 15730287.57 532 | - time: 1534086000 533 | close: 320.46 534 | high: 324.48 535 | low: 319.04 536 | open: 319.6 537 | volumefrom: 40728.33 538 | volumeto: 13118007.54 539 | - time: 1534100400 540 | close: 319.38 541 | high: 326.81 542 | low: 316.81 543 | open: 320.46 544 | volumefrom: 64023.37 545 | volumeto: 20558033.62 546 | - time: 1534114800 547 | close: 319.1 548 | high: 320.02 549 | low: 317.25 550 | open: 319.38 551 | volumefrom: 24486.39 552 | volumeto: 7804606.86 553 | - time: 1534129200 554 | close: 316.28 555 | high: 320.59 556 | low: 313.23 557 | open: 319.1 558 | volumefrom: 73444.52 559 | volumeto: 23303218.89 560 | - time: 1534143600 561 | close: 319.63 562 | high: 322.3 563 | low: 316.15 564 | open: 316.28 565 | volumefrom: 72156.29 566 | volumeto: 23038169.14 567 | - time: 1534158000 568 | close: 315.49 569 | high: 320.12 570 | low: 314.7 571 | open: 319.63 572 | volumefrom: 49060.98 573 | volumeto: 15603357.78 574 | - time: 1534172400 575 | close: 289.05 576 | high: 315.49 577 | low: 284.75 578 | open: 315.49 579 | volumefrom: 392116.41 580 | volumeto: 115805940.18 581 | - time: 1534186800 582 | close: 283.71 583 | high: 290.12 584 | low: 281.06 585 | open: 289.05 586 | volumefrom: 114111.61 587 | volumeto: 32715856.59 588 | - time: 1534201200 589 | close: 262.28 590 | high: 285.49 591 | low: 250.45 592 | open: 283.71 593 | volumefrom: 381418.55 594 | volumeto: 101121323.54 595 | - time: 1534215600 596 | close: 262.49 597 | high: 271.87 598 | low: 259.55 599 | open: 262.28 600 | volumefrom: 160720.41 601 | volumeto: 42849453.68 602 | - time: 1534230000 603 | close: 263.07 604 | high: 271.78 605 | low: 260.42 606 | open: 262.33 607 | volumefrom: 171794.88 608 | volumeto: 45667172.74 609 | - time: 1534244400 610 | close: 253.88 611 | high: 267.8 612 | low: 252.61 613 | open: 263.07 614 | volumefrom: 160633.57 615 | volumeto: 41891825.36 616 | - time: 1534258800 617 | close: 257.85 618 | high: 264.7 619 | low: 252.55 620 | open: 253.85 621 | volumefrom: 181986.9 622 | volumeto: 46877831.35 623 | - time: 1534273200 624 | close: 266.5 625 | high: 268.27 626 | low: 257.84 627 | open: 257.84 628 | volumefrom: 121848.87 629 | volumeto: 31933574.79 630 | - time: 1534287600 631 | close: 286.53 632 | high: 291.47 633 | low: 265.64 634 | open: 266.5 635 | volumefrom: 281893.73 636 | volumeto: 79250376.07 637 | - time: 1534302000 638 | close: 281.15 639 | high: 288.04 640 | low: 278.87 641 | open: 286.53 642 | volumefrom: 104431.68 643 | volumeto: 29716428.06 644 | - time: 1534316400 645 | close: 280.51 646 | high: 288.9 647 | low: 279.79 648 | open: 281.15 649 | volumefrom: 98471.16 650 | volumeto: 27976787.25 651 | - time: 1534330800 652 | close: 288.57 653 | high: 288.57 654 | low: 280.31 655 | open: 280.51 656 | volumefrom: 101944.92 657 | volumeto: 29050599.94 658 | - time: 1534345200 659 | close: 297.97 660 | high: 304.72 661 | low: 288.57 662 | open: 288.57 663 | volumefrom: 245340.71 664 | volumeto: 73136517.42 665 | - time: 1534359600 666 | close: 286.7 667 | high: 298.34 668 | low: 284.43 669 | open: 297.99 670 | volumefrom: 168197.66 671 | volumeto: 48785474.36 672 | - time: 1534374000 673 | close: 283.81 674 | high: 287.27 675 | low: 276.86 676 | open: 286.7 677 | volumefrom: 126124.19 678 | volumeto: 35489789.23 679 | - time: 1534388400 680 | close: 283.45 681 | high: 285.64 682 | low: 281.07 683 | open: 283.81 684 | volumefrom: 68689.97 685 | volumeto: 19491752.14 686 | - time: 1534402800 687 | close: 295.93 688 | high: 298.84 689 | low: 283.12 690 | open: 283.45 691 | volumefrom: 100105.56 692 | volumeto: 29201752.41 693 | - time: 1534417200 694 | close: 288.49 695 | high: 298.65 696 | low: 286.09 697 | open: 295.93 698 | volumefrom: 99736.63 699 | volumeto: 29092205.85 700 | - time: 1534431600 701 | close: 292.17 702 | high: 294.09 703 | low: 287.61 704 | open: 288.47 705 | volumefrom: 98468.85 706 | volumeto: 28704982.01 707 | - time: 1534446000 708 | close: 285.44 709 | high: 293.44 710 | low: 281.68 711 | open: 292.24 712 | volumefrom: 90984.3 713 | volumeto: 26114949.37 714 | - time: 1534460400 715 | close: 297.39 716 | high: 299.58 717 | low: 283.81 718 | open: 285.46 719 | volumefrom: 74329.25 720 | volumeto: 21697609.39 721 | - time: 1534474800 722 | close: 300.92 723 | high: 301.83 724 | low: 295.85 725 | open: 297.33 726 | volumefrom: 88418.42 727 | volumeto: 26421026.22 728 | - time: 1534489200 729 | close: 297.12 730 | high: 305.25 731 | low: 296.78 732 | open: 300.92 733 | volumefrom: 159876.69 734 | volumeto: 48140242.35 735 | - time: 1534503600 736 | close: 294.64 737 | high: 301.11 738 | low: 292.28 739 | open: 297.18 740 | volumefrom: 80003.15 741 | volumeto: 23803677.36 742 | - time: 1534518000 743 | close: 303.98 744 | high: 307.9 745 | low: 294.59 746 | open: 294.64 747 | volumefrom: 162174.81 748 | volumeto: 48897477.32 749 | - time: 1534532400 750 | close: 315.88 751 | high: 318.31 752 | low: 299.88 753 | open: 303.98 754 | volumefrom: 158333.54 755 | volumeto: 49046046.04 756 | - time: 1534546800 757 | close: 310.85 758 | high: 321.17 759 | low: 310.79 760 | open: 315.88 761 | volumefrom: 84255.35 762 | volumeto: 26642434.97 763 | - time: 1534561200 764 | close: 305.83 765 | high: 313.36 766 | low: 304.91 767 | open: 310.82 768 | volumefrom: 63738.34 769 | volumeto: 19759667.96 770 | - time: 1534575600 771 | close: 310.13 772 | high: 310.81 773 | low: 303.69 774 | open: 305.83 775 | volumefrom: 50150.64 776 | volumeto: 15430668.89 777 | - time: 1534590000 778 | close: 294.23 779 | high: 312.93 780 | low: 294.16 781 | open: 310.13 782 | volumefrom: 103712.83 783 | volumeto: 31539727.34 784 | - time: 1534604400 785 | close: 289.58 786 | high: 294.23 787 | low: 283.02 788 | open: 294.22 789 | volumefrom: 137330.26 790 | volumeto: 39539951.98 791 | - time: 1534618800 792 | close: 295.62 793 | high: 300.88 794 | low: 285.15 795 | open: 289.58 796 | volumefrom: 105379.84 797 | volumeto: 30904554.74 798 | - time: 1534633200 799 | close: 292.68 800 | high: 298.21 801 | low: 289.09 802 | open: 295.62 803 | volumefrom: 58043.12 804 | volumeto: 17031200.58 805 | - time: 1534647600 806 | close: 295.6 807 | high: 297.52 808 | low: 289.21 809 | open: 292.68 810 | volumefrom: 49533.69 811 | volumeto: 14523387.34 812 | - time: 1534662000 813 | close: 296.19 814 | high: 299.28 815 | low: 293 816 | open: 295.6 817 | volumefrom: 53705.29 818 | volumeto: 15909769.87 819 | - time: 1534676400 820 | close: 302.02 821 | high: 306.71 822 | low: 295.99 823 | open: 296.19 824 | volumefrom: 106910.97 825 | volumeto: 32266743.9 826 | - time: 1534690800 827 | close: 295.09 828 | high: 303.14 829 | low: 292.4 830 | open: 302.02 831 | volumefrom: 70770.18 832 | volumeto: 21002863.33 833 | - time: 1534705200 834 | close: 301.48 835 | high: 303.48 836 | low: 294.34 837 | open: 295.09 838 | volumefrom: 72036.71 839 | volumeto: 21586621.63 840 | - time: 1534719600 841 | close: 301.46 842 | high: 303.94 843 | low: 298.97 844 | open: 301.48 845 | volumefrom: 47616.27 846 | volumeto: 14353829.71 847 | - time: 1534734000 848 | close: 295.86 849 | high: 301.58 850 | low: 295.15 851 | open: 301.46 852 | volumefrom: 52069.92 853 | volumeto: 15556513.92 854 | - time: 1534748400 855 | close: 289.74 856 | high: 297.34 857 | low: 286.27 858 | open: 295.81 859 | volumefrom: 62716.78 860 | volumeto: 18269043.24 861 | - time: 1534762800 862 | close: 286.84 863 | high: 292.65 864 | low: 281.93 865 | open: 289.74 866 | volumefrom: 85511.18 867 | volumeto: 24556297.68 868 | - time: 1534777200 869 | close: 288.87 870 | high: 292.11 871 | low: 284.71 872 | open: 286.84 873 | volumefrom: 47937.51 874 | volumeto: 13830006.66 875 | - time: 1534791600 876 | close: 273.79 877 | high: 289.59 878 | low: 270.13 879 | open: 288.87 880 | volumefrom: 152345 881 | volumeto: 42164175.58 882 | - time: 1534806000 883 | close: 274.7 884 | high: 276.15 885 | low: 269.16 886 | open: 273.79 887 | volumefrom: 84661.8 888 | volumeto: 23064853.93 889 | - time: 1534820400 890 | close: 278.87 891 | high: 280.58 892 | low: 274.14 893 | open: 274.7 894 | volumefrom: 57877.51 895 | volumeto: 16061970.3 896 | - time: 1534834800 897 | close: 282.64 898 | high: 285.78 899 | low: 278.56 900 | open: 278.86 901 | volumefrom: 76819.08 902 | volumeto: 21703178.51 903 | - time: 1534849200 904 | close: 277.39 905 | high: 282.72 906 | low: 272.67 907 | open: 282.63 908 | volumefrom: 83887.1 909 | volumeto: 23325678.25 910 | - time: 1534863600 911 | close: 276.83 912 | high: 277.91 913 | low: 271.19 914 | open: 277.4 915 | volumefrom: 77793.12 916 | volumeto: 21405653.7 917 | - time: 1534878000 918 | close: 280.43 919 | high: 281.62 920 | low: 275.61 921 | open: 276.84 922 | volumefrom: 53929.04 923 | volumeto: 15008137.88 924 | - time: 1534892400 925 | close: 292.08 926 | high: 299 927 | low: 277.56 928 | open: 280.43 929 | volumefrom: 176869.99 930 | volumeto: 51501908.19 931 | - time: 1534906800 932 | close: 285.19 933 | high: 292.6 934 | low: 283.27 935 | open: 292.08 936 | volumefrom: 99454.77 937 | volumeto: 28661690.35 938 | - time: 1534921200 939 | close: 283.58 940 | high: 285.9 941 | low: 281.21 942 | open: 285.19 943 | volumefrom: 72278.04 944 | volumeto: 20537114.96 945 | - time: 1534935600 946 | close: 283.79 947 | high: 285.78 948 | low: 281.72 949 | open: 283.58 950 | volumefrom: 57536.13 951 | volumeto: 16407702.69 952 | - time: 1534950000 953 | close: 271.13 954 | high: 284.51 955 | low: 271.01 956 | open: 283.79 957 | volumefrom: 146185.31 958 | volumeto: 40467282.24 959 | - time: 1534964400 960 | close: 269.03 961 | high: 272.44 962 | low: 259.61 963 | open: 271.13 964 | volumefrom: 191557.15 965 | volumeto: 51344406.32 966 | - time: 1534978800 967 | close: 274.08 968 | high: 275.5 969 | low: 268.43 970 | open: 269.03 971 | volumefrom: 74834.73 972 | volumeto: 20507408.65 973 | - time: 1534993200 974 | close: 273.78 975 | high: 274.17 976 | low: 269.09 977 | open: 274.08 978 | volumefrom: 64289.31 979 | volumeto: 17575726.08 980 | - time: 1535007600 981 | close: 271.04 982 | high: 274.63 983 | low: 270.66 984 | open: 273.78 985 | volumefrom: 38214.3 986 | volumeto: 10528951.48 987 | - time: 1535022000 988 | close: 273.49 989 | high: 275 990 | low: 268.4 991 | open: 271.04 992 | volumefrom: 73640.94 993 | volumeto: 20169194.08 994 | - time: 1535036400 995 | close: 275.58 996 | high: 280.46 997 | low: 271.72 998 | open: 273.49 999 | volumefrom: 98381.06 1000 | volumeto: 27307901.42 1001 | - time: 1535050800 1002 | close: 275.14 1003 | high: 278.34 1004 | low: 270.52 1005 | open: 275.58 1006 | volumefrom: 75931.09 1007 | volumeto: 20912193.66 1008 | - time: 1535065200 1009 | close: 274.43 1010 | high: 276.32 1011 | low: 273.07 1012 | open: 275.14 1013 | volumefrom: 32967.76 1014 | volumeto: 9143781.59 1015 | - time: 1535079600 1016 | close: 274.89 1017 | high: 276.14 1018 | low: 272.99 1019 | open: 274.43 1020 | volumefrom: 38311.06 1021 | volumeto: 10614069.82 1022 | - time: 1535094000 1023 | close: 275.41 1024 | high: 276.57 1025 | low: 274.02 1026 | open: 274.89 1027 | volumefrom: 35806.37 1028 | volumeto: 9941622.4 1029 | - time: 1535108400 1030 | close: 271.77 1031 | high: 278.24 1032 | low: 270.11 1033 | open: 275.41 1034 | volumefrom: 67147.44 1035 | volumeto: 18529342.89 1036 | - time: 1535122800 1037 | close: 280.43 1038 | high: 281.84 1039 | low: 271.23 1040 | open: 271.77 1041 | volumefrom: 83034.42 1042 | volumeto: 22985350.5 1043 | - time: 1535137200 1044 | close: 280.98 1045 | high: 283.12 1046 | low: 278.61 1047 | open: 280.43 1048 | volumefrom: 60806.91 1049 | volumeto: 17141370.41 1050 | - time: 1535151600 1051 | close: 281.9 1052 | high: 283.2 1053 | low: 280.78 1054 | open: 280.98 1055 | volumefrom: 38824.49 1056 | volumeto: 10975994.75 1057 | - time: 1535166000 1058 | close: 277.35 1059 | high: 282 1060 | low: 277.26 1061 | open: 281.9 1062 | volumefrom: 32261.53 1063 | volumeto: 9040393.54 1064 | - time: 1535180400 1065 | close: 279.02 1066 | high: 279.78 1067 | low: 276.75 1068 | open: 277.35 1069 | volumefrom: 27949.7 1070 | volumeto: 7777691.09 1071 | - time: 1535194800 1072 | close: 279.62 1073 | high: 279.94 1074 | low: 277.03 1075 | open: 279.02 1076 | volumefrom: 23142.7 1077 | volumeto: 6447508.59 1078 | - time: 1535209200 1079 | close: 279.36 1080 | high: 280.68 1081 | low: 278.59 1082 | open: 279.62 1083 | volumefrom: 24618.22 1084 | volumeto: 6888958.22 1085 | - time: 1535223600 1086 | close: 278.92 1087 | high: 279.36 1088 | low: 277.41 1089 | open: 279.36 1090 | volumefrom: 17288.81 1091 | volumeto: 4819681.16 1092 | - time: 1535238000 1093 | close: 272.46 1094 | high: 280.34 1095 | low: 271.72 1096 | open: 278.92 1097 | volumefrom: 78175.46 1098 | volumeto: 21572746.11 1099 | - time: 1535252400 1100 | close: 271.9 1101 | high: 272.47 1102 | low: 269.57 1103 | open: 272.46 1104 | volumefrom: 39660.43 1105 | volumeto: 10761298.15 1106 | - time: 1535266800 1107 | close: 273.71 1108 | high: 274.16 1109 | low: 271.43 1110 | open: 271.9 1111 | volumefrom: 31198.27 1112 | volumeto: 8507042.32 1113 | - time: 1535281200 1114 | close: 274.72 1115 | high: 276.01 1116 | low: 273.27 1117 | open: 273.71 1118 | volumefrom: 56218.73 1119 | volumeto: 15454295.09 1120 | - time: 1535295600 1121 | close: 273.84 1122 | high: 274.86 1123 | low: 271.28 1124 | open: 274.72 1125 | volumefrom: 41363.57 1126 | volumeto: 11293589.54 1127 | - time: 1535310000 1128 | close: 273.12 1129 | high: 273.84 1130 | low: 271.78 1131 | open: 273.84 1132 | volumefrom: 36704.74 1133 | volumeto: 10014381.96 1134 | - time: 1535324400 1135 | close: 272.7 1136 | high: 274.92 1137 | low: 271.91 1138 | open: 273.12 1139 | volumefrom: 43115.65 1140 | volumeto: 11777648.96 1141 | - time: 1535338800 1142 | close: 278.18 1143 | high: 279.69 1144 | low: 271.88 1145 | open: 272.7 1146 | volumefrom: 91482.4 1147 | volumeto: 25350293.13 1148 | - time: 1535353200 1149 | close: 276.05 1150 | high: 278.6 1151 | low: 275.27 1152 | open: 278.18 1153 | volumefrom: 53991.1 1154 | volumeto: 14947488.74 1155 | - time: 1535367600 1156 | close: 276.14 1157 | high: 278.89 1158 | low: 274.16 1159 | open: 276.05 1160 | volumefrom: 59078.67 1161 | volumeto: 16332108.14 1162 | - time: 1535382000 1163 | close: 277.72 1164 | high: 278.47 1165 | low: 276.1 1166 | open: 276.14 1167 | volumefrom: 50164.64 1168 | volumeto: 13914674.52 1169 | - time: 1535396400 1170 | close: 280.36 1171 | high: 280.36 1172 | low: 276.26 1173 | open: 277.72 1174 | volumefrom: 49773.18 1175 | volumeto: 13820843.54 1176 | - time: 1535410800 1177 | close: 283.46 1178 | high: 288.16 1179 | low: 280.33 1180 | open: 280.36 1181 | volumefrom: 122252.01 1182 | volumeto: 34808608.81 1183 | - time: 1535425200 1184 | close: 284.76 1185 | high: 285.79 1186 | low: 283.44 1187 | open: 283.46 1188 | volumefrom: 42297.19 1189 | volumeto: 12053544.68 1190 | - time: 1535439600 1191 | close: 285.22 1192 | high: 286.08 1193 | low: 284.57 1194 | open: 284.76 1195 | volumefrom: 29593.98 1196 | volumeto: 8441176.56 1197 | - time: 1535454000 1198 | close: 288.35 1199 | high: 292.11 1200 | low: 285.21 1201 | open: 285.22 1202 | volumefrom: 101715.17 1203 | volumeto: 29367017.57 1204 | - time: 1535468400 1205 | close: 291.51 1206 | high: 292.26 1207 | low: 286.9 1208 | open: 288.35 1209 | volumefrom: 63080.54 1210 | volumeto: 18215544.84 1211 | - time: 1535482800 1212 | close: 296.13 1213 | high: 296.58 1214 | low: 291.51 1215 | open: 291.51 1216 | volumefrom: 77684.48 1217 | volumeto: 22862286.02 1218 | - time: 1535497200 1219 | close: 292.38 1220 | high: 297.82 1221 | low: 292.2 1222 | open: 296.13 1223 | volumefrom: 45278.87 1224 | volumeto: 13336144.32 1225 | - time: 1535511600 1226 | close: 292.21 1227 | high: 293.28 1228 | low: 290.34 1229 | open: 292.38 1230 | volumefrom: 46685.61 1231 | volumeto: 13612773.35 1232 | - time: 1535526000 1233 | close: 294.72 1234 | high: 296.16 1235 | low: 291.01 1236 | open: 292.21 1237 | volumefrom: 47853.62 1238 | volumeto: 14051151.74 1239 | - time: 1535540400 1240 | close: 292.86 1241 | high: 296.35 1242 | low: 291.95 1243 | open: 294.72 1244 | volumefrom: 49791.95 1245 | volumeto: 14669846.31 1246 | - time: 1535554800 1247 | close: 289.11 1248 | high: 292.97 1249 | low: 284.37 1250 | open: 292.86 1251 | volumefrom: 99235.64 1252 | volumeto: 28572826.55 1253 | - time: 1535569200 1254 | close: 290.64 1255 | high: 291.72 1256 | low: 287.54 1257 | open: 289.11 1258 | volumefrom: 44975.22 1259 | volumeto: 13039515.72 1260 | - time: 1535583600 1261 | close: 285.95 1262 | high: 290.88 1263 | low: 284.28 1264 | open: 290.64 1265 | volumefrom: 42663.51 1266 | volumeto: 12285023.53 1267 | - time: 1535598000 1268 | close: 286.29 1269 | high: 286.29 1270 | low: 285.26 1271 | open: 285.95 1272 | volumefrom: 799.23 1273 | volumeto: 228117.39 1274 | TimeTo: 1535598000 1275 | TimeFrom: 1533006000 1276 | FirstValueInArray: true 1277 | ConversionType: 1278 | type: direct 1279 | conversionSymbol: '' 1280 | BTC: 1281 | Response: Success 1282 | Type: 100 1283 | Aggregated: true 1284 | Data: 1285 | - time: 1533006000 1286 | close: 8138.45 1287 | high: 8152.65 1288 | low: 8088.44 1289 | open: 8126.83 1290 | volumefrom: 7087.31 1291 | volumeto: 57730213.96 1292 | - time: 1533020400 1293 | close: 7959.58 1294 | high: 8139.98 1295 | low: 7939.82 1296 | open: 8138.45 1297 | volumefrom: 15411.3 1298 | volumeto: 124170729.07 1299 | - time: 1533034800 1300 | close: 7740.68 1301 | high: 8025.99 1302 | low: 7674.65 1303 | open: 7959.58 1304 | volumefrom: 34163 1305 | volumeto: 267495532.52 1306 | - time: 1533049200 1307 | close: 7752.39 1308 | high: 7832.9 1309 | low: 7676.6 1310 | open: 7740.75 1311 | volumefrom: 17068.49 1312 | volumeto: 132608267.07 1313 | - time: 1533063600 1314 | close: 7707.19 1315 | high: 7769.92 1316 | low: 7677.28 1317 | open: 7752.71 1318 | volumefrom: 9930.61 1319 | volumeto: 76817602.15 1320 | - time: 1533078000 1321 | close: 7558.58 1322 | high: 7784.78 1323 | low: 7522.1 1324 | open: 7707.19 1325 | volumefrom: 24667.49 1326 | volumeto: 188703420.97 1327 | - time: 1533092400 1328 | close: 7577.68 1329 | high: 7583.41 1330 | low: 7471.67 1331 | open: 7558.84 1332 | volumefrom: 15804.54 1333 | volumeto: 119330505.84 1334 | - time: 1533106800 1335 | close: 7571.44 1336 | high: 7660.11 1337 | low: 7539.72 1338 | open: 7577.68 1339 | volumefrom: 11310.04 1340 | volumeto: 86217944.49 1341 | - time: 1533121200 1342 | close: 7587.81 1343 | high: 7644.33 1344 | low: 7529.68 1345 | open: 7571.44 1346 | volumefrom: 8625.31 1347 | volumeto: 65688778.85 1348 | - time: 1533135600 1349 | close: 7548.1 1350 | high: 7625.92 1351 | low: 7539.83 1352 | open: 7587.58 1353 | volumefrom: 8927.49 1354 | volumeto: 67776615.06 1355 | - time: 1533150000 1356 | close: 7595.02 1357 | high: 7634.95 1358 | low: 7449.31 1359 | open: 7548.1 1360 | volumefrom: 15352.64 1361 | volumeto: 115908946.41 1362 | - time: 1533164400 1363 | close: 7680.4 1364 | high: 7713.32 1365 | low: 7575.19 1366 | open: 7595.11 1367 | volumefrom: 11276.07 1368 | volumeto: 86509536.36 1369 | - time: 1533178800 1370 | close: 7650.3 1371 | high: 7686.64 1372 | low: 7632.74 1373 | open: 7681.01 1374 | volumefrom: 5145.1 1375 | volumeto: 39503936.71 1376 | - time: 1533193200 1377 | close: 7586.69 1378 | high: 7699.53 1379 | low: 7564.96 1380 | open: 7650.46 1381 | volumefrom: 9885 1382 | volumeto: 75705934.92 1383 | - time: 1533207600 1384 | close: 7544.23 1385 | high: 7589.01 1386 | low: 7486.61 1387 | open: 7588.86 1388 | volumefrom: 11263.6 1389 | volumeto: 85287228.29 1390 | - time: 1533222000 1391 | close: 7509.56 1392 | high: 7597.94 1393 | low: 7470.86 1394 | open: 7544.33 1395 | volumefrom: 11863.82 1396 | volumeto: 89615362.44 1397 | - time: 1533236400 1398 | close: 7559.41 1399 | high: 7581.17 1400 | low: 7509.59 1401 | open: 7509.59 1402 | volumefrom: 7201 1403 | volumeto: 54521430.54 1404 | - time: 1533250800 1405 | close: 7360.55 1406 | high: 7576.06 1407 | low: 7325 1408 | open: 7559.41 1409 | volumefrom: 17662.35 1410 | volumeto: 130970074.23 1411 | - time: 1533265200 1412 | close: 7366.26 1413 | high: 7373.54 1414 | low: 7296.76 1415 | open: 7360.42 1416 | volumefrom: 10706.02 1417 | volumeto: 78768450.67 1418 | - time: 1533279600 1419 | close: 7359.29 1420 | high: 7421.61 1421 | low: 7314.47 1422 | open: 7366.28 1423 | volumefrom: 12762.03 1424 | volumeto: 94255809.97 1425 | - time: 1533294000 1426 | close: 7477.01 1427 | high: 7497.82 1428 | low: 7336.06 1429 | open: 7359.29 1430 | volumefrom: 16746.75 1431 | volumeto: 124386652.44 1432 | - time: 1533308400 1433 | close: 7477.35 1434 | high: 7537.45 1435 | low: 7460.52 1436 | open: 7476.76 1437 | volumefrom: 10697.64 1438 | volumeto: 80399166.41 1439 | - time: 1533322800 1440 | close: 7426.79 1441 | high: 7478.72 1442 | low: 7368.13 1443 | open: 7477.39 1444 | volumefrom: 9774.19 1445 | volumeto: 72619419.29 1446 | - time: 1533337200 1447 | close: 7475.97 1448 | high: 7490.89 1449 | low: 7360.41 1450 | open: 7426.78 1451 | volumefrom: 7740.75 1452 | volumeto: 57760345.85 1453 | - time: 1533351600 1454 | close: 7423.41 1455 | high: 7481.47 1456 | low: 7414.27 1457 | open: 7475.98 1458 | volumefrom: 4740.5 1459 | volumeto: 35455646.25 1460 | - time: 1533366000 1461 | close: 7407.09 1462 | high: 7484.26 1463 | low: 7383.03 1464 | open: 7423.33 1465 | volumefrom: 5725.08 1466 | volumeto: 42733393.11 1467 | - time: 1533380400 1468 | close: 7060.33 1469 | high: 7421.7 1470 | low: 7010.56 1471 | open: 7407.09 1472 | volumefrom: 27704.52 1473 | volumeto: 199407346.74 1474 | - time: 1533394800 1475 | close: 6987.21 1476 | high: 7083.2 1477 | low: 6940.15 1478 | open: 7060.33 1479 | volumefrom: 21709.97 1480 | volumeto: 152375755.84 1481 | - time: 1533409200 1482 | close: 7013.84 1483 | high: 7041.04 1484 | low: 6968.15 1485 | open: 6987.18 1486 | volumefrom: 7272.39 1487 | volumeto: 51177126.72 1488 | - time: 1533423600 1489 | close: 6953.37 1490 | high: 7031.18 1491 | low: 6898.31 1492 | open: 7014.11 1493 | volumefrom: 11184.45 1494 | volumeto: 78072181.97 1495 | - time: 1533438000 1496 | close: 6972.31 1497 | high: 7019.55 1498 | low: 6949.81 1499 | open: 6953.4 1500 | volumefrom: 6470.7 1501 | volumeto: 45370830.76 1502 | - time: 1533452400 1503 | close: 7052.85 1504 | high: 7092.53 1505 | low: 6939.75 1506 | open: 6972.31 1507 | volumefrom: 7744.96 1508 | volumeto: 54562401.66 1509 | - time: 1533466800 1510 | close: 6935.86 1511 | high: 7067.72 1512 | low: 6905.02 1513 | open: 7052.92 1514 | volumefrom: 10246.48 1515 | volumeto: 71766368.89 1516 | - time: 1533481200 1517 | close: 7027.73 1518 | high: 7056.09 1519 | low: 6935.57 1520 | open: 6935.86 1521 | volumefrom: 9153.32 1522 | volumeto: 64286645.33 1523 | - time: 1533495600 1524 | close: 7039.69 1525 | high: 7089.12 1526 | low: 7015.72 1527 | open: 7027.9 1528 | volumefrom: 7805 1529 | volumeto: 55186845.01 1530 | - time: 1533510000 1531 | close: 7146.75 1532 | high: 7160.4 1533 | low: 7028.59 1534 | open: 7039.75 1535 | volumefrom: 10594.53 1536 | volumeto: 75380446.24 1537 | - time: 1533524400 1538 | close: 7088.41 1539 | high: 7149.71 1540 | low: 7079.48 1541 | open: 7147.1 1542 | volumefrom: 6884.41 1543 | volumeto: 49094113.77 1544 | - time: 1533538800 1545 | close: 6991.34 1546 | high: 7101.47 1547 | low: 6959.55 1548 | open: 7090.32 1549 | volumefrom: 9495.79 1550 | volumeto: 66738113.27 1551 | - time: 1533553200 1552 | close: 6961.2 1553 | high: 6998.63 1554 | low: 6896.99 1555 | open: 6991.34 1556 | volumefrom: 10587.04 1557 | volumeto: 73747696.72 1558 | - time: 1533567600 1559 | close: 6932.75 1560 | high: 6993.64 1561 | low: 6913.42 1562 | open: 6961.2 1563 | volumefrom: 8755.54 1564 | volumeto: 61000872.38 1565 | - time: 1533582000 1566 | close: 6892.77 1567 | high: 6962.72 1568 | low: 6854.79 1569 | open: 6932.83 1570 | volumefrom: 10396.8 1571 | volumeto: 71908860.9 1572 | - time: 1533596400 1573 | close: 6955.31 1574 | high: 7000.57 1575 | low: 6887.5 1576 | open: 6892.77 1577 | volumefrom: 10458.45 1578 | volumeto: 72818018.31 1579 | - time: 1533610800 1580 | close: 6962.52 1581 | high: 6974.55 1582 | low: 6935.17 1583 | open: 6955.34 1584 | volumefrom: 5557.44 1585 | volumeto: 38818349.39 1586 | - time: 1533625200 1587 | close: 7052.39 1588 | high: 7082.22 1589 | low: 6960.68 1590 | open: 6962.44 1591 | volumefrom: 11644.43 1592 | volumeto: 82244398.48 1593 | - time: 1533639600 1594 | close: 7099.37 1595 | high: 7150.86 1596 | low: 7052.39 1597 | open: 7052.39 1598 | volumefrom: 12710.29 1599 | volumeto: 90406770.54 1600 | - time: 1533654000 1601 | close: 7105.99 1602 | high: 7142.44 1603 | low: 7084.43 1604 | open: 7099.37 1605 | volumefrom: 8692.8 1606 | volumeto: 61967374.25 1607 | - time: 1533668400 1608 | close: 6753.85 1609 | high: 7109.08 1610 | low: 6685.02 1611 | open: 7105.99 1612 | volumefrom: 33733.47 1613 | volumeto: 230819734.66 1614 | - time: 1533682800 1615 | close: 6628 1616 | high: 6755.68 1617 | low: 6564 1618 | open: 6753.85 1619 | volumefrom: 20243.23 1620 | volumeto: 134440373.41 1621 | - time: 1533697200 1622 | close: 6551.98 1623 | high: 6629.85 1624 | low: 6399.57 1625 | open: 6628.11 1626 | volumefrom: 26371.51 1627 | volumeto: 171976232.6 1628 | - time: 1533711600 1629 | close: 6488.42 1630 | high: 6553.33 1631 | low: 6443.99 1632 | open: 6551.98 1633 | volumefrom: 12142.42 1634 | volumeto: 79125531.26 1635 | - time: 1533726000 1636 | close: 6498.39 1637 | high: 6538.28 1638 | low: 6426.31 1639 | open: 6488.38 1640 | volumefrom: 14398.27 1641 | volumeto: 93703880.99 1642 | - time: 1533740400 1643 | close: 6266.9 1644 | high: 6504.44 1645 | low: 6133.03 1646 | open: 6498.39 1647 | volumefrom: 41256.47 1648 | volumeto: 259770683.68 1649 | - time: 1533754800 1650 | close: 6269.8 1651 | high: 6361.4 1652 | low: 6246.08 1653 | open: 6267.23 1654 | volumefrom: 15390.88 1655 | volumeto: 97357330.06 1656 | - time: 1533769200 1657 | close: 6320.67 1658 | high: 6336.05 1659 | low: 6226.11 1660 | open: 6269.75 1661 | volumefrom: 11100.07 1662 | volumeto: 70110710.79 1663 | - time: 1533783600 1664 | close: 6353.7 1665 | high: 6387.05 1666 | low: 6284.67 1667 | open: 6320.67 1668 | volumefrom: 10034.83 1669 | volumeto: 63748214.48 1670 | - time: 1533798000 1671 | close: 6344.14 1672 | high: 6364.77 1673 | low: 6288.96 1674 | open: 6353.71 1675 | volumefrom: 9221.85 1676 | volumeto: 58562325.3 1677 | - time: 1533812400 1678 | close: 6507.06 1679 | high: 6535.2 1680 | low: 6195.02 1681 | open: 6344.14 1682 | volumefrom: 30177.65 1683 | volumeto: 192383605.48 1684 | - time: 1533826800 1685 | close: 6480.91 1686 | high: 6553.93 1687 | low: 6433.67 1688 | open: 6507.07 1689 | volumefrom: 15406.34 1690 | volumeto: 100133540.9 1691 | - time: 1533841200 1692 | close: 6607.33 1693 | high: 6631.23 1694 | low: 6465.16 1695 | open: 6480.96 1696 | volumefrom: 14687.94 1697 | volumeto: 96564947.09 1698 | - time: 1533855600 1699 | close: 6485.64 1700 | high: 6607.9 1701 | low: 6459 1702 | open: 6607.33 1703 | volumefrom: 10368.09 1704 | volumeto: 67931605.4 1705 | - time: 1533870000 1706 | close: 6453.66 1707 | high: 6496.7 1708 | low: 6389.35 1709 | open: 6486.01 1710 | volumefrom: 11094.88 1711 | volumeto: 71720430.47 1712 | - time: 1533884400 1713 | close: 6350.5 1714 | high: 6488.3 1715 | low: 6335.31 1716 | open: 6453.67 1717 | volumefrom: 12747.21 1718 | volumeto: 81884701.76 1719 | - time: 1533898800 1720 | close: 6434.88 1721 | high: 6529.93 1722 | low: 6318.11 1723 | open: 6353.62 1724 | volumefrom: 16070.35 1725 | volumeto: 103765644.5 1726 | - time: 1533913200 1727 | close: 6407.26 1728 | high: 6482.12 1729 | low: 6350.05 1730 | open: 6434.88 1731 | volumefrom: 12390.24 1732 | volumeto: 79712828.92 1733 | - time: 1533927600 1734 | close: 6097.89 1735 | high: 6414.5 1736 | low: 6025.91 1737 | open: 6407.26 1738 | volumefrom: 35486.85 1739 | volumeto: 219001650.96 1740 | - time: 1533942000 1741 | close: 6146.12 1742 | high: 6187.54 1743 | low: 6074.59 1744 | open: 6098.08 1745 | volumefrom: 14073.04 1746 | volumeto: 86453333.59 1747 | - time: 1533956400 1748 | close: 6090.55 1749 | high: 6153.8 1750 | low: 6014.26 1751 | open: 6146.12 1752 | volumefrom: 17329.68 1753 | volumeto: 105773195.44 1754 | - time: 1533970800 1755 | close: 6127.15 1756 | high: 6184.46 1757 | low: 6068.55 1758 | open: 6089.68 1759 | volumefrom: 9618.99 1760 | volumeto: 59073946.12 1761 | - time: 1533985200 1762 | close: 6130.63 1763 | high: 6167.24 1764 | low: 6079.3 1765 | open: 6127.15 1766 | volumefrom: 10189.67 1767 | volumeto: 62497937.91 1768 | - time: 1533999600 1769 | close: 6398.85 1770 | high: 6488.55 1771 | low: 6080.79 1772 | open: 6130.63 1773 | volumefrom: 29752.83 1774 | volumeto: 188044986.63 1775 | - time: 1534014000 1776 | close: 6404.38 1777 | high: 6425.74 1778 | low: 6366.17 1779 | open: 6398.85 1780 | volumefrom: 6967.57 1781 | volumeto: 44713135.56 1782 | - time: 1534028400 1783 | close: 6294.42 1784 | high: 6417.99 1785 | low: 6176.89 1786 | open: 6404.38 1787 | volumefrom: 16494.16 1788 | volumeto: 103641774.83 1789 | - time: 1534042800 1790 | close: 6352.46 1791 | high: 6389.72 1792 | low: 6260.85 1793 | open: 6294.42 1794 | volumefrom: 7185 1795 | volumeto: 45617174.12 1796 | - time: 1534057200 1797 | close: 6334.19 1798 | high: 6359.87 1799 | low: 6293.95 1800 | open: 6352.5 1801 | volumefrom: 4198.83 1802 | volumeto: 26710570.72 1803 | - time: 1534071600 1804 | close: 6312.12 1805 | high: 6376.07 1806 | low: 6210.01 1807 | open: 6334.19 1808 | volumefrom: 8483.06 1809 | volumeto: 53529177.98 1810 | - time: 1534086000 1811 | close: 6311.55 1812 | high: 6373.17 1813 | low: 6268.7 1814 | open: 6312.12 1815 | volumefrom: 8566.36 1816 | volumeto: 54239843.67 1817 | - time: 1534100400 1818 | close: 6348.02 1819 | high: 6483.39 1820 | low: 6227.64 1821 | open: 6311.55 1822 | volumefrom: 16281.71 1823 | volumeto: 103527514.23 1824 | - time: 1534114800 1825 | close: 6336.9 1826 | high: 6353.55 1827 | low: 6300.74 1828 | open: 6348.02 1829 | volumefrom: 4495.99 1830 | volumeto: 28591041.5 1831 | - time: 1534129200 1832 | close: 6427.08 1833 | high: 6494.63 1834 | low: 6314.06 1835 | open: 6336.93 1836 | volumefrom: 13678.01 1837 | volumeto: 87627595.93 1838 | - time: 1534143600 1839 | close: 6473.75 1840 | high: 6545.02 1841 | low: 6410.06 1842 | open: 6427.25 1843 | volumefrom: 9738.22 1844 | volumeto: 63338967.09 1845 | - time: 1534158000 1846 | close: 6386.77 1847 | high: 6483.93 1848 | low: 6363.35 1849 | open: 6473.75 1850 | volumefrom: 12634.6 1851 | volumeto: 81260933.01 1852 | - time: 1534172400 1853 | close: 6248.63 1854 | high: 6387.22 1855 | low: 6157.03 1856 | open: 6386.95 1857 | volumefrom: 21247.51 1858 | volumeto: 133309723.57 1859 | - time: 1534186800 1860 | close: 6269.12 1861 | high: 6307.82 1862 | low: 6213.3 1863 | open: 6248.63 1864 | volumefrom: 9523.17 1865 | volumeto: 59901253.18 1866 | - time: 1534201200 1867 | close: 5947.97 1868 | high: 6297.02 1869 | low: 5891.87 1870 | open: 6268.99 1871 | volumefrom: 34335.5 1872 | volumeto: 208270003.07 1873 | - time: 1534215600 1874 | close: 5983.31 1875 | high: 6029.46 1876 | low: 5907.8 1877 | open: 5947.95 1878 | volumefrom: 17220.56 1879 | volumeto: 103225728.46 1880 | - time: 1534230000 1881 | close: 6034.93 1882 | high: 6082.41 1883 | low: 5974.82 1884 | open: 5982.77 1885 | volumefrom: 15196.51 1886 | volumeto: 91900430.46 1887 | - time: 1534244400 1888 | close: 6035.81 1889 | high: 6169.62 1890 | low: 5987.07 1891 | open: 6034.93 1892 | volumefrom: 22893.3 1893 | volumeto: 139321844.13 1894 | - time: 1534258800 1895 | close: 6058 1896 | high: 6093.76 1897 | low: 5993.99 1898 | open: 6035.81 1899 | volumefrom: 16325.58 1900 | volumeto: 98887893.44 1901 | - time: 1534273200 1902 | close: 6141.36 1903 | high: 6178 1904 | low: 6058 1905 | open: 6058 1906 | volumefrom: 14982.01 1907 | volumeto: 91884247.82 1908 | - time: 1534287600 1909 | close: 6273.01 1910 | high: 6300.01 1911 | low: 6121.58 1912 | open: 6141.5 1913 | volumefrom: 15619.08 1914 | volumeto: 97583730.79 1915 | - time: 1534302000 1916 | close: 6330.91 1917 | high: 6386.08 1918 | low: 6232.6 1919 | open: 6273.01 1920 | volumefrom: 20539.72 1921 | volumeto: 129783434.93 1922 | - time: 1534316400 1923 | close: 6300.84 1924 | high: 6481.69 1925 | low: 6289.37 1926 | open: 6330.86 1927 | volumefrom: 30135.3 1928 | volumeto: 193241917.33 1929 | - time: 1534330800 1930 | close: 6417.86 1931 | high: 6422.02 1932 | low: 6300.84 1933 | open: 6300.84 1934 | volumefrom: 13505.44 1935 | volumeto: 86220311.86 1936 | - time: 1534345200 1937 | close: 6535.5 1938 | high: 6620.07 1939 | low: 6401.25 1940 | open: 6417.97 1941 | volumefrom: 30955.78 1942 | volumeto: 202737429.57 1943 | - time: 1534359600 1944 | close: 6342.19 1945 | high: 6541.88 1946 | low: 6316.86 1947 | open: 6535.5 1948 | volumefrom: 21171.94 1949 | volumeto: 135904507.27 1950 | - time: 1534374000 1951 | close: 6301.78 1952 | high: 6351.37 1953 | low: 6217.33 1954 | open: 6342.2 1955 | volumefrom: 13892.09 1956 | volumeto: 87455583.93 1957 | - time: 1534388400 1958 | close: 6307.71 1959 | high: 6338.51 1960 | low: 6266.85 1961 | open: 6301.78 1962 | volumefrom: 8290.51 1963 | volumeto: 52514960.5 1964 | - time: 1534402800 1965 | close: 6454.62 1966 | high: 6475.02 1967 | low: 6307.71 1968 | open: 6307.71 1969 | volumefrom: 12577.87 1970 | volumeto: 80750375.59 1971 | - time: 1534417200 1972 | close: 6329.02 1973 | high: 6478.07 1974 | low: 6294.8 1975 | open: 6454.62 1976 | volumefrom: 15457.97 1977 | volumeto: 98641481.71 1978 | - time: 1534431600 1979 | close: 6431.76 1980 | high: 6467.51 1981 | low: 6307.36 1982 | open: 6327.62 1983 | volumefrom: 12787.96 1984 | volumeto: 82097352.98 1985 | - time: 1534446000 1986 | close: 6285.07 1987 | high: 6443.84 1988 | low: 6248.68 1989 | open: 6431.84 1990 | volumefrom: 13377.41 1991 | volumeto: 84833333.15 1992 | - time: 1534460400 1993 | close: 6426.71 1994 | high: 6449.51 1995 | low: 6272.99 1996 | open: 6285.24 1997 | volumefrom: 8644.52 1998 | volumeto: 55092544.49 1999 | - time: 1534474800 2000 | close: 6511.98 2001 | high: 6528.98 2002 | low: 6409.23 2003 | open: 6426.75 2004 | volumefrom: 12912.95 2005 | volumeto: 83783201.48 2006 | - time: 1534489200 2007 | close: 6433.98 2008 | high: 6560.06 2009 | low: 6416.35 2010 | open: 6511.98 2011 | volumefrom: 16328.08 2012 | volumeto: 106111205.79 2013 | - time: 1534503600 2014 | close: 6466.25 2015 | high: 6526.83 2016 | low: 6432.71 2017 | open: 6433.98 2018 | volumefrom: 9098.02 2019 | volumeto: 59087740.93 2020 | - time: 1534518000 2021 | close: 6524.53 2022 | high: 6571.57 2023 | low: 6466.32 2024 | open: 6466.34 2025 | volumefrom: 13586.4 2026 | volumeto: 88703342.56 2027 | - time: 1534532400 2028 | close: 6575.87 2029 | high: 6589.2 2030 | low: 6464.43 2031 | open: 6524.53 2032 | volumefrom: 11354.17 2033 | volumeto: 74248650.35 2034 | - time: 1534546800 2035 | close: 6513.74 2036 | high: 6622.16 2037 | low: 6513.74 2038 | open: 6575.87 2039 | volumefrom: 11619.55 2040 | volumeto: 76661759.27 2041 | - time: 1534561200 2042 | close: 6490.29 2043 | high: 6552.83 2044 | low: 6482.32 2045 | open: 6513.51 2046 | volumefrom: 6746.93 2047 | volumeto: 44086246.77 2048 | - time: 1534575600 2049 | close: 6518.06 2050 | high: 6529.73 2051 | low: 6467.88 2052 | open: 6490.29 2053 | volumefrom: 5235.49 2054 | volumeto: 34144422.67 2055 | - time: 1534590000 2056 | close: 6362.91 2057 | high: 6543.01 2058 | low: 6339.07 2059 | open: 6518.06 2060 | volumefrom: 12484.92 2061 | volumeto: 80233610.62 2062 | - time: 1534604400 2063 | close: 6379.26 2064 | high: 6389.09 2065 | low: 6322.44 2066 | open: 6362.91 2067 | volumefrom: 10265.28 2068 | volumeto: 65357199.2 2069 | - time: 1534618800 2070 | close: 6420.01 2071 | high: 6452.69 2072 | low: 6335.54 2073 | open: 6379.21 2074 | volumefrom: 7423.05 2075 | volumeto: 47540664.48 2076 | - time: 1534633200 2077 | close: 6382.29 2078 | high: 6431.91 2079 | low: 6358.53 2080 | open: 6420.01 2081 | volumefrom: 5105.97 2082 | volumeto: 32693709.3 2083 | - time: 1534647600 2084 | close: 6380.48 2085 | high: 6407.06 2086 | low: 6330.56 2087 | open: 6382.29 2088 | volumefrom: 5349.3 2089 | volumeto: 34168771.29 2090 | - time: 1534662000 2091 | close: 6392.74 2092 | high: 6404.23 2093 | low: 6365.61 2094 | open: 6380.48 2095 | volumefrom: 4593.54 2096 | volumeto: 29406457.51 2097 | - time: 1534676400 2098 | close: 6446.6 2099 | high: 6483.75 2100 | low: 6392.8 2101 | open: 6392.8 2102 | volumefrom: 6833.81 2103 | volumeto: 44189820.5 2104 | - time: 1534690800 2105 | close: 6391.28 2106 | high: 6451.89 2107 | low: 6355.5 2108 | open: 6446.77 2109 | volumefrom: 7073.56 2110 | volumeto: 45280352.53 2111 | - time: 1534705200 2112 | close: 6539.38 2113 | high: 6560.88 2114 | low: 6376.78 2115 | open: 6391.62 2116 | volumefrom: 10605.7 2117 | volumeto: 68830565.67 2118 | - time: 1534719600 2119 | close: 6518.27 2120 | high: 6541.81 2121 | low: 6495.88 2122 | open: 6539.52 2123 | volumefrom: 6425.92 2124 | volumeto: 41928839.49 2125 | - time: 1534734000 2126 | close: 6456.24 2127 | high: 6520.46 2128 | low: 6446.11 2129 | open: 6518.27 2130 | volumefrom: 4752.8 2131 | volumeto: 30879848.49 2132 | - time: 1534748400 2133 | close: 6426.6 2134 | high: 6484.25 2135 | low: 6404.22 2136 | open: 6456.5 2137 | volumefrom: 6934.44 2138 | volumeto: 44804631.81 2139 | - time: 1534762800 2140 | close: 6466.21 2141 | high: 6491.32 2142 | low: 6416.82 2143 | open: 6426.67 2144 | volumefrom: 7299.15 2145 | volumeto: 47248309.62 2146 | - time: 1534777200 2147 | close: 6485.04 2148 | high: 6523.4 2149 | low: 6432.74 2150 | open: 6466.31 2151 | volumefrom: 7883.39 2152 | volumeto: 51178727.13 2153 | - time: 1534791600 2154 | close: 6323.37 2155 | high: 6493.61 2156 | low: 6316.54 2157 | open: 6485.4 2158 | volumefrom: 24897.42 2159 | volumeto: 158423170.26 2160 | - time: 1534806000 2161 | close: 6322.72 2162 | high: 6352.9 2163 | low: 6248.39 2164 | open: 6323.43 2165 | volumefrom: 15014.67 2166 | volumeto: 94756321.5 2167 | - time: 1534820400 2168 | close: 6404.74 2169 | high: 6411.59 2170 | low: 6299.61 2171 | open: 6322.71 2172 | volumefrom: 9143.23 2173 | volumeto: 58262665.98 2174 | - time: 1534834800 2175 | close: 6458.91 2176 | high: 6501.13 2177 | low: 6401.76 2178 | open: 6404.74 2179 | volumefrom: 12934.87 2180 | volumeto: 83663601.12 2181 | - time: 1534849200 2182 | close: 6440.43 2183 | high: 6462.76 2184 | low: 6406.06 2185 | open: 6458.91 2186 | volumefrom: 10459.43 2187 | volumeto: 67385179.77 2188 | - time: 1534863600 2189 | close: 6463.85 2190 | high: 6471.32 2191 | low: 6400.35 2192 | open: 6440.43 2193 | volumefrom: 10108.57 2194 | volumeto: 65081429.96 2195 | - time: 1534878000 2196 | close: 6489.23 2197 | high: 6506.69 2198 | low: 6419.06 2199 | open: 6463.85 2200 | volumefrom: 7732.42 2201 | volumeto: 50157865.13 2202 | - time: 1534892400 2203 | close: 6741.04 2204 | high: 6890.79 2205 | low: 6453.41 2206 | open: 6489.54 2207 | volumefrom: 34152.03 2208 | volumeto: 230229233.38 2209 | - time: 1534906800 2210 | close: 6696.77 2211 | high: 6766.96 2212 | low: 6679.43 2213 | open: 6741.08 2214 | volumefrom: 9891.45 2215 | volumeto: 66605137.96 2216 | - time: 1534921200 2217 | close: 6671.47 2218 | high: 6711.62 2219 | low: 6653.75 2220 | open: 6696.78 2221 | volumefrom: 10824.89 2222 | volumeto: 72551704.18 2223 | - time: 1534935600 2224 | close: 6664.49 2225 | high: 6697.93 2226 | low: 6643.87 2227 | open: 6671.47 2228 | volumefrom: 7771.87 2229 | volumeto: 52066344.66 2230 | - time: 1534950000 2231 | close: 6422.91 2232 | high: 6677.01 2233 | low: 6405.85 2234 | open: 6664.49 2235 | volumefrom: 21099.71 2236 | volumeto: 136893791.97 2237 | - time: 1534964400 2238 | close: 6367.21 2239 | high: 6468.05 2240 | low: 6264.34 2241 | open: 6422.69 2242 | volumefrom: 31130.66 2243 | volumeto: 198000631.17 2244 | - time: 1534978800 2245 | close: 6427.14 2246 | high: 6447.01 2247 | low: 6341.96 2248 | open: 6367.23 2249 | volumefrom: 9145.99 2250 | volumeto: 58735979.43 2251 | - time: 1534993200 2252 | close: 6443.79 2253 | high: 6475.22 2254 | low: 6386.88 2255 | open: 6427.14 2256 | volumefrom: 8384.94 2257 | volumeto: 53978263.8 2258 | - time: 1535007600 2259 | close: 6435.11 2260 | high: 6453.97 2261 | low: 6406.8 2262 | open: 6443.79 2263 | volumefrom: 7465.52 2264 | volumeto: 48159191.35 2265 | - time: 1535022000 2266 | close: 6451.25 2267 | high: 6494.16 2268 | low: 6404 2269 | open: 6435.11 2270 | volumefrom: 10299.81 2271 | volumeto: 66527127.02 2272 | - time: 1535036400 2273 | close: 6455.54 2274 | high: 6495.58 2275 | low: 6441.73 2276 | open: 6451.36 2277 | volumefrom: 7764.08 2278 | volumeto: 50297625.37 2279 | - time: 1535050800 2280 | close: 6518.64 2281 | high: 6575.28 2282 | low: 6379.36 2283 | open: 6455.35 2284 | volumefrom: 11001.16 2285 | volumeto: 71323234.56 2286 | - time: 1535065200 2287 | close: 6520.01 2288 | high: 6548.28 2289 | low: 6492.73 2290 | open: 6518.68 2291 | volumefrom: 5248.45 2292 | volumeto: 34303667.23 2293 | - time: 1535079600 2294 | close: 6530.5 2295 | high: 6535.11 2296 | low: 6491.15 2297 | open: 6519.53 2298 | volumefrom: 5401.19 2299 | volumeto: 35258581.32 2300 | - time: 1535094000 2301 | close: 6557.53 2302 | high: 6564.97 2303 | low: 6525.47 2304 | open: 6530.52 2305 | volumefrom: 10950.04 2306 | volumeto: 71789961.23 2307 | - time: 1535108400 2308 | close: 6525.87 2309 | high: 6588.66 2310 | low: 6481.76 2311 | open: 6557.53 2312 | volumefrom: 11417.52 2313 | volumeto: 74844157.44 2314 | - time: 1535122800 2315 | close: 6654.08 2316 | high: 6661.02 2317 | low: 6509.15 2318 | open: 6525.62 2319 | volumefrom: 14121.55 2320 | volumeto: 93150959.17 2321 | - time: 1535137200 2322 | close: 6700.58 2323 | high: 6731.34 2324 | low: 6616.7 2325 | open: 6653.82 2326 | volumefrom: 9630.41 2327 | volumeto: 64346857.28 2328 | - time: 1535151600 2329 | close: 6778.4 2330 | high: 6808.89 2331 | low: 6687.46 2332 | open: 6700.61 2333 | volumefrom: 10666.4 2334 | volumeto: 71945908.99 2335 | - time: 1535166000 2336 | close: 6704.14 2337 | high: 6792.21 2338 | low: 6691.74 2339 | open: 6778.4 2340 | volumefrom: 8861.85 2341 | volumeto: 60258932.53 2342 | - time: 1535180400 2343 | close: 6726.08 2344 | high: 6743.96 2345 | low: 6684.19 2346 | open: 6704.24 2347 | volumefrom: 4281.84 2348 | volumeto: 28872148.58 2349 | - time: 1535194800 2350 | close: 6742.26 2351 | high: 6755.95 2352 | low: 6692.44 2353 | open: 6726.08 2354 | volumefrom: 6660.74 2355 | volumeto: 44898653.11 2356 | - time: 1535209200 2357 | close: 6725.81 2358 | high: 6763.75 2359 | low: 6710.33 2360 | open: 6742.03 2361 | volumefrom: 4553.06 2362 | volumeto: 30787831.26 2363 | - time: 1535223600 2364 | close: 6745.37 2365 | high: 6746.78 2366 | low: 6710.14 2367 | open: 6725.81 2368 | volumefrom: 2996.25 2369 | volumeto: 20240909.04 2370 | - time: 1535238000 2371 | close: 6666.99 2372 | high: 6793.17 2373 | low: 6658.72 2374 | open: 6745.37 2375 | volumefrom: 9900.13 2376 | volumeto: 66765994.28 2377 | - time: 1535252400 2378 | close: 6631.53 2379 | high: 6667.25 2380 | low: 6588.48 2381 | open: 6666.99 2382 | volumefrom: 6456.75 2383 | volumeto: 42879222.88 2384 | - time: 1535266800 2385 | close: 6680.19 2386 | high: 6695.19 2387 | low: 6624.73 2388 | open: 6631.39 2389 | volumefrom: 7187.92 2390 | volumeto: 47994966.19 2391 | - time: 1535281200 2392 | close: 6711.83 2393 | high: 6757.11 2394 | low: 6683.35 2395 | open: 6683.35 2396 | volumefrom: 7500.63 2397 | volumeto: 50461935.33 2398 | - time: 1535295600 2399 | close: 6713.94 2400 | high: 6724.27 2401 | low: 6653.26 2402 | open: 6711.83 2403 | volumefrom: 5319.64 2404 | volumeto: 35606853.57 2405 | - time: 1535310000 2406 | close: 6701.5 2407 | high: 6721.27 2408 | low: 6682.62 2409 | open: 6713.94 2410 | volumefrom: 4851.28 2411 | volumeto: 32566599.4 2412 | - time: 1535324400 2413 | close: 6690.28 2414 | high: 6733.61 2415 | low: 6681.91 2416 | open: 6701.5 2417 | volumefrom: 4867.13 2418 | volumeto: 32696116.2 2419 | - time: 1535338800 2420 | close: 6734.2 2421 | high: 6752.57 2422 | low: 6674.84 2423 | open: 6690.55 2424 | volumefrom: 8106.67 2425 | volumeto: 54611005.57 2426 | - time: 1535353200 2427 | close: 6726.75 2428 | high: 6742.73 2429 | low: 6706.53 2430 | open: 6736.4 2431 | volumefrom: 7973.82 2432 | volumeto: 53744270.61 2433 | - time: 1535367600 2434 | close: 6737.27 2435 | high: 6781.8 2436 | low: 6707.51 2437 | open: 6726.75 2438 | volumefrom: 14950.37 2439 | volumeto: 100569606.37 2440 | - time: 1535382000 2441 | close: 6748.76 2442 | high: 6770.4 2443 | low: 6737.22 2444 | open: 6737.37 2445 | volumefrom: 7522.55 2446 | volumeto: 50858994.37 2447 | - time: 1535396400 2448 | close: 6767.33 2449 | high: 6774.71 2450 | low: 6728.97 2451 | open: 6748.76 2452 | volumefrom: 6398.2 2453 | volumeto: 43247889.75 2454 | - time: 1535410800 2455 | close: 6888.61 2456 | high: 6952.21 2457 | low: 6754.63 2458 | open: 6767.33 2459 | volumefrom: 22529.98 2460 | volumeto: 155325314.43 2461 | - time: 1535425200 2462 | close: 6924.29 2463 | high: 6936.74 2464 | low: 6888.63 2465 | open: 6888.63 2466 | volumefrom: 6597.05 2467 | volumeto: 45719772.69 2468 | - time: 1535439600 2469 | close: 6936.32 2470 | high: 6948.4 2471 | low: 6917.53 2472 | open: 6922.45 2473 | volumefrom: 7725.16 2474 | volumeto: 53809966.58 2475 | - time: 1535454000 2476 | close: 7071.61 2477 | high: 7098.1 2478 | low: 6936.28 2479 | open: 6936.32 2480 | volumefrom: 21693.98 2481 | volumeto: 153052042.78 2482 | - time: 1535468400 2483 | close: 7071.05 2484 | high: 7087.22 2485 | low: 7006.7 2486 | open: 7071.61 2487 | volumefrom: 11152.22 2488 | volumeto: 78795779.71 2489 | - time: 1535482800 2490 | close: 7092.11 2491 | high: 7136.71 2492 | low: 7046.6 2493 | open: 7071.46 2494 | volumefrom: 9378.26 2495 | volumeto: 66769657.03 2496 | - time: 1535497200 2497 | close: 7042.11 2498 | high: 7126.6 2499 | low: 7034.2 2500 | open: 7092.43 2501 | volumefrom: 6143.85 2502 | volumeto: 43592621.6 2503 | - time: 1535511600 2504 | close: 7065.43 2505 | high: 7088.37 2506 | low: 7031.48 2507 | open: 7042.11 2508 | volumefrom: 5181.89 2509 | volumeto: 36707532.79 2510 | - time: 1535526000 2511 | close: 7100.2 2512 | high: 7137.47 2513 | low: 7055.03 2514 | open: 7065.42 2515 | volumefrom: 8923.72 2516 | volumeto: 63431211.96 2517 | - time: 1535540400 2518 | close: 7095.71 2519 | high: 7127.98 2520 | low: 7073.43 2521 | open: 7100.2 2522 | volumefrom: 9024.37 2523 | volumeto: 64302643.69 2524 | - time: 1535554800 2525 | close: 7033.74 2526 | high: 7099.23 2527 | low: 6944.76 2528 | open: 7095.71 2529 | volumefrom: 15620.62 2530 | volumeto: 109587561.16 2531 | - time: 1535569200 2532 | close: 7077.98 2533 | high: 7083.34 2534 | low: 7011.32 2535 | open: 7033.26 2536 | volumefrom: 5938.3 2537 | volumeto: 41956658.14 2538 | - time: 1535583600 2539 | close: 7024.1 2540 | high: 7079.1 2541 | low: 6984.79 2542 | open: 7077.98 2543 | volumefrom: 5405.74 2544 | volumeto: 38109736.96 2545 | - time: 1535598000 2546 | close: 7021.93 2547 | high: 7024.17 2548 | low: 7015.69 2549 | open: 7024.1 2550 | volumefrom: 62.16 2551 | volumeto: 435410.89 2552 | TimeTo: 1535598000 2553 | TimeFrom: 1533006000 2554 | FirstValueInArray: true 2555 | ConversionType: 2556 | type: direct 2557 | conversionSymbol: '' 2558 | --------------------------------------------------------------------------------