Skip to main content

ProgramGarden

ProgramGarden은 AI 시대에 맞춰 파이썬을 모르는 투자자도 개인화된 시스템 트레이딩을 자동으로 수행할 수 있게 돕는 노드 기반 자동매매 DSL(Domain Specific Language) 오픈소스입니다.

노드를 조합하여 워크플로우를 정의하고, 실행 엔진이 이를 자동으로 처리합니다. LS증권 OpenAPI를 메인으로 해외 주식/선물 거래 자동화를 지원합니다.

공식 문서 및 커뮤니티

설치

pip install programgarden

# Poetry 사용 시 (개발 환경)
poetry add programgarden

요구 사항: Python 3.12+

빠른 시작

동기 실행

from programgarden import ProgramGarden

pg = ProgramGarden()

# 워크플로우 검증
result = pg.validate(workflow_definition)

# 워크플로우 실행 (완료 대기)
job_state = pg.run(
    definition=workflow_definition,
    context={"param": "value"},
    secrets={"appkey": "...", "appsecret": "..."},
    wait=True,
    timeout=60.0,
)

비동기 실행

from programgarden import ProgramGarden

pg = ProgramGarden()

# 워크플로우 비동기 실행 (리스너 연결)
job = await pg.run_async(
    definition=workflow_definition,
    context={"param": "value"},
    listeners=[MyExecutionListener()],
)

# 실행 중 제어
await job.stop()

Broker account trackers belong to the workflow job. Normal completion, stop(), cancel(), and force_stop() cancel pending broker startup/history tasks, stop account polling, and close each tracker's dedicated WebSocket. Late account PnL callbacks do not restart work after shutdown. This cleanup also covers partially initialized trackers and leaves other jobs running. Scheduled cycles reuse the same broker account tracker, including pending startup. A failed startup stops its tracker and closes its connection before a later cycle may try again; no immediate retry or extra workflow node is introduced. Already queued or in-flight PnL notifications have up to one second to finish after account trackers stop, before listeners close. On timeout, cleanup logs the pending count and cancels those tasks, allowing another 0.25 seconds for cancellation. A listener that suppresses cancellation is reported and remains tracked; it cannot hold job shutdown indefinitely. Delivery beyond this bounded grace is not guaranteed.

Overseas-stock OpenOrdersNode treats the observed terminal COSAQ00102 02679 empty envelope as open_orders=[] / count=0 without an error. The echoed date, market and filters must match its current-day pending query, the empty detail array must be explicitly present, and continuation must be terminal. This follows the finance package's observed-response reference and StockAccountTracker; 02679 is not a generic success code. Missing, malformed, mismatched and failed reads retain error / reason=fetch_failed so workflows can block new entries.

For one-shot workflows, an unhandled order_result.success=False returned directly by a main-flow node makes the final job status failed and emits WORKFLOW_FAILED. The original broker message remains in node diagnostics and the failure statistics. Successful downstream nodes do not erase that rejection. reason="no_signal" stays a normal no-op. Scheduled/resident workflows continue after a rejected cycle; SplitNode and automatic iteration retain their existing item-error continuation policies. Final failure is not inferred from cumulative errors_count or old logs. Raised main-node exceptions retain the existing fail-fast behavior.

New-order nodes for overseas stocks, overseas futures, and Korea stocks expose the catalog's result port as a list of outcome rows: {{ nodes.order.result }} can feed TableDisplayNode directly. Each row copies the final order_result and includes order_id from the existing top-level or nested order number. Legacy order_result and order_id outputs are unchanged. The projection occurs after fill confirmation, fractional-remainder annotation, and replay marking; an accepted order is not promoted to a fill without confirmation. Rejections and no_signal keep their original diagnostics/reason as an outcome row, with no fabricated order number. Dry runs add a simulated row while retaining the legacy flat simulation envelope; request/credential data is not copied into the row. Automatic iteration merges these rows into one list. Implicit SplitNode collection of a new-order result retains its legacy order_result row shape; explicit nodes.order.result bindings always read the list. Modify/cancel nodes retain their separately declared modify_result/cancel_result ports and are outside this change.

Explicit execution identities in local ledgers

WorkflowPositionTracker.record_fill(..., execution_id=...) can preserve a broker-provided execution identity. Within one ledger/product/provider/mode, order date and normalized order number, an identical replay returns the first classification without changing FIFO or history. Conflicting facts raise ExecutionIdentityConflictError. Positive numeric identifiers ignore padding; opaque identifiers retain case. Missing, blank and zero identifiers retain legacy behavior and are never inferred from time, price or quantity. Every pending partial fill is retained while its order acknowledgement is being recorded.

The migration is additive and does not invent identities for existing rows. Callers must establish consistent identifier and timestamp semantics first; exchange execution numbers and broker execution numbers are not interchangeable. This ledger API alone does not reconcile finite workflows after their shutdown.

Standalone futures TC3 callbacks interpret s_b_ccd="1" as sell and "2" as buy, matching the SDK contract. Unknown or blank side codes are logged and skipped before any inventory write is scheduled. With an app order lifecycle handler, TC3 still bypasses this legacy fill path: canonical REST reconciliation remains the sole writer. This side correction does not establish an alias between TC3 and REST execution identities, dates, or times.

Futures monetary evidence

Futures workflow PnL events preserve native gross estimates separately from accounting results. workflow_*, other_*, total_*, account and competition monetary/rate scalars remain null when accounting evidence is unavailable. pnl_by_currency contains gross price-change subtotals by contract currency; monetary_positions retains their basis/status and unmodified, unconfirmed broker_pnl_amount. No fee, FX, margin/equity return or verified contest score is inferred from these estimates. Actual zero and negative amounts are retained. currency is null for mixed or unavailable currencies. Consumers must handle nullable monetary fields and must not coerce them to zero.

Personal workflow execution metrics

WorkflowPositionTracker.personal_metrics() reads retained SQLite trade_history for the current product, provider and paper/live mode. Its independent WorkflowPnLEvent.personal_metrics envelope retains stock realized PnL after all positions close and counts distinct positive executed orders by valid stored order date and normalized order number. Partial fills of one dated order count once. This is the reporting runtime's retained workflow ledger, not a complete account history or verified contest score. No credential/account generation is claimed by this legacy local storage.

Stored stock realized PnL is gross long-only FIFO before fees. The reader validates sufficient recorded buys and consistency with stored results; mixed manual/product/provider/exchange FIFO ownership makes the affected symbol unavailable. Amounts remain separate by symbol/exchange, with currency null because the historical table has no currency evidence. No FX conversion or return denominator is invented. Futures executed-order counts are available under the same identity rules, but futures monetary results and portfolio MDD remain null: the retained FIFO lacks contract accounting and risk price windows are not equity curves. Invalid order identities also yield a null count with its reason.

The listener refreshes this evidence at most once per 10 seconds to avoid scanning retained history on every price tick; reused observations retain their original as_of. The cache is isolated to the actual tracker/product/provider/mode, and other broker-node callbacks do not receive it. A newly recorded fill may appear at the next refresh. Existing open-position PnL fields are unchanged. Core and engine consumers must ship together to support the additive event field.

Stock read failures

Stock open-order queries include current-day orders (ThdayBnsAppYn="1"). OpenOrdersNode reports unusable COSAQ00102 responses with error, reason="fetch_failed", and the original TR, HTTP status and broker response fields in diagnostics. Its legacy empty payload/count remains for compatibility; an error result is unavailable, not evidence of zero orders. A successful empty response requires documented success plus parsed echo and aggregate blocks. Unknown broker codes receive no inferred meaning.

Stock MarketDataNode preserves failed g3101 attempts under failures. An entirely failed fetch returns error and reason="fetch_failed"; mixed results retain usable values with _partial_failure and _failure_reason. The existing exchange fallback and empty upstream no-signal behavior remain. Futures master errors likewise report the observed catalogue and original broker fields without inferring account entitlement, token or quota causes.

Dry Run (워크플로우 검증용 모의 실행)

