diff --git a/opentech/apply/funds/admin.py b/opentech/apply/funds/admin.py
index fe493151f18df7ef9b75179009eb93744298add3..d9596a79b35ab1eb3c6cf91f969339a07a8ccde6 100644
--- a/opentech/apply/funds/admin.py
+++ b/opentech/apply/funds/admin.py
@@ -1,9 +1,43 @@
+from django.urls import reverse
+
 from wagtail.contrib.modeladmin.options import ModelAdmin, ModelAdminGroup
+from wagtail.contrib.modeladmin.helpers import PageButtonHelper
 
-from .models import ApplicationForm, FundType
+from .models import ApplicationForm, FundType, Round
 from opentech.apply.categories.admin import CategoryAdmin
 
 
+class ButtonsWithPreview(PageButtonHelper):
+    def preview_button(self, obj, classnames_add, classnames_exclude):
+        classnames = self.copy_button_classnames + classnames_add
+        cn = self.finalise_classname(classnames, classnames_exclude)
+        return {
+            'url': reverse('wagtailadmin_pages:view_draft', args=(obj.id,)),
+            'label': 'Preview',
+            'classname': cn,
+            'title': 'Preview this %s' % self.verbose_name,
+        }
+
+    def get_buttons_for_obj(self, obj, exclude=list(), classnames_add=list(),
+                            classnames_exclude=list()):
+        btns = super().get_buttons_for_obj(obj, exclude, classnames_add, classnames_exclude)
+
+        # Put preview before delete
+        btns.insert(-1, self.preview_button(obj, classnames_add, classnames_exclude))
+
+        return btns
+
+
+class RoundAdmin(ModelAdmin):
+    model = Round
+    menu_icon = 'doc-empty'
+    list_display = ('title', 'fund', 'start_date', 'end_date')
+    button_helper_class = ButtonsWithPreview
+
+    def fund(self, obj):
+        return obj.get_parent()
+
+
 class FundAdmin(ModelAdmin):
     model = FundType
     menu_icon = 'doc-empty'
@@ -17,4 +51,4 @@ class ApplicationFormAdmin(ModelAdmin):
 class ApplyAdminGroup(ModelAdminGroup):
     menu_label = 'Apply'
     menu_icon = 'folder-open-inverse'
-    items = (FundAdmin, ApplicationFormAdmin, CategoryAdmin)
+    items = (RoundAdmin, FundAdmin, ApplicationFormAdmin, CategoryAdmin)
diff --git a/opentech/apply/funds/migrations/0005_round.py b/opentech/apply/funds/migrations/0005_round.py
new file mode 100644
index 0000000000000000000000000000000000000000..e839c88745c36b286bf86a7ab58809fe350d5fdc
--- /dev/null
+++ b/opentech/apply/funds/migrations/0005_round.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.8 on 2018-01-18 15:59
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('wagtailcore', '0040_page_draft_title'),
+        ('funds', '0004_categoryblock_add_required_option'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Round',
+            fields=[
+                ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=('wagtailcore.page',),
+        ),
+    ]
diff --git a/opentech/apply/funds/migrations/0006_add_dates_to_round.py b/opentech/apply/funds/migrations/0006_add_dates_to_round.py
new file mode 100644
index 0000000000000000000000000000000000000000..747ef134788e1689cc12dc9d1e47105361b116c4
--- /dev/null
+++ b/opentech/apply/funds/migrations/0006_add_dates_to_round.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.8 on 2018-01-18 16:24
+from __future__ import unicode_literals
+
+import datetime
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('funds', '0005_round'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='round',
+            name='end_date',
+            field=models.DateField(blank=True, default=datetime.date.today),
+        ),
+        migrations.AddField(
+            model_name='round',
+            name='start_date',
+            field=models.DateField(blank=True, default=datetime.date.today),
+        ),
+    ]
diff --git a/opentech/apply/funds/migrations/0007_update_date_fields.py b/opentech/apply/funds/migrations/0007_update_date_fields.py
new file mode 100644
index 0000000000000000000000000000000000000000..7b6b2829535b0139d491a0be0589e4acad48430e
--- /dev/null
+++ b/opentech/apply/funds/migrations/0007_update_date_fields.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.8 on 2018-01-19 11:30
+from __future__ import unicode_literals
+
+import datetime
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('funds', '0006_add_dates_to_round'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='round',
+            name='end_date',
+            field=models.DateField(blank=True, default=datetime.date.today, help_text='When no end date is provided the round will remain open indefinitely.', null=True),
+        ),
+        migrations.AlterField(
+            model_name='round',
+            name='start_date',
+            field=models.DateField(default=datetime.date.today),
+        ),
+    ]
diff --git a/opentech/apply/funds/models.py b/opentech/apply/funds/models.py
index 6752f5165788346836a0e80d4d03a8d67ded24d7..9f575ea6a3e22748e9fd76da3268daa293cbf0c0 100644
--- a/opentech/apply/funds/models.py
+++ b/opentech/apply/funds/models.py
@@ -1,8 +1,18 @@
+from datetime import date
+
+from django.core.exceptions import ValidationError
 from django.db import models
