Балансировка

5 января 2020 г. 7:52

На данной странице представлена программная реализация функции балансировки. Подробное описание самой функции вы найдёте на странице Балансировка.

Модель данных, с которой работает ниже приведённый код описан на странице: Модель данных.

balancing.py
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-х шагов:

  1. Подготовка данных: 0. Prepare data
  2. Балансировка между брокерами: 1. Balancing by brokers
  3. Балансировка между потоками по каждому брокеру: 2. balancing by thread for each broker
  4. Создание транзакций: 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-файле:

balalncing (12,5 КБ)

balalncing_with_offset (12,6 КБ)

Страницы раздела "Дизайнерские решения и программный код ДУ"

Содержание

Присоединяйтесь к нашим чатам

Наши чаты разделены на информационные и флуд-чаты. В информационных чатах мы обсуждаем проекты, связанные с Viva Invest, а во флуд-чатах мы общаемся на разные темы: что-то весёлое, интересное, познавательное и т. д.

Чаты и группы Vuspace


Сайт доверительного управления Viva TM: vivazzi.pro/viva-tm/

Если есть вопросы, то сначала посмотрите FAQ. Если не нашли ответ, то пишите в комментариях, используйте контакты или можете:

Удачного вам финансового процветания!

P. S. Обращаюсь к участникам доверительного управления Viva TM: добавляя свой комментарий на моём сайте, пожалуйста, не размещайте свои реферальные ссылки для привлечения партнёров. Для построения вашей партнёрской сети используйте рекомендации на странице Партнёрская программа. Рекомендации по развитию структуры.

P. S. S. Все статьи и предлагаемые способы заработка пройдены через мой личный опыт и размещено для вас в ознакомительных целях, поэтому ответственность за свои действия несёте только вы. Пожалуйста, помните об этом. Если вы начинающий инвестор, прочтите Правила инвестора.

Оцените статью

0 из 5 (всего 0 оценок)

captcha
Отмеченные звёздочкой поля ( * ) являются обязательными для заполнения.

Спасибо за ваш отзыв!

После нажатия кнопки "Отправить" ваше сообщение будет доставлено мне на почту.

Автор статьи

Артём Мальцев

Создатель доверительного управления Viva TM и выбирающий стабильные и низкорискованные финансовые инструменты для приумножения своих финансов и повышения капитала своих партнёров.

Права на использование материала, расположенного на этой странице https://vivazzi.pro/ru/viva-tm/code/balancing/:

Разрешается копировать материал с указанием её автора и ссылки на оригинал без использования параметра rel="nofollow" в теге <a>. Использование:

Автор статьи: Артём Мальцев
Ссылка на статью: <a href="https://vivazzi.pro/ru/viva-tm/code/balancing/">https://vivazzi.pro/ru/viva-tm/code/balancing/</a>

Больше: Правила использования сайта

Представляю вашему вниманию книгу, написанную моим близким другом Максимом Макуриным: Секреты эффективного управления ассортиментом.

Книга предназначается для широкого круга читателей и, по мнению автора, будет полезна специалистам отдела закупок и логистики, категорийным и финансовым менеджерам, менеджерам по продажам, аналитикам, руководителям и директорам, в компетенции которых принятие решений по управлению ассортиментом.

Комментарии: 0

Вы можете оставить комментарий как незарегистрированный пользователь.

Но зарегистрировавшись, вы сможете:

  • получать оповещения об ответах
  • просматривать свои комментарии
  • иметь возможность использовать все функции разработанных сервисов

Для комментирования от своего имени войдите или зарегистрируйтесь на сайте Vuspace

Отправить

На данный момент нет специального поиска, поэтому я предлагаю воспользоваться обычной поисковой системой, например, Google, добавив "vivazzi" после своего запроса.

Попробуйте

Выберите валюту для отображения денежных единиц