feat: add pairings management page and integrate with Kraken API for syncing
feat: create configuration templates for alerts, Kraken settings, risk limits, and runtime settings refactor: streamline config form by including separate template files for better organization
This commit is contained in:
+34
-14
@@ -899,6 +899,25 @@ async def dashboard_audit(request: Request) -> HTMLResponse:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/dashboard/config/pairings", response_class=HTMLResponse)
|
||||||
|
async def dashboard_config_pairings_page(
|
||||||
|
request: Request,
|
||||||
|
search: str | None = None,
|
||||||
|
enabled: str | None = None,
|
||||||
|
) -> HTMLResponse:
|
||||||
|
"""Standalone pairings management page."""
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
request=request,
|
||||||
|
name="pairings.html",
|
||||||
|
context={
|
||||||
|
"title": "Currency Pairings",
|
||||||
|
"request": request,
|
||||||
|
"search": search or "",
|
||||||
|
"enabled": enabled or "all",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/dashboard/config", response_class=HTMLResponse)
|
@router.get("/dashboard/config", response_class=HTMLResponse)
|
||||||
async def dashboard_config_page(request: Request) -> HTMLResponse:
|
async def dashboard_config_page(request: Request) -> HTMLResponse:
|
||||||
d_context = await _dashboard_config_context(request)
|
d_context = await _dashboard_config_context(request)
|
||||||
@@ -1447,19 +1466,20 @@ async def dashboard_api_pairings_toggle(request: Request) -> HTMLResponse:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/dashboard/api/pairings/sync")
|
@router.post("/dashboard/api/pairings/sync", response_class=HTMLResponse)
|
||||||
async def dashboard_api_pairings_sync(request: Request) -> JSONResponse:
|
async def dashboard_api_pairings_sync(request: Request) -> HTMLResponse:
|
||||||
"""Trigger a re-sync of pairings from Kraken."""
|
"""Sync pairings from Kraken and return refreshed table."""
|
||||||
kraken_client = request.app.state.kraken_client
|
from arbitrade.config.pairing_sync import sync_pairings_from_kraken
|
||||||
|
|
||||||
store = request.app.state.store
|
store = request.app.state.store
|
||||||
summary = await sync_pairings_from_kraken(kraken_client, store)
|
kraken = getattr(request.app.state, "kraken_client", None)
|
||||||
|
if kraken is not None:
|
||||||
await _record_audit(
|
await sync_pairings_from_kraken(kraken, store)
|
||||||
request,
|
repo = _pairing_repo(request)
|
||||||
actor="dashboard_user",
|
pairings = await repo.list_pairings()
|
||||||
event_type="dashboard.pairings.sync",
|
pairings.sort(key=lambda p: (p.base_asset, p.quote_asset))
|
||||||
decision="approved",
|
return templates.TemplateResponse(
|
||||||
payload=summary, # type: ignore
|
request=request,
|
||||||
|
name="partials/pairings_table.html",
|
||||||
|
context={"request": request, "pairings": pairings},
|
||||||
)
|
)
|
||||||
|
|
||||||
return JSONResponse(summary)
|
|
||||||
|
|||||||
@@ -86,7 +86,8 @@ class OrderBook:
|
|||||||
BookLevel(price=price, volume=self._bids[price])
|
BookLevel(price=price, volume=self._bids[price])
|
||||||
for price in reversed(bid_keys[-depth:])
|
for price in reversed(bid_keys[-depth:])
|
||||||
]
|
]
|
||||||
asks = [BookLevel(price=price, volume=self._asks[price]) for price in ask_keys[:depth]]
|
asks = [BookLevel(price=price, volume=self._asks[price])
|
||||||
|
for price in ask_keys[:depth]]
|
||||||
return bids, asks
|
return bids, asks
|
||||||
|
|
||||||
def compute_checksum(self, depth: int = 10) -> int:
|
def compute_checksum(self, depth: int = 10) -> int:
|
||||||
|
|||||||
@@ -5,9 +5,10 @@
|
|||||||
</div>
|
</div>
|
||||||
{% set nav_links = [ {"url": "/dashboard", "label": "Dashboard", "class":
|
{% set nav_links = [ {"url": "/dashboard", "label": "Dashboard", "class":
|
||||||
"secondary"}, {"url": "/dashboard/config", "label": "Config", "class":
|
"secondary"}, {"url": "/dashboard/config", "label": "Config", "class":
|
||||||
"secondary"}, {"url": "/dashboard/backtesting", "label": "Backtesting",
|
"secondary"}, {"url": "/dashboard/config/pairings", "label": "Pairings",
|
||||||
"class": "secondary"}, {"url": "/dashboard/health", "label": "Health",
|
"class": "secondary"}, {"url": "/dashboard/backtesting", "label":
|
||||||
"class": "secondary"}, ] %}
|
"Backtesting", "class": "secondary"}, {"url": "/dashboard/health", "label":
|
||||||
|
"Health", "class": "secondary"}, ] %}
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
{% for link in nav_links %}
|
{% for link in nav_links %}
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -0,0 +1,192 @@
|
|||||||
|
<div class="card">
|
||||||
|
<div class="label">Alerting</div>
|
||||||
|
<label class="field checkbox">
|
||||||
|
<input name="alerts_enabled" type="checkbox" {{ alerts_enabled }} />
|
||||||
|
<span>Alerts enabled</span>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Min severity</span>
|
||||||
|
<select name="alert_min_severity">
|
||||||
|
{% for sev in ["info", "warning", "error", "critical"] %} {% set sel =
|
||||||
|
"selected" if alert_min_severity == sev else "" %}
|
||||||
|
<option value="{{ sev }}" {{ sel }}>{{ sev }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Dedup seconds</span>
|
||||||
|
<input
|
||||||
|
name="alert_dedup_seconds"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="1"
|
||||||
|
value="{{ alert_dedup_seconds }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field checkbox">
|
||||||
|
<input
|
||||||
|
name="alert_on_trade_events"
|
||||||
|
type="checkbox"
|
||||||
|
{{
|
||||||
|
alert_on_trade_events
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span>Trade events</span>
|
||||||
|
</label>
|
||||||
|
<label class="field checkbox">
|
||||||
|
<input
|
||||||
|
name="alert_on_error_events"
|
||||||
|
type="checkbox"
|
||||||
|
{{
|
||||||
|
alert_on_error_events
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span>Error events</span>
|
||||||
|
</label>
|
||||||
|
<label class="field checkbox">
|
||||||
|
<input
|
||||||
|
name="alert_on_threshold_events"
|
||||||
|
type="checkbox"
|
||||||
|
{{
|
||||||
|
alert_on_threshold_events
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span>Threshold events</span>
|
||||||
|
</label>
|
||||||
|
<label class="field checkbox">
|
||||||
|
<input
|
||||||
|
name="alert_on_system_events"
|
||||||
|
type="checkbox"
|
||||||
|
{{
|
||||||
|
alert_on_system_events
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span>System events</span>
|
||||||
|
</label>
|
||||||
|
<hr
|
||||||
|
style="
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
margin: 12px 0;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<label class="field checkbox">
|
||||||
|
<input
|
||||||
|
name="telegram_alerts_enabled"
|
||||||
|
type="checkbox"
|
||||||
|
{{
|
||||||
|
telegram_alerts_enabled
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span>Telegram</span>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Telegram bot token</span>
|
||||||
|
<input
|
||||||
|
name="telegram_bot_token"
|
||||||
|
type="password"
|
||||||
|
value="{{ telegram_bot_token }}"
|
||||||
|
placeholder="Bot token"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Telegram chat ID</span>
|
||||||
|
<input
|
||||||
|
name="telegram_chat_id"
|
||||||
|
type="text"
|
||||||
|
value="{{ telegram_chat_id }}"
|
||||||
|
placeholder="Chat ID"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<hr
|
||||||
|
style="
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
margin: 12px 0;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<label class="field checkbox">
|
||||||
|
<input
|
||||||
|
name="discord_alerts_enabled"
|
||||||
|
type="checkbox"
|
||||||
|
{{
|
||||||
|
discord_alerts_enabled
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span>Discord</span>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Discord webhook URL</span>
|
||||||
|
<input
|
||||||
|
name="discord_webhook_url"
|
||||||
|
type="password"
|
||||||
|
value="{{ discord_webhook_url }}"
|
||||||
|
placeholder="Webhook URL"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<hr
|
||||||
|
style="
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
margin: 12px 0;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<label class="field checkbox">
|
||||||
|
<input
|
||||||
|
name="email_alerts_enabled"
|
||||||
|
type="checkbox"
|
||||||
|
{{
|
||||||
|
email_alerts_enabled
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span>Email</span>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>SMTP host</span>
|
||||||
|
<input
|
||||||
|
name="email_smtp_host"
|
||||||
|
type="text"
|
||||||
|
value="{{ email_smtp_host }}"
|
||||||
|
placeholder="smtp.example.com"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>SMTP port</span>
|
||||||
|
<input
|
||||||
|
name="email_smtp_port"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="65535"
|
||||||
|
value="{{ email_smtp_port }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>SMTP username</span>
|
||||||
|
<input
|
||||||
|
name="email_smtp_username"
|
||||||
|
type="text"
|
||||||
|
value="{{ email_smtp_username }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>SMTP password</span>
|
||||||
|
<input
|
||||||
|
name="email_smtp_password"
|
||||||
|
type="password"
|
||||||
|
value=""
|
||||||
|
placeholder="Leave blank to keep existing"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>From address</span>
|
||||||
|
<input name="email_alert_from" type="text" value="{{ email_alert_from }}" />
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>To address</span>
|
||||||
|
<input name="email_alert_to" type="text" value="{{ email_alert_to }}" />
|
||||||
|
</label>
|
||||||
|
<label class="field checkbox">
|
||||||
|
<input name="email_smtp_use_tls" type="checkbox" {{ email_smtp_use_tls }} />
|
||||||
|
<span>Use TLS</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<div class="card">
|
||||||
|
<div class="label">Kraken Exchange</div>
|
||||||
|
<label class="field">
|
||||||
|
<span>REST URL</span>
|
||||||
|
<input name="kraken_rest_url" type="text" value="{{ kraken_rest_url }}" />
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>WebSocket URL</span>
|
||||||
|
<input name="kraken_ws_url" type="text" value="{{ kraken_ws_url }}" />
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Private rate limit (s)</span>
|
||||||
|
<input
|
||||||
|
name="kraken_private_rate_limit_seconds"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
value="{{ kraken_private_rate_limit_seconds }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>HTTP timeout (s)</span>
|
||||||
|
<input
|
||||||
|
name="kraken_http_timeout_seconds"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
step="0.5"
|
||||||
|
value="{{ kraken_http_timeout_seconds }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Retry attempts</span>
|
||||||
|
<input
|
||||||
|
name="kraken_retry_attempts"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="1"
|
||||||
|
value="{{ kraken_retry_attempts }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Retry base delay (s)</span>
|
||||||
|
<input
|
||||||
|
name="kraken_retry_base_delay_seconds"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
value="{{ kraken_retry_base_delay_seconds }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>API key</span>
|
||||||
|
<input name="kraken_api_key" type="text" value="{{ kraken_api_key }}" />
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>API secret</span>
|
||||||
|
<input
|
||||||
|
name="kraken_api_secret"
|
||||||
|
type="password"
|
||||||
|
value=""
|
||||||
|
placeholder="Leave blank to keep existing"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>API key permissions</span>
|
||||||
|
<input
|
||||||
|
name="kraken_api_key_permissions"
|
||||||
|
type="text"
|
||||||
|
value="{{ kraken_api_key_permissions }}"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>WS heartbeat timeout (s)</span>
|
||||||
|
<input
|
||||||
|
name="ws_heartbeat_timeout_seconds"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
step="1"
|
||||||
|
value="{{ ws_heartbeat_timeout_seconds }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>WS max staleness (s)</span>
|
||||||
|
<input
|
||||||
|
name="ws_max_staleness_seconds"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
step="1"
|
||||||
|
value="{{ ws_max_staleness_seconds }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<div class="card">
|
||||||
|
<div class="label">Risk & Guardrails</div>
|
||||||
|
<label class="field">
|
||||||
|
<span>Daily loss limit USD</span>
|
||||||
|
<input
|
||||||
|
name="daily_loss_limit_usd"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
value="{{ daily_loss_limit_value }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Cumulative loss limit USD</span>
|
||||||
|
<input
|
||||||
|
name="cumulative_loss_limit_usd"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
value="{{ cumulative_loss_limit_value }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Max source latency (ms)</span>
|
||||||
|
<input
|
||||||
|
name="max_source_latency_ms"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="1"
|
||||||
|
value="{{ max_source_latency_value }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Max apply latency (ms)</span>
|
||||||
|
<input
|
||||||
|
name="max_apply_latency_ms"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="1"
|
||||||
|
value="{{ max_apply_latency_value }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Max consecutive failures</span>
|
||||||
|
<input
|
||||||
|
name="max_consecutive_failures"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="1"
|
||||||
|
value="{{ max_consecutive_failures_value }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field checkbox">
|
||||||
|
<input name="kill_switch_active" type="checkbox" {{ kill_switch_active }} />
|
||||||
|
<span>Kill switch active</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
<div class="card">
|
||||||
|
<div class="label">Runtime</div>
|
||||||
|
<label class="field">
|
||||||
|
<span>App env</span>
|
||||||
|
<input type="text" value="{{ app_env }}" disabled />
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>App host</span>
|
||||||
|
<input name="app_host" type="text" value="{{ app_host }}" />
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>App port</span>
|
||||||
|
<input
|
||||||
|
name="app_port"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="65535"
|
||||||
|
value="{{ app_port }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Log level</span>
|
||||||
|
<select name="log_level">
|
||||||
|
{% for lvl in ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] %} {% set
|
||||||
|
sel = "selected" if log_level == lvl else "" %}
|
||||||
|
<option value="{{ lvl }}" {{ sel }}>{{ lvl }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label class="field checkbox">
|
||||||
|
<input name="log_json" type="checkbox" {{ log_json }} />
|
||||||
|
<span>JSON logs</span>
|
||||||
|
</label>
|
||||||
|
<label class="field checkbox">
|
||||||
|
<input name="paper_trading_mode" type="checkbox" {{ paper_trading_mode }} />
|
||||||
|
<span>Paper trading mode</span>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Trade capital USD</span>
|
||||||
|
<input
|
||||||
|
name="trade_capital_usd"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
value="{{ trade_capital_usd_value }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Max trade capital USD</span>
|
||||||
|
<input
|
||||||
|
name="max_trade_capital_usd"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
value="{{ max_trade_capital_usd_value }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Max concurrent trades</span>
|
||||||
|
<input
|
||||||
|
name="max_concurrent_trades"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
step="1"
|
||||||
|
value="{{ max_concurrent_trades_value }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Max exposure per asset USD</span>
|
||||||
|
<input
|
||||||
|
name="max_exposure_per_asset_usd"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
value="{{ max_exposure_per_asset_value }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Quote balance asset</span>
|
||||||
|
<input
|
||||||
|
name="quote_balance_asset"
|
||||||
|
type="text"
|
||||||
|
value="{{ quote_balance_asset }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Min order size USD</span>
|
||||||
|
<input
|
||||||
|
name="min_order_size_usd"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
value="{{ min_order_size_usd_value }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Tradable pairs (comma-separated)</span>
|
||||||
|
<input
|
||||||
|
name="tradable_pairs"
|
||||||
|
type="text"
|
||||||
|
placeholder="BTC/USD, ETH/BTC"
|
||||||
|
value="{{ tradable_pairs_value }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Strategy mode</span>
|
||||||
|
<select name="strategy_mode">
|
||||||
|
{% set sel = "selected" if strategy_mode == "incremental" else "" %}
|
||||||
|
<option value="incremental" {{ sel }}>incremental</option>
|
||||||
|
{% set sel = "selected" if strategy_mode == "paper" else "" %}
|
||||||
|
<option value="paper" {{ sel }}>paper</option>
|
||||||
|
{% set sel = "selected" if strategy_mode == "live" else "" %}
|
||||||
|
<option value="live" {{ sel }}>live</option>
|
||||||
|
{% if strategy_stat_arb_enabled %} {% set sel = "selected" if
|
||||||
|
strategy_mode == "stat_arb_experiment" else "" %}
|
||||||
|
<option value="stat_arb_experiment" {{ sel }}>stat_arb_experiment</option>
|
||||||
|
{% endif %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Strategy profit threshold</span>
|
||||||
|
<input
|
||||||
|
name="strategy_profit_threshold"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.0001"
|
||||||
|
value="{{ strategy_profit_threshold }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span>Max depth levels</span>
|
||||||
|
<input
|
||||||
|
name="strategy_max_depth_levels"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
step="1"
|
||||||
|
value="{{ strategy_max_depth_levels }}"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
{% extends "_base.html" %} {% block title %}{{ title }}{% endblock %} {% block
|
||||||
|
main_class %}shell{% endblock %} {% block header %} {% with
|
||||||
|
page_title="Currency Pairings",
|
||||||
|
page_subtitle="Enable/disable pairings, search, and sync from Kraken." %}
|
||||||
|
{% include "_header.html" %} {% endwith %} {% endblock %} {% block content %}
|
||||||
|
|
||||||
|
<div class="toolbar" style="margin-bottom: 16px; display: flex; gap: 8px">
|
||||||
|
<input
|
||||||
|
id="pairing-search"
|
||||||
|
type="text"
|
||||||
|
placeholder="Search pairings…"
|
||||||
|
value="{{ search or '' }}"
|
||||||
|
style="flex: 1; max-width: 300px"
|
||||||
|
hx-get="/dashboard/fragment/pairings"
|
||||||
|
hx-target="#pairings-table-container"
|
||||||
|
hx-trigger="keyup changed delay:300ms"
|
||||||
|
hx-include="#pairing-enabled-filter"
|
||||||
|
name="search"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<select
|
||||||
|
id="pairing-enabled-filter"
|
||||||
|
name="enabled"
|
||||||
|
hx-get="/dashboard/fragment/pairings"
|
||||||
|
hx-target="#pairings-table-container"
|
||||||
|
hx-trigger="change"
|
||||||
|
hx-include="#pairing-search"
|
||||||
|
>
|
||||||
|
<option value="all">All</option>
|
||||||
|
<option value="true" {{ 'selected' if enabled == 'true' else '' }}>
|
||||||
|
Enabled
|
||||||
|
</option>
|
||||||
|
<option value="false" {{ 'selected' if enabled == 'false' else '' }}>
|
||||||
|
Disabled
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
hx-post="/dashboard/api/pairings/sync"
|
||||||
|
hx-target="#pairings-table-container"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
Sync from Kraken
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="pairings-table-container">
|
||||||
|
{% include "partials/pairings_table.html" %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -4,657 +4,14 @@
|
|||||||
hx-post="{{ config_endpoint }}"
|
hx-post="{{ config_endpoint }}"
|
||||||
hx-target="#config-panel"
|
hx-target="#config-panel"
|
||||||
hx-swap="outerHTML"
|
hx-swap="outerHTML"
|
||||||
style="
|
style="grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 20px"
|
||||||
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
|
||||||
gap: 20px;
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<!-- Runtime -->
|
{% include "config/runtime.html" %}
|
||||||
<div class="card">
|
{% include "config/alerts.html" %}
|
||||||
<div class="label">Runtime</div>
|
{% include "config/kraken.html" %}
|
||||||
<label class="field">
|
{% include "config/risk.html" %}
|
||||||
<span>App env</span>
|
<div style="grid-column: 1 / -1">
|
||||||
<input type="text" value="{{ app_env }}" disabled />
|
<button type="submit" class="button">Save Settings</button>
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>App host</span>
|
|
||||||
<input name="app_host" type="text" value="{{ app_host }}" />
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>App port</span>
|
|
||||||
<input
|
|
||||||
name="app_port"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
max="65535"
|
|
||||||
value="{{ app_port }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Log level</span>
|
|
||||||
<select name="log_level">
|
|
||||||
{% for lvl in ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] %} {%
|
|
||||||
set sel = "selected" if log_level == lvl else "" %}
|
|
||||||
<option value="{{ lvl }}" {{ sel }}>{{ lvl }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
<label class="field checkbox">
|
|
||||||
<input name="log_json" type="checkbox" {{ log_json }} />
|
|
||||||
<span>JSON logs</span>
|
|
||||||
</label>
|
|
||||||
<label class="field checkbox">
|
|
||||||
<input
|
|
||||||
name="paper_trading_mode"
|
|
||||||
type="checkbox"
|
|
||||||
{{
|
|
||||||
paper_trading_mode
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span>Paper trading mode</span>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Trade capital USD</span>
|
|
||||||
<input
|
|
||||||
name="trade_capital_usd"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="0.01"
|
|
||||||
value="{{ trade_capital_usd_value }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Max trade capital USD</span>
|
|
||||||
<input
|
|
||||||
name="max_trade_capital_usd"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="0.01"
|
|
||||||
value="{{ max_trade_capital_usd_value }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Max concurrent trades</span>
|
|
||||||
<input
|
|
||||||
name="max_concurrent_trades"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
step="1"
|
|
||||||
value="{{ max_concurrent_trades_value }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Max exposure per asset USD</span>
|
|
||||||
<input
|
|
||||||
name="max_exposure_per_asset_usd"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="0.01"
|
|
||||||
value="{{ max_exposure_per_asset_value }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Quote balance asset</span>
|
|
||||||
<input
|
|
||||||
name="quote_balance_asset"
|
|
||||||
type="text"
|
|
||||||
value="{{ quote_balance_asset }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Min order size USD</span>
|
|
||||||
<input
|
|
||||||
name="min_order_size_usd"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="0.01"
|
|
||||||
value="{{ min_order_size_usd_value }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Tradable pairs (comma-separated)</span>
|
|
||||||
<input
|
|
||||||
name="tradable_pairs"
|
|
||||||
type="text"
|
|
||||||
placeholder="BTC/USD, ETH/BTC"
|
|
||||||
value="{{ tradable_pairs_value }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Strategy mode</span>
|
|
||||||
<select name="strategy_mode">
|
|
||||||
{% set sel = "selected" if strategy_mode == "incremental" else "" %}
|
|
||||||
<option value="incremental" {{ sel }}>incremental</option>
|
|
||||||
{% set sel = "selected" if strategy_mode == "paper" else "" %}
|
|
||||||
<option value="paper" {{ sel }}>paper</option>
|
|
||||||
{% set sel = "selected" if strategy_mode == "live" else "" %}
|
|
||||||
<option value="live" {{ sel }}>live</option>
|
|
||||||
{% if strategy_stat_arb_enabled %} {% set sel = "selected" if
|
|
||||||
strategy_mode == "stat_arb_experiment" else "" %}
|
|
||||||
<option value="stat_arb_experiment" {{ sel }}>
|
|
||||||
stat_arb_experiment
|
|
||||||
</option>
|
|
||||||
{% endif %}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Strategy profit threshold</span>
|
|
||||||
<input
|
|
||||||
name="strategy_profit_threshold"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="0.0001"
|
|
||||||
value="{{ strategy_profit_threshold }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Max depth levels</span>
|
|
||||||
<input
|
|
||||||
name="strategy_max_depth_levels"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
step="1"
|
|
||||||
value="{{ strategy_max_depth_levels }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Alerts -->
|
|
||||||
<div class="card">
|
|
||||||
<div class="label">Alerting</div>
|
|
||||||
<label class="field checkbox">
|
|
||||||
<input name="alerts_enabled" type="checkbox" {{ alerts_enabled }} />
|
|
||||||
<span>Alerts enabled</span>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Min severity</span>
|
|
||||||
<select name="alert_min_severity">
|
|
||||||
{% for sev in ["info", "warning", "error", "critical"] %} {% set sel =
|
|
||||||
"selected" if alert_min_severity == sev else "" %}
|
|
||||||
<option value="{{ sev }}" {{ sel }}>{{ sev }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Dedup seconds</span>
|
|
||||||
<input
|
|
||||||
name="alert_dedup_seconds"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="1"
|
|
||||||
value="{{ alert_dedup_seconds }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field checkbox">
|
|
||||||
<input
|
|
||||||
name="alert_on_trade_events"
|
|
||||||
type="checkbox"
|
|
||||||
{{
|
|
||||||
alert_on_trade_events
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span>Trade events</span>
|
|
||||||
</label>
|
|
||||||
<label class="field checkbox">
|
|
||||||
<input
|
|
||||||
name="alert_on_error_events"
|
|
||||||
type="checkbox"
|
|
||||||
{{
|
|
||||||
alert_on_error_events
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span>Error events</span>
|
|
||||||
</label>
|
|
||||||
<label class="field checkbox">
|
|
||||||
<input
|
|
||||||
name="alert_on_threshold_events"
|
|
||||||
type="checkbox"
|
|
||||||
{{
|
|
||||||
alert_on_threshold_events
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span>Threshold events</span>
|
|
||||||
</label>
|
|
||||||
<label class="field checkbox">
|
|
||||||
<input
|
|
||||||
name="alert_on_system_events"
|
|
||||||
type="checkbox"
|
|
||||||
{{
|
|
||||||
alert_on_system_events
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span>System events</span>
|
|
||||||
</label>
|
|
||||||
<hr
|
|
||||||
style="
|
|
||||||
border: none;
|
|
||||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
margin: 12px 0;
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<label class="field checkbox">
|
|
||||||
<input
|
|
||||||
name="telegram_alerts_enabled"
|
|
||||||
type="checkbox"
|
|
||||||
{{
|
|
||||||
telegram_alerts_enabled
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span>Telegram</span>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Telegram bot token</span>
|
|
||||||
<input
|
|
||||||
name="telegram_bot_token"
|
|
||||||
type="password"
|
|
||||||
value="{{ telegram_bot_token }}"
|
|
||||||
placeholder="Bot token"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Telegram chat ID</span>
|
|
||||||
<input
|
|
||||||
name="telegram_chat_id"
|
|
||||||
type="text"
|
|
||||||
value="{{ telegram_chat_id }}"
|
|
||||||
placeholder="Chat ID"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<hr
|
|
||||||
style="
|
|
||||||
border: none;
|
|
||||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
margin: 12px 0;
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<label class="field checkbox">
|
|
||||||
<input
|
|
||||||
name="discord_alerts_enabled"
|
|
||||||
type="checkbox"
|
|
||||||
{{
|
|
||||||
discord_alerts_enabled
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span>Discord</span>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Discord webhook URL</span>
|
|
||||||
<input
|
|
||||||
name="discord_webhook_url"
|
|
||||||
type="password"
|
|
||||||
value="{{ discord_webhook_url }}"
|
|
||||||
placeholder="Webhook URL"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<hr
|
|
||||||
style="
|
|
||||||
border: none;
|
|
||||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
margin: 12px 0;
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<label class="field checkbox">
|
|
||||||
<input
|
|
||||||
name="email_alerts_enabled"
|
|
||||||
type="checkbox"
|
|
||||||
{{
|
|
||||||
email_alerts_enabled
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span>Email</span>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>SMTP host</span>
|
|
||||||
<input
|
|
||||||
name="email_smtp_host"
|
|
||||||
type="text"
|
|
||||||
value="{{ email_smtp_host }}"
|
|
||||||
placeholder="smtp.example.com"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>SMTP port</span>
|
|
||||||
<input
|
|
||||||
name="email_smtp_port"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
max="65535"
|
|
||||||
value="{{ email_smtp_port }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>SMTP username</span>
|
|
||||||
<input
|
|
||||||
name="email_smtp_username"
|
|
||||||
type="text"
|
|
||||||
value="{{ email_smtp_username }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>SMTP password</span>
|
|
||||||
<input
|
|
||||||
name="email_smtp_password"
|
|
||||||
type="password"
|
|
||||||
value="{{ email_smtp_password }}"
|
|
||||||
placeholder="Leave blank to keep existing"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>From address</span>
|
|
||||||
<input
|
|
||||||
name="email_alert_from"
|
|
||||||
type="text"
|
|
||||||
value="{{ email_alert_from }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>To address</span>
|
|
||||||
<input name="email_alert_to" type="text" value="{{ email_alert_to }}" />
|
|
||||||
</label>
|
|
||||||
<label class="field checkbox">
|
|
||||||
<input
|
|
||||||
name="email_smtp_use_tls"
|
|
||||||
type="checkbox"
|
|
||||||
{{
|
|
||||||
email_smtp_use_tls
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span>Use TLS</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Kraken -->
|
|
||||||
<div class="card">
|
|
||||||
<div class="label">Kraken Exchange</div>
|
|
||||||
<label class="field">
|
|
||||||
<span>REST URL</span>
|
|
||||||
<input
|
|
||||||
name="kraken_rest_url"
|
|
||||||
type="text"
|
|
||||||
value="{{ kraken_rest_url }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>WebSocket URL</span>
|
|
||||||
<input name="kraken_ws_url" type="text" value="{{ kraken_ws_url }}" />
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Private rate limit (s)</span>
|
|
||||||
<input
|
|
||||||
name="kraken_private_rate_limit_seconds"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="0.01"
|
|
||||||
value="{{ kraken_private_rate_limit_seconds }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>HTTP timeout (s)</span>
|
|
||||||
<input
|
|
||||||
name="kraken_http_timeout_seconds"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
step="0.5"
|
|
||||||
value="{{ kraken_http_timeout_seconds }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Retry attempts</span>
|
|
||||||
<input
|
|
||||||
name="kraken_retry_attempts"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="1"
|
|
||||||
value="{{ kraken_retry_attempts }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Retry base delay (s)</span>
|
|
||||||
<input
|
|
||||||
name="kraken_retry_base_delay_seconds"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="0.01"
|
|
||||||
value="{{ kraken_retry_base_delay_seconds }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>API key</span>
|
|
||||||
<input
|
|
||||||
name="kraken_api_key"
|
|
||||||
type="text"
|
|
||||||
value="{{ kraken_api_key }}"
|
|
||||||
placeholder="API key"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>API secret</span>
|
|
||||||
<input
|
|
||||||
name="kraken_api_secret"
|
|
||||||
type="password"
|
|
||||||
value="{{ kraken_api_secret }}"
|
|
||||||
placeholder="API secret"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Key permissions</span>
|
|
||||||
<input
|
|
||||||
name="kraken_api_key_permissions"
|
|
||||||
type="text"
|
|
||||||
value="{{ kraken_api_key_permissions }}"
|
|
||||||
placeholder="query,trade"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Heartbeat timeout (s)</span>
|
|
||||||
<input
|
|
||||||
name="ws_heartbeat_timeout_seconds"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
step="1"
|
|
||||||
value="{{ ws_heartbeat_timeout_seconds }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Max staleness (s)</span>
|
|
||||||
<input
|
|
||||||
name="ws_max_staleness_seconds"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="0.5"
|
|
||||||
value="{{ ws_max_staleness_seconds }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Pairings -->
|
|
||||||
<div class="card" id="pairings-card">
|
|
||||||
<div class="label">Currency Pairings</div>
|
|
||||||
<div style="display: flex; gap: 8px; margin-bottom: 12px">
|
|
||||||
<input
|
|
||||||
id="pairing-search"
|
|
||||||
type="text"
|
|
||||||
placeholder="Search pairings..."
|
|
||||||
hx-get="/dashboard/fragment/pairings"
|
|
||||||
hx-target="#pairings-table-container"
|
|
||||||
hx-trigger="keyup changed delay:300ms"
|
|
||||||
hx-swap="innerHTML"
|
|
||||||
name="search"
|
|
||||||
style="
|
|
||||||
flex: 1;
|
|
||||||
padding: 6px 10px;
|
|
||||||
border-radius: 6px;
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
||||||
background: rgba(0, 0, 0, 0.3);
|
|
||||||
color: inherit;
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="button"
|
|
||||||
id="pairing-sync-btn"
|
|
||||||
hx-post="/dashboard/api/pairings/sync"
|
|
||||||
hx-target="#pairings-table-container"
|
|
||||||
hx-swap="innerHTML"
|
|
||||||
hx-trigger="click"
|
|
||||||
style="white-space: nowrap"
|
|
||||||
>
|
|
||||||
Sync from Kraken
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
id="pairings-table-container"
|
|
||||||
hx-get="/dashboard/fragment/pairings"
|
|
||||||
hx-trigger="load"
|
|
||||||
>
|
|
||||||
<div style="text-align: center; padding: 20px; opacity: 0.5">
|
|
||||||
Loading pairings...
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Risk -->
|
|
||||||
<div class="card">
|
|
||||||
<div class="label">Risk Limits</div>
|
|
||||||
<label class="field">
|
|
||||||
<span>Daily loss limit USD</span>
|
|
||||||
<input
|
|
||||||
name="daily_loss_limit_usd"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="0.01"
|
|
||||||
value="{{ daily_loss_limit_value }}"
|
|
||||||
placeholder="None"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Cumulative loss limit USD</span>
|
|
||||||
<input
|
|
||||||
name="cumulative_loss_limit_usd"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="0.01"
|
|
||||||
value="{{ cumulative_loss_limit_value }}"
|
|
||||||
placeholder="None"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Max source latency (ms)</span>
|
|
||||||
<input
|
|
||||||
name="max_source_latency_ms"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="1"
|
|
||||||
value="{{ max_source_latency_value }}"
|
|
||||||
placeholder="None"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Max apply latency (ms)</span>
|
|
||||||
<input
|
|
||||||
name="max_apply_latency_ms"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="1"
|
|
||||||
value="{{ max_apply_latency_value }}"
|
|
||||||
placeholder="None"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Max consecutive failures</span>
|
|
||||||
<input
|
|
||||||
name="max_consecutive_failures"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
step="1"
|
|
||||||
value="{{ max_consecutive_failures_value }}"
|
|
||||||
placeholder="None"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field checkbox">
|
|
||||||
<input
|
|
||||||
name="kill_switch_active"
|
|
||||||
type="checkbox"
|
|
||||||
{{
|
|
||||||
kill_switch_active
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span>Kill switch active</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Strategy Stat-Arb -->
|
|
||||||
<div class="card">
|
|
||||||
<div class="label">Stat-Arb Strategy</div>
|
|
||||||
<label class="field checkbox">
|
|
||||||
<input
|
|
||||||
name="strategy_enable_stat_arb_experiment"
|
|
||||||
type="checkbox"
|
|
||||||
{%
|
|
||||||
if
|
|
||||||
strategy_stat_arb_enabled
|
|
||||||
%}checked{%
|
|
||||||
endif
|
|
||||||
%}
|
|
||||||
/>
|
|
||||||
<span>Enable stat-arb experiment</span>
|
|
||||||
</label>
|
|
||||||
{% if strategy_stat_arb_enabled %}
|
|
||||||
<label class="field">
|
|
||||||
<span>Lookback window</span>
|
|
||||||
<input
|
|
||||||
name="strategy_stat_arb_lookback_window"
|
|
||||||
type="number"
|
|
||||||
min="2"
|
|
||||||
step="1"
|
|
||||||
value="{{ strategy_stat_arb_lookback_window }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Entry z-score</span>
|
|
||||||
<input
|
|
||||||
name="strategy_stat_arb_entry_zscore"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="0.1"
|
|
||||||
value="{{ strategy_stat_arb_entry_zscore }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Exit z-score</span>
|
|
||||||
<input
|
|
||||||
name="strategy_stat_arb_exit_zscore"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="0.1"
|
|
||||||
value="{{ strategy_stat_arb_exit_zscore }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="field">
|
|
||||||
<span>Max holding seconds</span>
|
|
||||||
<input
|
|
||||||
name="strategy_stat_arb_max_holding_seconds"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
step="1"
|
|
||||||
value="{{ strategy_stat_arb_max_holding_seconds }}"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Submit -->
|
|
||||||
<div
|
|
||||||
class="card"
|
|
||||||
style="display: flex; align-items: center; justify-content: center"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="button"
|
|
||||||
style="padding: 14px 32px; font-size: 1.1rem"
|
|
||||||
>
|
|
||||||
Save configuration
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user