+from django.db.models import Q
+from django.http import Http404
+from django.urls import reverse
+from django.utils.text import mark_safe
+
 from modelcluster.fields import ParentalKey
 from wagtail.wagtailadmin.edit_handlers import (
     FieldPanel,
     InlinePanel,
+    FieldRowPanel,
+    MultiFieldPanel,
     StreamFieldPanel,
 )
 from wagtail.wagtailcore.fields import StreamField
@@ -21,11 +31,16 @@ WORKFLOW_CLASS = {
 }
 
 
+def admin_url(page):
+    return reverse('wagtailadmin_pages:edit', args=(page.id,))
+
+
 class FundType(AbstractStreamForm):
     parent_page_types = ['apply_home.ApplyHomePage']
-    subpage_types = []  # type: ignore
+    subpage_types = ['funds.Round']
 
     base_form_class = WorkflowFormAdminForm
+
     WORKFLOWS = {
         'single': SingleStage.name,
         'double': DoubleStage.name,
@@ -41,11 +56,30 @@ class FundType(AbstractStreamForm):
     def workflow_class(self):
         return WORKFLOW_CLASS[self.get_workflow_display()]
 
+    @property
+    def open_round(self):
+        rounds = Round.objects.child_of(self).live().public().specific()
+        return rounds.filter(
+            Q(start_date__lte=date.today()) &
+            Q(Q(end_date__isnull=True) | Q(end_date__gte=date.today()))
+        ).first()
+
+    def next_deadline(self):
+        return self.open_round.end_date
+
     content_panels = AbstractStreamForm.content_panels + [
         FieldPanel('workflow'),
         InlinePanel('forms', label="Forms"),
     ]
 
+    def serve(self, request):
+        if hasattr(request, 'is_preview'):
+            return super().serve(request)
+
+        # delegate to the open_round to use the latest form instances
+        request.show_page = True
+        return self.open_round.serve(request)
+
 
 class FundForm(Orderable):
     form = models.ForeignKey('ApplicationForm')
@@ -67,3 +101,84 @@ class ApplicationForm(models.Model):
 
     def __str__(self):
         return self.name
+
+
+class Round(AbstractStreamForm):
+    parent_page_types = ['funds.FundType']
+    subpage_types = []  # type: ignore
+
+    start_date = models.DateField(default=date.today)
+    end_date = models.DateField(
+        blank=True,
+        null=True,
+        default=date.today,
+        help_text='When no end date is provided the round will remain open indefinitely.'
+    )
+
+    content_panels = AbstractStreamForm.content_panels + [
+        MultiFieldPanel([
+            FieldRowPanel([
+                FieldPanel('start_date'),
+                FieldPanel('end_date'),
+            ]),
+        ], heading="Dates")
+    ]
+
+    def get_defined_fields(self):
+        # Only return the first form, will need updating for when working with 2 stage WF
+        return self.get_parent().specific.forms.all()[0].fields
+
+    def clean(self):
+        super().clean()
+
+        if self.end_date and self.start_date > self.end_date:
+            raise ValidationError({
+                'end_date': 'End date must come after the start date',
+            })
+
+        if self.end_date:
+            conflict_query = (
+                Q(start_date__range=[self.start_date, self.end_date]) |
+                Q(end_date__range=[self.start_date, self.end_date]) |
+                Q(start_date__lte=self.start_date, end_date__gte=self.end_date)
+            )
+        else:
+            conflict_query = (
+                Q(start_date__lte=self.start_date, end_date__isnull=True) |
+                Q(end_date__gte=self.start_date)
+            )
+
+        if hasattr(self, 'parent_page'):
+            # Check if the create hook has added the parent page, we aren't an object yet.
+            # Ensures we can access related objects during the clean phase instead of save.
+            base_query = Round.objects.child_of(self.parent_page)
+        else:
+            # don't need parent page, we are an actual object now.
+            base_query = Round.objects.sibling_of(self)
+
+        conflicting_rounds = base_query.filter(
+            conflict_query
+        ).exclude(id=self.id)
+
+        if conflicting_rounds.exists():
+            error_message = mark_safe('Overlaps with the following rounds:<br> {}'.format(
+                '<br>'.join([
+                    f'<a href="{admin_url(round)}">{round.title}</a>: {round.start_date} - {round.end_date}'
+                    for round in conflicting_rounds]
+                )
+            ))
+            error = {
+                'start_date': error_message,
+            }
+            if self.end_date:
+                error['end_date'] = error_message
+
+            raise ValidationError(error)
+
+    def serve(self, request):
+        if hasattr(request, 'is_preview') or hasattr(request, 'show_page'):
+            return super().serve(request)
+
+        # We hide the round as only the open round is used which is displayed through the
+        # fund page
+        raise Http404()
diff --git a/opentech/apply/funds/templates/funds/fund_type.html b/opentech/apply/funds/templates/funds/fund_type.html
index 8b61a7da8ef66e6687e719402ce64fadb51f8593..4d3a7bbf85264de9a0cfb14f79cfa5970c8071e1 100644
--- a/opentech/apply/funds/templates/funds/fund_type.html
+++ b/opentech/apply/funds/templates/funds/fund_type.html
@@ -2,9 +2,9 @@
 {% load wagtailcore_tags %}
 
 {% block content %}
-    <h1>{{ page.title }}</h1>
+    <h1>{% block title %}{{ page.title }}{% endblock %}</h1>
 
-    <form action="{% pageurl page %}" method="POST">
+    <form action="" method="POST">
         {% csrf_token %}
 
         {% if form.errors or form.non_field_errors %}
diff --git a/opentech/apply/funds/templates/funds/round.html b/opentech/apply/funds/templates/funds/round.html
new file mode 100644
index 0000000000000000000000000000000000000000..db8010fbcb27e11a35626b7994e6314024ffcdce
--- /dev/null
+++ b/opentech/apply/funds/templates/funds/round.html
@@ -0,0 +1,3 @@
+{% extends "funds/fund_type.html" %}
+
+{% block title %}{{ page.get_parent.title }}{% endblock %}
diff --git a/opentech/apply/funds/templates/funds/round_landing.html b/opentech/apply/funds/templates/funds/round_landing.html
new file mode 100644
index 0000000000000000000000000000000000000000..f4f58593182e3d30195aaece1dbceafd2ba7f6e0
--- /dev/null
+++ b/opentech/apply/funds/templates/funds/round_landing.html
@@ -0,0 +1,3 @@
+{% extends "funds/fund_type_landing.html" %}
+
+{% block page_title %}{{ page.get_parent.title }}{% endblock %}
diff --git a/opentech/apply/funds/tests/factories.py b/opentech/apply/funds/tests/factories.py
index 59ce824cd9c725ae836c50b3fc64927ead04a381..aa37d55a8e2ad6bfe00e33916aa381eedaac16d4 100644
--- a/opentech/apply/funds/tests/factories.py
+++ b/opentech/apply/funds/tests/factories.py
@@ -1,8 +1,10 @@
+import datetime
+
 from django.forms import Form
 import factory
 import wagtail_factories
 
-from opentech.apply.funds.models import ApplicationForm, FundType, FundForm
+from opentech.apply.funds.models import ApplicationForm, FundType, FundForm, Round
 from opentech.apply.funds.workflow import Action, Phase, Stage, Workflow
 
 
@@ -135,3 +137,12 @@ class ApplicationFormFactory(factory.DjangoModelFactory):
         model = ApplicationForm
 
     name = factory.Faker('word')
+
+
+class RoundFactory(wagtail_factories.PageFactory):
+    class Meta:
+        model = Round
+
+    title = factory.Sequence('Round {}'.format)
+    start_date = factory.LazyFunction(datetime.date.today)
+    end_date = factory.LazyFunction(lambda: datetime.date.today() + datetime.timedelta(days=7))
diff --git a/opentech/apply/funds/tests/test_models.py b/opentech/apply/funds/tests/test_models.py
index a1ba5de9ec4cb22c95db5da41d80ac8a89a8306a..431676d67ef2af9934ae104923530950c794edf3 100644
--- a/opentech/apply/funds/tests/test_models.py
+++ b/opentech/apply/funds/tests/test_models.py
@@ -1,8 +1,15 @@
+from datetime import date, timedelta
+
+from django.core.exceptions import ValidationError
 from django.test import TestCase
 
 from opentech.apply.funds.workflow import SingleStage
 
-from .factories import FundTypeFactory
+from .factories import FundTypeFactory, RoundFactory
+
+
+def days_from_today(days):
+    return date.today() + timedelta(days=days)
 
 
 class TestFundModel(TestCase):
@@ -10,3 +17,108 @@ class TestFundModel(TestCase):
         fund = FundTypeFactory(parent=None)
         self.assertEqual(fund.workflow, 'single')
         self.assertEqual(fund.workflow_class, SingleStage)
+
+    def test_no_open_rounds(self):
+        fund = FundTypeFactory(parent=None)
+        self.assertIsNone(fund.open_round)
+
+    def test_open_ended_round(self):
+        fund = FundTypeFactory(parent=None)
+        open_round = RoundFactory(end_date=None, parent=fund)
+        self.assertEqual(fund.open_round, open_round)
+
+    def test_normal_round(self):
+        fund = FundTypeFactory(parent=None)
+        open_round = RoundFactory(parent=fund)
+        self.assertEqual(fund.open_round, open_round)
+
+    def test_closed_round(self):
+        fund = FundTypeFactory(parent=None)
+        yesterday = days_from_today(-1)
+        last_week = days_from_today(-7)
+        RoundFactory(start_date=last_week, end_date=yesterday, parent=fund)
+        self.assertIsNone(fund.open_round)
+
+    def test_round_not_open(self):
+        fund = FundTypeFactory(parent=None)
+        tomorrow = days_from_today(1)
+        RoundFactory(start_date=tomorrow, parent=fund)
+        self.assertIsNone(fund.open_round)
+
+    def test_multiple_open_rounds(self):
+        fund = FundTypeFactory(parent=None)
+        open_round = RoundFactory(parent=fund)
+        next_round_start = open_round.end_date + timedelta(days=1)
+        RoundFactory(start_date=next_round_start, end_date=None, parent=fund)
+        self.assertEqual(fund.open_round, open_round)
+
+
+class TestRoundModel(TestCase):
+    def setUp(self):
+        self.fund = FundTypeFactory(parent=None)
+
+    def make_round(self, **kwargs):
+        data = {'parent': self.fund}
+        data.update(kwargs)
+        return RoundFactory(**data)
+
+    def test_normal_start_end_doesnt_error(self):
+        self.make_round()
+
+    def test_end_before_start(self):
+        yesterday = date.today() - timedelta(days=1)
+        with self.assertRaises(ValidationError):
+            self.make_round(end_date=yesterday)
+
+    def test_end_overlaps(self):
+        existing_round = self.make_round()
+        overlapping_end = existing_round.end_date - timedelta(1)
+        start = existing_round.start_date - timedelta(1)
+        with self.assertRaises(ValidationError):
+            self.make_round(start_date=start, end_date=overlapping_end)
+
+    def test_start_overlaps(self):
+        existing_round = self.make_round()
+        overlapping_start = existing_round.start_date + timedelta(1)
+        end = existing_round.end_date + timedelta(1)
+        with self.assertRaises(ValidationError):
+            self.make_round(start_date=overlapping_start, end_date=end)
+
+    def test_inside_overlaps(self):
+        existing_round = self.make_round()
+        overlapping_start = existing_round.start_date + timedelta(1)
+        overlapping_end = existing_round.end_date - timedelta(1)
+        with self.assertRaises(ValidationError):
+            self.make_round(start_date=overlapping_start, end_date=overlapping_end)
+
+    def test_other_fund_not_impacting(self):
+        self.make_round()
+        new_fund = FundTypeFactory(parent=None)
+        # Will share the same start and end dates
+        self.make_round(parent=new_fund)
+
+    def test_can_create_without_end_date(self):
+        self.make_round(end_date=None)
+
+    def test_can_not_create_with_other_open_end_date(self):
+        existing_round = self.make_round(end_date=None)
+        start = existing_round.start_date + timedelta(1)
+        with self.assertRaises(ValidationError):
+            self.make_round(start_date=start, end_date=None)
+
+    def test_can_not_overlap_with_normal_round(self):
+        existing_round = self.make_round()
+        overlapping_start = existing_round.end_date - timedelta(1)
+        with self.assertRaises(ValidationError):
+            self.make_round(start_date=overlapping_start, end_date=None)
+
+    def test_can_not_overlap_clean(self):
+        existing_round = self.make_round()
+        overlapping_start = existing_round.end_date - timedelta(1)
+        new_round = RoundFactory.build(start_date=overlapping_start, end_date=None)
+
+        # we add on the parent page which gets included from a pre_create_hook
+        new_round.parent_page = self.fund
+
+        with self.assertRaises(ValidationError):
+            new_round.clean()
diff --git a/opentech/apply/funds/wagtail_hooks.py b/opentech/apply/funds/wagtail_hooks.py
index d9ad3c978b7ba35f8d762f27cde028290befa828..178e17a6694c669f0b8b181bcbc7801cbcf7c7d7 100644
--- a/opentech/apply/funds/wagtail_hooks.py
+++ b/opentech/apply/funds/wagtail_hooks.py
@@ -1,6 +1,15 @@
+from wagtail.wagtailcore import hooks
 from wagtail.contrib.modeladmin.options import modeladmin_register
 
 from .admin import ApplyAdminGroup
+from .models import Round
 
 
 modeladmin_register(ApplyAdminGroup)
+
+
+@hooks.register('before_create_page')
+def before_create_page(request, parent_page, page_class):
+    if page_class == Round:
+        page_class.parent_page = parent_page
+    return page_class
diff --git a/opentech/public/funds/models.py b/opentech/public/funds/models.py
index 0854ec5db9f1ebbf5937f77a0979b18bbb498af5..91d6c118aef8a7cb9bc372fcc84ed958503d24f3 100644
--- a/opentech/public/funds/models.py
+++ b/opentech/public/funds/models.py
@@ -47,6 +47,14 @@ class FundPage(BasePage):
         InlinePanel('related_pages', label="Related pages"),
     ]
 
