├── 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 | 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 |Please check your email
7 |
12 |
15 | \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 |
16 |
19 |
24 |
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 |
22 |
23 |
24 | Loan Due
25 | Amount Due: ₹ {{ user.loan }}
26 | Balance Available: ₹ {{ user.cash }}
27 | Pay an installment of ₹ 5,000
28 |
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 |
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 | S. No.
21 | Company
22 | Mode
23 | Price
24 | Quantity
25 |
26 |
27 |
28 | {% for object in object_list %}
29 |
30 | {{ forloop.counter }}
31 | {{ object.company }}
32 | {{ object.mode }}
33 | ₹ {{ object.price }}
34 | {{ object.num_stocks }}
35 |
36 | {% endfor %}
37 |
38 |
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 |
17 | Please check the news section frequently.
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Rank
28 | Name
29 | Net Worth
30 |
31 |
32 |
33 | {% for object in data %}
34 | {% if object.0 == request.user.username %}
35 |
36 | {{ forloop.counter }}
37 |
38 |
39 | {{ object.1 }}
40 |
41 |
42 | ₹ {{ object.2 }}
43 |
44 | {% else %}
45 |
46 | {{ forloop.counter }}
47 | {{ object.1 }}
48 | ₹ {{ object.2 }}
49 |
50 | {% endif %}
51 | {% endfor %}
52 |
53 |
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 |
40 |
41 |
42 |
43 |
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 |
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 | S.no
34 | Company
35 | Stocks Purchased
36 | Stocks Available
37 | Current Market Price
38 |
39 |
40 |
41 | {% for investment in investments %}
42 |
43 | {{ forloop.counter }}
44 |
45 |
46 | {{ investment.company }}
47 |
48 |
49 | {{ investment.stocks }}
50 | {{ investment.company.stocks_remaining }}
51 |
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 |
63 |
64 | {% endfor %}
65 |
66 |
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 |
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 |
--------------------------------------------------------------------------------