Signal Fabric is a lightweight server application framework for generating market signals on demand, developed in Python 3. It supports various targets (asset classes, markets, etc.) and signal operations through a modular architecture that allows for easy extension and customization.
Its uniqueness lies in providing signals that are valuable over meaningful time frames and that help reveal macro trends — while also being useful for fast-acting systems.
Current Version: 0.1.29 (alpha)
-
Scoped to financial domain, whether decentralized or centralized.
-
Remain fully open source to promote transparency and wider adoption.
-
Be entirely free for personal use and research purposes.
-
Offer automatic trading systems raw or processed data to support decisions-making.
-
Prototype composite signals from raw ones using Jupyter Notebook or similar environments.
-
Not designed for high‑frequency trading (HFT) or other fast trading systems.
-
Direct interaction with markets is explicitly out of scope.
-
The codebase is still stabilizing, and contributions via pull request are not open yet. Key interfaces are subject to refinement; feel free to experiment with modules, but keep in mind that breaking changes may occur.
-
Backtesting is supported for signals that produce predictions. This feature is useful for evaluating how such signals perform on historical data, but it is secondary to the project’s primary goal of delivering market metrics.
Path:
src
└── server
├── handlers # signal handlers
├── services # core services
└── serializers # result encodersHandlers:
RSISignal: RSI indicator signal handlerMASignal: MA indicator signal handler (supportssma,ema,wma)ATRSignal: ATR indicator signal handlerDMISignal: DMI indicator signal handler(combines ADX, DI+, DI-)PriceStructSignal: Price structure signal handlerTrendDirectSignal: Trend direction signal handlerMntmQualitySignal: Momentum Quality signal handlerMACDSignal: MACD indicator signal handler
Services:
CacheManager: factory-pattern based caching to create scoped cachesExchangeClient: abstract base type for exchange implementationsBinanceClient: provides access to OHLCV data from Binance CEXHyperliquidClient: provides access to OHLCV data from Hyperliquid DEXOHLCVFetcher: abstracts OHLCV data fetching and provides cachingOHLCVFileLoader: data provider that loads candles from JSON files on disk
Serializers:
json: built‑in option for JSON‑encoded output
def _compute_signal_impl(self, target: str, signal_op: str, request: Optional[dict]) -> Outcome:
"""
Compute RSI signal
"""
try:
# Extract parameters
period = int(request['period'])
timeframe = request['timeframe']
# Get OHLCVFetcher service via RuntimeContext
ohlcv_fetcher = self._context.get_service(self._ohlcv_service_name)
# Check service exists and has required method (duck typing to avoid class identity issues)
if not ohlcv_fetcher or not hasattr(ohlcv_fetcher, 'retrieve_ohlcv'):
return self._error(f"OHLCV service '{self._ohlcv_service_name}' not found or invalid")
# Fetch OHLCV data - need period + 100 candles for RSI warmup
limit = period + 100
candles = ohlcv_fetcher.retrieve_ohlcv(
symbol=self._symbol,
timeframe=timeframe,
limit=limit
)
if not candles or len(candles) < period:
return self._error(f"Insufficient candle data: need {period}, got {len(candles) if candles else 0}")
# Extract close prices as numpy array
close_prices = np.array([candle.close for candle in candles])
# Calculate RSI using TA-Lib
rsi_values = talib.RSI(close_prices, timeperiod=period)
# Prepare the list for output
rsi_list = rsi_values[~np.isnan(rsi_values)].round(2).tolist()
# Get the latest RSI value (last non-NaN value)
latest_rsi = None
for i in range(len(rsi_values) - 1, -1, -1):
if not np.isnan(rsi_values[i]):
latest_rsi = float(rsi_values[i])
break
if latest_rsi is None:
return self._error('Failed to compute RSI: all values are NaN')
# Determine regime
if latest_rsi >= 70:
regime = 'overbought'
elif latest_rsi <= 30:
regime = 'oversold'
else:
regime = 'neutral'
# Build result object - will be serialized by SignalProcessor
result_obj = {
'symbol': self._symbol,
'rsi': rsi_list,
'latest_rsi': round(latest_rsi, 2),
'regime': regime
}
return Outcome(
result=result_obj,
computation=self.get_metadata()['name']
)
except Exception as e:
self.logger.error(f"Error computing RSI: {e}", exc_info=True)
return self._error(str(e))from signal_fabric import GrpcClient, SignalOutcome
import signal_fabric
print(f"Using signal_fabric from: {signal_fabric.__file__}")
import json
with GrpcClient(host='localhost', port=9090, use_tls=False) as client:
outcome : SignalOutcome = client.process_signal(
target='BTC',
signal_name='rsi_hyperliquid',
signal_op='compute_rsi',
handler_request={
"period": 14,
"timeframe": "1h"
}
)
# Check for errors first
if outcome.errors:
print('We got errors:')
for err_id, err_message in outcome.errors.items():
print(f" - {err_id}: {err_message}")
print(f"\nResult: {outcome.result}")
if outcome.result_format:
print(f"Result format: {outcome.result_format}")
print(f"Computation: {outcome.computation}")
if outcome.handler_request:
print(f"Handler request (echoed): {outcome.handler_request}")
else:
if not outcome.result:
print("Error: Empty result received")
else:
format_info = f"result_format: {outcome.result_format}\n" if outcome.result_format else ""
print(f"""target: {outcome.target}
latest_rsi: {outcome.result['latest_rsi']}
regime: {outcome.result['regime']}
{format_info}computed_at: {outcome.computed_at}
handler_request (echoed): {outcome.handler_request if outcome.handler_request else 'None'}
""")pip install signal-fabric-client
python ./rsi_sample.pytarget: BTC
latest_rsi: 59.06050385691097
regime: neutral
result_format: json
computed_at: 2025-12-25T17:36:09.613636Z
handler_request (echoed): {'period': 14, 'timeframe': '1h'}server:
host: localhost
port: 9090
grpc_workers: 10
grpc_protocol: http # https also available
serializer: json
signals:
- rsi_hyperliquid:
_binding: rsi_signal
ohlcv_service: ohlcv_hyperliquid
- dmi_hyperliquid:
_binding: dmi_signal
ohlcv_service: ohlcv_hyperliquid
- atr_hyperliquid:
_binding: atr_signal
ohlcv_service: ohlcv_hyperliquid
- supertrend_hyperliquid:
_binding: supertrend_signal
ohlcv_service: ohlcv_hyperliquid
atr_signal: atr_hyperliquid
- ma_hyperliquid:
_binding: ma_signal
ohlcv_service: ohlcv_hyperliquid
- macd_hyperliquid:
_binding: macd_signal
ma_signal: ma_hyperliquid
fast_period: 12
slow_period: 26
signal_period: 9
services:
- cache_manager:
_service: cache
_binding: CacheManager
max_items: 10000
max_bytes: 104857600
- hyperliquid_client:
_service: exchange/hyperliquid_client
_binding: HyperliquidClient
_api_url: https://api.hyperliquid.xyz
_run_mode: prod
timeout_sec: 10.0
max_retries: 1
retry_delay: 0
- ohlcv_hyperliquid:
_service: exchange/data_providers
_binding: OHLCVFetcher
_using:
- hyperliquid_client
- cache_manager
ttl_1m: 60
ttl_5m: 300
ttl_1h: 3600
ttl_1d: 86400
ttl_default: closest # Auto-select nearest TTL