I believe this should handle it for you. Including monthly and daily. It also catches if you have defined a different rewrite slug than your custom post type slug.
/* This function handles setting up Date archive rewrite rules for
* ANY custom post type - You pass the CPT, and it will use the
* re-written slug if applicable.
*/
function eh_generate_date_archives( $cpt ) {
global $wp_rewrite;
$rules = array();
$post_type = get_post_type_object( $cpt );
$slug_archive = $post_type->has_archive;
if ( $slug_archive === false ) return $rules;
if ( $slug_archive === true ) {
$slug_archive = $post_type->rewrite['slug'] ? $post_type->rewrite['slug'] : $post_type->name;
}
$dates = array(
array(
'rule' => "([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})",
'vars' => array( 'year', 'monthnum', 'day' )
),
array(
'rule' => "([0-9]{4})/([0-9]{1,2})",
'vars' => array( 'year', 'monthnum' )
),
array(
'rule' => "([0-9]{4})",
'vars' => array( 'year' )
)
);
foreach ($dates as $data) {
$query = 'index.php?post_type=".$cpt;
$rule = $slug_archive."https://wordpress.stackexchange.com/".$data['rule'];
$i = 1;
foreach ( $data['vars'] as $var ) {
$query.= '&'.$var.'='.$wp_rewrite->preg_index($i);
$i++;
}
$rules[$rule."/?$"] = $query;
$rules[$rule."/feed/(feed|rdf|rss|rss2|atom)/?$"] = $query."&feed=".$wp_rewrite->preg_index($i);
$rules[$rule."/(feed|rdf|rss|rss2|atom)/?$"] = $query."&feed=".$wp_rewrite->preg_index($i);
$rules[$rule."/page/([0-9]{1,})/?$"] = $query."&paged=".$wp_rewrite->preg_index($i);
}
return $rules;
}