Can we use $y * \operatorname{sgn}(\hat y)$ as a loss function in linear regression? Can we use $y * \operatorname{sgn}(\hat y)$ as a loss function in linear regression where $\hat y$ is the prediction and $y$ is the target value, or is there any other loss function close to this which can be used?
 A: In regression, where you want to approximate the measured points $(x_i, y_i)$ with those from your estimator $(x_i, \hat y_i)$, a loss function should be a function involving some kind of distance between $y_i$ and $\hat y_i$, like e.g. the norm of the residual $\hat y_i - y_i$.
In your case, you have only one $y$-value in your loss, so, unless something is missing, this would not compare the measured data with the estimated data, i.e. it doesn't measure any loss.
So, the answer is: No, this is not a sensible loss function.
A: For a loss function to work effectively, its smallest values should occur where $y \approx x.$
Such small values are shown in dark blues in this filled contour plot of the standard quadratic loss function $\mathcal{L}(x,y) = (y-x)^2.$

Visually, you want the colors surrounding the diagonal line $y=x$ to be dark blue.
Here is a comparable plot of $\mathcal{L}(x,y) = y\operatorname{sgn}(x):$

The dark blues occur where $x$ and $y$ are large and of opposite signs.  This will not be an effective loss function unless you want your predictions to be as far away from the true values as you can possibly make them!

I hope it's now clear that any function that comes even close to this one will not be a useful loss function.  But at least you now have a rapid, reliable visual procedure to evaluate any candidate loss function you might care to think up.  To make this as easy as possible to do, here is R code to produce a plot of a loss function.
#
# Create an array of losses.
#
loss <- function(x, y) y * sign(x)
y <- x <- seq(-1, 1, length.out=201)
z <- outer(x, y, loss)
#
# Plot its values and the reference line y == x.
#
col <- rev(rainbow(13, 2/3, 1)[1:10]) # The color scale red ... blue
image(x, y, z, col=col, main=bquote(paste("Contours of ", .(body(loss)))))
contour(x, y, z, add=TRUE)
abline(0:1, lwd=2, col=gray(.25))

A: Let's exemplify with a bivariate normal distribution with $\vec{\mu} = \vec{0}$ and $\Sigma = \begin{bmatrix}1 & -0.9 \\ -0.9 & 1 \end{bmatrix}$.
Import libraries.
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf

Generate data set.
cov = [[1,-0.9],
       [-0.9,1]]
x,y = np.random.multivariate_normal(
    [0,0],
    cov=cov,
    size=1000).T
x = tf.reshape(tf.constant(x), (-1,1))
y = tf.reshape(tf.constant(y), (-1,1))

And let's define your loss function.
def sign_product_loss(y_true, y_pred):
    return tf.matmul(y_true, tf.math.sign(y_pred), transpose_a=True)

Define a simple linear model (slope and intercept as parameters).
model = tf.keras.Sequential(
    [
        tf.keras.Input(
            shape=(1,)
            ),
        tf.keras.layers.Dense(
            units=1,
            activation=None
            )
        ]
    )

For training, I selected the Nadam (Nesterov-accelerated adaptive moments) just b/c I find it a reliable default for toy problems. Other optimizers would also work excellently on this simple linear regression problem. In Tensorflow we must compile the model.
loss = sign_product_loss
opt = tf.keras.optimizers.Nadam()

model.compile(
    optimizer=opt,
    loss=loss
    )

Now train the model.
history = model.fit(x, y, epochs=100)

If we look at the loss history, we can see that the model did not obviously trend toward better or worse fit.

And plotting the predictions and the original data together, we can see that the resulting fit was not acceptable.


One way to study an objective function is to plot the contours of its surface over its input. See whuber's answer for an explanation. As a supplement to that answer, here is some quick Python code to produce filled contour plots.
import matplotlib.pyplot as plt
import numpy as np


def loss(x,y):
    return y * np.sign(x)

X, Y = np.meshgrid(*[np.linspace(-1,1,200)]*2)

Z = loss(X,Y)


fig, axes = plt.subplots()
g = axes.contourf(X, Y, Z, cmap='rainbow', levels=15)
##g.clabel(fontsize=10, inline=1, fmt='%.1f', colors='k') # Labels if you want them
axes.plot(*[[-1,1]]*2, color='k')
axes.text(-0.05, 0.05, '$y = x$', rotation=45)
axes.set_xlabel('x')
axes.set_ylabel('y')
axes.set_title('$y \\times \\operatorname{sign}(\\hat y)$')
axes.set_aspect('equal')
plt.tight_layout()
plt.show()

The above can be adjusted as desired, but currently outputs:

