Балансировка
5 января 2020 г. 7:52
На данной странице представлена программная реализация функции балансировки. Подробное описание самой функции вы найдёте на странице Балансировка.
Модель данных, с которой работает ниже приведённый код описан на странице: Модель данных.
def simple_deposit_balancing(date=None): """ @param date: balancing date """ def acc(_broker_id, _thread_number): return broker_data[_broker_id]['accounts'][_thread_number] def get_acc_type(_broker_id, _thread_number): return acc(_broker_id, _thread_number)['type'] # --------------- # 0. Prepare data # --------------- check_user_deposits() if date and type(date) in [str, unicode]: date = datetime.strptime(date, '%d.%m.%Y') site_info = SiteInfo.objects.get(name='viva_tm') params = json.loads(site_info.value) # prepare broker data broker_data = {} for broker_id, number, acc_type, limit, offset in TMAccount.objects.active().values_list('broker_id', 'number', 'account_type', 'amount_limit', 'broker__offset'): if broker_id not in broker_data: broker_data[broker_id] = { 'amount': offset, '{}_amount'.format(TMAccount.SAFETY): Decimal(0), 'unlimited_{}_count'.format(TMAccount.SAFETY): 0, '{}_amount'.format(TMAccount.PROFITABLE): Decimal(0), 'unlimited_{}_count'.format(TMAccount.PROFITABLE): 0, 'accounts': {} } if limit == Decimal(0): broker_data[broker_id]['unlimited_{}_count'.format(acc_type)] += 1 broker_data[broker_id]['accounts'][number] = {'type': acc_type, 'max_amount': limit} # prepare user data user_data = {} total_amount = 0 threads = Deposit.objects.active() if params['balancing']['excluded_users']: threads = threads.exclude(user_id__in=params['balancing']['excluded_users']) threads = threads.values_list('user_id', 'tm_account__number', 'amount', 'tm_account__broker__id', 'tm_account__account_type', 'id') for user_id, acc_number, amount, broker_id, acc_type, thread_id in threads: if user_id not in user_data: user_data[user_id] = {'thread_amounts': {}, 'thread_amounts_total': Decimal(0), 'thread_amounts_total_calc': Decimal(0), 'broker_amounts': {}} # regarding brokers if broker_id not in user_data[user_id]['broker_amounts']: user_data[user_id]['broker_amounts'][broker_id] = {'old': Decimal(0), 'new': Decimal(0), TMAccount.SAFETY: Decimal(0), TMAccount.PROFITABLE: Decimal(0)} user_data[user_id]['broker_amounts'][broker_id]['old'] += amount user_data[user_id]['broker_amounts'][broker_id]['new'] += amount user_data[user_id]['broker_amounts'][broker_id][acc_type] += amount # regarding threads if broker_id not in user_data[user_id]['thread_amounts']: user_data[user_id]['thread_amounts'][broker_id] = {} if acc_number not in user_data[user_id]['thread_amounts'][broker_id]: user_data[user_id]['thread_amounts'][broker_id][acc_number] = {'new': Decimal(0), 'old': Decimal(0), 'thread_id': thread_id} user_data[user_id]['thread_amounts'][broker_id][acc_number]['new'] += amount user_data[user_id]['thread_amounts'][broker_id][acc_number]['old'] += amount user_data[user_id]['thread_amounts_total'] += amount broker_data[broker_id]['amount'] += amount total_amount += amount # ----------------------- # 1. Balancing by brokers # ----------------------- # 1.1. Find ratio for brokers for broker_id, data in broker_data.items(): data['ratio'] = data['amount'] / total_amount for user_id, user_details in user_data.items(): # 1.2. Balance by brokers for broker_id, broker_details in user_details['broker_amounts'].items(): broker_details['new'] = user_details['thread_amounts_total'] * broker_data[broker_id]['ratio'] broker_details['delta'] = broker_details['new'] - broker_details['old'] # 1.3. Balance by thread (add delta to first deposit, it does not matter what deposit) for broker_id, threads in user_details['thread_amounts'].items(): thread_number, thread_details = threads.items()[0] thread_details['new'] += user_details['broker_amounts'][broker_id]['delta'] user_details['broker_amounts'][broker_id][get_acc_type(broker_id, thread_number)] += user_details['broker_amounts'][broker_id]['delta'] # -------------------------------------- # 2. balancing by thread for each broker # -------------------------------------- # 2.1. Balance according to min_amount_rel_for_profitable_deposits for user_id, user_details in user_data.items(): for broker_id, threads in user_details['thread_amounts'].items(): delta_p = Decimal(params['min_amount_rel_for_profitable_deposits']) * user_details['broker_amounts'][broker_id]['new'] - user_details['broker_amounts'][broker_id][TMAccount.PROFITABLE] use_delta_for_s = False use_delta_for_p = False for thread_number, thread_details in threads.items(): if not use_delta_for_s and get_acc_type(broker_id, thread_number) == TMAccount.SAFETY: thread_details['new'] -= delta_p user_details['broker_amounts'][broker_id][TMAccount.SAFETY] -= delta_p use_delta_for_s = True if not use_delta_for_p and get_acc_type(broker_id, thread_number) == TMAccount.PROFITABLE: thread_details['new'] += delta_p user_details['broker_amounts'][broker_id][TMAccount.PROFITABLE] += delta_p use_delta_for_p = True # calc total amount by account type broker_data[broker_id]['{}_amount'.format(get_acc_type(broker_id, thread_number))] += thread_details['new'] # 2.2. Find ratio by thread for user_id, user_details in user_data.items(): for broker_id, threads in user_details['thread_amounts'].items(): # calc total amount by account type by user threads amounts = {TMAccount.SAFETY: Decimal(0), TMAccount.PROFITABLE: Decimal(0)} for thread_number, thread_details in threads.items(): amounts[get_acc_type(broker_id, thread_number)] += thread_details['new'] # calc ratio for thread_number, thread_details in threads.items(): if acc(broker_id, thread_number)['max_amount'] != Decimal(0): amount = broker_data[broker_id]['{}_amount'.format(get_acc_type(broker_id, thread_number))] acc(broker_id, thread_number)['ratio'] = acc(broker_id, thread_number)['max_amount'] / amount # 2.3. Balance by thread using ratio # firstly, use ratio for account with max_amount limited_acc_amount = {TMAccount.SAFETY: Decimal(0), TMAccount.PROFITABLE: Decimal(0)} for thread_number, thread_details in threads.items(): if 'ratio' in acc(broker_id, thread_number): thread_details['new'] = amounts[get_acc_type(broker_id, thread_number)] * acc(broker_id, thread_number)['ratio'] limited_acc_amount[get_acc_type(broker_id, thread_number)] += thread_details['new'] user_details['thread_amounts_total_calc'] += thread_details['new'] # secondly, assign the remaining amount for flows without restriction for thread_number, thread_details in threads.items(): if 'ratio' not in acc(broker_id, thread_number): thread_details['new'] = (user_details['broker_amounts'][broker_id][get_acc_type(broker_id, thread_number)] - limited_acc_amount[get_acc_type(broker_id, thread_number)]) / broker_data[broker_id]['unlimited_{}_count'.format(get_acc_type(broker_id, thread_number))] user_details['thread_amounts_total_calc'] += thread_details['new'] # ---------------------- # 3. Create transactions # ---------------------- date = get_next_day_utc((date - timedelta(days=1)) if date else timezone.now().date()) operations = [] for user_id, user_details in user_data.items(): need_thread_amounts_total_check = True for broker_id, threads in user_details['thread_amounts'].items(): for thread_number, thread_details in threads.items(): # Repair balance if there is old and new thread balance is different # with adding delta to first account of first_broker if need_thread_amounts_total_check: if user_details['thread_amounts_total_calc'] != user_details['thread_amounts_total']: thread_details['new'] += user_details['thread_amounts_total'] - user_details['thread_amounts_total_calc'] need_thread_amounts_total_check = False operations.append( Operation(deposit_id=thread_details['thread_id'], amount=thread_details['new'] - thread_details['old'], date=date, status=Operation.BALANCING) ) Operation.objects.bulk_create(operations) Broker.objects.update(offset=Decimal(0))
Функция балансировки состоит из 4-х шагов:
- Подготовка данных:
0. Prepare data
- Балансировка между брокерами:
1. Balancing by brokers
- Балансировка между потоками по каждому брокеру:
2. balancing by thread for each broker
- Создание транзакций:
3. Create transactions
Если подготовка данных для более удобного и оптимального решения задачи занимает внушительное количество строк, то я обычно выделяю её в отдельный шаг с номером ноль.
Для более удобного понимания структуры массива данных broker_data
и user_data
вы можете смотреть на пример:
--- Data example --- broker_data = { 1: { # broker_id 'amount': 45000, 'safety_amount': 33750, 'unlimited_safety_count': 2, 'profitable_amount': 11250, 'unlimited_profitable_count': 1, 'ratio': Decimal('0.9'), 'accounts': { 100: { # number_account 'type': 'safety', # account_type (safety or profitable) 'max_amount': 8100, # if 0, then this account is unlimited ['ratio': 0.24,] # can be absent, if account is unlimited }, ... } }, ... } user_data = { 1: { # user_id 'broker_amounts': { # user stat by brokers 1: { # broker_id 'new': 13500, # new total amount (S-amount + P-amount) 'old': 15000, 'delta': -1500, 'safety': 11500, # S-amount 'profitable': 2000 # P-amount }, 2: {'new': 1500, 'old': 0, 'delta': 1500, 'S': 1500, 'P': 0} }, 'thread_amounts': { # user stat by threads 1: { # broker_id 100: { # tm_account_number 'new': Decimal('4500.000000000'), 'old': Decimal('6000.00000000'), 'thread_id': 1, }, 200: {'new': Decimal('1000.00000000'), 'old': Decimal('1000.00000000')}, 'thread_id': 2, 300: {'new': Decimal('6000.00000000'), 'old': Decimal('6000.00000000')}, 'thread_id': 3, 400: {'new': Decimal('0E-8'), 'old': Decimal('0E-8')}, 'thread_id': 4, 500: {'new': Decimal('2000.00000000'), 'old': Decimal('2000.00000000')}, 'thread_id': 5, 600: {'new': Decimal('0E-8'), 'old': Decimal('0E-8'), 'thread_id': 6,} }, 2: { 800: {'new': Decimal('1500.000000000'), 'old': Decimal('0E-8'), 'thread_id': 7}, 700: {'new': Decimal('0E-8'), 'old': Decimal('0E-8'), 'thread_id': 8} } }, 'thread_amounts_total': Decimal('15000.00000000'), 'thread_amounts_total_calc': Decimal('15000.00000000'), # for check of calculation errors }, ... }
Эти данные как раз получаются из примера, описанного на странице Балансировка.
Тестирование
Автоматизированный тест написан через обычный Django TestCase
и запускается стандартным образом через python manage.py test [path_to_tests]
. Во вкладках вы можете найти тестовые данные, которые используются при балансировке.
tests.py
users.json
site_info.json
deposits.json
# -*- coding: utf-8 -*- from __future__ import unicode_literals, division import json from decimal import Decimal from django.contrib.auth import get_user_model from django.db.models import Sum from django.test import TestCase from django.utils.translation import activate from sb_site_info.models import SiteInfo from viva_tm_wsc.models import Deposit, StatItem, TMAccount from viva_tm_wsc.utils import calc from viva_tm_wsc.utils.deposit_balancing import simple_deposit_balancing class BalancingTest(TestCase): fixtures = ('balancing/users.json', 'balancing/site_info.json', 'balancing/deposits.json') def test_balancing(self): activate('ru') # before testing we need update stat calc() # run balancing date = StatItem.objects.first().date # get first date for testing. Else date will be current date of test run. simple_deposit_balancing(date=date) # update stat calc() site_info = SiteInfo.objects.get(name='viva_tm') params = json.loads(site_info.value) deposits = Deposit.objects.without_excluded_users() # check amount_limit for tm_account in TMAccount.objects.active().exclude(amount_limit=Decimal(0)): self.assertEqual(tm_account.deposit_set.exclude(user_id__in=params['balancing']['excluded_users']).aggregate(Sum('amount')).get('amount__sum'), tm_account.amount_limit) # check ratio check_ratio = {} p_part = params['min_amount_rel_for_profitable_deposits'] * 100 for tm_account_id, email, title, user_extra in deposits.values_list('tm_account_id', 'user__email', 'tm_account__title', 'user__extra'): # email, title: for debug only self.assertEqual(user_extra['stat']['p_part'], p_part) if tm_account_id in check_ratio: self.assertEqual(user_extra['stat']['balance'][str(tm_account_id)], check_ratio[tm_account_id]) else: # save ratio for next check check_ratio[tm_account_id] = user_extra['stat']['balance'][str(tm_account_id)] def test_balancing_with_concrete_values(self): """ This test contains check with concrete values that can be changed if fixtures change command: test viva_tm_wsc.tests.BalancingTest.test_balancing_with_concrete_values --keepdb --failfast """ activate('ru') check_deposits = { 'user_1@vits.pro': {'S-1': 6000, 'S-2': 1000, 'CS-3': 6000, 'CS-4': 0, 'P-1': 2000, 'CP-2': 0, 'CS-5': 0, 'CP-3': 0}, 'user_2@vits.pro': {'S-1': 26000, 'S-2': 0, 'CS-3': 0, 'CS-4': 0, 'P-1': 0, 'CP-2': 4000, 'CS-5': 0, 'CP-3': 0}, 'user_3@vits.pro': {'S-1': 0, 'S-2': 0, 'CS-3': 0, 'CS-4': 0, 'P-1': 0, 'CP-2': 0, 'CS-5': 5000, 'CP-3': 0}, 'user_4@vits.pro': {'S-1': 1234, 'S-2': 0, 'CS-3': 0, 'CS-4': 0, 'P-1': 0, 'CP-2': 0, 'CS-5': 0, 'CP-3': 0} } # before testing we need update stat calc() # firstly, check amounts for amount, email, title in Deposit.objects.values_list('amount', 'user__email', 'tm_account__title'): self.assertEqual(amount, Decimal(check_deposits[email][title])) # run balancing date = StatItem.objects.first().date # get first date for testing. Else date will be current date of test run. simple_deposit_balancing(date=date) # update stat calc() # and now we can test balancing check_deposits = { 'user_1@vits.pro': {'S-1': 2430, 'S-2': 810, 'CS-3': 3442.5, 'CS-4': 3442.5, 'P-1': 2160, 'CP-2': 1215, 'CS-5': 1125, 'CP-3': 375}, 'user_2@vits.pro': {'S-1': 4860, 'S-2': 1620, 'CS-3': 6885, 'CS-4': 6885, 'P-1': 4320, 'CP-2': 2430, 'CS-5': 2250, 'CP-3': 750}, 'user_3@vits.pro': {'S-1': 810, 'S-2': 270, 'CS-3': 1147.5, 'CS-4': 1147.5, 'P-1': 720, 'CP-2': 405, 'CS-5': 375, 'CP-3': 125}, # excluded user 'user_4@vits.pro': {'S-1': 1234, 'S-2': 0, 'CS-3': 0, 'CS-4': 0, 'P-1': 0, 'CP-2': 0, 'CS-5': 0, 'CP-3': 0} } check_ratio = { 'user_1@vits.pro': {'S-1': 16.2, 'S-2': 5.4, 'CS-3': 22.95, 'CS-4': 22.95, 'CS-5': 7.5, 'P-1': 14.4, 'CP-2': 8.1, 'CP-3': 2.5}, 'user_2@vits.pro': {'S-1': 16.2, 'S-2': 5.4, 'CS-3': 22.95, 'CS-4': 22.95, 'CS-5': 7.5, 'P-1': 14.4, 'CP-2': 8.1, 'CP-3': 2.5}, 'user_3@vits.pro': {'S-1': 16.2, 'S-2': 5.4, 'CS-3': 22.95, 'CS-4': 22.95, 'CS-5': 7.5, 'P-1': 14.4, 'CP-2': 8.1, 'CP-3': 2.5}, # excluded user 'user_4@vits.pro': {'S-1': 100, 'S-2': 0, 'CS-3': 0, 'CS-4': 0, 'P-1': 0, 'CP-2': 0, 'CS-5': 0, 'CP-3': 0} } for amount, email, title, acc_id, extra in Deposit.objects.values_list('amount', 'user__email', 'tm_account__title', 'tm_account_id', 'user__extra'): self.assertEqual(amount, Decimal(check_deposits[email][title])) self.assertEqual(extra['stat']['balance'][str(acc_id)], check_ratio[email][title]) def test_balancing_with_concrete_values_and_offsets(self): """ This test contains check with concrete values that can be changed if fixtures change command: test viva_tm_wsc.tests.BalancingTest.test_balancing_with_concrete_values_and_offsets --keepdb --failfast """ activate('ru') check_deposits = { 'user_1@vits.pro': {'S-1': 6000, 'S-2': 1000, 'CS-3': 6000, 'CS-4': 0, 'P-1': 2000, 'CP-2': 0, 'CS-5': 0, 'CP-3': 0}, 'user_2@vits.pro': {'S-1': 26000, 'S-2': 0, 'CS-3': 0, 'CS-4': 0, 'P-1': 0, 'CP-2': 4000, 'CS-5': 0, 'CP-3': 0}, 'user_3@vits.pro': {'S-1': 0, 'S-2': 0, 'CS-3': 0, 'CS-4': 0, 'P-1': 0, 'CP-2': 0, 'CS-5': 5000, 'CP-3': 0}, 'user_4@vits.pro': {'S-1': 1234, 'S-2': 0, 'CS-3': 0, 'CS-4': 0, 'P-1': 0, 'CP-2': 0, 'CS-5': 0, 'CP-3': 0} } # set offset for brokers offsets = {'forex4you': -5000, 'roboforex': 5000} for broker in Broker.objects.all(): broker.offset = offsets[broker.name] broker.save(update_fields=('offset', )) # before testing we need update stat calc() # firstly, check amounts for amount, email, title in Deposit.objects.values_list('amount', 'user__email', 'tm_account__title'): self.assertEqual(amount, Decimal(check_deposits[email][title])) # run balancing date = StatItem.objects.first().date # get first date for testing. Else date will be current date of test run. simple_deposit_balancing(date=date) # update stat calc() # and now we can test balancing check_deposits = { 'user_1@vits.pro': {'S-1': 2430, 'S-2': 810, 'CS-3': 2880, 'CS-4': 2880, 'P-1': 2160, 'CP-2': 840, 'CS-5': 2250, 'CP-3': 750}, 'user_2@vits.pro': {'S-1': 4860, 'S-2': 1620, 'CS-3': 5760, 'CS-4': 5760, 'P-1': 4320, 'CP-2': 1680, 'CS-5': 4500, 'CP-3': 1500}, 'user_3@vits.pro': {'S-1': 810, 'S-2': 270, 'CS-3': 960, 'CS-4': 960, 'P-1': 720, 'CP-2': 280, 'CS-5': 750, 'CP-3': 250}, # excluded user 'user_4@vits.pro': {'S-1': 1234, 'S-2': 0, 'CS-3': 0, 'CS-4': 0, 'P-1': 0, 'CP-2': 0, 'CS-5': 0, 'CP-3': 0} } check_ratio = { 'user_1@vits.pro': {'S-1': 16.2, 'S-2': 5.4, 'CS-3': 19.2, 'CS-4': 19.2, 'CS-5': 15, 'P-1': 14.4, 'CP-2': 5.6, 'CP-3': 5}, 'user_2@vits.pro': {'S-1': 16.2, 'S-2': 5.4, 'CS-3': 19.2, 'CS-4': 19.2, 'CS-5': 15, 'P-1': 14.4, 'CP-2': 5.6, 'CP-3': 5}, 'user_3@vits.pro': {'S-1': 16.2, 'S-2': 5.4, 'CS-3': 19.2, 'CS-4': 19.2, 'CS-5': 15, 'P-1': 14.4, 'CP-2': 5.6, 'CP-3': 5}, # excluded user 'user_4@vits.pro': {'S-1': 100, 'S-2': 0, 'CS-3': 0, 'CS-4': 0, 'P-1': 0, 'CP-2': 0, 'CS-5': 0, 'CP-3': 0} } for amount, email, title, acc_id, extra in Deposit.objects.values_list('amount', 'user__email', 'tm_account__title', 'tm_account_id', 'user__extra'): self.assertEqual(amount, Decimal(check_deposits[email][title])) self.assertEqual(extra['stat']['balance'][str(acc_id)], check_ratio[email][title])
[ { "model": "email_auth.user", "pk": 1, "fields": { "password": "pbkdf2_sha256$30000$axUEA0wFRwUt$gcOfrcyN/P0wkyPBGWVSWG5EGm1WadURGYiK4naDS8A=", "is_superuser": true, "username": "user_1@vits.pro", "first_name": "Userov_1", "last_name": "User_1", "email": "user_1@vits.pro", "is_staff": true } }, { "model": "email_auth.user", "pk": 2, "fields": { "password": "pbkdf2_sha256$30000$zGngQJ6eNqIE$n4/EdBSwB1kAwzWtsabbCcOObMVMrd6qP9CGuY8PhUg=", "username": "user_2@vits.pro", "first_name": "Userov_2", "last_name": "User_2", "email": "user_2@vits.pro" } }, { "model": "email_auth.user", "pk": 3, "fields": { "password": "pbkdf2_sha256$30000$S4MY5OjtkxQV$D7J6Ylq2CIJ7ca6tw8gRyqiT5wIkzwc0/Po10rGGREY=", "username": "user_3@vits.pro", "first_name": "Userov_3", "last_name": "User_3", "email": "user_3@vits.pro" } }, { "model": "email_auth.user", "pk": 4, "fields": { "password": "pbkdf2_sha256$30000$BZ3pAWQvUDG1$PL/XqX+w/u/bpL2+Sbd2x6EGYpH0yLcB12QhyUuPVgg=", "username": "user_4@vits.pro", "first_name": "Userov_4", "last_name": "User_4", "email": "user_4@vits.pro" } }, { "model": "email_auth.user", "pk": 5, "fields": { "password": "pbkdf2_sha256$30000$BZ3pAWQvUDG1$PL/XqX+w/u/bpL2+Sbd2x6EGYpH0yLcB12QhyUuPVgg=", "username": "user_5@vits.pro", "first_name": "Userov_5", "last_name": "User_5", "email": "user_5@vits.pro" } } ]
[ { "model": "sb_site_info.siteinfo", "pk": 1, "fields": { "title": "Viva TM", "value": "{\"balancing\": {\"excluded_users\": [4]}, \"min_amount_rel_for_profitable_deposits\": 0.25}", "name": "viva_tm" } } ]
[ { "model": "viva_tm_wsc.tmaccount", "pk": 1, "fields": { "broker": 1, "number": 100, "title": "S-1", "creation_date": "2019-01-01", "account_type": "safety", "is_tm": true, "currency": "USD_CENT", "initial_currency": "USD", "ratio": "0.01000000", "preferred_currency": "USD", "order": 1, "amount": "1000000.00000000", "amount_limit": "8100.00000000" } }, { "model": "viva_tm_wsc.tmaccount", "pk": 2, "fields": { "broker": 1, "number": 200, "title": "S-2", "creation_date": "2019-01-01", "account_type": "safety", "is_tm": true, "currency": "USD_CENT", "initial_currency": "USD", "ratio": "0.01000000", "preferred_currency": "USD", "order": 2, "amount": "1000000.00000000", "amount_limit": "2700.00000000" } }, { "model": "viva_tm_wsc.tmaccount", "pk": 3, "fields": { "broker": 1, "number": 300, "title": "CS-3", "creation_date": "2019-01-01", "account_type": "safety", "is_tm": true, "currency": "USD_CENT", "initial_currency": "USD", "ratio": "0.01000000", "preferred_currency": "USD", "order": 3, "amount": "1000000.00000000", "amount_limit": "0E-8" } }, { "model": "viva_tm_wsc.tmaccount", "pk": 4, "fields": { "broker": 1, "number": 400, "title": "CS-4", "creation_date": "2019-01-01", "account_type": "safety", "is_tm": true, "currency": "USD_CENT", "initial_currency": "USD", "ratio": "0.01000000", "preferred_currency": "USD", "order": 4, "amount": "1000000.00000000", "amount_limit": "0E-8" } }, { "model": "viva_tm_wsc.tmaccount", "pk": 5, "fields": { "broker": 1, "number": 500, "title": "P-1", "creation_date": "2019-01-01", "account_type": "profitable", "is_tm": true, "currency": "USD_CENT", "initial_currency": "USD", "ratio": "0.01000000", "preferred_currency": "USD", "order": 5, "amount": "1000000.00000000", "amount_limit": "7200.00000000" } }, { "model": "viva_tm_wsc.tmaccount", "pk": 6, "fields": { "broker": 1, "number": 600, "title": "CP-2", "creation_date": "2019-01-01", "account_type": "profitable", "is_tm": true, "currency": "USD_CENT", "initial_currency": "USD", "ratio": "0.01000000", "preferred_currency": "USD", "order": 6, "amount": "1000000.00000000", "amount_limit": "0E-8" } }, { "model": "viva_tm_wsc.tmaccount", "pk": 7, "fields": { "broker": 2, "number": 700, "title": "CS-5", "creation_date": "2019-01-01", "account_type": "safety", "is_tm": true, "currency": "USD_CENT", "initial_currency": "USD", "ratio": "0.01000000", "preferred_currency": "USD", "order": 7, "amount": "1000000.00000000", "amount_limit": "0E-8" } }, { "model": "viva_tm_wsc.tmaccount", "pk": 8, "fields": { "broker": 2, "number": 800, "title": "CP-3", "creation_date": "2019-01-01", "account_type": "profitable", "is_tm": true, "currency": "USD_CENT", "initial_currency": "USD", "ratio": "0.01000000", "preferred_currency": "USD", "order": 8, "amount": "1000000.00000000", "amount_limit": "0E-8" } }, { "model": "viva_tm_wsc.tmaccount", "pk": 9, "fields": { "broker": 1, "number": 0, "creation_date": "2019-01-01", "account_type": "archive", "is_tm": false, "currency": "USD_CENT", "initial_currency": "USD", "ratio": "0.01000000", "preferred_currency": "USD", "order": 9, "amount": "5000000.00000000", "amount_limit": "1234.00000000" } }, { "model": "viva_tm_wsc.tmaccount", "pk": 10, "fields": { "broker": 1, "number": -1, "creation_date": "2019-01-01", "account_type": "private", "is_tm": false, "user": 1, "currency": "USD_CENT", "initial_currency": "USD", "ratio": "0.01000000", "preferred_currency": "USD", "order": 10, "amount": "700000.00000000", "amount_limit": "0E-8" } }, { "model": "viva_tm_wsc.broker", "pk": 1, "fields": { "title": "Forex4you", "name": "forex4you" } }, { "model": "viva_tm_wsc.broker", "pk": 2, "fields": { "title": "RoboForex", "name": "roboforex" } }, { "model": "viva_tm_wsc.operation", "pk": 1, "fields": { "deposit": 1, "status": "refill_is_transferred_to_tm", "transferred_amount": "60.00000000", "transferred_amount_currency": "USD", "amount": "6000.00000000", "creation_date": "2019-01-01T00:00:00Z", "date": "2019-01-01T00:00:00Z" } }, { "model": "viva_tm_wsc.operation", "pk": 2, "fields": { "deposit": 2, "status": "refill_is_transferred_to_tm", "transferred_amount": "10.00000000", "transferred_amount_currency": "USD", "amount": "1000.00000000", "creation_date": "2019-12-23T07:31:57.040Z", "date": "2019-01-01T00:00:00Z" } }, { "model": "viva_tm_wsc.operation", "pk": 3, "fields": { "deposit": 3, "status": "refill_is_transferred_to_tm", "transferred_amount": "60.00000000", "transferred_amount_currency": "USD", "amount": "6000.00000000", "creation_date": "2019-12-23T07:33:20.679Z", "date": "2019-01-01T00:00:00Z" } }, { "model": "viva_tm_wsc.operation", "pk": 4, "fields": { "deposit": 4, "status": "refill_is_transferred_to_tm", "transferred_amount": "20.00000000", "transferred_amount_currency": "USD", "amount": "2000.00000000", "creation_date": "2019-12-23T07:36:12.030Z", "date": "2019-01-01T00:00:00Z" } }, { "model": "viva_tm_wsc.operation", "pk": 5, "fields": { "deposit": 5, "status": "refill_is_transferred_to_tm", "transferred_amount": "260.00000000", "transferred_amount_currency": "USD", "amount": "26000.00000000", "creation_date": "2019-12-23T07:37:46.961Z", "date": "2019-01-01T00:00:00Z" } }, { "model": "viva_tm_wsc.operation", "pk": 6, "fields": { "deposit": 6, "status": "refill_is_transferred_to_tm", "transferred_amount": "40.00000000", "transferred_amount_currency": "USD", "amount": "4000.00000000", "creation_date": "2019-12-23T07:38:57.412Z", "date": "2019-01-01T00:00:00Z" } }, { "model": "viva_tm_wsc.operation", "pk": 7, "fields": { "deposit": 7, "status": "refill_is_transferred_to_tm", "transferred_amount": "50.00000000", "transferred_amount_currency": "USD", "amount": "5000.00000000", "creation_date": "2019-12-23T07:40:00.226Z", "date": "2019-01-01T00:00:00Z" } }, { "model": "viva_tm_wsc.operation", "pk": 8, "fields": { "deposit": 8, "status": "refill_is_transferred_to_tm", "transferred_amount": "12.3400000000", "transferred_amount_currency": "USD", "amount": "1234.00000000", "creation_date": "2019-12-25T04:21:21.404Z", "date": "2019-01-01T00:00:00Z" } }, { "model": "viva_tm_wsc.deposit", "pk": 1, "fields": { "tm_account": 1, "user": 1, "operation_amount_without_commission": "6000.00000000", "operation_amount_without_commission_currency": "RUB", "operation_amount": "0E-8", "operation_amount_new": "0E-8", "amount": "0E-8", "amount_new": "0E-8", "start_date": "2019-01-01T08:00:00Z", "end_date": null, "available_withdrawal_amount": "0E-8", "available_withdrawal_amount_new": "0E-8", "preferred_currency": "USD", "extra": {} } }, { "model": "viva_tm_wsc.deposit", "pk": 2, "fields": { "tm_account": 2, "user": 1, "amount": "0E-8", "start_date": "2019-01-01T08:00:00Z", "preferred_currency": "USD" } }, { "model": "viva_tm_wsc.deposit", "pk": 3, "fields": { "tm_account": 3, "user": 1, "amount": "0E-8", "start_date": "2019-01-01T08:00:00Z", "preferred_currency": "USD" } }, { "model": "viva_tm_wsc.deposit", "pk": 4, "fields": { "tm_account": 5, "user": 1, "amount": "0E-8", "start_date": "2019-01-01T08:00:00Z", "preferred_currency": "USD" } }, { "model": "viva_tm_wsc.deposit", "pk": 5, "fields": { "tm_account": 1, "user": 2, "amount": "0E-8", "start_date": "2019-01-01T00:00:00Z", "preferred_currency": "USD" } }, { "model": "viva_tm_wsc.deposit", "pk": 6, "fields": { "tm_account": 6, "user": 2, "amount": "0E-8", "start_date": "2019-01-01T00:00:00Z", "preferred_currency": "USD" } }, { "model": "viva_tm_wsc.deposit", "pk": 7, "fields": { "tm_account": 7, "user": 3, "amount": "0E-8", "start_date": "2019-01-01T00:00:00Z", "preferred_currency": "USD" } }, { "model": "viva_tm_wsc.deposit", "pk": 8, "fields": { "tm_account": 1, "user": 4, "amount": "0E-8", "start_date": "2019-01-01T00:00:00Z", "preferred_currency": "USD" } }, { "model": "viva_tm_wsc.statitem", "pk": 1, "fields": { "tm_account": 1, "date": "2019-01-01", "fact_amount": "1000000.00000000", "profit": "0E-8", "gain": "0E-8" } }, { "model": "viva_tm_wsc.statitem", "pk": 2, "fields": { "tm_account": 2, "date": "2019-01-01", "fact_amount": "1000000.00000000", "profit": "0E-8", "gain": "0E-8" } }, { "model": "viva_tm_wsc.statitem", "pk": 3, "fields": { "tm_account": 3, "date": "2019-01-01", "fact_amount": "1000000.00000000", "profit": "0E-8", "gain": "0E-8" } }, { "model": "viva_tm_wsc.statitem", "pk": 4, "fields": { "tm_account": 4, "date": "2019-01-01", "fact_amount": "1000000.00000000", "profit": "0E-8", "gain": "0E-8" } }, { "model": "viva_tm_wsc.statitem", "pk": 5, "fields": { "tm_account": 5, "date": "2019-01-01", "fact_amount": "1000000.00000000", "profit": "0E-8", "gain": "0E-8" } }, { "model": "viva_tm_wsc.statitem", "pk": 6, "fields": { "tm_account": 6, "date": "2019-01-01", "fact_amount": "1000000.00000000", "profit": "0E-8", "gain": "0E-8" } }, { "model": "viva_tm_wsc.statitem", "pk": 7, "fields": { "tm_account": 7, "date": "2019-01-01", "fact_amount": "1000000.00000000", "profit": "0E-8", "gain": "0E-8" } }, { "model": "viva_tm_wsc.statitem", "pk": 8, "fields": { "tm_account": 8, "date": "2019-01-01", "fact_amount": "1000000.00000000", "profit": "0E-8", "gain": "0E-8" } } ]
Для удобства вы можете изучать материал и параллельно проверять все расчёты в excel-файле:
Страницы раздела "Дизайнерские решения и программный код ДУ"
Содержание
Присоединяйтесь к нашим чатам
Наши чаты разделены на информационные и флуд-чаты. В информационных чатах мы обсуждаем проекты, связанные с Viva Invest, а во флуд-чатах мы общаемся на разные темы: что-то весёлое, интересное, познавательное и т. д.
Сайт доверительного управления Viva TM: vivazzi.pro/viva-tm/
Если есть вопросы, то сначала посмотрите FAQ. Если не нашли ответ, то пишите в комментариях, используйте контакты или можете:
Удачного вам финансового процветания!
P. S. Обращаюсь к участникам доверительного управления Viva TM: добавляя свой комментарий на моём сайте, пожалуйста, не размещайте свои реферальные ссылки для привлечения партнёров. Для построения вашей партнёрской сети используйте рекомендации на странице Партнёрская программа. Рекомендации по развитию структуры.
P. S. S. Все статьи и предлагаемые способы заработка пройдены через мой личный опыт и размещено для вас в ознакомительных целях, поэтому ответственность за свои действия несёте только вы. Пожалуйста, помните об этом. Если вы начинающий инвестор, прочтите Правила инвестора.
Представляю вашему вниманию книгу, написанную моим близким другом Максимом Макуриным: Секреты эффективного управления ассортиментом.
Книга предназначается для широкого круга читателей и, по мнению автора, будет полезна специалистам отдела закупок и логистики, категорийным и финансовым менеджерам, менеджерам по продажам, аналитикам, руководителям и директорам, в компетенции которых принятие решений по управлению ассортиментом.
Комментарии: 0