A market signal is observable information or an event that may change expectations about an investment. Signals can come from price data, company developments, geopolitical events, news, sentiment, insider trades, or public blockchain activity. A signal supports a decision; it does not guarantee an outcome. section 11.3.2 section 4.2.6
Useful signals become more actionable when they can be measured, compared, and combined:
- Technical signals compare indicators such as short- and long-period simple moving averages; a crossover can produce a buy or sell decision. section 11.3.1
- Fundamental or event signals include major contracts, evidence of project progress, and products exceeding expectations. section 4.2.6
- Quantitative signals can score price movement, keyword sentiment, or insider trades, then combine comparable scores in a decision scorecard. section 11.3.2
- Feature selection must distinguish useful signal from noise: too few features risk underfitting, while too many risk overfitting. section 8.1.4
Testing an SMA signal
A Python backtest loads historical prices, adds two SMA columns to a pandas DataFrame, buys when the shorter average rises above the longer one, and sells after a reverse crossover. It compares gains with a fixed investment baseline and subtracts transaction costs. Testing several window pairs can reveal large historical differences, but those results remain historical evidence rather than a forecast. section 11.3.1
Listing 11.1 Backtest
def backtest(df: pd.DataFrame, lower: int, upper: int, shares : int,
buy_first_day = False) -> pd.DataFrame:
schema={'date': 'datetime64[ns]', 'action': 'str', 'cash_movement':
'float64'}
results = pd.DataFrame(columns=schema.keys()).astype(schema)
if buy_first_day:
results.loc[len(results)] = [df["Date"].iloc[0], "Buy",
df["Close"].iloc[0] * shares * -1]
waiting_for_bear = True
else:
waiting_for_bear = False
for index, row in df.iterrows():
if not waiting_for_bear:
if row[f"sma_{lower}"] > row[f"sma_{upper}"]:
results.loc[len(results)] = [row.Date, "Buy",
row.Close * shares * -1]
waiting_for_bear = True
if waiting_for_bear:
if row[f"sma_{lower}"] < row[f"sma_{upper}"]:
results.loc[len(results)] = [row.Date,
"Sell",
row.Close * shares]
waiting_for_bear = False
if waiting_for_bear:
results.loc[len(results)] = [df["Date"].iloc[-1], "Sell",
df["Close"].iloc[-1] * shares]
return results
Inspect the backtest implementation that records dates, actions, and cash movements while enforcing alternating buy and sell signals. section 11.3.1
