├── market ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0002_auto_20180512_1727.py │ └── 0001_initial.py ├── tests.py ├── apps.py ├── admin.py ├── forms.py ├── urls.py ├── templates │ └── market │ │ ├── select_company.html │ │ ├── user_transaction_history.html │ │ ├── admin_company_change.html │ │ └── transaction_market.html ├── views.py ├── models.py └── fixtures │ └── market.json ├── accounts ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0002_auto_20180512_1727.py │ └── 0001_initial.py ├── passwords │ ├── __init__.py │ └── urls.py ├── tests.py ├── apps.py ├── templates │ └── accounts │ │ ├── register.html │ │ ├── login.html │ │ ├── news.html │ │ ├── loan.html │ │ ├── leaderboard.html │ │ └── profile.html ├── urls.py ├── admin.py ├── forms.py ├── views.py └── models.py ├── runtime.txt ├── stock_bridge ├── __init__.py ├── aws │ ├── __init__.py │ ├── utils.py │ └── conf.py ├── settings │ ├── __init__.py │ ├── production.py │ ├── base.py │ └── local.py ├── wsgi.py ├── views.py ├── utils.py ├── urls.py ├── decorators.py └── mixins.py ├── Procfile ├── .gitignore ├── db.sqlite3 ├── static_files ├── favicon.png ├── img │ ├── rules_1.png │ └── rules_2.png ├── fonts │ └── foughtknight.ttf └── css │ └── main.css ├── static_cdn └── static_root │ ├── favicon.png │ ├── img │ ├── rules_1.png │ └── rules_2.png │ └── css │ └── main.css ├── templates ├── registration │ ├── emails │ │ ├── verify.txt │ │ └── verify.html │ ├── password_change_done.html │ ├── password_reset_complete.html │ ├── password_reset_done.html │ ├── password_reset_email.html │ ├── password_reset_email.txt │ ├── password_change_form.html │ ├── password_reset_form.html │ ├── password_reset_confirm.html │ └── activation_error.html ├── instructions.html ├── base │ ├── css.html │ ├── js.html │ ├── footer.html │ └── navbar.html ├── base.html └── home.html ├── .env-sample ├── requirements.txt ├── manage.py ├── LICENSE └── README.md /market/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /accounts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.5 -------------------------------------------------------------------------------- /stock_bridge/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stock_bridge/aws/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /accounts/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /accounts/passwords/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /market/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn stock_bridge.wsgi --log-file - 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | notes/ 2 | 3 | .env 4 | .idea/ 5 | .vscode/ 6 | **/__pycache__/ 7 | *.py[cod] -------------------------------------------------------------------------------- /market/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /accounts/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/morphosis-nitmz/Stock-Bridge-2018/HEAD/db.sqlite3 -------------------------------------------------------------------------------- /static_files/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/morphosis-nitmz/Stock-Bridge-2018/HEAD/static_files/favicon.png -------------------------------------------------------------------------------- /market/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class MarketConfig(AppConfig): 5 | name = 'market' 6 | -------------------------------------------------------------------------------- /accounts/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AccountsConfig(AppConfig): 5 | name = 'accounts' 6 | -------------------------------------------------------------------------------- /static_files/img/rules_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/morphosis-nitmz/Stock-Bridge-2018/HEAD/static_files/img/rules_1.png -------------------------------------------------------------------------------- /static_files/img/rules_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/morphosis-nitmz/Stock-Bridge-2018/HEAD/static_files/img/rules_2.png -------------------------------------------------------------------------------- /stock_bridge/settings/__init__.py: -------------------------------------------------------------------------------- 1 | # from .base import * 2 | 3 | from .production import * 4 | 5 | # from .local import * 6 | -------------------------------------------------------------------------------- /static_cdn/static_root/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/morphosis-nitmz/Stock-Bridge-2018/HEAD/static_cdn/static_root/favicon.png -------------------------------------------------------------------------------- /static_files/fonts/foughtknight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/morphosis-nitmz/Stock-Bridge-2018/HEAD/static_files/fonts/foughtknight.ttf -------------------------------------------------------------------------------- /static_cdn/static_root/img/rules_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/morphosis-nitmz/Stock-Bridge-2018/HEAD/static_cdn/static_root/img/rules_1.png -------------------------------------------------------------------------------- /static_cdn/static_root/img/rules_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/morphosis-nitmz/Stock-Bridge-2018/HEAD/static_cdn/static_root/img/rules_2.png -------------------------------------------------------------------------------- /templates/registration/emails/verify.txt: -------------------------------------------------------------------------------- 1 | Hello, 2 | 3 | Please activate your account for {{ email }} by clicking the link below: 4 | 5 | {{ path }} 6 | 7 | Once you are activated, you can login! 8 | 9 | 10 | Thank You, 11 | 12 | Team Morphosis -------------------------------------------------------------------------------- /stock_bridge/aws/utils.py: -------------------------------------------------------------------------------- 1 | from storages.backends.s3boto3 import S3Boto3Storage 2 | 3 | # location of static and media inside bucket 4 | StaticRootS3BotoStorage = lambda: S3Boto3Storage(location='static') 5 | MediaRootS3BotoStorage = lambda: S3Boto3Storage(location='media') 6 | -------------------------------------------------------------------------------- /templates/registration/emails/verify.html: -------------------------------------------------------------------------------- 1 |

Hello,

2 |

Please activate your account for {{ email }} by clicking the link below:

3 |

{{ path }}

4 |

Once you are activated, you can login!

5 |

Thank You,

6 |

Team Morphosis

-------------------------------------------------------------------------------- /.env-sample: -------------------------------------------------------------------------------- 1 | SECRET_KEY=your_key 2 | EMAIL_HOST_USER=sendgrid_username 3 | EMAIL_HOST_PASSWORD=sendgrid_password 4 | AWS_GROUP_NAME=aws_group 5 | AWS_USER_NAME =aws_IAM 6 | AWS_ACCESS_KEY_ID=aws_access_key 7 | AWS_SECRET_ACCESS_KEY=aws_secret_access 8 | AWS_STORAGE_BUCKET_NAME=aws_bucket -------------------------------------------------------------------------------- /templates/registration/password_change_done.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |
5 |

Password successfully changed!

6 |
7 |
8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /market/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Company, InvestmentRecord, CompanyCMPRecord, Transaction 4 | 5 | 6 | admin.site.register(Company) 7 | admin.site.register(Transaction) 8 | admin.site.register(InvestmentRecord) 9 | admin.site.register(CompanyCMPRecord) 10 | -------------------------------------------------------------------------------- /templates/registration/password_reset_complete.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |
5 |

Password reset complete

6 | Login 7 |
8 |
9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /templates/registration/password_reset_done.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |
5 |

Rest Instructions Sent

6 |

Please check your email

7 |
8 |
9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /templates/registration/password_reset_email.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% blocktrans %} 3 | Hello, 4 | 5 | Reset your password on {{ domain }} for {{ user }}: 6 | {% endblocktrans %} 7 | 8 | {% block reset_link %} 9 | {{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %} 10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /templates/registration/password_reset_email.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% blocktrans %} 3 | Hello, 4 | 5 | Reset your password on {{ domain }} for {{ user }}: 6 | {% endblocktrans %} 7 | 8 | {% block reset_link %} 9 | {{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %} 10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==2.0.2 2 | djangorestframework==3.8.2 3 | django-common-helpers==0.9.1 4 | python-decouple==3.1 5 | numpy==1.14.3 6 | psycopg2==2.7.4 7 | pytz==2018.4 8 | dj-database-url==0.4.2 9 | gunicorn==19.8.1 10 | Pillow==5.1.0 11 | setuptools==36.3.0 12 | wheel==0.30.0 13 | boto==2.48.0 14 | boto3==1.7.4 15 | botocore==1.10.19 16 | s3transfer==0.1.13 17 | django-storages==1.6.6 -------------------------------------------------------------------------------- /templates/instructions.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | 4 | {% block base_head %} 5 | Instructions 6 | {% endblock %} 7 | 8 | {% block content %} 9 | 10 |
11 | 12 |
13 |
14 | 15 |
16 | 17 | {% endblock %} -------------------------------------------------------------------------------- /stock_bridge/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for stock_bridge project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | 15 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "stock_bridge.settings") 16 | 17 | application = get_wsgi_application() 18 | -------------------------------------------------------------------------------- /templates/registration/password_change_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |
5 |

Change your Password

6 |
{% csrf_token %} 7 | {{ form.as_p }} 8 | 9 | 10 |
11 |
12 |
13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /templates/base/css.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /templates/registration/password_reset_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |
5 |

Reset your Password

6 |
{% csrf_token %} 7 | {{ form.as_p }} 8 | 9 | 10 |
11 |
12 |
13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /stock_bridge/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.views.generic import View 3 | 4 | from .mixins import CountNewsMixin 5 | 6 | 7 | class HomeView(CountNewsMixin, View): 8 | 9 | def get(self, request, *args, **kwargs): 10 | return render(request, 'home.html', {}) 11 | 12 | def post(self, request, *args, **kwargs): 13 | return render(request, 'home.html', {}) 14 | 15 | 16 | def instruction_view(request): 17 | return render(request, 'instructions.html', {}) 18 | -------------------------------------------------------------------------------- /templates/registration/password_reset_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |
5 |

Set your Password

6 |
{% csrf_token %} 7 | {{ form.as_p }} 8 | 9 | 10 |
11 |
12 |
13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /stock_bridge/utils.py: -------------------------------------------------------------------------------- 1 | import random 2 | import string 3 | 4 | 5 | def random_string_generator(size=10, chars=string.ascii_lowercase + string.digits): 6 | return ''.join(random.choice(chars) for _ in range(size)) 7 | 8 | 9 | def unique_key_generator(instance): 10 | size = random.randint(30, 45) 11 | new_key = random_string_generator(size=size) 12 | 13 | Klass = instance.__class__ 14 | qs_exists = Klass.objects.filter(key=new_key).exists() 15 | if qs_exists: 16 | return unique_key_generator(instance) 17 | return new_key 18 | -------------------------------------------------------------------------------- /accounts/migrations/0002_auto_20180512_1727.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.2 on 2018-05-12 11:57 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('accounts', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='emailactivation', 16 | name='user', 17 | field=models.ForeignKey(on_delete=True, to=settings.AUTH_USER_MODEL), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /accounts/templates/accounts/register.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block base_head %} 4 | Register 5 | {% endblock %} 6 | 7 | {% block content %} 8 | 9 |
10 |
11 |

Register

12 |
13 |
14 |
15 |
16 |
{% csrf_token %} 17 | {{ form }} 18 | 19 |
20 |
21 |
22 | 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /accounts/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from .views import AccountEmailActivateView, LoanView, cancel_loan, deduct_interest 4 | 5 | 6 | app_name = 'Account' 7 | 8 | 9 | urlpatterns = [ 10 | url(r'^email/confirm/(?P[0-9A-Za-z]+)/$', AccountEmailActivateView.as_view(), name='email-activate'), 11 | url(r'^email/resend-activation/$', AccountEmailActivateView.as_view(), name='resend-activation'), 12 | url(r'^bank/loan$', LoanView.as_view(), name='loan'), 13 | url(r'^bank/loan/deduct$', cancel_loan, name='cancel_loan'), 14 | url(r'^bank/interest/deduct$', deduct_interest, name='deduct_interest'), 15 | ] 16 | -------------------------------------------------------------------------------- /templates/registration/activation_error.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 | 5 |
6 |
7 | {% if key %} 8 |

Activation Failed! Please try again.

9 | {% else %} 10 |

Re-activate your Email.

11 | {% endif %} 12 |
13 |
14 |
{% csrf_token %} 15 | {{ form.as_p }} 16 | 17 |
18 |
19 |
20 | 21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /accounts/templates/accounts/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block base_head %} 4 | Login 5 | {% endblock %} 6 | 7 | {% block content %} 8 | 9 |
10 |
11 |

Login

12 |
13 |
14 |
15 |
16 |
17 |
{% csrf_token %} 18 | {{ form }} 19 |
20 |
21 | 22 |
23 |
24 | Forgot Password? 25 |
26 |
27 |
28 |
29 |
30 | 31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /accounts/templates/accounts/news.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block base_head %} 4 | News 5 | {% endblock %} 6 | 7 | {% block content %} 8 | 9 |
10 |
11 |

News

12 |
13 |
14 | {% if object_list %} 15 |
16 | {% for object in object_list %} 17 |
18 | 21 |
22 | {% endfor %} 23 |
24 | {% else %} 25 |
26 |
27 |

No news available

28 |
29 |
30 | {% endif %} 31 | 32 | {% endblock %} -------------------------------------------------------------------------------- /market/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | from .models import TRANSACTION_MODES 4 | 5 | 6 | class StockTransactionForm(forms.Form): 7 | """ Form for users to make stocks transaction """ 8 | mode = forms.ChoiceField(choices=TRANSACTION_MODES) 9 | quantity = forms.CharField(required=True, widget=forms.TextInput(attrs={ 10 | 'class': 'form-control', 11 | 'pattern': '[0-9]+', 12 | 'title': 'Enter integers only', 13 | 'placeholder': 'Enter integers only' 14 | })) 15 | 16 | 17 | class CompanyChangeForm(forms.Form): 18 | """ Form for admin to change company's CMP """ 19 | price = forms.CharField(required=True, widget=forms.TextInput(attrs={ 20 | 'class': 'form-control', 21 | 'pattern': '[0-9]+', 22 | 'title': 'Enter integers only', 23 | 'placeholder': 'Enter integers only' 24 | })) 25 | -------------------------------------------------------------------------------- /templates/base/js.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "stock_bridge.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /market/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from .views import ( 4 | CompanySelectionView, 5 | CompanyTransactionView, 6 | CompanyCMPChartData, 7 | CompanyCMPCreateView, 8 | CompanyAdminCompanyUpdateView, 9 | deduct_tax, 10 | update_market 11 | ) 12 | 13 | 14 | app_name = 'Market' 15 | 16 | 17 | urlpatterns = [ 18 | url(r'^select/$', CompanySelectionView.as_view(), name='company_select'), 19 | url(r'^transact/(?P\w+)$', CompanyTransactionView.as_view(), name='transaction'), 20 | url(r'^admin/(?P\w+)$', CompanyAdminCompanyUpdateView.as_view(), name='admin'), 21 | url(r'^create/$', CompanyCMPCreateView.as_view(), name='create_cmp'), 22 | url(r'^company/api/(?P\w+)$', CompanyCMPChartData.as_view(), name='cmp_api_data'), 23 | url(r'^tax/$', deduct_tax, name='tax'), 24 | url(r'^update/$', update_market, name='update') 25 | ] 26 | -------------------------------------------------------------------------------- /market/templates/market/select_company.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block base_head %} 3 | Select 4 | {% endblock %} 5 | 6 | {% block content %} 7 | 8 |
9 |
10 |

Market

11 |
12 |
13 |
14 |
15 | 25 |
26 |
27 | 28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Morphosis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /accounts/passwords/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.contrib.auth import views as auth_views 3 | 4 | 5 | urlpatterns = [ 6 | url(r'^password/change/$', 7 | auth_views.PasswordChangeView.as_view(), 8 | name='password_change' 9 | ), 10 | url(r'^password/change/done/$', 11 | auth_views.PasswordChangeDoneView.as_view(), 12 | name='password_change_done' 13 | ), 14 | url(r'^password/reset/$', 15 | auth_views.PasswordResetView.as_view(), 16 | name='password_reset' 17 | ), 18 | url(r'^password/reset/done/$', 19 | auth_views.PasswordResetDoneView.as_view(), 20 | name='password_reset_done' 21 | ), 22 | url(r'^password/reset/\ 23 | (?P[0-9A-Za-z_\-]+)/\ 24 | (?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', 25 | auth_views.PasswordResetConfirmView.as_view(), 26 | name='password_reset_confirm' 27 | ), 28 | url(r'^password/reset/complete/$', 29 | auth_views.PasswordResetCompleteView.as_view(), 30 | name='password_reset_complete' 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /stock_bridge/aws/conf.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import os 3 | from decouple import config 4 | 5 | 6 | # AWS credentials 7 | AWS_GROUP_NAME = config('AWS_GROUP_NAME') 8 | AWS_USER_NAME = config('AWS_USER_NAME') 9 | AWS_ACCESS_KEY_ID = config('AWS_ACCESS_KEY_ID') 10 | AWS_SECRET_ACCESS_KEY = config('AWS_SECRET_ACCESS_KEY') 11 | 12 | AWS_FILE_EXPIRE = 200 13 | AWS_PRELOAD_METADATA = True 14 | AWS_QUERYSTRING_AUTH = False 15 | 16 | DEFAULT_FILE_STORAGE = 'stock_bridge.aws.utils.MediaRootS3BotoStorage' 17 | STATICFILES_STORAGE = 'stock_bridge.aws.utils.StaticRootS3BotoStorage' 18 | AWS_STORAGE_BUCKET_NAME = config('AWS_STORAGE_BUCKET_NAME') 19 | S3DIRECT_REGION = 'ap-south-1' 20 | S3_URL = '//%s.s3.amazonaws.com/' % AWS_STORAGE_BUCKET_NAME 21 | MEDIA_URL = '//%s.s3.amazonaws.com/media/' % AWS_STORAGE_BUCKET_NAME 22 | MEDIA_ROOT = MEDIA_URL 23 | STATIC_URL = S3_URL + 'static/' 24 | ADMIN_MEDIA_PREFIX = STATIC_URL + 'admin/' 25 | 26 | two_months = datetime.timedelta(days=61) 27 | date_two_months_later = datetime.date.today() + two_months 28 | expires = date_two_months_later.strftime("%A, %d %B %Y 20:00:00 GMT") 29 | 30 | AWS_HEADERS = { 31 | 'Expires': expires, 32 | 'Cache-Control': 'max-age=%d' % (int(two_months.total_seconds()), ), 33 | } 34 | -------------------------------------------------------------------------------- /accounts/templates/accounts/loan.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block base_head %} 4 | Bank 5 | {% endblock %} 6 | 7 | {% block content %} 8 | 9 |
10 |
11 |

World Bank

12 |
13 |
14 |
15 |
16 |

Credit Loan

17 |

Amount: ₹ 5,000

18 |
{% csrf_token %} 19 | 20 | 21 |
22 |
23 |
24 |

Loan Due

25 |

Amount Due: ₹ {{ user.loan }}
26 | Balance Available: ₹ {{ user.cash }}
27 | Pay an installment of ₹ 5,000

28 |
{% csrf_token %} 29 | 30 | 31 |
32 |
33 |
34 | 35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /static_files/css/main.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | 5 | #wrap { 6 | min-height: 75%; 7 | } 8 | 9 | #main { 10 | overflow:auto; 11 | padding-bottom:80px; /* this needs to be bigger than footer height*/ 12 | } 13 | 14 | .footer { 15 | position: relative; 16 | margin-top: -80px; /* negative value of footer height */ 17 | height: 80px; 18 | clear:both; 19 | padding-top:20px; 20 | } 21 | 22 | .dropdown .dropdown-menu a:hover { 23 | background-color: #dcdce1; 24 | color: #000; 25 | } 26 | 27 | .footer-custom { 28 | background-color: #dcdce1; 29 | padding: 60px 40px; 30 | color: #000; 31 | } 32 | 33 | a.dropdown-item.active { 34 | background-color:#D9CDCA; 35 | color: #000; 36 | } 37 | 38 | .anchor-black { 39 | text-decoration: none; 40 | color: black; 41 | } 42 | 43 | .image-home { 44 | background: url("https://entefy.com/uploads/editor/071f8a36715a8a6b7f7ded54074cc8c6.gif"); 45 | background-attachment: fixed; 46 | background-repeat: no-repeat; 47 | background-size: cover; 48 | height: 600px; 49 | opacity: 1; 50 | } 51 | 52 | .home-heading { 53 | color: rgb(255, 255, 255); 54 | text-align: center; 55 | padding-top: 200px; 56 | font-size: 22px; 57 | font-family: 'Aldrich'; 58 | } 59 | 60 | .heading { 61 | text-align: center; 62 | font-family: 'Aldrich'; 63 | } -------------------------------------------------------------------------------- /static_cdn/static_root/css/main.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | 5 | #wrap { 6 | min-height: 75%; 7 | } 8 | 9 | #main { 10 | overflow:auto; 11 | padding-bottom:80px; /* this needs to be bigger than footer height*/ 12 | } 13 | 14 | .footer { 15 | position: relative; 16 | margin-top: -80px; /* negative value of footer height */ 17 | height: 80px; 18 | clear:both; 19 | padding-top:20px; 20 | } 21 | 22 | .dropdown .dropdown-menu a:hover { 23 | background-color: #dcdce1; 24 | color: #000; 25 | } 26 | 27 | .footer-custom { 28 | background-color: #dcdce1; 29 | padding: 60px 40px; 30 | color: #000; 31 | } 32 | 33 | a.dropdown-item.active { 34 | background-color:#D9CDCA; 35 | color: #000; 36 | } 37 | 38 | .anchor-black { 39 | text-decoration: none; 40 | color: black; 41 | } 42 | 43 | .image-home { 44 | background: url("https://entefy.com/uploads/editor/071f8a36715a8a6b7f7ded54074cc8c6.gif"); 45 | background-attachment: fixed; 46 | background-repeat: no-repeat; 47 | background-size: cover; 48 | height: 600px; 49 | opacity: 1; 50 | } 51 | 52 | .home-heading { 53 | color: rgb(255, 255, 255); 54 | text-align: center; 55 | padding-top: 200px; 56 | font-size: 22px; 57 | font-family: 'Aldrich'; 58 | } 59 | 60 | .heading { 61 | text-align: center; 62 | font-family: 'Aldrich'; 63 | } -------------------------------------------------------------------------------- /market/migrations/0002_auto_20180512_1727.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.2 on 2018-05-12 11:57 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('market', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='companycmprecord', 16 | name='company', 17 | field=models.ForeignKey(on_delete=True, to='market.Company'), 18 | ), 19 | migrations.AlterField( 20 | model_name='investmentrecord', 21 | name='company', 22 | field=models.ForeignKey(on_delete=True, to='market.Company'), 23 | ), 24 | migrations.AlterField( 25 | model_name='investmentrecord', 26 | name='user', 27 | field=models.ForeignKey(on_delete=True, to=settings.AUTH_USER_MODEL), 28 | ), 29 | migrations.AlterField( 30 | model_name='transaction', 31 | name='company', 32 | field=models.ForeignKey(on_delete=True, to='market.Company'), 33 | ), 34 | migrations.AlterField( 35 | model_name='transaction', 36 | name='user', 37 | field=models.ForeignKey(on_delete=True, to=settings.AUTH_USER_MODEL), 38 | ), 39 | ] 40 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {% include 'base/css.html' %} 10 | {% include 'base/js.html' %} 11 | {% block base_head %}{% endblock %} 12 | 13 | 14 | 15 | {% include 'base/navbar.html' with brand_name='Stock Bridge' %} 16 | 17 |
18 |
19 | {% if messages %} 20 |
21 | {% for message in messages %} 22 |
23 | 24 | {% if message.level == DEFAULT_MESSAGE_LEVELS.ERROR %}Important: {% endif %} 25 | {{ message }} 26 | 27 |
28 | {% endfor %} 29 |
30 | {% endif %} 31 | 32 |
33 | {% block content %}{% endblock %} 34 |
35 | 36 |
37 | {% block content_fluid %}{% endblock %} 38 |
39 |
40 |
41 | 42 | {% block home_content %}{% endblock %} 43 | 44 | {% include 'base/footer.html' %} 45 | 46 | {% block javascript %}{% endblock %} 47 | 48 | 49 | -------------------------------------------------------------------------------- /stock_bridge/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.conf.urls import url, include 3 | from django.contrib import admin 4 | from django.conf.urls.static import static 5 | from django.contrib.auth.views import LogoutView 6 | from django.views.generic import RedirectView 7 | 8 | from .views import HomeView, instruction_view 9 | from accounts.views import RegisterView, LoginView, LeaderBoardView, ProfileView, NewsView 10 | from market.views import UserTransactionHistoryView 11 | 12 | 13 | urlpatterns = [ 14 | url(r'^$', HomeView.as_view(), name='home'), 15 | url(r'^login/$', LoginView.as_view(), name='login'), 16 | url(r'^register/$', RegisterView.as_view(), name='register'), 17 | url(r'^logout/$', LogoutView.as_view(), name='logout'), 18 | url(r'^profile/(?P[a-zA-Z0-9]+)/$', ProfileView.as_view(), name='profile'), 19 | url(r'^news/$', NewsView.as_view(), name='news'), 20 | url(r'^account/', include('accounts.urls', namespace='account')), 21 | url(r'^accounts/', include('accounts.passwords.urls')), 22 | url(r'^accounts/$', RedirectView.as_view(url='/account')), 23 | url(r'^leaderboard/$', LeaderBoardView.as_view(), name='leaderboard'), 24 | url(r'^instructions/$', instruction_view, name='instructions'), 25 | url(r'^stocks/', include('market.urls', namespace='market')), 26 | url(r'^history/$', UserTransactionHistoryView.as_view(), name='transaction_history'), 27 | url(r'^admin/', admin.site.urls), 28 | ] 29 | 30 | if settings.DEBUG: 31 | urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 32 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 33 | -------------------------------------------------------------------------------- /market/templates/market/user_transaction_history.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block base_head %} 4 | Transaction History 5 | {% endblock %} 6 | 7 | {% block content %} 8 | 9 |
10 |
11 |

Transaction History

12 |
13 |
14 |
15 |
16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {% for object in object_list %} 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | {% endfor %} 37 | 38 |
S. No.CompanyModePriceQuantity
{{ forloop.counter }}{{ object.company }}{{ object.mode }}₹ {{ object.price }}{{ object.num_stocks }}
39 |
40 |
41 |
42 | 43 | {% endblock %} -------------------------------------------------------------------------------- /stock_bridge/decorators.py: -------------------------------------------------------------------------------- 1 | try: 2 | from functools import wraps 3 | except ImportError: 4 | from django.utils.functional import wraps # Python 2.4 fallback. 5 | from django.utils.decorators import available_attrs 6 | from django.contrib import messages 7 | from django.contrib.auth import REDIRECT_FIELD_NAME 8 | from django.contrib.auth.decorators import login_required 9 | 10 | default_message = "You must Log In first!" 11 | 12 | 13 | def user_passes_test(test_func, message=default_message): 14 | """ 15 | Decorator for views that checks that the user passes the given test, 16 | setting a message in case of no success. The test should be a callable 17 | that takes the user object and returns True if the user passes. 18 | """ 19 | def decorator(view_func): 20 | @wraps(view_func, assigned=available_attrs(view_func)) 21 | def _wrapped_view(request, *args, **kwargs): 22 | if not test_func(request.user): 23 | messages.success(request, message) 24 | return view_func(request, *args, **kwargs) 25 | return _wrapped_view 26 | return decorator 27 | 28 | 29 | def login_required_message(function=None, message=default_message): 30 | """ 31 | Decorator for views that checks that the user is logged in, redirecting 32 | to the log-in page if necessary. 33 | """ 34 | actual_decorator = user_passes_test( 35 | lambda u: u.is_authenticated, 36 | message=message, 37 | ) 38 | if function: 39 | return actual_decorator(function) 40 | return actual_decorator 41 | 42 | 43 | def login_required_message_and_redirect(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None, message=default_message): 44 | 45 | if function: 46 | return login_required_message( 47 | login_required(function, redirect_field_name, login_url), 48 | message 49 | ) 50 | 51 | return lambda deferred_function: login_required_message_and_redirect(deferred_function, redirect_field_name, login_url, message) 52 | -------------------------------------------------------------------------------- /accounts/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.contrib.auth import get_user_model 3 | from django.contrib.auth.models import Group 4 | from django.contrib.auth.admin import UserAdmin as BaseUserAdmin 5 | 6 | from .forms import UserAdminCreationForm, UserAdminChangeForm 7 | from .models import EmailActivation, News 8 | 9 | 10 | User = get_user_model() 11 | 12 | 13 | class UserAdmin(BaseUserAdmin): 14 | # The forms to change and add user instances 15 | form = UserAdminChangeForm 16 | add_form = UserAdminCreationForm 17 | 18 | # The fields to be used in displaying the User model. 19 | # These override the definitions on the base UserAdmin 20 | # that reference specific fields on auth.User. 21 | list_display = ('username', 'email', 'is_superuser') 22 | list_filter = ('is_superuser', 'staff', 'is_active') 23 | fieldsets = ( 24 | (None, {'fields': ('username', 'email', 'password')}), 25 | # would have data we had fields like 'full_name' 26 | ('Personal info', {'fields': ('full_name', 'cash', 'loan', 'coeff_of_variation')}), 27 | ('Permissions', {'fields': ('is_superuser', 'staff', 'is_active')}), 28 | ) 29 | # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin 30 | # overrides get_fieldsets to use this attribute when creating a user. 31 | add_fieldsets = ( 32 | (None, { 33 | 'classes': ('wide',), 34 | 'fields': ( 35 | 'username', 'email', 'full_name', 'cash', 'loan', 'coeff_of_variation', 'password1', 'password2' 36 | )} 37 | ), 38 | ) 39 | search_fields = ('username', 'email', 'full_name') 40 | ordering = ('username', 'email') 41 | filter_horizontal = () 42 | 43 | 44 | admin.site.register(User, UserAdmin) 45 | 46 | # Remove Group Model from admin. We're not using it. 47 | admin.site.unregister(Group) 48 | 49 | 50 | class EmailActivationAdmin(admin.ModelAdmin): 51 | search_fields = ['email'] # Search guest users by email in admin panel 52 | 53 | class Meta: 54 | model = EmailActivation 55 | 56 | 57 | admin.site.register(EmailActivation, EmailActivationAdmin) 58 | 59 | admin.site.register(News) 60 | -------------------------------------------------------------------------------- /templates/base/footer.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /accounts/templates/accounts/leaderboard.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block base_head %} 4 | Leaderboard 5 | {% endblock %} 6 | 7 | {% block content %} 8 | 9 |
10 |
11 |

LeaderBoard

12 |
13 |
14 |
15 |
16 | 19 |
20 |
21 |
22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {% for object in data %} 34 | {% if object.0 == request.user.username %} 35 | 36 | 37 | 42 | 43 | 44 | {% else %} 45 | 46 | 47 | 48 | 49 | 50 | {% endif %} 51 | {% endfor %} 52 | 53 |
RankNameNet Worth
{{ forloop.counter }} 38 | 39 | {{ object.1 }} 40 | 41 | ₹ {{ object.2 }}
{{ forloop.counter }}{{ object.1 }}₹ {{ object.2 }}
54 |
55 |
56 |
57 | 58 | {% endblock %} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stock Bridge 2 | 3 | Keeping in view the real thrill of the market monotony, we are here with the Virtual Simulation of the same thrill 4 | and adrenaline rush through the event under the banner of **Morphosis, NIT Mizoram**. The virtual share market enables the participants 5 | to trade, buy, sell mortgage and showcase their rationality, their mettle against competitive decision making under 6 | pressure. The full time active virtual market will be a platform to showcase your inferential, pressure-handling 7 | capability. 8 | 9 | 10 | ### Tools Used 11 | 12 | - **Python** - Django, Django REST Framework 13 | - **Javascript** - Chart.js 14 | - **Bootstrap v4** 15 | 16 | 17 | ### Third Party Services Used 18 | 19 | - **Amazon Web Services (AWS)**: Stores all static and media files. 20 | - **Heroku**: Used to deploy the project in production environment. 21 | - **sendgrid**: Used to send transactional emails like email-id verification, order completion etc. 22 | 23 | 24 | ## Instructions for setting up the project 25 | 26 | 1. Clone the repository 27 | `git clone https://github.com/morphosis-nitmz/Stock-Bridge` 28 | 29 | 2. Rename the file **.env-sample** to **.env** and replace the value of `SECRET_KEY` with the secret key of your own project. To generate a new secret key 30 | - Go to terminal and create a new django project `django-admin startproject `. 31 | - Now get the value of `SECRET_KEY` in *settings.py* and use that as the secret key for the **Stock-Bridge project**. 32 | - Now delete that new django project. 33 | 34 | 3. **Sendgrid setup**: 35 | - Create an account on [sendgrid](https://sendgrid.com/). 36 | - Add your sendgrid username and password to `EMAIL_HOST_USER` and `EMAIL_HOST_PASSWORD` in **.env** respectively. 37 | - Change the email and name in `DEFAULT_FROM_EMAIL` and `MANAGERS` in all *settings files* with your name and email. 38 | 39 | 4. **Amazon Web Services (AWS) setup**: 40 | Follow this guide to setup AWS in the project: [AWS Setup](notes/aws_setup.md). After settings up AWS, add all the required values to **.env** 41 | 42 | 5. **Heroku setup**: 43 | Follow this guide to setup Heroku in the project: [Heroku Setup](notes/heroku_setup.md). 44 | 45 | 6. Run the following commands 46 | `python manage.py makemigrations` 47 | `python manage.py migrate` 48 | `python manage.py collectstatic` 49 | 50 | 7. Now load the entries in **Company** model into the database 51 | `python manage.py loaddata market/fixtures/market.json` 52 | -------------------------------------------------------------------------------- /templates/home.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Home 10 | {% include 'base/css.html' %} 11 | {% include 'base/js.html' %} 12 | 13 | 14 | 15 | {% include 'base/navbar.html' with brand_name='Stock Bridge' %} 16 | 17 |
18 |
19 |

STOCK BRIDGE

20 |
21 |
22 | 23 |
24 |
25 |
26 |
27 |

28 | Keeping in view the real thrill of the market monotony, we are here with the Virtual Simulation of the same thrill 29 | and adrenaline rush through the event under the banner of MORPHOSIS. The virtual share market enables the participants 30 | to trade, buy, sell mortgage and showcase their rationality, their mettle against competitive decision making under 31 | pressure. The full time active virtual market will be a platform to showcase your inferential, pressure-handling 32 | capability. So, be a part of it to experience the Raising Spirit of Competitiveness and explore your ability of Decision 33 | Making. 34 |

35 |
36 |
37 |
38 |
39 | 44 |
45 |

46 | Disclaimer: Though we have tried our best to avoid any unfortunate circumstances, 47 | but if in case any discrepancies occur and we are unable to resume the game then the player who 48 | was holding the top position on the leaderboard at the time of that event will be declared 49 | as the winner. The morphosis team will not be responsible for any inconviniences whatsoever. 50 |

51 |
52 |
53 | 54 | {% include 'base/footer.html' %} 55 | 56 | 57 | -------------------------------------------------------------------------------- /stock_bridge/mixins.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timedelta 2 | 3 | from django.conf import settings 4 | from django.shortcuts import redirect 5 | from django.utils.decorators import method_decorator 6 | from django.utils.http import is_safe_url 7 | 8 | from .decorators import login_required_message_and_redirect 9 | from market.models import Company, CompanyCMPRecord 10 | from accounts.models import News 11 | 12 | 13 | class CountNewsMixin(object): 14 | """ Count current news amount for display in navbar """ 15 | 16 | def dispatch(self, request, *args, **kwargs): 17 | request.session['news'] = News.objects.filter(is_active=True).count() 18 | return super(CountNewsMixin, self).dispatch(request, *args, **kwargs) 19 | 20 | 21 | class AdminRequiredMixin(object): 22 | 23 | def dispatch(self, request, *args, **kwargs): 24 | if request.user.is_authenticated and request.user.is_superuser: 25 | return super(AdminRequiredMixin, self).dispatch(request, *args, **kwargs) 26 | return redirect(getattr(settings, 'LOGIN_URL_REDIRECT', '/')) 27 | 28 | 29 | class LoginRequiredMixin(object): 30 | """ This mixin ensures that only logged in users can access the page """ 31 | 32 | @method_decorator(login_required_message_and_redirect) 33 | def dispatch(self, request, *args, **kwargs): 34 | return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs) 35 | 36 | 37 | class AnonymousRequiredMixin(object): 38 | """ This mixin ensures that logged in users cannot access the page """ 39 | 40 | def dispatch(self, request, *args, **kwargs): 41 | if request.user.is_authenticated: 42 | return redirect(getattr(settings, 'LOGIN_URL_REDIRECT', '/')) 43 | return super(AnonymousRequiredMixin, self).dispatch(request, *args, **kwargs) 44 | 45 | 46 | class RequestFormAttachMixin(object): 47 | """ This method is overridden to send additional data from the view to the form. 48 | In function based view, this can be done as "form = LoginForm(request=request)" 49 | """ 50 | 51 | def get_form_kwargs(self): 52 | kwargs = super(RequestFormAttachMixin, self).get_form_kwargs() 53 | kwargs['request'] = self.request 54 | return kwargs 55 | 56 | 57 | class NextUrlMixin(object): 58 | default_next = '/' 59 | 60 | def get_next_url(self): 61 | request = self.request 62 | next_ = request.GET.get('next') 63 | next_post = request.POST.get('next') 64 | redirect_path = next_ or next_post or None 65 | if is_safe_url(redirect_path, request.get_host()): 66 | return redirect_path 67 | return self.default_next 68 | -------------------------------------------------------------------------------- /market/templates/market/admin_company_change.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% load humanize %} 4 | 5 | {% block base_head %} 6 | Admin Market 7 | {% endblock %} 8 | 9 | {% block content %} 10 | 11 | 32 | 33 |
34 |
35 |
36 |

