# Introduction

### Problems&#x20;

We find ourselves at the heart of the AI revolution, a transformative era where jobs are increasingly being taken over by AI applications and robotics. The challenge is that AI isn't working for everyday people, but rather for big corporations and wealthy investors who can afford to invest in and create AI systems for their own benefit.

How can we, as everyday people, harness the value of the AI revolution to ensure AI works for us, rather than leaving us behind? This is a crucial challenge, and it's what 0max1's mission aims to solve.

### Solutions

The interest-bearing stablecoin USDi automatically distributes profits generated by AI. There are several ways for AI to work on valuable tasks and generate revenue in the near future. The current feasible solution that Quant AI offers is capturing arbitrage opportunities in the crypto market. Opportunities like funding rate arbitrage, spread arbitrage, and MEV arbitrage can continuously generate returns without exposing users to excessive risks. At 0max1, the Quant AI has reliably and consistently generated profits with a proven track record.


# Our Solution

0max1: A universal income protocol powered by AI

<figure><img src="https://3178843284-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDTuvot4fROkCCGtjC2y8%2Fuploads%2F0XrXFUmI4fJo2a2iBUKx%2Fimage.png?alt=media&amp;token=b7906da7-96a0-4807-bd08-2b6911dc7da0" alt=""><figcaption></figcaption></figure>

### **Where does the income come from?**&#x20;

In the near term, the primary source of income is from AI-trained trading bots that capture delta neutral and arbitrage opportunities. Strategies like Coin-Future Funding Rate Farming, Spread Arbitrage, and MEV are currently the major delta neutral strategies. In the long term, income will be derived from our community-owned robotic and AI services, such as autonomous driving taxis and household service robots. [Learn more.](/overview/our-solution/quant-ai-bot)

### **How is the money distributed?**&#x20;

USDi is an interest-bearing, delta-neutral synthetic dollar. It forms the backbone for distributing the wealth created by 0max1. Income generated by our community-owned AI bots is automatically distributed to USDi holders, based on the amount they hold and the length of time they hold it. [Learn more.](/overview/our-solution/usdi-stablecoin)

### **How to get USDi?**&#x20;

General individuals or DAOs can obtain USDi either by swapping it with their current crypto assets on-chain or by directly purchasing it with their fiat currency through our node operators via bank transfer or cash. Additionally, community members can become node operators and contribute their time commitment to earn USDi by running a node. [Learn more.](/overview/where-to-get-usdi)


# Quant AI Bot

How do we achieve universal income by Quant AI?

The first stage is through our innovative Quant AI bot.

<figure><img src="https://3178843284-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDTuvot4fROkCCGtjC2y8%2Fuploads%2Fhf59UWzI8hzbNLrh02QV%2Fimage.png?alt=media&amp;token=72f3ca44-6a82-4aea-bc84-53304362fa65" alt=""><figcaption></figcaption></figure>


# Coin Future Funding Rate Farming

<figure><img src="https://3178843284-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDTuvot4fROkCCGtjC2y8%2Fuploads%2F0YMs5b4zCSiISdBbYfjQ%2Fimage.png?alt=media&amp;token=03acf7b8-d5e2-4759-8ecf-4eec5ff2aef6" alt=""><figcaption></figcaption></figure>

All arbitrage trades by 0max1 are risk-free, offering stable and secure returns.

* Stable yield ranged between 10% \~ 22% for USDi passive income.
* All open positions and spot crypto assets are hedged against each other.


# Spread Arbitrage

<figure><img src="https://3178843284-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDTuvot4fROkCCGtjC2y8%2Fuploads%2FREl7CLS1PxPebgca9J8e%2Fimage.png?alt=media&amp;token=9246d017-221c-4cd4-9b96-8e9cf3ad314c" alt=""><figcaption></figcaption></figure>

Stable yield ranged between 8% \~ 25% for USDi passive income.

Delivery Futures contracts always converge with Spot prices at expiration dates.


# MEV Arbitrage

<figure><img src="https://3178843284-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDTuvot4fROkCCGtjC2y8%2Fuploads%2FmrIU4s8xUn4GRwnRBu9g%2Fimage.png?alt=media&amp;token=8939a8bd-aa7f-4286-85ef-68e77beb581b" alt=""><figcaption></figcaption></figure>

Onchain arbitrage bundles, completed within the same block to generate USDi passive income.

The revenue is the price spread between different swap pools and the cost is on chain gas fees.


# USDi Stablecoin

An interest-bearing synthetic dollar.

USDi is an interest-bearing synthetic dollar with a stable 1:1 value ratio to the USD, featuring automatically distributed interest. We are aiming to create a standardized welfare system with universal income for all USDi holders.&#x20;

USDi is backed by its underlying fully hedged assets, and the total circulation of USDi should always equal the value of these hedged assets to maintain a stable value of 1:1 with the USD.

All income generated within the 0max1 protocol is automatically distributed to USDi holders as a form of universal income welfare. Holders do not need to claim this; it is automatically distributed based on the weighted product of the USDi amount and the duration of holding.


# 1:1 Pegged to USD

USDi maintains a 1:1 peg to USD to ensure stable value. This is achieved by keeping the total asset value consistently above the total USDi in circulation. All underlying assets are fully liquid and completely hedged with derivative positions. This hedging strategy ensures the overall portfolio is resistant to market price movements and can be converted to USD instantly.

Yield generated from the portfolio steadily increases the total asset value. Once it surpasses a certain threshold and the total asset value significantly exceeds USDi in circulation, new USDi is minted and distributed to all USDi holders as profit gains. If the USDi in circulation ever exceeds the total asset value, the difference will be covered by burning USDi from the 0max1 reserve pool by sending it to the locked account, thereby reducing circulation.

### 0max1 Reserve Pool

During USDi gains distribution, a certain percentage of USDi is allocated from overall available profits to a treasury wallet. The purpose of this treasury wallet is to cover any related expenses for the 0max1 protocol and, more importantly, to establish a stabilization fund.