+    @property
+    def is_open(self):
+        return bool(self.fund_type.specific.open_round)
+
+    @property
+    def deadline(self):
+        return self.fund_type.specific.next_deadline()
+
 
 class FundIndex(BasePage):
     subpage_types = ['FundPage']
diff --git a/opentech/public/funds/templates/public_funds/fund_page.html b/opentech/public/funds/templates/public_funds/fund_page.html
index 7c6f6f51608f36eee2a489ef095097807322574e..56f758dd6919633d540c6fe75f83b5923e86ef4c 100644
--- a/opentech/public/funds/templates/public_funds/fund_page.html
+++ b/opentech/public/funds/templates/public_funds/fund_page.html
@@ -2,7 +2,7 @@
 {% load wagtailcore_tags wagtailimages_tags navigation_tags static %}
 
 {% block content %}
-    {% include "public_funds/includes/fund_apply_cta.html" with fund_type=page.fund_type.specific %}
+    {% include "public_funds/includes/fund_apply_cta.html" with fund_page=page %}
     <div class="wrapper wrapper--flex">
         <section class="section section--main">
             <h1>{{ page.title }}</h1>
@@ -11,6 +11,6 @@
             {% include_block page.body %}
         </section>
     </div>
-    {% include "public_funds/includes/fund_apply_cta.html" with fund_type=page.fund_type.specific %}
+    {% include "public_funds/includes/fund_apply_cta.html" with fund_page=page %}
     {% include "includes/relatedcontent.html" with related_pages=page.related_pages.all %}
 {% endblock %}
