Einloggen Suche | Aktive Themen
Kursreihe lesen Optionen
Ilhan Yesil
Geschrieben: Friday, March 31, 2017 10:05:36 PM
Gruppen: Kunde

Beiträge: 4

Liebes Taipan Team,

folgendes dauert recht lange (VisualStudio 13 C++):

for (int i = 1; i <= Schluss.get_Count(); i++) {
CKurs Schlusskurs;
Schlusskurs.AttachDispatch(Schluss.Item(i));
....
}


Gibt es keine Möglichkeit das Array auf einen Schlag zu lesen?

Die Wrapperklasse CKursReihe die durch den Assistenten von VC erzeugt wurde, enthält folgende Methoden:

Code:
void DatenArray(long Count, double * KursEintrag)
    {
        static BYTE parms[] = VTS_I4 VTS_PR8 ;
        InvokeHelper(0x2, DISPATCH_METHOD, VT_EMPTY, NULL, parms, Count, KursEintrag);
    }

Code:
    SAFEARRAY * get_KursArray()
    {
        InvokeHelper(0x3, DISPATCH_PROPERTYGET, VT_EMPTY, NULL, NULL);
    }

Code:
    SAFEARRAY * get_DatumArray()
    {
        InvokeHelper(0x4, DISPATCH_PROPERTYGET, VT_EMPTY, NULL, NULL);
    }

Code:
    SAFEARRAY * get_WertArray()
    {
        InvokeHelper(0x5, DISPATCH_PROPERTYGET, VT_EMPTY, NULL, NULL);
    }

Code:
    LPDISPATCH get_FaktorListe()
    {
        LPDISPATCH result;
        InvokeHelper(0x6, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&result, NULL);
        return result;
    }

Code:
    void DatenSafeArray(long Count, SAFEARRAY * * KursEintrag)
    {
        static BYTE parms[] = VTS_I4 VTS_UNKNOWN ;
        InvokeHelper(0x7, DISPATCH_METHOD, VT_EMPTY, NULL, parms, Count, KursEintrag);
    }


get_KursArray() oder get_WertArray() hätte ich für einen Kandidaten gehalten, aber es gibt kein return in der erzeugten Methode.

Was macht DatenSafeArray?

Gruß,
Ilhan
Daniel Beyersdorf
Geschrieben: Monday, April 3, 2017 1:37:22 PM

Gruppen: Mitarbeiter

Beiträge: 54

Tai-Pan RealtimeTai-Pan End-of-Daymarket makerbis. Realtime-Terminal
Hallo Ilhan,

Es gibt in der Tat ein effizienteren Weg die Daten auszulesen.
Wir arbeiten derzeit an einer neuen Dokumentation - leider ist diese noch nicht für Kunden einsehbar.

Um einfach an die Daten heran zu kommen, müssen Sie sich einmal die tpacc20.tlh und tpacc20.tli datei erzeugen.

Dies geht am einfachsten, wenn Sie in Ihrem Projekt unter den #include-Anweisungen eine #import-Anweisung für die 'TPACC20.DLL' schreiben.

in meinem Fall sieht dies so aus:
Code:
#import "c:\Program Files (x86)\Lenz + Partner AG\Tai-Pan Windows\17.0\TPACC20.DLL" no_namespace


nachdem das Projekt einmal erfolgreich kompiliert worden ist, sollten die Dateien tpacc20.tlh und tpacc20.tli erzeugt worden sein. (bei mir im Programm-Debug Ordner)
Kopieren Sie die Dateien in Ihr Programmverzeichnis und fügen Sie sie zum Projekt hinzu.
Die Import-Anweisung kann nun wieder entfernt oder auskommentiert werden.
includieren Sie stattdessen die eben erzeugte tpacc20.tlh
Code:
#include "tpacc20.tlh"


Nun müssten Sie folgendermaßen Zugriff auf die gewünschten Daten bekommen:

Code:

ITaiPanPtr    m_TaiPan = NULL;
if (m_TaiPan != NULL)
    m_TaiPan.Release();
