What's wrong with my implementation of the Adaline algorithm?
First of all, let me add this schema which I think is quite nice to understand the transition and improvement from the initial Rosenblatt's perceptron and the Adaline algorithm:

In Adaline, provided that the cost function (your y(t)-s(t)) is differentiable, the weights can be updated and there is no restriction of y and s having the same sign: the objective is to minimize the cost y-s.
Below you can find the code provided in the excellent book by Sebastian Raschka:
class AdalineSGD(object):
&quot;&quot;&quot;ADAptive LInear NEuron classifier.
    Parameters
    ------------
    eta : float
    Learning rate (between 0.0 and 1.0)
    n_iter : int
    Passes over the training dataset.
    shuffle : bool (default: True)
    Shuffles training data every epoch if True
    to prevent cycles.
    random_state : int
    Random number generator seed for random weight
    initialization.
    Attributes
    -----------
    w_ : 1d-array
    Weights after fitting.
    cost_ : list
    Sum-of-squares cost function value averaged over all
    training samples in each epoch.
&quot;&quot;&quot;
def __init__(self, eta=0.01, n_iter=10,
                shuffle=True, random_state=None):
    self.eta = eta
    self.n_iter = n_iter
    self.w_initialized = False
    self.shuffle = shuffle
    self.random_state = random_state

def fit(self, X, y):
    &quot;&quot;&quot; Fit training data.
    Parameters
    ----------
    X : {array-like}, shape = [n_samples, n_features]
    Training vectors, where n_samples is the number
    of samples and
    n_features is the number of features.
    y : array-like, shape = [n_samples]
    Target values.
    Returns
    -------
    self : object
    &quot;&quot;&quot;
    self._initialize_weights(X.shape[1])
    self.cost_ = []
    for i in range(self.n_iter):
        if self.shuffle:
            X, y = self._shuffle(X, y)
        cost = []
        for xi, target in zip(X, y):
            cost.append(self._update_weights(xi, target))
        avg_cost = sum(cost) / len(y)
        self.cost_.append(avg_cost)
    
    return self

def partial_fit(self, X, y):
    &quot;&quot;&quot;Fit training data without reinitializing the weights&quot;&quot;&quot;
    if not self.w_initialized:
        self._initialize_weights(X.shape[1])
    if y.ravel().shape[0] &gt; 1: #if we have more than one sample
        for xi, target in zip(X, y):
            self._update_weights(xi, target)
    else:
        self._update_weights(X, y)
    
    return self
    
def _shuffle(self, X, y):
    &quot;&quot;&quot;Shuffle training data&quot;&quot;&quot;
    r = self.rgen.permutation(len(y))
    
    return X[r], y[r]

def _initialize_weights(self, m):
    &quot;&quot;&quot;Initialize weights to small random numbers&quot;&quot;&quot;
    import numpy as np

    self.rgen = np.random.RandomState(self.random_state)
    self.w_ = self.rgen.normal(loc=0.0, scale=0.01,
                               size=1 + m)
    
    self.w_initialized = True

def _update_weights(self, xi, target):
    &quot;&quot;&quot;Apply Adaline learning rule to update the weights&quot;&quot;&quot;
    output = self.activation(self.net_input(xi))
    error = (target - output)
    self.w_[1:] += self.eta * xi.dot(error)
    self.w_[0] += self.eta * error
    cost = 0.5 * error**2

    return cost

def net_input(self, X):
    &quot;&quot;&quot;Calculate net input&quot;&quot;&quot;
    
    return np.dot(X, self.w_[1:]) + self.w_[0]

def activation(self, X):
    &quot;&quot;&quot;Compute linear activation&quot;&quot;&quot;
    return X

def predict(self, X):
    &quot;&quot;&quot;Return class label after unit step&quot;&quot;&quot;

    return np.where(self.activation(self.net_input(X))
                    &gt;= 0.0, 1, -1)