diff --git a/opentech/public/funds/templates/public_funds/includes/fund_apply_cta.html b/opentech/public/funds/templates/public_funds/includes/fund_apply_cta.html
index 1f81aa425819a5ce32bce596c0833547d4009086..b76d9adac020f2eed829b81d4cdd319f1023d929 100644
--- a/opentech/public/funds/templates/public_funds/includes/fund_apply_cta.html
+++ b/opentech/public/funds/templates/public_funds/includes/fund_apply_cta.html
@@ -2,12 +2,14 @@
 
 <div class="wrapper wrapper--flex">
     <div class="section section--apply-cta">
-        {% if fund_type.deadline %}
+        {% if fund_page.is_open %}
+            {% if fund_page.deadline %}
             <div class="deadline">
-                {% trans "Next deadline" %}: {{ fund_type.deadline|date:"M j, Y" }}
+                {% trans "Next deadline" %}: {{ fund_page.deadline|date:"M j, Y" }}
             </div>
+            {% endif %}
             <div class="apply-link">
-                <a class="button" href="{% pageurl fund_type %}">{% trans "Apply for this fund" %}</a>
+                <a class="button" href="{% pageurl fund_page.fund_type %}">{% trans "Apply for this fund" %}</a>
             </div>
         {% else %}
             <div class="deadline">
diff --git a/opentech/templates/base.html b/opentech/templates/base.html
index e2699382f6746b87218a6f5c22d40b4f73d177b0..2400f7871f65d77802e012a14bccc5f25186f267 100644
--- a/opentech/templates/base.html
+++ b/opentech/templates/base.html
@@ -137,7 +137,7 @@
             </div>
 
             <div class="wrapper wrapper--small">
-                <h1 class="header__title">{{ page.title }}</h1>
+                <h1 class="header__title">{% block page_title %}{{ page.title }}{% endblock %}</h1>
             </div>
             <svg class="header__icon header__icon--pixels header__icon--pixels-left"><use xlink:href="#hero-standard-left-pixels"></use></svg>
             <svg class="header__icon header__icon--pixels header__icon--pixels-right"><use xlink:href="#hero-standard-right-pixels"></use></svg>