php - SQL:如何使用 SQL 选择查询计算本月至今的 P&L(利润和损失)?

标签 php mysql sql

我需要计算一系列金融交易的每月至今 (MTD) P&L(损益)。假设下面的表格,一个包含交易,另一个包含价格:

交易:

Ticket_number   Asset_code  dt_ticket   Cost
1                  1        2014-12-05  700
2                  1        2015-01-30  750
3                  1        2015-07-15  800

价格:

 Asset_code | Dt_price  | PU
    1       | 2014-12-30| 800
    1       | 2015-06-30| 780
    1       | 2015-07-15| 715
    1       | 2015-07-31| 720

假设所有数量都等于 1,并且我的 Assets 数量超过 (= asset_code [1...n]) 所以,如果我在 7 月 31 日,我会看到类似的内容:

Tkt_num |Asset_code|dt_tkt    |  cost  |Prev. Price  |Closing Price | MTD P&L
  1     | 1        |2014-12-05|  700      | 780         |  720         | -60
  2     | 1        |2015-01-30|  750      | 780         |  720         | -60
  3     | 1        |2015-07-15|  800      | 800         |  720         | -80

公式:MTD = 收盘价(7 月 31 日)– 当日价格 [max(EOM Date, Ticket_date)] 在上表中,第一行和第二行的先前价格应为截至 6 月 30 日的价格,最后一行的价格应为本次交易的成本,因为它发生在上个月收盘后(或在同月)。 我的问题是:我可以使用 SQL Select 查询计算此 MTD 损益吗? 我知道我可以使用 PHP 逐行计算,但我想知道是否可以使用 SQL Select 来完成。

最佳答案

我将把这个问题分解并一次解决它。我建议您存储一个变量来跟踪结束日期,以便将来可以轻松地重新运行此查询:

SET @closing_date := '2015-07-31';

要获取之前的价格,请考虑以下步骤:

  • 如果门票发生在截止日期同月/同年,则之前的价格与门票费用相同。
  • 如果机票发生在上个月,则之前的价格是最近一个月月底的价格。

在您的示例中,您希望获取 2015 年 7 月 1 日之前发生的最新价格。由于您可以拥有多个 asset_code,我建议您获取每个 asset_code 的最新日期,如下所示:

SELECT asset_code, MAX(price_date) AS latestPriceDate
FROM prices
WHERE price_date < DATE_FORMAT(@closing_date, '%Y-%m-01')
GROUP BY asset_code;

要获取这些日期的价格,您需要连接回原始表:

SELECT p.*
FROM prices p
JOIN(
  SELECT asset_code, MAX(price_date) AS latestPriceDate
  FROM prices
  WHERE price_date < DATE_FORMAT(@closing_date, '%Y-%m-01')
  GROUP BY asset_code) tmp ON tmp.asset_code = p.asset_code AND tmp.latestPriceDate = p.price_date;

现在您可以获取该子查询并将其连接到事务表。您将需要一个 CASE 语句来检查该日期是否在成交月/年之内。如果小于该值,您可以选择交易成本。我还调整了连接条件,只拉取小于或等于截止日期的交易:

SELECT t.ticket_number, 
  t.asset_code, 
  t.ticket_date, 
  CASE WHEN MONTH(t.ticket_date) = MONTH(@closing_date) AND YEAR(t.ticket_date) = YEAR(@closing_date) THEN cost ELSE p.pu END AS 'Previous Price'
FROM transactions t
JOIN(
  SELECT p.*
  FROM prices p
  JOIN(
    SELECT asset_code, MAX(price_date) AS latestPriceDate
    FROM prices
    WHERE price_date < DATE_FORMAT(@closing_date, '%Y-%m-01')
    GROUP BY asset_code) tmp ON tmp.asset_code = p.asset_code AND tmp.latestPriceDate = p.price_date
  ) p ON p.asset_code = t.asset_code AND t.ticket_date <= @closing_date;

要获取每个 Assets 代码的收盘价,您必须进行类似的聚合。首先获取小于或等于截止日期的最新日期,然后连接回表以获取值:

SELECT p.asset_code, p.pu
FROM prices p
JOIN(
  SELECT asset_code, MAX(price_date) AS latestPriceDate
  FROM prices
  WHERE price_date <= @closing_date
  GROUP BY asset_code) tmp ON tmp.asset_code = p.asset_code AND tmp.latestPriceDate = p.price_date;

一旦你有了它,你就可以将它应用到你的整体查询中,如下所示:

SELECT t.ticket_number, 
  t.asset_code, 
  t.ticket_date, 
  CASE WHEN MONTH(t.ticket_date) = MONTH(@closing_date) AND YEAR(t.ticket_date) = YEAR(@closing_date) THEN cost ELSE p.pu END AS 'Previous Price',
  cp.pu AS closing_price
FROM transactions t
JOIN(
  SELECT p.*
  FROM prices p
  JOIN(
    SELECT asset_code, MAX(price_date) AS latestPriceDate
    FROM prices
    WHERE price_date < DATE_FORMAT(@closing_date, '%Y-%m-01')
    GROUP BY asset_code) tmp ON tmp.asset_code = p.asset_code AND tmp.latestPriceDate = p.price_date
  ) p ON p.asset_code = t.asset_code AND t.ticket_date <= @closing_date
JOIN(
  SELECT p.asset_code, p.pu
  FROM prices p
  JOIN(
    SELECT asset_code, MAX(price_date) AS latestPriceDate
    FROM prices
    WHERE price_date <= @closing_date
    GROUP BY asset_code) tmp ON tmp.asset_code = p.asset_code AND tmp.latestPriceDate = p.price_date
  ) cp ON cp.asset_code = p.asset_code;

最后要做的就是获取损益列,您可以通过选择 pu - 收盘价 - 上一个价格来完成此操作。不幸的是,您无法在选择中使用列别名,因此您必须再次写出公式:

SELECT t.ticket_number, 
  t.asset_code, 
  t.ticket_date, 
  CASE WHEN MONTH(t.ticket_date) = MONTH(@closing_date) AND YEAR(t.ticket_date) = YEAR(@closing_date) THEN cost ELSE p.pu END AS 'Previous Price',
  cp.pu AS 'Closing Price',
  cp.pu - CASE WHEN MONTH(t.ticket_date) = MONTH(@closing_date) AND YEAR(t.ticket_date) = YEAR(@closing_date) THEN cost ELSE p.pu END AS 'P&L'
FROM transactions t
JOIN(
  SELECT p.*
  FROM prices p
  JOIN(
    SELECT asset_code, MAX(price_date) AS latestPriceDate
    FROM prices
    WHERE price_date < DATE_FORMAT(@closing_date, '%Y-%m-01')
    GROUP BY asset_code) tmp ON tmp.asset_code = p.asset_code AND tmp.latestPriceDate = p.price_date
  ) p ON p.asset_code = t.asset_code AND t.ticket_date <= @closing_date
JOIN(
  SELECT p.asset_code, p.pu
  FROM prices p
  JOIN(
    SELECT asset_code, MAX(price_date) AS latestPriceDate
    FROM prices
    WHERE price_date <= @closing_date
    GROUP BY asset_code) tmp ON tmp.asset_code = p.asset_code AND tmp.latestPriceDate = p.price_date
  ) cp ON cp.asset_code = p.asset_code;

这是一个SQL Fiddle使用您的样本数据的示例。如果您不想使用不必使用的变量,只需将每个位置的 @ending_date 替换为您的日期即可。

关于php - SQL:如何使用 SQL 选择查询计算本月至今的 P&L(利润和损失)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31793405/

相关文章:

c# - 大批量 Entity Framework 更新比我自己批处理慢得多

php - 如何创建可部署的 JavaScript 文件

php - 在每个文件中包含 php 头文件

php - 创建/拆分字符串到标签的最佳方法

mysql - 当表 A 中存在条目 X 时,更新表 Ebay 中的条目,并按原样保留另一列中的条目

mysql - 为什么禁用编译器时我的安装程序 SQL 不运行?

php - 为什么我的 sql 语句可以在命令行上运行,但不能在 PHP 上的 mysqli_query 上运行?

sql - 将来自不同表的列的值插入到列中

javascript - 错误 laravel 项目与 Blade SourceMap

sql - 如何在 SQL Server Management Studio 中设置 1 到 0...1 的关系