m_TaiPan.CreateInstance(__uuidof(TaiPan));
if (m_TaiPan.GetInterfacePtr())
{
    _bstr_t    bstrWPK("D10000");
    IKursReihePtr    pKursReihe = m_TaiPan->GetKursReihe(bstrWPK, ktSchluss);
    if (pKursReihe != NULL && pKursReihe->GetCount()>0)
    {
        SAFEARRAY* pSAClose = NULL;
        TKursEintrag* pClose = NULL;
        long lCountClose = pKursReihe->GetCount();
        if (lCountClose>0)
        {
            pKursReihe->DatenSafeArray(lCountClose, &pSAClose);
            SafeArrayAccessData(pSAClose, (void**)&pClose);
            for(long i = 0;i<lCountClose;i++){
                //pClose[i].Wert;
                //pClose[i].Datum;
            }                
            SafeArrayUnaccessData(pSAClose);
            SafeArrayDestroy(pSAClose);
        }        
    }
}




Entwicklung | Lenz+Partner GmbH | vwd group
Phone: +49 231 9153-300 | Fax: +49 231 9153-399
entwicklung@lp-software.de | www.LP-software.de | www.vwd.com
Ilhan Yesil
Geschrieben: Monday, April 3, 2017 8:58:03 PM
Gruppen: Kunde

Beiträge: 4

Hallo Daniel,

das Beispiel funktioniert schon mal, vielen Dank. Ich hoffe die Dokumentation dauert nicht lange.

Gruß,
Ilhan

Ilhan Yesil
Geschrieben: Tuesday, April 18, 2017 11:13:42 PM
Gruppen: Kunde

Beiträge: 4

Das Lesen funktioniert jetzt ganz gut, aber ich habe folgendes Problem. Der 14.04 und 17.04 waren Feiertage. Ich habe heute ein Kursupdate gemacht. Die beiden Tage werden im Kurseditor angezeigt, die Daten sind naturgemäß leer. Wenn ich jetzt die Funktion GetKursReihe aufrufe, werden die beiden Feiertage mit den Daten des heutigen Tages gefüllt.
Ich habe auch ein Bereinigen der Datenbank über "Wartung"->"DB-Test..." "fehlerhafte Kurse aus Kursreihen entfernen" versucht, aber die beiden Feiertage bleiben drin.
Thorsten Kitzig
Geschrieben: Wednesday, April 19, 2017 10:24:06 AM
Gruppen: Insider

Beiträge: 22

Tai-Pan RealtimeTai-Pan End-of-Day
Hallo Ilhan,

die Funktion GetKursReihe(...) hat als 2. Parameter die Kursart.
ktSchluss ist hierbei die Schlusskursreihe mit aufgefüllten Lücken. Also immer eine vollständige Woche von Montag bis Freitag. Um die Kursreihe ohne Feitertage zu bekommen stehen 2 weitere Varianten der Konstanten ktSchluss zur Verfügung. ktSchlussML und ktSchlussOL. ML liefert die Kursreihe "MitLücken" und OL "OhneLücken". Die Lücken werden durch den Kurs 0.0 dargestellt.
Für eine Indikatorberechnung oder Kursverarbeitung ist die Variante ktSchlussOL die Einfachste, da hier einfach alle Kursdaten hintereinander geliefert werden.


Gruß

Thorsten Kitzig
Ilhan Yesil
Geschrieben: Wednesday, April 19, 2017 9:19:59 PM
Gruppen: Kunde

Beiträge: 4

Super, hat geklappt, Danke.
Baerbel Otremba
Geschrieben: Wednesday, July 19, 2017 11:51:10 AM
Gruppen: Kunde

Beiträge: 2

Ich habe das gleiche Problem, der Zugriff ist bei mir auch zu langsam, allerdings verwende ich C#

Wie ist das vorgehen in C# ?


Gruß
Sven
Baerbel Otremba
Geschrieben: Wednesday, July 19, 2017 11:51:14 AM
Gruppen: Kunde

Beiträge: 2

