Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Aufbau einer Währungstabelle (https://www.delphipraxis.net/183568-aufbau-einer-waehrungstabelle.html)

DSP 20. Jan 2015 16:32

Aufbau einer Währungstabelle
 
Tach Zusammen,

weiss grad nicht wo das Tehma hingehört, vielleicht hier oder in den Bereich der Datenbanken. Aber ich versuche es mal hier :oops:

Wollte ganz gerne in SQLite eine Währungstabelle anlegen, mit folgenden Aufbau:
{@Datum;@Kurstyp;@Währung_Von;@Währung_Nach;Kurs}
Jetzt wollte ich gerne performant und auch häufig die Tabelle abfragen. Dabei tritt das Problem auf, dass ich nicht weiss wann der letzte Eintrag in der Tabelle vorhanden ist. Daher die Frage an Euch, wie würdet ihr so eine Abfrage/Tabellendesign am besten gestallten, dass man auch bei vielen Einträgen mit möglichst geringen Kosten auskommt und der gültige Datensatz zuverlässig geliefert wird?

Schon mal vielen Dank für die Tipps
DSP

Sir Rufo 20. Jan 2015 17:13

AW: Aufbau einer Währungstabelle
 
Wer trägt denn die Daten in die Tabelle ein? Doch wohl die gleiche Anwendung.

Generell würde ich hier den gesamten Verhau grundsätzlich lösen.

Einen Wert-Typ für die Währung
Einen Wert-Typ für die Umrechnung
Einen Wert-Typ für den Währungsbetrag
Ein Repository für die Werte
Einen Umrechner der einem Währungsbetrag in eine andere Währung umrechnen kann

Ein Repository wird immer als Interface ausgelegt, dadurch kann man sehr schön auch einen Proxy/Cache dazwischenschalten, der hier in diesem Fall z.B. die Werte zwischenspeichert, so dass für jeden Wert nur eine Abfrage an die Datenbank geht und alle weiteren werden aus dem Cache bedient.

Wenn ich etwas Zeit finde, kann ich da mal ein Konstrukt zusammenbauen

jobo 21. Jan 2015 06:36

AW: Aufbau einer Währungstabelle
 
Zitat:

Zitat von Sir Rufo (Beitrag 1287181)
Generell würde ich hier den gesamten Verhau grundsätzlich lösen.

Einen Wert-Typ für die Währung
Einen Wert-Typ für die Umrechnung
Einen Wert-Typ für den Währungsbetrag
Ein Repository für die Werte
Einen Umrechner der einem Währungsbetrag in eine andere Währung umrechnen kann

Wenn ich die Anforderung richtig geahnt hab, ist hier noch das Datum im Spiel, also historisierte Daten, also Kursverläufe. Es wäre also ein gegebenes Eintragsdatum zu berücksichtigen und bei der Abfrage eine Vorschrift, die aus dem gewünschten Zeitpunkt und den nächsten vorhandenen Werten in der Historie einen definierten Wert ausspuckt (nächster, voriger, Mittelwert daraus, ...)

DSP 21. Jan 2015 07:51

AW: Aufbau einer Währungstabelle
 
Hallo Zusammen,

danke für die Hilfe, das Hauptproblem das sich für mich darstellt, wie kann man am effizientesten auf den aktuellen Datensatz zugreifen, wenn man den Schlüssel nicht kennt? Dazu kommt noch, dass das Rücklesen auch nicht besonderes gut ist, da es ggf auch zuvor keine Datensätze gibt. Der Anwender kann zur Umrechnung prinzipiell jedes mögliche Datum eingeben. Hier ist eben die Frage, wie man das am effizientesten lösen kann.

Schon mal vielen Dank
DSP

hstreicher 21. Jan 2015 08:58

AW: Aufbau einer Währungstabelle
 
Welche Felder haben denn einen Index ?

ggf auf die Typisch abgefragten Felder einen Index anlegen

jobo 21. Jan 2015 09:24

AW: Aufbau einer Währungstabelle
 
Zitat:

Zitat von DSP (Beitrag 1287234)
das Hauptproblem das sich für mich darstellt, wie kann man am effizientesten auf den aktuellen Datensatz zugreifen, wenn man den Schlüssel nicht kennt? Dazu kommt noch, dass das Rücklesen auch nicht besonderes gut ist, da es ggf auch zuvor keine Datensätze gibt. Der Anwender kann zur Umrechnung prinzipiell jedes mögliche Datum eingeben. Hier ist eben die Frage, wie man das am effizientesten lösen kann.

Was meinste Du mit "..auf den aktuellen Datensatz zugreifen.."?
Den zuletzt eingegebenen, weil es doch nur um aktuelle Wechselkurse geht?
Oder doch quer Beet ("der anwender kann ..jedes mögliche Datum eingeben..")?
Also ich vermute nochmal, dass ggF. Werte historisiert vorliegen und dass das abgefragte Datum bzw. der Kurs zu dieser Zeit exakt so wie abgefragt bestenfalls zufällig vorhanden ist.
Mglw. wäre hier ein Ansatz:
Code:
select * from wechselkurshistorie
where ..siehe unten bei DeddyH
Jenachdem ob was rauskommt oder nicht, muss noch geprüft werden, ob nur eine Bedingung erfüllt ist (möglicher Treffer ganz am Anfang oder Ende)
Weiter muss eingeschränkt werden, welche beiden Währungen den Kurs definieren (2 Fremdschlüssel?, also 2 weitere Einschränkungen im where bzw.Joins)
Was meinst Du mit Rücklesen?

DeddyH 21. Jan 2015 09:30

AW: Aufbau einer Währungstabelle
 
Ich hätte eher so gedacht (ungetestet):
SQL-Code:
SELECT
  *
FROM
  wechselkurse
WHERE
  Datum = (
    SELECT
      MAX(Datum)
    FROM
      wechselkurse
    WHERE
      Datum <= :Abfragedatum
  )

jobo 21. Jan 2015 09:42

AW: Aufbau einer Währungstabelle
 
Zitat:

Zitat von DeddyH (Beitrag 1287244)
Ich hätte eher so gedacht

Ja, hätt ich auch mal gedacht, dann würde da oben nicht so ein Schrott stehen :)

