commit 5f2472a11f8617681efa552e5b1f70dda7608f2b Author: TBS093A Date: Thu May 7 09:10:05 2020 +0200 init repository diff --git a/TradeApp/__init__.py b/TradeApp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/TradeApp/__pycache__/__init__.cpython-36.pyc b/TradeApp/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..f328291 Binary files /dev/null and b/TradeApp/__pycache__/__init__.cpython-36.pyc differ diff --git a/TradeApp/__pycache__/routing.cpython-36.pyc b/TradeApp/__pycache__/routing.cpython-36.pyc new file mode 100644 index 0000000..6329646 Binary files /dev/null and b/TradeApp/__pycache__/routing.cpython-36.pyc differ diff --git a/TradeApp/__pycache__/settings.cpython-36.pyc b/TradeApp/__pycache__/settings.cpython-36.pyc new file mode 100644 index 0000000..259653b Binary files /dev/null and b/TradeApp/__pycache__/settings.cpython-36.pyc differ diff --git a/TradeApp/__pycache__/urls.cpython-36.pyc b/TradeApp/__pycache__/urls.cpython-36.pyc new file mode 100644 index 0000000..1c58687 Binary files /dev/null and b/TradeApp/__pycache__/urls.cpython-36.pyc differ diff --git a/TradeApp/__pycache__/wsgi.cpython-36.pyc b/TradeApp/__pycache__/wsgi.cpython-36.pyc new file mode 100644 index 0000000..7609f03 Binary files /dev/null and b/TradeApp/__pycache__/wsgi.cpython-36.pyc differ diff --git a/TradeApp/routing.py b/TradeApp/routing.py new file mode 100644 index 0000000..4f95448 --- /dev/null +++ b/TradeApp/routing.py @@ -0,0 +1,12 @@ +from channels.auth import AuthMiddlewareStack +from channels.routing import ProtocolTypeRouter, URLRouter +import chat.routing + +application = ProtocolTypeRouter({ + # (http->django views is added by default) + 'websocket': AuthMiddlewareStack( + URLRouter( + chat.routing.websocket_urlpatterns + ) + ), +}) \ No newline at end of file diff --git a/TradeApp/settings.py b/TradeApp/settings.py new file mode 100644 index 0000000..b656752 --- /dev/null +++ b/TradeApp/settings.py @@ -0,0 +1,144 @@ +""" +Django settings for TradeApp project. + +Generated by 'django-admin startproject' using Django 2.2.6. + +For more information on this file, see +https://docs.djangoproject.com/en/2.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/2.2/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = ')o!ecewa-#m^$l$qy*pb)l&$swgt*xx8#j#hm3&2%^t#w-tqwu' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'generalApp', + 'chat', + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'corsheaders', + 'channels' +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'corsheaders.middleware.CorsMiddleware', + 'django.middleware.common.BrokenLinkEmailsMiddleware', + 'django.middleware.common.CommonMiddleware', +] + +CORS_ORIGIN_ALLOW_ALL = True + +# CORS_ORIGIN_ALLOW_ALL = False +# +# CORS_ORIGIN_WHITELIST = ( +# 'http//:localhost:8000', +# ) + +ROOT_URLCONF = 'TradeApp.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'TradeApp.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/2.2/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + +# Password validation +# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + +# Routing for channels websocket + +ASGI_APPLICATION = "TradeApp.routing.application" +CHANNEL_LAYERS = { + 'default': { + 'BACKEND': 'channels_redis.core.RedisChannelLayer', + 'CONFIG': { + "hosts": [('127.0.0.1', 6379)], + }, + }, +} + +# Internationalization +# https://docs.djangoproject.com/en/2.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/2.2/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/TradeApp/urls.py b/TradeApp/urls.py new file mode 100644 index 0000000..e9a2ff1 --- /dev/null +++ b/TradeApp/urls.py @@ -0,0 +1,23 @@ +"""TradeApp URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/2.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include + +urlpatterns = [ + path('index/', include('generalApp.urls')), + path('chat/', include('chat.urls')), + path('admin/', admin.site.urls), +] diff --git a/TradeApp/wsgi.py b/TradeApp/wsgi.py new file mode 100644 index 0000000..214083e --- /dev/null +++ b/TradeApp/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for TradeApp project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'TradeApp.settings') + +application = get_wsgi_application() diff --git a/chat/__init__.py b/chat/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/chat/__pycache__/__init__.cpython-36.pyc b/chat/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..563a854 Binary files /dev/null and b/chat/__pycache__/__init__.cpython-36.pyc differ diff --git a/chat/__pycache__/admin.cpython-36.pyc b/chat/__pycache__/admin.cpython-36.pyc new file mode 100644 index 0000000..99a7707 Binary files /dev/null and b/chat/__pycache__/admin.cpython-36.pyc differ diff --git a/chat/__pycache__/apps.cpython-36.pyc b/chat/__pycache__/apps.cpython-36.pyc new file mode 100644 index 0000000..964a36f Binary files /dev/null and b/chat/__pycache__/apps.cpython-36.pyc differ diff --git a/chat/__pycache__/consumers.cpython-36.pyc b/chat/__pycache__/consumers.cpython-36.pyc new file mode 100644 index 0000000..3a8487c Binary files /dev/null and b/chat/__pycache__/consumers.cpython-36.pyc differ diff --git a/chat/__pycache__/models.cpython-36.pyc b/chat/__pycache__/models.cpython-36.pyc new file mode 100644 index 0000000..4124499 Binary files /dev/null and b/chat/__pycache__/models.cpython-36.pyc differ diff --git a/chat/__pycache__/routing.cpython-36.pyc b/chat/__pycache__/routing.cpython-36.pyc new file mode 100644 index 0000000..0bf3a05 Binary files /dev/null and b/chat/__pycache__/routing.cpython-36.pyc differ diff --git a/chat/__pycache__/urls.cpython-36.pyc b/chat/__pycache__/urls.cpython-36.pyc new file mode 100644 index 0000000..f1db64c Binary files /dev/null and b/chat/__pycache__/urls.cpython-36.pyc differ diff --git a/chat/__pycache__/views.cpython-36.pyc b/chat/__pycache__/views.cpython-36.pyc new file mode 100644 index 0000000..172b1c0 Binary files /dev/null and b/chat/__pycache__/views.cpython-36.pyc differ diff --git a/chat/admin.py b/chat/admin.py new file mode 100644 index 0000000..ce85145 --- /dev/null +++ b/chat/admin.py @@ -0,0 +1,4 @@ +from django.contrib import admin +from .models import * + +admin.site.register(Lobby) \ No newline at end of file diff --git a/chat/consumers.py b/chat/consumers.py new file mode 100644 index 0000000..11c286c --- /dev/null +++ b/chat/consumers.py @@ -0,0 +1,61 @@ +from asgiref.sync import async_to_sync +from channels.generic.websocket import WebsocketConsumer +import json +from .models import Lobby + +class ChatConsumer(WebsocketConsumer): + def connect(self): + self.room_name = self.scope['url_route']['kwargs']['room_name'] + self.room_group_name = f'chat_{self.room_name}' + + checkSave = False + for lobby in Lobby.objects.all(): + if lobby.name == self.room_name: + lobby.userCount = lobby.userCount + 1 + Lobby.save(lobby) + checkSave = True + break + if checkSave == False: + lobbyRegister = Lobby() + lobbyRegister.name= self.room_name + lobbyRegister.userCount = 1 + Lobby.save(lobbyRegister) + + async_to_sync(self.channel_layer.group_add)( + self.room_group_name, + self.channel_name + ) + + self.accept() + + def disconnect(self, close_code): + + updateCountOfUsers = Lobby.objects.get(name = self.room_name) + updateCountOfUsers.userCount = updateCountOfUsers.userCount - 1 + if updateCountOfUsers.userCount == 0: + Lobby.delete(updateCountOfUsers) + else: + Lobby.save(updateCountOfUsers) + + async_to_sync(self.channel_layer.group_discard)( + self.room_group_name, + self.channel_name + ) + + def receive(self, text_data): + text_data_json = json.loads(text_data) + message = text_data_json + + async_to_sync(self.channel_layer.group_send)( + self.room_group_name, + { + 'type': 'chat_message', + 'message': message + } + ) + + def chat_message(self, event): + message = event['message'] + + # Send message to WebSocket + self.send(text_data=json.dumps(message)) \ No newline at end of file diff --git a/chat/migrations/0001_initial.py b/chat/migrations/0001_initial.py new file mode 100644 index 0000000..aaa371d --- /dev/null +++ b/chat/migrations/0001_initial.py @@ -0,0 +1,22 @@ +# Generated by Django 2.2.6 on 2020-05-01 15:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Lobby', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=30)), + ('userCount', models.IntegerField()), + ], + ), + ] diff --git a/chat/migrations/__init__.py b/chat/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/chat/migrations/__pycache__/0001_initial.cpython-36.pyc b/chat/migrations/__pycache__/0001_initial.cpython-36.pyc new file mode 100644 index 0000000..39938f4 Binary files /dev/null and b/chat/migrations/__pycache__/0001_initial.cpython-36.pyc differ diff --git a/chat/migrations/__pycache__/__init__.cpython-36.pyc b/chat/migrations/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..6305c0d Binary files /dev/null and b/chat/migrations/__pycache__/__init__.cpython-36.pyc differ diff --git a/chat/models.py b/chat/models.py new file mode 100644 index 0000000..5e0763b --- /dev/null +++ b/chat/models.py @@ -0,0 +1,25 @@ +from django.db import models +from django.http import HttpResponse +import json + +class Lobby(models.Model): + name = models.CharField(max_length=30) + userCount = models.IntegerField() + + def __str__(self): + return f"{self.id} {self.name} {self.userCount}" + + def fromDict(self, dict): + self.__dict__.update(dict) + + def toDict(self): + return {"id": self.id, + "name": self.name, + "userCount": self.userCount} + + def allObjectsDict(self): + objectAll = self.objects.all() + list = [] + for x in objectAll: + list.append(x.toDict()) + return json.dumps(list) \ No newline at end of file diff --git a/chat/routing.py b/chat/routing.py new file mode 100644 index 0000000..a1806ae --- /dev/null +++ b/chat/routing.py @@ -0,0 +1,7 @@ +from django.urls import re_path + +from . import consumers + +websocket_urlpatterns = [ + re_path(r'ws/(?P[^/]+)/$', consumers.ChatConsumer), +] \ No newline at end of file diff --git a/chat/urls.py b/chat/urls.py new file mode 100644 index 0000000..9b3230c --- /dev/null +++ b/chat/urls.py @@ -0,0 +1,8 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path('', views.chatIndex), + path('', views.chatConnect) +] diff --git a/chat/views.py b/chat/views.py new file mode 100644 index 0000000..7a6550a --- /dev/null +++ b/chat/views.py @@ -0,0 +1,18 @@ +from django.http import HttpResponse +from .models import Lobby +import json + +# REST Definition + +def chatIndex(request): + if request.method == 'GET': + return HttpResponse(Lobby.allObjectsDict(Lobby)) + if request.method == 'POST': + Lobby.save(json.load(request)) + +def chatConnect(request, roomName): + if request.method == 'GET': + return HttpResponse(f'connect to {roomName}') + if request.method == 'DELETE': + return HttpResponse('leave the chatroom') + \ No newline at end of file diff --git a/db.sqlite3 b/db.sqlite3 new file mode 100644 index 0000000..a7768d3 Binary files /dev/null and b/db.sqlite3 differ diff --git a/generalApp/__init__.py b/generalApp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/generalApp/__pycache__/__init__.cpython-36.pyc b/generalApp/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..d59f9cf Binary files /dev/null and b/generalApp/__pycache__/__init__.cpython-36.pyc differ diff --git a/generalApp/__pycache__/admin.cpython-36.pyc b/generalApp/__pycache__/admin.cpython-36.pyc new file mode 100644 index 0000000..cea926c Binary files /dev/null and b/generalApp/__pycache__/admin.cpython-36.pyc differ diff --git a/generalApp/__pycache__/apps.cpython-36.pyc b/generalApp/__pycache__/apps.cpython-36.pyc new file mode 100644 index 0000000..6dd6da6 Binary files /dev/null and b/generalApp/__pycache__/apps.cpython-36.pyc differ diff --git a/generalApp/__pycache__/exchangeVO.cpython-36.pyc b/generalApp/__pycache__/exchangeVO.cpython-36.pyc new file mode 100644 index 0000000..029ef50 Binary files /dev/null and b/generalApp/__pycache__/exchangeVO.cpython-36.pyc differ diff --git a/generalApp/__pycache__/methods.cpython-36.pyc b/generalApp/__pycache__/methods.cpython-36.pyc new file mode 100644 index 0000000..c2b7c46 Binary files /dev/null and b/generalApp/__pycache__/methods.cpython-36.pyc differ diff --git a/generalApp/__pycache__/models.cpython-36.pyc b/generalApp/__pycache__/models.cpython-36.pyc new file mode 100644 index 0000000..917c711 Binary files /dev/null and b/generalApp/__pycache__/models.cpython-36.pyc differ diff --git a/generalApp/__pycache__/session.cpython-36.pyc b/generalApp/__pycache__/session.cpython-36.pyc new file mode 100644 index 0000000..dd999a3 Binary files /dev/null and b/generalApp/__pycache__/session.cpython-36.pyc differ diff --git a/generalApp/__pycache__/urls.cpython-36.pyc b/generalApp/__pycache__/urls.cpython-36.pyc new file mode 100644 index 0000000..a05a39b Binary files /dev/null and b/generalApp/__pycache__/urls.cpython-36.pyc differ diff --git a/generalApp/__pycache__/utilities.cpython-36.pyc b/generalApp/__pycache__/utilities.cpython-36.pyc new file mode 100644 index 0000000..54da211 Binary files /dev/null and b/generalApp/__pycache__/utilities.cpython-36.pyc differ diff --git a/generalApp/__pycache__/utils.cpython-36.pyc b/generalApp/__pycache__/utils.cpython-36.pyc new file mode 100644 index 0000000..9f12e1e Binary files /dev/null and b/generalApp/__pycache__/utils.cpython-36.pyc differ diff --git a/generalApp/__pycache__/views.cpython-36.pyc b/generalApp/__pycache__/views.cpython-36.pyc new file mode 100644 index 0000000..a3c4c37 Binary files /dev/null and b/generalApp/__pycache__/views.cpython-36.pyc differ diff --git a/generalApp/admin.py b/generalApp/admin.py new file mode 100644 index 0000000..594befc --- /dev/null +++ b/generalApp/admin.py @@ -0,0 +1,14 @@ +from django.contrib import admin + +from .models import * + +admin.site.register(Users) +admin.site.register(Threads) +admin.site.register(Subjects) +admin.site.register(Comments) +admin.site.register(Ratings) +admin.site.register(Transactions) +admin.site.register(Triggers) +admin.site.register(Notifications) +# login: Admin +# password: Admin diff --git a/generalApp/apps.py b/generalApp/apps.py new file mode 100644 index 0000000..240543c --- /dev/null +++ b/generalApp/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class GeneralappConfig(AppConfig): + name = 'generalApp' diff --git a/generalApp/exchangeVO.py b/generalApp/exchangeVO.py new file mode 100644 index 0000000..d72b305 --- /dev/null +++ b/generalApp/exchangeVO.py @@ -0,0 +1,122 @@ +from django.http import HttpResponse +from datetime import datetime +from .models import * +from .utilities import * +import threading +import requests +import json +import os + +class ExchangeVO(): + + triggerList = None + + def __init__(self): + pass + + @classmethod + def checkTrigger(self): + triggerAll = Triggers.objects.filter(status = 1).values("id", "user_id", "status", "course_values_for_trigger", "date_of_trigger") + self.triggerList = [] + for x in triggerAll: + self.triggerList.append(x) + actualGraph = self.refreshGraph(1800) + for candle in actualGraph['candles']: + candleDate = datetime.strptime(candle['Date'], "%Y-%m-%d %H:%M:%S") + inArea = 0 + for trigger in self.triggerList: + triggerDate = datetime.strptime(trigger['date_of_trigger'], "%Y-%d-%m %H:%M") + if int(trigger['course_values_for_trigger']) > int(candle['Min']) and triggerDate < candleDate: + inArea += 1 + if int(trigger['course_values_for_trigger']) < int(candle['Max']) and triggerDate < candleDate: + inArea += 1 + if inArea == 2: + disableTrigger = Triggers.objects.get(id = int(trigger['id'])) + disableTrigger.status = 0 + message = f"Exchange got Your Trigger Value! \nTrigger Value: {trigger['course_values_for_trigger']} \nCandle Date: {candle['Date']}" + newNotification = Notifications(user_id = int(trigger['user_id']), message = message) + newNotification.save() + disableTrigger.save() + inArea = 0 + print(self.triggerList) + + @classmethod + def createActualPrognosis(self, request, time, price, privilige): + actualGraph = self.refreshGraph(time) + svg = [] + svgAll = 0 + volume = [] + actualCourse = 0 + for candle in actualGraph['candles']: + svg.append((float(candle['Close'] + candle['Open']) / 2)) + volume.append(float(candle['Volume'])) + actualCourse = float(candle['Close']) + datePayment = candle['Date'] + if svgAll == 0: + svgAll = svgAll + svg[-1] + else: + svgAll = (svgAll + svg[-1]) / 2 + onePercentOfActualCourse = actualCourse / 100 + percentsFromPriceToCourse = price / onePercentOfActualCourse + onePercentOfSvgCourse = svgAll / 100 + priceAfterCourse = percentsFromPriceToCourse * onePercentOfSvgCourse + difference = priceAfterCourse - price + percentDifference = difference / onePercentOfSvgCourse + data = { + "price" : price, + "price_forecast" : priceAfterCourse, + "percent_of_difference" : percentDifference, + "course_on_payment" : actualCourse, + "svg_of_all" : svgAll, + "date_of_transaction" : datePayment + } + return HttpResponse(json.dumps(data)) + + @classmethod + def refreshGraph(self, time): + graph = [] + try: + graph = self.createGraph(time) + except: + currentDirectory = os.path.dirname(__file__) + jsonPath = os.path.join(currentDirectory, '../testGraph.txt') + with open(jsonPath) as graphJson: + graph = json.load(graphJson) + return self.createGraph(time) + + @classmethod + def createGraph(self, time): + miliseconds = 1000 + firstResult = int(datetime.now().timestamp() * miliseconds) + lastResult = firstResult - 100000 * (time * 2) + url = f"https://api.bitbay.net/rest/trading/candle/history/BTC-PLN/{time}?from={lastResult}&to={firstResult}" + querystring = {"from": f"{lastResult}","to": f"{firstResult}"} + exchangeGraph = requests.request("GET", url, params=querystring) + response = json.loads(exchangeGraph.text) + graph = { 'candles': [], 'candlesCount' : 0, 'graphMin': float(response['items'][0][1]['h']), 'graphMax': 0 } + for x in range(len(response['items'])): + graph['candles'].append({'Open': 0, 'Close': 0, 'Max': 0, 'Min': 0, 'Volume': 0, 'Date': 0 }) + graph['candles'][x]['Open'] = float(response['items'][x][1]['o']) + graph['candles'][x]['Close'] = float(response['items'][x][1]['c']) + graph['candles'][x]['Max'] = float(response['items'][x][1]['h']) + graph['candles'][x]['Min'] = float(response['items'][x][1]['l']) + graph['candles'][x]['Volume'] = float(response['items'][x][1]['v']) + graph['candles'][x]['Date'] = str(datetime.fromtimestamp(int(response['items'][x][0])/1000.0)) + if graph['candles'][x]['Min'] < graph['graphMin']: + graph['graphMin'] = graph['candles'][x]['Min'] + if graph['candles'][x]['Max'] > graph['graphMax']: + graph['graphMax'] = graph['candles'][x]['Max'] + graph['candlesCount'] = len(response['items']) - 1 + return graph + + @classmethod + def getGraphView(self, request, time): + graph = [] + try: + graph = self.createGraph(time) + except: + currentDirectory = os.path.dirname(__file__) + jsonPath = os.path.join(currentDirectory, '../testGraph.txt') + with open(jsonPath) as graphJson: + graph = json.load(graphJson) + return HttpResponse(json.dumps(graph)) diff --git a/generalApp/methods.py b/generalApp/methods.py new file mode 100644 index 0000000..ac70cda --- /dev/null +++ b/generalApp/methods.py @@ -0,0 +1,166 @@ +from django.http import HttpResponse +from django.db import connection +from .exchangeVO import * +from .utilities import * +from .models import * +import requests +import json +import time + +# Exchange Asynchronic Notifications + +@newThread +def checkTriggerNotification(): + while True: + ExchangeVO.checkTrigger() + connection.close() + time.sleep(1800) + +# Exchange POST Methods + +def addTrigger(request, userID): + return Triggers.addObject(request, userID, 1) + +def addTransaction(request, userID): + return Transactions.addObject(request, userID, 1) + +def Prognosis(request, time, price): + return ExchangeVO.createActualPrognosis(request, time, price, 1) + +# Exchange GET Methods + +def getExchangeGraph(request, time): + return ExchangeVO.getGraphView(request, time) + +def getTrigger(request, id): + return Triggers.getObject(request, id, 1) + +def getTransaction(request, id): + return Transactions.getObject(request, id, 1) + +def getUserTriggers(request, userID): + return Triggers.getObjectsByParentID(request, userID, 1) + +def getUserTransactions(request, userID): + return Transactions.getObjectByParentID(request, userID, 1) + +def getTriggersAll(request): + return Triggers.getAllObjects(request, 2) + +def getTransactionsAll(request): + return Transactions.getAllObjects(request, 2) + +def getUserNotifications(request, userID): + return Notifications.getObjectsByParentID(request, userID, 1) + +# Exchange PUT Methods + +def putTrigger(request, id): + return Triggers.putObject(request, id, 1) + +def putTransaction(request, id): + return Transactions.putObject(request, id, 1) + +# Exchange DELETE Methods + +def deleteTrigger(request, id): + return Triggers.deleteObject(request, id, 1) + +def deleteTransaction(request, id): + return Transactions.deleteObject(request, id, 1) + +def deleteNotification(request, id): + return Notifications.deleteObject(request, id, 1) + +# Forum POST Methods + +def loginUser(request): + login = jsonLoad(request) + if login['login'] is not None and login['password'] is not None: + users = Users.objects.all() + for x in users: + if x.login == login['login']: + if checkPassHash(login['password'], x.password): + newSession = createSession(request, x.toDict()) + return HttpResponse(json.dumps({ 'token': newSession })) + return HttpResponse("Login Failed") + +def logoutUser(request): + return deleteSession(request) + +def registerUser(request): + return Users.addObject(request, None, None) + +def addThread(request): + return Threads.addObject(request, None, 2) + +def addSubject(request, threadID): + return Subjects.addObject(request, threadID, 1) + +def addComment(request, subjectID): + return Comments.addObject(request, subjectID, 1) + +def addRating(request, commentID): + return Ratings.addObject(request, commentID, 1) + +# Forum GET Methods + +def getUser(request, id): + return Users.getObject(request, id, 2) + +def getUsersAll(request): + return Users.getAllObjects(request, 2) + +def getThreadsAll(request): + return Threads.getAllObjects(request, 1) + +def getThreadSubjects(request, threadID): + return Subjects.getObjectsByParentID(request, threadID, 1) + +def getSubjectComments(request, subjectID): + return Comments.getObjectsByParentID(request, subjectID, 1) + +def getCommentRatings(request, commentID): + return Ratings.getObjectsByParentID(request, commentID, 1) + +# Forum PUT Methods + +def putUser(request, id): + return Users.putObject(request, id, 1) + + +def putThread(request, id): + return Threads.putObject(request, id, 2) + + +def putSubject(request, id): + return Subjects.putObject(request, id, 1) + + +def putComment(request, id): + return Comments.putObject(request, id, 1) + + +def putRating(request, id): + return Ratings.putObject(request, id, 1) + +# Forum DELETE Methods + +def deleteUser(request, id): + return Users.deleteObject(request, id, 1) + + +def deleteThread(request, id): + return Threads.deleteObject(request, id, 2) + + +def deleteSubject(request, id): + return Subjects.deleteObject(request, id, 1) + + +def deleteComment(request, id): + return Comments.deleteObject(request, id, 1) + + +def deleteRating(request, id): + return Ratings.deleteObject(request, id, 1) diff --git a/generalApp/migrations/0001_initial.py b/generalApp/migrations/0001_initial.py new file mode 100644 index 0000000..ac3d48d --- /dev/null +++ b/generalApp/migrations/0001_initial.py @@ -0,0 +1,110 @@ +# Generated by Django 2.2.6 on 2019-12-19 16:17 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Comments', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('text', models.CharField(max_length=255)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Users', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('login', models.CharField(max_length=30)), + ('password', models.CharField(max_length=30)), + ('email', models.EmailField(max_length=50)), + ('privilige', models.IntegerField(default=1)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Triggers', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('course_values_for_trigger', models.FloatField(default=255)), + ('date_of_trigger', models.DateTimeField(verbose_name='date of trigger')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='generalApp.Users')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Transactions', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('price', models.FloatField(default=255)), + ('price_forecast', models.FloatField(default=255)), + ('currency', models.CharField(max_length=255)), + ('date_of_transaction', models.DateTimeField(verbose_name='date of transaction')), + ('course_on_payment', models.FloatField(default=255)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='generalApp.Users')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Threads', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=30)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='generalApp.Users')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Subjects', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=30)), + ('thread', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='generalApp.Threads')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='generalApp.Users')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Ratings', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('value', models.IntegerField()), + ('comment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='generalApp.Comments')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='generalApp.Users')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='comments', + name='subject', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='generalApp.Subjects'), + ), + migrations.AddField( + model_name='comments', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='generalApp.Users'), + ), + ] diff --git a/generalApp/migrations/0002_triggers_status.py b/generalApp/migrations/0002_triggers_status.py new file mode 100644 index 0000000..46182af --- /dev/null +++ b/generalApp/migrations/0002_triggers_status.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.6 on 2019-12-29 13:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('generalApp', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='triggers', + name='status', + field=models.IntegerField(default=1), + ), + ] diff --git a/generalApp/migrations/0003_auto_20191229_1407.py b/generalApp/migrations/0003_auto_20191229_1407.py new file mode 100644 index 0000000..6410703 --- /dev/null +++ b/generalApp/migrations/0003_auto_20191229_1407.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.6 on 2019-12-29 14:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('generalApp', '0002_triggers_status'), + ] + + operations = [ + migrations.AlterField( + model_name='triggers', + name='date_of_trigger', + field=models.CharField(max_length=255), + ), + ] diff --git a/generalApp/migrations/0004_notifications.py b/generalApp/migrations/0004_notifications.py new file mode 100644 index 0000000..ad78019 --- /dev/null +++ b/generalApp/migrations/0004_notifications.py @@ -0,0 +1,25 @@ +# Generated by Django 2.2.6 on 2019-12-29 15:59 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('generalApp', '0003_auto_20191229_1407'), + ] + + operations = [ + migrations.CreateModel( + name='Notifications', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('message', models.CharField(max_length=255)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='generalApp.Users')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/generalApp/migrations/0005_users_avatar.py b/generalApp/migrations/0005_users_avatar.py new file mode 100644 index 0000000..37e513c --- /dev/null +++ b/generalApp/migrations/0005_users_avatar.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.6 on 2020-01-03 16:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('generalApp', '0004_notifications'), + ] + + operations = [ + migrations.AddField( + model_name='users', + name='avatar', + field=models.CharField(default='none', max_length=255), + ), + ] diff --git a/generalApp/migrations/0006_auto_20200113_2147.py b/generalApp/migrations/0006_auto_20200113_2147.py new file mode 100644 index 0000000..4932d2d --- /dev/null +++ b/generalApp/migrations/0006_auto_20200113_2147.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.6 on 2020-01-13 21:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('generalApp', '0005_users_avatar'), + ] + + operations = [ + migrations.AlterField( + model_name='comments', + name='text', + field=models.CharField(max_length=1000), + ), + ] diff --git a/generalApp/migrations/0007_auto_20200115_1743.py b/generalApp/migrations/0007_auto_20200115_1743.py new file mode 100644 index 0000000..b26e51c --- /dev/null +++ b/generalApp/migrations/0007_auto_20200115_1743.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.6 on 2020-01-15 17:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('generalApp', '0006_auto_20200113_2147'), + ] + + operations = [ + migrations.AlterField( + model_name='users', + name='password', + field=models.CharField(max_length=200), + ), + ] diff --git a/generalApp/migrations/__init__.py b/generalApp/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/generalApp/migrations/__pycache__/0001_initial.cpython-36.pyc b/generalApp/migrations/__pycache__/0001_initial.cpython-36.pyc new file mode 100644 index 0000000..5c336b6 Binary files /dev/null and b/generalApp/migrations/__pycache__/0001_initial.cpython-36.pyc differ diff --git a/generalApp/migrations/__pycache__/0002_triggers_status.cpython-36.pyc b/generalApp/migrations/__pycache__/0002_triggers_status.cpython-36.pyc new file mode 100644 index 0000000..e2d0e5f Binary files /dev/null and b/generalApp/migrations/__pycache__/0002_triggers_status.cpython-36.pyc differ diff --git a/generalApp/migrations/__pycache__/0003_auto_20191229_1407.cpython-36.pyc b/generalApp/migrations/__pycache__/0003_auto_20191229_1407.cpython-36.pyc new file mode 100644 index 0000000..90041cb Binary files /dev/null and b/generalApp/migrations/__pycache__/0003_auto_20191229_1407.cpython-36.pyc differ diff --git a/generalApp/migrations/__pycache__/0004_notifications.cpython-36.pyc b/generalApp/migrations/__pycache__/0004_notifications.cpython-36.pyc new file mode 100644 index 0000000..5b48424 Binary files /dev/null and b/generalApp/migrations/__pycache__/0004_notifications.cpython-36.pyc differ diff --git a/generalApp/migrations/__pycache__/0005_users_avatar.cpython-36.pyc b/generalApp/migrations/__pycache__/0005_users_avatar.cpython-36.pyc new file mode 100644 index 0000000..b9fbbbd Binary files /dev/null and b/generalApp/migrations/__pycache__/0005_users_avatar.cpython-36.pyc differ diff --git a/generalApp/migrations/__pycache__/0006_auto_20200113_2147.cpython-36.pyc b/generalApp/migrations/__pycache__/0006_auto_20200113_2147.cpython-36.pyc new file mode 100644 index 0000000..4a498fc Binary files /dev/null and b/generalApp/migrations/__pycache__/0006_auto_20200113_2147.cpython-36.pyc differ diff --git a/generalApp/migrations/__pycache__/0007_auto_20200115_1743.cpython-36.pyc b/generalApp/migrations/__pycache__/0007_auto_20200115_1743.cpython-36.pyc new file mode 100644 index 0000000..8e642e1 Binary files /dev/null and b/generalApp/migrations/__pycache__/0007_auto_20200115_1743.cpython-36.pyc differ diff --git a/generalApp/migrations/__pycache__/__init__.cpython-36.pyc b/generalApp/migrations/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..c6d565b Binary files /dev/null and b/generalApp/migrations/__pycache__/__init__.cpython-36.pyc differ diff --git a/generalApp/models.py b/generalApp/models.py new file mode 100644 index 0000000..5543417 --- /dev/null +++ b/generalApp/models.py @@ -0,0 +1,350 @@ +from django.db import models +from django.http import HttpResponse +from django.db.models import Avg +from datetime import datetime +from .utilities import * + +class ObjectAbstract(models.Model): + + @classmethod + def addObject(self, request, parentID, privilige): + if self.modelIsUser(self) or checkSession(request, privilige): + object = jsonLoad(request) + if self.modelIsUser(self): + object['privilige'] = 1 + object['password'] = createPassHash(object['password']) + if self.checkUniqueValues(self, parentID, object): + return self.saveObject(self, parentID, object) + else: + return HttpResponse("Object Is Already Exist") + else: + return HttpResponse("No Permission") + + def modelIsUser(model): + return model == Users + + def checkUniqueValues(model, parentID, objectDict): + objectsAll = model.allObjectsDict(model) + for x in objectsAll: + if model == Users: + if x['login'].upper() == objectDict['login'].upper(): + return False + elif model == Threads: + if x['name'].upper() == objectDict['name'].upper(): + return False + elif model == Ratings: + if int(x['user_id']) == int(objectDict['user_id']) and int(x['comment_id']) == parentID: + return False + return True + + def allObjectsDict(model): + objectAll = model.objects.all() + list = [] + for x in objectAll: + list.append(x.toDict()) + return list + + def modelIsNotUser(model): + return model != Users + + def saveObject(model, parentID, objectDict): + newObject = model() + newObject.fromDict(objectDict) + if model.modelHaveParent(model): + newObject.setParentID(parentID) + if model.modelIsTrigger(model): + newObject.setActualTime() + newObject.save() + if model.modelIsSubject(model) and model.newCommentInNewSubject(objectDict): + newComment = Comments(subject = newObject) + newComment.fromDict(objectDict['comment']) + newComment.save() + return HttpResponse(f"{model.__name__}/{Comments}: Add new Objects: {newObject.toDict()} and {newComment.toDict()}") + return HttpResponse(f"{model.__name__}: Add new Object: {newObject.toDict()}") + + def modelHaveParent(model): + return model != Threads and model != Users + + def modelIsTrigger(model): + return model == Triggers + + def modelIsSubject(model): + return model == Subjects + + def newCommentInNewSubject(objectDict): + return 'comment' in objectDict + + @classmethod + def getObject(self, request, objectID, privilige): + return self.getObjectNormal(self, objectID) + + def getObjectNormal(model, objectID): + object = model.objects.get(pk = objectID).toDict() + return HttpResponse(json.dumps(object)) + + @classmethod + def getAllObjects(self, request, privilige): + objectsAll = self.allObjectsDict(self) + return HttpResponse(json.dumps(objectsAll)) + + @classmethod + def getObjectsByParentID(self, request, parentID, privilige): + if self.modelHaveParent(self): + return HttpResponse(self.getAllByParentID(parentID)) + return HttpResponse("No Permission") + + @classmethod + def putObject(self, request, objectID, privilige): + if checkSession(request, privilige): + object = jsonLoad(request) + return self.updateObject(self, request, object, objectID) + else: + return HttpResponse("No Permission") + + def updateObject(model, request, objectDict, objectID): + objectOld = model.objects.get(pk = objectID) + if model.modelIsUser(model): + if checkPassHash(objectDict['passwordOld'], objectOld.password): + if 'passwordNew' in objectDict.keys(): + objectDict['password'] = createPassHash(objectDict['passwordNew']) + else: + return HttpResponse('Bad Password') + objectOld.fromDict(objectDict) + if checkUserPermission(objectOld.toDict(), request): + objectOld.save() + return HttpResponse(f"{model.__name__}: {objectOld.toDict()} has been updated") + else: + return HttpResponse("No Permission") + + @classmethod + def deleteObject(self, request, objectID, privilige): + if checkSession(request, privilige): + objectDel = self.objects.get(pk = objectID) + if checkUserPermission(objectDel.toDict(), request): + if self.modelIsUser(self): + if checkPassHash(objectDict['password'], objectDel.password): + pass + else: + return HttpResponse("Bad Password") + objectDel.delete() + return HttpResponse(f"{self.__name__}: {objectDel} has been deleted") + else: + return HttpResponse("No Permission") + else: + return HttpResponse("No Permission") + + class Meta: + abstract = True + +class Users(ObjectAbstract): + login = models.CharField(max_length=30) + password = models.CharField(max_length=200) + email = models.EmailField(max_length=50) + avatar = models.CharField(max_length=255, default='none') + privilige = models.IntegerField(default=1) + + def __str__(self): + return f"{self.id} {self.login}" + + def fromDict(self, dict): + self.__dict__.update(dict) + + def toDict(self): + return {"id": self.id, + "login": self.login, + "avatar": self.avatar, + "email": self.email, + "privilige": self.privilige} + +class Threads(ObjectAbstract): + name = models.CharField(max_length=30) + user = models.ForeignKey(Users, on_delete = models.CASCADE) + + def __str__(self): + return self.name + + def fromDict(self, dict): + self.__dict__.update(dict) + + def toDict(self): + return {"id": self.id, + "name": self.name, + "user_id": self.user.id, + "moderator": self.user.login, + "moderator_avatar": self.user.avatar, + "moderator_privilige": self.user.privilige} + +class Subjects(ObjectAbstract): + name = models.CharField(max_length=30) + user = models.ForeignKey(Users, on_delete = models.CASCADE) + thread = models.ForeignKey(Threads, on_delete = models.CASCADE) + + def __str__(self): + return f"{self.id} {self.name}" + + def setParentID(self, parentID): + self.__dict__.update({ "thread_id": parentID }) + + @classmethod + def getAllByParentID(self, parentID): + list = [ x.toDict() for x in self.objects.filter(thread_id = parentID)] + return json.dumps(list) + + def fromDict(self, dict): + self.__dict__.update(dict) + + def toDict(self): + return {"id": self.id, + "name": self.name, + "user_id": self.user.id, + "author": self.user.login, + "author_avatar": self.user.avatar, + "author_privilige": self.user.privilige, + "thread_id": self.thread.id, + "thread_name": self.thread.name} + +class Comments(ObjectAbstract): + text = models.CharField(max_length=1000) + user = models.ForeignKey(Users, on_delete = models.CASCADE) + subject = models.ForeignKey(Subjects, on_delete = models.CASCADE) + + def __str__(self): + return f"{self.user} -> {self.subject}" + + def setParentID(self, parentID): + self.__dict__.update({ "subject_id": parentID }) + + @classmethod + def getAllByParentID(self, parentID): + list = [ x.toDict() for x in self.objects.filter(subject_id = parentID)] + return json.dumps(list) + + def commentSVG(self): + return + + def fromDict(self, dict): + self.__dict__.update(dict) + + def toDict(self): + return {"id": self.id, + "text": self.text, + "ratings_avg": Ratings.objects.filter(comment_id = self.id).aggregate(Avg('value')), + "user_id": self.user.id, + "author": self.user.login, + "author_avatar": self.user.avatar, + "author_privilige": self.user.privilige, + "subject_id": self.subject.id, + "subject_name": self.subject.name} + +class Ratings(ObjectAbstract): + value = models.IntegerField() + user = models.ForeignKey(Users, on_delete = models.CASCADE) + comment = models.ForeignKey(Comments, on_delete = models.CASCADE) + + def __str__(self): + return f"{self.user}, value: {self.value} -> comment in: {self.comment.subject}" + + def setParentID(self, parentID): + self.__dict__.update({ "comment_id": parentID }) + + @classmethod + def getAllByParentID(self, parentID): + list = [ x.toDict() for x in self.objects.filter(comment_id = parentID)] + return json.dumps(list) + + def fromDict(self, dict): + self.__dict__.update(dict) + + def toDict(self): + return {"id": self.id, + "value": self.value, + "user_id": self.user.id, + "author": self.user.login, + "author_avatar": self.user.avatar, + "comment_id": self.comment.id, + "subject": self.comment.subject.name} + +class Transactions(ObjectAbstract): + price = models.FloatField(default=255) + price_forecast = models.FloatField(default=255) + currency = models.CharField(max_length=255) + date_of_transaction = models.DateTimeField('date of transaction') + course_on_payment = models.FloatField(default=255) + user = models.ForeignKey(Users, on_delete = models.CASCADE) + + def __str__(self): + return f"{self.user.login}, cash: {self.price}, prognosis: {self.price_forecast}" + + def setParentID(self, parentID): + self.__dict__.update({ "user_id": parentID }) + + @classmethod + def getAllByParentID(self, parentID): + list = [ x.toDict() for x in self.objects.filter(user_id = parentID)] + return json.dumps(list) + + def fromDict(self, dict): + self.__dict__.update(dict) + + def toDict(self): + return {"id": self.id, + "price": self.price, + "currency": self.currency, + "date_of_transaction": self.date_of_transaction, + "course_on_payment": self.course_on_payment, + "user_id": self.user.id, + "author": self.user.login, + "exchange_id": self.exchange.id} + +class Triggers(ObjectAbstract): + course_values_for_trigger = models.FloatField(default=255) + date_of_trigger = models.CharField(max_length=255) + status = models.IntegerField(default=1) + user = models.ForeignKey(Users, on_delete = models.CASCADE) + + def __str__(self): + return f"{self.user.login}, trigger value: {self.course_values_for_trigger}, date: {self.date_of_trigger}" + + def setParentID(self, parentID): + self.__dict__.update({ "user_id": parentID }) + + @classmethod + def getAllByParentID(self, parentID): + list = [ x.toDict() for x in self.objects.filter(user_id = parentID)] + return json.dumps(list) + + def fromDict(self, dict): + self.__dict__.update(dict) + + def toDict(self): + return {"id": self.id, + "course_values_for_trigger": self.course_values_for_trigger, + "date_of_trigger": self.date_of_trigger, + "status": self.status, + "user_id": self.user.id, + "author": self.user.login,} + + def setActualTime(self): + self.date_of_trigger = str(datetime.now().strftime("%Y-%d-%m %H:%M")) + +class Notifications(ObjectAbstract): + message = models.CharField(max_length=255) + user = models.ForeignKey(Users, on_delete = models.CASCADE) + + def __str__(self): + return f"Message: {self.message}, for User: {self.user.login}" + + def setParentID(self, parentID): + self.__dict__.update({ "user_id": parentID }) + + @classmethod + def getAllByParentID(self, parentID): + return json.dumps(list(self.objects.filter(user_id = parentID).values())) + + def fromDict(self, dict): + self.__dict__.update(dict) + + def toDict(self): + return {"id": self.id, + "message": self.message, + "user_id": self.user.id} diff --git a/generalApp/tests.py b/generalApp/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/generalApp/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/generalApp/urls.py b/generalApp/urls.py new file mode 100644 index 0000000..431ecb9 --- /dev/null +++ b/generalApp/urls.py @@ -0,0 +1,30 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path('authUser', views.authUser), + path('user', views.users), + path('user/', views.user), + + path('thread', views.threads), + path('thread/', views.thread), + path('thread//subject', views.subjects), + path('subject/', views.subject), + path('subject//comment', views.comments), + path('comment/', views.comment), + path('comment//rating', views.ratings), + path('rating/', views.rating), + + path('exchange/', views.exchangeGraph), + path('exchange//prognosis/', views.exchangePrognosis), + path('user//transaction', views.transactions), + path('transaction/', views.transaction), + path('transaction/all', views.transactionsAll), + path('user//trigger', views.triggers), + path('trigger/', views.trigger), + path('trigger/all', views.triggersAll), + path('user//notification', views.notifications), + path('notification/', views.notification) + +] diff --git a/generalApp/utilities.py b/generalApp/utilities.py new file mode 100644 index 0000000..27612dd --- /dev/null +++ b/generalApp/utilities.py @@ -0,0 +1,121 @@ +from hmac import compare_digest as checkHash +from django.http import HttpResponse +from threading import Thread +import threading +import requests +import crypt +import json +import jwt + +# Session / Token Methods + +tokenKey = 'U0VDUkVUX1BBU1NfQ0hFQ0s#!@#SDS!#' +tokens = [] + +def createSession(request, userDict): + newToken = createToken(userDict) + tokens.append(newToken) + return newToken + +def createToken(userDict): + return jwt.encode( { 'payload': userDict }, tokenKey, algorithm = 'HS256' ).decode('UTF-8') + +def checkSession(request, privilige): + token = tryGetTokenFromRequest(request) + for currentToken in tokens: + if token == currentToken: + if decodeToken(currentToken)['payload']['privilige'] >= privilige: + return True + else: + return False + return False + +def tryGetTokenFromRequest(request): + try: + return jsonLoad(request)['token'] + except: + pass + +def decodeToken(token): + return jwt.decode( token, tokenKey, algorithms = ['HS256'] ) + +def checkUserPermission(modelDict, request): + + def UserIsAdmin(token): + return decodeToken(token)['payload']['privilige'] == 3 + + def UserIsModer(token): + return decodeToken(token)['payload']['privilige'] == 2 + + def checkUserChanges(modelDict, token): + return decodeToken(token)['payload']['id'] == modelDict['user_id'] + + def checkUser(modelDict, token): + return decodeToken(token)['payload']['id'] == modelDict['id'] + + def modelIsNotUser(modelDict): + return 'user_id' in modelDict + + def modelIsUser(modelDict): + return 'login' in modelDict + + def checkCheats(modelDict, token): + if 'privilige' in modelDict: + if modelDict['privilige'] != decodeToken(token)['payload']['privilige']: + return True + else: + return False + else: + return False + + token = tryGetTokenFromRequest(request) + if modelIsNotUser(modelDict): + if UserIsAdmin(token): + return True + elif UserIsModer(token): + return True + elif checkUserChanges(modelDict, token): + return True + else: + return False + elif modelIsUser(modelDict): + if UserIsAdmin(token): + return True + elif checkCheats(modelDict, token): + return False + elif checkUser(modelDict, token): + return True + else: + return False + +def deleteSession(request): + token = jsonLoad(request)['token'] + try: + tokens.remove(token) + return HttpResponse("Session Has Been Deleted") + except: + return HttpResponse("Session Delete Error") + +# Security Hash / Crypt Methods + +def createPassHash(password): + return crypt.crypt(password) + +def checkPassHash(password, hashedPass): + return checkHash(hashedPass, crypt.crypt(password, hashedPass)) + +# Thread Method + +def newThread(function): + + def decorator(*args, **kwargs): + thread = Thread(target = function, args = args, kwargs = kwargs) + thread.daemon = True + thread.start() + + return decorator + +# JSON Load Method + +def jsonLoad(self): + return json.loads(self.body.decode('utf-8')) diff --git a/generalApp/views.py b/generalApp/views.py new file mode 100644 index 0000000..539fc98 --- /dev/null +++ b/generalApp/views.py @@ -0,0 +1,179 @@ +from django.http import HttpResponse +from .methods import * + +# Threads Start + +checkTriggerNotification() + +# REST Definition + +def authUser(request): + if request.method == 'POST': + return loginUser(request) + elif request.method == 'DELETE': + return logoutUser(request) + else: + return HttpResponse('Bad Request Method') + + +def users(request): + if request.method == 'GET': + return getUsersAll(request) + elif request.method == 'POST': + return registerUser(request) + else: + return HttpResponse('Bad Request Method') + + +def user(request, id): + if request.method == 'GET': + return getUser(request, id) + elif request.method == 'PUT': + return putUser(request, id) + elif request.method == 'DELETE': + return deleteUser(request, id) + else: + return HttpResponse('Bad Request Method') + + +def threads(request): + if request.method == 'GET': + return getThreadsAll(request) + elif request.method == 'POST': + return addThread(request) + else: + return HttpResponse('Bad Request Method') + + +def thread(request, id): + if request.method == 'PUT': + return putThread(request, id) + elif request.method == 'DELETE': + return deleteThread(request, id) + else: + return HttpResponse('Bad Request Method') + + +def subjects(request, threadID): + if request.method == 'GET': + return getThreadSubjects(request, threadID) + elif request.method == 'POST': + return addSubject(request, threadID) + else: + return HttpResponse('Bad Request Method') + + +def subject(request, id): + if request.method == 'PUT': + return putSubject(request, id) + elif request.method == 'DELETE': + return deleteSubject(request, id) + else: + return HttpResponse('Bad Request Method') + + +def comments(request, subjectID): + if request.method == 'GET': + return getSubjectComments(request, subjectID) + elif request.method == 'POST': + return addComment(request, subjectID) + else: + return HttpResponse('Bad Request Method') + + +def comment(request, id): + if request.method == 'PUT': + return putComment(request, id) + elif request.method == 'DELETE': + return deleteComment(request, id) + else: + return HttpResponse('Bad Request Method') + + +def ratings(request, commentID): + if request.method == 'GET': + return getCommentRatings(request, commentID) + elif request.method == 'POST': + return addRating(request, commentID) + else: + return HttpResponse('Bad Request Method') + +def rating(request, id): + if request.method == 'PUT': + return putRating(request, id) + elif request.method == 'DELETE': + return deleteRating(request, id) + else: + return HttpResponse('Bad Request Method') + +def exchangeGraph(request, time): + if request.method == 'GET': + return getExchangeGraph(request, time) + else: + return HttpResponse('Bad Request Method') + +def exchangePrognosis(request, price, time): + if request.method == 'GET': + return Prognosis(request, time, price) + else: + return HttpResponse('Bad Request Method') + +def transactions(request, userID): + if request.method == 'GET': + return getUserTransactions(request, userID) + elif request.method == 'POST': + return addTransaction(request, userID) + else: + return HttpResponse('Bad Request Method') + +def transaction(request, id): + if request.method == 'GET': + return getTransaction(request, id) + elif request.method == 'PUT': + return putTransaction(request, id) + elif request.method == 'DELETE': + return deleteTransaction(request, id) + else: + return HttpResponse('Bad Request Method') + +def transactionsAll(request): + if request.method == 'GET': + return getTransactionsAll(request) + else: + return HttpResponse('Bad Request Method') + +def triggers(request, userID): + if request.method == 'GET': + return getUserTriggers(request, userID) + elif request.method == 'POST': + return addTrigger(request, userID) + else: + return HttpResponse('Bad Request Method') + +def trigger(request, id): + if request.method == 'GET': + return getTrigger(request, id) + if request.method == 'PUT': + return putTrigger(request, id) + elif request.method == 'DELETE': + return deleteTrigger(request, id) + else: + return HttpResponse('Bad Request Method') + +def triggersAll(request): + if request.method == 'GET': + return getTriggersAll(request) + else: + return HttpResponse('Bad Request Method') + +def notifications(request, userID): + if request.method == 'GET': + return getUserNotifications(request, userID) + else: + return HttpResponse('Bad Request Method') + +def notification(request, id): + if request.method == 'DELETE': + return deleteNotification(request, id) + else: + return HttpResponse('Bad Request Method') diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..d1264ec --- /dev/null +++ b/manage.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'TradeApp.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..51f9ce1 --- /dev/null +++ b/run.sh @@ -0,0 +1,4 @@ + +sudo docker run -p 6379:6379 -d redis:5 +python3 manage.py runserver 9090 +exit $? \ No newline at end of file diff --git a/testGraph.txt b/testGraph.txt new file mode 100644 index 0000000..d24757d --- /dev/null +++ b/testGraph.txt @@ -0,0 +1 @@ +{"candles": [{"Open": 31090.0, "Close": 31150.0, "Max": 31150.0, "Min": 31072.0, "Volume": 83.69864705, "Date": "2020-01-11 20:00:00"}, {"Open": 31135.79, "Close": 31140.0, "Max": 31150.0, "Min": 31050.0, "Volume": 98.74688844, "Date": "2020-01-11 20:30:00"}, {"Open": 31140.0, "Close": 31147.99, "Max": 31148.98, "Min": 31100.0, "Volume": 0.47959552, "Date": "2020-01-11 21:00:00"}, {"Open": 31147.99, "Close": 30997.59, "Max": 31353.0, "Min": 30767.49, "Volume": 55.58070245, "Date": "2020-01-11 21:30:00"}, {"Open": 30997.54, "Close": 30857.69, "Max": 30997.55, "Min": 30660.0, "Volume": 97.43097249, "Date": "2020-01-11 22:00:00"}, {"Open": 30852.04, "Close": 30819.5, "Max": 30922.56, "Min": 30705.01, "Volume": 2.19461501, "Date": "2020-01-11 22:30:00"}, {"Open": 30706.02, "Close": 30700.0, "Max": 30840.52, "Min": 30700.0, "Volume": 11.26341727, "Date": "2020-01-11 23:00:00"}, {"Open": 30700.0, "Close": 30653.09, "Max": 30802.47, "Min": 30570.0, "Volume": 66.47023779, "Date": "2020-01-11 23:30:00"}, {"Open": 30580.0, "Close": 30709.98, "Max": 30710.0, "Min": 30450.0, "Volume": 85.46835314, "Date": "2020-01-12 00:00:00"}, {"Open": 30693.8, "Close": 30821.37, "Max": 30830.28, "Min": 30664.56, "Volume": 0.1643201, "Date": "2020-01-12 00:30:00"}, {"Open": 30797.89, "Close": 30777.8, "Max": 30847.45, "Min": 30702.01, "Volume": 57.02058524, "Date": "2020-01-12 01:00:00"}, {"Open": 30777.08, "Close": 30786.64, "Max": 30882.45, "Min": 30776.73, "Volume": 2.75756843, "Date": "2020-01-12 01:30:00"}, {"Open": 30775.12, "Close": 30745.0, "Max": 30780.92, "Min": 30663.39, "Volume": 16.62436649, "Date": "2020-01-12 02:00:00"}, {"Open": 30745.0, "Close": 30751.62, "Max": 30929.03, "Min": 30745.0, "Volume": 31.65647486, "Date": "2020-01-12 02:30:00"}, {"Open": 30771.97, "Close": 30778.46, "Max": 30921.9, "Min": 30746.23, "Volume": 52.77955865, "Date": "2020-01-12 03:00:00"}, {"Open": 30859.04, "Close": 30746.26, "Max": 30876.24, "Min": 30746.26, "Volume": 0.01241428, "Date": "2020-01-12 03:30:00"}, {"Open": 30852.19, "Close": 30898.63, "Max": 30915.0, "Min": 30767.63, "Volume": 49.14441159, "Date": "2020-01-12 04:00:00"}, {"Open": 30914.53, "Close": 30843.56, "Max": 30914.53, "Min": 30727.48, "Volume": 0.04327263, "Date": "2020-01-12 04:30:00"}, {"Open": 30780.89, "Close": 30872.06, "Max": 30888.0, "Min": 30775.79, "Volume": 49.33392522, "Date": "2020-01-12 05:00:00"}, {"Open": 30824.93, "Close": 30888.0, "Max": 30888.0, "Min": 30723.88, "Volume": 37.22308216, "Date": "2020-01-12 05:30:00"}, {"Open": 30730.76, "Close": 31068.05, "Max": 31099.98, "Min": 30730.76, "Volume": 98.29251354, "Date": "2020-01-12 06:00:00"}, {"Open": 30981.45, "Close": 30944.84, "Max": 31019.41, "Min": 30912.25, "Volume": 16.58028944, "Date": "2020-01-12 06:30:00"}, {"Open": 30929.45, "Close": 30875.0, "Max": 30929.45, "Min": 30864.8, "Volume": 35.57468971, "Date": "2020-01-12 07:00:00"}, {"Open": 30875.0, "Close": 30926.11, "Max": 30969.97, "Min": 30784.39, "Volume": 33.59614461, "Date": "2020-01-12 07:30:00"}, {"Open": 30926.1, "Close": 31000.0, "Max": 31000.0, "Min": 30770.6, "Volume": 42.00316365, "Date": "2020-01-12 08:00:00"}, {"Open": 31000.0, "Close": 31000.0, "Max": 31000.0, "Min": 30890.26, "Volume": 36.47100562, "Date": "2020-01-12 08:30:00"}, {"Open": 31000.0, "Close": 31100.0, "Max": 31100.0, "Min": 30952.47, "Volume": 25.28336602, "Date": "2020-01-12 09:00:00"}, {"Open": 31100.0, "Close": 30989.61, "Max": 31100.0, "Min": 30989.6, "Volume": 24.66432031, "Date": "2020-01-12 09:30:00"}, {"Open": 31000.0, "Close": 30955.0, "Max": 31000.0, "Min": 30900.0, "Volume": 51.11043445, "Date": "2020-01-12 10:00:00"}, {"Open": 30955.0, "Close": 30954.0, "Max": 30955.0, "Min": 30902.0, "Volume": 4.01944326, "Date": "2020-01-12 10:30:00"}, {"Open": 30954.0, "Close": 30999.93, "Max": 31000.0, "Min": 30844.16, "Volume": 64.22182095, "Date": "2020-01-12 11:00:00"}, {"Open": 30999.93, "Close": 30974.94, "Max": 31000.0, "Min": 30870.0, "Volume": 0.30499842, "Date": "2020-01-12 11:30:00"}, {"Open": 30974.93, "Close": 31034.99, "Max": 31049.98, "Min": 30870.0, "Volume": 56.97751381, "Date": "2020-01-12 12:00:00"}, {"Open": 31035.0, "Close": 31071.33, "Max": 31095.5, "Min": 30885.13, "Volume": 9.14756352, "Date": "2020-01-12 12:30:00"}, {"Open": 31071.33, "Close": 31033.0, "Max": 31071.33, "Min": 30918.73, "Volume": 22.84543636, "Date": "2020-01-12 13:00:00"}, {"Open": 31032.99, "Close": 31090.0, "Max": 31100.0, "Min": 30911.6, "Volume": 18.52646709, "Date": "2020-01-12 13:30:00"}, {"Open": 31090.0, "Close": 31111.02, "Max": 31198.99, "Min": 30991.05, "Volume": 24.46334917, "Date": "2020-01-12 14:00:00"}, {"Open": 31111.0, "Close": 31088.07, "Max": 31111.0, "Min": 30959.39, "Volume": 34.74964329, "Date": "2020-01-12 14:30:00"}, {"Open": 31088.05, "Close": 31093.0, "Max": 31093.0, "Min": 31017.73, "Volume": 22.86843902, "Date": "2020-01-12 15:00:00"}, {"Open": 31093.0, "Close": 31092.99, "Max": 31093.0, "Min": 31031.77, "Volume": 19.85899872, "Date": "2020-01-12 15:30:00"}, {"Open": 31092.99, "Close": 31090.0, "Max": 31092.99, "Min": 30984.78, "Volume": 22.37810785, "Date": "2020-01-12 16:00:00"}, {"Open": 31079.0, "Close": 30948.58, "Max": 31079.0, "Min": 30871.03, "Volume": 18.12199469, "Date": "2020-01-12 16:30:00"}, {"Open": 30900.0, "Close": 31079.87, "Max": 31093.0, "Min": 30824.58, "Volume": 19.61834984, "Date": "2020-01-12 17:00:00"}, {"Open": 31079.67, "Close": 30967.29, "Max": 31079.67, "Min": 30809.27, "Volume": 13.48341491, "Date": "2020-01-12 17:30:00"}, {"Open": 30929.8, "Close": 30940.0, "Max": 30971.14, "Min": 30886.53, "Volume": 9.68353688, "Date": "2020-01-12 18:00:00"}, {"Open": 30940.0, "Close": 30940.0, "Max": 30940.0, "Min": 30864.35, "Volume": 10.30357678, "Date": "2020-01-12 18:30:00"}, {"Open": 30940.0, "Close": 31028.92, "Max": 31050.0, "Min": 30881.42, "Volume": 21.58807229, "Date": "2020-01-12 19:00:00"}, {"Open": 31020.0, "Close": 30968.0, "Max": 31020.0, "Min": 30894.41, "Volume": 42.54429655, "Date": "2020-01-12 19:30:00"}, {"Open": 30968.0, "Close": 31089.69, "Max": 31099.97, "Min": 30965.44, "Volume": 19.8954233, "Date": "2020-01-12 20:00:00"}, {"Open": 31089.68, "Close": 31050.0, "Max": 31098.98, "Min": 30965.44, "Volume": 11.61762663, "Date": "2020-01-12 20:30:00"}, {"Open": 31050.0, "Close": 31045.0, "Max": 31050.0, "Min": 30952.2, "Volume": 48.38108663, "Date": "2020-01-12 21:00:00"}, {"Open": 31045.0, "Close": 30967.25, "Max": 31049.0, "Min": 30921.58, "Volume": 25.07674092, "Date": "2020-01-12 21:30:00"}, {"Open": 30976.82, "Close": 30998.19, "Max": 31049.77, "Min": 30700.01, "Volume": 82.84647021, "Date": "2020-01-12 22:00:00"}, {"Open": 31046.88, "Close": 31068.98, "Max": 31075.0, "Min": 30957.21, "Volume": 3.48328786, "Date": "2020-01-12 22:30:00"}, {"Open": 31068.98, "Close": 31109.97, "Max": 31150.0, "Min": 31053.96, "Volume": 49.85778027, "Date": "2020-01-12 23:00:00"}, {"Open": 31110.0, "Close": 31200.0, "Max": 31200.0, "Min": 31043.81, "Volume": 15.20422786, "Date": "2020-01-12 23:30:00"}, {"Open": 31200.0, "Close": 31161.5, "Max": 31300.0, "Min": 31065.36, "Volume": 30.65032052, "Date": "2020-01-13 00:00:00"}, {"Open": 31082.78, "Close": 31045.97, "Max": 31214.94, "Min": 31016.72, "Volume": 28.42423382, "Date": "2020-01-13 00:30:00"}, {"Open": 31074.85, "Close": 31050.62, "Max": 31074.85, "Min": 31046.33, "Volume": 23.88616036, "Date": "2020-01-13 01:00:00"}, {"Open": 31050.8, "Close": 31074.85, "Max": 31074.85, "Min": 31049.4, "Volume": 14.37152331, "Date": "2020-01-13 01:30:00"}, {"Open": 31047.78, "Close": 31036.74, "Max": 31048.13, "Min": 31036.74, "Volume": 25.6769379, "Date": "2020-01-13 02:00:00"}, {"Open": 31024.24, "Close": 30990.62, "Max": 31043.82, "Min": 30947.69, "Volume": 6.19950688, "Date": "2020-01-13 02:30:00"}, {"Open": 30970.0, "Close": 30900.0, "Max": 30973.5, "Min": 30900.0, "Volume": 32.713999, "Date": "2020-01-13 03:00:00"}, {"Open": 30850.17, "Close": 30949.97, "Max": 30950.28, "Min": 30832.82, "Volume": 31.429126, "Date": "2020-01-13 03:30:00"}, {"Open": 30848.16, "Close": 30863.67, "Max": 30880.38, "Min": 30847.38, "Volume": 36.71182422, "Date": "2020-01-13 04:00:00"}, {"Open": 30911.28, "Close": 30938.65, "Max": 30938.85, "Min": 30819.08, "Volume": 10.22732675, "Date": "2020-01-13 04:30:00"}, {"Open": 30938.06, "Close": 30881.35, "Max": 30938.06, "Min": 30806.56, "Volume": 0.121225, "Date": "2020-01-13 05:00:00"}, {"Open": 30850.0, "Close": 30819.11, "Max": 30850.0, "Min": 30800.0, "Volume": 0.3189036, "Date": "2020-01-13 05:30:00"}, {"Open": 30819.11, "Close": 30886.8, "Max": 30920.01, "Min": 30801.79, "Volume": 26.0479585, "Date": "2020-01-13 06:00:00"}, {"Open": 30850.0, "Close": 30611.21, "Max": 30851.85, "Min": 30600.01, "Volume": 49.47062423, "Date": "2020-01-13 06:30:00"}, {"Open": 30728.48, "Close": 30711.21, "Max": 30770.19, "Min": 30647.47, "Volume": 89.82562266, "Date": "2020-01-13 07:00:00"}, {"Open": 30733.38, "Close": 30770.0, "Max": 30825.8, "Min": 30671.0, "Volume": 82.00119679, "Date": "2020-01-13 07:30:00"}, {"Open": 30805.01, "Close": 30897.11, "Max": 30897.11, "Min": 30651.23, "Volume": 16.23079517, "Date": "2020-01-13 08:00:00"}, {"Open": 30825.0, "Close": 30935.66, "Max": 30935.99, "Min": 30744.17, "Volume": 1.98609253, "Date": "2020-01-13 08:30:00"}, {"Open": 30935.99, "Close": 30899.74, "Max": 30951.78, "Min": 30779.97, "Volume": 0.23393462, "Date": "2020-01-13 09:00:00"}, {"Open": 30899.71, "Close": 30914.15, "Max": 30943.41, "Min": 30781.78, "Volume": 58.5851238, "Date": "2020-01-13 09:30:00"}, {"Open": 30943.64, "Close": 30930.0, "Max": 30943.64, "Min": 30864.24, "Volume": 2.37808346, "Date": "2020-01-13 10:00:00"}, {"Open": 30930.0, "Close": 30910.0, "Max": 30966.0, "Min": 30825.05, "Volume": 50.98999147, "Date": "2020-01-13 10:30:00"}, {"Open": 30910.0, "Close": 30940.0, "Max": 30999.99, "Min": 30882.58, "Volume": 2.13565825, "Date": "2020-01-13 11:00:00"}, {"Open": 30940.0, "Close": 30875.08, "Max": 30940.0, "Min": 30844.43, "Volume": 0.78327151, "Date": "2020-01-13 11:30:00"}, {"Open": 30853.16, "Close": 30753.51, "Max": 30853.16, "Min": 30701.69, "Volume": 2.75751171, "Date": "2020-01-13 12:00:00"}, {"Open": 30817.99, "Close": 30801.3, "Max": 30849.77, "Min": 30730.0, "Volume": 1.60986353, "Date": "2020-01-13 12:30:00"}, {"Open": 30801.3, "Close": 30883.21, "Max": 30890.99, "Min": 30800.0, "Volume": 34.79195197, "Date": "2020-01-13 13:00:00"}, {"Open": 30883.21, "Close": 30847.82, "Max": 30886.94, "Min": 30749.9, "Volume": 36.51017572, "Date": "2020-01-13 13:30:00"}, {"Open": 30847.82, "Close": 30818.06, "Max": 30877.18, "Min": 30767.54, "Volume": 17.35219138, "Date": "2020-01-13 14:00:00"}, {"Open": 30818.06, "Close": 30874.99, "Max": 30891.42, "Min": 30750.0, "Volume": 23.1864501, "Date": "2020-01-13 14:30:00"}, {"Open": 30863.78, "Close": 30828.94, "Max": 30900.0, "Min": 30755.8, "Volume": 46.6067205, "Date": "2020-01-13 15:00:00"}, {"Open": 30828.81, "Close": 30738.83, "Max": 30900.0, "Min": 30683.93, "Volume": 50.80004987, "Date": "2020-01-13 15:30:00"}, {"Open": 30738.83, "Close": 30798.84, "Max": 30871.76, "Min": 30699.44, "Volume": 50.11406126, "Date": "2020-01-13 16:00:00"}, {"Open": 30789.43, "Close": 30825.78, "Max": 30825.78, "Min": 30739.03, "Volume": 5.73508711, "Date": "2020-01-13 16:30:00"}, {"Open": 30825.78, "Close": 30870.5, "Max": 30937.91, "Min": 30778.79, "Volume": 52.72385607, "Date": "2020-01-13 17:00:00"}, {"Open": 30850.15, "Close": 30805.74, "Max": 30934.63, "Min": 30740.3, "Volume": 8.39852519, "Date": "2020-01-13 17:30:00"}, {"Open": 30808.01, "Close": 30802.27, "Max": 30873.49, "Min": 30740.33, "Volume": 54.14579375, "Date": "2020-01-13 18:00:00"}, {"Open": 30802.27, "Close": 30830.58, "Max": 30830.58, "Min": 30750.51, "Volume": 3.65744069, "Date": "2020-01-13 18:30:00"}, {"Open": 30830.58, "Close": 30866.18, "Max": 30866.68, "Min": 30750.1, "Volume": 50.81925828, "Date": "2020-01-13 19:00:00"}, {"Open": 30866.18, "Close": 30862.95, "Max": 30896.81, "Min": 30830.03, "Volume": 3.05041528, "Date": "2020-01-13 19:30:00"}, {"Open": 30862.93, "Close": 30910.99, "Max": 30938.17, "Min": 30801.0, "Volume": 45.74285358, "Date": "2020-01-13 20:00:00"}, {"Open": 30910.99, "Close": 30995.55, "Max": 31000.0, "Min": 30832.36, "Volume": 3.06251588, "Date": "2020-01-13 20:30:00"}, {"Open": 30966.37, "Close": 30964.97, "Max": 31000.0, "Min": 30950.0, "Volume": 29.77511646, "Date": "2020-01-13 21:00:00"}, {"Open": 30964.97, "Close": 30982.03, "Max": 31073.57, "Min": 30917.15, "Volume": 25.92082335, "Date": "2020-01-13 21:30:00"}, {"Open": 30992.48, "Close": 30910.9, "Max": 30994.38, "Min": 30905.78, "Volume": 31.15721052, "Date": "2020-01-13 22:00:00"}, {"Open": 30914.71, "Close": 30906.25, "Max": 30954.52, "Min": 30905.77, "Volume": 35.54177048, "Date": "2020-01-13 22:30:00"}, {"Open": 30906.25, "Close": 30953.43, "Max": 30973.43, "Min": 30895.0, "Volume": 34.79974459, "Date": "2020-01-13 23:00:00"}, {"Open": 30898.98, "Close": 30851.02, "Max": 30958.19, "Min": 30800.01, "Volume": 71.58615858, "Date": "2020-01-13 23:30:00"}, {"Open": 30861.53, "Close": 31000.29, "Max": 31000.29, "Min": 30861.53, "Volume": 49.29568211, "Date": "2020-01-14 00:00:00"}, {"Open": 31011.9, "Close": 31368.76, "Max": 31524.5, "Min": 31011.9, "Volume": 132.39281646, "Date": "2020-01-14 00:30:00"}, {"Open": 31429.1, "Close": 31721.26, "Max": 31879.99, "Min": 31429.1, "Volume": 134.0003886, "Date": "2020-01-14 01:00:00"}, {"Open": 31789.47, "Close": 32079.21, "Max": 32100.0, "Min": 31694.44, "Volume": 146.58678812, "Date": "2020-01-14 01:30:00"}, {"Open": 32072.76, "Close": 31751.07, "Max": 32072.76, "Min": 31705.39, "Volume": 116.26064932, "Date": "2020-01-14 02:00:00"}, {"Open": 31887.99, "Close": 32029.91, "Max": 32029.91, "Min": 31821.32, "Volume": 37.02260688, "Date": "2020-01-14 02:30:00"}, {"Open": 31980.28, "Close": 31713.35, "Max": 31994.67, "Min": 31710.87, "Volume": 41.10052952, "Date": "2020-01-14 03:00:00"}, {"Open": 31794.46, "Close": 32004.0, "Max": 32004.0, "Min": 31794.46, "Volume": 29.26621358, "Date": "2020-01-14 03:30:00"}, {"Open": 32004.0, "Close": 32335.0, "Max": 32350.0, "Min": 32004.0, "Volume": 153.67212001, "Date": "2020-01-14 04:00:00"}, {"Open": 32332.54, "Close": 32330.0, "Max": 32500.0, "Min": 32330.0, "Volume": 64.77235855, "Date": "2020-01-14 04:30:00"}, {"Open": 32400.02, "Close": 32398.99, "Max": 32498.5, "Min": 32227.35, "Volume": 10.31902208, "Date": "2020-01-14 05:00:00"}, {"Open": 32399.0, "Close": 32198.0, "Max": 32438.54, "Min": 32000.1, "Volume": 8.0216314, "Date": "2020-01-14 05:30:00"}, {"Open": 32140.95, "Close": 32210.01, "Max": 32321.5, "Min": 32140.95, "Volume": 77.72804472, "Date": "2020-01-14 06:00:00"}, {"Open": 32235.32, "Close": 32220.0, "Max": 32350.21, "Min": 32170.03, "Volume": 51.753819, "Date": "2020-01-14 06:30:00"}, {"Open": 32280.99, "Close": 32364.67, "Max": 32400.0, "Min": 32170.08, "Volume": 66.56717874, "Date": "2020-01-14 07:00:00"}, {"Open": 32171.94, "Close": 32383.65, "Max": 32400.0, "Min": 32171.94, "Volume": 29.385695, "Date": "2020-01-14 07:30:00"}, {"Open": 32175.0, "Close": 32344.0, "Max": 32372.0, "Min": 32174.97, "Volume": 31.56207259, "Date": "2020-01-14 08:00:00"}, {"Open": 32342.99, "Close": 32291.99, "Max": 32342.99, "Min": 32224.23, "Volume": 58.92939208, "Date": "2020-01-14 08:30:00"}, {"Open": 32291.99, "Close": 32295.0, "Max": 32297.99, "Min": 32173.16, "Volume": 45.66876063, "Date": "2020-01-14 09:00:00"}, {"Open": 32295.0, "Close": 32195.47, "Max": 32297.99, "Min": 32170.06, "Volume": 38.38855525, "Date": "2020-01-14 09:30:00"}, {"Open": 32248.99, "Close": 32104.98, "Max": 32295.44, "Min": 32100.0, "Volume": 36.20415046, "Date": "2020-01-14 10:00:00"}, {"Open": 32104.98, "Close": 32284.7, "Max": 32284.99, "Min": 31900.03, "Volume": 8.22061298, "Date": "2020-01-14 10:30:00"}, {"Open": 32284.69, "Close": 31820.42, "Max": 32285.0, "Min": 31800.04, "Volume": 87.57580561, "Date": "2020-01-14 11:00:00"}, {"Open": 32136.3, "Close": 32100.01, "Max": 32136.3, "Min": 31820.74, "Volume": 13.99541909, "Date": "2020-01-14 11:30:00"}, {"Open": 32100.01, "Close": 32248.0, "Max": 32269.98, "Min": 32100.0, "Volume": 127.86384558, "Date": "2020-01-14 12:00:00"}, {"Open": 32248.0, "Close": 32190.39, "Max": 32498.99, "Min": 32100.0, "Volume": 32.7296336, "Date": "2020-01-14 12:30:00"}, {"Open": 32190.37, "Close": 32398.89, "Max": 32398.89, "Min": 32150.01, "Volume": 86.50232537, "Date": "2020-01-14 13:00:00"}, {"Open": 32398.99, "Close": 32495.38, "Max": 32497.11, "Min": 32283.0, "Volume": 10.48228624, "Date": "2020-01-14 13:30:00"}, {"Open": 32495.38, "Close": 32999.97, "Max": 33000.0, "Min": 32288.51, "Volume": 117.42275644, "Date": "2020-01-14 14:00:00"}, {"Open": 32999.96, "Close": 32900.01, "Max": 33199.98, "Min": 32699.04, "Volume": 100.97595796, "Date": "2020-01-14 14:30:00"}, {"Open": 32919.68, "Close": 33000.0, "Max": 33000.0, "Min": 32703.69, "Volume": 159.76691621, "Date": "2020-01-14 15:00:00"}, {"Open": 33000.0, "Close": 33046.18, "Max": 33079.97, "Min": 32852.9, "Volume": 118.72440166, "Date": "2020-01-14 15:30:00"}, {"Open": 32996.58, "Close": 32525.83, "Max": 33035.62, "Min": 32300.53, "Volume": 18.3070231, "Date": "2020-01-14 16:00:00"}, {"Open": 32524.99, "Close": 32673.77, "Max": 32939.38, "Min": 32360.0, "Volume": 93.17250567, "Date": "2020-01-14 16:30:00"}, {"Open": 32673.77, "Close": 33023.65, "Max": 33142.44, "Min": 32673.77, "Volume": 123.82239412, "Date": "2020-01-14 17:00:00"}, {"Open": 33041.99, "Close": 33249.98, "Max": 33398.99, "Min": 32850.02, "Volume": 50.71209703, "Date": "2020-01-14 17:30:00"}, {"Open": 33249.98, "Close": 33447.21, "Max": 33447.26, "Min": 33102.03, "Volume": 181.67455038, "Date": "2020-01-14 18:00:00"}, {"Open": 33438.31, "Close": 33212.49, "Max": 33450.0, "Min": 32900.01, "Volume": 5.34780429, "Date": "2020-01-14 18:30:00"}, {"Open": 33212.49, "Close": 33084.99, "Max": 33266.92, "Min": 32900.01, "Volume": 133.66388363, "Date": "2020-01-14 19:00:00"}, {"Open": 33069.99, "Close": 32921.05, "Max": 33090.0, "Min": 32900.03, "Volume": 68.13319656, "Date": "2020-01-14 19:30:00"}, {"Open": 33088.99, "Close": 33032.89, "Max": 33089.0, "Min": 32921.12, "Volume": 126.63226688, "Date": "2020-01-14 20:00:00"}, {"Open": 33032.89, "Close": 32947.95, "Max": 33060.0, "Min": 32700.12, "Volume": 14.21573713, "Date": "2020-01-14 20:30:00"}, {"Open": 32944.98, "Close": 32896.03, "Max": 32944.98, "Min": 32717.11, "Volume": 67.35503797, "Date": "2020-01-14 21:00:00"}, {"Open": 32896.03, "Close": 32940.58, "Max": 33050.0, "Min": 32896.02, "Volume": 46.68565275, "Date": "2020-01-14 21:30:00"}, {"Open": 32940.58, "Close": 33059.99, "Max": 33060.0, "Min": 32899.98, "Volume": 60.64368603, "Date": "2020-01-14 22:00:00"}, {"Open": 33058.99, "Close": 33059.98, "Max": 33060.0, "Min": 32998.99, "Volume": 13.78857534, "Date": "2020-01-14 22:30:00"}, {"Open": 33059.98, "Close": 33100.0, "Max": 33100.0, "Min": 32998.99, "Volume": 44.6759928, "Date": "2020-01-14 23:00:00"}, {"Open": 33128.2, "Close": 33304.52, "Max": 33520.0, "Min": 33030.02, "Volume": 226.59739768, "Date": "2020-01-14 23:30:00"}, {"Open": 33227.31, "Close": 33369.05, "Max": 33480.0, "Min": 32900.0, "Volume": 126.09650636, "Date": "2020-01-15 00:00:00"}, {"Open": 33479.99, "Close": 33106.76, "Max": 33479.99, "Min": 32900.01, "Volume": 150.26999975, "Date": "2020-01-15 00:30:00"}, {"Open": 33106.75, "Close": 32856.98, "Max": 33247.86, "Min": 32800.0, "Volume": 179.21401128, "Date": "2020-01-15 01:00:00"}, {"Open": 32868.36, "Close": 33081.61, "Max": 33157.33, "Min": 32800.06, "Volume": 124.37461203, "Date": "2020-01-15 01:30:00"}, {"Open": 33083.85, "Close": 33264.74, "Max": 33296.94, "Min": 32950.03, "Volume": 106.31521555, "Date": "2020-01-15 02:00:00"}, {"Open": 33264.2, "Close": 33239.39, "Max": 33264.2, "Min": 32960.0, "Volume": 47.61557527, "Date": "2020-01-15 02:30:00"}, {"Open": 33236.75, "Close": 33198.68, "Max": 33236.86, "Min": 33027.6, "Volume": 107.83399395, "Date": "2020-01-15 03:00:00"}, {"Open": 33164.63, "Close": 32999.09, "Max": 33198.06, "Min": 32810.01, "Volume": 45.05741621, "Date": "2020-01-15 03:30:00"}, {"Open": 32998.75, "Close": 32483.0, "Max": 32998.75, "Min": 32483.0, "Volume": 26.28861685, "Date": "2020-01-15 04:00:00"}, {"Open": 32480.01, "Close": 32599.74, "Max": 32715.25, "Min": 32450.0, "Volume": 123.74892433, "Date": "2020-01-15 04:30:00"}, {"Open": 32500.11, "Close": 32549.99, "Max": 32675.42, "Min": 32399.98, "Volume": 2.31334318, "Date": "2020-01-15 05:00:00"}, {"Open": 32400.01, "Close": 32270.0, "Max": 32550.0, "Min": 32267.02, "Volume": 14.76365909, "Date": "2020-01-15 05:30:00"}, {"Open": 32546.01, "Close": 32445.0, "Max": 32546.01, "Min": 32270.0, "Volume": 0.7646701, "Date": "2020-01-15 06:00:00"}, {"Open": 32445.0, "Close": 32620.01, "Max": 32621.89, "Min": 32310.01, "Volume": 7.48756148, "Date": "2020-01-15 06:30:00"}, {"Open": 32620.01, "Close": 32610.01, "Max": 32621.89, "Min": 32604.99, "Volume": 7.24294268, "Date": "2020-01-15 07:00:00"}, {"Open": 32610.01, "Close": 32449.99, "Max": 32610.01, "Min": 32348.0, "Volume": 74.58734283, "Date": "2020-01-15 07:30:00"}, {"Open": 32449.98, "Close": 32574.98, "Max": 32574.99, "Min": 32300.01, "Volume": 15.38745243, "Date": "2020-01-15 08:00:00"}, {"Open": 32574.98, "Close": 32562.95, "Max": 32574.99, "Min": 32400.01, "Volume": 51.72283559, "Date": "2020-01-15 08:30:00"}, {"Open": 32562.99, "Close": 32596.65, "Max": 32604.38, "Min": 32375.0, "Volume": 38.4271044, "Date": "2020-01-15 09:00:00"}, {"Open": 32596.62, "Close": 32999.7, "Max": 32999.88, "Min": 32375.01, "Volume": 6.27617116, "Date": "2020-01-15 09:30:00"}, {"Open": 32999.7, "Close": 32996.99, "Max": 33049.99, "Min": 32705.05, "Volume": 25.35192695, "Date": "2020-01-15 10:00:00"}, {"Open": 32996.99, "Close": 33187.97, "Max": 33187.99, "Min": 32900.0, "Volume": 44.95076789, "Date": "2020-01-15 10:30:00"}, {"Open": 33187.97, "Close": 33043.98, "Max": 33187.97, "Min": 32979.5, "Volume": 1.73455037, "Date": "2020-01-15 11:00:00"}, {"Open": 33043.98, "Close": 32998.07, "Max": 33043.98, "Min": 32900.0, "Volume": 7.79481189, "Date": "2020-01-15 11:30:00"}, {"Open": 32998.07, "Close": 32990.0, "Max": 32999.0, "Min": 32800.01, "Volume": 13.3931099, "Date": "2020-01-15 12:00:00"}, {"Open": 32990.0, "Close": 32966.52, "Max": 33000.0, "Min": 32862.66, "Volume": 154.10978063, "Date": "2020-01-15 12:30:00"}, {"Open": 32873.81, "Close": 33100.02, "Max": 33197.0, "Min": 32873.8, "Volume": 10.35398222, "Date": "2020-01-15 13:00:00"}, {"Open": 33100.02, "Close": 33399.99, "Max": 33399.99, "Min": 33100.0, "Volume": 23.40399744, "Date": "2020-01-15 13:30:00"}, {"Open": 33399.99, "Close": 33299.99, "Max": 33489.99, "Min": 33102.01, "Volume": 9.06890721, "Date": "2020-01-15 14:00:00"}, {"Open": 33299.99, "Close": 33399.81, "Max": 33489.97, "Min": 33143.93, "Volume": 94.30600321, "Date": "2020-01-15 14:30:00"}, {"Open": 33399.7, "Close": 33207.79, "Max": 33399.73, "Min": 32916.65, "Volume": 51.39760061, "Date": "2020-01-15 15:00:00"}, {"Open": 33207.78, "Close": 33083.95, "Max": 33207.84, "Min": 33001.0, "Volume": 61.55392306, "Date": "2020-01-15 15:30:00"}, {"Open": 33083.95, "Close": 33184.99, "Max": 33199.69, "Min": 32873.8, "Volume": 98.51673779, "Date": "2020-01-15 16:00:00"}, {"Open": 33184.99, "Close": 33138.02, "Max": 33224.68, "Min": 33007.66, "Volume": 1.80367129, "Date": "2020-01-15 16:30:00"}, {"Open": 33180.99, "Close": 32865.21, "Max": 33202.26, "Min": 32498.94, "Volume": 113.00296892, "Date": "2020-01-15 17:00:00"}, {"Open": 32938.4, "Close": 32989.98, "Max": 33118.35, "Min": 32666.03, "Volume": 2.7169386, "Date": "2020-01-15 17:30:00"}, {"Open": 32989.99, "Close": 32752.27, "Max": 32989.99, "Min": 32600.0, "Volume": 18.48685506, "Date": "2020-01-15 18:00:00"}, {"Open": 32754.53, "Close": 32800.0, "Max": 32989.99, "Min": 32700.0, "Volume": 46.87518283, "Date": "2020-01-15 18:30:00"}, {"Open": 32844.98, "Close": 32969.69, "Max": 32989.99, "Min": 32844.96, "Volume": 58.73300902, "Date": "2020-01-15 19:00:00"}, {"Open": 32969.68, "Close": 32900.0, "Max": 32969.69, "Min": 32860.0, "Volume": 1.13034249, "Date": "2020-01-15 19:30:00"}, {"Open": 32900.0, "Close": 32899.95, "Max": 32900.0, "Min": 32859.84, "Volume": 54.38986651, "Date": "2020-01-15 20:00:00"}, {"Open": 32899.95, "Close": 33129.65, "Max": 33149.98, "Min": 32859.84, "Volume": 2.43989605, "Date": "2020-01-15 20:30:00"}, {"Open": 33129.44, "Close": 33199.77, "Max": 33200.0, "Min": 32920.1, "Volume": 36.5356959, "Date": "2020-01-15 21:00:00"}, {"Open": 33199.77, "Close": 33189.03, "Max": 33199.79, "Min": 32920.06, "Volume": 54.57189256, "Date": "2020-01-15 21:30:00"}, {"Open": 33189.02, "Close": 33096.82, "Max": 33194.99, "Min": 32920.25, "Volume": 36.91199134, "Date": "2020-01-15 22:00:00"}, {"Open": 33096.82, "Close": 33200.0, "Max": 33200.0, "Min": 33000.04, "Volume": 12.9130503, "Date": "2020-01-15 22:30:00"}, {"Open": 33200.0, "Close": 33100.0, "Max": 33248.97, "Min": 33011.5, "Volume": 42.1483899, "Date": "2020-01-15 23:00:00"}, {"Open": 33100.0, "Close": 33145.09, "Max": 33199.97, "Min": 33081.06, "Volume": 6.58223194, "Date": "2020-01-15 23:30:00"}], "candlesCount": 199, "graphMin": 30450.0, "graphMax": 33520.0} diff --git a/testGraph2.txt b/testGraph2.txt new file mode 100644 index 0000000..3977ef2 --- /dev/null +++ b/testGraph2.txt @@ -0,0 +1 @@ +{"candles": [{"Open": 30818.06, "Close": 30874.99, "Max": 30891.42, "Min": 30750.0, "Volume": 23.1864501, "Date": "2020-01-13 14:30:00"}, {"Open": 30863.78, "Close": 30828.94, "Max": 30900.0, "Min": 30755.8, "Volume": 46.6067205, "Date": "2020-01-13 15:00:00"}, {"Open": 30828.81, "Close": 30738.83, "Max": 30900.0, "Min": 30683.93, "Volume": 50.80004987, "Date": "2020-01-13 15:30:00"}, {"Open": 30738.83, "Close": 30798.84, "Max": 30871.76, "Min": 30699.44, "Volume": 50.11406126, "Date": "2020-01-13 16:00:00"}, {"Open": 30789.43, "Close": 30825.78, "Max": 30825.78, "Min": 30739.03, "Volume": 5.73508711, "Date": "2020-01-13 16:30:00"}, {"Open": 30825.78, "Close": 30870.5, "Max": 30937.91, "Min": 30778.79, "Volume": 52.72385607, "Date": "2020-01-13 17:00:00"}, {"Open": 30850.15, "Close": 30805.74, "Max": 30934.63, "Min": 30740.3, "Volume": 8.39852519, "Date": "2020-01-13 17:30:00"}, {"Open": 30808.01, "Close": 30802.27, "Max": 30873.49, "Min": 30740.33, "Volume": 54.14579375, "Date": "2020-01-13 18:00:00"}, {"Open": 30802.27, "Close": 30830.58, "Max": 30830.58, "Min": 30750.51, "Volume": 3.65744069, "Date": "2020-01-13 18:30:00"}, {"Open": 30830.58, "Close": 30866.18, "Max": 30866.68, "Min": 30750.1, "Volume": 50.81925828, "Date": "2020-01-13 19:00:00"}, {"Open": 30866.18, "Close": 30862.95, "Max": 30896.81, "Min": 30830.03, "Volume": 3.05041528, "Date": "2020-01-13 19:30:00"}, {"Open": 30862.93, "Close": 30910.99, "Max": 30938.17, "Min": 30801.0, "Volume": 45.74285358, "Date": "2020-01-13 20:00:00"}, {"Open": 30910.99, "Close": 30995.55, "Max": 31000.0, "Min": 30832.36, "Volume": 3.06251588, "Date": "2020-01-13 20:30:00"}, {"Open": 30966.37, "Close": 30964.97, "Max": 31000.0, "Min": 30950.0, "Volume": 29.77511646, "Date": "2020-01-13 21:00:00"}, {"Open": 30964.97, "Close": 30982.03, "Max": 31073.57, "Min": 30917.15, "Volume": 25.92082335, "Date": "2020-01-13 21:30:00"}, {"Open": 30992.48, "Close": 30910.9, "Max": 30994.38, "Min": 30905.78, "Volume": 31.15721052, "Date": "2020-01-13 22:00:00"}, {"Open": 30914.71, "Close": 30906.25, "Max": 30954.52, "Min": 30905.77, "Volume": 35.54177048, "Date": "2020-01-13 22:30:00"}, {"Open": 30906.25, "Close": 30953.43, "Max": 30973.43, "Min": 30895.0, "Volume": 34.79974459, "Date": "2020-01-13 23:00:00"}, {"Open": 30898.98, "Close": 30851.02, "Max": 30958.19, "Min": 30800.01, "Volume": 71.58615858, "Date": "2020-01-13 23:30:00"}, {"Open": 30861.53, "Close": 31000.29, "Max": 31000.29, "Min": 30861.53, "Volume": 49.29568211, "Date": "2020-01-14 00:00:00"}, {"Open": 31011.9, "Close": 31368.76, "Max": 31524.5, "Min": 31011.9, "Volume": 132.39281646, "Date": "2020-01-14 00:30:00"}, {"Open": 31429.1, "Close": 31721.26, "Max": 31879.99, "Min": 31429.1, "Volume": 134.0003886, "Date": "2020-01-14 01:00:00"}, {"Open": 31789.47, "Close": 32079.21, "Max": 32100.0, "Min": 31694.44, "Volume": 146.58678812, "Date": "2020-01-14 01:30:00"}, {"Open": 32072.76, "Close": 31751.07, "Max": 32072.76, "Min": 31705.39, "Volume": 116.26064932, "Date": "2020-01-14 02:00:00"}, {"Open": 31887.99, "Close": 32029.91, "Max": 32029.91, "Min": 31821.32, "Volume": 37.02260688, "Date": "2020-01-14 02:30:00"}, {"Open": 31980.28, "Close": 31713.35, "Max": 31994.67, "Min": 31710.87, "Volume": 41.10052952, "Date": "2020-01-14 03:00:00"}, {"Open": 31794.46, "Close": 32004.0, "Max": 32004.0, "Min": 31794.46, "Volume": 29.26621358, "Date": "2020-01-14 03:30:00"}, {"Open": 32004.0, "Close": 32335.0, "Max": 32350.0, "Min": 32004.0, "Volume": 153.67212001, "Date": "2020-01-14 04:00:00"}, {"Open": 32332.54, "Close": 32330.0, "Max": 32500.0, "Min": 32330.0, "Volume": 64.77235855, "Date": "2020-01-14 04:30:00"}, {"Open": 32400.02, "Close": 32398.99, "Max": 32498.5, "Min": 32227.35, "Volume": 10.31902208, "Date": "2020-01-14 05:00:00"}, {"Open": 32399.0, "Close": 32198.0, "Max": 32438.54, "Min": 32000.1, "Volume": 8.0216314, "Date": "2020-01-14 05:30:00"}, {"Open": 32140.95, "Close": 32210.01, "Max": 32321.5, "Min": 32140.95, "Volume": 77.72804472, "Date": "2020-01-14 06:00:00"}, {"Open": 32235.32, "Close": 32220.0, "Max": 32350.21, "Min": 32170.03, "Volume": 51.753819, "Date": "2020-01-14 06:30:00"}, {"Open": 32280.99, "Close": 32364.67, "Max": 32400.0, "Min": 32170.08, "Volume": 66.56717874, "Date": "2020-01-14 07:00:00"}, {"Open": 32171.94, "Close": 32383.65, "Max": 32400.0, "Min": 32171.94, "Volume": 29.385695, "Date": "2020-01-14 07:30:00"}, {"Open": 32175.0, "Close": 32344.0, "Max": 32372.0, "Min": 32174.97, "Volume": 31.56207259, "Date": "2020-01-14 08:00:00"}, {"Open": 32342.99, "Close": 32291.99, "Max": 32342.99, "Min": 32224.23, "Volume": 58.92939208, "Date": "2020-01-14 08:30:00"}, {"Open": 32291.99, "Close": 32295.0, "Max": 32297.99, "Min": 32173.16, "Volume": 45.66876063, "Date": "2020-01-14 09:00:00"}, {"Open": 32295.0, "Close": 32195.47, "Max": 32297.99, "Min": 32170.06, "Volume": 38.38855525, "Date": "2020-01-14 09:30:00"}, {"Open": 32248.99, "Close": 32104.98, "Max": 32295.44, "Min": 32100.0, "Volume": 36.20415046, "Date": "2020-01-14 10:00:00"}, {"Open": 32104.98, "Close": 32284.7, "Max": 32284.99, "Min": 31900.03, "Volume": 8.22061298, "Date": "2020-01-14 10:30:00"}, {"Open": 32284.69, "Close": 31820.42, "Max": 32285.0, "Min": 31800.04, "Volume": 87.57580561, "Date": "2020-01-14 11:00:00"}, {"Open": 32136.3, "Close": 32100.01, "Max": 32136.3, "Min": 31820.74, "Volume": 13.99541909, "Date": "2020-01-14 11:30:00"}, {"Open": 32100.01, "Close": 32248.0, "Max": 32269.98, "Min": 32100.0, "Volume": 127.86384558, "Date": "2020-01-14 12:00:00"}, {"Open": 32248.0, "Close": 32190.39, "Max": 32498.99, "Min": 32100.0, "Volume": 32.7296336, "Date": "2020-01-14 12:30:00"}, {"Open": 32190.37, "Close": 32398.89, "Max": 32398.89, "Min": 32150.01, "Volume": 86.50232537, "Date": "2020-01-14 13:00:00"}, {"Open": 32398.99, "Close": 32495.38, "Max": 32497.11, "Min": 32283.0, "Volume": 10.48228624, "Date": "2020-01-14 13:30:00"}, {"Open": 32495.38, "Close": 32999.97, "Max": 33000.0, "Min": 32288.51, "Volume": 117.42275644, "Date": "2020-01-14 14:00:00"}, {"Open": 32999.96, "Close": 32900.01, "Max": 33199.98, "Min": 32699.04, "Volume": 100.97595796, "Date": "2020-01-14 14:30:00"}, {"Open": 32919.68, "Close": 33000.0, "Max": 33000.0, "Min": 32703.69, "Volume": 159.76691621, "Date": "2020-01-14 15:00:00"}, {"Open": 33000.0, "Close": 33046.18, "Max": 33079.97, "Min": 32852.9, "Volume": 118.72440166, "Date": "2020-01-14 15:30:00"}, {"Open": 32996.58, "Close": 32525.83, "Max": 33035.62, "Min": 32300.53, "Volume": 18.3070231, "Date": "2020-01-14 16:00:00"}, {"Open": 32524.99, "Close": 32673.77, "Max": 32939.38, "Min": 32360.0, "Volume": 93.17250567, "Date": "2020-01-14 16:30:00"}, {"Open": 32673.77, "Close": 33023.65, "Max": 33142.44, "Min": 32673.77, "Volume": 123.82239412, "Date": "2020-01-14 17:00:00"}, {"Open": 33041.99, "Close": 33249.98, "Max": 33398.99, "Min": 32850.02, "Volume": 50.71209703, "Date": "2020-01-14 17:30:00"}, {"Open": 33249.98, "Close": 33447.21, "Max": 33447.26, "Min": 33102.03, "Volume": 181.67455038, "Date": "2020-01-14 18:00:00"}, {"Open": 33438.31, "Close": 33212.49, "Max": 33450.0, "Min": 32900.01, "Volume": 5.34780429, "Date": "2020-01-14 18:30:00"}, {"Open": 33212.49, "Close": 33084.99, "Max": 33266.92, "Min": 32900.01, "Volume": 133.66388363, "Date": "2020-01-14 19:00:00"}, {"Open": 33069.99, "Close": 32921.05, "Max": 33090.0, "Min": 32900.03, "Volume": 68.13319656, "Date": "2020-01-14 19:30:00"}, {"Open": 33088.99, "Close": 33032.89, "Max": 33089.0, "Min": 32921.12, "Volume": 126.63226688, "Date": "2020-01-14 20:00:00"}, {"Open": 33032.89, "Close": 32947.95, "Max": 33060.0, "Min": 32700.12, "Volume": 14.21573713, "Date": "2020-01-14 20:30:00"}, {"Open": 32944.98, "Close": 32896.03, "Max": 32944.98, "Min": 32717.11, "Volume": 67.35503797, "Date": "2020-01-14 21:00:00"}, {"Open": 32896.03, "Close": 32940.58, "Max": 33050.0, "Min": 32896.02, "Volume": 46.68565275, "Date": "2020-01-14 21:30:00"}, {"Open": 32940.58, "Close": 33059.99, "Max": 33060.0, "Min": 32899.98, "Volume": 60.64368603, "Date": "2020-01-14 22:00:00"}, {"Open": 33058.99, "Close": 33059.98, "Max": 33060.0, "Min": 32998.99, "Volume": 13.78857534, "Date": "2020-01-14 22:30:00"}, {"Open": 33059.98, "Close": 33100.0, "Max": 33100.0, "Min": 32998.99, "Volume": 44.6759928, "Date": "2020-01-14 23:00:00"}, {"Open": 33128.2, "Close": 33304.52, "Max": 33520.0, "Min": 33030.02, "Volume": 226.59739768, "Date": "2020-01-14 23:30:00"}, {"Open": 33227.31, "Close": 33369.05, "Max": 33480.0, "Min": 32900.0, "Volume": 126.09650636, "Date": "2020-01-15 00:00:00"}, {"Open": 33479.99, "Close": 33106.76, "Max": 33479.99, "Min": 32900.01, "Volume": 150.26999975, "Date": "2020-01-15 00:30:00"}, {"Open": 33106.75, "Close": 32856.98, "Max": 33247.86, "Min": 32800.0, "Volume": 179.21401128, "Date": "2020-01-15 01:00:00"}, {"Open": 32868.36, "Close": 33081.61, "Max": 33157.33, "Min": 32800.06, "Volume": 124.37461203, "Date": "2020-01-15 01:30:00"}, {"Open": 33083.85, "Close": 33264.74, "Max": 33296.94, "Min": 32950.03, "Volume": 106.31521555, "Date": "2020-01-15 02:00:00"}, {"Open": 33264.2, "Close": 33239.39, "Max": 33264.2, "Min": 32960.0, "Volume": 47.61557527, "Date": "2020-01-15 02:30:00"}, {"Open": 33236.75, "Close": 33198.68, "Max": 33236.86, "Min": 33027.6, "Volume": 107.83399395, "Date": "2020-01-15 03:00:00"}, {"Open": 33164.63, "Close": 32999.09, "Max": 33198.06, "Min": 32810.01, "Volume": 45.05741621, "Date": "2020-01-15 03:30:00"}, {"Open": 32998.75, "Close": 32483.0, "Max": 32998.75, "Min": 32483.0, "Volume": 26.28861685, "Date": "2020-01-15 04:00:00"}, {"Open": 32480.01, "Close": 32599.74, "Max": 32715.25, "Min": 32450.0, "Volume": 123.74892433, "Date": "2020-01-15 04:30:00"}, {"Open": 32500.11, "Close": 32549.99, "Max": 32675.42, "Min": 32399.98, "Volume": 2.31334318, "Date": "2020-01-15 05:00:00"}, {"Open": 32400.01, "Close": 32270.0, "Max": 32550.0, "Min": 32267.02, "Volume": 14.76365909, "Date": "2020-01-15 05:30:00"}, {"Open": 32546.01, "Close": 32445.0, "Max": 32546.01, "Min": 32270.0, "Volume": 0.7646701, "Date": "2020-01-15 06:00:00"}, {"Open": 32445.0, "Close": 32620.01, "Max": 32621.89, "Min": 32310.01, "Volume": 7.48756148, "Date": "2020-01-15 06:30:00"}, {"Open": 32620.01, "Close": 32610.01, "Max": 32621.89, "Min": 32604.99, "Volume": 7.24294268, "Date": "2020-01-15 07:00:00"}, {"Open": 32610.01, "Close": 32449.99, "Max": 32610.01, "Min": 32348.0, "Volume": 74.58734283, "Date": "2020-01-15 07:30:00"}, {"Open": 32449.98, "Close": 32574.98, "Max": 32574.99, "Min": 32300.01, "Volume": 15.38745243, "Date": "2020-01-15 08:00:00"}, {"Open": 32574.98, "Close": 32562.95, "Max": 32574.99, "Min": 32400.01, "Volume": 51.72283559, "Date": "2020-01-15 08:30:00"}, {"Open": 32562.99, "Close": 32596.65, "Max": 32604.38, "Min": 32375.0, "Volume": 38.4271044, "Date": "2020-01-15 09:00:00"}, {"Open": 32596.62, "Close": 32999.7, "Max": 32999.88, "Min": 32375.01, "Volume": 6.27617116, "Date": "2020-01-15 09:30:00"}, {"Open": 32999.7, "Close": 32996.99, "Max": 33049.99, "Min": 32705.05, "Volume": 25.35192695, "Date": "2020-01-15 10:00:00"}, {"Open": 32996.99, "Close": 33187.97, "Max": 33187.99, "Min": 32900.0, "Volume": 44.95076789, "Date": "2020-01-15 10:30:00"}, {"Open": 33187.97, "Close": 33043.98, "Max": 33187.97, "Min": 32979.5, "Volume": 1.73455037, "Date": "2020-01-15 11:00:00"}, {"Open": 33043.98, "Close": 32998.07, "Max": 33043.98, "Min": 32900.0, "Volume": 7.79481189, "Date": "2020-01-15 11:30:00"}, {"Open": 32998.07, "Close": 32990.0, "Max": 32999.0, "Min": 32800.01, "Volume": 13.3931099, "Date": "2020-01-15 12:00:00"}, {"Open": 32990.0, "Close": 32966.52, "Max": 33000.0, "Min": 32862.66, "Volume": 154.10978063, "Date": "2020-01-15 12:30:00"}, {"Open": 32873.81, "Close": 33100.02, "Max": 33197.0, "Min": 32873.8, "Volume": 10.35398222, "Date": "2020-01-15 13:00:00"}, {"Open": 33100.02, "Close": 33399.99, "Max": 33399.99, "Min": 33100.0, "Volume": 23.40399744, "Date": "2020-01-15 13:30:00"}, {"Open": 33399.99, "Close": 33299.99, "Max": 33489.99, "Min": 33102.01, "Volume": 9.06890721, "Date": "2020-01-15 14:00:00"}, {"Open": 33299.99, "Close": 33399.81, "Max": 33489.97, "Min": 33143.93, "Volume": 94.30600321, "Date": "2020-01-15 14:30:00"}, {"Open": 33399.7, "Close": 33207.79, "Max": 33399.73, "Min": 32916.65, "Volume": 51.39760061, "Date": "2020-01-15 15:00:00"}, {"Open": 33207.78, "Close": 33083.95, "Max": 33207.84, "Min": 33001.0, "Volume": 61.55392306, "Date": "2020-01-15 15:30:00"}, {"Open": 33083.95, "Close": 33184.99, "Max": 33199.69, "Min": 32873.8, "Volume": 98.51673779, "Date": "2020-01-15 16:00:00"}, {"Open": 33184.99, "Close": 33138.02, "Max": 33224.68, "Min": 33007.66, "Volume": 1.80367129, "Date": "2020-01-15 16:30:00"}, {"Open": 33180.99, "Close": 32865.21, "Max": 33202.26, "Min": 32498.94, "Volume": 113.00296892, "Date": "2020-01-15 17:00:00"}, {"Open": 32938.4, "Close": 32989.98, "Max": 33118.35, "Min": 32666.03, "Volume": 2.7169386, "Date": "2020-01-15 17:30:00"}, {"Open": 32989.99, "Close": 32752.27, "Max": 32989.99, "Min": 32600.0, "Volume": 18.48685506, "Date": "2020-01-15 18:00:00"}, {"Open": 32754.53, "Close": 32800.0, "Max": 32989.99, "Min": 32700.0, "Volume": 46.87518283, "Date": "2020-01-15 18:30:00"}, {"Open": 32844.98, "Close": 32969.69, "Max": 32989.99, "Min": 32844.96, "Volume": 58.73300902, "Date": "2020-01-15 19:00:00"}, {"Open": 32969.68, "Close": 32900.0, "Max": 32969.69, "Min": 32860.0, "Volume": 1.13034249, "Date": "2020-01-15 19:30:00"}, {"Open": 32900.0, "Close": 32899.95, "Max": 32900.0, "Min": 32859.84, "Volume": 54.38986651, "Date": "2020-01-15 20:00:00"}, {"Open": 32899.95, "Close": 33129.65, "Max": 33149.98, "Min": 32859.84, "Volume": 2.43989605, "Date": "2020-01-15 20:30:00"}, {"Open": 33129.44, "Close": 33199.77, "Max": 33200.0, "Min": 32920.1, "Volume": 36.5356959, "Date": "2020-01-15 21:00:00"}, {"Open": 33199.77, "Close": 33189.03, "Max": 33199.79, "Min": 32920.06, "Volume": 54.57189256, "Date": "2020-01-15 21:30:00"}, {"Open": 33189.02, "Close": 33096.82, "Max": 33194.99, "Min": 32920.25, "Volume": 36.91199134, "Date": "2020-01-15 22:00:00"}, {"Open": 33096.82, "Close": 33200.0, "Max": 33200.0, "Min": 33000.04, "Volume": 12.9130503, "Date": "2020-01-15 22:30:00"}, {"Open": 33200.0, "Close": 33100.0, "Max": 33248.97, "Min": 33011.5, "Volume": 42.1483899, "Date": "2020-01-15 23:00:00"}, {"Open": 33100.0, "Close": 33174.55, "Max": 33199.99, "Min": 32816.01, "Volume": 27.37165618, "Date": "2020-01-15 23:30:00"}, {"Open": 33173.47, "Close": 32860.39, "Max": 33173.48, "Min": 32860.39, "Volume": 42.28545436, "Date": "2020-01-16 00:00:00"}, {"Open": 32899.72, "Close": 32831.37, "Max": 32899.72, "Min": 32821.0, "Volume": 19.92235291, "Date": "2020-01-16 00:30:00"}, {"Open": 32860.31, "Close": 32658.43, "Max": 32860.31, "Min": 32655.23, "Volume": 28.50873227, "Date": "2020-01-16 01:00:00"}, {"Open": 32657.9, "Close": 32617.77, "Max": 32657.9, "Min": 32557.45, "Volume": 37.9353543, "Date": "2020-01-16 01:30:00"}, {"Open": 32617.76, "Close": 32528.87, "Max": 32790.99, "Min": 32500.0, "Volume": 94.07071174, "Date": "2020-01-16 02:00:00"}, {"Open": 32544.32, "Close": 32774.04, "Max": 32797.09, "Min": 32543.45, "Volume": 14.46929114, "Date": "2020-01-16 02:30:00"}, {"Open": 32728.9, "Close": 32379.99, "Max": 32728.9, "Min": 32379.99, "Volume": 54.92036857, "Date": "2020-01-16 03:00:00"}, {"Open": 32446.63, "Close": 32641.71, "Max": 32661.23, "Min": 32446.63, "Volume": 36.59493989, "Date": "2020-01-16 03:30:00"}, {"Open": 32630.54, "Close": 32439.66, "Max": 32669.6, "Min": 32379.99, "Volume": 50.40146903, "Date": "2020-01-16 04:00:00"}, {"Open": 32380.6, "Close": 32592.74, "Max": 32730.02, "Min": 32380.6, "Volume": 35.58508568, "Date": "2020-01-16 04:30:00"}, {"Open": 32727.61, "Close": 32725.45, "Max": 32799.44, "Min": 32600.0, "Volume": 13.8962464, "Date": "2020-01-16 05:00:00"}, {"Open": 32725.66, "Close": 32728.6, "Max": 32743.2, "Min": 32725.47, "Volume": 39.56431596, "Date": "2020-01-16 05:30:00"}, {"Open": 32725.71, "Close": 32697.08, "Max": 32725.71, "Min": 32662.04, "Volume": 79.6013702, "Date": "2020-01-16 06:00:00"}, {"Open": 32696.6, "Close": 32697.14, "Max": 32702.33, "Min": 32692.31, "Volume": 15.82501996, "Date": "2020-01-16 06:30:00"}, {"Open": 32697.1, "Close": 32693.2, "Max": 32697.26, "Min": 32650.0, "Volume": 19.90067421, "Date": "2020-01-16 07:00:00"}, {"Open": 32693.21, "Close": 32693.27, "Max": 32693.49, "Min": 32550.0, "Volume": 102.13753371, "Date": "2020-01-16 07:30:00"}, {"Open": 32693.12, "Close": 32799.96, "Max": 32799.99, "Min": 32663.8, "Volume": 121.73385047, "Date": "2020-01-16 08:00:00"}, {"Open": 32799.01, "Close": 32730.23, "Max": 32799.99, "Min": 32697.56, "Volume": 79.52252482, "Date": "2020-01-16 08:30:00"}, {"Open": 32720.9, "Close": 32477.77, "Max": 32720.9, "Min": 32477.77, "Volume": 61.23897593, "Date": "2020-01-16 09:00:00"}, {"Open": 32598.22, "Close": 32543.36, "Max": 32710.47, "Min": 32400.0, "Volume": 45.48240204, "Date": "2020-01-16 09:30:00"}, {"Open": 32710.05, "Close": 32547.95, "Max": 32710.05, "Min": 32400.0, "Volume": 23.3366237, "Date": "2020-01-16 10:00:00"}, {"Open": 32548.49, "Close": 32597.97, "Max": 32597.97, "Min": 32400.02, "Volume": 6.95080988, "Date": "2020-01-16 10:30:00"}, {"Open": 32597.97, "Close": 32548.62, "Max": 32710.49, "Min": 32379.99, "Volume": 31.50441392, "Date": "2020-01-16 11:00:00"}, {"Open": 32488.16, "Close": 32859.62, "Max": 32859.62, "Min": 32488.16, "Volume": 36.76609492, "Date": "2020-01-16 11:30:00"}, {"Open": 32800.0, "Close": 33002.03, "Max": 33149.99, "Min": 32800.0, "Volume": 27.50983494, "Date": "2020-01-16 12:00:00"}, {"Open": 33055.28, "Close": 33002.49, "Max": 33055.28, "Min": 33000.0, "Volume": 2.98710087, "Date": "2020-01-16 12:30:00"}, {"Open": 32996.48, "Close": 32799.99, "Max": 32996.48, "Min": 32671.57, "Volume": 3.55505766, "Date": "2020-01-16 13:00:00"}, {"Open": 32799.73, "Close": 32859.88, "Max": 32894.72, "Min": 32697.04, "Volume": 3.66166867, "Date": "2020-01-16 13:30:00"}, {"Open": 32859.88, "Close": 32799.99, "Max": 32970.0, "Min": 32710.59, "Volume": 3.93591966, "Date": "2020-01-16 14:00:00"}, {"Open": 32799.99, "Close": 32862.62, "Max": 32888.88, "Min": 32725.19, "Volume": 2.81044341, "Date": "2020-01-16 14:30:00"}, {"Open": 32862.61, "Close": 32891.97, "Max": 32900.0, "Min": 32810.18, "Volume": 50.91440801, "Date": "2020-01-16 15:00:00"}, {"Open": 32891.99, "Close": 32960.0, "Max": 32960.0, "Min": 32847.0, "Volume": 29.3506365, "Date": "2020-01-16 15:30:00"}, {"Open": 32960.0, "Close": 32817.78, "Max": 32960.0, "Min": 32710.99, "Volume": 30.62638002, "Date": "2020-01-16 16:00:00"}, {"Open": 32817.78, "Close": 32718.0, "Max": 32817.79, "Min": 32629.01, "Volume": 154.81355475, "Date": "2020-01-16 16:30:00"}, {"Open": 32720.62, "Close": 32873.99, "Max": 32899.99, "Min": 32718.0, "Volume": 30.90286294, "Date": "2020-01-16 17:00:00"}, {"Open": 32873.98, "Close": 32897.77, "Max": 32940.0, "Min": 32800.5, "Volume": 36.4215039, "Date": "2020-01-16 17:30:00"}, {"Open": 32897.76, "Close": 32761.0, "Max": 32919.78, "Min": 32718.0, "Volume": 113.04749676, "Date": "2020-01-16 18:00:00"}, {"Open": 32761.0, "Close": 32910.84, "Max": 32948.99, "Min": 32750.77, "Volume": 7.26342964, "Date": "2020-01-16 18:30:00"}, {"Open": 32907.26, "Close": 32949.9, "Max": 32950.0, "Min": 32774.72, "Volume": 25.48256464, "Date": "2020-01-16 19:00:00"}, {"Open": 32949.9, "Close": 32940.0, "Max": 32949.9, "Min": 32830.79, "Volume": 54.36089746, "Date": "2020-01-16 19:30:00"}, {"Open": 32940.0, "Close": 32940.0, "Max": 32940.0, "Min": 32877.59, "Volume": 79.69695578, "Date": "2020-01-16 20:00:00"}, {"Open": 32940.0, "Close": 32999.91, "Max": 33000.0, "Min": 32889.99, "Volume": 5.02749006, "Date": "2020-01-16 20:30:00"}, {"Open": 32999.91, "Close": 32802.84, "Max": 33000.0, "Min": 32560.02, "Volume": 57.7807036, "Date": "2020-01-16 21:00:00"}, {"Open": 32802.84, "Close": 32865.26, "Max": 32912.6, "Min": 32675.37, "Volume": 14.6708684, "Date": "2020-01-16 21:30:00"}, {"Open": 32865.25, "Close": 32953.45, "Max": 32954.93, "Min": 32740.0, "Volume": 31.39911946, "Date": "2020-01-16 22:00:00"}, {"Open": 32953.15, "Close": 32966.31, "Max": 33000.0, "Min": 32899.99, "Volume": 30.96292165, "Date": "2020-01-16 22:30:00"}, {"Open": 32988.99, "Close": 32980.0, "Max": 33000.0, "Min": 32933.03, "Volume": 29.49776173, "Date": "2020-01-16 23:00:00"}, {"Open": 32956.02, "Close": 32999.99, "Max": 33000.0, "Min": 32865.44, "Volume": 25.09366486, "Date": "2020-01-16 23:30:00"}, {"Open": 32999.99, "Close": 32916.1, "Max": 32999.99, "Min": 32865.44, "Volume": 36.32628584, "Date": "2020-01-17 00:00:00"}, {"Open": 32915.68, "Close": 32910.13, "Max": 32999.86, "Min": 32823.98, "Volume": 10.38864531, "Date": "2020-01-17 00:30:00"}, {"Open": 32949.7, "Close": 32999.98, "Max": 32999.98, "Min": 32929.78, "Volume": 23.37683587, "Date": "2020-01-17 01:00:00"}, {"Open": 32975.02, "Close": 32989.91, "Max": 32999.94, "Min": 32907.62, "Volume": 25.85553698, "Date": "2020-01-17 01:30:00"}, {"Open": 32989.88, "Close": 32989.95, "Max": 32989.95, "Min": 32849.78, "Volume": 23.62502769, "Date": "2020-01-17 02:00:00"}, {"Open": 32957.88, "Close": 33021.22, "Max": 33021.22, "Min": 32957.88, "Volume": 21.27380451, "Date": "2020-01-17 02:30:00"}, {"Open": 33021.22, "Close": 33156.78, "Max": 33200.0, "Min": 33012.01, "Volume": 40.10323466, "Date": "2020-01-17 03:00:00"}, {"Open": 33156.82, "Close": 33138.14, "Max": 33199.98, "Min": 33125.72, "Volume": 75.79057766, "Date": "2020-01-17 03:30:00"}, {"Open": 33138.39, "Close": 33193.99, "Max": 33200.0, "Min": 33138.39, "Volume": 48.14799432, "Date": "2020-01-17 04:00:00"}, {"Open": 33197.99, "Close": 33137.72, "Max": 33197.99, "Min": 33052.7, "Volume": 16.48521889, "Date": "2020-01-17 04:30:00"}, {"Open": 33081.7, "Close": 33200.0, "Max": 33200.0, "Min": 33081.7, "Volume": 10.97878429, "Date": "2020-01-17 05:00:00"}, {"Open": 33200.0, "Close": 33250.0, "Max": 33250.0, "Min": 33170.02, "Volume": 9.34950924, "Date": "2020-01-17 05:30:00"}, {"Open": 33250.0, "Close": 33470.0, "Max": 33470.0, "Min": 33250.0, "Volume": 48.28228939, "Date": "2020-01-17 06:00:00"}, {"Open": 33470.0, "Close": 33469.99, "Max": 33470.0, "Min": 33395.01, "Volume": 58.07216839, "Date": "2020-01-17 06:30:00"}, {"Open": 33467.0, "Close": 33474.68, "Max": 33474.69, "Min": 33400.01, "Volume": 17.29952205, "Date": "2020-01-17 07:00:00"}, {"Open": 33450.0, "Close": 33500.0, "Max": 33500.0, "Min": 33449.01, "Volume": 3.20205393, "Date": "2020-01-17 07:30:00"}, {"Open": 33500.0, "Close": 33869.77, "Max": 33967.11, "Min": 33500.0, "Volume": 56.78192331, "Date": "2020-01-17 08:00:00"}, {"Open": 33900.0, "Close": 33720.0, "Max": 33906.46, "Min": 33640.0, "Volume": 41.19734451, "Date": "2020-01-17 08:30:00"}, {"Open": 33710.0, "Close": 33842.3, "Max": 34000.0, "Min": 33650.04, "Volume": 59.55008461, "Date": "2020-01-17 09:00:00"}, {"Open": 33842.3, "Close": 33970.18, "Max": 34000.0, "Min": 33800.0, "Volume": 63.11921925, "Date": "2020-01-17 09:30:00"}, {"Open": 33970.18, "Close": 33773.96, "Max": 34012.96, "Min": 33746.98, "Volume": 59.73699411, "Date": "2020-01-17 10:00:00"}, {"Open": 33819.99, "Close": 33898.98, "Max": 33972.63, "Min": 33714.0, "Volume": 37.71204274, "Date": "2020-01-17 10:30:00"}, {"Open": 33898.99, "Close": 33830.01, "Max": 34000.0, "Min": 33821.16, "Volume": 40.44026402, "Date": "2020-01-17 11:00:00"}, {"Open": 33987.99, "Close": 33859.44, "Max": 33987.99, "Min": 33800.0, "Volume": 22.16286876, "Date": "2020-01-17 11:30:00"}, {"Open": 33859.44, "Close": 33500.0, "Max": 33874.12, "Min": 33480.0, "Volume": 10.98567418, "Date": "2020-01-17 12:00:00"}, {"Open": 33500.0, "Close": 33555.0, "Max": 33718.71, "Min": 33500.0, "Volume": 3.77999493, "Date": "2020-01-17 12:30:00"}, {"Open": 33551.04, "Close": 33673.51, "Max": 33673.51, "Min": 33500.0, "Volume": 49.98764188, "Date": "2020-01-17 13:00:00"}, {"Open": 33673.51, "Close": 33701.73, "Max": 33729.07, "Min": 33558.59, "Volume": 54.59719818, "Date": "2020-01-17 13:30:00"}, {"Open": 33701.73, "Close": 33523.25, "Max": 33714.63, "Min": 33462.4, "Volume": 52.81971837, "Date": "2020-01-17 14:00:00"}, {"Open": 33501.0, "Close": 33498.65, "Max": 33626.35, "Min": 33400.0, "Volume": 32.76818469, "Date": "2020-01-17 14:30:00"}, {"Open": 33498.65, "Close": 33588.0, "Max": 33627.24, "Min": 33400.01, "Volume": 94.98696135, "Date": "2020-01-17 15:00:00"}, {"Open": 33588.0, "Close": 33722.0, "Max": 33794.59, "Min": 33552.87, "Volume": 6.55049184, "Date": "2020-01-17 15:30:00"}, {"Open": 33722.0, "Close": 33917.79, "Max": 33946.0, "Min": 33722.0, "Volume": 148.18762042, "Date": "2020-01-17 16:00:00"}, {"Open": 33917.79, "Close": 33860.96, "Max": 34000.0, "Min": 33860.96, "Volume": 8.43825364, "Date": "2020-01-17 16:30:00"}, {"Open": 33915.98, "Close": 33950.05, "Max": 34058.39, "Min": 33771.03, "Volume": 69.02091738, "Date": "2020-01-17 17:00:00"}, {"Open": 33950.05, "Close": 33989.96, "Max": 34045.18, "Min": 33888.91, "Volume": 86.21982364, "Date": "2020-01-17 17:30:00"}, {"Open": 33989.96, "Close": 34045.42, "Max": 34045.42, "Min": 33943.96, "Volume": 0.56891675, "Date": "2020-01-17 18:00:00"}], "candlesCount": 199, "graphMin": 30683.93, "graphMax": 34058.39}