diff --git a/geomagio/DerivedTimeseriesFactory.py b/geomagio/DerivedTimeseriesFactory.py
index 535b8e875d5997e559d0734dfb1232518eba99e9..648a33f95fec4553561c88dbd014ace2aada35aa 100644
--- a/geomagio/DerivedTimeseriesFactory.py
+++ b/geomagio/DerivedTimeseriesFactory.py
@@ -1,6 +1,6 @@
 from typing import List, Optional
 
-from obspy import Stream, UTCDateTime
+from obspy import Stream, Trace, UTCDateTime
 
 from .algorithm import Algorithm, DeltaFAlgorithm, XYZAlgorithm
 from .TimeseriesFactory import TimeseriesFactory, TimeseriesUtility
@@ -54,44 +54,26 @@ class DerivedTimeseriesFactory(TimeseriesFactory):
             )
         missing = get_missing(timeseries, channels)
         if missing and add_empty_channels:
-            timeseries += self._get_empty_channels(
-                starttime=starttime,
-                endtime=endtime,
-                observatory=observatory,
-                channels=channels,
-                data_type=type,
-                interval=interval,
-                location=timeseries[0].stats.location,
-            )
+            for channel in missing:
+                timeseries += self._get_empty_trace(
+                    starttime=starttime,
+                    endtime=endtime,
+                    observatory=observatory,
+                    channel=channel,
+                    data_type=type,
+                    interval=interval,
+                )
         # file-based factories return all channels found in file
         timeseries = Stream([t for t in timeseries if t.stats.channel in channels])
-        return timeseries
-
-    def _get_empty_channels(
-        self,
-        starttime: UTCDateTime,
-        endtime: UTCDateTime,
-        observatory: str,
-        channels: List[str],
-        data_type: str,
-        interval: str,
-        location: str,
-    ) -> Stream:
-        """create empty channels"""
-        output_stream = Stream()
         for channel in channels:
