Блокчейн без посредников: как мы отправили ценные бумаги в распределенный реестр

Вся экономическая деятельность исторически построена на посредниках. Любая, даже несложная сделка между двумя сторонами сопровождается привлечением разнообразных посредников — банки, биржи, клиринговые палаты и т.д. Исключение посредников, возможно, сделало бы взаимодействие более эффективным. Так почему бы не попробовать построить на базе блокчейна новую, децентрализованную инфраструктуру, где участники сделки могут работать напрямую? В этом посте мы расскажем о том, как начали путь к такой инфраструктуре: развивали у себя блокчейн-сделки и в итоге провели РЕПО — займ денег под обеспечение ценными бумагами.

5y08enodezvkfd1qngslkh-pl4y.png

Краткосрочные облигации


Нашей первой внебиржевой финансовой сделкой на блокчейне стал выпуск краткосрочной облигации сотового оператора МТС при участии Национального Расчетного Депозитария (НРД). Это своеобразный «центробанк» всех депозитариев. Депозитарии — это инфраструктурные посредники, ведущие учет владельцев ценных бумаг и их выпуск.

В той сделке МТС вызовом функции смарт-контракта фиксировала в блокчейне волеизъявление о продаже ценных бумаг Сбербанку, а он подтверждал в блокчейне согласие с условиями сделки. Подписанные обеими сторонами встречные поручения поступали на узел НРД, исполнявшего их в своих учетных системах. Помимо этого, в блокчейне отображались счета участников сделки в ценных бумагах и деньгах.

В том проекте мы выбрали open source платформу Hyperledger Fabric 1.1, предназначенную для создания закрытых корпоративных блокчейн-решений. Публичные блокчейны здесь не подходят, потому что нам необходимо обеспечивать приватность данных. С такими ограничениями мы столкнулись в факторинговом пилоте Сбербанка с М.Видео, который был реализован на блокчейне Ethereum. В отличие от него, Hyperledger Fabric позволяет поместить всех участников сделки в выделенный канал, где они могут обмениваться любой необходимой информацией и обрабатывать ее полнофункциональными смарт-контрактами.

Исходный код проекта по эмиссии облигации МТС был выложен в открытый доступ на GitHub. Даже не вдаваясь в алгоритм работы, можно понять, что в жизненном цикле сделки блокчейну отводилась довольно скромная роль транспорта клиринговых поручений. С другой стороны, на основе этих поручений изменялись балансы счетов — так что с точки зрения бизнес-логики это было интересней, чем простой сервис электронного документооборота.

Главным преимуществом решения была универсальность. Схема «два контрагента и регистратор» покрывает практически любую сделку на внебиржевом рынке, а с небольшими изменениями — большую часть коммерческих сделок вообще.

«РЕПО 1.0»


В новом проекте на блокчейне мы решили показать, как в децентрализованной системе реализовать РЕПО — repurchase agreement — займ денег под обеспечение ценными бумагами. Обычно эти и другие сделки на внебиржевом рынке проходят через посредников — депозитарий, клиринг-хаусы, брокеров.

В этом проекте мы заключали сделку РЕПО между Сбербанком и зарубежным партнером. В нем использовался уже Hyperledger Fabric версии 1.2. По сравнению с облигациями МТС у нас было два отличия:

  • К блокчейну подключалось только двое участников сделки, чьи депозитарии — Euroclear и Clearstream — получали все поручения по традиционным каналам передачи данных из бэк-офисов Сбербанка и его контрагента.
  • В смарт-контракте мы реализовали сложную бизнес-логику: ежедневно в блокчейн загружались котировки ценной бумаги, выступавшей обеспечением по займу, а смарт-контракт рассчитывал необходимость и сумму досрочного погашения, с учетом изменившейся стоимости обеспечения, дисконта, календаря выходных бирж и других параметров. Такую P2P-синхронизацию алгоритмов расчета между участниками невозможно получить без распределенного реестра. Это гораздо удобнее чем самостоятельный расчет обязательств и сумм каждой стороной — никаких трудоемких сверок, никаких подтверждений.


Между контрагентами организовали чат и документооборот в рамках канала. Данные по ним сохранялись в блокчейн. После каждого изменения в распределенном реестре участники канала получали оповещение на электронную почту.

«РЕПО 1.0» мы прорабатывали и с юридической стороны. С помощью одной крупной юридической фирмы проводили анализ кейсов Высокого суда Лондона. Кроме того, ЭЦП банка и его контрагента использовали разные криптографические алгоритмы.

Как работает «РЕПО 1.0»?


У каждого участника сделки есть своя блокчейн-нода. Все ноды соединены друг с другом в P2P-сеть. Предположим, нужно заключить сделку. Мы разворачиваем между участниками сделки смарт-контракт, где полностью описан финансовый инструмент.

f42e38b7e23eac1217952790f217a4e9.png

После создания контракта с нашей стороны его подписывает трейдер. Клиент также просматривает и подписывает контракт. Затем подписи просматриваются и верифицируются. В этом кейсе сделка проводилась по английскому праву, данные об ЭЦП были внесены в документ GMRA. Для подписания со стороны клиента нужна верификация того, что в сертификате подписи присутствует уполномоченное для этого лицо. Наконец, клиент акцептует контракт и соглашается со всеми условиями. К подписанному контракту можно прикреплять любое количество документов.

После этого контракт получает статус «в работе». Контракт «в работе» автоматически перерасчитывается при подгрузке новых рыночных цен. Если в контракте присутствует ценная бумага, берется рыночная цена, производится перерасчет Loan-To-Value (LTV) — отношения суммы займа к стоимости обеспечения в ценных бумагах.  LTV — один из ключевых терминов в сделке РЕПО, его значение прописывается в контракте. Цена акций резко возросла — и LTV становится меньше чем указан в GMRA (когда речь идет об английском праве). Соответственно, банк возвращает клиенту ценные бумаги (как один из вариантов), так как с учетом новых цен получается, что банк держит более высокое обеспечение.

Но если LTV становится больше, то программа позволяет распечатать collateral notice — уведомление клиенту о необходимости внести дополнительное обеспечение (акции или деньги), чтобы значение LTV вернулось к начальному. Раньше collateral notice можно было отправить только почтой, для этого создавались отдельные документы, и во время создания этих документов LTV мог снова меняться. Сейчас мы с клиентом видим одни и те же расчеты онлайн, можем легко взаимодействовать.

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

«РЕПО 2.0»


Мы хотели, чтобы наше РЕПО на блокчейне могло инициировать движение реальных активов на основании своей внутренней логики. Но в РЕПО 1.0 из-за организационных трудностей с подключением западных депозитариев мы этого достигнуть еще не смогли. Так что мы начали новый пилот «РЕПО 2.0». У него было две цели:

  • Сделка должна проходить с участием двух сторон и депозитария, максимально задействовать инфраструктуру проекта облигации МТС.
  • Блокчейн необходимо наделить полномочиями по переоценке обеспечения и выставлению маржин-колла, который мог бы автоматически исполняться депозитарием, подключенным к распределенной сети.


К проекту сразу захотел подключиться НРД. Чтобы приземлить сделку, инициированную в блокчейне, на консервативное поле федеральных законов, регулирующих отечественный финансовый рынок, мы с юристами проработали пятистраничное допсоглашение к договору электронного документооборота. Его подписали все участники сделки и НРД.

НРД в этой сделке выступил клиринг-хаусом. Он выполнял все поручения по движению средств и ценных бумаг. Эта сделка была заключена по российскому праву.

Клиент акцептовал договор электронной подписью. Затем договор своей подписью акцептовал Сбербанк — проверил соответствие всех параметров нужным значениям и полномочия человека, акцептовавшего со стороны клиента. После этого контракт ушел в работу. НРД подгружал рыночные данные, смарт-контракт производил перерасчеты.

Как работает «РЕПО 2.0»?


Для разворачивания сети и взаимодействия клиентского интерфейса с чейн-кодом мы использовали решение Fabric Starter. Вместо штатного для HLF grpc-интерфейса оно предоставляет REST API, что в нашем случае значительно снизило трудоемкость интеграции.

07a86a997c7017a122b03aad7995f6ad.png

Сеть поднималась следующим образом. Каждая из трех сторон после предварительной установки на сервере Docker запускала Fabric Starter, который создавал контейнеры с компонентами узла. Эти компоненты включали внешний peer для взаимодействия с другими организациями и REST API-сервис, через который шло взаимодействие узла с клиентским приложением. При запуске Starter также задавалась конфигурация блокчейн-сети и создавался приватный канал, в который устанавливался чейн-код с endorsement-policy. В нашем случае каждая транзакция должна иметь подписи всех трех участников.

Для организации связи серверов участников на фазе тестирования использовали Docker Swarm, однако для совершения реальной сделки по соображениям безопасности перешли на DNS. За транспорт сообщений отвечает сама платформа, данные передаются через интернет с шифрованием TLS.

Техническая сторона вопроса


Процесс разработки распределенного приложения на HLF начинается довольно традиционно — со структур данных и чейн-кода (по сути, набора хранимых процедур), вызов которого приводит к сохранению, изменению или чтению этих структур из леджера. Платформа позволяет использовать для разработки чейн-кодов и СУБД для локального хранилища различные языки программирования. Мы отдаем предпочтение Go и CouchDB соответственно.

Центральная сущность для проектов РЕПО в нашей модели данных — это сам контракт и его дочерние обязательства. Они создавались для каждого из двух пилотов, а также для маржин-коллов. Такая архитектура была шагом вперед по сравнению с моделью облигации МТС, которая базировалась на сущности «Поручение». Самостоятельные объекты создавались и для ценных бумаг, которые, таким образом, были частично токенизированы. А вот развитие эксперимента с ведением счетов и виртуальной токенизацией денег мы решили отложить до одной из следующих версий решения.

Основные функции нашего решения:

  • Создать контракт.
  • Подписать контракт своей ЭЦП, подтверждающий согласие с условиями контракта.
  • Загрузить рыночные цены и запустить пересчет стоимости обеспечения. Ее отклонение от заданного порога вызывало создание нового обязательства по маржин-коллу.
  • Отразить статус исполнения обязательства.


С технической стороны здесь наиболее интересна процедура переоценки. Разберем ее подробнее.

По бизнес-процессу процедура должна запускаться раз в день, после того, как Оракул (в пилоте «РЕПО 2.0» его роль исполнял НРД) загрузил в систему обновленные котировки ценных бумаг.

func (t *CIBContract) recalculationData(stub shim.ChaincodeStubInterface, loadData *loadDataType, curDay string) pb.Response {...}


Главный цикл процедуры проходит по всем ценным бумагам, для которых были обновлены котировки.

for _, securities := range loadData.Securities {...}


Далее осуществляется несколько проверок. Например, если на бирже, с которой поступили рыночные данные, сегодня выходной день, то пересчет не должен происходить.

if t.checkHoliday(stub, contract.Settings.Calendars) == "yes" {

hisYes := historyType{curDay, "LoadData. Calendar", "System", "LoadData. Today is holiday ! No load market data to contract !"}
...
       contract.History = append(contract.History, hisYes)
       …
       err = stub.PutState(contrID, contractJSONasBytes)
}


Для расчета обновленной цены облигаций к загруженной чистой цене прибавляется начисленный купонный доход (НКД). В пилоте была реализована поддержка схемы 30/360 для расчета НКД.

priceIzm = float64(securities.Price + float64(securities.CouponRate)*float64((int(settlementDate.Year()) - int(dateStart.Year()))*360 + (int(settlementDate.Month()) - int(dateStart.Month()))*30 + (int(settlementDate.Day()) - int(dateStart.Day())))*100/360/100)

curCurrVal = priceIzm


Если валюта сделки отличается от валюты, в которой котируется ценная бумага, осуществляется курсовой пересчет.

