Балансировка
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