If there is ever an event where the amount of USDi in circulation is greater than the overall assets, USDi from this treasury account will be burned and sent back to the locked address account. In this way, the USDi in circulation will be reduced to ensure the underlying assets backing each USDi always maintain a value above $1.

We need a chart to explain this: the following is a placeholder:

<figure><img src="https://3178843284-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDTuvot4fROkCCGtjC2y8%2Fuploads%2FyuhXXTdL40BRNWfuAlwj%2Fimage.png?alt=media&amp;token=f8e3a2c5-6b9b-412b-8652-065807affa74" alt=""><figcaption></figcaption></figure>


# Auto Income Distribution

<figure><img src="https://3178843284-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDTuvot4fROkCCGtjC2y8%2Fuploads%2FxvuJBU9gbQPkjtC5vWFk%2Fimage.png?alt=media&amp;token=004af527-3729-4d6a-8e6a-d799e600c6ee" alt=""><figcaption></figcaption></figure>

Passive income is auto-distributed to USDi holders periodically without the need to claim and pay gas fees.

The USDi Payout Calculator will calculate the available distribution values  periodically. Any supplus amount of distribution values will be distributed to all USDi holders based on cumulated distribution weight score (cumDWS) index.&#x20;

The cumDWS index is measuring the amount of USDi and holding period of each USDi holders. The more USDi you are holding for  longer time period you are holding, the higher cumDWS score you will have. &#x20;

### Definitions:

**Available distribution value (ADV)**= total assets value - USDi in circulation&#x20;

**Distribution Value (DV)** = Distribution % \* ADV

**Distribution Amount of Fees (DAF)**= DV\* Reserve %

**Final Distribution Amount after reserve (FDA)** = DV - DV\*0.2

**cumDWS** is the cumulated amount of DWS for each wallet address.

**Block number period(BNP)** to represent the length of of holding period.&#x20;

**BNP**= current block number (current BN)- last block number (last BN)

Distribution W**eight Score(DWS)**= BNP \* Balance of USDi&#x20;

**cumDWS** is the cumulated amount of DWS for each wallet address&#x20;

**User Undistributed USDi**=cumDWS % \* FDV

<br>

<br>

| Distribution ID | Available distribution value | Distribution Value (DV) | Distribution Amount of Fees (DAF) | Final Distribution Amount after management fees (FDA) |
| --------------- | ---------------------------- | ----------------------- | --------------------------------- | ----------------------------------------------------- |

<br>


# 0max1 Token

The 0max1 Token is the utility and governance token, mined exclusively by USDi and 0max1 token holders. There are no pre-sales, private sales, or pre-mining events for the 0max1 token. The tokens are created by the community and are fair-launched exclusively for the community.

&#x20;

<figure><img src="https://3178843284-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDTuvot4fROkCCGtjC2y8%2Fuploads%2FpJZZsKkkb0nvOlYfMU3b%2Fimage.png?alt=media&amp;token=4bb52982-f53e-4aec-a9ad-5fede865a8f7" alt=""><figcaption></figcaption></figure>

### Governance&#x20;

Key configurations for the protocol are decided by 0max1 holders through voting. Here is a list of key configurations:

<table><thead><tr><th width="211">Configurations</th><th width="438">Discription</th><th>Value</th></tr></thead><tbody><tr><td>Reserve %</td><td>the percentage that distributed to reserve pool</td><td>100%</td></tr><tr><td>0max1 Token Rewards</td><td>the issue speed of 0max1 token</td><td>/Block</td></tr><tr><td>0max1 Token Rewards % for 0max1 Holders</td><td>the percentage that distributed to 0max1 token holders</td><td>0%</td></tr><tr><td>Yield Booster</td><td>the USDi booster rewards for 0max1 token holders</td><td></td></tr><tr><td>Target APR Funding</td><td>the fund from reserve to achieve a target APR</td><td></td></tr></tbody></table>


# Where to get USDi

### USDi Money Flow

<figure><img src="https://3178843284-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDTuvot4fROkCCGtjC2y8%2Fuploads%2FkLXauDkzF7mOjaTc6phj%2Fimage.png?alt=media&amp;token=ab5e1af4-f15f-408f-85b4-13d9dc78e7df" alt=""><figcaption></figcaption></figure>


# Delta Neutral and Arbitrages

1\) Delta Neutral - price fluctuation was completely excluded;

2\) Arbitrage: it's risk free especially when executed by AI automation.


# Transparency and Auditability:

1\) All transaction records and portfolio positions are publicly available for public auditing;

2\) Real-time audits on-chain.


# Addresses

List of Addresses&#x20;

{% tabs %}
{% tab title="Ethereum" %}

<table data-full-width="true"><thead><tr><th width="183">Name </th><th>Address</th></tr></thead><tbody><tr><td>USDi Token</td><td></td></tr><tr><td>0max1 Token</td><td></td></tr><tr><td>0max1 Reserve</td><td></td></tr><tr><td>0max1 Rewards</td><td></td></tr><tr><td>USDi Rewards</td><td></td></tr></tbody></table>
{% endtab %}

{% tab title="Binance Smart Chain" %}

|   |   |   |
| - | - | - |
|   |   |   |
|   |   |   |
|   |   |   |

{% endtab %}
{% endtabs %}


# Human-error Free with AI Automation:

Trades are driven and analyzed by AI-trained bots, eliminating human error/biased decisions often associated with crypto funds.


# Data Source

Binance API:

docs: <https://binance-docs.github.io/apidocs/delivery/en/#general-info>

Copy

```
url = 'https://dapi.binance.com/dapi/v1/exchangeInfo'
```

apiKey dbefbc809e3e83c283a984c3a1459732ea7db1360ca80c5c2c8867408d28cc83

Method:

Data:

Sample Code:


# Simulator

the purpose of 0max1 simulator is to give the most optimized configurations for 0max1 monitor. This recommended configurations is based on historic backtest simulation results.

Steps:

```
1. Start

2. Read Data 
Raw history funding rates data ('df'): fatched by Monitor.
Simulation Configurations Data ('simulations_df'): contains multiple simulations with varying configurations in each simulation.

4. Define Functions
'token_score_func(symbol, simulations_id, model_num)': 
It is used to calculate the scores for every token within each simulation. 
'simulation_apr_func(Tokens_names, simulations_id, model_num)':
It is used to calculate the APR for each simulation based on the top tokens that are selected by their scores.

5. Model Generation Process 
    - The model initially selects a 'simulations_id' from 'simulations_df'
    - Generates the simulation 10 times over 30 days, utilizing 90 data points at a frequency of every 3 days (yielding 9 data points each)
    - Calculates scores for every 40 tokens within each generation by using the function 'token_score_func(symbol, simulations_id, model_num)' 
    - Selectes the top tokens ('Token_names') based on their scores ('top3_score')
    - Calculates the realized APR of the simulation ('cal_apr') by using the function 'simulation_apr_func(Token_names, simulations_id, model_num)'
    - Stores all 10 generations realized APRs for this single simulation in 'simulation_total_apr'
    - Computes the average APR and stores in 'simulation_avg_apr'
    - Repeat the process for every simulation and the results are stored in 'sorted_simulation_avg_apr'
    - The whole model generation process will repeat every 30 days (90 data points)

6. Collect and Prepare Final Data
Compile all processed and optimized realized APR into a final DataFrame 'max_realized_apr'
Format and sort each simulation's realized APR score and merge it with Simulation Configurations Data 'simulations_df'

7. Output Results
Display the final DataFrame 'final_output'
```


# Definitions

## Raw data:

* **Historical Funding Rates Data (df):** this DataFrame reads the CSV file containing original historical funding rate data collected by Monitor. To verify the simulation, you can use the monitor by restricting the time range and resetting the weight and allocation.
* **Simulation Configurations Input (simulations\_df):** this dataframe reads the CSV file containing inputs for different period funding rate weights and allocation weights for each simulation. These are the configurations that will be used to calculate tokens' scores and the simulation's realized APRs. (**Simulations\_df** is created by **Weight\_Generator**)
  * **funding rate weights inputs**: are established for a token's average funding rates over different periods—3 days, 7 days, 30 days, previous funding rate, and next funding rate within a simulation. These funding rate weights are defined in the code as&#x20;

    ```python
    WEIGHTS = [W3, W7, W30, W_prev, W_next]
    ```
  * **allocation weights inputs:** are determined for the top-ranked tokens selected based on their scores within a simulation. These allocation weights are defined in the code as&#x20;

    ```python
    ALLOCATIONS = [A1, A2, A3]
    ```

## Calculations:

**symbol:** token's name&#x20;

$$symbol∈unique(df\[′symbol′])$$

**simulations\_id:** index id of the specific simulation in 'simulations\_df'

$$simulation\_id∈Simulation:ID\[0,1,2,3,4,5,...., n],   \ :::where : Simulation: ID \isin simulations\_df$$

**model\_num:** is the number represents the $$ith$$ generation for a simulation that generates in the model&#x20;

$$model\_num \isin \[1,2,3,4,5,6,7,8,9,10]$$ &#x20;

**group:** is the subset of 'df' where the token symbol matches $$symbol$$, specifically the funding rate data.

$$\bold{i}$$: calculates index based on the length of 'group', the model number, and other factors. This will identify the rates that will be collected, beginning with index $$i$$.&#x20;

$$i = (len(group)::mod:90)+(model\_num−1)×9$$

$$\bold{W}$$: List of weight arrays, where each weight array corresponds to a particular simulation&#x20;

$$weights=\[W\_j​\[simulations\_id] : for : W\_j​ : in :WEIGHTS]$$

$$\bold{A}$$: List of allocation weight arrays, where each allocation weight array corresponds to a particular rank for a token that selected in a simulation

$$allocations =\[A\_j​\[simulations\_id] : for : A\_j​ : in :ALLOCATIONS]$$

* **Token Scores Function 'token\_score\_func(symbol, simulations\_id, model\_num)':** is a function to calculate all tokens' scores of a simulation in a single generation.&#x20;

  **Average Periodic APR Calculations:** Calculate average funding rates over different periods and scale them to get APRs. For the APR calculations for different periods

$$
APR\_3 = Mean(group\[i: i+9])\times 3\times360\times100
\\
APR\_7 = Mean(group\[i: i+21])\times 3\times360\times100
\\
APR\_{30} = Mean(group\[i: i+90])\times 3\times360\times100
\\
APR\_{prev} = Mean(group\[i+1])\times 3\times360\times100
\\
APR\_{next} = Mean(group\[i])\times 3\times360\times100
$$

$$
token\_score\_func= \displaystyle\sum\_{k}^m(APR\_k(symbol, i) × W\_k\[simulation\_id]) ,
\\
for: k \isin m =\[3,: 7, :30, :prev,:next]
$$

* **Simulation Realized APR Function 'simulation\_apr\_func(Tokens\_names, simulations\_id, model\_num)':** is a function to calculate a simulation's APR in a single generation, using the top 3 tokens chosen by **Token Scores Function**.

  **Average 3 days funding rate:** Calculate average funding rate over 3 days for the token identified by 'symbol' that selected by its score in a simulation.

$$
avg\_3Days\_rate = Mean(group\_{symbol}\[i-9 : i])
$$

$$
simulation\_apr\_func= \displaystyle\sum\_{j}^r(avg\_3Days\_rate(symbol\_j) × A\_j\[simulation\_id]) ,
\newline
for: j \isin r =\[1,: 2, :3, :...],
\newline
\
'r':represents:top:token's:rank:in:a:simulation.
$$

* **Average Simulation APR in all generations ('simulation\_avg\_apr')**:  is a average realized APR of a single simulation in all 10 generations.

$$
simulation\_{avg}\_{apr} =  \displaystyle\sum\_{h=1}^{T}simulation\_apr\_func\_{h} ::\div:: T
\\
for: h\isin model\_num ,
\\
where :
T = \sum{model\_num},
\\
$$

## Output data:

**Final Output ('final\_output'):** final output is a merged table that includes 'funding\_rate\_weight\_df' with an additional column called 'Realized APR' and 'Tokens'. 'Realized APR' contains the average APRs from each of the 9 simulations across a total of 10 generations."

**The Most Optimized Configurations:** is the recommend configuration selected from a simulation with the highest 'Realized APR' in the final output, which will be fed into the monitor.&#x20;


# Sample codes and details

## Input:

**Weight Generator**

* **Weight Part**

```python
import pandas as pd
import numpy as np
import itertools

def generate_weights_with_constraints_25(num_weights, num_simulation=70):
    possible_values = [0, 0.25, 0.5, 0.75, 1]
    simulations = []
    
    # Generate all possible combinations
    all_combinations = list(itertools.product(possible_values, repeat=num_weights))
    valid_combinations = [combo for combo in all_combinations if np.isclose(sum(combo), 1)]
    
    simulations = np.random.choice(len(valid_combinations), num_simulation, replace=False)
    simulations = [valid_combinations[i] for i in simulations]

    return simulations

# For 0.25 interval, total number of simulation are 70
num_weights = 5
num_simulation = 70

simulations = generate_weights_with_constraints_25(num_weights, num_simulation)
simulations = pd.DataFrame(simulations
```

* **Allocation Part**

```python
def generate_allcations_with_constraints_10(num_allocations, num_simulation=70):
    possible_values = [0, 0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]
    simulations = []
    
    # Generate all possible combinations
    all_combinations = list(itertools.product(possible_values, repeat=num_allocations))
    valid_combinations = [combo for combo in all_combinations if np.isclose(sum(combo), 1)]
    
    simulations = np.random.choice(len(valid_combinations), num_simulation, replace=False)
    simulations = [valid_combinations[i] for i in simulations]

    return simulations

# For 0.1 interval, total number of allocations are 66
num_allocations = 3
num_simulation = 66

allocations_simulation = generate_allcations_with_constraints_10(num_allocations, num_simulation)
allocations_simulation = pd.DataFrame(allocations_simulation)
```

* **Data Combination**

```python
simulations['Re-Balance Frequence Day'] = 3
allocations_simulation['Re-Balance Frequence Day'] = 3


simulations_df = pd.merge(simulations, allocations_simulation, on='Re-Balance Frequence Day')
simulations_df = simulations_df.reset_index()

simulations_df = simulations_df.rename(columns={'index': 'Simulation ID',
                                       '0_y':'% Allocation for Priority 1', 
                                       '1_y':'% Allocation for Priority 2', 
                                       '2_y': '% Allocation for Priority 3',
                                       '0_x': 'Previous Funding Rate APR Weight',
                                       '1_x': 'Next Funding Rate APR Weight',
                                       '2_x': '3 Day Cum Funding APR Weight',
                                       3: '7 Day Cum Funding APR Weight',
                                       4: '30 Day Cum Funding APR Weight'
                                       })

simulations_df.to_csv('new_funding_rate_weight_df_2.csv', index=False)
```

**Simulation Configurations Input:**&#x20;

* **funding rate weights input**
* **allocation weights input**

**Historical Funding Rates Data:**

```python
simulations_df = pd.read_csv('/~path/funding_rate_weight_df.csv')

# Define the weights by properly calling tolist() with parentheses
W3 = simulations_df['3 Day Cum Funding APR Weight'].tolist()
W7 = simulations_df['7 Day Cum Funding APR Weight'].tolist()
W30 = simulations_df['30 Day Cum Funding APR Weight'].tolist()
W_next = simulations_df['Next Funding Rate APR Weight'].tolist()
W_prev = simulations_df['Previous Funding Rate APR Weight'].tolist()
A1 = simulations_df['% Allocation for Priority 1'].tolist()
A2 = simulations_df['% Allocation for Priority 2'].tolist()
A3 = simulations_df['% Allocation for Priority 3'].tolist()
F = simulations_df['Re-Balance Frequence Day'].tolist()

# Global weights for APR calculation
WEIGHTS = [W3, W7, W30, W_prev, W_next]
ALLOCATIONS = [A1, A2, A3]
```

**Creating feasible DataFrame**

```python
# Load the data
with open('historical_funding_rates_0707.json', 'r') as file:
    data = json.load(file)

frames = []
for symbol, records in data.items():
    df = pd.json_normalize(records)
    df['symbol'] = symbol
    
    # Check if 'fundingRate' exists in the dataframe and convert to numeric
    if 'fundingRate' in df.columns:
        df['fundingRate'] = pd.to_numeric(df['fundingRate'], errors='coerce')
    else:
        df['fundingRate'] = pd.NA  # Assign a missing value indicator if 'fundingRate' is not present
    
    frames.append(df)

# Concatenate all frames into a single DataFrame
df = pd.concat(frames, ignore_index=True)

# Convert 'fundingTime' from milliseconds to a datetime object
df['fundingTime'] = pd.to_datetime(df['fundingTime'], unit='ms')

# Split 'fundingTime' into separate date and time components
df['date'] = df['fundingTime'].dt.date
df['time'] = df['fundingTime'].dt.time

# Drop rows where any column has NaN values
df = df.dropna()

# Sort by 'date' in descending order
df = df.sort_values(by='date', ascending=False)

# Display the first few rows to verify
df = df[['symbol', 'date', 'time', 'fundingRate', 'markPrice']]
```

## Math functions & formulas:

* **Token Scores Function:**

  **'token\_score\_func(symbol, simulations\_id, model\_num)'**
* **Simulation Realized APR Function:**

  **'simulation\_apr\_func(Tokens\_names, simulations\_id, model\_num)'**&#x20;

```python
def token_score_func(symbol, simulations_id, model_num):

    group = df.loc[df['symbol'] == symbol, 'fundingRate']
    i = len(group) % 90 + (model_num - 1) * 9

    aprs = [
        group.iloc[i : i + 9].mean() * 3 * 360 * 100,
        group.iloc[i : 21 + i].mean() * 3 * 360 * 100,
        group.iloc[i : 90 + i].mean() * 3 * 360 * 100,
        group.iloc[1 + i] * 3 * 360 * 100,
        group.iloc[i] * 3 * 360 * 100 
    ]
    
    #simulations_id = simulations_id - 1 
    weights = [W[simulations_id] for W in WEIGHTS]  # Gather weights for each APR based on simulation ID
    
    # Calculate the weighted APR score
    apr_score = sum(apr * weight for apr, weight in zip(aprs, weights))
    
    return apr_score
```

```python
def simulation_apr_func(Tokens_names, simulations_id, model_num):
    top3_rates = []

    for symbol in Tokens_names:
        group = df.loc[df['symbol'] == symbol, 'fundingRate']
        i = len(group) % 90 + (model_num - 1) * 9
        rates = group.iloc[i - 9 : i ].mean()
        top3_rates.append(rates)
    
    allocations = [A[simulations_id] for A in ALLOCATIONS]
    sim_apr = sum(rate * allocation for rate, allocation in zip(top3_rates, allocations)) * 360 * 3
    
    return sim_apr
```

```python
token_score = {}
simulaiton_id = simulations_df.index.tolist()
model_nums = range(1,11)
token_score_rank = []
simulation_avg_apr = {}

for id in simulaiton_id:
    simulation_total_apr = 0  

    for i in model_nums:
        token_score = {}
        simulation_apr = {}

        for symbol in symbols:        
            score = token_score_func(symbol, id, i)
            token_score[(symbol, id, i)] = score

        top3_score = dict(sorted(token_score.items(), key=lambda item: item[1],reverse=True)[:3])

        Token_names = [key[0] for key in top3_score.keys()]

        cal_apr = simulation_apr_func(Token_names, id, i)

        simulation_apr[(f'Model Num {i}', f'Simulation {id}')] = cal_apr
        simulation_total_apr += cal_apr 

    simulation_avg_apr[id] = simulation_total_apr / len(model_nums)


sorted_simulation_avg_apr = dict(sorted(simulation_avg_apr.items(), key=lambda item: item[1], reverse=True))
sorted_simulation_avg_apr
```

## Output

* **Funding rate weight and Portfolio allocation configurations for monitor**

```python
max_realized_apr = [
    {'Simulation ID': key, 'Realized APR': value}
    for key, value in sorted_simulation_avg_apr.items()
]

max_realized_apr_token = [
    {'Simulation ID': key, 'Tokens': value}
    for key, value in simulation_top3_token.items()
]

max_realized_apr = pd.DataFrame(max_realized_apr)
max_realized_apr.reset_index(drop=True, inplace=True)

max_realized_apr_token = pd.DataFrame(max_realized_apr_token)

max_realized_apr = pd.merge(max_realized_apr, max_realized_apr_token, on='Simulation ID')
max_realized_apr.to_csv('max_realized_apr_may.csv', index=False)
max_realized_apr
```

<figure><img src="https://3178843284-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDTuvot4fROkCCGtjC2y8%2Fuploads%2F0kGSePRMRY2DA3JFSIuZ%2Fimage.png?alt=media&amp;token=240c9f82-5f44-4a4d-81c3-149dc817b7c2" alt=""><figcaption><p>max_realized_apr</p></figcaption></figure>


# Monitor

The goal of 0max1 monitor is to give live suggestions of portfolio target allocation %, based on pre-decided configurations from 0max1 simulator. It takes input from the exchange public API and calculate Apr score for each asset then give suggestions.


# Process

**Process:**&#x20;

```
1. Start
﻿
2. Set API Key
Retrieve the Binance API key from environment variables.
﻿
3. Fetch All Futures Symbols
Use get_futures_symbols() to get a list of all futures symbols available on   Binance.
﻿
4. For Each Symbol, Fetch Funding Rate History
Loop through each symbol.
Use get_funding_rate_history(symbol, 60) to fetch the last 60 days' worth of funding rate data for each symbol.
﻿
5. Data Processing and APR Calculation
For each dataset fetched:
Calculate 3-day, 7-day, and 30-day average funding rates.
Annualize these averages to get APRs by multiplying the average rates by 365 and converting to percentage.
Store these calculations in the DataFrame.
﻿
6. Optimize APR Score
For the funding rate data of each symbol, use optimize_apr_score(group) to compute the optimized APR score based on weighted averages of the 3-day, 7-day, 30-day, and the most recent funding rates.
﻿
7. Collect and Prepare Final Data
Compile all processed and optimized data into a final DataFrame.
Format and sort data based on optimized APR scores.
﻿
8. Output Results
Display the final DataFrame.
```


# Definitions

<pre><code>1. Environment Setup:
api_key: This variable retrieves the API key from the environment variables to securely access the Binance API without hardcoding sensitive information.

2. Global Constants:
Weights (WEIGHTS): These are coefficients used to calculate the Annual Percentage Rate (APR) score based on the funding rates over various periods. They reflect the importance of each period's funding rate in determining the overall APR score. The weights are defined as follows:
W3: Weight for the 3-day  funding rate.
W7: Weight for the 7-day  funding rate.
W30: Weight for the 30-day  funding rate.
W_next: Weight for the next funding rate.
W_prev: Weight for the previous funding rate.

<strong>3. Function Definitions:
</strong>get_futures_symbols(): Calls the Binance API to retrieve a list of all available futures symbols.
get_funding_rate_history(symbol, display_days=30): Fetches the funding rate history for a specified futures symbol over a set number of days from the Binance API.
calculate_apr_score(group): Computes the APR score for a given dataset of funding rates using predefined weights. This score helps in assessing the profitability of each contract.

4. Data Retrieval and Processing:
Initially, all futures symbols are fetched and stored.
For each symbol, its funding rate history is retrieved for the last 60 days.
This data is then processed to compute the APR score using the function calculate_apr_score.

<strong>5. Data Aggregation and Output:
</strong>All the processed data is aggregated into a DataFrame.
The DataFrame is sorted by APR score, and the top 5 entries are assigned allocation percentages based on their rankings.
The final DataFrame is formatted for readability and saved as a CSV file named APR.csv.
<strong>
</strong>6. Post-Processing:
Certain columns in the DataFrame are formatted as percentages for clarity.
The DataFrame includes columns like symbol, time, funding rates, APR score, and allocation percentage, making it informative and easy to understand.
</code></pre>


# Sample codes and details.

## Sample Code:

```python
import requests
import pandas as pd
import os
import websocket
import json
import ssl
import threading
import datetime

# Set Binance API Key as an environment variable
api_key = os.getenv('BINANCE_API_KEY')
latest_funding_rates = {}

def on_message(ws, message):
    global latest_funding_rates
    data = json.loads(message)
    if 'stream' in data:
        symbol = data['stream'].split('@')[0].upper()  # Extract symbol from stream name in uppercase
        latest_funding_rates[symbol] = float(data['data']['r'])  # Store the rate

def on_error(ws, error):
    print("Error:", error)

def on_close(ws, code, reason):
    print(f"WebSocket closed with code {code}, reason {reason}")

def on_open(ws):
    global streams
    symbols = get_futures_symbols()
    streams = [f"{symbol.lower()}@markPrice" for symbol in symbols]
    subscribe_message = json.dumps({
        "method": "SUBSCRIBE",
        "params": streams,
        "id": 1
    })
    ws.send(subscribe_message)

def connect_websocket():
    websocket.enableTrace(False)
    ws = websocket.WebSocketApp("wss://dstream.binance.com/stream",
                                on_open=on_open,
                                on_message=on_message,
                                on_error=on_error,
                                on_close=on_close)

    def run_ws():
        ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})

    thread = threading.Thread(target=run_ws)
    thread.start()
    return ws, thread

def get_futures_symbols():
    url = 'https://dapi.binance.com/dapi/v1/exchangeInfo'
    headers = {'X-MBX-APIKEY': api_key}
    response = requests.get(url, headers=headers)
    if response.status_code == 200:
        data = response.json()
        symbols = [item['symbol'] for item in data['symbols'] if item['contractType'] == 'PERPETUAL']
        return symbols
    return []

# Global weights for APR calculation
WEIGHTS = [W3, W7, W30, W_prev, W_next]

def calculate_apr_score(group, symbol):
    symbol = symbol.upper()  # Ensure the symbol is in uppercase
    aprs = [
        group.head(9)['fundingRate'].mean() * 3 * 360 * 100,
        group.head(21)['fundingRate'].mean() * 3 * 360 * 100,
        group.head(90)['fundingRate'].mean() * 3 * 360 * 100,
        group.iloc[1]['fundingRate'] * 3 * 360 * 100,
        latest_funding_rates.get(symbol, group.iloc[0]['fundingRate']) * 3 * 360 * 100  # Use real-time rate if available
    ]
    apr_score = sum(apr * weight for apr, weight in zip(aprs, WEIGHTS))
    return apr_score

def get_funding_rate_history(symbol, display_days=30):
    url = 'https://dapi.binance.com/dapi/v1/fundingRate'
    params = {'symbol': symbol, 'limit': display_days * 3}
    headers = {'X-MBX-APIKEY': api_key}
    response = requests.get(url, params=params, headers=headers)
    if response.status_code == 200:
        data = response.json()
        df = pd.DataFrame(data)
        df['fundingRate'] = pd.to_numeric(df['fundingRate'], errors='coerce')
        df['Time'] = pd.to_datetime(df['fundingTime'], unit='ms', utc=True).dt.tz_convert(None)
        return df.sort_values('Time', ascending=False).head(display_days)
    return pd.DataFrame()

def process_data():
    ws, thread = connect_websocket()
    thread.join(timeout=30)  # Wait for the WebSocket to collect data
    symbols = get_futures_symbols()
    all_data_frames = []

    for symbol in symbols:
        df = get_funding_rate_history(symbol, 60)
        if not df.empty:
            df['symbol'] = symbol
            all_data_frames.append(df)

    final_data_list = []
    for data in all_data_frames:
        symbol = data['symbol'].iloc[0]
        apr_score = calculate_apr_score(data, symbol)
        latest_data = data.sort_values('Time', ascending=False).iloc[0].to_dict()
        latest_data['Previous Funding Rate'] = data.iloc[1]['fundingRate'] * 360 * 3 * 100
        latest_data['Next Funding Rate'] = latest_funding_rates.get(symbol, data.iloc[0]['fundingRate']) * 360 * 3 * 100
        latest_data['3 Day Cum Funding APR'] = data.head(9)['fundingRate'].mean() * 360 * 3 * 100
        latest_data['7 Day Cum Funding APR'] = data.head(21)['fundingRate'].mean() * 360 * 3 * 100
        latest_data['30 Day Cum Funding APR'] = data.head(90)['fundingRate'].mean() * 360 * 3 * 100
        latest_data['APR Score'] = f"{apr_score:.3f}%"
        # Select only the desired columns
        final_data_list.append({
            'symbol': latest_data['symbol'],
            'Time': latest_data['Time'],
            'Previous Funding Rate': f"{latest_data['Previous Funding Rate']:.3f}%",
            'Next Funding Rate': f"{latest_data['Next Funding Rate']:.3f}%",
            '3 Day Cum Funding APR': f"{latest_data['3 Day Cum Funding APR']:.3f}%",
            '7 Day Cum Funding APR': f"{latest_data['7 Day Cum Funding APR']:.3f}%",
            '30 Day Cum Funding APR': f"{latest_data['30 Day Cum Funding APR']:.3f}%",
            'APR Score': latest_data['APR Score']
        })

    final_data = pd.DataFrame(final_data_list)
    final_data.sort_values('APR Score', ascending=False, inplace=True)
    # Add timestamp to file name
    timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%d_%H-%M-%S_UTC')
    file_name = f'APR_results_{timestamp}.csv'
    final_data.to_csv(file_name, index=False)
    print(f"Data saved to '{file_name}'")

    if ws.sock and ws.sock.connected:
        ws.close()

if __name__ == "__main__":
    process_data()
```

## **Math formula**

<pre><code>1. Average Funding Rate Calculation:
Average Funding Rate_n = (Sum of Funding Rates over the last n periods) / n
Here, n represents the number of periods, such as 3, 7, or 30 days.

<strong>2. Annualization of the Funding Rate:
</strong>Annualized Funding APR_n = Average Funding Rate_n x 360 x 3 x 100
This converts the average funding rate for n days into an annual percentage rate by multiplying by the number of days in a year (365) and then converting it to a percentage by multiplying by 100.

3. APR Score Calculation with Weights:
APR Score = (Annualized Funding APR_3 x W_3) + (Annualized Funding APR_7 x W_7) + (Annualized Funding APR_30 x W_30) + (Annualized Funding APR_next x W_next) + (Annualized Funding APR_prev x W_prev)
The APR Score is calculated by taking the weighted average of several Annualized Funding APRs for different periods. This is done by multiplying each Annualized Funding APR by its respective weight and then summing up these products.
</code></pre>

## **Output**

<figure><img src="https://3178843284-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDTuvot4fROkCCGtjC2y8%2Fuploads%2FCAyfpFMhNiVhoSmbOlEY%2F%E6%88%AA%E5%B1%8F2024-04-20%20%E4%B8%8A%E5%8D%8812.18.07.png?alt=media&amp;token=7b66d94c-8c19-4e02-867f-698500b33dc7" alt=""><figcaption></figcaption></figure>


# APIs for Funding ARB Analytics

## Base URL

<https://max1-funding-arb.uc.r.appspot.com/>&#x20;

## Endpoints

### 1. Logging In (\*\*\*Required First Step \*\*\*)

* Endpoint: /login&#x20;
* Method: POST&#x20;
* Description: login and retrieve JWT token

a. Request

* Headers: Content-Type: application/json&#x20;
* Query Parameters: None
* Body:
  * username (string): The username of the user.
  * password (string): The password of the user.

b. Responce

* Status Code: 200 OK
* Body:

```
{ "access_token": "<access_token>", "refresh_token": "<refresh_token>" }
```

c. Example

```
fetch('https://max1-funding-arb.uc.r.appspot.com/login', {
   method: 'POST',
   headers: {
       'Content-Type': 'application/json'
   },
   body: JSON.stringify({
       username: 'admin',
       password: 'admin'
   })
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
```

### 2. Get Raw Data Table (requires JWT token)

* Endpoint: /raw\_data\_table
* Method: GET
* Description: Retrieves all records from the raw\_data\_table.

a. Request

* Headers: None
* Query Parameters: None
* Body: None

b. Response

* Status Code: 200 OK
* **Body:** JSON array of objects containing the raw data records.

c. Example

```
fetch('https://max1-funding-arb.uc.r.appspot.com/raw_data_table', {
   method: 'GET',
   headers: {
       'Authorization': 'Bearer YOUR_JWT_TOKEN_HERE'
   }
})
.then(response => {
   if (!response.ok) {
       throw new Error('Network response was not ok');
   }
   return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
```

d. Response Example

```
[
{"funding_id":1,"fundingrate":"-0.00021167","fundingtime":1713456000000,"markprice":"63515.40000000","symbol":"BTCUSD_PERP","time":"Thu, 27 Jun 2024 08:03:24 GMT"},
{"funding_id":2,"fundingrate":"-0.00029339","fundingtime":1713484800000,"markprice":"63477.88531564","symbol":"BTCUSD_PERP","time":"Thu, 27 Jun 2024 08:03:24 GMT"},
  …
]
```

### 3.1  Get Portfolio Track Records (requires JWT token)

* Endpoint: /portfolio\_track\_records
* Method: GET
* Description: Retrieves all records from the portfolio\_track\_records table.

a. Request

* Headers: None
* Query Parameters:
* &#x20; \- page (optional): The page number to retrieve. Defaults to all records if not provided.
* &#x20; \- limit (optional): The number of records per page. Defaults to all records if not provided.
* Body: None

b. Response

* Status Code: 200 OK
* Body: JSON array of objects containing the portfolio track records.

c. Example

```
fetch('https://max1-funding-arb.uc.r.appspot.com/portfolio_track_records?page=1&limit=60, {
   method: 'GET',
   headers: {
       'Authorization': 'Bearer YOUR_JWT_TOKEN_HERE'
   }
})
.then(response => {
   if (!response.ok) {
       throw new Error('Network response was not ok');
   }
   return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

```

d. Response Example

```
{[ 
{"id":1,"money_at_risk_percent":"-0.7635539234855009","net_exposure_value":"-0.7577440087800085","portfolio_implied_30days_earnings":"0.8605773848134116","portfolio_implied_apr":"0.10406109150689863","timestamp":"Fri, 28 Jun 2024 04:20:33 GMT","total_assets_value":"99.23909569098001"},

{"id":2,"money_at_risk_percent":"-0.7301864268488106","net_exposure_value":"-0.7248933446900026","portfolio_implied_30days_earnings":"0.8606027411036695","portfolio_implied_apr":"0.10402641079258573","timestamp":"Fri, 28 Jun 2024 07:05:01 GMT","total_assets_value":"99.27510537526001"},
…..
],
"total_records": 120, 
"page": 1, 
"limit": 60
}
```

### 3.2 Get Latest Portfolio Data food a time range (requires JWT token)

* Endpoint: /track\_records\_daily\_latest
* Method: GET
* Description: retrieves the latest records for each day up to 1:00 UTC within a specified date range. If no date range is provided, it fetches the latest records up to 1:00 UTC for all available days.

a. Request

* Headers: None
* Query Parameters:&#x20;
* &#x20; \- start\_date (optional, string, format: YYYY-MM-DD
* &#x20; \- The start date of the range from which to fetch records.
* &#x20; \- Example: 2024-08-03
* &#x20; \- end\_date (optional, string, format: YYYY-MM-DD)
* &#x20; \- The end date of the range up to which to fetch records.
* &#x20; \- Example: 2024-09-02
* Body: None

b. Response

* Status Code: 200 OK
* Body: JSON array of objects containing the track of records data.

c. Example

```
fetch('https://max1-funding-arb.uc.r.appspot.com/track_records_daily_latest?start_date=2024-08-03&end_date=2024-09-02', {
   method: 'GET',
   headers: {
       'Authorization': 'Bearer YOUR_JWT_TOKEN_HERE'
   }
})
.then(response => {
   if (!response.ok) {
       throw new Error('Network response was not ok');
   }
   return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
// Some code
```

### 4. Get Portfolio Latest Data (requires JWT token)

* Endpoint: /portfolio\_latest
* Method: GET
* Description: Retrieves all records from the portfolio\_latest table.

a. Request

* Headers: None
* Query Parameters: None
* Body: None

b. Response

* Status Code: 200 OK
* Body: JSON array of objects containing the latest portfolio data.

c. Example

```
fetch('https://max1-funding-arb.uc.r.appspot.com/portfolio_latest', {
   method: 'GET',
   headers: {
       'Authorization': 'Bearer YOUR_JWT_TOKEN_HERE'
   }
})
.then(response => {
   if (!response.ok) {
       throw new Error('Network response was not ok');
   }
   return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
```

d. Response Example

```
[ 
{"amount":"6.8792","assets_types":"Crypto","assets_value":"62.2633","implied_apr":"0.0","mark_price":"9.051","order_value_required":"0.0","portfolio_allocation":"0.6271","product_category":"Coin-M Futures","symbol":"UNI","target_allocation_percent":"0.0","utc_timestamp":"Tue, 02 Jul 2024 08:20:19 GMT"},
{"amount":"0.2512","assets_types":"Crypto","assets_value":"37.0242","implied_apr":"0.0","mark_price":"147.389","order_value_required":"0.0","portfolio_allocation":"0.3729","product_category":"Coin-M Futures","symbol":"SOL","target_allocation_percent":"0.0","utc_timestamp":"Tue, 02 Jul 2024 08:20:19 GMT"},

  …
]
```

### 5. Get Monitor APR Attribute Data (requires JWT token)

* Endpoint: /monitor\_apr\_attribute
* Method: GET
* Description: Retrieves all records from the monitor\_apr\_attribute table.

a. Request

* Headers: None
* Query Parameters: None
* Body: None

b. Response

* Status Code: 200 OK
* Body: JSON array of objects containing the monitor APR attribute data.

c. Example

```
fetch('https://max1-funding-arb.uc.r.appspot.com/monitor_apr_attribute', {
   method: 'GET',
   headers: {
       'Authorization': 'Bearer YOUR_JWT_TOKEN_HERE'
   }
})
.then(response => {
   if (!response.ok) {
       throw new Error('Network response was not ok');
   }
   return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
```

d. Response Example

```
[
 {"30_day_cum_funding":"0.09370907999999999","3_day_cum_funding":"0.1376676","7_day_cum_funding":"0.07607777142857143","allocation_percentage":"0.4","apr_id":1,"apr_score":"0.1449632622857143","next_funding_rate":"0.2306664","previous_funding_rate":"0.14273280000000002","simulator_id":null,"symbol":"BNBUSD_PERP","time":"Thu, 27 Jun 2024 08:00:00 GMT"},

{"30_day_cum_funding":"0.12468504000000001","3_day_cum_funding":"0.13084440000000003","7_day_cum_funding":"0.09699222857142857","allocation_percentage":"0.35","apr_id":2,"apr_score":"0.11432026971428573","next_funding_rate":"0.10800000000000001","previous_funding_rate":"0.10800000000000001","simulator_id":null,"symbol":"RUNEUSD_PERP","time":"Thu, 27 Jun 2024 08:00:00 GMT"},
…..
]

```

### 6. Refresh Token

1. Endpoint: /refresh
2. Method: POST
3. Description: This endpoint allows a user to refresh their access token using a valid refresh token.

a. Request

* Headers&#x20;
  * Authorization: Bearer \<refresh\_token>
  * Content-Type: application/json
* Body: None

b. Response

* Status Code: 200 OK
* Body: {"access\_token": "\<new\_access\_token>"}

### 7. Trigger Cloud Function DataSync

1. Endpoint: /run\_dataSync
2. Method: GET
3. Description: Triggers the dataSync function to fetch data and store into database&#x20;

a. Request

* Headers: None
* Query Parameters: None
* Body: None

b. Response

* Status Code: 200 OK
* Body: None

### 8. Trigger Cloud Run Monitor

1. Endpoint: /run\_monitor
2. Method: GET
3. Description: Triggers the monitor instance to fetch data and store into database

a. Request

* Headers: None
* Query Parameters: None
* Body: None

b. Response

* Status Code: 200 OK
* Body: None

### 9. Trigger Cloud Run Portfolio

1. Endpoint: /run\_portfolio
2. Method: GET
3. Description: Triggers the portfolio instance to fetch data and store into database

a. Request

* Headers: None
* Query Parameters: None
* Body: None

b. Response

* Status Code: 200 OK
* Body: None


