Your problem of historical data is in fact quite common: business related data is often related to time.
One way to handle this is to take snapshots. It's the solution proposed by @CandleOrange. But it seems also to be your assumption: in your sizing you expect to have a different occurence of each combination for each month (completely equivalent to the snapshot approach). But you think to keep it in the database.
As an example, one leading business software package manages sales figures in this way: for every month, it sums up the sales of that month, for each combination of customer category, product group, and region. In that way it can produce sales figures according to the criterias and for any given time period, without having to read the millions and millions of underlying transactions.
In fact, there is no difference here between data and historical data. From a business point of view, the month are integral part of the data that is managed. And it is frequent to compare sales figures of one yer to the previous year, but also of one month to the previous month, or the first quarter of this year with the first quarter of the 2 last years.
Date of validity approach (time dependent versioning)
But the snapshot approach does not always suits the need. Very often data can be time dependent. For example salesman X can be responsible for a region for March 1st to August 18th and for another region from August 19th to today. So what region keep for him for August ? None is more exact than the other.
For graph relationships it's event worse. Because there are time dependent aspects in every data and in every relation. Let's take an example: Manager A is responsible of department X from January 20th to September 20th. Employee B is assigned to department X from Last year to January 22, and employee C is assigned to that department since he joined the company and is sill here. So who does the Manager A manage ? It completely depends on the date.
Now, what's the size of the database if you compare time dependent approach and snapshot ? For the snapshot you have to clone every relevant value every month. So for the 3 persons and 1 department of our example, you'll have 36 records over a year. And you still are not able to hold the full truth. For the time dependent approach, you have your raw data 3 records, and you only have additional data when a change occurs, so 6 records overall.
Time dependent versioning of data represents hence very accurately the real world, and space savy. But it makes design of queries a lot more complex.
To avoid a complete query nightmare, you can work by default with the current version of each object (e.g. end validity is empty), and create time limited clones (start and end validity date set) every time you change important data. You would then access these clones and their validity dates only for the couple of time-dependent queries that require it.
Support by database systems
For most database systems, you have to take care yourself for the time dependent aspects. Time dependency of data elements must be identified from the start and it's hard case-by-case design work.
But as already said: it's a common challenge. So fortunately there is some support around: