我需要计算一系列金融交易的每月至今 (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/