DeddyH 21. Jan 2015 09:55

AW: Aufbau einer Währungstabelle
 
Mach Dir nichts draus, sowas passiert mir auch öfter :)

Sir Rufo 21. Jan 2015 12:38

AW: Aufbau einer Währungstabelle
 
Mein Vorschlag dazu wäre
SQL-Code:
DROP TABLE IF EXISTS "currency_rates";
DROP TABLE IF EXISTS "currencies";

-- Stammdaten Währungen

CREATE TABLE "currencies" (
    "ISOCODE" VARCHAR(3) NOT NULL,
    "ISONUM" int(3) NOT NULL,
    "ISONUMdate" date NOT NULL,
    "Name" text,
    "Format" text,
   PRIMARY KEY("ISOCODE")
);
CREATE INDEX "idx_currencies_ISONUM" ON currencies (ISONUM, ISONUMdate);
CREATE UNIQUE INDEX "idx_currencies_ISONUMCODE" ON currencies (ISONUM, ISONUMdate, ISOCODE);

-- Währungs-Daten

INSERT INTO "currencies" ( "ISOCODE", "ISONUM", "ISONUMdate", "Name", "Format" )
  VALUES ( 'EUR', 978, '1990-01-01', 'Euro', '%f €' );
INSERT INTO "currencies" ( "ISOCODE", "ISONUM", "ISONUMdate", "Name", "Format" )
  VALUES ( 'USD', 840, '1990-01-01', 'Dollar', '$ %f' );

-- Besonderheiten bei der Belegung der ISONUM Werte

INSERT INTO "currencies" ( "ISOCODE", "ISONUM", "ISONUMdate", "Name", "Format" )
  VALUES ( 'BUK', 104, '1952-07-01', 'Burma Kyat', '%f BUK' );
INSERT INTO "currencies" ( "ISOCODE", "ISONUM", "ISONUMdate", "Name", "Format" )
  VALUES ( 'MMK', 104, '1990-02-01', 'Myanmar Kyat', '%f MMK' );