if contract.GeneralTerms.PurchasePrice.Currency != securities.Currency {
curCurrName = securities.Currency + "-" + contract.GeneralTerms.PurchasePrice.Currency
                for _, currency := range loadData.Currencies {
                      if currency.Name == curCurrName {
                            curCurrVal = priceIzm * currency.Value  
                       }
               }
}


Теперь нам необходимо рассчитать LTV. Сохраним старое значение коэффициента для истории.

oldCurLTV := contract.MarginingTerms.CurrentLTV


Необходимо учесть маржин-коллы, исполненные в течении жизни сделки. Требования могут приходить с обеих сторон, причем в двух формах:

  • Ценными бумагами. Заемщик вносит дополнительное обеспечение в случае падения рыночной цены обеспечения. Кредитор возвращает часть обеспечения в случае роста цены.
  • Деньгами. Заемщик досрочно возвращает часть кредита, которая перестала покрываться подешевевшим обеспечением. Кредитор увеличивает сумму займа в ответ на рост стоимости обеспечения.


В первом случае просто обновляется количество бумаг в обеспечении. А в случае внесения денег на них так же необходимо начислять доходность, указанную в дополнительных условиях сделки.

for _, addCollateral := range contract.MarginingTerms.AddCollateral {
currSumCollateral := addCollateral.Sum + (addCollateral.Sum*contract.MarginingTerms.RateOnCashMargin*float64(deltaColDate) /     
        float64(contract.MarginingTerms.Basis))/100
...
        allSumCollateral = allSumCollateral + currSumCollateral
        ...     
        
ht := historyType{curDay, System", "LoadData. Recalculation data(addCollateral) Contract " + contrID + " - currSumCollateral: " + strconv.FormatFloat(float64(currSumCollateral), 'f', 2, 64) ... }
         ...
         contract.History = append(contract.History, ht)
}


Мы рассчитываем общую сумму обратного выкупа — по сути это величина кредита с процентами, которую нам необходимо вернуть.

rePurchasePriceCur := contract.GeneralTerms.PurchasePrice.Sum + (contract.GeneralTerms.PurchasePrice.Sum*contract.GeneralTerms.RepoRate*float64(deltaSigningDate)/float64(contract.MarginingTerms.Basis))/100


Теперь рассчитываем коэффициент LTV. Для этого вычитаем из цены выкупа внесенное обеспечение в деньгах и делим получившуюся величину на общую стоимость бумаг в обеспечении. Суммы, которые вносил кредитор, идут со знаком »–» и будут прибавлены к цене выкупа.

contract.MarginingTerms.CurrentLTV = (rePurchasePriceCur - allSumCollateral) * float64(100) / (float64(contract.GeneralTerms.PurchasedSecurities.Quantity) * curCurrVal)


Наконец рассчитываем триггеры в контракте. Эта же процедура создаст объекты поручений на маржин-колл, если значение LTV отклонилось от заданного коридора.      
    

contract = t.checkTriggerEvents(stub, "LoadData", contract, curDay, securities)


И записываем информацию в историю для отображения на UI.

ht := historyType{curDay, "System", "LoadData. Recalculation data(change curLTV, ADTV) Contract " + contrID + " - oldCurLTV: " + strconv.FormatFloat(float64(oldCurLTV), 'f', 2, 64) + ", newCurLTV: " + strconv.FormatFloat(float64(contract.MarginingTerms.CurrentLTV), 'f', 2, 64)...}

contract.History = append(contract.History, ht)


Подведем итоги


Такая схема может работать не только с ценными бумагами и контрактами, но и в других сценариях. Например, с поставками электроэнергии, где есть разные тарифы, разные подключения в разное время. Или с факторингом — кредитованием поставщиков по сигналам отгрузки товара. В экономике много юзкейсов, когда каждый использует свои источники данных, которые приходится сверять.

Наша цель — создать сеть, которая соединяет банки друг с другом и их клиентами в масштабах страны, и с помощью смарт-контрактов описать в ней контракты не крипто-, а традиционной экономики — финансовые инструменты. Такая сеть будет стабильной, открытой, и, как полагается в P2P-сети, здесь ни у кого не будет особого статуса.

© Habrahabr.ru