Business Case #2: 
Portman Porteføljestyringssystem & Web API

I forrige post om anvendelse af Portman til afkastberegninger, tog vi udgangspunkt i to danske finansielle virksomheders omlægning til API’et for, at se på hvornår API’et repræsenterer et fordelagtigt alternativ – og hvornår det ikke gør. I denne post går vi i detaljerne med, hvordan API’et kan bruges og implementeres.

Gik du glip af den første business case? Find den her!

Af Michael Gaihede, Manager | 27 november 2017

Hvordan bruges API’et?

Hvordan API’et bruges vil i vid udstrækning afhænge af hvordan systemet, som bruger Portmans data er skruet sammen, og hvilken teknologi der anvendes.

I det følgende vil vi gennemgå et par eksempler, som vil vise, at det ret nemt kan lade sig gøre at implementere samtlige API funktioner (i ens eget univers) med forholdsvis beskeden indsats. Eksemplerne er lavet i .NET med programmeringssproget C# samt en række tilknyttede biblioteker. Anvender man i stedet f.eks. Java som programmeringssprog, er man nødt til at lokalisere tilsvarende moduler/biblioteker i Javas univers.

Uanset hvilken platform eller framework man har tænkt sig at bruge, vil det dog være en rigtig god ide at udnytte et open-source system ved navn ”Postman” (kan downloades her: Get Postman). Ved hjælp af Postman kan man afprøve samtlige API metoder uden at skulle programmere, og på en væsentlig nemmere måde end hvis man brugte en normal webbrowser. Det understøtter desuden en lang række konfigurationsparametre, som kan anvendes i kaldene. Man skal derfor ikke begynde at programmere mod et REST API, før man har fået det til at virke i Postman.

Den seneste Portman API version 7.17 indeholder mere end 80 forskellige metoder, som kan kaldes fra ens eget klientsystem. Den største opgave ved at omlægge fra direkte database kald til API metoder, vil være at specificere de data, der modtages fra API’et i hver metode – typisk i form af en klasse indeholdende et antal properties, som hver især betegner et enkelt data element. Husk at en REST API funktion typisk returnerer data i enten XML eller JSON format – som begge er tekstbaserede. Der er derfor ens eget ansvar at sørge for at konvertere disse data til binære værdier, man rent faktisk kan arbejde videre med.

For eksempel findes et API kald, som kan indlæse beholdningsdata på et enkelt instrument i en portefølje tilhørende en enkelt kunde. Selve API metoden er specificeret således:

GET api/clients/{clientNo}/portfolios/{portfolioNo}/holdings/{holdingId}

Hvor data i {} angiver input data – eksempelvis {clientNo} kundenummer, {portfolioNo} porteføljenummer og {holdingId} instrument identifikationen – f.eks. således:

GET api/clients/101/portfolios/0000030025/holdings/DK0002028190

For at afprøve at API’et rent faktisk virker, kan man copy/paste ovenstående linje (selvfølgelig med kunde/portefølje/instrument id’er der er gældende for ens eget Portman system) ind i Postman, eksekvere http funktionen og eksempelvis modtage data som vist herunder:

 

{
    “ClientNo”: “0000030025”,
    “PortfolioNo”: “101”,
    “SecurityId”: “DK0002028190”,
    “SecurityCurrencyCode”: “DKK”,
    “SecurityToBankCurrencyRate”: 100,
    “SecurityName”: “3 NDA 2 44 IO10”,
    “Quantity”: 19430372.55,
    “QuantityDrawn”: 0,
    “PortfolioCostPricePct”: 2.9399478121643008,
    “PortfolioMarketValuePct”: 1.8616931699923385,
    “AssetGroupMarketValuePct”: 1.8616931699923378,
    “MarketPrice”: 105.4,
    “MarketPriceDate”: “2017-09-04T00:00:00”,
    “CostPrice”: 20197872.27,
    “AvgCostPrice”: 103.95000002200163,
    “MarketValueInstr”: 20479612.667700004,
    “MarketValueBank”: 20479612.667700004,
    “MarketValueClient”: 20479612.667700004,
    “UnrealizedProfitLoss”: 281740.39770000428,
    “Group1Code”: 0,
    “Group1Text”: “Gips opdeling”,
    “Group2Code”: 0,
    “Group2Text”: “CRS Risikorapport”,
    “KickbackYTD”: 0,
    “KickbackTotal”: 0
    }

Bemærk at ovenstående data er i JSON format (i kaldet til REST metoden angives hvilket format, man ønsker data returneret i). Som udgangspunkt er JSON data at foretrække frem for XML: Det fylder væsentlig mindre – bliver derfor hurtigere overført – og er bedre understøttet i de fleste nyere IT systemer og moduler. Ulempen er, at man ikke i samme udstrækning som i XML ser anvendelse af såkaldte schemaer som bl.a. muliggør validering af modtagne data – men det er dog sjældent et problem.

Når man kan modtage data fra en REST API metode, er næste opgave dels at specificere en klasse indeholdende de modtagne data, og dels at sørge for selve konverteringen til binære data.