INSERT INTO "currencies" ( "ISOCODE", "ISONUM", "ISONUMdate", "Name", "Format" )
  VALUES ( 'SUR', 810, '1923-01-01', 'Sowjetischer Rubel', '%f SUR' );
INSERT INTO "currencies" ( "ISOCODE", "ISONUM", "ISONUMdate", "Name", "Format" )
  VALUES ( 'RUR', 810, '1991-01-01', 'Russicher Rubel', '%f RUR' );
INSERT INTO "currencies" ( "ISOCODE", "ISONUM", "ISONUMdate", "Name", "Format" )
  VALUES ( 'RUB', 643, '1998-01-01', 'Neuer Russischer Rubel', '%f RUB' );

-- Wechselkurse

CREATE TABLE "currency_rates" (
    "sourceISOCODE" varchar(3) NOT NULL,
    "destinationISOCODE" varchar(3) NOT NULL,
    "ratetype" varchar(3) NOT NULL,
    "ratedate" "date" NOT NULL,
    "rate" real NOT NULL,
   PRIMARY KEY("sourceISOCODE","destinationISOCODE","ratetype","ratedate"),
   CONSTRAINT "fk_source_currency" FOREIGN KEY ("sourceISOCODE")
     REFERENCES "currencies" ("ISOCODE") ON DELETE CASCADE ON UPDATE CASCADE,
   CONSTRAINT "fk_destination_currency" FOREIGN KEY ("destinationISOCODE")
     REFERENCES "currencies" ("ISOCODE") ON DELETE CASCADE ON UPDATE CASCADE
);

-- Daten Wechselkurse

INSERT INTO "currency_rates" ("sourceISOCODE", "destinationISOCODE", "ratetype", "ratedate", "rate")
  VALUES ( "EUR", "USD", "AWK", date('2015-01-01'), 1.11 );
INSERT INTO "currency_rates" ("sourceISOCODE", "destinationISOCODE", "ratetype", "ratedate", "rate")
  VALUES ( "EUR", "USD", "AWK", date('2015-01-02'), 1.12 );
INSERT INTO "currency_rates" ("sourceISOCODE", "destinationISOCODE", "ratetype", "ratedate", "rate")
  VALUES ( "EUR", "USD", "AWK", date('2015-01-03'), 1.13 );
INSERT INTO "currency_rates" ("sourceISOCODE", "destinationISOCODE", "ratetype", "ratedate", "rate")
  VALUES ( "EUR", "USD", "AWK", date('2015-01-04'), 1.14 );
INSERT INTO "currency_rates" ("sourceISOCODE", "destinationISOCODE", "ratetype", "ratedate", "rate")
  VALUES ( "EUR", "USD", "AWK", date('2015-01-05'), 1.15 );
INSERT INTO "currency_rates" ("sourceISOCODE", "destinationISOCODE", "ratetype", "ratedate", "rate")
  VALUES ( "EUR", "USD", "AWK", date('2015-01-06'), 1.16 );
INSERT INTO "currency_rates" ("sourceISOCODE", "destinationISOCODE", "ratetype", "ratedate", "rate")
  VALUES ( "EUR", "USD", "AWK", date('2015-01-07'), 1.17 );

-- Abfrage eines konkreten Wechselkurs

SELECT *
FROM currency_rates
WHERE sourceISOCODE = 'EUR'
AND destinationISOCODE = 'USD'
AND ratetype = 'AWK'
AND ratedate <= date( '2015-01-03' )
ORDER BY ratedate DESC
LIMIT 1;

smallie 21. Jan 2015 14:31

AW: Aufbau einer Währungstabelle
 
Ich denke, es ist keine gute Idee, für jede beliebige Kombination von Währungen Umrechnungskurse vorzuhalten.

Besser wäre es nur den Umrechnungskurse Fremdwährung/EURO zu speichern und bei Bedarf zu rechnen. So sah das auch die EZB bei der EURO-Umstellung vor.

Sir Rufo 21. Jan 2015 15:29

AW: Aufbau einer Währungstabelle
 
Zitat:

Zitat von smallie (Beitrag 1287309)
Ich denke, es ist keine gute Idee, für jede beliebige Kombination von Währungen Umrechnungskurse vorzuhalten.

Besser wäre es nur den Umrechnungskurse Fremdwährung/EURO zu speichern und bei Bedarf zu rechnen. So sah das auch die EZB bei der EURO-Umstellung vor.

Öhhh, wo steht das, dass das so gemacht wird? Und du wolltest wahrscheinlich auch sagen, dass man immer die Umrechnungskurse von seiner Basis-Währung zur Fremd-Währung speichert und die Umrechnung dann bei Fremd-Währung zu Fremd-Währung immer erst über die eigene Basis-Währung geht.

Und mit meinem Datenbank-Modell kann man doch tatsächlich mehrere Basis-Währungen verwalten (ergo geht auch nur eine). Aber warum soll ich das Design jetzt schon auf eine Basis-Währung festlegen, wenn es doch ohne Probleme auch für mehrere geht. Dann schreibe ich mir den gesamten Rotz einmal und kann den immer wiederverwenden und auch dann, wenn die Anwendung unterschiedliche Aspekte (unterschiedliche Unternehmen mit unterschiedlichen Basis-Währungen) verwalten soll? Das wäre dann doch einfach nur blind, oder? ;)

p80286 21. Jan 2015 15:38

AW: Aufbau einer Währungstabelle
 
Zitat:

Zitat von smallie (Beitrag 1287309)
Ich denke, es ist keine gute Idee, für jede beliebige Kombination von Währungen Umrechnungskurse vorzuhalten.

Besser wäre es nur den Umrechnungskurse Fremdwährung/EURO zu speichern und bei Bedarf zu rechnen. So sah das auch die EZB bei der EURO-Umstellung vor.

und in der Praxis machen die Wechselkurshopper, die zb. USDollar -> türk. Lira -> Euro -> USDollar wechseln mit diesen minimalen Unterschieden ihr Geld.
Jede (frei konvertierbare) Währung hat eigene Wechselkurse. Aus diesem Grunde werden viele Geschäfte nur in USDollar (oder Euro,Schweizer Franken) abgewickelt, damit das Wechselkursrisiko möglichst gering bleibt.

Gruß
K-H

Sir Rufo 21. Jan 2015 16:01

AW: Aufbau einer Währungstabelle
 
Gerade für dieses Währungs-Thema macht es Sinn eigene Daten-Typen anzulegen, denn dann wird man typsicher im Kontext.

Kleines Beispiel:
Delphi-Quellcode:
program dp_183568;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  CurrencyValue in 'CurrencyValue.pas',
  MemoryCurrencyRepository in 'MemoryCurrencyRepository.pas';

procedure Prepare;
begin
  TDateType.Freezed := False;
  TCurrencyType.DEFAULT := 'EUR';
  TCurrencyValue.CurrencyRepository := TMemoryCurrencyRepository.Create;

  TCurrencyValue.CurrencyRepository.StoreConvertValue( TCurrencyConvertValue.Create( '01.01.2000', TCurrencyFactorValue.Create( 'EUR', 'DEM', 1.9558 ) ) );
  TCurrencyValue.CurrencyRepository.StoreConvertValue( TCurrencyConvertValue.Create( '01.01.2000', TCurrencyFactorValue.Create( 'EUR', 'ATS', 13.7603 ) ) );

  TCurrencyValue.CurrencyRepository.StoreConvertValue( TCurrencyConvertValue.Create( '01.10.2014', TCurrencyFactorValue.Create( 'EUR', 'USD', 1.00 ) ) );
  TCurrencyValue.CurrencyRepository.StoreConvertValue( TCurrencyConvertValue.Create( '01.11.2014', TCurrencyFactorValue.Create( 'EUR', 'USD', 1.10 ) ) );
  TCurrencyValue.CurrencyRepository.StoreConvertValue( TCurrencyConvertValue.Create( '01.12.2014', TCurrencyFactorValue.Create( 'EUR', 'USD', 1.20 ) ) );
  TCurrencyValue.CurrencyRepository.StoreConvertValue( TCurrencyConvertValue.Create( '01.01.2015', TCurrencyFactorValue.Create( 'EUR', 'USD', 1.30 ) ) );
