예제 #1
0
def ma(s, N, dates=None):
    '''Create running N-period arithmetic average of input series.'''

    dates = dates or core.current_dates()
    vals = series_dates_values(s, dates)
    fd = dates.first_date()
    ld = dates.last_date()
    outv = [None for dt in range(fd, ld + 1)]

    total = 0
    consecutive = 0

    for ndx, dt in enumerate(dates):
        val = vals[ndx]
        if core.is_valid_num(val):
            total += val
            consecutive += 1
        else:
            consecutive = 0
            total = 0
        if consecutive > N:
            total -= vals[ndx - N]
        if consecutive >= N:
            outv[dt - fd] = (total / N)

    return core.vector_series(outv, fd, name="SMA({})".format(N))
예제 #2
0
def repeated(s, repeat_last=False, dates=None):
    '''Fill missing values in a series with preceding ones, optionally
    continuing the last valid observation.'''

    dates = dates or core.current_dates()
    vals = list(map(s.f, dates))

    fd = dates.first_date()
    ld = dates.last_date()

    prev = vals[0]
    last_valid_date = fd
    for ndx, val in enumerate(vals):
        if ndx and not core.is_valid_num(val):
            vals[ndx] = prev
        else:
            last_valid_date = dates.vec[ndx]
        prev = vals[ndx]

    fd = dates.first_date()
    ld = dates.last_date()

    outv = [None for dt in range(fd, ld + 1)]

    for (dt, val) in zip(dates, vals):
        if (not repeat_last) and (dt > last_valid_date):
            break
        if core.is_valid_num(val):
            outv[dt - fd] = val

    return core.vector_series(outv, fd)
예제 #3
0
def mo_days(s, days, dates=None):
    '''Similar to :code:`mo(s,N)`, :code:`mo_days(s,days)` calculates 
    the new/old ratios along the specified dates for each new, but 
    each old-date is based upon calendar days rather than periods.
    If the calendar days back does not line up with a market day, the
    value of most recent available earlier observation is selected.'''

    sf = s.f
    sf_old = (fudge(s)).f

    if days < 1:
        raise Exception('expected positive days')

    dates = dates or core.current_dates()
    dv = dates.vec
    fd = dv[0]
    outv = [None for dt in range(dv[0], dv[-1] + 1)]

    for dt in dates:
        r = ratio(sf_old(dt - days), sf(dt))
        if core.is_valid_num(r):
            outv[dt - fd] = r

    name = "mo_days({},{})".format(abbreviate(s), days)
    return core.vector_series(outv, fd, name=name)
예제 #4
0
def prepend(s, *, surrogate=None, dates=None):

    dates = (dates or core.current_dates())
    if surrogate is None:
        raise Exception("surrogate required")
    ob = first_ob(add(s, surrogate), dates=dates)
    if ob is None:
        raise Exception("no observation")

    base_f = s.f
    surr_f = surrogate.f
    common_date = ob[0]
    ratio = s.f(ob[0]) / surr_f(ob[0])

    dv = dates.vec
    fd = dates.first_date()
    ld = dates.last_date()
    outv = [None for dt in range(fd, ld + 1)]

    for (ndx, dt) in enumerate(dv):
        val = base_f(dt)
        if not core.is_valid_num(val) and dt < common_date:
            val = surr_f(dt) * ratio
        if core.is_valid_num(val):
            outv[dt - fd] = val

    return core.vector_series(outv, fd, name="prepend(...)")
예제 #5
0
def conviction(s, N, dates=None):
    '''A conviction signal series avoids signal whipsaws by forcing signals to
    persist N periods before firing.  The signal, if fired, is delayed by N periods
    as well.  For instance, with a conviction of 1, two sequential signals are 
    simply erased.  If not erased, the signal values of the input are delayed by 
    one period.'''

    dates = dates or core.current_dates()
    dv = [dt for dt in dates]
    vals = list(map(s.f, dates))
    sigv = signalify_vector_copy(vals)

    last_ndx = -1000000
    for (ndx, sig) in enumerate(sigv):
        if sig:
            if (ndx - last_ndx <= N):
                sigv[ndx] = None
                sigv[last_ndx] = None
            last_ndx = ndx

    fd = dates.first_date()
    ld = dates.last_date()
    outv = [None for dt in range(fd, ld + 1)]

    # warp
    for (sig, dt) in zip(sigv, dv[N:]):
        if sig:
            outv[dt - fd] = sig

    return core.vector_series(outv, fd)
예제 #6
0
def obs_to_series(obs, name=None):
    '''Convert a list of (date,value) tuples to a series.'''

    valids = [(core.to_jdate(ob[0]), ob[1]) for ob in obs if ob]
    valids.sort()
    if not len(valids):
        return core.series(lambda dt: None, name)
    fd = valids[0][0]
    ld = valids[-1][0]
    vec = [None for x in range(fd, ld + 1)]
    for (dt, val) in valids:
        vec[dt - fd] = val
    return core.vector_series(vec, fd, name=name)
예제 #7
0
def to_signals(s, dates=None):
    '''Transform a series into non-repeating negative-one (-1) where the input is negative
       non-repeating one (1) where the input is non-negative.'''

    dates = dates or core.current_dates()
    vals = list(map(s.f, dates.vec))
    sigv = signalify_vector_copy(vals)

    fd = dates.first_date()
    ld = dates.last_date()
    outv = [None for dt in range(fd, ld + 1)]

    for (dt, sig) in zip(dates, sigv):
        if sig:
            outv[dt - fd] = sig

    return core.vector_series(outv, fd, name='sigs({})'.format(abbreviate(s)))
예제 #8
0
def fractional(s, fraction, dates=None):
    '''A fractional smoothes a series such that the current value is weighted by some fraction 
    in (0..1) added to the previous value weighted by (1 - fraction).'''

    dates = dates or core.current_dates()
    fd, ld = s.first_date(), s.last_date()
    outv = [None for dt in range(fd, ld + 1)]
    remainder = 1 - fraction
    prev = None
    f = s.f
    for dt in dates:
        val = f(dt)
        newVal = ((fraction * val + remainder * prev) if
                  (core.is_valid_num(prev)
                   and core.is_valid_num(val)) else val)
        outv[dt - fd] = newVal
        prev = newVal
    return core.vector_series(outv,
                              fd,
                              name="fractional({},{})".format(
                                  abbreviate(s), fraction))
예제 #9
0
def warp(s, N, dates=None):
    '''warp creates an N-period shift of values within dateset.  Negative periods shift data
    backward in time.  Often a signal series if warped with N=1 to measure 
    performance of a trading scheme if trading happens the next market day.'''

    dates = (dates or core.current_dates())
    dv = dates.vec
    sf = s.f
    fd = dates.first_date()
    ld = dates.last_date()
    outv = [None for dt in range(fd, ld + 1)]

    for (ndx, dt) in enumerate(dv):
        val = sf(dt)
        if core.is_valid_num(val):
            new_ndx = ndx + N
            if 0 <= new_ndx < len(dv):
                new_dt = dv[new_ndx]
                outv[new_dt - fd] = val

    return core.vector_series(outv, fd, name="warp({})".format(N))
예제 #10
0
def mo(s, N, dates=None):
    '''Return the N-period ratio series of new/old values.'''

    if N < 1:
        raise Exception('requires period > 0')

    dates = dates or core.current_dates()
    sf = s.f
    dv = dates.vec
    shifted_dv = dv[min(N, len(dv)):]
    outv = [None for dt in range(dv[0], dv[-1] + 1)]
    fd = dv[0]

    for (early, late) in zip(dv, shifted_dv):
        e_val = sf(early)
        l_val = sf(late)
        r = ratio(e_val, l_val)
        if core.is_valid_num(r):
            outv[late - fd] = r

    name = "mo({},{})".format(abbreviate(s), N)
    return core.vector_series(outv, fd, name=name)
예제 #11
0
def unrepeated(s, dates=None):
    '''Copy the input series, suppressing repeated values.'''

    dates = dates or core.current_dates()
    vals = list(map(s.f, dates))

    prev = vals[0]
    for ndx, val in enumerate(vals):
        if ndx and val == prev:
            vals[ndx] = None
        prev = val

    fd = dates.first_date()
    ld = dates.last_date()

    outv = [None for dt in range(fd, ld + 1)]

    for (dt, val) in zip(dates, vals):
        if core.is_valid_num(val):
            outv[dt - fd] = val

    return core.vector_series(outv, fd)
예제 #12
0
def window_series (s, N, proc, dates=None, missing_data_permitted=False):

    dates = (dates or core.current_dates())
    dv = dates.vec
    sf = s.f
    fd = dates.first_date()
    ld = dates.last_date()
    vv = [(n if core.is_valid_num(n) else None) for n in (sf(dt) for dt in dv)]
    outv = [ None for dt in range(fd,ld+1) ]

    count = 0
    for (ndx, dt) in enumerate(dv):
        val = sf(dt)
        count = (count+1 if (missing_data_permitted or core.is_valid_num(val)) else 0)
        if count >= N:
            stop = ndx+1
            start = stop-N
            result = proc(vv[start:stop])
            if core.is_valid_num(result):
                outv[dt - fd] = result

    return core.vector_series(outv, fd, name="window_series({})".format(N))
예제 #13
0
def reversals(s, down_factor=1.0, up_factor=1.0, dates=None):
    '''When a series ascends above up-factor multiplied by a preceding 
    local minimum, a buy (1) signal is produced.  Upon 
    descending below the product of a local maximum and down-factor, 
    a sell (-1) signal is produced.
    '''
    dates = dates or core.current_dates()
    fd, ld = core.first_date(dates), core.last_date(dates)
    outv = [None for dt in range(fd, ld + 1)]
    min_ob = max_ob = first_ob(s, dates)
    sf = s.f
    state = None

    for dt in dates:

        val = sf(dt)
        if not core.is_valid_num(val):
            continue

        if val > max_ob[1]:
            max_ob = (dt, val)
        if val < min_ob[1]:
            min_ob = (dt, val)

        if (1 != state) and (val > min_ob[1] * up_factor):
            max_ob = min_ob = (dt, val)
            outv[dt - fd] = 1
            state = 1

        elif (-1 != state) and (val < max_ob[1] * down_factor):
            max_ob = min_ob = (dt, val)
            outv[dt - fd] = -1
            state = -1

    return core.vector_series(outv,
                              fd,
                              name="reversals({})".format(abbreviate(s)))
예제 #14
0
def equity_line(s,
                signals,
                initial_value=100,
                alternate_investment=None,
                dates=None):
    '''The point of most signal generation is to go in and out of invested position
    on some series, optionally buying an alternate investment such as short-duration debt.
    The equity_line series represents the value of the investment after treating signals
    as entrance and exit points during the dateset indicated.
    '''

    dates = dates or core.current_dates()
    dv = dates.vec
    fd = dates.first_date()
    ld = dates.last_date()
    alternate_investment = alternate_investment or constant(1)

    alt_vals = series_dates_values(alternate_investment, dates)
    inv_vals = series_dates_values(s, dates)
    sig_vals = series_dates_values(signals, dates)

    outv = [None for dt in range(fd, ld + 1)]

    product = 1.0
    buy = True
    prev_inv = prev_alt = None
    first_sig_ob = first_ob(signals, dates=dates)
    if not first_sig_ob:
        raise Exception("signal series is empty")

    for (dt, alt, inv, sig) in zip(dv, alt_vals, inv_vals, sig_vals):
        if dt < first_sig_ob[0]:
            continue
        if sig or (core.is_valid_num(alt) and core.is_valid_num(inv)):
            change = None
            if not core.is_valid_num(inv):
                raise Exception("missing investment observation at {}".format(
                    core.jdate_to_text(dt)))
            if not core.is_valid_num(alt):
                raise Exception(
                    "missing alternate_investment observation at {}".format(
                        core.jdate_to_text(dt)))
            if buy:
                if prev_inv:
                    change = inv / prev_inv
                else:
                    change = 1.0
            else:
                if prev_alt:
                    change = alt / prev_alt
                else:
                    change = 1.0

            # prior to having signal, nothing was invested
            if dt <= first_sig_ob[0]:
                change = 1.0

            new_buy = (sig > 0) if sig else buy
            new_product = product * change

            outv[dt - fd] = new_product * initial_value

            product = new_product
            buy = new_buy
            prev_inv = inv
            prev_alt = alt

    return core.vector_series(outv, fd)