Skill: yannlong/ifind (version 1.0.0)

--- name: ifind description: | Use a local Python wrapper around the official iFinD QuantAPI HTTP endpoints on quantapi.51ifind.com. Use when the user wants iFinD market, macro, fund, code-conversion, calendar, report, or portfolio data through HTTP without downloading third-party binaries. This skill is especially appropriate when a local `ifind_api.py` wrapper or custom iFinD HTTP integration should be reused, and when refresh_token acquisition and secure local storage must be checked before any API call. --- # iFinD Use this skill to query iFinD via the local Python wrapper in `scripts/ifind_api.py`. ## Environment preparation Before first use, verify Python dependencies. Required runtime dependency: - `requests` A pinned dependency file is provided at `scripts/requirements.txt`. Check quickly with: ```bash python3 -c "import requests; print(requests.__version__)" ``` If missing, install it before running any API script: ```bash python3 -m pip install -r scripts/requirements.txt ``` If the environment is externally managed, prefer a virtual environment, but installing `requests` is allowed for this skill when needed. ## First rule: confirm refresh_token before querying Before any data call: 1. Check token status with: `python3 scripts/ifind_token_store.py status` 2. If missing, prefer letting the agent obtain it directly from the official site: - open `https://quantapi.51ifind.com` - log in with the user's existing session, or ask the user to complete login if interactive approval is needed - go to the account information page - locate the `refresh_token` 3. Store it only through: `python3 scripts/ifind_token_store.py set --token '<TOKEN>'` 4. Only fall back to asking the user to provide the token if the browser path is unavailable or login cannot be completed. Never ask the user to paste shell commands that would echo the token back into chat. Never print the token after storing it. ## Preferred token acquisition workflow Prefer this order: 1. Browser login to `https://quantapi.51ifind.com` and read the token from account information 2. iFinD 超级命令客户端 → 工具 → `refresh_token` 查询 3. Ask the user directly only as a fallback If browser automation is used, keep the flow minimal and do not browse unrelated pages. Once the token is found, store it immediately and stop exposing it in further output. ## Token storage policy - Storage path: `~/.openclaw/skills/ifind/credentials.json` - File permission target: owner read/write only (`600` on POSIX) - The store script writes the file and tightens permissions automatically. - The request script reads the refresh_token from that file unless `IFIND_REFRESH_TOKEN` is already present in the environment. - Prefer the stored token over hardcoding tokens into code or notes. ## Quick workflow ### 1. Verify dependencies and token state Run: ```bash python3 -c "import requests; print(requests.__version__)" python3 scripts/ifind_token_store.py status ``` If `requests` is missing, install it first: ```bash python3 -m pip install -r scripts/requirements.txt ``` If the token is missing, obtain it through the preferred browser workflow and store it first. ### 2. Choose a calling path - For direct endpoint calls, use `scripts/ifind_request.py endpoint` - For common market-data work, use `scripts/ifind_request.py preset` - For Python integration or patching, read `scripts/ifind_api.py` - For endpoint details and payload shape, read `references/iFind_API_Reference.md` ### 3. Run the query Examples: ```bash python3 scripts/ifind_request.py preset realtime \ --codes '300033.SZ,600000.SH' \ --indicators 'open,high,low,latest,changeRatio,volume,amount' python3 scripts/ifind_request.py preset ohlcv \ --codes '000300.SH' \ --startdate '2025-01-01' \ --enddate '2025-03-01' python3 scripts/ifind_request.py endpoint get_thscode \ --payload '{"seccode":"300033","functionpara":{"mode":"seccode","sectype":"","market":"","tradestatus":"0","isexact":"0"}}' ``` ## Output discipline When using returned data in analysis: - state the endpoint used - include the symbol universe / code list - include the requested date range or timestamp - include the returned `perf`, `dataVol`, or other useful metadata when relevant - if the API returns an error, surface the error code and message rather than guessing ## References - `references/iFind_API_Reference.md`: local API wrapper reference and examples from the user - `references/token-and-storage.md`: refresh_token acquisition and storage procedure - `scripts/ifind_api.py`: local Python wrapper - `scripts/ifind_request.py`: CLI for endpoint/preset calls - `scripts/ifind_token_store.py`: token status/set/remove helper