end;

procedure OutputCurrencyValueIn( AValue: TCurrencyValue; ACurrency: TCurrencyType );
begin
  Writeln( AValue.ToString, ' ==(', TDateType.TODAY.ToString, ')=> ', AValue.Convert( ACurrency ).ToString );
end;

procedure Test;
var
  LValue: TCurrencyValue;
begin
  LValue := 500; // Wegen dem DEFAULT Wert sind das 500,00 EUR

  OutputCurrencyValueIn( LValue, 'DEM' );
  OutputCurrencyValueIn( LValue, 'ATS' );

  OutputCurrencyValueIn( LValue, 'USD' );
  TDateType.TODAY := '15.12.2014'; // TODAY-Datum festlegen
  OutputCurrencyValueIn( LValue, 'USD' );
  TDateType.Freezed := False; // TODAY folgt nun wieder dem SYSTEM-Datum
  OutputCurrencyValueIn( LValue, 'USD' );

  TDateType.TODAY := '15.11.2014'; // TODAY-Datum festlegen
  OutputCurrencyValueIn( LValue, 'USD' );
  TDateType.TODAY := '15.10.2014'; // TODAY-Datum festlegen
  OutputCurrencyValueIn( LValue, 'USD' );
  TDateType.TODAY := '15.09.2014'; // TODAY-Datum festlegen
  OutputCurrencyValueIn( LValue, 'USD' );
  TDateType.Freezed := False; // TODAY folgt wieder dem SYSTEM-Datum
end;

begin
  try
    Prepare;
    Test;
  except
    on E: Exception do
      Writeln( E.ClassName, ': ', E.Message );
  end;
  Readln;

end.
Oder um das Wechsel-Orgien-Beispiel aufzugreifen, würde man einfach nur schreiben
Delphi-Quellcode:
// Fremdwährung erfassen
LValue := TCurrencyValue.Create( 1000, 'USD' );
// Wechseldifferenz mit dem jeweiligen Ausgabe-Kurs berechnen
LChangeDiff := Lvalue.Convert( 'TRL', 'AGK' ).Convert( 'EUR', 'AGK' ).Convert( 'USD', 'AGK' );
// Haben wir etwas verdient?
if LChangeDiff.Value > 0 then
  WriteLn( 'Lass uns das machen, wir verdienen daran ', LChangeDiff.ToString, ' was aktuell ', LChangeDiff.Convert( 'EUR', 'AGK' ), ' entspricht.' );
;)

DSP 24. Jan 2015 17:52

AW: Aufbau einer Währungstabelle
 
Hallo Zusammen,

vielen Dank für die Unterstützung. Ich denke, ich werde die Deddy Variante verwenden, da sie mir am performantesten vorkommt. Keine zusätzliche Ergebnistabelle, welche zu sortieren und abzuarbeiten ist.

Code:
SELECT
  *
FROM
  wechselkurse
WHERE
  Datum = (
    SELECT
      MAX(Datum)
    FROM
      wechselkurse
    WHERE
      Datum <= :Abfragedatum
  )
Das Tabellenwerk werde ich mir mit einem ORM realisieren, da ist dann das Tabellenwerk direkt im ORM gekapselt und wenn man dann die Währungsfunktionalität benötigt, reicht es praktisch eine Unit in den Code einzubinden.

Ob man dann über eine Basiswährung geht oder und wie man die Faktoren behandelt, sind dann nur kleine Implementierungsdetails.

Grüsse
DSP

mensch72 25. Jan 2015 09:37

AW: Aufbau einer Währungstabelle
 
Bei den Wechselkursen hin&her ist hier leider ein entscheidender Fakt vergessen worden:
An- & Verkaufskurs sind immer UNTERSCHIEDLICH. Zu erfassen sind also immer ein Geldkurs & ein Briefkurs, auch als Bid/Ask bekannt.

Wenn es genau werden soll/muss, zählt auch die Uhrzeit. Denn alles vor dem (EZB)Fixing wird zum Vortageskurs abgerechnet, alles danach zum "Tageskurs"