Ich habe das gleiche Problem, der Zugriff ist bei mir auch zu langsam, allerdings verwende ich C#

Wie ist das vorgehen in C# ?


Gruß
Sven
Christian Kuntz
Geschrieben: Thursday, August 24, 2017 7:13:15 PM
Gruppen: Kunde

Beiträge: 1

Hallo Baerbel, Hallo alle interessierten wink

bei mir hat folgender Ansatz in C# funktioniert:
Ich habe mir eine Class Library "TaiPan.Reader" geschrieben, die unter anderem folgende statischen Methoden enthält:

1. Konstruktion: (Hinweis: die Klasse heißt derzeit noch "Application" und ist static)
static Application()
{
Task<TaiPan> initTaiPanTask = Task<TaiPan>.Factory.StartNew(() => new TaiPanClass(), CancellationToken.None, TaskCreationOptions.None, Scheduler);
initTaiPanTask.Wait();
App = initTaiPanTask.Result;
AnzahlKataloge = App.KatalogListe.Count;
}

Das Objekt Scheduler nagelt mir alles auf das Single Threaded Apartment fest, obwohl ich TPL (async/await) benutze.
Es wird vor dem Konstruktor wie folgt initialisiert:

/// <summary>
/// This scheduler should be used exclusively for all Tasks accessing the TaiPan COM server.
/// It ensures thread-safe operations by enforcing the STA model.
/// </summary>
private static readonly StaTaskScheduler Scheduler = new StaTaskScheduler( 8 );

private static readonly TaiPan App;



Du findest den StaTaskScheduler im Paket MSFT.ParallelExtensionsExtras von Stephen Cleary, das Du Dir mit NuGet in Dein Projekt lädst.
Siehe hierzu bei Interesse https://blogs.msdn.microsoft.com/pfxteam/2010/04/07/parallelextensionsextras-tour-5-stataskscheduler/

Dann habe ich eine Methode, die mir jeweils zwei Arrays für einen bestimmten Kurstypen liefert: Die Datumswerte und die Kurse.
Das klappt nach meinen Tests in C# besser, als der Umgang mit dem SafeArray:

private static (Array tage, Array kurse) GetKursreiheArrays(string wpk, KursTyp typ)
{
var kursreihe = App.KursReihe[wpk, typ];
return kursreihe == null
? (Array.Empty<object>(), Array.Empty<object>())
: (kursreihe.Count == 0
? (Array.Empty<object>(), Array.Empty<object>())
: (kursreihe.DatumArray, kursreihe.WertArray));
}

Um jetzt eine schöne OHLCV Kursreihe für ein einzelnes Wertpapier zu bekommen, verwende ich folgendes:

