United States Patent and Trademark Office
Patent Maintenance
Introduction
The USPTO Patent Maintenance dataset tracks what companies do with the patents they already own. Every US patent has to be renewed three times, at three and a half, seven and a half, and eleven and a half years after it is granted, and each renewal costs money. A company that pays keeps the patent. A company that stops paying lets it lapse into the public domain.
That decision is made deliberately, patent by patent, by people who have read the portfolio. It is one of the few moments where a company puts a price on a piece of its own intellectual property and acts on it, and it becomes public because the patent register is a public record.
The data covers 2,896 US Equities, starting in April 1985, and is delivered weekly. It is created by joining the USPTO maintenance fee event file to the PatentsView disambiguated assignee tables, then resolving each canonical company to its listed ticker.
This dataset depends on the US Equity Security Master dataset because the US Equity Security Master dataset contains information on splits, dividends, and symbol changes.
For more information about the Patent Maintenance dataset, including CLI commands and pricing, see the dataset listing.
About the Provider
The United States Patent and Trademark Office is the agency of the Department of Commerce that grants US patents and registers trademarks. It has recorded every maintenance fee payment and every lapse since the renewal system began in 1981, and publishes them through its Open Data Portal as a public domain bulk product with no restriction on commercial reuse.
Source: United States Patent and Trademark Office (USPTO). QuantConnect processes and normalizes the original USPTO public data. QuantConnect is not affiliated with or endorsed by the USPTO.
Getting Started
The following snippet demonstrates how to request data from the USPTO Patent Maintenance dataset:
ibm = self.add_equity("IBM", Resolution.DAILY).symbol
self._patents = self.add_data(USPTOPatentMaintenance, ibm).symbol var ibm = AddEquity("IBM", Resolution.Daily).Symbol;
_patents = AddData<USPTOPatentMaintenance>(ibm).Symbol;
Requesting Data
To add USPTO Patent Maintenance data to your algorithm, call the AddDataadd_data method. Save a reference to the dataset Symbol so you can access the data later in your algorithm.
class USPTOPatentMaintenanceAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2020, 1, 1)
self.set_end_date(2021, 1, 1)
ibm = self.add_equity("IBM", Resolution.DAILY).symbol
self._patents = self.add_data(USPTOPatentMaintenance, ibm).symbol public class USPTOPatentMaintenanceAlgorithm : QCAlgorithm
{
private Symbol _patents;
public override void Initialize()
{
SetStartDate(2020, 1, 1);
SetEndDate(2021, 1, 1);
var ibm = AddEquity("IBM", Resolution.Daily).Symbol;
_patents = AddData<USPTOPatentMaintenance>(ibm).Symbol;
}
}
Accessing Data
To get the current USPTO Patent Maintenance data, index the current Slice with the dataset Symbol. Slice objects deliver unique events to your algorithm as they happen, but the Slice may not contain data for your dataset at every time step. To avoid issues, check if the Slice contains the data you want before you index it.
USPTO publishes the maintenance fee file every Tuesday, covering events through the preceding Monday. One data point is one publication week: its `time` is the start of the week and it reaches your algorithm on the Tuesday the file is published.
def on_data(self, slice: Slice) -> None:
points = slice.get(USPTOPatentMaintenance)
if self._patents in points:
point = points[self._patents]
self.log(f"{point.symbol} expired {point.patents_expired} patents on {point.time}") public override void OnData(Slice slice)
{
var points = slice.Get<USPTOPatentMaintenance>();
if (points.ContainsKey(_patents))
{
var point = points[_patents];
Log($"{point.Symbol} expired {point.PatentsExpired} patents on {point.Time}");
}
}
Historical Data
To get historical USPTO Patent Maintenance data, call the Historyhistory method with the dataset Symbol. If there is no data in the period you request, the history result is empty.
history = self.history[USPTOPatentMaintenance](self._patents, 100, Resolution.DAILY)
var history = History<USPTOPatentMaintenance>(_patents, 100, Resolution.Daily);
For more information about historical data, see History Requests.
Universe Selection
To select a dynamic universe of US Equities based on USPTO Patent Maintenance data, call the AddUniverseadd_universe method with the USPTOPatentMaintenanceUniverse class and a selection function.
def initialize(self) -> None:
self.universe_settings.resolution = Resolution.DAILY
self.add_universe(USPTOPatentMaintenanceUniverse, self.universe_selection)
def universe_selection(self, data: List[USPTOPatentMaintenanceUniverse]) -> List[Symbol]:
renewing = [d for d in data if d.net_maintenance_change is not None and d.net_maintenance_change > 0]
renewing.sort(key=lambda d: d.net_maintenance_change, reverse=True)
return [d.symbol for d in renewing[:10]] UniverseSettings.Resolution = Resolution.Daily;
AddUniverse<USPTOPatentMaintenanceUniverse>(data =>
{
return data
.OfType<USPTOPatentMaintenanceUniverse>()
.Where(d => d.NetMaintenanceChange.HasValue && d.NetMaintenanceChange.Value > 0)
.OrderByDescending(d => d.NetMaintenanceChange.Value)
.Take(10)
.Select(d => d.Symbol);
});
Remove Subscriptions
To remove your subscription to USPTO Patent Maintenance data, call the RemoveSecurityremove_security method.
self.remove_security(self._patents)
RemoveSecurity(_patents);
If you subscribe to USPTO Patent Maintenance data for assets in a dynamic universe, remove the dataset subscription when the asset leaves your universe. To view a common design pattern, see Track Security Changes.
Example Applications
The USPTO Patent Maintenance dataset lets you trade on how a company manages the intellectual property it already holds, rather than on how much of it the company files. Examples include the following strategies:
- Ranking a universe by renewals against abandonments, and favouring the companies still paying to keep their portfolio alive.
- Detecting a change of regime at a single company, where a business that renewed almost everything starts abandoning patents in volume. That shift usually accompanies a cost programme.
- Treating twelfth-year renewals as a conviction measure, since paying to keep an eleven and a half year old patent alive says it still earns its cost.
- Comparing an abandonment rate against sector peers, to separate a company decision from an industry-wide one.
Classic Algorithm Example
The following example algorithm trades IBM on the rate of change in its patent renewals against its own trailing quarter, buying when abandonment slows and selling when it accelerates.
from AlgorithmImports import *
from QuantConnect.DataSource import *
class USPTOAlgorithm(QCAlgorithm):
'''Example algorithm using USPTO patent activity as a source of alpha. A mature portfolio always
abandons more patents than it renews, so the absolute sign of the net change carries no
information. What does carry information is the rate against the company's own history: when
abandonment slows relative to the trailing quarter, the company is choosing to keep paying for
patents it was previously letting go.'''
def initialize(self) -> None:
self.set_start_date(2020, 1, 1)
self.set_end_date(2021, 1, 1)
self.set_cash(100000)
self._ibm = self.add_equity("IBM", Resolution.DAILY).symbol
# Patent activity is an equity-linked signal on the same security we trade.
self._patents = self.add_data(USPTOPatentMaintenance, self._ibm).symbol
# Maintenance fee events publish weekly, so a quarter of readings is roughly 13 points.
self._net = RollingWindow(13)
def on_data(self, slice: Slice) -> None:
points = slice.get(USPTOPatentMaintenance)
if self._patents not in points:
return
point = points[self._patents]
self.debug(f"{self.time:%Y-%m-%d} expired: {point.patents_expired}, "
f"renewed 12yr: {point.maintenance_paid_twelfth_year}")
if point.net_maintenance_change is None:
return
if self._net.is_ready:
# Compare the newest reading with the trailing quarter it is about to join. Above the
# average means abandonment is slowing; below it means the pruning is accelerating.
average = sum(self._net) / self._net.count
if point.net_maintenance_change > average and not self.portfolio[self._ibm].invested:
self.set_holdings(self._ibm, 1)
elif point.net_maintenance_change < average and self.portfolio[self._ibm].invested:
self.liquidate(self._ibm)
self._net.add(point.net_maintenance_change)
def on_order_event(self, order_event: OrderEvent) -> None:
if order_event.status == OrderStatus.FILLED:
self.debug(f"{self.time} - Filled: {order_event.symbol} {order_event.fill_quantity}") using QuantConnect.Data;
using QuantConnect.Orders;
using QuantConnect.Algorithm;
using QuantConnect.DataSource;
using QuantConnect.Indicators;
/// <summary>
/// Example algorithm using USPTO patent activity as a source of alpha. A mature portfolio
/// always abandons more patents than it renews, so the absolute sign of the net change carries
/// no information. What does carry information is the rate against the company's own history:
/// when abandonment slows relative to the trailing quarter, the company is choosing to keep
/// paying for patents it was previously letting go.
/// </summary>
public class USPTOAlgorithm : QCAlgorithm
{
private Symbol _ibm;
private Symbol _patents;
private RollingWindow<decimal> _net;
/// <summary>
/// Initialise the data and resolution required, as well as the cash and start-end dates.
/// </summary>
public override void Initialize()
{
SetStartDate(2020, 1, 1);
SetEndDate(2021, 1, 1);
SetCash(100000);
_ibm = AddEquity("IBM", Resolution.Daily).Symbol;
// Patent activity is an equity-linked signal on the same security we trade.
_patents = AddData<USPTOPatentMaintenance>(_ibm).Symbol;
// Maintenance fee events publish weekly, so a quarter of readings is roughly 13 points.
_net = new RollingWindow<decimal>(13);
}
/// <summary>
/// OnData event is the primary entry point for your algorithm. Each new data point is here.
/// </summary>
/// <param name="slice">Slice object keyed by symbol containing the data</param>
public override void OnData(Slice slice)
{
var points = slice.Get<USPTOPatentMaintenance>();
if (!points.ContainsKey(_patents))
{
return;
}
var point = points[_patents];
Debug($"{Time:yyyy-MM-dd} expired: {point.PatentsExpired}, renewed 12yr: {point.MaintenancePaidTwelfthYear}");
if (!point.NetMaintenanceChange.HasValue)
{
return;
}
if (_net.IsReady)
{
// Compare the newest reading with the trailing quarter it is about to join. Above
// the average means abandonment is slowing; below it means pruning is accelerating.
var total = 0m;
foreach (var value in _net)
{
total += value;
}
var average = total / _net.Count;
if (point.NetMaintenanceChange.Value > average && !Portfolio[_ibm].Invested)
{
SetHoldings(_ibm, 1);
}
else if (point.NetMaintenanceChange.Value < average && Portfolio[_ibm].Invested)
{
Liquidate(_ibm);
}
}
_net.Add(point.NetMaintenanceChange.Value);
}
/// <summary>
/// Order fill event handler.
/// </summary>
/// <param name="orderEvent">Order event details</param>
public override void OnOrderEvent(OrderEvent orderEvent)
{
if (orderEvent.Status.IsFill())
{
Debug($"{Time} - Filled: {orderEvent.Symbol} {orderEvent.FillQuantity}");
}
}
}
Data Point Attributes
The USPTO Patent Maintenance dataset provides USPTOPatentMaintenance and USPTOPatentMaintenanceUniverse objects.
USPTOPatentMaintenance
USPTOPatentMaintenance objects have the following attributes:
USPTOPatentMaintenanceUniverse
USPTOPatentMaintenanceUniverse objects have the following attributes: