Skip to content

Commit

Permalink
fix(docs): shannon strategy updated (No-Trade-No-Life#433)
Browse files Browse the repository at this point in the history
* fix(kernel): enforce order matching round by volume_step
* fix(docs): shannon strategy updated
* fix(gui): Static Product Loading
* refactor(agent): remove use_general_product
* fix(docs): some fixes
  • Loading branch information
zccz14 authored Feb 18, 2024
1 parent e1bff86 commit 6af0db0
Show file tree
Hide file tree
Showing 16 changed files with 88 additions and 122 deletions.
10 changes: 10 additions & 0 deletions common/changes/@yuants/agent/2024-02-18-01-42.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@yuants/agent",
"comment": "remove use_general_product",
"type": "minor"
}
],
"packageName": "@yuants/agent"
}
10 changes: 10 additions & 0 deletions common/changes/@yuants/kernel/2024-02-18-01-40.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@yuants/kernel",
"comment": "enforce order matching round by volume_step",
"type": "patch"
}
],
"packageName": "@yuants/kernel"
}
10 changes: 10 additions & 0 deletions common/changes/@yuants/kernel/2024-02-18-01-42.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@yuants/kernel",
"comment": "remove use_general_product",
"type": "minor"
}
],
"packageName": "@yuants/kernel"
}
1 change: 0 additions & 1 deletion libraries/agent/etc/agent.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ export interface IAgentConf {
is_real?: boolean;
kernel_id?: string;
start_time?: string;
use_general_product?: boolean;
}

// @public
Expand Down
12 changes: 1 addition & 11 deletions libraries/agent/src/AgentScene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ export interface IAgentConf {

/** Kernel ID */
kernel_id?: string;
/** 使用标准品种信息 */
use_general_product?: boolean;
/** 是否禁用打印日志 */
disable_log?: boolean;
}
Expand Down Expand Up @@ -91,12 +89,6 @@ export const agentConfSchema: JSONSchema7 = {
type: 'string',
default: 'Model',
},
use_general_product: {
type: 'boolean',
title: '使用标准品种信息',
description: '使用标准品种信息作为品种信息,但仍沿用具体品种的行情数据',
default: false,
},
disable_log: {
type: 'boolean',
title: '禁用打印日志',
Expand All @@ -122,9 +114,7 @@ export const AgentScene = async (terminal: Terminal, agentConf: IAgentConf) => {
kernel.log = undefined;
}
const productDataUnit = new ProductDataUnit(kernel);
const productLoadingUnit = new ProductLoadingUnit(kernel, terminal, productDataUnit, {
use_general_product: agentConf.use_general_product,
});
const productLoadingUnit = new ProductLoadingUnit(kernel, terminal, productDataUnit);
const quoteDataUnit = new QuoteDataUnit(kernel);
const tickDataUnit = new TickDataUnit(kernel);
const periodDataUnit = new PeriodDataUnit(kernel, quoteDataUnit);
Expand Down
10 changes: 3 additions & 7 deletions libraries/kernel/etc/kernel.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,9 +617,7 @@ export class ProductDataUnit extends BasicUnit {

// @public
export class ProductLoadingUnit extends BasicUnit {
constructor(kernel: Kernel, terminal: Terminal, productDataUnit: ProductDataUnit, options?: {
use_general_product?: boolean | undefined;
} | undefined);
constructor(kernel: Kernel, terminal: Terminal, productDataUnit: ProductDataUnit, options?: {} | undefined);
// (undocumented)
dump(): {
productTasks: {
Expand All @@ -632,9 +630,7 @@ export class ProductLoadingUnit extends BasicUnit {
// (undocumented)
onInit(): Promise<void>;
// (undocumented)
options?: {
use_general_product?: boolean | undefined;
} | undefined;
options?: {} | undefined;
// (undocumented)
productDataUnit: ProductDataUnit;
// (undocumented)
Expand Down Expand Up @@ -778,7 +774,7 @@ export class TickDataUnit extends BasicUnit {

// Warnings were encountered during analysis:
//
// src/units/OrderMatchingUnit.ts:270:11 - (ae-forgotten-export) The symbol "IMatchingRange" needs to be exported by the entry point index.d.ts
// src/units/OrderMatchingUnit.ts:275:11 - (ae-forgotten-export) The symbol "IMatchingRange" needs to be exported by the entry point index.d.ts

// (No @packageDocumentation comment for this package)

Expand Down
7 changes: 6 additions & 1 deletion libraries/kernel/src/units/OrderMatchingUnit.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { encodePath } from '@yuants/data-model';
import { IOrder, IPeriod, ITick, OrderDirection, OrderStatus, OrderType } from '@yuants/protocol';
import { roundToStep } from '@yuants/utils';
import { Subject, Subscription } from 'rxjs';
import { Kernel } from '../kernel';
import { AccountInfoUnit } from './AccountInfoUnit';
Expand Down Expand Up @@ -214,11 +215,15 @@ export class OrderMatchingUnit extends BasicUnit {
continue;
}
isSomeOrderTraded = true;
const theProduct = this.productDataUnit.mapProductIdToProduct[order.product_id];
const volume_step = theProduct.volume_step ?? 1;
const volume = roundToStep(order.volume, volume_step);
const theOrder = {
...order,
timestamp_in_us: this.kernel.currentTimestamp * 1000,
traded_price: tradedPrice,
traded_volume: order.volume,
volume,
traded_volume: volume,
status: OrderStatus.TRADED,
};
// 成交
Expand Down
88 changes: 9 additions & 79 deletions libraries/kernel/src/units/ProductLoadingUnit.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,34 @@
import { formatTime } from '@yuants/data-model';
import { IProduct, Terminal } from '@yuants/protocol';
import { defaultIfEmpty, filter, lastValueFrom, map, mergeMap, of, tap, throwIfEmpty, toArray } from 'rxjs';
import { defaultIfEmpty, lastValueFrom, map, tap } from 'rxjs';
import { Kernel } from '../kernel';
import { BasicUnit } from './BasicUnit';
import { ProductDataUnit } from './ProductDataUnit';

// GSR
interface IGeneralSpecificRelation {
// general_datasource_id 一定是 Y 常量,因此不需要特别存储
// general_datasource_id: string;
/** 标准品种ID */
general_product_id: string; // XAUUSD
/** 具体数据源 ID */
specific_datasource_id: string; // TradingView
/** 具体品种 ID */
specific_product_id: string; // FX:XAUUSD
}

/**
* 品种加载单元
* Product Loading Unit: load product info from storage
* @public
*/
export class ProductLoadingUnit extends BasicUnit {
constructor(
public kernel: Kernel,
public terminal: Terminal,
public productDataUnit: ProductDataUnit,
public options?: {
use_general_product?: boolean;
},
public options?: {},
) {
super(kernel);
}
productTasks: { datasource_id: string; product_id: string }[] = [];

async onInit() {
this.kernel.log?.(formatTime(Date.now()), `开始加载品种信息: 共 ${this.productTasks.length} 个品种`);
this.kernel.log?.(
formatTime(Date.now()),
`start product loading: all ${this.productTasks.length} product(s)`,
);
for (const task of this.productTasks) {
this.kernel.log?.(
formatTime(Date.now()),
`正在加载 ${task.datasource_id} / ${task.product_id} 的品种信息...`,
`product loading: ${task.datasource_id} / ${task.product_id}`,
);
await lastValueFrom(
this.terminal
Expand All @@ -48,72 +37,13 @@ export class ProductLoadingUnit extends BasicUnit {
tags: { datasource_id: task.datasource_id, product_id: task.product_id },
})
.pipe(
// ISSUE: 有时候确实没有定义这个品种,技术指标观察器的场景中只需要行情数据,不强制需要品种信息
// 在其他场景中,如果忘记配置品种信息,造成的潜在危害更大,因此用配置按需抑制此错误
map((x) => x.origin),
mergeMap((specificProduct) => {
this.kernel.log?.(formatTime(Date.now()), `具体品种`, JSON.stringify(specificProduct));
if (specificProduct.datasource_id !== 'Y' && this.options?.use_general_product) {
return this.terminal
.queryDataRecords<IGeneralSpecificRelation>({ type: 'general_specific_relation' })
.pipe(
map((x) => x.origin),
filter(
(x) =>
x.specific_datasource_id === task.datasource_id &&
x.specific_product_id === task.product_id,
),
throwIfEmpty(
() => new Error(`无法找到 ${task.datasource_id} / ${task.product_id} 的标准品种关系`),
),
map((x) => x.general_product_id),
tap((general_product_id) => {
this.kernel.log?.(formatTime(Date.now()), `匹配标准品种 "${general_product_id}"`);
}),
)
.pipe(
mergeMap((general_product_id) =>
this.terminal
.queryDataRecords<IProduct>({
type: 'product',
tags: { datasource_id: 'Y', product_id: general_product_id },
})
.pipe(
//
throwIfEmpty(() => new Error(`无法找到 ${general_product_id} 的标准品种`)),
map((x) => x.origin),
tap((generalProduct) => {
this.kernel.log?.(
formatTime(Date.now()),
`获取标准品种 "${generalProduct.product_id}"`,
JSON.stringify(generalProduct),
);
}),
map((generalProduct) => ({
...generalProduct,
datasource_id: specificProduct.datasource_id,
product_id: specificProduct.product_id,
})),
),
),
// 此处代码是多余的,但是为了避免后续的代码出现问题,保留此处代码
throwIfEmpty(() => new Error(`无法找到标准品种`)),
);
}
return of(specificProduct);
}),

defaultIfEmpty<IProduct, IProduct>({
datasource_id: task.datasource_id,
product_id: task.product_id,
}),
tap((product) => {
this.kernel.log?.(
formatTime(Date.now()),
`加载到 ${product.datasource_id}/${product.product_id} 品种`,
JSON.stringify(product),
);
// Issue: 如果不推到 products 列表中可能会导致后续的性能审计遇到一些问题
this.kernel.log?.(formatTime(Date.now()), 'product loaded', JSON.stringify(product));
this.productDataUnit.mapProductIdToProduct[product.product_id] = product;
}),
),
Expand Down
34 changes: 27 additions & 7 deletions ui/docs/docs/basics/how-to-calculate-margin.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,37 @@ sidebar_position: 5
The standard margin model is:

Used Margin = sum of all positions' used margin / account's leverage.

- Each position's used margin = Position Volume \* Product Value Speed \* Factor of Asset \* Base Currency Exchange Rate \* Product Margin Rate
- Factor of Asset = 1 if FX or Bond Spot, otherwise Open Price of Position.
- Base Currency Exchange Rate = the exchange rate of **product's base currency** vs **account's margin currency** at the time of position opening.
$$
\text{Standard Margin} = \frac{\sum_{\text{Position}} {\text{Volume} \times \text{Value Scale}} \times P(\text{Value Scale Unit}, C, t_1) \times P(C, A, t_1) \times \text{Margin Rate} }{\text{Account Leverage}}
$$

When:

- $A$ is the account's margin currency; $B$ is the product itself; $C$ is the product's quote currency;
- For any assets $x$, $y$ and time $t$, $P(x, y, t)$ is the price of two assets $x$ vs $y$ at time $t$, and $t_1$ is the time of position entering; $t_2$ is the time of position exiting;
- $P(B, C, t)$ is the price of the product at time $t$; You can see it directly everywhere in the market;
- Volume is the number of contract, non-negative;
- Open Price $P(B, C, t_1)$ is the price of the product at the time of position entering;
- Value scale is a constant multiple item for the product, usually 1 in spots, and is called "contract size" in futures or options contracts.
- Margin rate is a constant multiple item for the product, equivalent 1 in spots, and less than 1 in leverage trading.
- $P(C, A, t)$ is the exchange rate of the quote currency vs the margin currency at time $t$;
- if the quote currency is the same as the margin currency, then $P(C, A, t) = 1$, you can ignore this item;
- Value scale unit is the unit of value scale, usually refers to the product itself ($B$), or the product's quote currency ($C$);
- if it refers to the product itself, then $P(\text{Value Scale Unit}, C, t_1) = P(B, C, t_1) = \text{Open Price}$;
- if it refers to the product's quote currency, then $P(\text{Value Scale Unit}, C, t_1) = P(C, C, t_1) = 1$;
- for example, if the product specified that 1 contract = 100 shares of stock, then the value scale unit is the stock itself, and the value scale is 100;
- for another example, if the product specified that 1 contract = stocks worth 1000 USD, then the value scale unit is the quote currency, and the value scale is 1000;
- Actually when the quote currency and margin currency are different, it is not possible to directly obtain the precise exchange rate $P(C, A, t_1)$. However, the exchange usually directly shows the precise standard margin in the history orders. So we can deduce the exchange rate $P(C, A, t_1)$ from the standard margin.

Once a position is opened, the used margin will not change with the price.

`free = equity - used`

The available margin will fluctuate with the fluctuation of the equity, and the necessary condition for opening a position is that the available margin is not less than the margin required to open the position.

**Margin-equity ratio** = 100% \* used margin / equity
$$
\text{Margin-equity Ratio} = \frac{\text{Used Margin}}{Equity} \times 100\%
$$

Margin-equity ratio is used to measure the overall risk of an account.

Expand All @@ -30,7 +48,9 @@ Margin-equity ratio is used to measure the overall risk of an account.

When the margin-equity ratio is too low, it means that the account funds are not fully utilized.

Actual leverage ratio = Margin-equity ratio \* account leverage ratio
$$
\text{Actual Leverage} = \text{Margin-equity ratio} \times \text{Account Leverage}
$$

Sometimes we are more inclined to use the actual leverage ratio to express the utilization rate of funds.
When the actual leverage ratio is less than 100%, it means that no matter how the price fluctuates, as long as it is non-negative , the account will not be liquidated to zero. However, if the volatility of the variety price is very small, using too little leverage will waste the funds in the account. Therefore, the setting of the actual leverage ratio should be related to the volatility of the trading variety price. However, in the stock market, unless financing and securities lending, since the account leverage ratio is 1, the actual leverage ratio cannot exceed 100%.
Expand Down
3 changes: 2 additions & 1 deletion ui/docs/docs/basics/what-is-product.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ The specifications for evaluating products have the following dimensions:
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
| `product_id` | (**Required**) Which product does this specification information belong to? How to distinguish it from other products? | `"XAUUSD"` |
| `datasource_id` | (**Required**) Who provided this specification information? | `"Y"` |
| `currency` | (**Required**) What currency is used for trading this product? | `"USD"` |
| `name` | How do humans generally refer to this species? | `"Gold Spot vs US Dollar"` |
| `quote_currency` | The quote currency to price the product. | `"USD"` |
| `base_currency` | Only available in foreign exchange product. If defined, the price of this product (P(this, quote_currency)) can be treated as P(base_currency, quote_currency) | |
| `value_scale` | How many units of the subject matter is equivalent to 1 lot? How much profit and loss will be generated by changes in unit prices? Some places are also called "contract multipliers". Default to 1 | `100` |
| `value_scale_unit` | What is the unit of this value scale? Leave it undefined if unit is product itself. Or set to currency if needed. | |
| `price_step` | What is the minimum price per hop for this product? The quoted price in the market must be an integer multiple of this unit. However, the actual transaction price does not need to comply with this constraint. Default to 1 | `0.01` |
Expand Down
7 changes: 5 additions & 2 deletions ui/docs/docs/tutorial-basics/creating-your-first-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export default () => {
const datasource_id = useParamString('DataSource', 'Y');
const product_id = useParamString('Product');
const period = useParamString('Period', 'PT1H');
const currency = useParamString('Currency', 'USD');
// Get the product information and price data
const product = useProduct(datasource_id, product_id);
const { close } = useOHLC(datasource_id, product_id, period);
Expand Down Expand Up @@ -155,7 +156,7 @@ export default () => {
const initial_balance = useParamNumber('Initial Balance', 100_000);
const threshold = useParamNumber('Threshold', 1);
// Get the account information
const accountInfo = useAccountInfo();
const accountInfo = useAccountInfo({ currency });
// Use a simple position manager
const [actualVolume, setVolume] = useSimplePositionManager(accountInfo.account_id, product_id);
// Re-balance the position
Expand All @@ -166,7 +167,9 @@ export default () => {
const totalValueToHold = totalValue * 0.5;
// infer the volume to hold
const valuePerVolume =
price * (product.value_speed ?? 1) * (product.is_underlying_base_currency ? -1 / price : 1);
(product.value_scale ?? 1) *
(product.value_scale_unit ? 1 : price) *
(product.quote_currency === accountInfo.money.currency ? 1 : -1 / price);
const expectedVolume = totalValueToHold / valuePerVolume;
// calculate the error rate
const volume_step = product.volume_step ?? 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export default () => {
const datasource_id = useParamString('DataSource', 'Y');
const product_id = useParamString('Product');
const period = useParamString('Period', 'PT1H');
const currency = useParamString('Currency', 'USD');
// 获取品种信息和价格数据
const product = useProduct(datasource_id, product_id);
const { close } = useOHLC(datasource_id, product_id, period);
Expand Down Expand Up @@ -155,7 +156,7 @@ export default () => {
const initial_balance = useParamNumber('Initial Balance', 100_000);
const threshold = useParamNumber('Threshold', 1);
// 获取账户信息
const accountInfo = useAccountInfo();
const accountInfo = useAccountInfo({ currency });
// 使用单一头寸管理器
const [actualVolume, setVolume] = useSimplePositionManager(accountInfo.account_id, product_id);
// 重新平衡头寸
Expand All @@ -166,7 +167,9 @@ export default () => {
const totalValueToHold = totalValue * 0.5;
// 推断要持有的头寸量
const valuePerVolume =
price * (product.value_speed ?? 1) * (product.is_underlying_base_currency ? -1 / price : 1);
(product.value_scale ?? 1) *
(product.value_scale_unit ? 1 : price) *
(product.quote_currency === accountInfo.money.currency ? 1 : -1 / price);
const expectedVolume = totalValueToHold / valuePerVolume;
// 计算误差率
const volume_step = product.volume_step ?? 1;
Expand Down
Loading

0 comments on commit 6af0db0

Please sign in to comment.