실제 주문/알림/Realtime WebSocket 연결 없이 워크플로우를 검증합니다.

  • ScheduleNode / TradingHoursFilterNode → 1 cycle 후 즉시 종료
  • 주문 노드 → LS API 미호출, {"order_id": "DRYRUN-<uuid>", "status": "simulated", ...} 반환
  • Realtime 노드 → WebSocket 미개방, {"status": "skipped_dry_run"} 반환
  • Messaging 노드(Telegram 등) → no-op, {"status": "simulated"} 반환
  • 조회/백테스트 노드 → 기존 동작 유지 (실제 API 경로)
job = await pg.run_async(
    definition=workflow_definition,
    context={"dry_run": True},
    secrets={...},
)

워크플로우 정의 (JSON)

{
  "nodes": [
    {"id": "broker", "type": "OverseasStockBrokerNode", "credential_id": "cred-1"},
    {"id": "account", "type": "OverseasStockAccountNode"},
    {"id": "rsi", "type": "ConditionNode", "plugin": "RSI", "data": "{{ nodes.historical.values }}"}
  ],
  "edges": [
    {"from": "broker", "to": "account"},
    {"from": "account", "to": "rsi"}
  ],
  "credentials": [
    {
      "credential_id": "cred-1",
      "type": "broker_ls_overseas_stock",
      "data": [
        {"key": "appkey", "value": "", "type": "password", "label": "App Key"},
        {"key": "appsecret", "value": "", "type": "password", "label": "App Secret"}
      ]
    }
  ]
}

주요 특징

  • 노드 기반 DSL: 72개 내장 노드를 조합하여 코딩 없이 자동매매 전략 구성
  • 실시간 처리: WebSocket 기반 실시간 시세, 계좌, 체결 이벤트 수신
  • AI Agent 통합: LLMModelNode + AIAgentNode로 LLM 기반 분석/의사결정
  • 플러그인 확장: 67개 내장 전략 플러그인 (RSI, MACD, 볼린저밴드, 이치모쿠, 듀얼모멘텀, 터틀브레이크아웃 등)
  • ExecutionListener: 10개 이상의 콜백으로 실행 상태 실시간 모니터링
  • 위험 관리: WorkflowRiskTracker로 HWM/drawdown 추적, 포지션 사이징
  • 동적 노드 주입: 런타임에 커스텀 노드 등록 및 실행

아키텍처

5-Layer Architecture:
1. Registry Layer   - 노드/플러그인 메타데이터 (73개 노드, 77개 플러그인)
2. Credential Layer - 인증 정보 관리
3. Definition Layer - JSON 워크플로우 정의 (노드, 엣지, 크레덴셜)
4. Job Layer        - 상태 유지 실행 인스턴스 (최대 24시간 장기 실행)
5. Event Layer      - ExecutionListener 콜백 이벤트

ExecutionListener 콜백

콜백 설명
on_node_state_change 노드 실행 상태 변경
on_edge_state_change 엣지 실행 상태 변경
on_log 로그 이벤트
on_job_state_change Job 생명주기
on_display_data 차트/테이블 출력 데이터
on_workflow_pnl_update 실시간 수익률 (FIFO 기반)
on_retry 노드 재시도 이벤트
on_token_usage AI 토큰 사용량
on_ai_tool_call AI Agent 도구 호출
on_llm_stream LLM 스트리밍 출력
on_risk_event 위험 임계값 이벤트
on_notification 투자자 알림 (시그널, 리스크, 워크플로우 상태, 스케줄, 재시도 소진)

변경 로그

자세한 변경 사항은 CHANGELOG.md를 참고하세요.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

programgarden-1.38.0.tar.gz (469.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

programgarden-1.38.0-py3-none-any.whl (488.9 kB view details)

Uploaded Python 3

File details

Details for the file programgarden-1.38.0.tar.gz.

File metadata

  • Download URL: programgarden-1.38.0.tar.gz
  • Upload date:
  • Size: 469.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.12.13 Darwin/25.6.0

File hashes

Hashes for programgarden-1.38.0.tar.gz
Algorithm Hash digest
SHA256 d65fdebbb39b6c138627b9aadc2d32608c62d5e5cd1025bedd72371105e216b0
MD5 7fde04cfe0f961ea597fe6c635481ac6
BLAKE2b-256 a7a66badef074ab97ed248f4b7f30650da7b5a98625a48b031b3d32cb0da1818

See more details on using hashes here.

File details

Details for the file programgarden-1.38.0-py3-none-any.whl.

File metadata

  • Download URL: programgarden-1.38.0-py3-none-any.whl
  • Upload date:
  • Size: 488.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.12.13 Darwin/25.6.0

File hashes

Hashes for programgarden-1.38.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fe2ebb5863012294cdc7afeffa47445fe6aa3b441fc817180d6c79cd4f23c982
MD5 4931a9a87dc854114e14d3da64be69e1
BLAKE2b-256 78849bce88161b24582aa4035b3e15e27394bc6032e3aed449b3225c72f60f89

See more details on using hashes here.

Release history Release notifications | RSS feed

1.40.1

2 files

1.40.0

2 files

1.39.1

2 files

1.39.0

2 files

1.38.2

2 files

1.38.1

2 files

This release

1.38.0 This release

2 files

1.37.11

2 files

1.37.10

2 files

1.37.9

2 files

1.37.8

2 files

1.37.7

2 files

1.37.6

2 files

1.37.5

2 files

1.37.4

2 files

1.37.3

2 files

1.37.2

2 files

1.37.1

2 files

1.37.0

2 files

1.36.0

2 files

1.35.1

2 files

1.35.0

2 files

1.34.0

2 files

1.33.5

2 files

1.33.4

2 files

1.33.3

2 files

1.33.2

2 files

1.33.1

2 files

1.33.0

2 files

1.32.0

2 files

1.31.2

2 files

1.31.1

2 files

1.31.0

2 files

1.30.0

2 files

1.29.4

2 files

1.29.3

2 files

1.29.2

2 files

1.29.1

2 files

1.29.0

2 files

1.28.0

2 files

1.27.0

2 files

1.26.0

2 files

1.25.0

2 files

1.24.2

2 files

1.24.1

2 files

1.24.0

2 files

1.23.1

2 files

1.23.0

2 files

1.22.4

2 files

1.22.3

2 files

1.22.2

2 files

1.22.1

2 files

1.22.0

2 files

1.21.11

2 files

1.21.10

2 files

1.21.9

2 files

1.21.7

2 files

1.21.6

2 files

1.21.5

2 files

1.21.4

2 files

1.21.3

2 files

1.21.2

2 files

1.21.1

2 files

1.21.0

2 files

1.20.1

2 files

1.20.0

2 files

1.19.0

2 files

1.18.0

2 files

1.17.1

2 files

1.17.0

2 files

1.16.2

2 files

1.16.1

2 files

1.16.0

2 files

1.15.9

2 files

1.15.8

2 files

1.15.7

2 files

1.15.6

2 files

1.15.5

2 files

1.15.4

2 files

1.15.3

2 files

1.15.2

2 files

1.15.1

2 files

1.15.0

2 files

1.14.0

2 files

1.13.0

2 files

1.12.0

2 files

1.11.1

2 files

1.11.0

2 files

1.10.0

2 files

1.9.1

2 files

1.9.0

2 files

1.8.0

2 files

1.7.1

2 files

1.7.0

2 files

1.6.0

2 files

1.5.0

2 files

1.4.0

2 files

1.3.0

2 files

1.2.1

2 files

0.1.28

2 files

0.1.27

2 files

0.1.26

2 files

0.1.25

2 files

0.1.24

2 files

0.1.23

2 files

0.1.22

2 files

0.1.21

2 files

0.1.20

2 files

0.1.19

2 files

0.1.18

2 files

0.1.17

2 files

0.1.16

2 files

0.1.15

2 files

0.1.14

2 files

0.1.13

2 files

0.1.12

2 files

0.1.11

2 files

0.1.9

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page