public static async Task<IList<KurseOL>> GetKurseOLArrayMethodAsync(string wpk, CancellationToken cancelToken, IProgress<int> reportProgress)
{
IList<Task<(Array tage, Array kurse)>> arraysHolenTasks = new List<Task<(Array tage, Array kurse)>>(5);
var getOpenTask = Task<(Array tage, Array kurse)>.Factory.StartNew(
() => GetKursreiheArrays(wpk, KursTyp.ktEroeffnungOL), cancelToken, TaskCreationOptions.LongRunning, Scheduler);
arraysHolenTasks.Add(getOpenTask);
var getHighTask = Task<(Array tage, Array kurse)>.Factory.StartNew(
() => GetKursreiheArrays(wpk, KursTyp.ktHochOL), cancelToken, TaskCreationOptions.LongRunning, Scheduler);
arraysHolenTasks.Add(getHighTask);
var getLowTask = Task<(Array tage, Array kurse)>.Factory.StartNew(
() => GetKursreiheArrays(wpk, KursTyp.ktTiefOL), cancelToken, TaskCreationOptions.LongRunning, Scheduler);
arraysHolenTasks.Add(getLowTask);
var getCloseTask = Task<(Array tage, Array kurse)>.Factory.StartNew(
() => GetKursreiheArrays(wpk, KursTyp.ktSchlussOL), cancelToken, TaskCreationOptions.LongRunning, Scheduler);
arraysHolenTasks.Add(getCloseTask);
var getVolTask = Task<(Array tage, Array kurse)>.Factory.StartNew(
() => GetKursreiheArrays(wpk, KursTyp.ktVolumenOL), cancelToken, TaskCreationOptions.LongRunning, Scheduler);
arraysHolenTasks.Add(getVolTask);
cancelToken.ThrowIfCancellationRequested();
await Task.WhenAll(arraysHolenTasks).ConfigureAwait(false);
var open = getOpenTask.Result;
var high = getHighTask.Result;
var low = getLowTask.Result;
var close = getCloseTask.Result;
var vol = getVolTask.Result;
IDictionary<DateTime, KurseOL> result = new Dictionary<DateTime, KurseOL>();
for (int i = 0; i < close.tage.Length; i++)
{
var datum = Convert.ToDateTime(close.tage.GetValue(i));
var kurs = Convert.ToDouble(close.kurse.GetValue(i));
result.Add(datum, new KurseOL { Datum = datum, Wpk = wpk, Close = kurs });
}

for (int i = 0; i < open.tage.Length; i++)
{
var datum = Convert.ToDateTime(open.tage.GetValue(i));
var kurs = Convert.ToDouble(open.kurse.GetValue(i));
if (result.ContainsKey(datum))
{
result[datum].Open = kurs;
}
else
{
result.Add(datum, new KurseOL { Datum = datum, Wpk = wpk, Open = kurs });
}
}

for (int i = 0; i < high.tage.Length; i++)
{
var datum = Convert.ToDateTime(high.tage.GetValue(i));
var kurs = Convert.ToDouble(high.kurse.GetValue(i));
if (result.ContainsKey(datum))
{
result[datum].High = kurs;
}
else
{
result.Add(datum, new KurseOL { Datum = datum, Wpk = wpk, High = kurs });
}
}

for (int i = 0; i < low.tage.Length; i++)
{
var datum = Convert.ToDateTime(low.tage.GetValue(i));
var kurs = Convert.ToDouble(low.kurse.GetValue(i));
if (result.ContainsKey(datum))
{
result[datum].Low = kurs;
}
else
{
result.Add(datum, new KurseOL { Datum = datum, Wpk = wpk, Low = kurs });
}
}

for (int i = 0; i < vol.tage.Length; i++)
{
var datum = Convert.ToDateTime(vol.tage.GetValue(i));
var kurs = Convert.ToDouble(vol.kurse.GetValue(i));
if (result.ContainsKey(datum))
{
result[datum].Volume = kurs;
}
else
{
result.Add(datum, new KurseOL { Datum = datum, Wpk = wpk, Volume = kurs });
}
}

reportProgress?.Report(result.Count);
return result.Values.ToList();
}

Die diversen FOR Schleifen wirken redundant und ReSharper nervt auch ständig, er wolle sie in LINQ Anweisungen umwandeln, aber so ist es performanter.

Auf meinem Notebook konnte ich so etwa 120 komplette Kursreihen (OHLCV) je Sekunde auslesen, was 600x2 Arrays und etwa 350.000 einzelnen Datensätzen (bzw. 3.500.000 Arrayelementen) entsprach.

Derzeit bastele ich daran, das in annähernd demselben Tempo in meine SQL Server Datenbank zu kriegen...

Du solltest .NET 4.7 benutzen, damit z.B. die named Tuples (Array tage, Array kurse) funktionieren.

Bei Fragen komm gerne auf mich zurück.

Würde mich freuen, wenn wir hier einen Interface- / C# Entwickler Austausch hinbekämen.

EDIT: Wenn ich die Werte mit TPL Dataflow und EF weiterverarbeite, um sie auf meine Datenbank zu schreiben, geht die Geschwindigkeit ziemlich in die Knie;

habe jetzt 110 Minuten gebraucht, um 161.005 Kursreihen mit insges. 344,2 Mio Kursen in meine DB zu kopieren.

Ich werde in nächster Zeit etwas experimentieren, wie ich das beschleunigen kann, bin aber bis hier schon mal ganz zufrieden.
Was ich noch nicht so schnell hinkriege, ist das Laden der diversen Stammdaten, insbesondere der daranhängenden Arrays (EBIT, Dividenden, ...).

Wenn das mal alles passt und ich erste Backtests auf der DB hinkriege, mehr dazu hier und in einem extra Blog...

Thorsten Kitzig
Geschrieben: Friday, August 25, 2017 5:00:10 PM
Gruppen: Insider

Beiträge: 22

Tai-Pan RealtimeTai-Pan End-of-Day
Hier mal ein paar nützliche Informationen zur Verwendung von COM-Objekten in C#
Speed:
Jeder Aufruf einer Methode eines COM-Objektes, und damit ist auch der Zugriff per [ ] gemeint, muss von .net über die erzeugte Wrapper-Klasse erfolgen. Alle COM Objekte werden als OutOfProcess-Objekt behandelt. Deshalb muss bei jedem Aufruf die Prozessgrenze zwischen der .net-Applikation und dem Wrapper für COM überwunden werden. Das kann nur ca. 28.000 bis 30.000 mal/sec. erfolgen. Wenn also eine Kursreihe mit 5000 Kursen für jeweils OHLCV (das sind dann schon 25.000 einzelne Kurse) per [ ] abgerufen werden, kann das schon mal 1 Sec. / Kursreihe dauern. 2 Sec. wenn jeweils auch noch das Datum mit abgerufen wird. Aus diesem Grund sollten die Kursreihen per .DatumArray und .KursArray geladen werden. Das reduziert die Anzahl der COM-Aufrufe erheblich. (Im Beispiel von 25.000 auf 5 bzw. mit Datumsreihe von 50.000 auf 10) nach dem Aufruf liegen alle Daten dann als .net-Daten vor und können ohne lange Wartezeiten verarbeitet werden.
Bei einem Win32-Programm ist das verhalten nicht so, da der Tai-Pan COM-Server dann ein InProcess-Objekt ist und keine Prozessgrenzen mehr überwunden werden müssen.
ForEach:
Der Aufruf von foreach ruft vom Objekt X ein enum-Interface ab, welches foreach dann für die Schleife verwendet. Wenn nun aber nach dem Abrufen des enum-Interfaces keine weiteren Aufrufe an das Object X mehr im Quelltext vorkommen, erkennt das die optimierung des Compilers und setzt den Referenzcounter bereits auf 0. Das kann dazu führen, dass der Garbage Collector von .net das Objekt X freigeben will obwohl noch ein foreach läuft. Dieses Problem tritt vor allem bei größeren Listen von Kursen oder Katalogen auf. Das lässt sich nur dadurch ändern, dass nach der foreach Schleife noch Zugriffe auf das Objekt X existieren, oder der Zugriff auf eine For()-Schleife geändert wird.
Dieses Problem hatte ich auch schon mal hier im TPR-Forum beschrieben. Das kann durchaus auch beim Verarbeiten von Katalogen auftauchen, wenn alle Einträge im Katalog per ForEach verarbeitet werden.

Hier der Code aus dem Tai-Pan Realtime Forum
Code:
for (int nReadIndex = 1; nReadIndex <= stammCol.Count; nReadIndex++)
{
    Stamminformationen stamm = stammCol[nReadIndex];
    Debug.WriteLine("{0}   {1}", stamm.Name, stamm.Symbol, stamm.FullSymbol);
}
ACHTUNG: Com-Aufzählungen beginnen immer bei 1 als kleinster Index!

Grüße

Thorsten Kitzig
Benutzer die diese Diskussion aktuell lesen
Guest

Powered by Yet Another Forum.net version 1.9.1.8 (NET v4.0) - 3/29/2008
Copyright © 2003-2008 Yet Another Forum.net. All rights reserved.

Durch die Nutzung der Webseite stimmen Sie der Verwendung von Cookies zu. Weitere Informationen zum Datenschutz finden Sie hier