Leider funktioniert auch das Beispiel mit der Wechselorgie im Dreieck nicht so einfach, das man es sich machen ließe, wen der vermeintliche Ertrag Positiv ist...
Erstens fehlt hier gleich 2x oder 3x der Bid/Ask-Spread plus der Aussführungsspread/Transaktionsgebühren. Zweitens braucht es zum historischen prüfen eine gute(teure) Kurshistory mit einer sehr hohen Zeitauflösung(im Idealfall bis Millisekunden) und zur Liveausführung einer möglichen Transaktion eine schnelle automatische Orderausführung.


Für eine Lagerbestandsbewertung oder für eine Rechnungsstellung in Fremdwährung reicht so eine "einfache" Kursdatenbank eventuell, aber man sollte wissen wo die Grenzen des Konzepts liegen. Umgedreht wäre eine Lösung mit z.B. 300Mio Datensätzätzen für alle auf die Millisekunde genau erfassten EUR/USD Transaktionen der letzten 10Jahre total OverSized, wenn ein einfacher Tageskurs reicht. (zufällig kenne/habe ich beides, und weiß das sobald die Zeit sekundengenau über Zeitzonen hinweg ins Spiel kommt, der Spaß aufhört und es ernst wird)

-> man schaue in sein Pflichten-/Lastenheft und prüfe, welche Anforderung an Live Genauigkeit und historische Reproduziebarkeit notwendig bzw. sinnvoll ist
=> erst dann lässt sich die Frage nach dem sinnvollem Aufbau einer Währungstabelle samt passender (ORM)Klassenstruktur praktisch beantworten
-> eine lokale Kurshistory als Cache ist ok, aber zum Füllen der History sollte man online die Kurse abfragen. Für Tageskurse gibt es kostenlose (Web)Services, für komplette Historien oder zeitnahe bis zu Realtime Kursen gibt es kaum kostenlose Quellen, aber gegen Geld gibt es für jede Anforderung irgendwo ein passendes Datenabo.

Dejan Vu 25. Jan 2015 10:01

AW: Aufbau einer Währungstabelle
 
Deine Ausführungen (bis auf bid/ask) gelten aber nur zum Daytraden, oder?
Das Bankensystem, das wir betreuen, enthält eine Währungstabelle, die 'nur' täglich aktualisiert wird.
Ggf. kann man sich den Kurs auch online holen.

mensch72 25. Jan 2015 10:47

AW: Aufbau einer Währungstabelle
 
Das Bid&Ask beim "DayTraden" sehr wichtig sind ist klar, aber wir leben hier ja im bürokratischem Steuerparadies Deutschland... und wenn man die Daten hat, kann man damit auch andere praktische Anwendungsmöglichkeiten finden, welche mangels Datenbestand/Datenzugriff auch nicht jede beliebige Software bieten kann. In dem Bereich lässt sich im passendem Kundenkreis durchaus zum gegenseitigen Vorteil was verdienen.

Z.B.: Wenn die Häufigkeit oder die Zahlen auch normaler Fremdwährungsgeschäfte durch normale Rechnungslegung groß genug sind, dann lohnt plötzlich auch das sehr genaue herausrechnen und kontenrichtige Buchen von "Kosten des Geldtransfers"(Bid/Ask Aufschlag als Betriebsausgabe), "Kosten der Währungsumrechnung"(Bid-Ask Spread als Betriebsausgabe), und "Gewinnen/Verlusten aus Wechselkursen"(Kursdifferenz zw. Beschaffung und Verkauf, also steuerlich relevanter und aufrechenbarer Gewinn&Verlust).

Unsere Kunden bringen mit solch genauer Buchung & Kontrolle zwar regelmäßig ihre transaktionsausführenden Banken und die zuständigen Finanzämter/Steuerprüfer in Bedrängnis, aber es ist nicht einzusehen, warum man per pauschalierter Abrechnung der Umrechnung einen schlechteren Kurs akzeptieren soll, als den welchen die Bank real WorstCase zum Zeitpunkt der Buchung selbst ausgewiesen und genutzt hat.


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:05 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz