Recently I've been collecting lots of personal data for statistics, including my long-time stock holdings. The data is stored with InfluxDB, and the frontend uses Grafana for permission control and page display. So I want to share some InfluxDB usage and common query statements. The content shared here constitutes no investment advice.
Recently I’ve been collecting lots of data around me for statistics, and reported my long-time stock holdings too. The data is stored using InfluxDB,
and the frontend also uses Grafana for permission control and page display. So I want to share some InfluxDB usage and common query statements.
The content shared in this article constitutes no investment advice.
Currently my Sinolink account holds about 170k, with a return rate around 10%. Certainly no comparison with the experts wielding millions and doubled returns.
I added a section to the blog for displaying Grafana charts — view it directly at Stocks
Data Sources
I’ve been playing in A-shares for several years; last year I discovered the QMT tool, which can interact with your own securities account using Python:
Enabling it is fairly simple — Sinolink’s threshold is 300k; just ask your account manager to enable it.
Last year I wrote my own tool for scheduled investments; recently while collecting data around me, I collected holdings and returns as well.
There’s actually quite a lot of other data, but I feel my holdings are most suitable for charting and sharing — readers may also be more interested.
Below is the data-writing function. Writing this blog I noticed my code is a bit casual — as long as it runs for now.
# report the current account's overall data p = Point("balance") \ .tag('account', stock_account) \ .field("total_asset", b.total_asset) \ .field("market_value", b.market_value) \ .time(dt.datetime.utcnow(), WritePrecision.S) with client.write_api(write_options=SYNCHRONOUS) as write_api: write_api.write(bucket=bucket, record=p)
# report current holdings data for s in stocks: p = Point("position") \ .tag('account', stock_account) \ .tag('stock_code', s.code) \ .tag('stock_name', s.name) \ .tag('market', s.market) \ # current holding volume .field('volume', s.volume) \ # current per-share price .field('volume_price', s.volume_price) \ # current total holding price .field('market_price', s.market_price) \ # per-share cost .field('open_price', s.open_price) \ .field('position_ratio', round(s.market_price / b.market_value, 3)) \ .time(dt.datetime.utcnow(), WritePrecision.S)
with client.write_api(write_options=SYNCHRONOUS) as write_api: write_api.write(bucket=bucket, record=p)
Click the image to enlarge and view this example:
Some InfluxDB Query Statements Shared
While making charts I had the chance to query with InfluxDB, so I organized some statements I used.
It can build custom functions — truly powerful; I hope sharing brings readers some inspiration.
from(bucket: "Stock") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r["_measurement"] == "balance") |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value") |> map(fn: (r) => ({ _time: r._time, // the map function computes holdings amount / total amount — e.g. I have 170k, 160k in stocks, so the position is 16/17=0.94 _value: r.market_value / r.total_asset, })) // drop some columns we don't want |> drop(columns: ["_field", "account", "stock_code", "market", "total_asset", "market_value"]) |> set(key: "_measurement", value: "仓位")
InfluxDB source data:
In Grafana, you can make:
Computing My Actual Return Rate
This chart actually includes the amount I’ve invested — data that theoretically changes over time. For example, if I invested 15k this month,
the actual return rate should be computed with the updated holdings data.
The scheme I can think of: add a function to augment a field whose meaning indicates the amount invested so far:
// the getBase function here looks up its own time period from the table and returns the corresponding invested amount. // For example, I invested 15k on July 15, so data after July 15 becomes 160k. // When I continue investing on some day in August, another row can be added to mark it. getBase = (query) => { tdata = ( inputVal |> array.filter(fn: (x) => { return query._time >= x.date }) ) return tdata[0].val }
Someone will surely ask why I share holdings data for free. What I want to say: even after seeing my position distribution, you most likely won’t
buy these stocks of mine. Here’s why:
Theory is ignored entirely — just as with the Kelly formula already existing, people still stay fully invested forever, forever teary-eyed
My 10% return is beneath their notice
The many existing paid consultation groups — lots of stock investors trust group members’ judgments or inside information more
Still, let me emphasize once more: this article only shares technology and constitutes no investment advice.