-            output_stream += TimeseriesUtility.create_empty_trace(
-                starttime=starttime,
-                endtime=endtime,
+            self._set_metadata(
+                stream=timeseries.select(channel=channel),
                 observatory=observatory,
                 channel=channel,
-                type=data_type,
+                type=type,
                 interval=interval,
-                network="NT",
-                station=observatory,
-                location=location,
             )
-        return output_stream
+        return timeseries
 
     def _get_derived_channels(
         self,
@@ -178,6 +160,46 @@ class DerivedTimeseriesFactory(TimeseriesFactory):
                 )
         return Stream()
 
+    def _get_empty_trace(
+        self,
+        starttime: UTCDateTime,
+        endtime: UTCDateTime,
+        observatory: str,
+        channel: str,
+        data_type: str,
+        interval: str,
+        network: str = "NT",
+        location: str = "",
+    ) -> Trace:
+        """creates empty trace"""
+        return self.factory._get_empty_trace(
+            starttime,
+            endtime,
+            observatory,
+            channel,
+            data_type,
+            interval,
+            network=network,
+            location=location,
+        )
+
+    def _set_metadata(
+        self, stream: Stream, observatory: str, channel: str, type: str, interval: str
+    ):
+        """set metadata for a given stream/channel
+        Parameters
+        ----------
+        observatory
+            observatory code
+        channel
+            edge channel code {MVH, MVE, MVD, ...}
+        type
+            data type {definitive, quasi-definitive, variation}
+        interval
+            interval length {minute, second}
+        """
+        return self.factory._set_metadata(stream, observatory, channel, type, interval)
+
 
 def get_missing(input: Stream, desired: List[str]) -> List[str]:
     """Return missing channels from input"""
diff --git a/geomagio/TimeseriesFactory.py b/geomagio/TimeseriesFactory.py
index 064b868d6618b2d8cfedf2a8b12df342169cfe88..3d92f9c58f3b0ed26f24c4c97801747bf98b8d90 100644
--- a/geomagio/TimeseriesFactory.py
+++ b/geomagio/TimeseriesFactory.py
@@ -297,6 +297,31 @@ class TimeseriesFactory(object):
         """
         raise NotImplementedError('"write_file" not implemented')
 
+    def _get_empty_trace(
+        self,
+        starttime: obspy.core.UTCDateTime,
+        endtime: obspy.core.UTCDateTime,
+        observatory: str,
+        channel: str,
+        data_type: str,
+        interval: str,
+        network: str = "NT",
+        location: str = "",
+    ) -> obspy.core.Trace:
+        """creates empty trace"""
+        trace = TimeseriesUtility.create_empty_trace(
+            starttime=starttime,
+            endtime=endtime,
+            observatory=observatory,
+            channel=channel,
+            type=data_type,
+            interval=interval,
+            station=observatory,
+            network=network,
+            location=location,
+        )
+        return trace
+
     def _get_file_from_url(self, url):
         """Get a file for writing.
 
@@ -505,3 +530,25 @@ class TimeseriesFactory(object):
         else:
             raise TimeseriesFactoryException('Unsupported type "%s"' % type)
         return type_name
+
+    def _set_metadata(
+        self,
+        stream: obspy.core.Stream,
+        observatory: str,
+        channel: str,
+        type: str,
+        interval: str,
+    ):
+        """set metadata for a given stream/channel
+        Parameters
+        ----------
+        observatory
+            observatory code
+        channel
+            edge channel code {MVH, MVE, MVD, ...}
+        type
+            data type {definitive, quasi-definitive, variation}
+        interval
+            interval length {minute, second}
+        """
+        pass
diff --git a/geomagio/edge/EdgeFactory.py b/geomagio/edge/EdgeFactory.py
index 5dbde248fe229a42b9b68072c97337bdeb62ba94..c25955fe141ab4114ed5c28b4a95d32ff1cfe2ed 100644
--- a/geomagio/edge/EdgeFactory.py
+++ b/geomagio/edge/EdgeFactory.py
@@ -347,16 +347,15 @@ class EdgeFactory(TimeseriesFactory):
             trace.data = trace.data.astype("i4")
         data.merge()
         if data.count() == 0 and add_empty_channels:
-            data += TimeseriesUtility.create_empty_trace(
-                starttime,
-                endtime,
-                observatory,
-                channel,
-                type,
-                interval,
-                sncl.network,
-                sncl.station,
-                sncl.location,
+            data += self._get_empty_trace(
+                starttime=starttime,
+                endtime=endtime,
+                observatory=observatory,
+                channel=channel,
+                data_type=type,
+                interval=interval,
+                network=sncl.network,
+                location=sncl.location,
             )
         self._set_metadata(data, observatory, channel, type, interval)
         return data
@@ -469,20 +468,26 @@ class EdgeFactory(TimeseriesFactory):
             ric.forceout()
         ric.close()
 
-    def _set_metadata(self, stream, observatory, channel, type, interval):
+    def _set_metadata(
+        self,
+        stream: obspy.core.Stream,
+        observatory: str,
+        channel: str,
+        type: str,
+        interval: str,
+    ):
         """set metadata for a given stream/channel
         Parameters
         ----------
-        observatory : str
+        observatory
             observatory code
-        channel : str
+        channel
             edge channel code {MVH, MVE, MVD, ...}
-        type : str
+        type
             data type {definitive, quasi-definitive, variation}
-        interval : str
+        interval
             interval length {minute, second}
         """
-
         for trace in stream:
             self.observatoryMetadata.set_metadata(
                 trace.stats, observatory, channel, type, interval
diff --git a/geomagio/edge/MiniSeedFactory.py b/geomagio/edge/MiniSeedFactory.py
index d708b30f47b7c21907b415b6e817c0e2b15f5024..94103caee23b037e93eb6d0dfc967a92e8951721 100644
--- a/geomagio/edge/MiniSeedFactory.py
+++ b/geomagio/edge/MiniSeedFactory.py
@@ -353,16 +353,15 @@ class MiniSeedFactory(TimeseriesFactory):
         )
         data.merge()
         if data.count() == 0 and add_empty_channels:
-            data += TimeseriesUtility.create_empty_trace(
-                starttime,
-                endtime,
-                observatory,
-                channel,
-                type,
-                interval,
-                sncl.network,
-                sncl.station,
-                sncl.location,
+            data += self._get_empty_trace(
+                starttime=starttime,
+                endtime=endtime,
+                observatory=observatory,
+                channel=channel,
+                data_type=type,
+                interval=interval,
+                network=sncl.network,
+                location=sncl.location,
             )
         self._set_metadata(data, observatory, channel, type, interval)
         return data
@@ -501,20 +500,26 @@ class MiniSeedFactory(TimeseriesFactory):
         # finally, send to edge
         self.write_client.send(to_write)
 
-    def _set_metadata(self, stream, observatory, channel, type, interval):
+    def _set_metadata(
+        self,
+        stream: obspy.core.Stream,
+        observatory: str,
+        channel: str,
+        type: str,
+        interval: str,
+    ):
         """set metadata for a given stream/channel
         Parameters
         ----------
-        observatory : str
+        observatory
             observatory code
-        channel : str
+        channel
             edge channel code {MVH, MVE, MVD, ...}
-        type : str
+        type
             data type {definitive, quasi-definitive, variation}
-        interval : str
-            interval length {'day', 'hour', 'minute', 'second', 'tenhertz'}
+        interval
+            interval length {minute, second}
         """
-
         for trace in stream:
             self.observatoryMetadata.set_metadata(
                 trace.stats, observatory, channel, type, interval