(Acest articol a fost publicat pentru prima dată pe Laboratorul software al lui Adamși a contribuit cu drag la R-Bloggers). (Puteți raporta problema despre conținutul de pe această pagină aici)
Doriți să vă împărtășiți conținutul pe R-Bloggers? Faceți clic aici dacă aveți un blog sau aici dacă nu.
Introducere
Recent, am folosit Python’s yfinance Biblioteca pentru a efectua o analiză simplă a portofoliului. În loc să folosesc un caiet Jupyter pentru a rula codul Python și pentru a vizualiza rezultatele, m -am gândit că ar putea fi o idee plăcută să construiți o aplicație desktop .NET folosind Winui 3.0, XAML și C#. Aplicația vă permite să definiți un portofoliu și gestionați grafic prezentarea rezultatelor. Analiza de bază este realizată folosind Python. Pentru a apela codul Python de la C#, folosesc python.net. Scopul acestei postări pe blog este de a descrie modul de utilizare Python.net Pentru a apela funcțiile Python dintr -o aplicație C#. Cererea completă de portofoliomanager este disponibilă pe GitHub.

Principalul avantaj al acestei abordări este că permite accesul la ecosistemul extins al Python din .NET. În acest caz specific, am vrut să construiesc o aplicație simplă de gestionare a portofoliului, profitând în același timp de Python’s yfinance biblioteca pentru a obține datele financiare pe care mi le -am dorit și să le folosesc Pandas şi Ghânză pentru a efectua calculele. De asemenea, mi-am dorit un proiect ceva mai mult din lumea reală folosind Python.net. Informațiile despre acest lucru sunt oarecum limitate, iar exemplele sunt puțin schițate.
Versiuni
Folosim următoarele versiuni:
- Python 3.12
- yfinance 0.2.54
- Python.net 3.0.5
API -ul Python
Pentru a începe, am scris un script simplu de analiză a portofoliului. Scriptul se bazează pe o aplicație de gestionare a portofoliului scrisă în Python. Cu toate acestea, am refactat funcționalitatea principală pentru a separa calculele și analiza de stratul de prezentare (grafic). Acest lucru mi -a permis să apelez funcțiile Python de la C# pentru a obține datele și apoi să procesez datele (convertite) în aplicația C#.
API -ul este format din patru funcții.
-
portfolio_returns(ticker_values, start_date, end_date)Această funcție ia ticherele (și valorile) furnizate de utilizator, data de început și data de încheiere. Funcția calculează greutățile de bilet și obține prețurile apropiate ajustate (dacă sunt disponibile) pentru fiecare căscător folosind yfinance bibliotecă. Funcția returnează un dicționar cu greutățile tickerului, un date de date care conține returnări individuale de bilet și returnarea generală a portofoliului.
-
perform_portfolio_analysis(data, ticker_weights, risk_free_rate)Această funcție ia portofoliul returnări de date calculate anterior, greutățile tickerului și o rată fără riscuri furnizată de utilizator și folosește acest lucru pentru a calcula randamentele de securitate individuale, returnările cumulate, volatilitatea și raporturile Sharpe. Acestea sunt returnate într -un dicționar.
-
benchmark_returns(benchmark, start_date, end_date)Această funcție obține datele de referință din yfinance folosind parametrii furnizați. De asemenea, returnează un dicționar.
-
portfolio_vs_benchmark(port_returns, benchmark_returns, risk_free_rate)Această funcție calculează randamentele cumulate, volatilitatea anuală și raporturile Sharpe atât pentru portofoliu, cât și pentru referința folosind datele obținute în funcțiile anterioare. Oferă o comparație cot la cot pentru a evalua performanța portofoliului în raport cu referința.
Aceste patru funcții definesc API -ul utilizat de aplicația .NET.
Aplicația .NET
Premise
Pentru aplicația .NET trebuie să descărcăm și să construim Python.net. Codul este disponibil de la https://github.com/pythonnet/pythonnet. Odată descărcat, deschideți fișierul de soluție (pythonnet.sln) și selectați Python.Runtime ca proiect de pornire. Apoi faceți clic dreapta și construiți versiunea de depanare. Dacă construcția are succes, Python.Runtime.dll va fi localizat în directorul pythonnet sub pythonnet runtime.
Aplicația de portofoliomanager
După ce a construit Python.Runtime.dlltrebuie să -l adăugăm la aplicația Manager de portofoliu. Aceasta este o aplicație WinUI 3.0 (C#/XAML) care folosește setul de instrumente Windows Community pentru injecția de dependență și arhitectura MVVM. De asemenea, folosește o bază de date SQLite pentru a gestiona o colecție de unul sau mai multe portofolii.
În afară de cadrul de testare (Portofoliomanager.tests.mstest), soluția constă din două proiecte principale: Portofoliomanager şi Portofoliomanager.core. Portofoliomanager Proiectul se ocupă de logica aplicației, setările utilizatorului, vizualizările și modelele de vizualizare. Logica reutilizabilă și modelele de date sunt localizate în Portofoliomanager.core proiect. Deci adăugăm Python.Runtime.dll la Dependențe aici sub Adunări nodul.


Serviciul Python
În biblioteca de bază, definim un simplu IPythonService Interfață care încapsulează inițializarea și apelurile la API -ul Python.
public interface IPythonService
{
void Initialize(string pythonDll);
bool RunPortfolioAnalysis(PortfolioItem portfolio);
}
În Portofoliomanager Proiect, în app.xaml.cs, adăugăm IPythonService la lista serviciilor care vor fi activate
... services.AddSingleton();
În cele din urmă, trecem o instanță a IPythonService în PortfolioDetailViewModel. Acest model de vizualizare gestionează ieșirea graficelor ca urmare a rulării analizei portofoliului. Când vizualizarea este activată după ce faceți clic pe butonul „Run”, PythonService Poate fi inițializat și modelul de vizualizare poate rula analiza portofoliului pe baza portofoliului selectat.
public PortfolioDetailViewModel(IPythonService pythonService)
{
_pythonService = pythonService;
var settings = App.GetService();
var pythonDll = settings.PythonLibrary;
_pythonService.Initialize(pythonDll);
//...
}
Inițializarea PythonService constă în stabilirea calea către Python dll. Aceasta este configurată prin pagina Setări aplicații. În plus, am configurat câteva fluxuri de ieșire goale pentru a redirecționa orice ieșire a consolei de la Python.Runtime.
Analiza portofoliului
Funcția principală RunPortfolioAnalysis se numește cu cele selectate în prezent PortfolioItem. PortfolioItem Conține parametrii furnizați de utilizator care au fost obținuți din UI.


RunPortfolioAnalysis Funcția încarcă scriptul portofoliu_analize.py și îl transmite pe python.net, care creează un Python module obiect. module Obiectul este utilizat pentru a accesa funcțiile și variabilele scriptului. Folosim cuvântul cheie dinamic al lui C#pentru a întârzia legarea. Putem vedea funcțiile și parametrii disponibili dinamic, dacă extindem module variabilă în fereastra de ceas.


Analiza portofoliului se realizează apelând la funcțiile Python folosind parametrii furnizați de utilizator și orice rezultate intermediare. Odată ce acestea au fost convertite în mod corespunzător, acestea sunt copiate în membrii datelor din portofoliu.
...
dynamic portfolioResults = module.portfolio_returns(tickers, startDate, endDate);
var pyTickerWeights = new PyDict(portfolioResults("weights"));
portfolio.TickerWeights = Interop.Converters.ConvertTickerWeights(pyTickerWeights);
...
În acest fragment, obiectul Python pyTickerWeights este extras din dicționar returnat de la apel module.portfolio_returns. Python „dict” este transformat într -un C# List. ConvertTickerWeights Funcția doar iterează peste fiecare ticker și obține greutatea corespunzătoare. Acest lucru este apoi atribuit la TickerWeights Membru al articolului de portofoliu. TickerWeights Lista este apoi utilizată într -un grafic de plăcintă care afișează alocarea procentuală a tickerilor în portofoliu.
Odată ce analiza este completă, PortfolioDetailViewModel inițializează PlotModel cu datele din elementul de portofoliu, care apoi actualizează vizualizarea graficului, deoarece este legat de specific PlotModel.
Merită subliniat faptul că performanța este destul de lentă. Se pare că există două aspecte principale în acest sens:
- Inițializarea motorului Python pare să dureze ceva timp și poate că acest lucru ar putea fi făcut o dată la pornire.
-
PortfolioDetailViewModelRedă șase grafice cu diferite seturi de date în serie. Mi se pare că acest lucru ar putea fi mai bine refactat pentru a utiliza procesarea asincronă C#.
În prezent investighez ambele aceste probleme.
Stratul interop
Acest strat oferă facilități pentru a se converti între tipurile C# și Python. Tipurile de bază (șiruri, Ints, duble etc.) sunt convertite în mod transparent. De la C#, folosim .ToPython() Metoda de extensie pentru a converti la un obiect Python. Și de la Python, folosim .AsManagedObject(typeof(T)) Funcție pentru a returna un obiect C# pe care apoi îl putem arunca. Convertirea dintr -un PANDAS DataFrame sau o serie poate fi ceva mai implicată.
Învelire
Această postare pe blog a descris cum să apelați funcțiile Python dintr -o aplicație desktop C# folosind python.net. Acest aranjament (Winui 3.0 C# Front-End, Python.Net și Python) are avantajul flexibilității. Abordarea este destul de generalizabilă. Aș fi putut la fel de ușor să creez un model TensorFlow și să -l numesc dintr -o aplicație C#, dacă asta ar fi fost interesul meu. Pe de altă parte, principalul dezavantaj este performanța lentă. Acesta este un domeniu pe care îl investighez în prezent.