{{ object.name }}

37 |

38 | Industry: {{ object.industry }}
39 | Cap Type: {{ object.get_cap }}
40 | Cap Size: ₹ {{ object.cap|intcomma }} 41 |

42 |
43 |
44 |
45 |
46 |
47 |

{{ object.code }}

48 | Total Stocks: {{ object.stocks_offered }}
49 | Stocks Remaining: {{ object.stocks_remaining }}
50 | Current Price per stock: ₹ {{ object.cmp }} 51 | {% if object.change >= 0 %} 52 | ({{ object.change }}%)
53 | {% elif object.change < 0 %} 54 | ({{ object.change }}%)
55 | {% endif %} 56 |
57 |
58 |
59 |
60 |
{% csrf_token %} 61 | {{ form.as_p }} 62 | 63 |
64 |
65 |
66 | 67 | {% endblock %} 68 | -------------------------------------------------------------------------------- /accounts/templates/accounts/profile.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load humanize %} 3 | 4 | {% block base_head %} 5 | Profile 6 | {% endblock %} 7 | 8 | {% block content %} 9 | 10 |
11 |
12 |
13 |

{{ object.full_name }}

14 |

{{ object.username }}

15 |

{{ object.email }}

16 |
17 |
18 |
19 |
20 |
21 |

Net Worth: ₹ {{ net_worth|intcomma }}

22 |
23 |
24 | {% if investments %} 25 |
26 |
27 |

Investment Record:

28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | {% for investment in investments %} 42 | 43 | 44 | 49 | 50 | 51 | 63 | 64 | {% endfor %} 65 | 66 |
S.noCompanyStocks PurchasedStocks AvailableCurrent Market Price
{{ forloop.counter }} 45 | 46 | {{ investment.company }} 47 | 48 | {{ investment.stocks }}{{ investment.company.stocks_remaining }} 52 | ₹ {{ investment.company.cmp }} 53 | {% if investment.company.change >= 0 %} 54 | 55 | ({{ investment.company.change }}%) 56 |
57 | {% elif investment.company.change < 0 %} 58 | 59 | ({{ investment.company.change }}%) 60 |
61 | {% endif %} 62 |
67 |
68 |
69 | {% endif %} 70 | 71 | {% endblock %} -------------------------------------------------------------------------------- /accounts/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.8 on 2018-05-04 08:22 3 | from __future__ import unicode_literals 4 | 5 | from decimal import Decimal 6 | from django.conf import settings 7 | from django.db import migrations, models 8 | import django.db.models.deletion 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | initial = True 14 | 15 | dependencies = [ 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='User', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('password', models.CharField(max_length=128, verbose_name='password')), 24 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 25 | ('username', models.CharField(max_length=120, unique=True)), 26 | ('email', models.EmailField(max_length=255, unique=True)), 27 | ('full_name', models.CharField(blank=True, max_length=255, null=True)), 28 | ('cash', models.DecimalField(decimal_places=2, default=Decimal('5000'), max_digits=20)), 29 | ('loan', models.DecimalField(decimal_places=2, default=Decimal('5000'), max_digits=20)), 30 | ('loan_count', models.IntegerField(default=1)), 31 | ('loan_count_absolute', models.IntegerField(default=1)), 32 | ('coeff_of_variation', models.DecimalField(decimal_places=2, default=0.0, max_digits=20)), 33 | ('is_active', models.BooleanField(default=True)), 34 | ('staff', models.BooleanField(default=False)), 35 | ('is_superuser', models.BooleanField(default=False)), 36 | ('timestamp', models.DateTimeField(auto_now_add=True)), 37 | ('updated', models.DateTimeField(auto_now=True)), 38 | ], 39 | options={ 40 | 'ordering': ['-cash', 'coeff_of_variation'], 41 | }, 42 | ), 43 | migrations.CreateModel( 44 | name='EmailActivation', 45 | fields=[ 46 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 47 | ('email', models.EmailField(max_length=254)), 48 | ('key', models.CharField(blank=True, max_length=120, null=True)), 49 | ('activated', models.BooleanField(default=False)), 50 | ('forced_expire', models.BooleanField(default=False)), 51 | ('expires', models.IntegerField(default=7)), 52 | ('timestamp', models.DateTimeField(auto_now_add=True)), 53 | ('update', models.DateTimeField(auto_now=True)), 54 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 55 | ], 56 | ), 57 | migrations.CreateModel( 58 | name='News', 59 | fields=[ 60 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 61 | ('title', models.CharField(max_length=120)), 62 | ('content', models.TextField()), 63 | ('is_active', models.BooleanField(default=True)), 64 | ('timestamp', models.DateTimeField(auto_now_add=True)), 65 | ('updated', models.DateTimeField(auto_now=True)), 66 | ], 67 | options={ 68 | 'ordering': ['-timestamp', '-updated'], 69 | }, 70 | ), 71 | ] 72 | -------------------------------------------------------------------------------- /templates/base/navbar.html: -------------------------------------------------------------------------------- 1 | {% load humanize %} 2 | {% url 'home' as home_url %} 3 | {% url 'login' as login_url %} 4 | {% url 'register' as register_url %} 5 | {% url 'logout' as logout_url %} 6 | {% url 'transaction_history' as transaction_history_url %} 7 | {% url 'leaderboard' as leaderboard_url %} 8 | {% url 'news' as news_url %} 9 | {% url 'account:loan' as loan_url %} 10 | {% url 'instructions' as instructions_url %} 11 | {% url 'profile' request.user.username as profile_url %} 12 | {% url 'market:company_select' as market_url %} 13 | 14 | -------------------------------------------------------------------------------- /market/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.8 on 2018-05-04 08:22 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | initial = True 13 | 14 | dependencies = [ 15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='Company', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('code', models.CharField(max_length=10, unique=True)), 24 | ('name', models.CharField(max_length=20, unique=True)), 25 | ('cap', models.DecimalField(decimal_places=2, default=0.0, max_digits=20)), 26 | ('cmp', models.DecimalField(decimal_places=2, default=0.0, max_digits=20)), 27 | ('change', models.DecimalField(decimal_places=2, default=0.0, max_digits=10)), 28 | ('stocks_offered', models.IntegerField(default=0)), 29 | ('stocks_remaining', models.IntegerField(default=models.IntegerField(default=0))), 30 | ('cap_type', models.CharField(blank=True, choices=[('small', 'Small Cap'), ('mid', 'Mid Cap'), ('large', 'Large Cap')], max_length=20, null=True)), 31 | ('industry', models.CharField(blank=True, max_length=120, null=True)), 32 | ('temp_stocks_bought', models.IntegerField(default=0)), 33 | ('temp_stocks_sold', models.IntegerField(default=0)), 34 | ('timestamp', models.DateTimeField(auto_now_add=True)), 35 | ('updated', models.DateTimeField(auto_now=True)), 36 | ], 37 | options={ 38 | 'ordering': ['cap_type', 'code'], 39 | }, 40 | ), 41 | migrations.CreateModel( 42 | name='CompanyCMPRecord', 43 | fields=[ 44 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 45 | ('cmp', models.DecimalField(decimal_places=2, default=0.0, max_digits=20)), 46 | ('timestamp', models.DateTimeField(auto_now_add=True)), 47 | ('updated', models.DateTimeField(auto_now=True)), 48 | ('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='market.Company')), 49 | ], 50 | options={ 51 | 'ordering': ['-timestamp'], 52 | }, 53 | ), 54 | migrations.CreateModel( 55 | name='InvestmentRecord', 56 | fields=[ 57 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 58 | ('stocks', models.IntegerField(default=0)), 59 | ('updated', models.DateTimeField(auto_now=True)), 60 | ('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='market.Company')), 61 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 62 | ], 63 | ), 64 | migrations.CreateModel( 65 | name='Transaction', 66 | fields=[ 67 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 68 | ('num_stocks', models.IntegerField(default=0)), 69 | ('price', models.DecimalField(decimal_places=2, default=0.0, max_digits=20)), 70 | ('mode', models.CharField(choices=[('buy', 'BUY'), ('sell', 'SELL')], max_length=10)), 71 | ('user_net_worth', models.DecimalField(decimal_places=2, default=0.0, max_digits=20)), 72 | ('timestamp', models.DateTimeField(auto_now_add=True)), 73 | ('updated', models.DateTimeField(auto_now=True)), 74 | ('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='market.Company')), 75 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 76 | ], 77 | options={ 78 | 'ordering': ['-timestamp'], 79 | }, 80 | ), 81 | migrations.AlterUniqueTogether( 82 | name='investmentrecord', 83 | unique_together=set([('user', 'company')]), 84 | ), 85 | ] 86 | -------------------------------------------------------------------------------- /market/templates/market/transaction_market.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% load humanize %} 4 | {% block base_head %} 5 | Transaction 6 | {% endblock %} 7 | 8 | {% block javascript %} 9 | 10 | 56 | 57 | {% endblock %} 58 | 59 | {% block content %} 60 | 61 | 82 | 83 |
84 |
85 |
86 |

{{ object.name }}

87 |

88 | Industry: {{ object.industry }}
89 | Cap Type: {{ object.get_cap }}
90 | Cap Size: ₹ {{ object.cap|intcomma }} 91 |

92 |
93 |
94 |
95 |
96 |
97 | 98 |
99 |
100 |

{{ object.code }}

101 | Total Stocks: {{ object.stocks_offered }}
102 | Stocks Remaining: {{ object.stocks_remaining }}
103 | Current Price per stock: ₹ {{ object.cmp }} 104 | {% if object.change >= 0 %} 105 | ({{ object.change }}%)
106 | {% elif object.change < 0 %} 107 | ({{ object.change }}%)
108 | {% endif %} 109 | Stocks Currently Owned: {{ stocks_owned }} 110 |
111 |
112 |
113 |
114 |

Balance Available: ₹ {{ request.user.cash }}


115 |
{% csrf_token %} 116 | {{ form.as_p }} 117 | 118 |
119 |
120 |
121 | 122 | {% endblock %} 123 | -------------------------------------------------------------------------------- /stock_bridge/settings/production.py: -------------------------------------------------------------------------------- 1 | import os 2 | import dj_database_url 3 | from decimal import Decimal 4 | from decouple import config 5 | from datetime import datetime 6 | 7 | 8 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 9 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 10 | 11 | 12 | # Quick-start development settings - unsuitable for production 13 | # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ 14 | 15 | # SECURITY WARNING: keep the secret key used in production secret! 16 | SECRET_KEY = config('SECRET_KEY') 17 | 18 | # SECURITY WARNING: don't run with debug turned on in production! 19 | DEBUG = False 20 | 21 | ALLOWED_HOSTS = ['.herokuapp.com', '.morphosis.org.in'] 22 | 23 | EMAIL_HOST = 'smtp.sendgrid.net' 24 | EMAIL_HOST_USER = config('EMAIL_HOST_USER') 25 | EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD') 26 | EMAIL_PORT = 587 27 | EMAIL_USE_TLS = True 28 | BASE_URL = 'stock-bridge.herokuapp.com' 29 | 30 | DEFAULT_ACTIVATION_DAYS = 7 31 | 32 | # This allows sendgrid to send emails to the specified id whenever server error occurs. 33 | DEFAULT_FROM_EMAIL = 'Morphosis Stock Bridge ' 34 | MANAGERS = ( 35 | ('Morphosis NITMZ', 'morphosis@nitmz.ac.in'), 36 | ) 37 | ADMINS = MANAGERS 38 | 39 | # Bank Data 40 | DEFAULT_LOAN_AMOUNT = Decimal(5000.00) 41 | MAX_LOAN_ISSUE = 2 42 | RATE_OF_INTEREST = Decimal(0.15) # 15% 43 | TAX_RATE = Decimal(0.40) # 40% 44 | 45 | # Global settings 46 | START_TIME = datetime(2018, 5, 4, 19, 30, 0) 47 | STOP_TIME = datetime(2018, 5, 5, 2, 00, 0) 48 | 49 | 50 | # Application definition 51 | 52 | INSTALLED_APPS = [ 53 | 'django.contrib.admin', 54 | 'django.contrib.auth', 55 | 'django.contrib.contenttypes', 56 | 'django.contrib.sessions', 57 | 'django.contrib.messages', 58 | 'django.contrib.staticfiles', 59 | 'django.contrib.humanize', 60 | 61 | # 3rd party 62 | 'rest_framework', 63 | 'storages', 64 | 65 | # custom apps 66 | 'accounts', 67 | 'market' 68 | ] 69 | 70 | 71 | # Replace the built-in values 72 | AUTH_USER_MODEL = 'accounts.User' 73 | LOGIN_URL = '/login/' 74 | LOGIN_URL_REDIRECT = '/' 75 | LOGOUT_URL = '/logout/' 76 | 77 | MIDDLEWARE = [ 78 | 'django.middleware.security.SecurityMiddleware', 79 | 'django.contrib.sessions.middleware.SessionMiddleware', 80 | 'django.middleware.common.CommonMiddleware', 81 | 'django.middleware.csrf.CsrfViewMiddleware', 82 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 83 | 'django.contrib.messages.middleware.MessageMiddleware', 84 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 85 | ] 86 | 87 | ROOT_URLCONF = 'stock_bridge.urls' 88 | LOGOUT_REDIRECT_URL = '/login/' 89 | 90 | TEMPLATES = [ 91 | { 92 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 93 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 94 | 'APP_DIRS': True, 95 | 'OPTIONS': { 96 | 'context_processors': [ 97 | 'django.template.context_processors.debug', 98 | 'django.template.context_processors.request', 99 | 'django.contrib.auth.context_processors.auth', 100 | 'django.contrib.messages.context_processors.messages', 101 | ], 102 | }, 103 | }, 104 | ] 105 | 106 | WSGI_APPLICATION = 'stock_bridge.wsgi.application' 107 | 108 | 109 | # Database 110 | 111 | DATABASES = { 112 | 'default': dj_database_url.config( 113 | default=config('DATABASE_URL') 114 | ) 115 | } 116 | 117 | 118 | # Password validation 119 | # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators 120 | 121 | AUTH_PASSWORD_VALIDATORS = [ 122 | { 123 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 124 | }, 125 | { 126 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 127 | }, 128 | { 129 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 130 | }, 131 | { 132 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 133 | }, 134 | ] 135 | 136 | 137 | # Internationalization 138 | # https://docs.djangoproject.com/en/1.11/topics/i18n/ 139 | 140 | LANGUAGE_CODE = 'en-us' 141 | 142 | TIME_ZONE = 'Asia/Kolkata' 143 | 144 | USE_I18N = True 145 | 146 | USE_L10N = True 147 | 148 | USE_TZ = True 149 | 150 | 151 | # Static files (CSS, JavaScript, Images) 152 | # https://docs.djangoproject.com/en/1.11/howto/static-files/ 153 | 154 | STATIC_URL = '/static/' 155 | 156 | STATICFILES_DIRS = [ 157 | os.path.join(BASE_DIR, "static_files") 158 | ] 159 | 160 | STATIC_ROOT = os.path.join(BASE_DIR, 'static_cdn', 'static_root') 161 | 162 | MEDIA_URL = '/media/' 163 | MEDIA_ROOT = os.path.join(BASE_DIR, 'static_cdn', 'media_root') 164 | 165 | from stock_bridge.aws.conf import * 166 | 167 | 168 | # SSL/TLS settings for https security 169 | CORS_REPLACE_HTTPS_REFERER = True 170 | HOST_SCHEME = "https://" 171 | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') 172 | SECURE_SSL_REDIRECT = True 173 | SESSION_COOKIE_SECURE = True 174 | CSRF_COOKIE_SECURE = True 175 | SECURE_HSTS_INCLUDE_SUBDOMAINS = True 176 | SECURE_HSTS_SECONDS = 1000000 177 | SECURE_FRAME_DENY = True 178 | 179 | -------------------------------------------------------------------------------- /stock_bridge/settings/base.py: -------------------------------------------------------------------------------- 1 | import os 2 | from decimal import Decimal 3 | from decouple import config 4 | from datetime import datetime 5 | 6 | 7 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 8 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 9 | 10 | 11 | # Quick-start development settings - unsuitable for production 12 | # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ 13 | 14 | # SECURITY WARNING: keep the secret key used in production secret! 15 | SECRET_KEY = config('SECRET_KEY') 16 | 17 | # SECURITY WARNING: don't run with debug turned on in production! 18 | DEBUG = True 19 | 20 | ALLOWED_HOSTS = ['0.0.0.0', '127.0.0.1', 'localhost'] 21 | 22 | EMAIL_HOST = 'smtp.sendgrid.net' 23 | EMAIL_HOST_USER = config('EMAIL_HOST_USER') 24 | EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD') 25 | EMAIL_PORT = 587 26 | EMAIL_USE_TLS = True 27 | BASE_URL = '127.0.0.1:8000' 28 | 29 | DEFAULT_ACTIVATION_DAYS = 7 30 | 31 | # This allows sendgrid to send emails to the specified id whenever server error occurs. 32 | DEFAULT_FROM_EMAIL = 'Morphosis Stock Bridge ' 33 | MANAGERS = ( 34 | ('Morphosis NITMZ', 'morphosis@nitmz.ac.in'), 35 | ) 36 | ADMINS = MANAGERS 37 | 38 | # Bank Data 39 | DEFAULT_LOAN_AMOUNT = Decimal(5000.00) 40 | MAX_LOAN_ISSUE = 2 41 | RATE_OF_INTEREST = Decimal(0.15) # 15% 42 | TAX_RATE = Decimal(0.40) # 40% 43 | 44 | # Global settings 45 | START_TIME = datetime(2018, 5, 4, 19, 30, 0) 46 | STOP_TIME = datetime(2018, 5, 5, 2, 00, 0) 47 | 48 | 49 | # Application definition 50 | 51 | INSTALLED_APPS = [ 52 | 'django.contrib.admin', 53 | 'django.contrib.auth', 54 | 'django.contrib.contenttypes', 55 | 'django.contrib.sessions', 56 | 'django.contrib.messages', 57 | 'django.contrib.staticfiles', 58 | 'django.contrib.humanize', 59 | 60 | # 3rd party 61 | 'rest_framework', 62 | 'storages', 63 | 64 | # custom apps 65 | 'accounts', 66 | 'market' 67 | ] 68 | 69 | 70 | # Replace the built-in values 71 | AUTH_USER_MODEL = 'accounts.User' 72 | LOGIN_URL = '/login/' 73 | LOGIN_URL_REDIRECT = '/' 74 | LOGOUT_URL = '/logout/' 75 | 76 | MIDDLEWARE = [ 77 | 'django.middleware.security.SecurityMiddleware', 78 | 'django.contrib.sessions.middleware.SessionMiddleware', 79 | 'django.middleware.common.CommonMiddleware', 80 | 'django.middleware.csrf.CsrfViewMiddleware', 81 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 82 | 'django.contrib.messages.middleware.MessageMiddleware', 83 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 84 | ] 85 | 86 | ROOT_URLCONF = 'stock_bridge.urls' 87 | LOGOUT_REDIRECT_URL = '/login/' 88 | 89 | TEMPLATES = [ 90 | { 91 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 92 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 93 | 'APP_DIRS': True, 94 | 'OPTIONS': { 95 | 'context_processors': [ 96 | 'django.template.context_processors.debug', 97 | 'django.template.context_processors.request', 98 | 'django.contrib.auth.context_processors.auth', 99 | 'django.contrib.messages.context_processors.messages', 100 | ], 101 | }, 102 | }, 103 | ] 104 | 105 | WSGI_APPLICATION = 'stock_bridge.wsgi.application' 106 | 107 | 108 | # Database 109 | # https://docs.djangoproject.com/en/1.11/ref/settings/#databases 110 | 111 | DATABASES = { 112 | 'default': { 113 | 'ENGINE': 'django.db.backends.sqlite3', 114 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 115 | } 116 | } 117 | 118 | 119 | # Password validation 120 | # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators 121 | 122 | AUTH_PASSWORD_VALIDATORS = [ 123 | { 124 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 125 | }, 126 | { 127 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 128 | }, 129 | { 130 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 131 | }, 132 | { 133 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 134 | }, 135 | ] 136 | 137 | 138 | # Internationalization 139 | # https://docs.djangoproject.com/en/1.11/topics/i18n/ 140 | 141 | LANGUAGE_CODE = 'en-us' 142 | 143 | TIME_ZONE = 'Asia/Kolkata' 144 | 145 | USE_I18N = True 146 | 147 | USE_L10N = True 148 | 149 | USE_TZ = True 150 | 151 | 152 | # Static files (CSS, JavaScript, Images) 153 | # https://docs.djangoproject.com/en/1.11/howto/static-files/ 154 | 155 | STATIC_URL = '/static/' 156 | 157 | STATICFILES_DIRS = [ 158 | os.path.join(BASE_DIR, "static_files") 159 | ] 160 | 161 | STATIC_ROOT = os.path.join(BASE_DIR, 'static_cdn', 'static_root') 162 | 163 | MEDIA_URL = '/media/' 164 | MEDIA_ROOT = os.path.join(BASE_DIR, 'static_cdn', 'media_root') 165 | 166 | # from stock_bridge.aws.conf import * 167 | 168 | 169 | # removing SSL/TLS settings for local environment 170 | CORS_REPLACE_HTTPS_REFERER = False 171 | HOST_SCHEME = "http://" 172 | SECURE_PROXY_SSL_HEADER = None 173 | SECURE_SSL_REDIRECT = False 174 | SESSION_COOKIE_SECURE = False 175 | CSRF_COOKIE_SECURE = False 176 | SECURE_HSTS_SECONDS = None 177 | SECURE_HSTS_INCLUDE_SUBDOMAINS = False 178 | SECURE_FRAME_DENY = False 179 | -------------------------------------------------------------------------------- /stock_bridge/settings/local.py: -------------------------------------------------------------------------------- 1 | import os 2 | from decimal import Decimal 3 | from decouple import config 4 | from datetime import datetime 5 | 6 | 7 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 8 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 9 | 10 | 11 | # Quick-start development settings - unsuitable for production 12 | # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ 13 | 14 | # SECURITY WARNING: keep the secret key used in production secret! 15 | SECRET_KEY = config('SECRET_KEY') 16 | 17 | # SECURITY WARNING: don't run with debug turned on in production! 18 | DEBUG = True 19 | 20 | ALLOWED_HOSTS = ['0.0.0.0', '127.0.0.1', 'localhost'] 21 | 22 | EMAIL_HOST = 'smtp.sendgrid.net' 23 | EMAIL_HOST_USER = config('EMAIL_HOST_USER') 24 | EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD') 25 | EMAIL_PORT = 587 26 | EMAIL_USE_TLS = True 27 | BASE_URL = '127.0.0.1:8000' 28 | 29 | DEFAULT_ACTIVATION_DAYS = 7 30 | 31 | # This allows sendgrid to send emails to the specified id whenever server error occurs. 32 | DEFAULT_FROM_EMAIL = 'Morphosis Stock Bridge ' 33 | MANAGERS = ( 34 | ('Morphosis NITMZ', 'morphosis@nitmz.ac.in'), 35 | ) 36 | ADMINS = MANAGERS 37 | 38 | # Bank Data 39 | DEFAULT_LOAN_AMOUNT = Decimal(5000.00) 40 | MAX_LOAN_ISSUE = 2 41 | RATE_OF_INTEREST = Decimal(0.15) # 15% 42 | TAX_RATE = Decimal(0.40) # 40% 43 | 44 | # Global settings 45 | START_TIME = datetime(2018, 5, 4, 19, 30, 0) 46 | STOP_TIME = datetime(2018, 5, 5, 2, 00, 0) 47 | 48 | 49 | # Application definition 50 | 51 | INSTALLED_APPS = [ 52 | 'django.contrib.admin', 53 | 'django.contrib.auth', 54 | 'django.contrib.contenttypes', 55 | 'django.contrib.sessions', 56 | 'django.contrib.messages', 57 | 'django.contrib.staticfiles', 58 | 'django.contrib.humanize', 59 | 60 | # 3rd party 61 | 'rest_framework', 62 | 'storages', 63 | 64 | # custom apps 65 | 'accounts', 66 | 'market' 67 | ] 68 | 69 | 70 | # Replace the built-in values 71 | AUTH_USER_MODEL = 'accounts.User' 72 | LOGIN_URL = '/login/' 73 | LOGIN_URL_REDIRECT = '/' 74 | LOGOUT_URL = '/logout/' 75 | 76 | MIDDLEWARE = [ 77 | 'django.middleware.security.SecurityMiddleware', 78 | 'django.contrib.sessions.middleware.SessionMiddleware', 79 | 'django.middleware.common.CommonMiddleware', 80 | 'django.middleware.csrf.CsrfViewMiddleware', 81 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 82 | 'django.contrib.messages.middleware.MessageMiddleware', 83 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 84 | ] 85 | 86 | ROOT_URLCONF = 'stock_bridge.urls' 87 | LOGOUT_REDIRECT_URL = '/login/' 88 | 89 | TEMPLATES = [ 90 | { 91 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 92 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 93 | 'APP_DIRS': True, 94 | 'OPTIONS': { 95 | 'context_processors': [ 96 | 'django.template.context_processors.debug', 97 | 'django.template.context_processors.request', 98 | 'django.contrib.auth.context_processors.auth', 99 | 'django.contrib.messages.context_processors.messages', 100 | ], 101 | }, 102 | }, 103 | ] 104 | 105 | WSGI_APPLICATION = 'stock_bridge.wsgi.application' 106 | 107 | 108 | # Database 109 | # https://docs.djangoproject.com/en/1.11/ref/settings/#databases 110 | 111 | DATABASES = { 112 | 'default': { 113 | 'ENGINE': 'django.db.backends.sqlite3', 114 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 115 | } 116 | } 117 | 118 | 119 | # Password validation 120 | # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators 121 | 122 | AUTH_PASSWORD_VALIDATORS = [ 123 | { 124 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 125 | }, 126 | { 127 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 128 | }, 129 | { 130 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 131 | }, 132 | { 133 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 134 | }, 135 | ] 136 | 137 | 138 | # Internationalization 139 | # https://docs.djangoproject.com/en/1.11/topics/i18n/ 140 | 141 | LANGUAGE_CODE = 'en-us' 142 | 143 | TIME_ZONE = 'Asia/Kolkata' 144 | 145 | USE_I18N = True 146 | 147 | USE_L10N = True 148 | 149 | USE_TZ = True 150 | 151 | 152 | # Static files (CSS, JavaScript, Images) 153 | # https://docs.djangoproject.com/en/1.11/howto/static-files/ 154 | 155 | STATIC_URL = '/static/' 156 | 157 | STATICFILES_DIRS = [ 158 | os.path.join(BASE_DIR, "static_files") 159 | ] 160 | 161 | STATIC_ROOT = os.path.join(BASE_DIR, 'static_cdn', 'static_root') 162 | 163 | MEDIA_URL = '/media/' 164 | MEDIA_ROOT = os.path.join(BASE_DIR, 'static_cdn', 'media_root') 165 | 166 | # from stock_bridge.aws.conf import * 167 | 168 | 169 | # removing SSL/TLS settings for local environment 170 | CORS_REPLACE_HTTPS_REFERER = False 171 | HOST_SCHEME = "http://" 172 | SECURE_PROXY_SSL_HEADER = None 173 | SECURE_SSL_REDIRECT = False 174 | SESSION_COOKIE_SECURE = False 175 | CSRF_COOKIE_SECURE = False 176 | SECURE_HSTS_SECONDS = None 177 | SECURE_HSTS_INCLUDE_SUBDOMAINS = False 178 | SECURE_FRAME_DENY = False 179 | -------------------------------------------------------------------------------- /accounts/forms.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from django import forms 4 | from django.contrib.auth import authenticate, login, get_user_model 5 | from django.contrib.auth.forms import ReadOnlyPasswordHashField 6 | from django.urls import reverse 7 | from django.utils.safestring import mark_safe 8 | 9 | from .models import EmailActivation 10 | 11 | 12 | User = get_user_model() 13 | 14 | 15 | class ReactivateEmailForm(forms.Form): 16 | email = forms.EmailField() 17 | 18 | def clean_email(self): 19 | email = self.cleaned_data.get('email') 20 | qs = EmailActivation.objects.email_exists(email) 21 | if not qs.exists(): 22 | register_link = reverse('register') 23 | msg = """This email does not exist. 24 | Would you like to register?""".format(link=register_link) 25 | raise forms.ValidationError(mark_safe(msg)) 26 | return email 27 | 28 | 29 | class UserAdminCreationForm(forms.ModelForm): 30 | password1 = forms.CharField(label='Password', widget=forms.PasswordInput) 31 | password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput) 32 | 33 | class Meta: 34 | model = User 35 | fields = ('username', 'email', 'full_name') 36 | 37 | def clean_password2(self): 38 | # Check that the two password entries match 39 | password1 = self.cleaned_data.get("password1") 40 | password2 = self.cleaned_data.get("password2") 41 | if password1 and password2 and password1 != password2: 42 | raise forms.ValidationError("Passwords don't match") 43 | return password2 44 | 45 | def save(self, commit=True): 46 | # Save the provided password in hashed format 47 | user = super(UserAdminCreationForm, self).save(commit=False) 48 | user.set_password(self.cleaned_data["password1"]) 49 | if commit: 50 | user.save() 51 | return user 52 | 53 | 54 | class UserAdminChangeForm(forms.ModelForm): 55 | password = ReadOnlyPasswordHashField() 56 | 57 | class Meta: 58 | model = User 59 | fields = ( 60 | 'username', 'email', 'full_name', 'password', 'is_active', 61 | 'cash', 'loan', 'coeff_of_variation', 'is_superuser' 62 | ) 63 | 64 | def clean_password(self): 65 | return self.initial["password"] 66 | 67 | 68 | class LoginForm(forms.Form): 69 | username = forms.CharField(label='Username', widget=forms.TextInput( 70 | attrs={'class': 'form-control my-2', 'placeholder': 'Username'} 71 | )) 72 | password = forms.CharField(widget=forms.PasswordInput( 73 | attrs={'class': 'form-control my-2', 'placeholder': 'Password'} 74 | )) 75 | 76 | def __init__(self, request, *args, **kwargs): 77 | self.request = request 78 | super(LoginForm, self).__init__(*args, **kwargs) 79 | 80 | def clean(self): # This clean() method gets the entire form's data 81 | """ 82 | With this function, the form itself handles the entire login process and only if the login 83 | is successful, it sends the data to the respective view. 84 | """ 85 | request = self.request 86 | username = self.cleaned_data.get('username') 87 | password = self.cleaned_data.get('password') 88 | 89 | response = { 90 | 'success': False, 91 | 'message': 'Login failed.' 92 | } 93 | 94 | user_qs = User.objects.filter(username=username, is_active=False) 95 | if user_qs.exists(): 96 | # email is registered but not active 97 | email = user_qs.first().email 98 | link = reverse('account:resend-activation') 99 | reconfirm_msg = """Go to resend confirmation email. 100 | """.format(resend_link=link) 101 | is_email_confirmable = EmailActivation.objects.filter(email=email).confirmable().exists() 102 | email_activation_exists = EmailActivation.objects.email_exists(email).exists() 103 | if is_email_confirmable: 104 | msg1 = 'Please check your email to confirm your account or ' + reconfirm_msg.lower() 105 | response['message'] = msg1 106 | # raise forms.ValidationError(mark_safe(msg1)) 107 | elif email_activation_exists: 108 | msg2 = 'Email not confirmed. ' + reconfirm_msg 109 | response['message'] = msg2 110 | # raise forms.ValidationError(mark_safe(msg2)) 111 | else: 112 | response['message'] = 'This user is inactive.' 113 | # raise forms.ValidationError('This user is inactive.') 114 | return response 115 | 116 | user = authenticate(request, username=username, password=password) 117 | # If the is_active field is false, then the authenticate() method by default returns None 118 | if user is None: 119 | response['message'] = 'The username or the password is incorrect! Please try again.' 120 | # raise forms.ValidationError('Invalid credentials') 121 | return response 122 | login(request, user) 123 | response['success'] = True 124 | self.user = user 125 | return response 126 | 127 | 128 | class RegisterForm(forms.ModelForm): 129 | username = forms.CharField(label='Username', widget=forms.TextInput( 130 | attrs={'class': 'form-control my-2', 'placeholder': 'Username'} 131 | )) 132 | full_name = forms.CharField(label='Full Name', widget=forms.TextInput( 133 | attrs={'class': 'form-control my-2', 'placeholder': 'Full Name'} 134 | )) 135 | email = forms.EmailField(label='Email', widget=forms.EmailInput( 136 | attrs={'class': 'form-control my-2', 'placeholder': 'Email'} 137 | )) 138 | password1 = forms.CharField(label='Password', widget=forms.PasswordInput( 139 | attrs={'class': 'form-control my-2', 'placeholder': 'Password'} 140 | )) 141 | password2 = forms.CharField(label='Confirm Password', widget=forms.PasswordInput( 142 | attrs={'class': 'form-control my-2', 'placeholder': 'Password'} 143 | )) 144 | 145 | class Meta: 146 | model = User 147 | fields = ('username', 'full_name', 'email') 148 | 149 | def clean_password2(self): 150 | """ Check that the two password entries match """ 151 | password1 = self.cleaned_data.get("password1") 152 | password2 = self.cleaned_data.get("password2") 153 | if password1 and password2 and password1 != password2: 154 | raise forms.ValidationError("Passwords don't match") 155 | return password2 156 | 157 | def clean_username(self): 158 | username = self.cleaned_data.get('username') 159 | if not re.match(r'^[\w]+$', username): # Username must contain only alphanumeric characters 160 | raise forms.ValidationError('username can contain only alphabets and numbers') 161 | return username 162 | 163 | def save(self, commit=True): 164 | """ Save the provided password in hashed format """ 165 | user = super(RegisterForm, self).save(commit=False) 166 | user.set_password(self.cleaned_data["password1"]) 167 | user.is_active = False 168 | if commit: 169 | user.save() 170 | return user 171 | 172 | -------------------------------------------------------------------------------- /market/views.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from decimal import Decimal 3 | 4 | from django.conf import settings 5 | from django.contrib.auth import get_user_model 6 | from django.shortcuts import render, redirect 7 | from django.http import HttpResponseRedirect, HttpResponse 8 | from django.views.generic import View, ListView 9 | from django.urls import reverse 10 | from django.contrib import messages 11 | from django.contrib.auth.decorators import login_required 12 | from django.utils import timezone 13 | from django.utils.timezone import localtime 14 | 15 | from rest_framework.views import APIView 16 | from rest_framework.response import Response 17 | 18 | from .models import Company, CompanyCMPRecord, InvestmentRecord, Transaction 19 | from .forms import StockTransactionForm, CompanyChangeForm 20 | from stock_bridge.mixins import LoginRequiredMixin, CountNewsMixin, AdminRequiredMixin 21 | 22 | 23 | User = get_user_model() 24 | START_TIME = timezone.make_aware(getattr(settings, 'START_TIME')) 25 | STOP_TIME = timezone.make_aware(getattr(settings, 'STOP_TIME')) 26 | 27 | 28 | @login_required 29 | def deduct_tax(request): 30 | """ Deduct income tax """ 31 | if request.user.is_superuser: 32 | for user in User.objects.all(): 33 | tax = user.cash * Decimal(0.4) 34 | user.cash -= tax 35 | user.save() 36 | return HttpResponse('success') 37 | return redirect('/') 38 | 39 | 40 | @login_required 41 | def update_market(request): 42 | """ Update company's cmp after applying formula """ 43 | if request.user.is_superuser: 44 | # update company cmp data 45 | company_qs = Company.objects.all() 46 | for company in company_qs: 47 | company.update_cmp() 48 | obj = CompanyCMPRecord.objects.create(company=company, cmp=company.cmp) 49 | return HttpResponse('cmp updated') 50 | return redirect('/') 51 | 52 | 53 | class CompanyAdminCompanyUpdateView(AdminRequiredMixin, View): 54 | """ View for admin to change company's CMP """ 55 | 56 | def get(self, request, *args, **kwargs): 57 | company = Company.objects.get(code=kwargs.get('code')) 58 | return render(request, 'market/admin_company_change.html', { 59 | 'object': company, 60 | 'company_list': Company.objects.all(), 61 | 'form': CompanyChangeForm() 62 | }) 63 | 64 | def post(self, request, *args, **kwargs): 65 | company = Company.objects.get(code=kwargs.get('code')) 66 | price = request.POST.get('price') 67 | old_price = company.cmp 68 | company.cmp = Decimal(int(price)) 69 | company.save() 70 | company.calculate_change(old_price) 71 | print('price', int(price)) 72 | url = reverse('market:admin', kwargs={'code': company.code}) 73 | return HttpResponseRedirect(url) 74 | 75 | 76 | class CompanyCMPCreateView(View): 77 | 78 | def get(self, request, *args, **kwargs): 79 | for company in Company.objects.all(): 80 | obj = CompanyCMPRecord.objects.create(company=company, cmp=company.cmp) 81 | return HttpResponse('success') 82 | 83 | 84 | class CompanySelectionView(LoginRequiredMixin, CountNewsMixin, View): 85 | 86 | def get(self, request, *args, **kwargs): 87 | return render(request, 'market/select_company.html', { 88 | 'object_list': Company.objects.all() 89 | }) 90 | 91 | 92 | class CompanyCMPChartData(APIView): # used django rest framework 93 | authentication_classes = [] 94 | permission_classes = [] 95 | 96 | def get(self, request, format=None, *args, **kwargs): 97 | qs = CompanyCMPRecord.objects.filter(company__code=kwargs.get('code')) 98 | if qs.count() > 15: 99 | qs = qs[:15] 100 | qs = reversed(qs) # reverse timestamp sorting i.e. latest data should be in front 101 | labels = [] 102 | cmp_data = [] 103 | for cmp_record in qs: 104 | labels.append(localtime(cmp_record.timestamp).strftime('%H:%M')) 105 | cmp_data.append(cmp_record.cmp) 106 | current_cmp = Company.objects.get(code=kwargs.get('code')).cmp 107 | if cmp_data[-1] != current_cmp: 108 | labels.append(timezone.make_aware(datetime.now()).strftime('%H:%M')) 109 | cmp_data.append(current_cmp) 110 | data = { 111 | "labels": labels, 112 | "cmp_data": cmp_data, 113 | } 114 | return Response(data) 115 | 116 | 117 | class CompanyTransactionView(LoginRequiredMixin, CountNewsMixin, View): 118 | 119 | def get(self, request, *args, **kwargs): 120 | company = Company.objects.get(code=kwargs.get('code')) 121 | obj, created = InvestmentRecord.objects.get_or_create(user=request.user, company=company) 122 | stocks_owned = obj.stocks 123 | return render(request, 'market/transaction_market.html', { 124 | 'object': company, 125 | 'company_list': Company.objects.all(), 126 | 'stocks_owned': stocks_owned, 127 | 'form': StockTransactionForm() 128 | }) 129 | 130 | def post(self, request, *args, **kwargs): 131 | company = Company.objects.get(code=kwargs.get('code')) 132 | current_time = timezone.make_aware(datetime.now()) 133 | if current_time >= START_TIME and current_time <= STOP_TIME: 134 | user = request.user 135 | mode = request.POST.get('mode') 136 | quantity = int(request.POST.get('quantity')) 137 | price = company.cmp 138 | investment_obj, obj_created = InvestmentRecord.objects.get_or_create(user=user, company=company) 139 | if quantity > 0: 140 | if mode == 'buy': 141 | purchase_amount = Decimal(quantity) * price 142 | if user.cash >= purchase_amount: 143 | if company.stocks_remaining >= quantity: 144 | # user.buy_stocks(quantity, price) 145 | # company.user_buy_stocks(quantity) 146 | # investment_obj.add_stocks(quantity) 147 | obj = Transaction.objects.create( 148 | user=user, 149 | company=company, 150 | num_stocks=quantity, 151 | price=price, 152 | mode=mode, 153 | user_net_worth=InvestmentRecord.objects.calculate_net_worth(user) 154 | ) 155 | messages.success(request, 'Transaction Complete!') 156 | else: 157 | messages.error(request, 'The company does not have that many stocks left!') 158 | else: 159 | messages.error(request, 'Insufficient Balance for this transaction!') 160 | elif mode == 'sell': 161 | if quantity <= investment_obj.stocks and quantity <= company.stocks_offered: 162 | # user.sell_stocks(quantity, price) 163 | # company.user_sell_stocks(quantity) 164 | # investment_obj.reduce_stocks(quantity) 165 | obj = Transaction.objects.create( 166 | user=user, 167 | company=company, 168 | num_stocks=quantity, 169 | price=price, 170 | mode=mode, 171 | user_net_worth=InvestmentRecord.objects.calculate_net_worth(user) 172 | ) 173 | messages.success(request, 'Transaction Complete!') 174 | else: 175 | messages.error(request, 'Please enter a valid quantity!') 176 | else: 177 | messages.error(request, 'Please enter a valid mode!') 178 | else: 179 | messages.error(request, 'The quantity cannot be negative!') 180 | else: 181 | # msg = 'The market will be live from {start} to {stop}'.format( 182 | # start=START_TIME.strftime('%H:%M'), 183 | # stop=STOP_TIME.strftime('%H:%M') 184 | # ) 185 | msg = 'The market is closed!' 186 | messages.info(request, msg) 187 | url = reverse('market:transaction', kwargs={'code': company.code}) 188 | return HttpResponseRedirect(url) 189 | 190 | 191 | class UserTransactionHistoryView(LoginRequiredMixin, CountNewsMixin, ListView): 192 | template_name = 'market/user_transaction_history.html' 193 | 194 | def get_queryset(self, *args, **kwargs): 195 | return Transaction.objects.get_by_user(user=self.request.user) 196 | -------------------------------------------------------------------------------- /accounts/views.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from django.conf import settings 4 | from django.shortcuts import render, redirect 5 | from django.http import Http404, HttpResponse 6 | from django.contrib import messages 7 | from django.contrib.auth import get_user_model 8 | from django.contrib.auth.decorators import login_required 9 | from django.views.generic import ListView, DetailView, FormView, CreateView, View 10 | from django.views.generic.edit import FormMixin 11 | from django.utils.safestring import mark_safe 12 | from django.utils import timezone 13 | from django.utils.decorators import method_decorator 14 | from django.urls import reverse 15 | 16 | from .forms import LoginForm, RegisterForm, ReactivateEmailForm 17 | from .models import EmailActivation, News 18 | from stock_bridge.mixins import ( 19 | AnonymousRequiredMixin, 20 | RequestFormAttachMixin, 21 | NextUrlMixin, 22 | LoginRequiredMixin, 23 | CountNewsMixin 24 | ) 25 | from market.models import InvestmentRecord 26 | 27 | 28 | User = get_user_model() 29 | 30 | START_TIME = timezone.make_aware(getattr(settings, 'START_TIME')) 31 | STOP_TIME = timezone.make_aware(getattr(settings, 'STOP_TIME')) 32 | 33 | 34 | @login_required 35 | def cancel_loan(request): 36 | """ Deduct entire loan amount from user's balance """ 37 | if request.user.is_superuser: 38 | for user in User.objects.all(): 39 | user.cancel_loan() 40 | return HttpResponse('Loan Deducted', status=200) 41 | return redirect('home') 42 | 43 | 44 | @login_required 45 | def deduct_interest(request): 46 | """ Deduct interest from user's balance """ 47 | if request.user.is_superuser: 48 | for user in User.objects.all(): 49 | user.deduct_interest() 50 | return HttpResponse('Interest Deducted', status=200) 51 | return redirect('home') 52 | 53 | 54 | class NewsView(LoginRequiredMixin, CountNewsMixin, ListView): 55 | template_name = 'accounts/news.html' 56 | queryset = News.objects.filter(is_active=True) 57 | 58 | 59 | class LoanView(LoginRequiredMixin, CountNewsMixin, View): 60 | 61 | def get(self, request, *args, **kwargs): 62 | return render(request, 'accounts/loan.html', { 63 | 'user': request.user 64 | }) 65 | 66 | def post(self, request, *args, **kwargs): 67 | current_time = timezone.make_aware(datetime.now()) 68 | if current_time >= START_TIME and current_time <= STOP_TIME: # transaction has to be within game time 69 | mode = request.POST.get('mode') 70 | user = request.user 71 | if mode == 'issue': 72 | if user.issue_loan(): 73 | messages.success(request, 'Loan has been issued.') 74 | else: 75 | messages.error(request, 'You can issue loan only 1 time!') 76 | elif mode == 'pay': 77 | if user.pay_installment(): 78 | messages.success(request, 'Installment paid!') 79 | else: 80 | messages.error( 81 | request, 82 | 'Minimum installment amount has to be INR 5,000 and you should have sufficient balance.' 83 | ) 84 | else: 85 | # msg = 'The market will be live from {start} to {stop}'.format( 86 | # start=START_TIME.strftime('%H:%M'), 87 | # stop=STOP_TIME.strftime('%H:%M') 88 | # ) 89 | msg = 'The market is closed!' 90 | messages.info(request, msg) 91 | return redirect('account:loan') 92 | 93 | 94 | class ProfileView(LoginRequiredMixin, CountNewsMixin, DetailView): 95 | template_name = 'accounts/profile.html' 96 | 97 | def dispatch(self, request, *args, **kwargs): 98 | # only the user himself can view his own profile 99 | if request.user.username != kwargs.get('username'): 100 | return redirect('/') 101 | return super(ProfileView, self).dispatch(request, *args, **kwargs) 102 | 103 | def get_object(self, *args, **kwargs): 104 | username = self.kwargs.get('username') 105 | instance = User.objects.filter(username=username).first() 106 | if instance is None: 107 | raise Http404('User not found') 108 | return instance 109 | 110 | def get_context_data(self, *args, **kwargs): 111 | context = super(ProfileView, self).get_context_data(*args, **kwargs) 112 | qs = InvestmentRecord.objects.filter(user=self.request.user) 113 | if qs.count() >= 1: 114 | context['net_worth'] = InvestmentRecord.objects.calculate_net_worth(self.request.user) 115 | context['investments'] = qs 116 | return context 117 | 118 | 119 | class LeaderBoardView(CountNewsMixin, View): 120 | template_name = 'accounts/leaderboard.html' 121 | 122 | def get(self, request, *args, **kwargs): 123 | data = [] 124 | user_qs = User.objects.all() 125 | for user in user_qs: 126 | net_worth = InvestmentRecord.objects.calculate_net_worth(user) 127 | data.append((user.username, user.full_name, net_worth, user.coeff_of_variation)) 128 | data = sorted(data, key=lambda d: (-d[2], d[3])) 129 | return render(request, 'accounts/leaderboard.html', {'data': data}) 130 | 131 | 132 | class AccountEmailActivateView(FormMixin, View): 133 | success_url = '/login/' 134 | form_class = ReactivateEmailForm 135 | key = None 136 | 137 | def get(self, request, key=None, *args, **kwargs): 138 | self.key = key 139 | if key is not None: 140 | qs = EmailActivation.objects.filter(key__iexact=key) 141 | confirm_qs = qs.confirmable() 142 | if confirm_qs.count() == 1: # Not confirmed but confirmable 143 | obj = confirm_qs.first() 144 | obj.activate() 145 | messages.success(request, 'Your email has been confirmed! Please login to continue.') 146 | return redirect('login') 147 | else: 148 | activated_qs = qs.filter(activated=True) 149 | if activated_qs.exists(): 150 | reset_link = reverse('password_reset') 151 | msg = """Your email has already been confirmed. 152 | Do you want to reset you password?""".format(link=reset_link) 153 | messages.success(request, mark_safe(msg)) 154 | return redirect('login') 155 | context = {'form': self.get_form(), 'key': key} # get_form() works because of the mixin 156 | return render(request, 'registration/activation_error.html', context) 157 | 158 | def post(self, request, *args, **kwargs): 159 | # create a form to receive an email 160 | form = self.get_form() 161 | if form.is_valid(): 162 | return self.form_valid(form) 163 | else: 164 | return self.form_invalid(form) 165 | 166 | def form_valid(self, form): 167 | msg = 'Activation link sent. Please check your email.' 168 | messages.success(self.request, msg) 169 | email = form.cleaned_data.get('email') 170 | obj = EmailActivation.objects.email_exists(email).first() 171 | user = obj.user 172 | new_activation = EmailActivation.objects.create(user=user, email=email) 173 | new_activation.send_activation() 174 | return super(AccountEmailActivateView, self).form_valid(form) 175 | 176 | def form_invalid(self, form): 177 | """ 178 | This method had to be explicitly written because this view uses the basic django "View" class. 179 | If it had used some other view like ListView etc. Django would have handled it automatically. 180 | """ 181 | context = {'form': form, 'key': self.key} 182 | return render(self.request, 'registration/activation_error.html', context) 183 | 184 | 185 | class LoginView(AnonymousRequiredMixin, RequestFormAttachMixin, NextUrlMixin, FormView): 186 | form_class = LoginForm 187 | template_name = 'accounts/login.html' 188 | success_url = '/' 189 | default_url = '/' 190 | default_next = '/' 191 | 192 | def form_valid(self, form): 193 | request = self.request 194 | response = form.cleaned_data 195 | if not response.get('success'): 196 | messages.warning(request, mark_safe(response.get('message'))) 197 | return redirect('login') 198 | next_path = self.get_next_url() 199 | return redirect(next_path) 200 | 201 | 202 | class RegisterView(AnonymousRequiredMixin, CreateView): 203 | form_class = RegisterForm 204 | template_name = 'accounts/register.html' 205 | success_url = '/login/' 206 | 207 | def form_valid(self, form): 208 | super(RegisterView, self).form_valid(form) 209 | messages.success(self.request, 'Verification link sent! Please check your email.') 210 | return redirect(self.success_url) 211 | -------------------------------------------------------------------------------- /market/models.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | 3 | from django.db import models 4 | from django.db.models.signals import pre_save, post_save 5 | from django.contrib.auth import get_user_model 6 | from django.urls import reverse 7 | 8 | 9 | User = get_user_model() 10 | 11 | TRANSACTION_MODES = ( 12 | ('buy', 'BUY'), 13 | ('sell', 'SELL') 14 | ) 15 | 16 | CAP_TYPES = ( 17 | ('small', 'Small Cap'), 18 | ('mid', 'Mid Cap'), 19 | ('large', 'Large Cap') 20 | ) 21 | 22 | 23 | class Company(models.Model): 24 | code = models.CharField(max_length=10, unique=True) 25 | name = models.CharField(max_length=20, unique=True) 26 | cap = models.DecimalField(max_digits=20, decimal_places=2, default=0.00) # Company's worth 27 | cmp = models.DecimalField(max_digits=20, decimal_places=2, default=0.00) 28 | change = models.DecimalField(max_digits=10, decimal_places=2, default=0.00) # Company's change in cmp w.r.t last price 29 | stocks_offered = models.IntegerField(default=0) 30 | stocks_remaining = models.IntegerField(default=stocks_offered) 31 | cap_type = models.CharField(max_length=20, choices=CAP_TYPES, blank=True, null=True) 32 | industry = models.CharField(max_length=120, blank=True, null=True) 33 | temp_stocks_bought = models.IntegerField(default=0) # Refresh after every CMP update 34 | temp_stocks_sold = models.IntegerField(default=0) # Refresh after every CMP update 35 | timestamp = models.DateTimeField(auto_now_add=True) 36 | updated = models.DateTimeField(auto_now=True) 37 | 38 | class Meta: 39 | ordering = ['cap_type', 'code'] 40 | 41 | def __str__(self): 42 | return self.name 43 | 44 | def get_absolute_url(self): 45 | """ URL for user market transaction """ 46 | return reverse('market:transaction', kwargs={'code': self.code}) 47 | 48 | def get_absolute_admin_url(self): 49 | """ URL for admin change """ 50 | return reverse('market:admin', kwargs={'code': self.code}) 51 | 52 | def get_cap(self): 53 | cap = self.cap_type 54 | if cap == 'small': 55 | return 'Small Cap' 56 | if cap == 'mid': 57 | return 'Mid Cap' 58 | return 'Large Cap' 59 | 60 | def calculate_change(self, old_price): 61 | """ Calculate CMP change """ 62 | print('old', old_price) 63 | self.change = ((self.cmp - old_price) / old_price) * Decimal(100.00) 64 | print(self.change) 65 | self.save() 66 | 67 | def update_cmp(self): 68 | old_price = self.cmp 69 | self.cmp += ( 70 | self.cmp * Decimal(self.temp_stocks_bought) - self.cmp * Decimal(self.temp_stocks_sold) 71 | ) / Decimal(self.stocks_offered) 72 | self.calculate_change(old_price) 73 | self.temp_stocks_bought = 0 74 | self.temp_stocks_sold = 0 75 | self.save() 76 | 77 | def user_buy_stocks(self, quantity): 78 | if quantity <= self.stocks_remaining: 79 | self.stocks_remaining -= quantity 80 | self.temp_stocks_bought += quantity 81 | self.save() 82 | return True 83 | return False 84 | 85 | def user_sell_stocks(self, quantity): 86 | if quantity <= self.stocks_offered: 87 | self.stocks_remaining += quantity 88 | self.temp_stocks_sold += quantity 89 | self.save() 90 | return True 91 | return False 92 | 93 | 94 | def pre_save_company_receiver(sender, instance, *args, **kwargs): 95 | if instance.cmp <= Decimal(0.00): 96 | instance.cmp = Decimal(0.01) 97 | 98 | pre_save.connect(pre_save_company_receiver, sender=Company) 99 | 100 | 101 | def post_save_company_receiver(sender, instance, created, *args, **kwargs): 102 | if created: 103 | user_qs = User.objects.all() 104 | for user in user_qs: 105 | obj, create = InvestmentRecord.objects.get_or_create(user=user, company=instance) 106 | 107 | post_save.connect(post_save_company_receiver, sender=Company) 108 | 109 | 110 | class TransactionQuerySet(models.query.QuerySet): 111 | 112 | def get_by_user(self, user): 113 | return self.filter(user=user) 114 | 115 | def get_by_company(self, company): 116 | return self.filter(company=company) 117 | 118 | def get_by_user_and_company(self, user, company): 119 | return self.get_by_user(user).get_by_company(company) 120 | 121 | 122 | class TransactionManager(models.Manager): 123 | 124 | def get_queryset(self): 125 | return TransactionQuerySet(self.model, using=self._db) 126 | 127 | def get_by_user(self, user): 128 | return self.get_queryset().get_by_user(user) 129 | 130 | def get_by_company(self, company): 131 | return self.get_queryset().get_by_company(company) 132 | 133 | def get_by_user_and_company(self, user, company): 134 | return self.get_queryset().get_by_user_and_company(user, company) 135 | 136 | 137 | class Transaction(models.Model): 138 | user = models.ForeignKey(User, on_delete=True) 139 | company = models.ForeignKey(Company, on_delete=True) 140 | num_stocks = models.IntegerField(default=0) 141 | price = models.DecimalField(max_digits=20, decimal_places=2, default=0.00) 142 | mode = models.CharField(max_length=10, choices=TRANSACTION_MODES) 143 | user_net_worth = models.DecimalField(max_digits=20, decimal_places=2, default=0.00) # user's net worth after current transaction 144 | timestamp = models.DateTimeField(auto_now_add=True) 145 | updated = models.DateTimeField(auto_now=True) 146 | 147 | objects = TransactionManager() 148 | 149 | class Meta: 150 | ordering = ['-timestamp'] 151 | 152 | def __str__(self): 153 | return '{user}: {company} - {time}'.format( 154 | user=self.user.username, company=self.company.name, time=self.timestamp 155 | ) 156 | 157 | 158 | def pre_save_transaction_receiver(sender, instance, *args, **kwargs): 159 | amount = InvestmentRecord.objects.calculate_net_worth(instance.user) 160 | instance.user_net_worth = amount 161 | 162 | # transaction completion reflection in other models 163 | investment_obj, obj_created = InvestmentRecord.objects.get_or_create( 164 | user=instance.user, company=instance.company 165 | ) 166 | if instance.mode == 'buy': 167 | instance.user.buy_stocks(instance.num_stocks, instance.price) 168 | instance.company.user_buy_stocks(instance.num_stocks) 169 | investment_obj.add_stocks(instance.num_stocks) 170 | elif instance.mode == 'sell': 171 | instance.user.sell_stocks(instance.num_stocks, instance.price) 172 | instance.company.user_sell_stocks(instance.num_stocks) 173 | investment_obj.reduce_stocks(instance.num_stocks) 174 | 175 | pre_save.connect(pre_save_transaction_receiver, sender=Transaction) 176 | 177 | 178 | def post_save_transaction_create_receiver(sender, instance, created, *args, **kwargs): 179 | if created: 180 | # changes to user model 181 | net_worth_list = [ 182 | transaction.user_net_worth for transaction in Transaction.objects.filter(user=instance.user) 183 | ] 184 | instance.user.update_cv(net_worth_list) 185 | 186 | post_save.connect(post_save_transaction_create_receiver, sender=Transaction) 187 | 188 | 189 | class InvestmentRecordQuerySet(models.query.QuerySet): 190 | 191 | def get_by_user(self, user): 192 | return self.filter(user=user) 193 | 194 | def get_by_company(self, company): 195 | return self.filter(company=company) 196 | 197 | 198 | class InvestmentRecordManager(models.Manager): 199 | 200 | def get_queryset(self): 201 | return InvestmentRecordQuerySet(self.model, using=self._db) 202 | 203 | def get_by_user(self, user): 204 | return self.get_queryset().get_by_user(user=user) 205 | 206 | def get_by_company(self, company): 207 | return self.get_queryset().get_by_company(company=company) 208 | 209 | def calculate_net_worth(self, user): 210 | qs = self.get_by_user(user) 211 | amount = Decimal(0.00) 212 | for inv in qs: 213 | amount += Decimal(inv.stocks) * inv.company.cmp 214 | return amount + user.cash 215 | 216 | 217 | class InvestmentRecord(models.Model): 218 | user = models.ForeignKey(User, on_delete=True) 219 | company = models.ForeignKey(Company, on_delete=True) 220 | stocks = models.IntegerField(default=0) 221 | updated = models.DateTimeField(auto_now=True) 222 | 223 | objects = InvestmentRecordManager() 224 | 225 | class Meta: 226 | unique_together = ('user', 'company') 227 | 228 | def __str__(self): 229 | return self.user.username + ' - ' + self.company.code 230 | 231 | def add_stocks(self, num_stocks): 232 | self.stocks += num_stocks 233 | self.save() 234 | 235 | def reduce_stocks(self, num_stocks): 236 | if self.stocks >= num_stocks: 237 | self.stocks -= num_stocks 238 | self.save() 239 | 240 | 241 | def post_save_user_create_receiver(sender, instance, created, *args, **kwargs): 242 | if created: 243 | for company in Company.objects.all(): 244 | obj = InvestmentRecord.objects.create(user=instance, company=company) 245 | 246 | post_save.connect(post_save_user_create_receiver, sender=User) 247 | 248 | 249 | class CompanyCMPRecord(models.Model): 250 | """ This model is used for keeping record of company's cmp in order to use Chart.js """ 251 | company = models.ForeignKey(Company, on_delete=True) 252 | cmp = models.DecimalField(max_digits=20, decimal_places=2, default=0.00) 253 | timestamp = models.DateTimeField(auto_now_add=True) 254 | updated = models.DateTimeField(auto_now=True) 255 | 256 | class Meta: 257 | ordering = ['-timestamp'] 258 | 259 | def __str__(self): 260 | return self.company.code 261 | -------------------------------------------------------------------------------- /accounts/models.py: -------------------------------------------------------------------------------- 1 | from datetime import timedelta 2 | from decimal import Decimal 3 | import numpy as np 4 | 5 | from django.conf import settings 6 | from django.db import models 7 | from django.db.models import Q 8 | from django.db.models.signals import pre_save, post_save 9 | from django.contrib.auth.models import AbstractBaseUser, BaseUserManager 10 | from django.utils import timezone 11 | from django.core.mail import send_mail 12 | from django.urls import reverse 13 | from django.template.loader import get_template 14 | 15 | from stock_bridge.utils import unique_key_generator 16 | 17 | 18 | DEFAULT_ACTIVATION_DAYS = getattr(settings, 'DEFAULT_ACTIVATION_DAYS', 7) 19 | DEFAULT_LOAN_AMOUNT = getattr(settings, 'DEFAULT_LOAN_AMOUNT', Decimal(10000.00)) 20 | RATE_OF_INTEREST = getattr(settings, 'RATE_OF_INTEREST', Decimal(0.15)) 21 | MAX_LOAN_ISSUE = getattr(settings, 'MAX_LOAN_ISSUE') 22 | 23 | 24 | class UserManager(BaseUserManager): 25 | 26 | def create_user(self, username, email, password=None, full_name=None, is_active=True, is_staff=False, is_superuser=False): 27 | if not username: 28 | raise ValueError('Users must have a unique username.') 29 | if not email: 30 | raise ValueError('Users must have an email.') 31 | if not password: 32 | raise ValueError('Users must have a password.') 33 | 34 | user_obj = self.model( 35 | username=username, 36 | email=self.normalize_email(email), 37 | full_name=full_name 38 | ) 39 | user_obj.set_password(password) 40 | user_obj.is_active = is_active 41 | user_obj.staff = is_staff 42 | user_obj.is_superuser = is_superuser 43 | user_obj.cash = 0.00 44 | user_obj.save(using=self._db) 45 | return user_obj 46 | 47 | def create_staffuser(self, username, email, full_name=None, password=None): 48 | user = self.create_user( 49 | username, 50 | email, 51 | password=password, 52 | full_name=full_name, 53 | is_staff=True 54 | ) 55 | return user 56 | 57 | def create_superuser(self, username, email, full_name=None, password=None): 58 | user = self.create_user( 59 | username, 60 | email, 61 | password=password, 62 | full_name=full_name, 63 | is_staff=True, 64 | is_superuser=True 65 | ) 66 | return user 67 | 68 | 69 | class User(AbstractBaseUser): 70 | username = models.CharField(unique=True, max_length=120) 71 | email = models.EmailField(unique=True, max_length=255) 72 | full_name = models.CharField(max_length=255, blank=True, null=True) 73 | cash = models.DecimalField(max_digits=20, decimal_places=2, default=DEFAULT_LOAN_AMOUNT) 74 | loan = models.DecimalField(max_digits=20, decimal_places=2, default=DEFAULT_LOAN_AMOUNT) 75 | loan_count = models.IntegerField(default=1) # For arithmetic interest calculation 76 | loan_count_absolute = models.IntegerField(default=1) # For overall loan issue count 77 | coeff_of_variation = models.DecimalField(max_digits=20, decimal_places=2, default=0.00) # For tie breaker in leaderboard 78 | is_active = models.BooleanField(default=True) 79 | staff = models.BooleanField(default=False) 80 | is_superuser = models.BooleanField(default=False) 81 | timestamp = models.DateTimeField(auto_now_add=True) 82 | updated = models.DateTimeField(auto_now=True) 83 | 84 | USERNAME_FIELD = 'username' 85 | REQUIRED_FIELDS = ['email'] 86 | 87 | objects = UserManager() 88 | 89 | class Meta: 90 | ordering = ['-cash', 'coeff_of_variation'] 91 | 92 | def __str__(self): 93 | return self.username 94 | 95 | def get_full_name(self): 96 | if self.full_name: 97 | return self.full_name 98 | return self.email 99 | 100 | def get_short_name(self): 101 | return self.username 102 | 103 | def has_perm(self, perm, object=None): 104 | """ Does the user have a specific permission? """ 105 | return True 106 | 107 | def has_module_perms(self, app_label): 108 | """ Does the user have permissions to view the app 'app_label'? """ 109 | return True 110 | 111 | @property 112 | def is_staff(self): 113 | return self.staff 114 | 115 | def buy_stocks(self, quantity, price): 116 | purchase_amount = Decimal(quantity) * price 117 | if self.cash >= purchase_amount: 118 | self.cash -= Decimal(quantity) * price 119 | self.save() 120 | return True 121 | return False 122 | 123 | def sell_stocks(self, quantity, price): 124 | self.cash += Decimal(quantity) * price 125 | self.save() 126 | 127 | def issue_loan(self): 128 | if self.loan_count_absolute < MAX_LOAN_ISSUE: 129 | self.loan_count += 1 130 | self.loan_count_absolute += 1 131 | self.loan += DEFAULT_LOAN_AMOUNT 132 | self.cash += DEFAULT_LOAN_AMOUNT 133 | self.save() 134 | return True 135 | return False 136 | 137 | def pay_installment(self): 138 | if self.loan >= DEFAULT_LOAN_AMOUNT and self.cash >= DEFAULT_LOAN_AMOUNT and self.loan_count > 0: 139 | self.loan_count -= 1 140 | self.loan -= DEFAULT_LOAN_AMOUNT 141 | self.cash -= DEFAULT_LOAN_AMOUNT 142 | self.save() 143 | return True 144 | return False 145 | 146 | def cancel_loan(self): 147 | self.loan_count = 0 148 | self.loan_count_absolute = 0 149 | self.cash = self.cash - self.loan 150 | self.loan = Decimal(0.00) 151 | self.save() 152 | 153 | def deduct_interest(self): 154 | amount = (self.loan * (Decimal(1.0) + RATE_OF_INTEREST)) # After 1 year 155 | compound_interest = abs(amount - self.loan) 156 | self.cash -= compound_interest 157 | self.save() 158 | 159 | def update_cv(self, net_worth_list): 160 | self.coeff_of_variation = Decimal(np.std(net_worth_list) / np.mean(net_worth_list)) 161 | self.save() 162 | 163 | 164 | class EmailActivationQuerySet(models.query.QuerySet): 165 | 166 | def confirmable(self): 167 | """ 168 | Returns those emails which can be confirmed i.e. which are not activated and expired 169 | """ 170 | now = timezone.now() 171 | start_range = now - timedelta(days=DEFAULT_ACTIVATION_DAYS) 172 | end_range = now 173 | return self.filter(activated=False, forced_expire=False).filter( 174 | timestamp__gt=start_range, timestamp__lte=end_range 175 | ) 176 | 177 | 178 | class EmailActivationManager(models.Manager): 179 | 180 | def get_queryset(self): 181 | return EmailActivationQuerySet(self.model, using=self._db) 182 | 183 | def confirmable(self): 184 | return self.get_queryset().confirmable() 185 | 186 | def email_exists(self, email): 187 | """ 188 | EmailActivation is created when the user is created. When only EmailActivation is deleted, User object 189 | still remains i.e. email still exists. But this function will send nothing because for this function 190 | self.get_queryset() is None. So both user and EmailActivation should exist together for this to work. 191 | """ 192 | return self.get_queryset().filter( 193 | Q(email=email) | Q(user__email=email) 194 | ).filter(activated=False) 195 | 196 | 197 | class EmailActivation(models.Model): 198 | user = models.ForeignKey(User, on_delete=True) 199 | email = models.EmailField() 200 | key = models.CharField(max_length=120, blank=True, null=True) # activation key 201 | activated = models.BooleanField(default=False) 202 | forced_expire = models.BooleanField(default=False) # link expired manually 203 | expires = models.IntegerField(default=7) # automatic expire (after days) 204 | timestamp = models.DateTimeField(auto_now_add=True) 205 | update = models.DateTimeField(auto_now=True) 206 | 207 | objects = EmailActivationManager() 208 | 209 | def __str__(self): 210 | return self.email 211 | 212 | def can_activate(self): 213 | qs = EmailActivation.objects.filter(pk=self.pk).confirmable() 214 | if qs.exists(): 215 | return True 216 | return False 217 | 218 | def activate(self): 219 | if self.can_activate(): 220 | user = self.user 221 | user.is_active = True 222 | user.save() 223 | self.activated = True 224 | self.save() 225 | return True 226 | return False 227 | 228 | def send_activation(self): 229 | if not self.activated and not self.forced_expire: 230 | if self.key: 231 | base_url = getattr(settings, 'HOST_SCHEME') + getattr(settings, 'BASE_URL') 232 | key_path = reverse('account:email-activate', kwargs={'key': self.key}) 233 | path = '{base}{path}'.format(base=base_url, path=key_path) 234 | context = { 235 | 'path': path, 236 | 'email': self.email 237 | } 238 | txt_ = get_template('registration/emails/verify.txt').render(context) 239 | html_ = get_template('registration/emails/verify.html').render(context) 240 | subject = 'Morphosis Stock Bridge - Verify your Account' 241 | from_email = settings.DEFAULT_FROM_EMAIL 242 | recipient_list = [self.email] 243 | sent_mail = send_mail( 244 | subject, 245 | txt_, # If content_type is text/plain 246 | from_email, 247 | recipient_list, 248 | html_message=html_, # If content_type is text/html 249 | fail_silently=False # If false, then an email will be sent if error occurs while sending the email 250 | ) 251 | return sent_mail 252 | return False 253 | 254 | 255 | def pre_save_email_activation_receiver(sender, instance, *args, **kwargs): 256 | if not instance.activated and not instance.forced_expire and not instance.key: 257 | instance.key = unique_key_generator(instance) 258 | 259 | pre_save.connect(pre_save_email_activation_receiver, sender=EmailActivation) 260 | 261 | 262 | def post_save_user_create_receiver(sender, instance, created, *args, **kwargs): 263 | if created: 264 | email_obj = EmailActivation.objects.create(user=instance, email=instance.email) 265 | email_obj.send_activation() 266 | 267 | post_save.connect(post_save_user_create_receiver, sender=User) 268 | 269 | 270 | class News(models.Model): 271 | title = models.CharField(max_length=120) 272 | content = models.TextField() 273 | is_active = models.BooleanField(default=True) # Inactive news won't appear in dashboard 274 | timestamp = models.DateTimeField(auto_now_add=True) 275 | updated = models.DateTimeField(auto_now=True) 276 | 277 | class Meta: 278 | ordering = ['-timestamp', '-updated'] 279 | 280 | def __str__(self): 281 | return self.title 282 | -------------------------------------------------------------------------------- /market/fixtures/market.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "market.company", 4 | "pk": 1, 5 | "fields": { 6 | "code": "NCSFT", 7 | "name": "Nicrosoft", 8 | "cap": "100000000000.00", 9 | "cmp": "115.78", 10 | "change": "0.00", 11 | "stocks_offered": 100000, 12 | "stocks_remaining": 100000, 13 | "cap_type": "large", 14 | "industry": "IT Sector", 15 | "temp_stocks_bought": 0, 16 | "temp_stocks_sold": 0, 17 | "timestamp": "2018-04-28T05:10:03.092Z", 18 | "updated": "2018-05-04T13:20:42.457Z" 19 | } 20 | }, 21 | { 22 | "model": "market.company", 23 | "pk": 2, 24 | "fields": { 25 | "code": "DGL", 26 | "name": "Doogle", 27 | "cap": "150000000000.00", 28 | "cmp": "120.02", 29 | "change": "0.02", 30 | "stocks_offered": 100000, 31 | "stocks_remaining": 100000, 32 | "cap_type": "large", 33 | "industry": "IT Sector", 34 | "temp_stocks_bought": 0, 35 | "temp_stocks_sold": 0, 36 | "timestamp": "2018-04-28T05:11:04.035Z", 37 | "updated": "2018-05-04T13:19:32.344Z" 38 | } 39 | }, 40 | { 41 | "model": "market.company", 42 | "pk": 3, 43 | "fields": { 44 | "code": "SYM", 45 | "name": "Symfosis", 46 | "cap": "80000000000.00", 47 | "cmp": "175.36", 48 | "change": "0.00", 49 | "stocks_offered": 100000, 50 | "stocks_remaining": 100000, 51 | "cap_type": "large", 52 | "industry": "IT Sector", 53 | "temp_stocks_bought": 0, 54 | "temp_stocks_sold": 0, 55 | "timestamp": "2018-04-28T05:11:58.668Z", 56 | "updated": "2018-05-04T13:20:53.255Z" 57 | } 58 | }, 59 | { 60 | "model": "market.company", 61 | "pk": 4, 62 | "fields": { 63 | "code": "OIL", 64 | "name": "Oil Wells", 65 | "cap": "45000000000.00", 66 | "cmp": "250.21", 67 | "change": "0.00", 68 | "stocks_offered": 65000, 69 | "stocks_remaining": 65000, 70 | "cap_type": "mid", 71 | "industry": "Oil Sector", 72 | "temp_stocks_bought": 0, 73 | "temp_stocks_sold": 0, 74 | "timestamp": "2018-04-28T05:13:12.769Z", 75 | "updated": "2018-05-04T13:29:30.818Z" 76 | } 77 | }, 78 | { 79 | "model": "market.company", 80 | "pk": 5, 81 | "fields": { 82 | "code": "KMCG", 83 | "name": "KMCG", 84 | "cap": "42000000000.00", 85 | "cmp": "260.11", 86 | "change": "0.00", 87 | "stocks_offered": 65000, 88 | "stocks_remaining": 65000, 89 | "cap_type": "mid", 90 | "industry": "Consumer Durables and Retail Sector", 91 | "temp_stocks_bought": 0, 92 | "temp_stocks_sold": 0, 93 | "timestamp": "2018-04-28T05:13:55.903Z", 94 | "updated": "2018-05-04T13:28:36.705Z" 95 | } 96 | }, 97 | { 98 | "model": "market.company", 99 | "pk": 6, 100 | "fields": { 101 | "code": "SUD", 102 | "name": "Suduki", 103 | "cap": "40000000000.00", 104 | "cmp": "300.33", 105 | "change": "0.00", 106 | "stocks_offered": 65000, 107 | "stocks_remaining": 65000, 108 | "cap_type": "mid", 109 | "industry": "Manufacturing Sector", 110 | "temp_stocks_bought": 0, 111 | "temp_stocks_sold": 0, 112 | "timestamp": "2018-04-28T05:15:56.147Z", 113 | "updated": "2018-05-04T13:29:50.545Z" 114 | } 115 | }, 116 | { 117 | "model": "market.company", 118 | "pk": 7, 119 | "fields": { 120 | "code": "FUR", 121 | "name": "Furnilon", 122 | "cap": "800000000.00", 123 | "cmp": "700.21", 124 | "change": "0.00", 125 | "stocks_offered": 30000, 126 | "stocks_remaining": 30000, 127 | "cap_type": "small", 128 | "industry": "Furniture Sector", 129 | "temp_stocks_bought": 0, 130 | "temp_stocks_sold": 0, 131 | "timestamp": "2018-04-28T05:16:56.926Z", 132 | "updated": "2018-05-04T13:31:57.441Z" 133 | } 134 | }, 135 | { 136 | "model": "market.company", 137 | "pk": 8, 138 | "fields": { 139 | "code": "NLC", 140 | "name": "Nelcrow", 141 | "cap": "700000000.00", 142 | "cmp": "750.21", 143 | "change": "0.00", 144 | "stocks_offered": 30000, 145 | "stocks_remaining": 30000, 146 | "cap_type": "small", 147 | "industry": "Apparels and Shopping", 148 | "temp_stocks_bought": 0, 149 | "temp_stocks_sold": 0, 150 | "timestamp": "2018-04-28T05:17:34.468Z", 151 | "updated": "2018-05-04T13:32:06.805Z" 152 | } 153 | }, 154 | { 155 | "model": "market.company", 156 | "pk": 9, 157 | "fields": { 158 | "code": "DPS", 159 | "name": "Dipsim", 160 | "cap": "500000000.00", 161 | "cmp": "922.49", 162 | "change": "0.00", 163 | "stocks_offered": 30000, 164 | "stocks_remaining": 30000, 165 | "cap_type": "small", 166 | "industry": "Telecommunication", 167 | "temp_stocks_bought": 0, 168 | "temp_stocks_sold": 0, 169 | "timestamp": "2018-04-28T05:18:11.990Z", 170 | "updated": "2018-05-04T13:31:42.582Z" 171 | } 172 | }, 173 | { 174 | "model": "market.transaction", 175 | "pk": 1, 176 | "fields": { 177 | "user": 2, 178 | "company": 5, 179 | "num_stocks": 5, 180 | "price": "260.11", 181 | "mode": "buy", 182 | "user_net_worth": "5000.00", 183 | "timestamp": "2018-05-04T10:47:13.734Z", 184 | "updated": "2018-05-04T10:47:13.734Z" 185 | } 186 | }, 187 | { 188 | "model": "market.transaction", 189 | "pk": 2, 190 | "fields": { 191 | "user": 2, 192 | "company": 1, 193 | "num_stocks": 10, 194 | "price": "115.74", 195 | "mode": "buy", 196 | "user_net_worth": "5000.00", 197 | "timestamp": "2018-05-04T10:48:00.871Z", 198 | "updated": "2018-05-04T10:48:00.871Z" 199 | } 200 | }, 201 | { 202 | "model": "market.transaction", 203 | "pk": 3, 204 | "fields": { 205 | "user": 2, 206 | "company": 2, 207 | "num_stocks": 2, 208 | "price": "120.00", 209 | "mode": "buy", 210 | "user_net_worth": "5000.00", 211 | "timestamp": "2018-05-04T10:48:39.844Z", 212 | "updated": "2018-05-04T10:48:39.844Z" 213 | } 214 | }, 215 | { 216 | "model": "market.transaction", 217 | "pk": 4, 218 | "fields": { 219 | "user": 3, 220 | "company": 2, 221 | "num_stocks": 20, 222 | "price": "120.00", 223 | "mode": "buy", 224 | "user_net_worth": "5000.00", 225 | "timestamp": "2018-05-04T10:49:00.753Z", 226 | "updated": "2018-05-04T10:49:00.753Z" 227 | } 228 | }, 229 | { 230 | "model": "market.transaction", 231 | "pk": 5, 232 | "fields": { 233 | "user": 3, 234 | "company": 1, 235 | "num_stocks": 20, 236 | "price": "115.74", 237 | "mode": "buy", 238 | "user_net_worth": "5000.00", 239 | "timestamp": "2018-05-04T10:49:47.129Z", 240 | "updated": "2018-05-04T10:49:47.129Z" 241 | } 242 | }, 243 | { 244 | "model": "market.transaction", 245 | "pk": 6, 246 | "fields": { 247 | "user": 2, 248 | "company": 1, 249 | "num_stocks": 500, 250 | "price": "115.74", 251 | "mode": "buy", 252 | "user_net_worth": "998177.12", 253 | "timestamp": "2018-05-04T10:51:36.260Z", 254 | "updated": "2018-05-04T10:51:36.260Z" 255 | } 256 | }, 257 | { 258 | "model": "market.transaction", 259 | "pk": 7, 260 | "fields": { 261 | "user": 2, 262 | "company": 2, 263 | "num_stocks": 500, 264 | "price": "120.00", 265 | "mode": "buy", 266 | "user_net_worth": "998177.12", 267 | "timestamp": "2018-05-04T10:51:46.344Z", 268 | "updated": "2018-05-04T10:51:46.344Z" 269 | } 270 | }, 271 | { 272 | "model": "market.transaction", 273 | "pk": 8, 274 | "fields": { 275 | "user": 2, 276 | "company": 1, 277 | "num_stocks": 1000, 278 | "price": "115.74", 279 | "mode": "buy", 280 | "user_net_worth": "998177.12", 281 | "timestamp": "2018-05-04T10:52:28.679Z", 282 | "updated": "2018-05-04T10:52:28.679Z" 283 | } 284 | }, 285 | { 286 | "model": "market.transaction", 287 | "pk": 9, 288 | "fields": { 289 | "user": 2, 290 | "company": 3, 291 | "num_stocks": 500, 292 | "price": "175.36", 293 | "mode": "buy", 294 | "user_net_worth": "998177.12", 295 | "timestamp": "2018-05-04T10:52:49.601Z", 296 | "updated": "2018-05-04T10:52:49.601Z" 297 | } 298 | }, 299 | { 300 | "model": "market.transaction", 301 | "pk": 10, 302 | "fields": { 303 | "user": 2, 304 | "company": 1, 305 | "num_stocks": 500, 306 | "price": "115.74", 307 | "mode": "buy", 308 | "user_net_worth": "998177.12", 309 | "timestamp": "2018-05-04T10:53:28.906Z", 310 | "updated": "2018-05-04T10:53:28.906Z" 311 | } 312 | }, 313 | { 314 | "model": "market.transaction", 315 | "pk": 11, 316 | "fields": { 317 | "user": 2, 318 | "company": 1, 319 | "num_stocks": 800, 320 | "price": "115.74", 321 | "mode": "buy", 322 | "user_net_worth": "998177.12", 323 | "timestamp": "2018-05-04T10:53:37.582Z", 324 | "updated": "2018-05-04T10:53:37.582Z" 325 | } 326 | }, 327 | { 328 | "model": "market.transaction", 329 | "pk": 12, 330 | "fields": { 331 | "user": 2, 332 | "company": 1, 333 | "num_stocks": 500, 334 | "price": "115.75", 335 | "mode": "buy", 336 | "user_net_worth": "998205.22", 337 | "timestamp": "2018-05-04T10:54:10.266Z", 338 | "updated": "2018-05-04T10:54:10.266Z" 339 | } 340 | }, 341 | { 342 | "model": "market.transaction", 343 | "pk": 13, 344 | "fields": { 345 | "user": 2, 346 | "company": 1, 347 | "num_stocks": 500, 348 | "price": "115.75", 349 | "mode": "buy", 350 | "user_net_worth": "998205.22", 351 | "timestamp": "2018-05-04T10:54:49.559Z", 352 | "updated": "2018-05-04T10:54:49.559Z" 353 | } 354 | }, 355 | { 356 | "model": "market.transaction", 357 | "pk": 14, 358 | "fields": { 359 | "user": 2, 360 | "company": 1, 361 | "num_stocks": 1000, 362 | "price": "115.75", 363 | "mode": "buy", 364 | "user_net_worth": "998205.22", 365 | "timestamp": "2018-05-04T10:54:56.737Z", 366 | "updated": "2018-05-04T10:54:56.737Z" 367 | } 368 | }, 369 | { 370 | "model": "market.transaction", 371 | "pk": 15, 372 | "fields": { 373 | "user": 2, 374 | "company": 1, 375 | "num_stocks": 1000, 376 | "price": "115.75", 377 | "mode": "buy", 378 | "user_net_worth": "998205.22", 379 | "timestamp": "2018-05-04T10:55:04.681Z", 380 | "updated": "2018-05-04T10:55:04.681Z" 381 | } 382 | }, 383 | { 384 | "model": "market.transaction", 385 | "pk": 16, 386 | "fields": { 387 | "user": 2, 388 | "company": 1, 389 | "num_stocks": 1000, 390 | "price": "115.75", 391 | "mode": "buy", 392 | "user_net_worth": "998205.22", 393 | "timestamp": "2018-05-04T10:55:12.923Z", 394 | "updated": "2018-05-04T10:55:12.923Z" 395 | } 396 | }, 397 | { 398 | "model": "market.transaction", 399 | "pk": 17, 400 | "fields": { 401 | "user": 2, 402 | "company": 1, 403 | "num_stocks": 500, 404 | "price": "115.75", 405 | "mode": "buy", 406 | "user_net_worth": "998205.22", 407 | "timestamp": "2018-05-04T10:55:24.561Z", 408 | "updated": "2018-05-04T10:55:24.562Z" 409 | } 410 | }, 411 | { 412 | "model": "market.transaction", 413 | "pk": 18, 414 | "fields": { 415 | "user": 2, 416 | "company": 1, 417 | "num_stocks": 10, 418 | "price": "115.75", 419 | "mode": "buy", 420 | "user_net_worth": "998205.22", 421 | "timestamp": "2018-05-04T10:55:59.618Z", 422 | "updated": "2018-05-04T10:55:59.618Z" 423 | } 424 | }, 425 | { 426 | "model": "market.transaction", 427 | "pk": 19, 428 | "fields": { 429 | "user": 2, 430 | "company": 1, 431 | "num_stocks": 10, 432 | "price": "115.78", 433 | "mode": "buy", 434 | "user_net_worth": "998424.82", 435 | "timestamp": "2018-05-04T10:56:09.332Z", 436 | "updated": "2018-05-04T10:56:09.332Z" 437 | } 438 | }, 439 | { 440 | "model": "market.transaction", 441 | "pk": 20, 442 | "fields": { 443 | "user": 2, 444 | "company": 1, 445 | "num_stocks": 3, 446 | "price": "115.78", 447 | "mode": "buy", 448 | "user_net_worth": "998424.82", 449 | "timestamp": "2018-05-04T10:56:34.341Z", 450 | "updated": "2018-05-04T10:56:34.341Z" 451 | } 452 | }, 453 | { 454 | "model": "market.transaction", 455 | "pk": 21, 456 | "fields": { 457 | "user": 2, 458 | "company": 2, 459 | "num_stocks": 1, 460 | "price": "120.00", 461 | "mode": "buy", 462 | "user_net_worth": "998424.82", 463 | "timestamp": "2018-05-04T10:59:29.776Z", 464 | "updated": "2018-05-04T10:59:29.776Z" 465 | } 466 | }, 467 | { 468 | "model": "market.transaction", 469 | "pk": 22, 470 | "fields": { 471 | "user": 2, 472 | "company": 9, 473 | "num_stocks": 1000, 474 | "price": "900.14", 475 | "mode": "buy", 476 | "user_net_worth": "1000998355.29", 477 | "timestamp": "2018-05-04T11:00:52.195Z", 478 | "updated": "2018-05-04T11:00:52.195Z" 479 | } 480 | }, 481 | { 482 | "model": "market.transaction", 483 | "pk": 23, 484 | "fields": { 485 | "user": 2, 486 | "company": 9, 487 | "num_stocks": 10000, 488 | "price": "900.14", 489 | "mode": "buy", 490 | "user_net_worth": "1000998355.29", 491 | "timestamp": "2018-05-04T11:01:02.702Z", 492 | "updated": "2018-05-04T11:01:02.702Z" 493 | } 494 | }, 495 | { 496 | "model": "market.transaction", 497 | "pk": 24, 498 | "fields": { 499 | "user": 2, 500 | "company": 9, 501 | "num_stocks": 50000, 502 | "price": "900.14", 503 | "mode": "buy", 504 | "user_net_worth": "1000998355.29", 505 | "timestamp": "2018-05-04T11:01:15.948Z", 506 | "updated": "2018-05-04T11:01:15.948Z" 507 | } 508 | }, 509 | { 510 | "model": "market.transaction", 511 | "pk": 25, 512 | "fields": { 513 | "user": 2, 514 | "company": 9, 515 | "num_stocks": 1000, 516 | "price": "927.59", 517 | "mode": "sell", 518 | "user_net_worth": "1002672805.29", 519 | "timestamp": "2018-05-04T11:02:07.379Z", 520 | "updated": "2018-05-04T11:02:07.379Z" 521 | } 522 | }, 523 | { 524 | "model": "market.transaction", 525 | "pk": 26, 526 | "fields": { 527 | "user": 2, 528 | "company": 9, 529 | "num_stocks": 10000, 530 | "price": "927.13", 531 | "mode": "sell", 532 | "user_net_worth": "1002645205.29", 533 | "timestamp": "2018-05-04T11:02:45.076Z", 534 | "updated": "2018-05-04T11:02:45.076Z" 535 | } 536 | }, 537 | { 538 | "model": "market.transaction", 539 | "pk": 27, 540 | "fields": { 541 | "user": 3, 542 | "company": 2, 543 | "num_stocks": 15, 544 | "price": "120.00", 545 | "mode": "buy", 546 | "user_net_worth": "14715.60", 547 | "timestamp": "2018-05-04T11:34:05.909Z", 548 | "updated": "2018-05-04T11:34:05.909Z" 549 | } 550 | }, 551 | { 552 | "model": "market.investmentrecord", 553 | "pk": 1, 554 | "fields": { 555 | "user": 1, 556 | "company": 1, 557 | "stocks": 0, 558 | "updated": "2018-04-29T12:30:56.365Z" 559 | } 560 | }, 561 | { 562 | "model": "market.investmentrecord", 563 | "pk": 2, 564 | "fields": { 565 | "user": 1, 566 | "company": 2, 567 | "stocks": 0, 568 | "updated": "2018-04-29T12:30:56.566Z" 569 | } 570 | }, 571 | { 572 | "model": "market.investmentrecord", 573 | "pk": 3, 574 | "fields": { 575 | "user": 1, 576 | "company": 3, 577 | "stocks": 0, 578 | "updated": "2018-04-29T12:30:56.754Z" 579 | } 580 | }, 581 | { 582 | "model": "market.investmentrecord", 583 | "pk": 4, 584 | "fields": { 585 | "user": 1, 586 | "company": 4, 587 | "stocks": 0, 588 | "updated": "2018-04-29T12:30:56.954Z" 589 | } 590 | }, 591 | { 592 | "model": "market.investmentrecord", 593 | "pk": 5, 594 | "fields": { 595 | "user": 1, 596 | "company": 5, 597 | "stocks": 0, 598 | "updated": "2018-04-29T12:30:57.132Z" 599 | } 600 | }, 601 | { 602 | "model": "market.investmentrecord", 603 | "pk": 6, 604 | "fields": { 605 | "user": 1, 606 | "company": 6, 607 | "stocks": 0, 608 | "updated": "2018-04-29T12:30:57.331Z" 609 | } 610 | }, 611 | { 612 | "model": "market.investmentrecord", 613 | "pk": 7, 614 | "fields": { 615 | "user": 1, 616 | "company": 7, 617 | "stocks": 0, 618 | "updated": "2018-04-29T12:30:57.686Z" 619 | } 620 | }, 621 | { 622 | "model": "market.investmentrecord", 623 | "pk": 8, 624 | "fields": { 625 | "user": 1, 626 | "company": 8, 627 | "stocks": 0, 628 | "updated": "2018-04-29T12:30:58.308Z" 629 | } 630 | }, 631 | { 632 | "model": "market.investmentrecord", 633 | "pk": 9, 634 | "fields": { 635 | "user": 1, 636 | "company": 9, 637 | "stocks": 0, 638 | "updated": "2018-04-29T12:30:58.785Z" 639 | } 640 | }, 641 | { 642 | "model": "market.investmentrecord", 643 | "pk": 10, 644 | "fields": { 645 | "user": 2, 646 | "company": 2, 647 | "stocks": 503, 648 | "updated": "2018-05-04T10:59:29.569Z" 649 | } 650 | }, 651 | { 652 | "model": "market.investmentrecord", 653 | "pk": 11, 654 | "fields": { 655 | "user": 2, 656 | "company": 1, 657 | "stocks": 7333, 658 | "updated": "2018-05-04T10:56:34.108Z" 659 | } 660 | }, 661 | { 662 | "model": "market.investmentrecord", 663 | "pk": 12, 664 | "fields": { 665 | "user": 2, 666 | "company": 3, 667 | "stocks": 500, 668 | "updated": "2018-05-04T10:52:49.386Z" 669 | } 670 | }, 671 | { 672 | "model": "market.investmentrecord", 673 | "pk": 13, 674 | "fields": { 675 | "user": 2, 676 | "company": 5, 677 | "stocks": 5, 678 | "updated": "2018-05-04T10:47:13.530Z" 679 | } 680 | }, 681 | { 682 | "model": "market.investmentrecord", 683 | "pk": 14, 684 | "fields": { 685 | "user": 2, 686 | "company": 4, 687 | "stocks": 0, 688 | "updated": "2018-05-04T10:42:05.819Z" 689 | } 690 | }, 691 | { 692 | "model": "market.investmentrecord", 693 | "pk": 15, 694 | "fields": { 695 | "user": 2, 696 | "company": 6, 697 | "stocks": 0, 698 | "updated": "2018-05-04T10:42:06.043Z" 699 | } 700 | }, 701 | { 702 | "model": "market.investmentrecord", 703 | "pk": 16, 704 | "fields": { 705 | "user": 2, 706 | "company": 9, 707 | "stocks": 50000, 708 | "updated": "2018-05-04T11:02:44.876Z" 709 | } 710 | }, 711 | { 712 | "model": "market.investmentrecord", 713 | "pk": 17, 714 | "fields": { 715 | "user": 2, 716 | "company": 7, 717 | "stocks": 0, 718 | "updated": "2018-05-04T10:42:06.556Z" 719 | } 720 | }, 721 | { 722 | "model": "market.investmentrecord", 723 | "pk": 18, 724 | "fields": { 725 | "user": 2, 726 | "company": 8, 727 | "stocks": 0, 728 | "updated": "2018-05-04T10:42:06.744Z" 729 | } 730 | }, 731 | { 732 | "model": "market.investmentrecord", 733 | "pk": 19, 734 | "fields": { 735 | "user": 3, 736 | "company": 2, 737 | "stocks": 35, 738 | "updated": "2018-05-04T11:34:05.698Z" 739 | } 740 | }, 741 | { 742 | "model": "market.investmentrecord", 743 | "pk": 20, 744 | "fields": { 745 | "user": 3, 746 | "company": 1, 747 | "stocks": 20, 748 | "updated": "2018-05-04T10:49:46.941Z" 749 | } 750 | }, 751 | { 752 | "model": "market.investmentrecord", 753 | "pk": 21, 754 | "fields": { 755 | "user": 3, 756 | "company": 3, 757 | "stocks": 0, 758 | "updated": "2018-05-04T10:46:08.602Z" 759 | } 760 | }, 761 | { 762 | "model": "market.investmentrecord", 763 | "pk": 22, 764 | "fields": { 765 | "user": 3, 766 | "company": 5, 767 | "stocks": 0, 768 | "updated": "2018-05-04T10:46:08.801Z" 769 | } 770 | }, 771 | { 772 | "model": "market.investmentrecord", 773 | "pk": 23, 774 | "fields": { 775 | "user": 3, 776 | "company": 4, 777 | "stocks": 0, 778 | "updated": "2018-05-04T10:46:08.990Z" 779 | } 780 | }, 781 | { 782 | "model": "market.investmentrecord", 783 | "pk": 24, 784 | "fields": { 785 | "user": 3, 786 | "company": 6, 787 | "stocks": 0, 788 | "updated": "2018-05-04T10:46:09.200Z" 789 | } 790 | }, 791 | { 792 | "model": "market.investmentrecord", 793 | "pk": 25, 794 | "fields": { 795 | "user": 3, 796 | "company": 9, 797 | "stocks": 0, 798 | "updated": "2018-05-04T10:46:09.400Z" 799 | } 800 | }, 801 | { 802 | "model": "market.investmentrecord", 803 | "pk": 26, 804 | "fields": { 805 | "user": 3, 806 | "company": 7, 807 | "stocks": 0, 808 | "updated": "2018-05-04T10:46:09.623Z" 809 | } 810 | }, 811 | { 812 | "model": "market.investmentrecord", 813 | "pk": 27, 814 | "fields": { 815 | "user": 3, 816 | "company": 8, 817 | "stocks": 0, 818 | "updated": "2018-05-04T10:46:09.829Z" 819 | } 820 | }, 821 | { 822 | "model": "market.investmentrecord", 823 | "pk": 28, 824 | "fields": { 825 | "user": 4, 826 | "company": 2, 827 | "stocks": 0, 828 | "updated": "2018-05-04T10:46:50.783Z" 829 | } 830 | }, 831 | { 832 | "model": "market.investmentrecord", 833 | "pk": 29, 834 | "fields": { 835 | "user": 4, 836 | "company": 1, 837 | "stocks": 0, 838 | "updated": "2018-05-04T10:46:51.055Z" 839 | } 840 | }, 841 | { 842 | "model": "market.investmentrecord", 843 | "pk": 30, 844 | "fields": { 845 | "user": 4, 846 | "company": 3, 847 | "stocks": 0, 848 | "updated": "2018-05-04T10:46:51.280Z" 849 | } 850 | }, 851 | { 852 | "model": "market.investmentrecord", 853 | "pk": 31, 854 | "fields": { 855 | "user": 4, 856 | "company": 5, 857 | "stocks": 0, 858 | "updated": "2018-05-04T10:46:51.487Z" 859 | } 860 | }, 861 | { 862 | "model": "market.investmentrecord", 863 | "pk": 32, 864 | "fields": { 865 | "user": 4, 866 | "company": 4, 867 | "stocks": 0, 868 | "updated": "2018-05-04T10:46:51.702Z" 869 | } 870 | }, 871 | { 872 | "model": "market.investmentrecord", 873 | "pk": 33, 874 | "fields": { 875 | "user": 4, 876 | "company": 6, 877 | "stocks": 0, 878 | "updated": "2018-05-04T10:46:51.912Z" 879 | } 880 | }, 881 | { 882 | "model": "market.investmentrecord", 883 | "pk": 34, 884 | "fields": { 885 | "user": 4, 886 | "company": 9, 887 | "stocks": 0, 888 | "updated": "2018-05-04T10:46:52.097Z" 889 | } 890 | }, 891 | { 892 | "model": "market.investmentrecord", 893 | "pk": 35, 894 | "fields": { 895 | "user": 4, 896 | "company": 7, 897 | "stocks": 0, 898 | "updated": "2018-05-04T10:46:52.300Z" 899 | } 900 | }, 901 | { 902 | "model": "market.investmentrecord", 903 | "pk": 36, 904 | "fields": { 905 | "user": 4, 906 | "company": 8, 907 | "stocks": 0, 908 | "updated": "2018-05-04T10:46:52.511Z" 909 | } 910 | }, 911 | { 912 | "model": "market.investmentrecord", 913 | "pk": 37, 914 | "fields": { 915 | "user": 5, 916 | "company": 2, 917 | "stocks": 0, 918 | "updated": "2018-05-04T10:47:28.352Z" 919 | } 920 | }, 921 | { 922 | "model": "market.investmentrecord", 923 | "pk": 38, 924 | "fields": { 925 | "user": 5, 926 | "company": 1, 927 | "stocks": 0, 928 | "updated": "2018-05-04T10:47:28.600Z" 929 | } 930 | }, 931 | { 932 | "model": "market.investmentrecord", 933 | "pk": 39, 934 | "fields": { 935 | "user": 5, 936 | "company": 3, 937 | "stocks": 0, 938 | "updated": "2018-05-04T10:47:28.788Z" 939 | } 940 | }, 941 | { 942 | "model": "market.investmentrecord", 943 | "pk": 40, 944 | "fields": { 945 | "user": 5, 946 | "company": 5, 947 | "stocks": 0, 948 | "updated": "2018-05-04T10:47:28.951Z" 949 | } 950 | }, 951 | { 952 | "model": "market.investmentrecord", 953 | "pk": 41, 954 | "fields": { 955 | "user": 5, 956 | "company": 4, 957 | "stocks": 0, 958 | "updated": "2018-05-04T10:47:29.117Z" 959 | } 960 | }, 961 | { 962 | "model": "market.investmentrecord", 963 | "pk": 42, 964 | "fields": { 965 | "user": 5, 966 | "company": 6, 967 | "stocks": 0, 968 | "updated": "2018-05-04T10:47:29.295Z" 969 | } 970 | }, 971 | { 972 | "model": "market.investmentrecord", 973 | "pk": 43, 974 | "fields": { 975 | "user": 5, 976 | "company": 9, 977 | "stocks": 0, 978 | "updated": "2018-05-04T10:47:29.483Z" 979 | } 980 | }, 981 | { 982 | "model": "market.investmentrecord", 983 | "pk": 44, 984 | "fields": { 985 | "user": 5, 986 | "company": 7, 987 | "stocks": 0, 988 | "updated": "2018-05-04T10:47:29.687Z" 989 | } 990 | }, 991 | { 992 | "model": "market.investmentrecord", 993 | "pk": 45, 994 | "fields": { 995 | "user": 5, 996 | "company": 8, 997 | "stocks": 0, 998 | "updated": "2018-05-04T10:47:29.871Z" 999 | } 1000 | }, 1001 | { 1002 | "model": "market.companycmprecord", 1003 | "pk": 1, 1004 | "fields": { 1005 | "company": 2, 1006 | "cmp": "120.00", 1007 | "timestamp": "2018-05-04T10:43:21.807Z", 1008 | "updated": "2018-05-04T10:43:21.807Z" 1009 | } 1010 | }, 1011 | { 1012 | "model": "market.companycmprecord", 1013 | "pk": 2, 1014 | "fields": { 1015 | "company": 1, 1016 | "cmp": "115.74", 1017 | "timestamp": "2018-05-04T10:43:22.295Z", 1018 | "updated": "2018-05-04T10:43:22.295Z" 1019 | } 1020 | }, 1021 | { 1022 | "model": "market.companycmprecord", 1023 | "pk": 3, 1024 | "fields": { 1025 | "company": 3, 1026 | "cmp": "175.36", 1027 | "timestamp": "2018-05-04T10:43:22.559Z", 1028 | "updated": "2018-05-04T10:43:22.559Z" 1029 | } 1030 | }, 1031 | { 1032 | "model": "market.companycmprecord", 1033 | "pk": 4, 1034 | "fields": { 1035 | "company": 5, 1036 | "cmp": "260.11", 1037 | "timestamp": "2018-05-04T10:43:22.771Z", 1038 | "updated": "2018-05-04T10:43:22.771Z" 1039 | } 1040 | }, 1041 | { 1042 | "model": "market.companycmprecord", 1043 | "pk": 5, 1044 | "fields": { 1045 | "company": 4, 1046 | "cmp": "250.21", 1047 | "timestamp": "2018-05-04T10:43:22.948Z", 1048 | "updated": "2018-05-04T10:43:22.948Z" 1049 | } 1050 | }, 1051 | { 1052 | "model": "market.companycmprecord", 1053 | "pk": 6, 1054 | "fields": { 1055 | "company": 6, 1056 | "cmp": "300.33", 1057 | "timestamp": "2018-05-04T10:43:23.170Z", 1058 | "updated": "2018-05-04T10:43:23.170Z" 1059 | } 1060 | }, 1061 | { 1062 | "model": "market.companycmprecord", 1063 | "pk": 7, 1064 | "fields": { 1065 | "company": 9, 1066 | "cmp": "900.14", 1067 | "timestamp": "2018-05-04T10:43:23.392Z", 1068 | "updated": "2018-05-04T10:43:23.392Z" 1069 | } 1070 | }, 1071 | { 1072 | "model": "market.companycmprecord", 1073 | "pk": 8, 1074 | "fields": { 1075 | "company": 7, 1076 | "cmp": "700.21", 1077 | "timestamp": "2018-05-04T10:43:23.592Z", 1078 | "updated": "2018-05-04T10:43:23.592Z" 1079 | } 1080 | }, 1081 | { 1082 | "model": "market.companycmprecord", 1083 | "pk": 9, 1084 | "fields": { 1085 | "company": 8, 1086 | "cmp": "750.21", 1087 | "timestamp": "2018-05-04T10:43:23.814Z", 1088 | "updated": "2018-05-04T10:43:23.814Z" 1089 | } 1090 | }, 1091 | { 1092 | "model": "market.companycmprecord", 1093 | "pk": 10, 1094 | "fields": { 1095 | "company": 2, 1096 | "cmp": "120.00", 1097 | "timestamp": "2018-05-04T10:48:23.975Z", 1098 | "updated": "2018-05-04T10:48:23.975Z" 1099 | } 1100 | }, 1101 | { 1102 | "model": "market.companycmprecord", 1103 | "pk": 11, 1104 | "fields": { 1105 | "company": 1, 1106 | "cmp": "115.74", 1107 | "timestamp": "2018-05-04T10:48:24.170Z", 1108 | "updated": "2018-05-04T10:48:24.170Z" 1109 | } 1110 | }, 1111 | { 1112 | "model": "market.companycmprecord", 1113 | "pk": 12, 1114 | "fields": { 1115 | "company": 3, 1116 | "cmp": "175.36", 1117 | "timestamp": "2018-05-04T10:48:24.354Z", 1118 | "updated": "2018-05-04T10:48:24.354Z" 1119 | } 1120 | }, 1121 | { 1122 | "model": "market.companycmprecord", 1123 | "pk": 13, 1124 | "fields": { 1125 | "company": 5, 1126 | "cmp": "260.11", 1127 | "timestamp": "2018-05-04T10:48:24.535Z", 1128 | "updated": "2018-05-04T10:48:24.535Z" 1129 | } 1130 | }, 1131 | { 1132 | "model": "market.companycmprecord", 1133 | "pk": 14, 1134 | "fields": { 1135 | "company": 4, 1136 | "cmp": "250.21", 1137 | "timestamp": "2018-05-04T10:48:24.935Z", 1138 | "updated": "2018-05-04T10:48:24.935Z" 1139 | } 1140 | }, 1141 | { 1142 | "model": "market.companycmprecord", 1143 | "pk": 15, 1144 | "fields": { 1145 | "company": 6, 1146 | "cmp": "300.33", 1147 | "timestamp": "2018-05-04T10:48:25.108Z", 1148 | "updated": "2018-05-04T10:48:25.108Z" 1149 | } 1150 | }, 1151 | { 1152 | "model": "market.companycmprecord", 1153 | "pk": 16, 1154 | "fields": { 1155 | "company": 9, 1156 | "cmp": "900.14", 1157 | "timestamp": "2018-05-04T10:48:25.307Z", 1158 | "updated": "2018-05-04T10:48:25.307Z" 1159 | } 1160 | }, 1161 | { 1162 | "model": "market.companycmprecord", 1163 | "pk": 17, 1164 | "fields": { 1165 | "company": 7, 1166 | "cmp": "700.21", 1167 | "timestamp": "2018-05-04T10:48:25.489Z", 1168 | "updated": "2018-05-04T10:48:25.489Z" 1169 | } 1170 | }, 1171 | { 1172 | "model": "market.companycmprecord", 1173 | "pk": 18, 1174 | "fields": { 1175 | "company": 8, 1176 | "cmp": "750.21", 1177 | "timestamp": "2018-05-04T10:48:25.689Z", 1178 | "updated": "2018-05-04T10:48:25.689Z" 1179 | } 1180 | }, 1181 | { 1182 | "model": "market.companycmprecord", 1183 | "pk": 19, 1184 | "fields": { 1185 | "company": 2, 1186 | "cmp": "120.00", 1187 | "timestamp": "2018-05-04T10:48:33.975Z", 1188 | "updated": "2018-05-04T10:48:33.975Z" 1189 | } 1190 | }, 1191 | { 1192 | "model": "market.companycmprecord", 1193 | "pk": 20, 1194 | "fields": { 1195 | "company": 1, 1196 | "cmp": "115.74", 1197 | "timestamp": "2018-05-04T10:48:34.238Z", 1198 | "updated": "2018-05-04T10:48:34.238Z" 1199 | } 1200 | }, 1201 | { 1202 | "model": "market.companycmprecord", 1203 | "pk": 21, 1204 | "fields": { 1205 | "company": 3, 1206 | "cmp": "175.36", 1207 | "timestamp": "2018-05-04T10:48:34.409Z", 1208 | "updated": "2018-05-04T10:48:34.409Z" 1209 | } 1210 | }, 1211 | { 1212 | "model": "market.companycmprecord", 1213 | "pk": 22, 1214 | "fields": { 1215 | "company": 5, 1216 | "cmp": "260.11", 1217 | "timestamp": "2018-05-04T10:48:34.609Z", 1218 | "updated": "2018-05-04T10:48:34.609Z" 1219 | } 1220 | }, 1221 | { 1222 | "model": "market.companycmprecord", 1223 | "pk": 23, 1224 | "fields": { 1225 | "company": 4, 1226 | "cmp": "250.21", 1227 | "timestamp": "2018-05-04T10:48:34.797Z", 1228 | "updated": "2018-05-04T10:48:34.797Z" 1229 | } 1230 | }, 1231 | { 1232 | "model": "market.companycmprecord", 1233 | "pk": 24, 1234 | "fields": { 1235 | "company": 6, 1236 | "cmp": "300.33", 1237 | "timestamp": "2018-05-04T10:48:35.008Z", 1238 | "updated": "2018-05-04T10:48:35.008Z" 1239 | } 1240 | }, 1241 | { 1242 | "model": "market.companycmprecord", 1243 | "pk": 25, 1244 | "fields": { 1245 | "company": 9, 1246 | "cmp": "900.14", 1247 | "timestamp": "2018-05-04T10:48:35.185Z", 1248 | "updated": "2018-05-04T10:48:35.185Z" 1249 | } 1250 | }, 1251 | { 1252 | "model": "market.companycmprecord", 1253 | "pk": 26, 1254 | "fields": { 1255 | "company": 7, 1256 | "cmp": "700.21", 1257 | "timestamp": "2018-05-04T10:48:35.396Z", 1258 | "updated": "2018-05-04T10:48:35.396Z" 1259 | } 1260 | }, 1261 | { 1262 | "model": "market.companycmprecord", 1263 | "pk": 27, 1264 | "fields": { 1265 | "company": 8, 1266 | "cmp": "750.21", 1267 | "timestamp": "2018-05-04T10:48:35.592Z", 1268 | "updated": "2018-05-04T10:48:35.592Z" 1269 | } 1270 | }, 1271 | { 1272 | "model": "market.companycmprecord", 1273 | "pk": 28, 1274 | "fields": { 1275 | "company": 2, 1276 | "cmp": "120.00", 1277 | "timestamp": "2018-05-04T10:48:43.970Z", 1278 | "updated": "2018-05-04T10:48:43.970Z" 1279 | } 1280 | }, 1281 | { 1282 | "model": "market.companycmprecord", 1283 | "pk": 29, 1284 | "fields": { 1285 | "company": 1, 1286 | "cmp": "115.74", 1287 | "timestamp": "2018-05-04T10:48:44.149Z", 1288 | "updated": "2018-05-04T10:48:44.149Z" 1289 | } 1290 | }, 1291 | { 1292 | "model": "market.companycmprecord", 1293 | "pk": 30, 1294 | "fields": { 1295 | "company": 3, 1296 | "cmp": "175.36", 1297 | "timestamp": "2018-05-04T10:48:44.349Z", 1298 | "updated": "2018-05-04T10:48:44.349Z" 1299 | } 1300 | }, 1301 | { 1302 | "model": "market.companycmprecord", 1303 | "pk": 31, 1304 | "fields": { 1305 | "company": 5, 1306 | "cmp": "260.11", 1307 | "timestamp": "2018-05-04T10:48:44.549Z", 1308 | "updated": "2018-05-04T10:48:44.549Z" 1309 | } 1310 | }, 1311 | { 1312 | "model": "market.companycmprecord", 1313 | "pk": 32, 1314 | "fields": { 1315 | "company": 4, 1316 | "cmp": "250.21", 1317 | "timestamp": "2018-05-04T10:48:44.722Z", 1318 | "updated": "2018-05-04T10:48:44.722Z" 1319 | } 1320 | }, 1321 | { 1322 | "model": "market.companycmprecord", 1323 | "pk": 33, 1324 | "fields": { 1325 | "company": 6, 1326 | "cmp": "300.33", 1327 | "timestamp": "2018-05-04T10:48:44.915Z", 1328 | "updated": "2018-05-04T10:48:44.915Z" 1329 | } 1330 | }, 1331 | { 1332 | "model": "market.companycmprecord", 1333 | "pk": 34, 1334 | "fields": { 1335 | "company": 9, 1336 | "cmp": "900.14", 1337 | "timestamp": "2018-05-04T10:48:45.099Z", 1338 | "updated": "2018-05-04T10:48:45.099Z" 1339 | } 1340 | }, 1341 | { 1342 | "model": "market.companycmprecord", 1343 | "pk": 35, 1344 | "fields": { 1345 | "company": 7, 1346 | "cmp": "700.21", 1347 | "timestamp": "2018-05-04T10:48:45.281Z", 1348 | "updated": "2018-05-04T10:48:45.282Z" 1349 | } 1350 | }, 1351 | { 1352 | "model": "market.companycmprecord", 1353 | "pk": 36, 1354 | "fields": { 1355 | "company": 8, 1356 | "cmp": "750.21", 1357 | "timestamp": "2018-05-04T10:48:45.469Z", 1358 | "updated": "2018-05-04T10:48:45.469Z" 1359 | } 1360 | }, 1361 | { 1362 | "model": "market.companycmprecord", 1363 | "pk": 37, 1364 | "fields": { 1365 | "company": 2, 1366 | "cmp": "120.00", 1367 | "timestamp": "2018-05-04T10:48:53.972Z", 1368 | "updated": "2018-05-04T10:48:53.973Z" 1369 | } 1370 | }, 1371 | { 1372 | "model": "market.companycmprecord", 1373 | "pk": 38, 1374 | "fields": { 1375 | "company": 1, 1376 | "cmp": "115.74", 1377 | "timestamp": "2018-05-04T10:48:54.275Z", 1378 | "updated": "2018-05-04T10:48:54.275Z" 1379 | } 1380 | }, 1381 | { 1382 | "model": "market.companycmprecord", 1383 | "pk": 39, 1384 | "fields": { 1385 | "company": 3, 1386 | "cmp": "175.36", 1387 | "timestamp": "2018-05-04T10:48:54.477Z", 1388 | "updated": "2018-05-04T10:48:54.477Z" 1389 | } 1390 | }, 1391 | { 1392 | "model": "market.companycmprecord", 1393 | "pk": 40, 1394 | "fields": { 1395 | "company": 5, 1396 | "cmp": "260.11", 1397 | "timestamp": "2018-05-04T10:48:54.666Z", 1398 | "updated": "2018-05-04T10:48:54.666Z" 1399 | } 1400 | }, 1401 | { 1402 | "model": "market.companycmprecord", 1403 | "pk": 41, 1404 | "fields": { 1405 | "company": 4, 1406 | "cmp": "250.21", 1407 | "timestamp": "2018-05-04T10:48:54.850Z", 1408 | "updated": "2018-05-04T10:48:54.850Z" 1409 | } 1410 | }, 1411 | { 1412 | "model": "market.companycmprecord", 1413 | "pk": 42, 1414 | "fields": { 1415 | "company": 6, 1416 | "cmp": "300.33", 1417 | "timestamp": "2018-05-04T10:48:55.044Z", 1418 | "updated": "2018-05-04T10:48:55.044Z" 1419 | } 1420 | }, 1421 | { 1422 | "model": "market.companycmprecord", 1423 | "pk": 43, 1424 | "fields": { 1425 | "company": 9, 1426 | "cmp": "900.14", 1427 | "timestamp": "2018-05-04T10:48:55.228Z", 1428 | "updated": "2018-05-04T10:48:55.228Z" 1429 | } 1430 | }, 1431 | { 1432 | "model": "market.companycmprecord", 1433 | "pk": 44, 1434 | "fields": { 1435 | "company": 7, 1436 | "cmp": "700.21", 1437 | "timestamp": "2018-05-04T10:48:55.417Z", 1438 | "updated": "2018-05-04T10:48:55.417Z" 1439 | } 1440 | }, 1441 | { 1442 | "model": "market.companycmprecord", 1443 | "pk": 45, 1444 | "fields": { 1445 | "company": 8, 1446 | "cmp": "750.21", 1447 | "timestamp": "2018-05-04T10:48:55.598Z", 1448 | "updated": "2018-05-04T10:48:55.598Z" 1449 | } 1450 | }, 1451 | { 1452 | "model": "market.companycmprecord", 1453 | "pk": 46, 1454 | "fields": { 1455 | "company": 2, 1456 | "cmp": "120.00", 1457 | "timestamp": "2018-05-04T10:49:03.971Z", 1458 | "updated": "2018-05-04T10:49:03.971Z" 1459 | } 1460 | }, 1461 | { 1462 | "model": "market.companycmprecord", 1463 | "pk": 47, 1464 | "fields": { 1465 | "company": 1, 1466 | "cmp": "115.74", 1467 | "timestamp": "2018-05-04T10:49:04.259Z", 1468 | "updated": "2018-05-04T10:49:04.259Z" 1469 | } 1470 | }, 1471 | { 1472 | "model": "market.companycmprecord", 1473 | "pk": 48, 1474 | "fields": { 1475 | "company": 3, 1476 | "cmp": "175.36", 1477 | "timestamp": "2018-05-04T10:49:04.473Z", 1478 | "updated": "2018-05-04T10:49:04.473Z" 1479 | } 1480 | }, 1481 | { 1482 | "model": "market.companycmprecord", 1483 | "pk": 49, 1484 | "fields": { 1485 | "company": 5, 1486 | "cmp": "260.11", 1487 | "timestamp": "2018-05-04T10:49:04.650Z", 1488 | "updated": "2018-05-04T10:49:04.650Z" 1489 | } 1490 | }, 1491 | { 1492 | "model": "market.companycmprecord", 1493 | "pk": 50, 1494 | "fields": { 1495 | "company": 4, 1496 | "cmp": "250.21", 1497 | "timestamp": "2018-05-04T10:49:04.828Z", 1498 | "updated": "2018-05-04T10:49:04.828Z" 1499 | } 1500 | }, 1501 | { 1502 | "model": "market.companycmprecord", 1503 | "pk": 51, 1504 | "fields": { 1505 | "company": 6, 1506 | "cmp": "300.33", 1507 | "timestamp": "2018-05-04T10:49:04.995Z", 1508 | "updated": "2018-05-04T10:49:04.995Z" 1509 | } 1510 | }, 1511 | { 1512 | "model": "market.companycmprecord", 1513 | "pk": 52, 1514 | "fields": { 1515 | "company": 9, 1516 | "cmp": "900.14", 1517 | "timestamp": "2018-05-04T10:49:05.195Z", 1518 | "updated": "2018-05-04T10:49:05.195Z" 1519 | } 1520 | }, 1521 | { 1522 | "model": "market.companycmprecord", 1523 | "pk": 53, 1524 | "fields": { 1525 | "company": 7, 1526 | "cmp": "700.21", 1527 | "timestamp": "2018-05-04T10:49:05.383Z", 1528 | "updated": "2018-05-04T10:49:05.383Z" 1529 | } 1530 | }, 1531 | { 1532 | "model": "market.companycmprecord", 1533 | "pk": 54, 1534 | "fields": { 1535 | "company": 8, 1536 | "cmp": "750.21", 1537 | "timestamp": "2018-05-04T10:49:05.549Z", 1538 | "updated": "2018-05-04T10:49:05.549Z" 1539 | } 1540 | }, 1541 | { 1542 | "model": "market.companycmprecord", 1543 | "pk": 55, 1544 | "fields": { 1545 | "company": 2, 1546 | "cmp": "120.00", 1547 | "timestamp": "2018-05-04T10:49:13.970Z", 1548 | "updated": "2018-05-04T10:49:13.970Z" 1549 | } 1550 | }, 1551 | { 1552 | "model": "market.companycmprecord", 1553 | "pk": 56, 1554 | "fields": { 1555 | "company": 1, 1556 | "cmp": "115.74", 1557 | "timestamp": "2018-05-04T10:49:14.158Z", 1558 | "updated": "2018-05-04T10:49:14.158Z" 1559 | } 1560 | }, 1561 | { 1562 | "model": "market.companycmprecord", 1563 | "pk": 57, 1564 | "fields": { 1565 | "company": 3, 1566 | "cmp": "175.36", 1567 | "timestamp": "2018-05-04T10:49:14.303Z", 1568 | "updated": "2018-05-04T10:49:14.303Z" 1569 | } 1570 | }, 1571 | { 1572 | "model": "market.companycmprecord", 1573 | "pk": 58, 1574 | "fields": { 1575 | "company": 5, 1576 | "cmp": "260.11", 1577 | "timestamp": "2018-05-04T10:49:14.458Z", 1578 | "updated": "2018-05-04T10:49:14.458Z" 1579 | } 1580 | }, 1581 | { 1582 | "model": "market.companycmprecord", 1583 | "pk": 59, 1584 | "fields": { 1585 | "company": 4, 1586 | "cmp": "250.21", 1587 | "timestamp": "2018-05-04T10:49:14.668Z", 1588 | "updated": "2018-05-04T10:49:14.668Z" 1589 | } 1590 | }, 1591 | { 1592 | "model": "market.companycmprecord", 1593 | "pk": 60, 1594 | "fields": { 1595 | "company": 6, 1596 | "cmp": "300.33", 1597 | "timestamp": "2018-05-04T10:49:14.857Z", 1598 | "updated": "2018-05-04T10:49:14.857Z" 1599 | } 1600 | }, 1601 | { 1602 | "model": "market.companycmprecord", 1603 | "pk": 61, 1604 | "fields": { 1605 | "company": 9, 1606 | "cmp": "900.14", 1607 | "timestamp": "2018-05-04T10:49:15.036Z", 1608 | "updated": "2018-05-04T10:49:15.036Z" 1609 | } 1610 | }, 1611 | { 1612 | "model": "market.companycmprecord", 1613 | "pk": 62, 1614 | "fields": { 1615 | "company": 7, 1616 | "cmp": "700.21", 1617 | "timestamp": "2018-05-04T10:49:15.189Z", 1618 | "updated": "2018-05-04T10:49:15.189Z" 1619 | } 1620 | }, 1621 | { 1622 | "model": "market.companycmprecord", 1623 | "pk": 63, 1624 | "fields": { 1625 | "company": 8, 1626 | "cmp": "750.21", 1627 | "timestamp": "2018-05-04T10:49:15.356Z", 1628 | "updated": "2018-05-04T10:49:15.356Z" 1629 | } 1630 | }, 1631 | { 1632 | "model": "market.companycmprecord", 1633 | "pk": 64, 1634 | "fields": { 1635 | "company": 2, 1636 | "cmp": "120.00", 1637 | "timestamp": "2018-05-04T10:49:23.972Z", 1638 | "updated": "2018-05-04T10:49:23.972Z" 1639 | } 1640 | }, 1641 | { 1642 | "model": "market.companycmprecord", 1643 | "pk": 65, 1644 | "fields": { 1645 | "company": 1, 1646 | "cmp": "115.74", 1647 | "timestamp": "2018-05-04T10:49:24.198Z", 1648 | "updated": "2018-05-04T10:49:24.198Z" 1649 | } 1650 | }, 1651 | { 1652 | "model": "market.companycmprecord", 1653 | "pk": 66, 1654 | "fields": { 1655 | "company": 3, 1656 | "cmp": "175.36", 1657 | "timestamp": "2018-05-04T10:49:24.387Z", 1658 | "updated": "2018-05-04T10:49:24.387Z" 1659 | } 1660 | }, 1661 | { 1662 | "model": "market.companycmprecord", 1663 | "pk": 67, 1664 | "fields": { 1665 | "company": 5, 1666 | "cmp": "260.11", 1667 | "timestamp": "2018-05-04T10:49:24.553Z", 1668 | "updated": "2018-05-04T10:49:24.553Z" 1669 | } 1670 | }, 1671 | { 1672 | "model": "market.companycmprecord", 1673 | "pk": 68, 1674 | "fields": { 1675 | "company": 4, 1676 | "cmp": "250.21", 1677 | "timestamp": "2018-05-04T10:49:24.719Z", 1678 | "updated": "2018-05-04T10:49:24.719Z" 1679 | } 1680 | }, 1681 | { 1682 | "model": "market.companycmprecord", 1683 | "pk": 69, 1684 | "fields": { 1685 | "company": 6, 1686 | "cmp": "300.33", 1687 | "timestamp": "2018-05-04T10:49:24.897Z", 1688 | "updated": "2018-05-04T10:49:24.897Z" 1689 | } 1690 | }, 1691 | { 1692 | "model": "market.companycmprecord", 1693 | "pk": 70, 1694 | "fields": { 1695 | "company": 9, 1696 | "cmp": "900.14", 1697 | "timestamp": "2018-05-04T10:49:25.063Z", 1698 | "updated": "2018-05-04T10:49:25.063Z" 1699 | } 1700 | }, 1701 | { 1702 | "model": "market.companycmprecord", 1703 | "pk": 71, 1704 | "fields": { 1705 | "company": 7, 1706 | "cmp": "700.21", 1707 | "timestamp": "2018-05-04T10:49:25.249Z", 1708 | "updated": "2018-05-04T10:49:25.249Z" 1709 | } 1710 | }, 1711 | { 1712 | "model": "market.companycmprecord", 1713 | "pk": 72, 1714 | "fields": { 1715 | "company": 8, 1716 | "cmp": "750.21", 1717 | "timestamp": "2018-05-04T10:49:25.429Z", 1718 | "updated": "2018-05-04T10:49:25.429Z" 1719 | } 1720 | }, 1721 | { 1722 | "model": "market.companycmprecord", 1723 | "pk": 73, 1724 | "fields": { 1725 | "company": 2, 1726 | "cmp": "120.00", 1727 | "timestamp": "2018-05-04T10:49:33.972Z", 1728 | "updated": "2018-05-04T10:49:33.972Z" 1729 | } 1730 | }, 1731 | { 1732 | "model": "market.companycmprecord", 1733 | "pk": 74, 1734 | "fields": { 1735 | "company": 1, 1736 | "cmp": "115.74", 1737 | "timestamp": "2018-05-04T10:49:34.150Z", 1738 | "updated": "2018-05-04T10:49:34.150Z" 1739 | } 1740 | }, 1741 | { 1742 | "model": "market.companycmprecord", 1743 | "pk": 75, 1744 | "fields": { 1745 | "company": 3, 1746 | "cmp": "175.36", 1747 | "timestamp": "2018-05-04T10:49:34.349Z", 1748 | "updated": "2018-05-04T10:49:34.349Z" 1749 | } 1750 | }, 1751 | { 1752 | "model": "market.companycmprecord", 1753 | "pk": 76, 1754 | "fields": { 1755 | "company": 5, 1756 | "cmp": "260.11", 1757 | "timestamp": "2018-05-04T10:49:34.549Z", 1758 | "updated": "2018-05-04T10:49:34.549Z" 1759 | } 1760 | }, 1761 | { 1762 | "model": "market.companycmprecord", 1763 | "pk": 77, 1764 | "fields": { 1765 | "company": 4, 1766 | "cmp": "250.21", 1767 | "timestamp": "2018-05-04T10:49:34.715Z", 1768 | "updated": "2018-05-04T10:49:34.715Z" 1769 | } 1770 | }, 1771 | { 1772 | "model": "market.companycmprecord", 1773 | "pk": 78, 1774 | "fields": { 1775 | "company": 6, 1776 | "cmp": "300.33", 1777 | "timestamp": "2018-05-04T10:49:34.893Z", 1778 | "updated": "2018-05-04T10:49:34.893Z" 1779 | } 1780 | }, 1781 | { 1782 | "model": "market.companycmprecord", 1783 | "pk": 79, 1784 | "fields": { 1785 | "company": 9, 1786 | "cmp": "900.14", 1787 | "timestamp": "2018-05-04T10:49:35.092Z", 1788 | "updated": "2018-05-04T10:49:35.092Z" 1789 | } 1790 | }, 1791 | { 1792 | "model": "market.companycmprecord", 1793 | "pk": 80, 1794 | "fields": { 1795 | "company": 7, 1796 | "cmp": "700.21", 1797 | "timestamp": "2018-05-04T10:49:35.270Z", 1798 | "updated": "2018-05-04T10:49:35.270Z" 1799 | } 1800 | }, 1801 | { 1802 | "model": "market.companycmprecord", 1803 | "pk": 81, 1804 | "fields": { 1805 | "company": 8, 1806 | "cmp": "750.21", 1807 | "timestamp": "2018-05-04T10:49:35.455Z", 1808 | "updated": "2018-05-04T10:49:35.455Z" 1809 | } 1810 | }, 1811 | { 1812 | "model": "market.companycmprecord", 1813 | "pk": 82, 1814 | "fields": { 1815 | "company": 2, 1816 | "cmp": "120.00", 1817 | "timestamp": "2018-05-04T10:49:43.998Z", 1818 | "updated": "2018-05-04T10:49:43.998Z" 1819 | } 1820 | }, 1821 | { 1822 | "model": "market.companycmprecord", 1823 | "pk": 83, 1824 | "fields": { 1825 | "company": 1, 1826 | "cmp": "115.74", 1827 | "timestamp": "2018-05-04T10:49:44.622Z", 1828 | "updated": "2018-05-04T10:49:44.622Z" 1829 | } 1830 | }, 1831 | { 1832 | "model": "market.companycmprecord", 1833 | "pk": 84, 1834 | "fields": { 1835 | "company": 3, 1836 | "cmp": "175.36", 1837 | "timestamp": "2018-05-04T10:49:44.796Z", 1838 | "updated": "2018-05-04T10:49:44.796Z" 1839 | } 1840 | }, 1841 | { 1842 | "model": "market.companycmprecord", 1843 | "pk": 85, 1844 | "fields": { 1845 | "company": 5, 1846 | "cmp": "260.11", 1847 | "timestamp": "2018-05-04T10:49:45.000Z", 1848 | "updated": "2018-05-04T10:49:45.000Z" 1849 | } 1850 | }, 1851 | { 1852 | "model": "market.companycmprecord", 1853 | "pk": 86, 1854 | "fields": { 1855 | "company": 4, 1856 | "cmp": "250.21", 1857 | "timestamp": "2018-05-04T10:49:45.188Z", 1858 | "updated": "2018-05-04T10:49:45.188Z" 1859 | } 1860 | }, 1861 | { 1862 | "model": "market.companycmprecord", 1863 | "pk": 87, 1864 | "fields": { 1865 | "company": 6, 1866 | "cmp": "300.33", 1867 | "timestamp": "2018-05-04T10:49:45.399Z", 1868 | "updated": "2018-05-04T10:49:45.399Z" 1869 | } 1870 | }, 1871 | { 1872 | "model": "market.companycmprecord", 1873 | "pk": 88, 1874 | "fields": { 1875 | "company": 9, 1876 | "cmp": "900.14", 1877 | "timestamp": "2018-05-04T10:49:45.598Z", 1878 | "updated": "2018-05-04T10:49:45.599Z" 1879 | } 1880 | }, 1881 | { 1882 | "model": "market.companycmprecord", 1883 | "pk": 89, 1884 | "fields": { 1885 | "company": 7, 1886 | "cmp": "700.21", 1887 | "timestamp": "2018-05-04T10:49:45.798Z", 1888 | "updated": "2018-05-04T10:49:45.798Z" 1889 | } 1890 | }, 1891 | { 1892 | "model": "market.companycmprecord", 1893 | "pk": 90, 1894 | "fields": { 1895 | "company": 8, 1896 | "cmp": "750.21", 1897 | "timestamp": "2018-05-04T10:49:45.998Z", 1898 | "updated": "2018-05-04T10:49:45.998Z" 1899 | } 1900 | }, 1901 | { 1902 | "model": "market.companycmprecord", 1903 | "pk": 91, 1904 | "fields": { 1905 | "company": 2, 1906 | "cmp": "120.00", 1907 | "timestamp": "2018-05-04T10:49:54.210Z", 1908 | "updated": "2018-05-04T10:49:54.210Z" 1909 | } 1910 | }, 1911 | { 1912 | "model": "market.companycmprecord", 1913 | "pk": 92, 1914 | "fields": { 1915 | "company": 1, 1916 | "cmp": "115.74", 1917 | "timestamp": "2018-05-04T10:49:54.406Z", 1918 | "updated": "2018-05-04T10:49:54.407Z" 1919 | } 1920 | }, 1921 | { 1922 | "model": "market.companycmprecord", 1923 | "pk": 93, 1924 | "fields": { 1925 | "company": 3, 1926 | "cmp": "175.36", 1927 | "timestamp": "2018-05-04T10:49:54.592Z", 1928 | "updated": "2018-05-04T10:49:54.592Z" 1929 | } 1930 | }, 1931 | { 1932 | "model": "market.companycmprecord", 1933 | "pk": 94, 1934 | "fields": { 1935 | "company": 5, 1936 | "cmp": "260.11", 1937 | "timestamp": "2018-05-04T10:49:54.783Z", 1938 | "updated": "2018-05-04T10:49:54.783Z" 1939 | } 1940 | }, 1941 | { 1942 | "model": "market.companycmprecord", 1943 | "pk": 95, 1944 | "fields": { 1945 | "company": 4, 1946 | "cmp": "250.21", 1947 | "timestamp": "2018-05-04T10:49:54.957Z", 1948 | "updated": "2018-05-04T10:49:54.957Z" 1949 | } 1950 | }, 1951 | { 1952 | "model": "market.companycmprecord", 1953 | "pk": 96, 1954 | "fields": { 1955 | "company": 6, 1956 | "cmp": "300.33", 1957 | "timestamp": "2018-05-04T10:49:55.161Z", 1958 | "updated": "2018-05-04T10:49:55.161Z" 1959 | } 1960 | }, 1961 | { 1962 | "model": "market.companycmprecord", 1963 | "pk": 97, 1964 | "fields": { 1965 | "company": 9, 1966 | "cmp": "900.14", 1967 | "timestamp": "2018-05-04T10:49:55.360Z", 1968 | "updated": "2018-05-04T10:49:55.360Z" 1969 | } 1970 | }, 1971 | { 1972 | "model": "market.companycmprecord", 1973 | "pk": 98, 1974 | "fields": { 1975 | "company": 7, 1976 | "cmp": "700.21", 1977 | "timestamp": "2018-05-04T10:49:55.582Z", 1978 | "updated": "2018-05-04T10:49:55.582Z" 1979 | } 1980 | }, 1981 | { 1982 | "model": "market.companycmprecord", 1983 | "pk": 99, 1984 | "fields": { 1985 | "company": 8, 1986 | "cmp": "750.21", 1987 | "timestamp": "2018-05-04T10:49:55.767Z", 1988 | "updated": "2018-05-04T10:49:55.768Z" 1989 | } 1990 | }, 1991 | { 1992 | "model": "market.companycmprecord", 1993 | "pk": 100, 1994 | "fields": { 1995 | "company": 2, 1996 | "cmp": "120.00", 1997 | "timestamp": "2018-05-04T10:50:54.067Z", 1998 | "updated": "2018-05-04T10:50:54.068Z" 1999 | } 2000 | }, 2001 | { 2002 | "model": "market.companycmprecord", 2003 | "pk": 101, 2004 | "fields": { 2005 | "company": 1, 2006 | "cmp": "115.74", 2007 | "timestamp": "2018-05-04T10:50:54.387Z", 2008 | "updated": "2018-05-04T10:50:54.387Z" 2009 | } 2010 | }, 2011 | { 2012 | "model": "market.companycmprecord", 2013 | "pk": 102, 2014 | "fields": { 2015 | "company": 3, 2016 | "cmp": "175.36", 2017 | "timestamp": "2018-05-04T10:50:54.591Z", 2018 | "updated": "2018-05-04T10:50:54.591Z" 2019 | } 2020 | }, 2021 | { 2022 | "model": "market.companycmprecord", 2023 | "pk": 103, 2024 | "fields": { 2025 | "company": 5, 2026 | "cmp": "260.11", 2027 | "timestamp": "2018-05-04T10:50:54.798Z", 2028 | "updated": "2018-05-04T10:50:54.798Z" 2029 | } 2030 | }, 2031 | { 2032 | "model": "market.companycmprecord", 2033 | "pk": 104, 2034 | "fields": { 2035 | "company": 4, 2036 | "cmp": "250.21", 2037 | "timestamp": "2018-05-04T10:50:55.024Z", 2038 | "updated": "2018-05-04T10:50:55.024Z" 2039 | } 2040 | }, 2041 | { 2042 | "model": "market.companycmprecord", 2043 | "pk": 105, 2044 | "fields": { 2045 | "company": 6, 2046 | "cmp": "300.33", 2047 | "timestamp": "2018-05-04T10:50:55.209Z", 2048 | "updated": "2018-05-04T10:50:55.209Z" 2049 | } 2050 | }, 2051 | { 2052 | "model": "market.companycmprecord", 2053 | "pk": 106, 2054 | "fields": { 2055 | "company": 9, 2056 | "cmp": "900.14", 2057 | "timestamp": "2018-05-04T10:50:55.533Z", 2058 | "updated": "2018-05-04T10:50:55.534Z" 2059 | } 2060 | }, 2061 | { 2062 | "model": "market.companycmprecord", 2063 | "pk": 107, 2064 | "fields": { 2065 | "company": 7, 2066 | "cmp": "700.21", 2067 | "timestamp": "2018-05-04T10:50:55.730Z", 2068 | "updated": "2018-05-04T10:50:55.730Z" 2069 | } 2070 | }, 2071 | { 2072 | "model": "market.companycmprecord", 2073 | "pk": 108, 2074 | "fields": { 2075 | "company": 8, 2076 | "cmp": "750.21", 2077 | "timestamp": "2018-05-04T10:50:55.918Z", 2078 | "updated": "2018-05-04T10:50:55.918Z" 2079 | } 2080 | }, 2081 | { 2082 | "model": "market.companycmprecord", 2083 | "pk": 109, 2084 | "fields": { 2085 | "company": 2, 2086 | "cmp": "120.00", 2087 | "timestamp": "2018-05-04T10:51:54.067Z", 2088 | "updated": "2018-05-04T10:51:54.067Z" 2089 | } 2090 | }, 2091 | { 2092 | "model": "market.companycmprecord", 2093 | "pk": 110, 2094 | "fields": { 2095 | "company": 1, 2096 | "cmp": "115.74", 2097 | "timestamp": "2018-05-04T10:51:54.283Z", 2098 | "updated": "2018-05-04T10:51:54.284Z" 2099 | } 2100 | }, 2101 | { 2102 | "model": "market.companycmprecord", 2103 | "pk": 111, 2104 | "fields": { 2105 | "company": 3, 2106 | "cmp": "175.36", 2107 | "timestamp": "2018-05-04T10:51:54.487Z", 2108 | "updated": "2018-05-04T10:51:54.487Z" 2109 | } 2110 | }, 2111 | { 2112 | "model": "market.companycmprecord", 2113 | "pk": 112, 2114 | "fields": { 2115 | "company": 5, 2116 | "cmp": "260.11", 2117 | "timestamp": "2018-05-04T10:51:54.665Z", 2118 | "updated": "2018-05-04T10:51:54.665Z" 2119 | } 2120 | }, 2121 | { 2122 | "model": "market.companycmprecord", 2123 | "pk": 113, 2124 | "fields": { 2125 | "company": 4, 2126 | "cmp": "250.21", 2127 | "timestamp": "2018-05-04T10:51:54.842Z", 2128 | "updated": "2018-05-04T10:51:54.842Z" 2129 | } 2130 | }, 2131 | { 2132 | "model": "market.companycmprecord", 2133 | "pk": 114, 2134 | "fields": { 2135 | "company": 6, 2136 | "cmp": "300.33", 2137 | "timestamp": "2018-05-04T10:51:55.041Z", 2138 | "updated": "2018-05-04T10:51:55.042Z" 2139 | } 2140 | }, 2141 | { 2142 | "model": "market.companycmprecord", 2143 | "pk": 115, 2144 | "fields": { 2145 | "company": 9, 2146 | "cmp": "900.14", 2147 | "timestamp": "2018-05-04T10:51:55.242Z", 2148 | "updated": "2018-05-04T10:51:55.242Z" 2149 | } 2150 | }, 2151 | { 2152 | "model": "market.companycmprecord", 2153 | "pk": 116, 2154 | "fields": { 2155 | "company": 7, 2156 | "cmp": "700.21", 2157 | "timestamp": "2018-05-04T10:51:55.418Z", 2158 | "updated": "2018-05-04T10:51:55.418Z" 2159 | } 2160 | }, 2161 | { 2162 | "model": "market.companycmprecord", 2163 | "pk": 117, 2164 | "fields": { 2165 | "company": 8, 2166 | "cmp": "750.21", 2167 | "timestamp": "2018-05-04T10:51:55.614Z", 2168 | "updated": "2018-05-04T10:51:55.614Z" 2169 | } 2170 | }, 2171 | { 2172 | "model": "market.companycmprecord", 2173 | "pk": 118, 2174 | "fields": { 2175 | "company": 2, 2176 | "cmp": "120.00", 2177 | "timestamp": "2018-05-04T10:52:54.073Z", 2178 | "updated": "2018-05-04T10:52:54.073Z" 2179 | } 2180 | }, 2181 | { 2182 | "model": "market.companycmprecord", 2183 | "pk": 119, 2184 | "fields": { 2185 | "company": 1, 2186 | "cmp": "115.74", 2187 | "timestamp": "2018-05-04T10:52:54.515Z", 2188 | "updated": "2018-05-04T10:52:54.515Z" 2189 | } 2190 | }, 2191 | { 2192 | "model": "market.companycmprecord", 2193 | "pk": 120, 2194 | "fields": { 2195 | "company": 3, 2196 | "cmp": "175.36", 2197 | "timestamp": "2018-05-04T10:52:54.737Z", 2198 | "updated": "2018-05-04T10:52:54.737Z" 2199 | } 2200 | }, 2201 | { 2202 | "model": "market.companycmprecord", 2203 | "pk": 121, 2204 | "fields": { 2205 | "company": 5, 2206 | "cmp": "260.11", 2207 | "timestamp": "2018-05-04T10:52:54.956Z", 2208 | "updated": "2018-05-04T10:52:54.956Z" 2209 | } 2210 | }, 2211 | { 2212 | "model": "market.companycmprecord", 2213 | "pk": 122, 2214 | "fields": { 2215 | "company": 4, 2216 | "cmp": "250.21", 2217 | "timestamp": "2018-05-04T10:52:55.166Z", 2218 | "updated": "2018-05-04T10:52:55.167Z" 2219 | } 2220 | }, 2221 | { 2222 | "model": "market.companycmprecord", 2223 | "pk": 123, 2224 | "fields": { 2225 | "company": 6, 2226 | "cmp": "300.33", 2227 | "timestamp": "2018-05-04T10:52:55.370Z", 2228 | "updated": "2018-05-04T10:52:55.371Z" 2229 | } 2230 | }, 2231 | { 2232 | "model": "market.companycmprecord", 2233 | "pk": 124, 2234 | "fields": { 2235 | "company": 9, 2236 | "cmp": "900.14", 2237 | "timestamp": "2018-05-04T10:52:55.603Z", 2238 | "updated": "2018-05-04T10:52:55.603Z" 2239 | } 2240 | }, 2241 | { 2242 | "model": "market.companycmprecord", 2243 | "pk": 125, 2244 | "fields": { 2245 | "company": 7, 2246 | "cmp": "700.21", 2247 | "timestamp": "2018-05-04T10:52:55.814Z", 2248 | "updated": "2018-05-04T10:52:55.814Z" 2249 | } 2250 | }, 2251 | { 2252 | "model": "market.companycmprecord", 2253 | "pk": 126, 2254 | "fields": { 2255 | "company": 8, 2256 | "cmp": "750.21", 2257 | "timestamp": "2018-05-04T10:52:56.037Z", 2258 | "updated": "2018-05-04T10:52:56.037Z" 2259 | } 2260 | }, 2261 | { 2262 | "model": "market.companycmprecord", 2263 | "pk": 127, 2264 | "fields": { 2265 | "company": 2, 2266 | "cmp": "120.00", 2267 | "timestamp": "2018-05-04T10:53:54.065Z", 2268 | "updated": "2018-05-04T10:53:54.065Z" 2269 | } 2270 | }, 2271 | { 2272 | "model": "market.companycmprecord", 2273 | "pk": 128, 2274 | "fields": { 2275 | "company": 1, 2276 | "cmp": "115.74", 2277 | "timestamp": "2018-05-04T10:53:54.290Z", 2278 | "updated": "2018-05-04T10:53:54.290Z" 2279 | } 2280 | }, 2281 | { 2282 | "model": "market.companycmprecord", 2283 | "pk": 129, 2284 | "fields": { 2285 | "company": 3, 2286 | "cmp": "175.36", 2287 | "timestamp": "2018-05-04T10:53:54.489Z", 2288 | "updated": "2018-05-04T10:53:54.490Z" 2289 | } 2290 | }, 2291 | { 2292 | "model": "market.companycmprecord", 2293 | "pk": 130, 2294 | "fields": { 2295 | "company": 5, 2296 | "cmp": "260.11", 2297 | "timestamp": "2018-05-04T10:53:54.674Z", 2298 | "updated": "2018-05-04T10:53:54.674Z" 2299 | } 2300 | }, 2301 | { 2302 | "model": "market.companycmprecord", 2303 | "pk": 131, 2304 | "fields": { 2305 | "company": 4, 2306 | "cmp": "250.21", 2307 | "timestamp": "2018-05-04T10:53:54.888Z", 2308 | "updated": "2018-05-04T10:53:54.888Z" 2309 | } 2310 | }, 2311 | { 2312 | "model": "market.companycmprecord", 2313 | "pk": 132, 2314 | "fields": { 2315 | "company": 6, 2316 | "cmp": "300.33", 2317 | "timestamp": "2018-05-04T10:53:55.077Z", 2318 | "updated": "2018-05-04T10:53:55.077Z" 2319 | } 2320 | }, 2321 | { 2322 | "model": "market.companycmprecord", 2323 | "pk": 133, 2324 | "fields": { 2325 | "company": 9, 2326 | "cmp": "900.14", 2327 | "timestamp": "2018-05-04T10:53:55.273Z", 2328 | "updated": "2018-05-04T10:53:55.273Z" 2329 | } 2330 | }, 2331 | { 2332 | "model": "market.companycmprecord", 2333 | "pk": 134, 2334 | "fields": { 2335 | "company": 7, 2336 | "cmp": "700.21", 2337 | "timestamp": "2018-05-04T10:53:55.499Z", 2338 | "updated": "2018-05-04T10:53:55.500Z" 2339 | } 2340 | }, 2341 | { 2342 | "model": "market.companycmprecord", 2343 | "pk": 135, 2344 | "fields": { 2345 | "company": 8, 2346 | "cmp": "750.21", 2347 | "timestamp": "2018-05-04T10:53:55.706Z", 2348 | "updated": "2018-05-04T10:53:55.706Z" 2349 | } 2350 | }, 2351 | { 2352 | "model": "market.companycmprecord", 2353 | "pk": 136, 2354 | "fields": { 2355 | "company": 2, 2356 | "cmp": "120.00", 2357 | "timestamp": "2018-05-04T10:54:54.067Z", 2358 | "updated": "2018-05-04T10:54:54.067Z" 2359 | } 2360 | }, 2361 | { 2362 | "model": "market.companycmprecord", 2363 | "pk": 137, 2364 | "fields": { 2365 | "company": 1, 2366 | "cmp": "115.75", 2367 | "timestamp": "2018-05-04T10:54:54.248Z", 2368 | "updated": "2018-05-04T10:54:54.248Z" 2369 | } 2370 | }, 2371 | { 2372 | "model": "market.companycmprecord", 2373 | "pk": 138, 2374 | "fields": { 2375 | "company": 3, 2376 | "cmp": "175.36", 2377 | "timestamp": "2018-05-04T10:54:54.441Z", 2378 | "updated": "2018-05-04T10:54:54.441Z" 2379 | } 2380 | }, 2381 | { 2382 | "model": "market.companycmprecord", 2383 | "pk": 139, 2384 | "fields": { 2385 | "company": 5, 2386 | "cmp": "260.11", 2387 | "timestamp": "2018-05-04T10:54:54.618Z", 2388 | "updated": "2018-05-04T10:54:54.618Z" 2389 | } 2390 | }, 2391 | { 2392 | "model": "market.companycmprecord", 2393 | "pk": 140, 2394 | "fields": { 2395 | "company": 4, 2396 | "cmp": "250.21", 2397 | "timestamp": "2018-05-04T10:54:54.807Z", 2398 | "updated": "2018-05-04T10:54:54.807Z" 2399 | } 2400 | }, 2401 | { 2402 | "model": "market.companycmprecord", 2403 | "pk": 141, 2404 | "fields": { 2405 | "company": 6, 2406 | "cmp": "300.33", 2407 | "timestamp": "2018-05-04T10:54:54.981Z", 2408 | "updated": "2018-05-04T10:54:54.981Z" 2409 | } 2410 | }, 2411 | { 2412 | "model": "market.companycmprecord", 2413 | "pk": 142, 2414 | "fields": { 2415 | "company": 9, 2416 | "cmp": "900.14", 2417 | "timestamp": "2018-05-04T10:54:55.269Z", 2418 | "updated": "2018-05-04T10:54:55.269Z" 2419 | } 2420 | }, 2421 | { 2422 | "model": "market.companycmprecord", 2423 | "pk": 143, 2424 | "fields": { 2425 | "company": 7, 2426 | "cmp": "700.21", 2427 | "timestamp": "2018-05-04T10:54:55.447Z", 2428 | "updated": "2018-05-04T10:54:55.447Z" 2429 | } 2430 | }, 2431 | { 2432 | "model": "market.companycmprecord", 2433 | "pk": 144, 2434 | "fields": { 2435 | "company": 8, 2436 | "cmp": "750.21", 2437 | "timestamp": "2018-05-04T10:54:55.661Z", 2438 | "updated": "2018-05-04T10:54:55.661Z" 2439 | } 2440 | }, 2441 | { 2442 | "model": "market.companycmprecord", 2443 | "pk": 145, 2444 | "fields": { 2445 | "company": 2, 2446 | "cmp": "120.00", 2447 | "timestamp": "2018-05-04T10:55:54.063Z", 2448 | "updated": "2018-05-04T10:55:54.063Z" 2449 | } 2450 | }, 2451 | { 2452 | "model": "market.companycmprecord", 2453 | "pk": 146, 2454 | "fields": { 2455 | "company": 1, 2456 | "cmp": "115.75", 2457 | "timestamp": "2018-05-04T10:55:54.288Z", 2458 | "updated": "2018-05-04T10:55:54.288Z" 2459 | } 2460 | }, 2461 | { 2462 | "model": "market.companycmprecord", 2463 | "pk": 147, 2464 | "fields": { 2465 | "company": 3, 2466 | "cmp": "175.36", 2467 | "timestamp": "2018-05-04T10:55:54.514Z", 2468 | "updated": "2018-05-04T10:55:54.514Z" 2469 | } 2470 | }, 2471 | { 2472 | "model": "market.companycmprecord", 2473 | "pk": 148, 2474 | "fields": { 2475 | "company": 5, 2476 | "cmp": "260.11", 2477 | "timestamp": "2018-05-04T10:55:54.725Z", 2478 | "updated": "2018-05-04T10:55:54.725Z" 2479 | } 2480 | }, 2481 | { 2482 | "model": "market.companycmprecord", 2483 | "pk": 149, 2484 | "fields": { 2485 | "company": 4, 2486 | "cmp": "250.21", 2487 | "timestamp": "2018-05-04T10:55:54.958Z", 2488 | "updated": "2018-05-04T10:55:54.958Z" 2489 | } 2490 | }, 2491 | { 2492 | "model": "market.companycmprecord", 2493 | "pk": 150, 2494 | "fields": { 2495 | "company": 6, 2496 | "cmp": "300.33", 2497 | "timestamp": "2018-05-04T10:55:55.157Z", 2498 | "updated": "2018-05-04T10:55:55.157Z" 2499 | } 2500 | }, 2501 | { 2502 | "model": "market.companycmprecord", 2503 | "pk": 151, 2504 | "fields": { 2505 | "company": 9, 2506 | "cmp": "900.14", 2507 | "timestamp": "2018-05-04T10:55:55.353Z", 2508 | "updated": "2018-05-04T10:55:55.353Z" 2509 | } 2510 | }, 2511 | { 2512 | "model": "market.companycmprecord", 2513 | "pk": 152, 2514 | "fields": { 2515 | "company": 7, 2516 | "cmp": "700.21", 2517 | "timestamp": "2018-05-04T10:55:55.557Z", 2518 | "updated": "2018-05-04T10:55:55.557Z" 2519 | } 2520 | }, 2521 | { 2522 | "model": "market.companycmprecord", 2523 | "pk": 153, 2524 | "fields": { 2525 | "company": 8, 2526 | "cmp": "750.21", 2527 | "timestamp": "2018-05-04T10:55:55.779Z", 2528 | "updated": "2018-05-04T10:55:55.779Z" 2529 | } 2530 | }, 2531 | { 2532 | "model": "market.companycmprecord", 2533 | "pk": 154, 2534 | "fields": { 2535 | "company": 2, 2536 | "cmp": "120.00", 2537 | "timestamp": "2018-05-04T10:56:54.065Z", 2538 | "updated": "2018-05-04T10:56:54.065Z" 2539 | } 2540 | }, 2541 | { 2542 | "model": "market.companycmprecord", 2543 | "pk": 155, 2544 | "fields": { 2545 | "company": 1, 2546 | "cmp": "115.78", 2547 | "timestamp": "2018-05-04T10:56:54.310Z", 2548 | "updated": "2018-05-04T10:56:54.310Z" 2549 | } 2550 | }, 2551 | { 2552 | "model": "market.companycmprecord", 2553 | "pk": 156, 2554 | "fields": { 2555 | "company": 3, 2556 | "cmp": "175.36", 2557 | "timestamp": "2018-05-04T10:56:54.498Z", 2558 | "updated": "2018-05-04T10:56:54.498Z" 2559 | } 2560 | }, 2561 | { 2562 | "model": "market.companycmprecord", 2563 | "pk": 157, 2564 | "fields": { 2565 | "company": 5, 2566 | "cmp": "260.11", 2567 | "timestamp": "2018-05-04T10:56:54.709Z", 2568 | "updated": "2018-05-04T10:56:54.709Z" 2569 | } 2570 | }, 2571 | { 2572 | "model": "market.companycmprecord", 2573 | "pk": 158, 2574 | "fields": { 2575 | "company": 4, 2576 | "cmp": "250.21", 2577 | "timestamp": "2018-05-04T10:56:54.909Z", 2578 | "updated": "2018-05-04T10:56:54.909Z" 2579 | } 2580 | }, 2581 | { 2582 | "model": "market.companycmprecord", 2583 | "pk": 159, 2584 | "fields": { 2585 | "company": 6, 2586 | "cmp": "300.33", 2587 | "timestamp": "2018-05-04T10:56:55.110Z", 2588 | "updated": "2018-05-04T10:56:55.110Z" 2589 | } 2590 | }, 2591 | { 2592 | "model": "market.companycmprecord", 2593 | "pk": 160, 2594 | "fields": { 2595 | "company": 9, 2596 | "cmp": "900.14", 2597 | "timestamp": "2018-05-04T10:56:55.349Z", 2598 | "updated": "2018-05-04T10:56:55.350Z" 2599 | } 2600 | }, 2601 | { 2602 | "model": "market.companycmprecord", 2603 | "pk": 161, 2604 | "fields": { 2605 | "company": 7, 2606 | "cmp": "700.21", 2607 | "timestamp": "2018-05-04T10:56:55.549Z", 2608 | "updated": "2018-05-04T10:56:55.549Z" 2609 | } 2610 | }, 2611 | { 2612 | "model": "market.companycmprecord", 2613 | "pk": 162, 2614 | "fields": { 2615 | "company": 8, 2616 | "cmp": "750.21", 2617 | "timestamp": "2018-05-04T10:56:55.741Z", 2618 | "updated": "2018-05-04T10:56:55.741Z" 2619 | } 2620 | }, 2621 | { 2622 | "model": "market.companycmprecord", 2623 | "pk": 163, 2624 | "fields": { 2625 | "company": 2, 2626 | "cmp": "120.00", 2627 | "timestamp": "2018-05-04T10:57:54.058Z", 2628 | "updated": "2018-05-04T10:57:54.058Z" 2629 | } 2630 | }, 2631 | { 2632 | "model": "market.companycmprecord", 2633 | "pk": 164, 2634 | "fields": { 2635 | "company": 1, 2636 | "cmp": "115.78", 2637 | "timestamp": "2018-05-04T10:57:54.428Z", 2638 | "updated": "2018-05-04T10:57:54.428Z" 2639 | } 2640 | }, 2641 | { 2642 | "model": "market.companycmprecord", 2643 | "pk": 165, 2644 | "fields": { 2645 | "company": 3, 2646 | "cmp": "175.36", 2647 | "timestamp": "2018-05-04T10:57:54.860Z", 2648 | "updated": "2018-05-04T10:57:54.860Z" 2649 | } 2650 | }, 2651 | { 2652 | "model": "market.companycmprecord", 2653 | "pk": 166, 2654 | "fields": { 2655 | "company": 5, 2656 | "cmp": "260.11", 2657 | "timestamp": "2018-05-04T10:57:55.248Z", 2658 | "updated": "2018-05-04T10:57:55.248Z" 2659 | } 2660 | }, 2661 | { 2662 | "model": "market.companycmprecord", 2663 | "pk": 167, 2664 | "fields": { 2665 | "company": 4, 2666 | "cmp": "250.21", 2667 | "timestamp": "2018-05-04T10:57:55.471Z", 2668 | "updated": "2018-05-04T10:57:55.471Z" 2669 | } 2670 | }, 2671 | { 2672 | "model": "market.companycmprecord", 2673 | "pk": 168, 2674 | "fields": { 2675 | "company": 6, 2676 | "cmp": "300.33", 2677 | "timestamp": "2018-05-04T10:57:55.693Z", 2678 | "updated": "2018-05-04T10:57:55.693Z" 2679 | } 2680 | }, 2681 | { 2682 | "model": "market.companycmprecord", 2683 | "pk": 169, 2684 | "fields": { 2685 | "company": 9, 2686 | "cmp": "900.14", 2687 | "timestamp": "2018-05-04T10:57:55.914Z", 2688 | "updated": "2018-05-04T10:57:55.914Z" 2689 | } 2690 | }, 2691 | { 2692 | "model": "market.companycmprecord", 2693 | "pk": 170, 2694 | "fields": { 2695 | "company": 7, 2696 | "cmp": "700.21", 2697 | "timestamp": "2018-05-04T10:57:56.170Z", 2698 | "updated": "2018-05-04T10:57:56.170Z" 2699 | } 2700 | }, 2701 | { 2702 | "model": "market.companycmprecord", 2703 | "pk": 171, 2704 | "fields": { 2705 | "company": 8, 2706 | "cmp": "750.21", 2707 | "timestamp": "2018-05-04T10:57:56.365Z", 2708 | "updated": "2018-05-04T10:57:56.365Z" 2709 | } 2710 | }, 2711 | { 2712 | "model": "market.companycmprecord", 2713 | "pk": 172, 2714 | "fields": { 2715 | "company": 2, 2716 | "cmp": "120.00", 2717 | "timestamp": "2018-05-04T10:58:01.384Z", 2718 | "updated": "2018-05-04T10:58:01.384Z" 2719 | } 2720 | }, 2721 | { 2722 | "model": "market.companycmprecord", 2723 | "pk": 173, 2724 | "fields": { 2725 | "company": 1, 2726 | "cmp": "115.78", 2727 | "timestamp": "2018-05-04T10:58:02.082Z", 2728 | "updated": "2018-05-04T10:58:02.082Z" 2729 | } 2730 | }, 2731 | { 2732 | "model": "market.companycmprecord", 2733 | "pk": 174, 2734 | "fields": { 2735 | "company": 3, 2736 | "cmp": "175.36", 2737 | "timestamp": "2018-05-04T10:58:02.682Z", 2738 | "updated": "2018-05-04T10:58:02.682Z" 2739 | } 2740 | }, 2741 | { 2742 | "model": "market.companycmprecord", 2743 | "pk": 175, 2744 | "fields": { 2745 | "company": 5, 2746 | "cmp": "260.11", 2747 | "timestamp": "2018-05-04T10:58:03.248Z", 2748 | "updated": "2018-05-04T10:58:03.248Z" 2749 | } 2750 | }, 2751 | { 2752 | "model": "market.companycmprecord", 2753 | "pk": 176, 2754 | "fields": { 2755 | "company": 4, 2756 | "cmp": "250.21", 2757 | "timestamp": "2018-05-04T10:58:03.847Z", 2758 | "updated": "2018-05-04T10:58:03.847Z" 2759 | } 2760 | }, 2761 | { 2762 | "model": "market.companycmprecord", 2763 | "pk": 177, 2764 | "fields": { 2765 | "company": 6, 2766 | "cmp": "300.33", 2767 | "timestamp": "2018-05-04T10:58:04.456Z", 2768 | "updated": "2018-05-04T10:58:04.456Z" 2769 | } 2770 | }, 2771 | { 2772 | "model": "market.companycmprecord", 2773 | "pk": 178, 2774 | "fields": { 2775 | "company": 9, 2776 | "cmp": "900.14", 2777 | "timestamp": "2018-05-04T10:58:05.100Z", 2778 | "updated": "2018-05-04T10:58:05.100Z" 2779 | } 2780 | }, 2781 | { 2782 | "model": "market.companycmprecord", 2783 | "pk": 179, 2784 | "fields": { 2785 | "company": 7, 2786 | "cmp": "700.21", 2787 | "timestamp": "2018-05-04T10:58:05.828Z", 2788 | "updated": "2018-05-04T10:58:05.828Z" 2789 | } 2790 | }, 2791 | { 2792 | "model": "market.companycmprecord", 2793 | "pk": 180, 2794 | "fields": { 2795 | "company": 8, 2796 | "cmp": "750.21", 2797 | "timestamp": "2018-05-04T10:58:06.653Z", 2798 | "updated": "2018-05-04T10:58:06.653Z" 2799 | } 2800 | }, 2801 | { 2802 | "model": "market.companycmprecord", 2803 | "pk": 181, 2804 | "fields": { 2805 | "company": 2, 2806 | "cmp": "120.00", 2807 | "timestamp": "2018-05-04T10:58:28.054Z", 2808 | "updated": "2018-05-04T10:58:28.054Z" 2809 | } 2810 | }, 2811 | { 2812 | "model": "market.companycmprecord", 2813 | "pk": 182, 2814 | "fields": { 2815 | "company": 1, 2816 | "cmp": "115.78", 2817 | "timestamp": "2018-05-04T10:58:28.659Z", 2818 | "updated": "2018-05-04T10:58:28.659Z" 2819 | } 2820 | }, 2821 | { 2822 | "model": "market.companycmprecord", 2823 | "pk": 183, 2824 | "fields": { 2825 | "company": 3, 2826 | "cmp": "175.36", 2827 | "timestamp": "2018-05-04T10:58:29.196Z", 2828 | "updated": "2018-05-04T10:58:29.196Z" 2829 | } 2830 | }, 2831 | { 2832 | "model": "market.companycmprecord", 2833 | "pk": 184, 2834 | "fields": { 2835 | "company": 5, 2836 | "cmp": "260.11", 2837 | "timestamp": "2018-05-04T10:58:29.773Z", 2838 | "updated": "2018-05-04T10:58:29.773Z" 2839 | } 2840 | }, 2841 | { 2842 | "model": "market.companycmprecord", 2843 | "pk": 185, 2844 | "fields": { 2845 | "company": 4, 2846 | "cmp": "250.21", 2847 | "timestamp": "2018-05-04T10:58:30.372Z", 2848 | "updated": "2018-05-04T10:58:30.372Z" 2849 | } 2850 | }, 2851 | { 2852 | "model": "market.companycmprecord", 2853 | "pk": 186, 2854 | "fields": { 2855 | "company": 6, 2856 | "cmp": "300.33", 2857 | "timestamp": "2018-05-04T10:58:31.056Z", 2858 | "updated": "2018-05-04T10:58:31.056Z" 2859 | } 2860 | }, 2861 | { 2862 | "model": "market.companycmprecord", 2863 | "pk": 187, 2864 | "fields": { 2865 | "company": 9, 2866 | "cmp": "900.14", 2867 | "timestamp": "2018-05-04T10:58:31.670Z", 2868 | "updated": "2018-05-04T10:58:31.670Z" 2869 | } 2870 | }, 2871 | { 2872 | "model": "market.companycmprecord", 2873 | "pk": 188, 2874 | "fields": { 2875 | "company": 7, 2876 | "cmp": "700.21", 2877 | "timestamp": "2018-05-04T10:58:32.331Z", 2878 | "updated": "2018-05-04T10:58:32.332Z" 2879 | } 2880 | }, 2881 | { 2882 | "model": "market.companycmprecord", 2883 | "pk": 189, 2884 | "fields": { 2885 | "company": 8, 2886 | "cmp": "750.21", 2887 | "timestamp": "2018-05-04T10:58:32.923Z", 2888 | "updated": "2018-05-04T10:58:32.924Z" 2889 | } 2890 | }, 2891 | { 2892 | "model": "market.companycmprecord", 2893 | "pk": 190, 2894 | "fields": { 2895 | "company": 2, 2896 | "cmp": "120.00", 2897 | "timestamp": "2018-05-04T11:00:01.397Z", 2898 | "updated": "2018-05-04T11:00:01.397Z" 2899 | } 2900 | }, 2901 | { 2902 | "model": "market.companycmprecord", 2903 | "pk": 191, 2904 | "fields": { 2905 | "company": 1, 2906 | "cmp": "115.78", 2907 | "timestamp": "2018-05-04T11:00:01.952Z", 2908 | "updated": "2018-05-04T11:00:01.953Z" 2909 | } 2910 | }, 2911 | { 2912 | "model": "market.companycmprecord", 2913 | "pk": 192, 2914 | "fields": { 2915 | "company": 3, 2916 | "cmp": "175.36", 2917 | "timestamp": "2018-05-04T11:00:02.540Z", 2918 | "updated": "2018-05-04T11:00:02.540Z" 2919 | } 2920 | }, 2921 | { 2922 | "model": "market.companycmprecord", 2923 | "pk": 193, 2924 | "fields": { 2925 | "company": 5, 2926 | "cmp": "260.11", 2927 | "timestamp": "2018-05-04T11:00:03.117Z", 2928 | "updated": "2018-05-04T11:00:03.117Z" 2929 | } 2930 | }, 2931 | { 2932 | "model": "market.companycmprecord", 2933 | "pk": 194, 2934 | "fields": { 2935 | "company": 4, 2936 | "cmp": "250.21", 2937 | "timestamp": "2018-05-04T11:00:03.860Z", 2938 | "updated": "2018-05-04T11:00:03.860Z" 2939 | } 2940 | }, 2941 | { 2942 | "model": "market.companycmprecord", 2943 | "pk": 195, 2944 | "fields": { 2945 | "company": 6, 2946 | "cmp": "300.33", 2947 | "timestamp": "2018-05-04T11:00:04.438Z", 2948 | "updated": "2018-05-04T11:00:04.438Z" 2949 | } 2950 | }, 2951 | { 2952 | "model": "market.companycmprecord", 2953 | "pk": 196, 2954 | "fields": { 2955 | "company": 9, 2956 | "cmp": "900.14", 2957 | "timestamp": "2018-05-04T11:00:05.114Z", 2958 | "updated": "2018-05-04T11:00:05.114Z" 2959 | } 2960 | }, 2961 | { 2962 | "model": "market.companycmprecord", 2963 | "pk": 197, 2964 | "fields": { 2965 | "company": 7, 2966 | "cmp": "700.21", 2967 | "timestamp": "2018-05-04T11:00:05.735Z", 2968 | "updated": "2018-05-04T11:00:05.735Z" 2969 | } 2970 | }, 2971 | { 2972 | "model": "market.companycmprecord", 2973 | "pk": 198, 2974 | "fields": { 2975 | "company": 8, 2976 | "cmp": "750.21", 2977 | "timestamp": "2018-05-04T11:00:06.544Z", 2978 | "updated": "2018-05-04T11:00:06.544Z" 2979 | } 2980 | }, 2981 | { 2982 | "model": "market.companycmprecord", 2983 | "pk": 199, 2984 | "fields": { 2985 | "company": 2, 2986 | "cmp": "120.00", 2987 | "timestamp": "2018-05-04T11:01:27.130Z", 2988 | "updated": "2018-05-04T11:01:27.130Z" 2989 | } 2990 | }, 2991 | { 2992 | "model": "market.companycmprecord", 2993 | "pk": 200, 2994 | "fields": { 2995 | "company": 1, 2996 | "cmp": "115.78", 2997 | "timestamp": "2018-05-04T11:01:28.129Z", 2998 | "updated": "2018-05-04T11:01:28.129Z" 2999 | } 3000 | }, 3001 | { 3002 | "model": "market.companycmprecord", 3003 | "pk": 201, 3004 | "fields": { 3005 | "company": 3, 3006 | "cmp": "175.36", 3007 | "timestamp": "2018-05-04T11:01:28.927Z", 3008 | "updated": "2018-05-04T11:01:28.928Z" 3009 | } 3010 | }, 3011 | { 3012 | "model": "market.companycmprecord", 3013 | "pk": 202, 3014 | "fields": { 3015 | "company": 5, 3016 | "cmp": "260.11", 3017 | "timestamp": "2018-05-04T11:01:29.590Z", 3018 | "updated": "2018-05-04T11:01:29.590Z" 3019 | } 3020 | }, 3021 | { 3022 | "model": "market.companycmprecord", 3023 | "pk": 203, 3024 | "fields": { 3025 | "company": 4, 3026 | "cmp": "250.21", 3027 | "timestamp": "2018-05-04T11:01:30.193Z", 3028 | "updated": "2018-05-04T11:01:30.193Z" 3029 | } 3030 | }, 3031 | { 3032 | "model": "market.companycmprecord", 3033 | "pk": 204, 3034 | "fields": { 3035 | "company": 6, 3036 | "cmp": "300.33", 3037 | "timestamp": "2018-05-04T11:01:30.814Z", 3038 | "updated": "2018-05-04T11:01:30.815Z" 3039 | } 3040 | }, 3041 | { 3042 | "model": "market.companycmprecord", 3043 | "pk": 205, 3044 | "fields": { 3045 | "company": 9, 3046 | "cmp": "927.59", 3047 | "timestamp": "2018-05-04T11:01:31.420Z", 3048 | "updated": "2018-05-04T11:01:31.420Z" 3049 | } 3050 | }, 3051 | { 3052 | "model": "market.companycmprecord", 3053 | "pk": 206, 3054 | "fields": { 3055 | "company": 7, 3056 | "cmp": "700.21", 3057 | "timestamp": "2018-05-04T11:01:31.956Z", 3058 | "updated": "2018-05-04T11:01:31.956Z" 3059 | } 3060 | }, 3061 | { 3062 | "model": "market.companycmprecord", 3063 | "pk": 207, 3064 | "fields": { 3065 | "company": 8, 3066 | "cmp": "750.21", 3067 | "timestamp": "2018-05-04T11:01:32.545Z", 3068 | "updated": "2018-05-04T11:01:32.545Z" 3069 | } 3070 | }, 3071 | { 3072 | "model": "market.companycmprecord", 3073 | "pk": 208, 3074 | "fields": { 3075 | "company": 2, 3076 | "cmp": "120.00", 3077 | "timestamp": "2018-05-04T11:02:01.434Z", 3078 | "updated": "2018-05-04T11:02:01.434Z" 3079 | } 3080 | }, 3081 | { 3082 | "model": "market.companycmprecord", 3083 | "pk": 209, 3084 | "fields": { 3085 | "company": 1, 3086 | "cmp": "115.78", 3087 | "timestamp": "2018-05-04T11:02:02.005Z", 3088 | "updated": "2018-05-04T11:02:02.005Z" 3089 | } 3090 | }, 3091 | { 3092 | "model": "market.companycmprecord", 3093 | "pk": 210, 3094 | "fields": { 3095 | "company": 3, 3096 | "cmp": "175.36", 3097 | "timestamp": "2018-05-04T11:02:02.631Z", 3098 | "updated": "2018-05-04T11:02:02.631Z" 3099 | } 3100 | }, 3101 | { 3102 | "model": "market.companycmprecord", 3103 | "pk": 211, 3104 | "fields": { 3105 | "company": 5, 3106 | "cmp": "260.11", 3107 | "timestamp": "2018-05-04T11:02:03.259Z", 3108 | "updated": "2018-05-04T11:02:03.259Z" 3109 | } 3110 | }, 3111 | { 3112 | "model": "market.companycmprecord", 3113 | "pk": 212, 3114 | "fields": { 3115 | "company": 4, 3116 | "cmp": "250.21", 3117 | "timestamp": "2018-05-04T11:02:03.896Z", 3118 | "updated": "2018-05-04T11:02:03.896Z" 3119 | } 3120 | }, 3121 | { 3122 | "model": "market.companycmprecord", 3123 | "pk": 213, 3124 | "fields": { 3125 | "company": 6, 3126 | "cmp": "300.33", 3127 | "timestamp": "2018-05-04T11:02:04.524Z", 3128 | "updated": "2018-05-04T11:02:04.524Z" 3129 | } 3130 | }, 3131 | { 3132 | "model": "market.companycmprecord", 3133 | "pk": 214, 3134 | "fields": { 3135 | "company": 9, 3136 | "cmp": "927.59", 3137 | "timestamp": "2018-05-04T11:02:05.216Z", 3138 | "updated": "2018-05-04T11:02:05.216Z" 3139 | } 3140 | }, 3141 | { 3142 | "model": "market.companycmprecord", 3143 | "pk": 215, 3144 | "fields": { 3145 | "company": 7, 3146 | "cmp": "700.21", 3147 | "timestamp": "2018-05-04T11:02:05.849Z", 3148 | "updated": "2018-05-04T11:02:05.849Z" 3149 | } 3150 | }, 3151 | { 3152 | "model": "market.companycmprecord", 3153 | "pk": 216, 3154 | "fields": { 3155 | "company": 8, 3156 | "cmp": "750.21", 3157 | "timestamp": "2018-05-04T11:02:06.481Z", 3158 | "updated": "2018-05-04T11:02:06.481Z" 3159 | } 3160 | }, 3161 | { 3162 | "model": "market.companycmprecord", 3163 | "pk": 217, 3164 | "fields": { 3165 | "company": 2, 3166 | "cmp": "120.00", 3167 | "timestamp": "2018-05-04T11:02:14.446Z", 3168 | "updated": "2018-05-04T11:02:14.446Z" 3169 | } 3170 | }, 3171 | { 3172 | "model": "market.companycmprecord", 3173 | "pk": 218, 3174 | "fields": { 3175 | "company": 1, 3176 | "cmp": "115.78", 3177 | "timestamp": "2018-05-04T11:02:15.011Z", 3178 | "updated": "2018-05-04T11:02:15.012Z" 3179 | } 3180 | }, 3181 | { 3182 | "model": "market.companycmprecord", 3183 | "pk": 219, 3184 | "fields": { 3185 | "company": 3, 3186 | "cmp": "175.36", 3187 | "timestamp": "2018-05-04T11:02:15.589Z", 3188 | "updated": "2018-05-04T11:02:15.589Z" 3189 | } 3190 | }, 3191 | { 3192 | "model": "market.companycmprecord", 3193 | "pk": 220, 3194 | "fields": { 3195 | "company": 5, 3196 | "cmp": "260.11", 3197 | "timestamp": "2018-05-04T11:02:16.244Z", 3198 | "updated": "2018-05-04T11:02:16.244Z" 3199 | } 3200 | }, 3201 | { 3202 | "model": "market.companycmprecord", 3203 | "pk": 221, 3204 | "fields": { 3205 | "company": 4, 3206 | "cmp": "250.21", 3207 | "timestamp": "2018-05-04T11:02:16.964Z", 3208 | "updated": "2018-05-04T11:02:16.964Z" 3209 | } 3210 | }, 3211 | { 3212 | "model": "market.companycmprecord", 3213 | "pk": 222, 3214 | "fields": { 3215 | "company": 6, 3216 | "cmp": "300.33", 3217 | "timestamp": "2018-05-04T11:02:17.663Z", 3218 | "updated": "2018-05-04T11:02:17.664Z" 3219 | } 3220 | }, 3221 | { 3222 | "model": "market.companycmprecord", 3223 | "pk": 223, 3224 | "fields": { 3225 | "company": 9, 3226 | "cmp": "927.13", 3227 | "timestamp": "2018-05-04T11:02:18.573Z", 3228 | "updated": "2018-05-04T11:02:18.574Z" 3229 | } 3230 | }, 3231 | { 3232 | "model": "market.companycmprecord", 3233 | "pk": 224, 3234 | "fields": { 3235 | "company": 7, 3236 | "cmp": "700.21", 3237 | "timestamp": "2018-05-04T11:02:19.416Z", 3238 | "updated": "2018-05-04T11:02:19.416Z" 3239 | } 3240 | }, 3241 | { 3242 | "model": "market.companycmprecord", 3243 | "pk": 225, 3244 | "fields": { 3245 | "company": 8, 3246 | "cmp": "750.21", 3247 | "timestamp": "2018-05-04T11:02:20.071Z", 3248 | "updated": "2018-05-04T11:02:20.071Z" 3249 | } 3250 | }, 3251 | { 3252 | "model": "market.companycmprecord", 3253 | "pk": 226, 3254 | "fields": { 3255 | "company": 2, 3256 | "cmp": "120.00", 3257 | "timestamp": "2018-05-04T11:02:48.600Z", 3258 | "updated": "2018-05-04T11:02:48.600Z" 3259 | } 3260 | }, 3261 | { 3262 | "model": "market.companycmprecord", 3263 | "pk": 227, 3264 | "fields": { 3265 | "company": 1, 3266 | "cmp": "115.78", 3267 | "timestamp": "2018-05-04T11:02:49.248Z", 3268 | "updated": "2018-05-04T11:02:49.248Z" 3269 | } 3270 | }, 3271 | { 3272 | "model": "market.companycmprecord", 3273 | "pk": 228, 3274 | "fields": { 3275 | "company": 3, 3276 | "cmp": "175.36", 3277 | "timestamp": "2018-05-04T11:02:49.858Z", 3278 | "updated": "2018-05-04T11:02:49.858Z" 3279 | } 3280 | }, 3281 | { 3282 | "model": "market.companycmprecord", 3283 | "pk": 229, 3284 | "fields": { 3285 | "company": 5, 3286 | "cmp": "260.11", 3287 | "timestamp": "2018-05-04T11:02:50.502Z", 3288 | "updated": "2018-05-04T11:02:50.502Z" 3289 | } 3290 | }, 3291 | { 3292 | "model": "market.companycmprecord", 3293 | "pk": 230, 3294 | "fields": { 3295 | "company": 4, 3296 | "cmp": "250.21", 3297 | "timestamp": "2018-05-04T11:02:51.100Z", 3298 | "updated": "2018-05-04T11:02:51.100Z" 3299 | } 3300 | }, 3301 | { 3302 | "model": "market.companycmprecord", 3303 | "pk": 231, 3304 | "fields": { 3305 | "company": 6, 3306 | "cmp": "300.33", 3307 | "timestamp": "2018-05-04T11:02:51.722Z", 3308 | "updated": "2018-05-04T11:02:51.722Z" 3309 | } 3310 | }, 3311 | { 3312 | "model": "market.companycmprecord", 3313 | "pk": 232, 3314 | "fields": { 3315 | "company": 9, 3316 | "cmp": "922.49", 3317 | "timestamp": "2018-05-04T11:02:52.409Z", 3318 | "updated": "2018-05-04T11:02:52.409Z" 3319 | } 3320 | }, 3321 | { 3322 | "model": "market.companycmprecord", 3323 | "pk": 233, 3324 | "fields": { 3325 | "company": 7, 3326 | "cmp": "700.21", 3327 | "timestamp": "2018-05-04T11:02:53.093Z", 3328 | "updated": "2018-05-04T11:02:53.093Z" 3329 | } 3330 | }, 3331 | { 3332 | "model": "market.companycmprecord", 3333 | "pk": 234, 3334 | "fields": { 3335 | "company": 8, 3336 | "cmp": "750.21", 3337 | "timestamp": "2018-05-04T11:02:53.741Z", 3338 | "updated": "2018-05-04T11:02:53.741Z" 3339 | } 3340 | }, 3341 | { 3342 | "model": "market.companycmprecord", 3343 | "pk": 235, 3344 | "fields": { 3345 | "company": 2, 3346 | "cmp": "120.00", 3347 | "timestamp": "2018-05-04T11:33:52.086Z", 3348 | "updated": "2018-05-04T11:33:52.087Z" 3349 | } 3350 | }, 3351 | { 3352 | "model": "market.companycmprecord", 3353 | "pk": 236, 3354 | "fields": { 3355 | "company": 1, 3356 | "cmp": "115.78", 3357 | "timestamp": "2018-05-04T11:33:52.718Z", 3358 | "updated": "2018-05-04T11:33:52.719Z" 3359 | } 3360 | }, 3361 | { 3362 | "model": "market.companycmprecord", 3363 | "pk": 237, 3364 | "fields": { 3365 | "company": 3, 3366 | "cmp": "175.36", 3367 | "timestamp": "2018-05-04T11:33:53.418Z", 3368 | "updated": "2018-05-04T11:33:53.418Z" 3369 | } 3370 | }, 3371 | { 3372 | "model": "market.companycmprecord", 3373 | "pk": 238, 3374 | "fields": { 3375 | "company": 5, 3376 | "cmp": "260.11", 3377 | "timestamp": "2018-05-04T11:33:54.016Z", 3378 | "updated": "2018-05-04T11:33:54.016Z" 3379 | } 3380 | }, 3381 | { 3382 | "model": "market.companycmprecord", 3383 | "pk": 239, 3384 | "fields": { 3385 | "company": 4, 3386 | "cmp": "250.21", 3387 | "timestamp": "2018-05-04T11:33:54.593Z", 3388 | "updated": "2018-05-04T11:33:54.593Z" 3389 | } 3390 | }, 3391 | { 3392 | "model": "market.companycmprecord", 3393 | "pk": 240, 3394 | "fields": { 3395 | "company": 6, 3396 | "cmp": "300.33", 3397 | "timestamp": "2018-05-04T11:33:55.137Z", 3398 | "updated": "2018-05-04T11:33:55.137Z" 3399 | } 3400 | }, 3401 | { 3402 | "model": "market.companycmprecord", 3403 | "pk": 241, 3404 | "fields": { 3405 | "company": 9, 3406 | "cmp": "922.49", 3407 | "timestamp": "2018-05-04T11:33:55.968Z", 3408 | "updated": "2018-05-04T11:33:55.969Z" 3409 | } 3410 | }, 3411 | { 3412 | "model": "market.companycmprecord", 3413 | "pk": 242, 3414 | "fields": { 3415 | "company": 7, 3416 | "cmp": "700.21", 3417 | "timestamp": "2018-05-04T11:33:56.590Z", 3418 | "updated": "2018-05-04T11:33:56.590Z" 3419 | } 3420 | }, 3421 | { 3422 | "model": "market.companycmprecord", 3423 | "pk": 243, 3424 | "fields": { 3425 | "company": 8, 3426 | "cmp": "750.21", 3427 | "timestamp": "2018-05-04T11:33:57.144Z", 3428 | "updated": "2018-05-04T11:33:57.145Z" 3429 | } 3430 | }, 3431 | { 3432 | "model": "market.companycmprecord", 3433 | "pk": 244, 3434 | "fields": { 3435 | "company": 2, 3436 | "cmp": "120.02", 3437 | "timestamp": "2018-05-04T11:34:23.759Z", 3438 | "updated": "2018-05-04T11:34:23.759Z" 3439 | } 3440 | }, 3441 | { 3442 | "model": "market.companycmprecord", 3443 | "pk": 245, 3444 | "fields": { 3445 | "company": 1, 3446 | "cmp": "115.78", 3447 | "timestamp": "2018-05-04T11:34:24.414Z", 3448 | "updated": "2018-05-04T11:34:24.414Z" 3449 | } 3450 | }, 3451 | { 3452 | "model": "market.companycmprecord", 3453 | "pk": 246, 3454 | "fields": { 3455 | "company": 3, 3456 | "cmp": "175.36", 3457 | "timestamp": "2018-05-04T11:34:25.002Z", 3458 | "updated": "2018-05-04T11:34:25.002Z" 3459 | } 3460 | }, 3461 | { 3462 | "model": "market.companycmprecord", 3463 | "pk": 247, 3464 | "fields": { 3465 | "company": 5, 3466 | "cmp": "260.11", 3467 | "timestamp": "2018-05-04T11:34:25.645Z", 3468 | "updated": "2018-05-04T11:34:25.645Z" 3469 | } 3470 | }, 3471 | { 3472 | "model": "market.companycmprecord", 3473 | "pk": 248, 3474 | "fields": { 3475 | "company": 4, 3476 | "cmp": "250.21", 3477 | "timestamp": "2018-05-04T11:34:26.266Z", 3478 | "updated": "2018-05-04T11:34:26.266Z" 3479 | } 3480 | }, 3481 | { 3482 | "model": "market.companycmprecord", 3483 | "pk": 249, 3484 | "fields": { 3485 | "company": 6, 3486 | "cmp": "300.33", 3487 | "timestamp": "2018-05-04T11:34:27.009Z", 3488 | "updated": "2018-05-04T11:34:27.009Z" 3489 | } 3490 | }, 3491 | { 3492 | "model": "market.companycmprecord", 3493 | "pk": 250, 3494 | "fields": { 3495 | "company": 9, 3496 | "cmp": "922.49", 3497 | "timestamp": "2018-05-04T11:34:27.620Z", 3498 | "updated": "2018-05-04T11:34:27.620Z" 3499 | } 3500 | }, 3501 | { 3502 | "model": "market.companycmprecord", 3503 | "pk": 251, 3504 | "fields": { 3505 | "company": 7, 3506 | "cmp": "700.21", 3507 | "timestamp": "2018-05-04T11:34:28.207Z", 3508 | "updated": "2018-05-04T11:34:28.207Z" 3509 | } 3510 | }, 3511 | { 3512 | "model": "market.companycmprecord", 3513 | "pk": 252, 3514 | "fields": { 3515 | "company": 8, 3516 | "cmp": "750.21", 3517 | "timestamp": "2018-05-04T11:34:29.235Z", 3518 | "updated": "2018-05-04T11:34:29.235Z" 3519 | } 3520 | } 3521 | ] 3522 | --------------------------------------------------------------------------------