path: scripts/requirements.txt
content: requests>=2.31.0


path: scripts/ifind_request.py
content: #!/usr/bin/env python3
import argparse
import json
from ifind_api import IFindAPI, IFindHistoryData, IFindRealtimeData
from ifind_token_store import load_refresh_token


def build_client(kind: str):
    [REDACTED_SECRET])
    if kind == 'history':
        return IFindHistoryData(token)
    if kind == 'realtime':
        return IFindRealtimeData(token)
    return IFindAPI(token)


def run_endpoint(args) -> dict:
    api = build_client('base')
    payload = json.loads(args.payload)
    return api._call_api(args.endpoint, payload)


def run_preset(args) -> dict:
    if args.preset == 'realtime':
        api = build_client('realtime')
        return api.get_realtime(args.codes, args.indicators)
    if args.preset == 'ohlcv':
        api = build_client('history')
        return api.get_ohlc(
            codes=args.codes,
            startdate=args.startdate,
            enddate=args.enddate,
            adjust=args.adjust,
            period=args.period,
        )
    if args.preset == 'edb':
        api = build_client('base')
        return api.get_edb_data(
            indicators=args.indicators,
            startdate=args.startdate,
            enddate=args.enddate,
        )
    if args.preset == 'thscode':
        api = build_client('base')
        if args.seccode:
            return api.get_thscode(seccode=args.seccode, mode='seccode', isexact=args.isexact)
        return api.get_thscode(secname=args.secname, mode='secname', isexact=args.isexact)
    raise SystemExit(f'unsupported preset: {args.preset}')


def main() -> int:
    parser = argparse.ArgumentParser(description='Call iFinD QuantAPI via local wrapper')
    sub = parser.add_subparsers(dest='command', required=True)

    endpoint = sub.add_parser('endpoint')
    endpoint.add_argument('endpoint')
    endpoint.add_argument('--payload', required=True, help='JSON payload string')

    preset = sub.add_parser('preset')
    preset.add_argument('preset', choices=['realtime', 'ohlcv', 'edb', 'thscode'])
    preset.add_argument('--codes')
    preset.add_argument('--indicators')
    preset.add_argument('--startdate')
    preset.add_argument('--enddate')
    preset.add_argument('--adjust', default='1')
    preset.add_argument('--period', default='D')
    preset.add_argument('--seccode')
    preset.add_argument('--secname')
    preset.add_argument('--isexact', default='0')

    args = parser.parse_args()
    if args.command == 'endpoint':
        result = run_endpoint(args)
    else:
        result = run_preset(args)
    print(json.dumps(result, ensure_ascii=False, indent=2))
    return 0


if __name__ == '__main__':
    raise SystemExit(main())


path: scripts/ifind_token_store.py
content: #!/usr/bin/env python3
import argparse
import json
import os
import stat
from datetime import datetime, timezone
from pathlib import Path

STORE_DIR = Path.home() / '.openclaw' / 'skills' / 'ifind'
STORE_PATH = STORE_DIR / 'credentials.json'


def _ensure_dir() -> None:
    STORE_DIR.mkdir(parents=True, exist_ok=True)


def _chmod_owner_only(path: Path) -> None:
    try:
        path.chmod(stat.S_IRUSR | stat.S_IWUSR)
    except OSError:
        pass


def status() -> int:
    env_token = os.environ.get('IFIND_REFRESH_TOKEN')
    if env_token:
        print(json.dumps({
            'status': 'ok',
            'source': 'environment',
            'path': None,
            'message': 'refresh_token is available via IFIND_REFRESH_TOKEN'
        }, ensure_ascii=False))
        return 0

    if STORE_PATH.exists():
        print(json.dumps({
            'status': 'ok',
            'source': 'file',
            'path': str(STORE_PATH),
            'message': 'refresh_token is stored locally'
        }, ensure_ascii=False))
        return 0

    print(json.dumps({
        'status': 'missing',
        'source': None,
        'path': str(STORE_PATH),
        'message': 'refresh_token is not configured'
    }, ensure_ascii=False))
    return 1


def set_token(token: str) -> int:
    [REDACTED_SECRET])
    if not token:
        raise SystemExit('token is empty')
    _ensure_dir()
    payload = {
        'refresh_token': token,
        'updated_at': datetime.now(timezone.utc).isoformat()
    }
    STORE_PATH.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding='utf-8')
    _chmod_owner_only(STORE_PATH)
    print(json.dumps({
        'status': 'ok',
        'path': str(STORE_PATH),
        'message': 'refresh_token stored'
    }, ensure_ascii=False))
    return 0


def remove_token() -> int:
    if STORE_PATH.exists():
        STORE_PATH.unlink()
    print(json.dumps({
        'status': 'ok',
        'path': str(STORE_PATH),
        'message': 'refresh_token removed'
    }, ensure_ascii=False))
    return 0


def load_refresh_token() -> str:
    env_token = os.environ.get('IFIND_REFRESH_TOKEN')
    if env_token:
        return env_token.strip()
    if not STORE_PATH.exists():
        raise RuntimeError(f'missing refresh_token, expected at {STORE_PATH}')
    data = json.loads(STORE_PATH.read_text(encoding='utf-8'))
    [REDACTED_SECRET]'refresh_token') or '').strip()
    if not token:
        raise RuntimeError(f'empty refresh_token in {STORE_PATH}')
    return token


def main() -> int:
    parser = argparse.ArgumentParser(description='Manage iFinD refresh_token storage')
    sub = parser.add_subparsers(dest='command', required=True)

    sub.add_parser('status')
    p_set = sub.add_parser('set')
    p_set.add_argument('--token', required=True)
    sub.add_parser('remove')

    args = parser.parse_args()
    if args.command == 'status':
        return status()
    if args.command == 'set':
        return set_token(args.token)
    if args.command == 'remove':
        return remove_token()
    raise SystemExit(2)


if __name__ == '__main__':
    raise SystemExit(main())


path: scripts/ifind_api.py
content: from typing import Optional, Dict, List, Any
from datetime import datetime, timedelta
import requests
import time
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

BASE_URL = "https://quantapi.51ifind.com/api/v1"


class IFindAPI:
    def __init__(self, refresh_token: str):
        self.refresh_token = refresh_token
        self.access_token: Optional[str] = None
        self.access_token_expires: Optional[datetime] = None

    def get_access_token(self) -> str:
        if self.access_token and self.access_token_expires:
            if datetime.now() < self.access_token_expires - timedelta(hours=1):
                return self.access_token

        headers = {
            "refresh_token": self.refresh_token,
            "Content-Type": "application/json",
        }

        response = requests.post(
            f"{BASE_URL}/get_access_token", headers=headers, timeout=30
        )
        response.raise_for_status()
        result = response.json()
        if result.get("errorcode") == 0:
            self.access_token = result.get("data", {}).get("access_token")
            self.access_token_expires = datetime.now() + timedelta(days=6)
            logger.info("成功获取access_token,有效期至: %s", self.access_token_expires)
            return self.access_token
        raise RuntimeError(f"获取access_token失败: {result.get('errmsg')}")

    def update_access_token(self) -> str:
        headers = {
            "refresh_token": self.refresh_token,
            "Content-Type": "application/json",
        }
        response = requests.post(
            f"{BASE_URL}/update_access_token", headers=headers, timeout=30
        )
        response.raise_for_status()
        result = response.json()
        if result.get("errorcode") == 0:
            self.access_token = result.get("data", {}).get("access_token")
            self.access_token_expires = datetime.now() + timedelta(days=6)
            logger.info("成功更新access_token,有效期至: %s", self.access_token_expires)
            return self.access_token
        raise RuntimeError(f"更新access_token失败: {result.get('errmsg')}")

    def _call_api(
        self, endpoint: str, payload: Dict[str, Any], max_retries: int = 3
    ) -> Dict[str, Any]:
        [REDACTED_SECRET])
        headers = {"access_token": token, "Content-Type": "application/json"}
        url = f"{BASE_URL}/{endpoint}"

        for attempt in range(max_retries):
            try:
                response = requests.post(url, headers=headers, json=payload, timeout=60)
                response.raise_for_status()
                result = response.json()

                if result.get("errorcode") == 0:
                    return result

                error_msg = result.get("errmsg", "未知错误")
                logger.warning("API调用失败: %s", error_msg)
                if "token" in error_msg.lower() or "expired" in error_msg.lower():
                    self.access_token = None
                    self.access_token_expires = None
                    [REDACTED_SECRET])
                    headers["access_token"] = token

                if attempt < max_retries - 1:
                    time.sleep(2**attempt)
                    continue
                raise RuntimeError(f"API调用失败: {error_msg}")

            except requests.exceptions.Timeout:
                if attempt < max_retries - 1:
                    time.sleep(2**attempt)
                    continue
                raise
            except requests.exceptions.RequestException:
                if attempt < max_retries - 1:
                    time.sleep(2**attempt)
                    continue
                raise

        raise RuntimeError("API调用失败,已达到最大重试次数")

    def get_basic_data(self, codes: str, indipara: List[Dict[str, Any]]) -> Dict[str, Any]:
        return self._call_api("basic_data_service", {"codes": codes, "indipara": indipara})

    def get_date_sequence(
        self,
        codes: str,
        startdate: str,
        enddate: str,
        indipara: Optional[List[Dict[str, Any]]] = None,
        functionpara: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        payload = {"codes": codes, "startdate": startdate, "enddate": enddate}
        if indipara:
            payload["indipara"] = indipara
        if functionpara:
            payload["functionpara"] = functionpara
        return self._call_api("date_sequence", payload)

    def get_history_data(
        self,
        codes: str,
        startdate: str,
        enddate: str,
        indicators: str,
        functionpara: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        payload = {
            "reqBody": {
                "codes": codes,
                "startdate": startdate,
                "enddate": enddate,
                "indicators": indicators,
            }
        }
        if functionpara:
            payload["reqBody"]["functionpara"] = functionpara
        return self._call_api("history_data", payload)

    def get_high_frequency(
        self,
        codes: str,
        indicators: str,
        starttime: str,
        endtime: str,
        functionpara: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        payload = {
            "codes": codes,
            "indicators": indicators,
            "starttime": starttime,
            "endtime": endtime,
        }
        if functionpara:
            payload["functionpara"] = functionpara
        return self._call_api("high_frequency", payload)

    def get_real_time_quotation(self, codes: str, indicators: str, functionpara: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        payload = {"codes": codes, "indicators": indicators}
        if functionpara:
            payload["functionpara"] = functionpara
        return self._call_api("real_time_quotation", payload)

    def get_snap_shot(self, codes: str, indicators: str, starttime: str, endtime: str) -> Dict[str, Any]:
        return self._call_api("snap_shot", {"codes": codes, "indicators": indicators, "starttime": starttime, "endtime": endtime})

    def get_edb_data(self, indicators: str, startdate: str, enddate: str, functionpara: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        payload = {"reqBody": {"indicators": indicators, "startdate": startdate, "enddate": enddate}}
        if functionpara:
            payload["reqBody"]["functionpara"] = functionpara
        return self._call_api("edb_service", payload)

    def get_data_pool(self, reportname: str, functionpara: Dict[str, Any], outputpara: Optional[str] = None) -> Dict[str, Any]:
        payload = {"reqBody": {"reportname": reportname, "functionpara": functionpara}}
        if outputpara:
            payload["reqBody"]["outputpara"] = outputpara
        return self._call_api("data_pool", payload)

    def get_data_volume(self) -> Dict[str, Any]:
        [REDACTED_SECRET])
        headers = {"access_token": token, "Content-Type": "application/json"}
        response = requests.get(f"{BASE_URL}/get_data_volume", headers=headers, timeout=30)
        response.raise_for_status()
        return response.json()

    def get_error_message(self, errorcode: int) -> Dict[str, Any]:
        return self._call_api("get_error_message", {"reqBody": {"errorcode": errorcode}})

    def get_thscode(
        self,
        seccode: Optional[str] = None,
        secname: Optional[str] = None,
        mode: str = "seccode",
        sectype: str = "",
        market: str = "",
        tradestatus: str = "0",
        isexact: str = "0",
    ) -> Dict[str, Any]:
        functionpara = {
            "mode": mode,
            "sectype": sectype,
            "market": market,
            "tradestatus": tradestatus,
            "isexact": isexact,
        }
        payload = {"functionpara": functionpara}
        if seccode:
            payload["seccode"] = seccode
        if secname:
            payload["secname"] = secname
        return self._call_api("get_thscode", payload)

    def query_report(self, codes: str, functionpara: Optional[Dict[str, Any]] = None, outputpara: Optional[str] = None) -> Dict[str, Any]:
        payload = {"reqBody": {"codes": codes}}
        if functionpara:
            payload["reqBody"]["functionpara"] = functionpara
        if outputpara:
            payload["reqBody"]["outputpara"] = outputpara
        return self._call_api("report_query", payload)


class IFindHistoryData(IFindAPI):
    OHLC = "open,high,low,close"
    OHLCV = "open,high,low,close,volume"
    NO_ADJUST = "1"
    FORWARD_ADJUST = "2"
    BACKWARD_ADJUST = "3"
    DAILY = "D"
    WEEKLY = "W"
    MONTHLY = "M"

    def get_ohlc(self, codes: str, startdate: str, enddate: str, adjust: str = NO_ADJUST, period: str = DAILY) -> Dict[str, Any]:
        return self.get_history_data(
            codes=codes,
            startdate=startdate,
            enddate=enddate,
            indicators=self.OHLCV,
            functionpara={"CPS": adjust, "Interval": period},
        )


class IFindRealtimeData(IFindAPI):
    BASIC = "tradeDate,tradeTime,preClose,open,high,low,latest,avgPrice,change,changeRatio"

    def get_realtime(self, codes: str, indicators: Optional[str] = None) -> Dict[str, Any]:
        return self.get_real_time_quotation(codes, indicators or self.BASIC)


path: references/iFind_API_Reference.md
content: # iFinD API Reference

Skill name: `ifind`

This reference is copied from the user's local `~/iFind_API_Reference.md` and documents the Python wrapper bundled in this skill.

## 目录
1. [类和方法总览](#类和方法总览)
2. [IFindAPI 类](#ifindapi-类)
3. [IFindHistoryData 类](#ifindhistorydata-类)
4. [IFindRealtimeData 类](#ifindrealtimedata-类)
5. [常量定义](#常量定义)
6. [响应格式](#响应格式)
7. [错误码表](#错误码表)

---

## 类和方法总览

### IFindAPI 类
核心API封装类,提供所有数据接口。

| 方法 | 说明 |
|------|------|
| `get_access_token()` | 获取access_token |
| `update_access_token()` | 刷新access_token |
| `get_history_data()` | 获取历史行情 |
| `get_real_time_quotation()` | 获取实时行情 |
| `get_high_frequency()` | 获取高频序列(分钟级) |
| `get_snap_shot()` | 获取日内快照 |
| `get_basic_data()` | 获取基础数据(财务指标) |
| `get_date_sequence()` | 获取日期序列 |
| `get_trade_dates()` | 查询交易日 |
| `get_date_offset()` | 日期偏移计算 |
| `get_thscode()` | 证券代码转换 |
| `smart_stock_picking()` | 智能选股 |
| `get_data_volume()` | 数据量查询 |
| `get_error_message()` | 错误信息查询 |
| `query_report()` | 公告查询 |
| `get_edb_data()` | 经济数据库(EDB) |
| `get_data_pool()` | 专题报表 |
| `portfolio_*()` | 组合管理相关 |

### IFindHistoryData 类
历史行情专用便捷类。

| 方法 | 说明 |
|------|------|
| `get_ohlc()` | 获取OHLC数据 |
| `get_full_quote()` | 获取完整行情 |

### IFindRealtimeData 类
实时行情专用便捷类。

| 方法 | 说明 |
|------|------|
| `get_realtime()` | 获取实时行情 |
| `get_order_book()` | 获取十档委托 |

---

## IFindAPI 类

### 初始化

```python
from risk_parity_strategy.data.ifind_api import IFindAPI, REFRESH_TOKEN

api = IFindAPI(REFRESH_TOKEN)
```

### get_access_token()

获取或刷新access_token。

**返回**: `str` - access_token

```python
[REDACTED_SECRET])
```

### update_access_token()

强制刷新access_token(会使所有旧token失效)。

**返回**: `str` - 新的access_token

### get_history_data()

获取股票历史行情数据。

**参数**:
- `codes` (str): 股票代码,多个用逗号分隔,如 `"300033.SZ,600000.SH"`
- `startdate` (str): 开始日期,格式 `"YYYY-MM-DD"`
- `enddate` (str): 结束日期,格式 `"YYYY-MM-DD"`
- `indicators` (str): 指标列表,用逗号分隔
- `functionpara` (dict, optional): 函数参数

**返回**: `dict` - API响应

```python
result = api.get_history_data(
    codes="300033.SZ",
    startdate="2024-01-01",
    enddate="2025-03-14",
    indicators="open,high,low,close,volume"
)
```

### get_real_time_quotation()

获取股票实时行情。

**参数**:
- `codes` (str): 股票代码
- `indicators` (str): 指标列表
- `functionpara` (dict, optional): 函数参数

### get_high_frequency()

获取高频序列(分钟级K线)。

### get_snap_shot()

获取日内快照数据。

### get_basic_data()

获取基础数据(财务指标等)。

### get_date_sequence()

获取日期序列数据。

### get_trade_dates()

查询交易日。

### get_date_offset()

计算日期偏移。

### get_thscode()

证券代码/简称转换。

### smart_stock_picking()

智能选股搜索。

### get_data_volume()

查询数据量配额。

### query_report()

公告查询。

---

## IFindHistoryData 类

便捷的历史行情获取类。

### get_ohlc()

获取OHLC数据。

### get_full_quote()

获取完整行情数据。

---

## IFindRealtimeData 类

便捷的实时行情获取类。

### get_realtime()

获取实时行情。

### get_order_book()

获取实时行情(含十档委托)。

---

## 常量定义

### 复权类型 (IFindHistoryData)

```python
NO_ADJUST = "1"
FORWARD_ADJUST = "2"
BACKWARD_ADJUST = "3"
```

### 时间周期 (IFindHistoryData)

```python
DAILY = "D"
WEEKLY = "W"
MONTHLY = "M"
QUARTERLY = "Q"
SEMI_YEARLY = "S"
YEARLY = "Y"
```

---

## 响应格式

API返回JSON格式,结构如下:

```json
{
  "errorcode": 0,
  "errmsg": "Success!",
  "tables": [],
  "datatype": [],
  "dataVol": 10,
  "perf": 25
}
```

---

## 错误码表

| 错误码 | 说明 |
|--------|------|
| 0 | 成功 |
| -1300 | token无效 |
| -1301 | refresh_token无效 |
| -1302 | access_token无效 |
| -4219 | 权限不足 |
| -4400 | 每分钟最多600条请求 |

For full method details, examples, and longer indicator lists, read the original user file if deeper expansion is needed.


path: references/token-and-storage.md
content: # refresh_token acquisition and storage

## Acquisition

Prefer getting the iFinD `refresh_token` directly by agent-operated browser flow:

1. Open `https://quantapi.51ifind.com`
2. Use the existing browser login session if available
3. If login is required, let the user complete or approve the login interactively
4. Go to the account information page
5. Read the `refresh_token`
6. Store it immediately through the token store script

Secondary source:

- iFinD 超级命令客户端 → 工具 → `refresh_token` 查询

Fallback only when needed:

- Ask the user to provide the token directly

## Storage

This skill stores the token at:

- `~/.openclaw/skills/ifind/credentials.json`

Example structure:

```json
{
  "refresh_token": "...",
  "updated_at": "2026-03-15T10:00:00+08:00"
}
```

## Rules

- Write only through `scripts/ifind_token_store.py`
- Do not log the token
- Do not print the token after reading it from the browser
- Do not paste the token into shell history if avoidable
- Tighten file permissions after writing
- Prefer environment override only for temporary one-off runs
- Before running API scripts, make sure Python dependency `requests` is available; if not, install it with `python3 -m pip install -r scripts/requirements.txt`

## Commands

```bash
python3 scripts/ifind_token_store.py status
python3 scripts/ifind_token_store.py set --token '<TOKEN>'
python3 scripts/ifind_token_store.py remove
```

`status` reports only whether the token exists and where it is stored. It must never print the token value.


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)