For at definere en ny klasse til de modtagne data kan man f.eks. bruge en anden fiks lille open-source utility ved navn ”Quicktype” (kan hentes her: https://quicktype.io/). Quicktype er i stand til at generere en klasse ud fra et JSON datafragment – som f.eks. det ovenstående – og kan generere klasser i 7 -8 forskellige programmeringssprog (inklusive C#), så man slipper for omfattende og fejlbehæftet tastearbejde. I det nedenstående er vist den genererede kode fra Quicktype applikationen:

namespace QuickType
  {
    using System;
    using System.Net;
    using System.Collections.Generic;
    using Newtonsoft.Json;
    public partial class PfInstHold
      {
        [JsonProperty(“Group2Text”)]
        public string Group2Text { get; set; 
        [JsonProperty(“PortfolioCostPricePct”)]
        public double PortfolioCostPricePct { get; set; }
        [JsonProperty(“CostPrice”)]
        public double CostPrice { get; set; }
        [JsonProperty(“AvgCostPrice”)]
        public double AvgCostPrice { get; set; }
        [JsonProperty(“AssetGroupMarketValuePct”)]
        public double AssetGroupMarketValuePct { get; set; }
        [JsonProperty(“ClientNo”)
        public string ClientNo { get; set; }
        [JsonProperty(“Group1Text”)]
        public string Group1Text { get; set; }
        [JsonProperty(“Group1Code”)]
        public long Group1Code { get; set; }
        [JsonProperty(“Group2Code”)]
        public long Group2Code { get; set; }
        [JsonProperty(“MarketPriceDate”)]
        public string MarketPriceDate { get; set; }
        [JsonProperty(“KickbackYTD”)]
        public long KickbackYTD { get; set; }
        [JsonProperty(“KickbackTotal”)]
        public long KickbackTotal { get; set; }
        [JsonProperty(“MarketPrice”)]
        public double MarketPrice { get; set; }
        [JsonProperty(“MarketValueClient”)]
        public double MarketValueClient { get; set; }
        [JsonProperty(“MarketValueBank”)]
        public double MarketValueBank { get; set; }
        [JsonProperty(“MarketValueInstr”)]
        public double MarketValueInstr { get; set; }
        [JsonProperty(“QuantityDrawn”)]
        public long QuantityDrawn { get; set; }
        [JsonProperty(“PortfolioNo”)]
        public string PortfolioNo { get; set; }
        [JsonProperty(“PortfolioMarketValuePct”)]
        public double PortfolioMarketValuePct { get; set; }
       [JsonProperty(“Quantity”)]
        public double Quantity { get; set; }
        [JsonProperty(“SecurityId”)]
        public string SecurityId { get; set; }
        [JsonProperty(“SecurityToBankCurrencyRate”)]
        public long SecurityToBankCurrencyRate { get; set; }
        [JsonProperty(“SecurityCurrencyCode”)]
        public string SecurityCurrencyCode { get; set; }
        [JsonProperty(“SecurityName”)]
        public string SecurityName { get; set; }
        [JsonProperty(“UnrealizedProfitLoss”)]
        public double UnrealizedProfitLoss { get; set; }
      }
    }

 

Som man kan se, har Quicktype automatisk tilføjet [JsonProperty(“PropertyName”)]ovenover hver property, som blev indlæst fra REST API kaldet (hvor PropertyName betegner navnet på den pågældende REST property). Dette er eller kan være meget positivt, hvis ens platform refererer til et eksternt og meget brugt C# bibliotek ved navn NewtonSoft.JSON. Ved dels at inkludere dette bibliotek (som angivet ved ”using Newtonsoft.Json” i starten) samt at angive [JsonProperty(“PropertyName”)] for hver property kan man rent faktisk sørge for konvertering til binære værdier (næsten) uden nogen form for programmering.

 

Ved at generere klasserne i C# ved hjælp af Quicktype skal man dog være opmærksom på, at datatypen på hver enkelt property ikke nødvendigvis er korrekt, fordi Quicktype udleder datatypen på baggrund af modtagne data. For eksempel er de to properties Quantity og QuantityDrawn genereret som hhv. double og long i datatype, hvilket ikke er korrekt (de skal ifølge API beskrivelsen være decimal begge to).

 

Efter generering af en klasse er man derfor nødt til at sammenligne datatypen på hver property med den officielle specifikation, og rette fejlagtige typer (typisk kun talværdier), hvilket dog hurtigt er gjort.

 

Når samtlige ønskede klasser er genereret og verificeret i forhold til API dokumentationen, er det tid til at etablere resten af systemet. Der er mange måder, hvorpå man kan kalde en REST metode i C#. En af de mere brugte er ved at anvende et .NET bibliotek ved navn ”RestSharp” (som kan installeres vha. NuGet).

Man kan herefter f.eks. oprette et test projekt i Visual Studio, hvori man indsætter følgende:

 

Ovenstående testmetode anvender en URI som metode identifikation: Per default vil constructor’en new RestRequest() oprette et http GET kald, mens resten af URIen specificerer, hvilken Portman API metode der kaldes – i dette tilfælde returneres en liste af instrument beholdninger for en given portefølje.

Man kunne i stedet have defineret en metode som selv oprettede URI strengen på baggrund af de tilhørende parametre – f.eks.:

List<PfInstHold> GetPortfolioHoldings(long ClientId, long PortfolioId)

(måske med string i stedet for long argumenter) og tilsvarende oprette metoder svarende til hver enkelt af Portman API funktionerne.

 

Det korte af det lange er blot, at der er tale om meget begrænset ressourceanvendelse for at kunne etablere et fuldt funktionsdygtigt metodebibliotek – i C# – som i praksis kan blive en 1:1 udgave af Portmans Web API.

LÆS MERE: Robotics Software

 

Hvert metodekald vil andrage 3-10 linjer kode, hvor langt størstedelen af tidsforbruget vil gå med at mappe og tjekke de genererede klassedefinitioner (som tidligere beskrevet). Selve konverteringen fra JSON til binære værdier kan i praksis udføres af en enkelt funktion (som vist i GetDataFromJSON metoden ovenfor).

 

Ideelt set havde Vitec Aloc sørget for at beskrive og dokumentere Portman API’et i det såkaldte ”OpenAPI” format (se her: https://swagger.io/docs/specification/about/), som konceptuelt minder om WSDL (Web Services Description Language), der tidligere var populært.  Havde det været tilfældet, kunne man anvende open-source produktet ”Swagger” til – helt automatisk – at dokumentere og generere et tilsvarende API til en ønsket platform (Swagger understøtter en lang række programmeringssprog – C++, C#, Java, R, Typescript, Node.js, Perl, Python m.fl.).

 

Brug af OpenAPI/Swagger ville desuden gøre det forholdsvis nemmere at håndtere nye versioner af Portman API’et, hvis/når der opstår forskelle i metodekald og/eller returnerede data mellem releases. Swagger indeholder et tool ved navn ”Swagger::Diff ”, som netop kan identificere såkaldte ”breaking-changes” mellem to versioner af et API.

 

En ting man skal være opmærksom på er, at Portman’s Web API ikke kan erstatte en direkte database adgang på en række punkter. En eksisterende Portman kunde havde f.eks. udviklet en applikation, som i vid udstrækning baserede sig på Portman data. Applikationen var konstrueret således, at næsten alle Portman data indlæstes i memory strukturerer under opstart. En af disse datastrukturer var en liste med stamdata på samtlige instrumenter registreret i databasen. Hver gang en metode i applikationen skulle bruge instrument stamdata, refereredes denne liste og det ønskede element blev udlæst – en aktivitet som fandt sted rigtig mange steder og gange i applikationen.

LÆS MERE: Reviewing the IT landscape

 

Denne funktionalitet findes ganske enkelt ikke i API’et: Det er ikke muligt at rekvirere stamdata på samtlige instrumenter i databasen. Fra API’et kan kun indlæses stamdata for ét instrument ad gangen – med udgangspunkt i et instrument id som man ikke kan få en oversigt over. Problemet i denne situation kan f.eks. løses ved enten at erstatte læsningen fra memory strukturen med et kald til enkelt-instrument API metoden, ved at oprette en dynamisk caching liste, eller på anden vis. Konklusionen er blot at det kan være temmelig tidskrævende, at tilrette så man får et velfungerende system.

 

Konklusion

Vitec Aloc har udviklet et Web API til sit Portman system, som på sigt forventes at skulle erstatte den direkte database adgang i mange af de løsninger, som eksisterende kunder har etableret. Det er der en vis logik – og fornuft – i set fra både leverandørens og kundernes synspunkter: Ideelt set får kunderne et mere robust systemkompleks i og med, at der ikke sker ændringer i API’et (i forhold til databasen), mens leverandøren frit kan vælge at ændre i databasen uden hensyntagen til kundernes dataanvendelse.

Kunderne skal dog gøre sig klart, at hvis man tidligere har tilgået Portman data direkte fra databasen, vil der blive tale om en række udviklingsaktiviteter ifm. overgang til Portmans Web API. Hvor omfattende disse vil være, afhænger primært af hvor struktureret og elegant de eksisterende systemer er designet. Isoleret set er det dog forholdsvist billigt at integrere API’et i de fleste moderne udviklingsmiljøer som f.eks. .NET.

 

Afslutningsvis skal det understreges, at CMP er uafhængig af Vitec Aloc, men har adskillige medarbejdere, som har arbejdet og stor erfaring med Portman. Tilsvarende har vi hjulpet mange virksomheder med Portman systemet, såvel forretnings- som IT-mæssigt.

 

 

Michael Gaihede, Manager

Michael har mere end 25 års erfaring som forretnings- og IT konsulent med fokus på finansielle virksomheder.

Lige siden starten af sin karriere har Michael været en del af IT udvikling med både udarbejdelse og implementering af porteføljemodeller, samt teknisk IT implementering. Michael har arbejdet på et stort antal platforme, med anvendelse af en lang række databasesystemer.

Michael er den del af CMPs stærke team af IT konsulenter, som leverer værdi til organisationen, ved at tilbyde afgørende ekspertise på tværs af en række tekniske områder.

Læs mere om Michael og vores andre konsulenter her.